summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/$common.js17
-rw-r--r--modules/blog/blog.js2
-rw-r--r--modules/changes/changes.js2
-rw-r--r--modules/proxy/proxy.js2
-rw-r--r--modules/save/save.js2
-rw-r--r--modules/statusbot/statusbot.js50
-rw-r--r--modules/timetrack/timetrack.js2
-rw-r--r--modules/timetrack2/timetrack2.js2
-rw-r--r--modules/timetrack3/timetrack3.js2
-rw-r--r--modules/todo/todo.js2
-rw-r--r--modules/unicode/unicode.js6
-rw-r--r--modules/up_log/index.html37
-rw-r--r--modules/up_log/up_log.js27
-rwxr-xr-xmodules/vacancies/bak/chalmers.sh30
-rwxr-xr-xmodules/vacancies/getters/aarhus.sh8
-rwxr-xr-xmodules/vacancies/getters/cambridge.sh9
-rwxr-xr-xmodules/vacancies/getters/glasgow.sh8
-rwxr-xr-xmodules/vacancies/getters/mpg.sh8
-rw-r--r--modules/vacancies/vacancies.js84
-rw-r--r--modules/zelfoverhoor/zelfoverhoor.js4
20 files changed, 271 insertions, 33 deletions
diff --git a/modules/$common.js b/modules/$common.js
index 59b9a66..f7ec42a 100644
--- a/modules/$common.js
+++ b/modules/$common.js
@@ -4,13 +4,28 @@ var path=require("path"),
var cwd=process.cwd();
var globalAccounts=require(cwd+"/globalAccounts.json");
+var statusbotObject={
+ // calls cb with http status code
+ "send":function statusbotSend(sender,text,cb){
+ if(statusbotObject._handler==null){
+ console.log("[common.statusbot] No statusbot yet");
+ cb(500);
+ return;
+ }
+ statusbotObject._handler(sender,text,cb);
+ },
+ "_handler":null, // to be set from the statusbot module
+};
+
module.exports={
"serverdir":cwd,
"webfilesdir":cwd+"/web_files",
"persistdir":cwd+"/persist",
+
"simpleHTMLescape":function simpleHTMLescape(str){
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
},
+
"authgen":function authgen(accounts){ //omit `accounts` to use the globalAccounts list
if(!accounts)accounts=globalAccounts;
return function (req,res,next){
@@ -32,4 +47,6 @@ module.exports={
return unauth(res);
};
},
+
+ "statusbot":statusbotObject,
};
diff --git a/modules/blog/blog.js b/modules/blog/blog.js
index 3985fdb..4843d4b 100644
--- a/modules/blog/blog.js
+++ b/modules/blog/blog.js
@@ -112,7 +112,7 @@ module.exports = (app, io, _moddir) => {
next();
});
- app.get("/blog/*", (req, res) => {
+ app.get("/blog/*rest", (req, res) => {
if (req.path.indexOf("/.") != -1) {
res.sendStatus(404);
return;
diff --git a/modules/changes/changes.js b/modules/changes/changes.js
index 0d8fa24..9fa1e44 100644
--- a/modules/changes/changes.js
+++ b/modules/changes/changes.js
@@ -320,7 +320,7 @@ function performCleanup(){
module.exports=function(app,io,_moddir){
moddir=_moddir;
- app.all(["/changes","/changes/*"],cmn.authgen());
+ app.all(["/changes","/changes/*rest"],cmn.authgen());
app.get("/changes",function(req,res){
res.sendFile(moddir+"/changes.html");
});
diff --git a/modules/proxy/proxy.js b/modules/proxy/proxy.js
index ccec53a..3ebe236 100644
--- a/modules/proxy/proxy.js
+++ b/modules/proxy/proxy.js
@@ -39,7 +39,7 @@ module.exports=function(app,io,moddir){
return false;
}
- app.all("/proxy/:id/*",function(req,res){
+ app.all("/proxy/:id/*rest",function(req,res){
var id=req.params.id;
var path="/"+req.path.split("/").slice(3).join("/");
if(iddict[id]){
diff --git a/modules/save/save.js b/modules/save/save.js
index 9873a52..ae1cfaf 100644
--- a/modules/save/save.js
+++ b/modules/save/save.js
@@ -46,7 +46,7 @@ module.exports=function(app,io,_moddir){
});
});
- app.all(["/save/read","/save/read/*"],cmn.authgen());
+ app.all(["/save/read","/save/read/*rest"],cmn.authgen());
app.get("/save/read",function(req,res){
res.sendFile(moddir+"/read.html");
diff --git a/modules/statusbot/statusbot.js b/modules/statusbot/statusbot.js
index 94894a9..38d115b 100644
--- a/modules/statusbot/statusbot.js
+++ b/modules/statusbot/statusbot.js
@@ -279,6 +279,35 @@ function matrixSendMsgLogin(text, cb) {
});
}
+// controller for message sending
+const ratelimit = new PRatelimit(1000);
+
+// Calls cb with http status code
+function handleMessage(sender, text, cb) {
+ if (typeof sender != "string" || typeof text != "string" || sender.indexOf("\n") != -1) {
+ cb(400);
+ return;
+ }
+
+ ratelimit.submit(rlcb => {
+ ptimeout(5000,
+ cb2 => matrixSendMsgLogin(`[${sender}] ${text}`, cb2),
+ (finished, success) => {
+ if (!finished) {
+ logFailure(`Timed out on message: [${sender}] ${text}`, () => {});
+ cb(504); // gateway timeout
+ } else if (!success) {
+ logFailure(`Unsuccessful for message: [${sender}] ${text}`, () => {});
+ cb(503); // service unavailable
+ } else cb(200);
+ rlcb();
+ }
+ );
+ });
+}
+
+cmn.statusbot._handler = handleMessage;
+
module.exports = function(app, io, _moddir) {
moddir = _moddir;
@@ -291,30 +320,11 @@ module.exports = function(app, io, _moddir) {
return false;
}
- const ratelimit = new PRatelimit(1000);
-
augmentConfig(config, state => {
gstate = state;
app.post("/statusbot", bodyParser.json(), cmn.authgen(accounts), (req, res) => {
- if (typeof req.body.sender != "string" || typeof req.body.text != "string") {
- return res.sendStatus(400);
- }
- ratelimit.submit(rlcb => {
- ptimeout(5000,
- cb => matrixSendMsgLogin(`[${req.body.sender}] ${req.body.text}`, cb),
- (finished, success) => {
- if (!finished) {
- res.sendStatus(504); // gateway timeout
- logFailure(`Timed out on message: [${req.body.sender}] ${req.body.text}`, () => {});
- } else if (!success) {
- res.sendStatus(503); // service unavailable
- logFailure(`Unsuccessful for message: [${req.body.sender}] ${req.body.text}`, () => {});
- } else res.sendStatus(200);
- rlcb();
- }
- );
- });
+ handleMessage(req.body.sender, req.body.text, status => res.sendStatus(status));
});
});
};
diff --git a/modules/timetrack/timetrack.js b/modules/timetrack/timetrack.js
index 886ea1e..b654ef1 100644
--- a/modules/timetrack/timetrack.js
+++ b/modules/timetrack/timetrack.js
@@ -159,7 +159,7 @@ module.exports=function(app,io,_moddir){
});
});
- app.all(["/timetrack","/timetrack/*"],authMiddleware); //for all the other endpoints
+ app.all(["/timetrack","/timetrack/*rest"],authMiddleware); //for all the other endpoints
app.get("/timetrack",function(req,res){
res.sendFile(moddir+"/timetrack.html");
diff --git a/modules/timetrack2/timetrack2.js b/modules/timetrack2/timetrack2.js
index cb22132..f3d35ae 100644
--- a/modules/timetrack2/timetrack2.js
+++ b/modules/timetrack2/timetrack2.js
@@ -163,7 +163,7 @@ module.exports = function(app, io, _moddir){
});
});
- app.all([ROOT_ENDPOINT, ROOT_ENDPOINT+"/*"], authMiddleware); //for all the other endpoints
+ app.all([ROOT_ENDPOINT, ROOT_ENDPOINT+"/*rest"], authMiddleware); //for all the other endpoints
app.get(ROOT_ENDPOINT, function(req, res){
res.sendFile(moddir + "/timetrack.html");
diff --git a/modules/timetrack3/timetrack3.js b/modules/timetrack3/timetrack3.js
index dbb0e5b..ba1d9c0 100644
--- a/modules/timetrack3/timetrack3.js
+++ b/modules/timetrack3/timetrack3.js
@@ -202,7 +202,7 @@ module.exports = function(app, io, _moddir){
});
// for all the other endpoints, authorisation is needed
- app.all([ROOT_ENDPOINT, ROOT_ENDPOINT + "/*"], authMiddleware);
+ app.all([ROOT_ENDPOINT, ROOT_ENDPOINT + "/*rest"], authMiddleware);
// - -> html
app.get(ROOT_ENDPOINT, (req, res) => {
diff --git a/modules/todo/todo.js b/modules/todo/todo.js
index b5d3417..239dc19 100644
--- a/modules/todo/todo.js
+++ b/modules/todo/todo.js
@@ -170,7 +170,7 @@ module.exports=function(app,io,_moddir){
});
});
- app.all(["/todo","/todo/*"],authMiddleware); //for all the other endpoints
+ app.all(["/todo","/todo/*rest"],authMiddleware); //for all the other endpoints
app.get("/todo",function(req,res){
var contents=fs.readFileSync(moddir+"/todo.html","utf8");
diff --git a/modules/unicode/unicode.js b/modules/unicode/unicode.js
index 4b313fc..84191fa 100644
--- a/modules/unicode/unicode.js
+++ b/modules/unicode/unicode.js
@@ -1,11 +1,15 @@
"use strict";
+// TODO
+// - https://www.unicode.org/Public/UCD/latest/ucd/NameAliases.txt (https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-4/#G2082)
+
const cmn = require("../$common.js");
const fs = require("fs");
const path = require("path");
const https = require("https");
+// https://www.unicode.org/reports/tr44/#UnicodeData.txt
const fCODE = 0;
const fNAME = 1;
const fCATEGORY = 2;
@@ -15,7 +19,7 @@ const fDIGIT = 7;
const fNUMERIC = 8;
const fMIRRORED = 9;
const fOLDNAME = 10;
-const fCOMMENT = 11;
+// comment (always null by spec, deprecated)
const fUPPERCASE = 12;
const fLOWERCASE = 13;
const fTITLECASE = 14;
diff --git a/modules/up_log/index.html b/modules/up_log/index.html
new file mode 100644
index 0000000..d8371d9
--- /dev/null
+++ b/modules/up_log/index.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Uplog</title>
+<style>
+#log {
+ border: 1px gray solid;
+}
+</style>
+<script>
+function get() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", location.href + "/log/" + document.getElementById("count").value);
+ xhr.responseType = "text";
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ document.getElementById("log").innerHTML = "";
+ document.getElementById("log").appendChild(document.createTextNode(xhr.responseText));
+ } else {
+ alert(xhr.responseText);
+ }
+ }
+ };
+ xhr.send();
+}
+</script>
+</head>
+<body>
+Count: <input type="number" id="count" value="100" min="1" onchange="get()">
+<input type="button" onclick="get()" value="Get">
+<br>
+<pre id="log"></pre>
+<script>get();</script>
+</body>
+</html>
diff --git a/modules/up_log/up_log.js b/modules/up_log/up_log.js
index 609d705..61d2af0 100644
--- a/modules/up_log/up_log.js
+++ b/modules/up_log/up_log.js
@@ -4,7 +4,7 @@ const bodyParser = require("body-parser");
let moddir = null;
-module.exports=function(app,io,_moddir){
+module.exports = function(app, io, _moddir) {
moddir = _moddir;
let config, accounts;
@@ -15,7 +15,7 @@ module.exports=function(app,io,_moddir){
return false;
}
- app.post("/up_log/log", bodyParser.json(), cmn.authgen(accounts), (req,res) => {
+ app.post("/up_log/log", bodyParser.json(), cmn.authgen(accounts), (req, res) => {
if (typeof req.body.machine != "string") {
return res.sendStatus(400);
}
@@ -26,4 +26,27 @@ module.exports=function(app,io,_moddir){
res.status(200).end();
});
+
+ app.get("/up_log/log/:count", cmn.authgen(), (req, res) => {
+ const reqln = +req.params.count;
+ if (reqln < 0 || reqln % 1 != 0 || isNaN(reqln)) { res.sendStatus(400); return; }
+
+ fs.readFile(moddir + "/log.txt", (err, data) => {
+ if (err) { res.sendStatus(500); return; }
+
+ let i = data.length - 1 - (data[data.length-1] == 10);
+ let nln = 0;
+ while (true) {
+ if (data[i] == 10) { nln++; if (nln >= reqln) { i++; break; } }
+ i--;
+ if (i <= 0) { nln++; break; }
+ }
+
+ res.send(data.slice(i));
+ });
+ });
+
+ app.get("/up_log", cmn.authgen(), (req, res) => {
+ res.sendFile(moddir + "/index.html");
+ });
};
diff --git a/modules/vacancies/bak/chalmers.sh b/modules/vacancies/bak/chalmers.sh
new file mode 100755
index 0000000..5583223
--- /dev/null
+++ b/modules/vacancies/bak/chalmers.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+url='https://web103.reachmee.com/ext/I003/304/main?site=5&validator=a72aeedd63ec10de71e46f8d91d0d57c&lang=UK'
+
+script='
+0,/<div id="mainjoblist">/d
+/<tbody>/,/<\/tbody>/!d
+
+/<\/tr>/ {
+ s/.*//
+ x
+ s/\n//g
+ s/^ | //
+ /Technical and Administrative staff/d
+ /PhD Student Positions/d
+ p
+ d
+}
+
+/<td>/!d
+/^\s*<\/td>\s*$/d
+s|\s*<td>\(.*\)</td>\s*|\1|
+s|.*Application deadline:.*display:\s*none">\([^<]*\)</span>.*|\1|
+s|.*a href=.*reachmee.*/job.*job_id[^>]*>\([^<]*\)</a>.*|\1|
+s/^/ | /
+H
+'
+
+curl -s "$url" | sed -n "$script"
diff --git a/modules/vacancies/getters/aarhus.sh b/modules/vacancies/getters/aarhus.sh
new file mode 100755
index 0000000..8acbe6f
--- /dev/null
+++ b/modules/vacancies/getters/aarhus.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+curl -sL 'https://cs.au.dk/about-us/vacancies/' \
+ | grep 'DYCON\.Em[^ ]*vacancies' \
+ | head -1 \
+ | sed 's/^[^[]*//; s/; *$//' \
+ | jq -r '.[] | (.title + " [https://cs.au.dk" + .link + "]")'
diff --git a/modules/vacancies/getters/cambridge.sh b/modules/vacancies/getters/cambridge.sh
new file mode 100755
index 0000000..f5f9fe1
--- /dev/null
+++ b/modules/vacancies/getters/cambridge.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+curl -sL 'https://www.cam.ac.uk/jobs/term/Department-of-Computer-Science-and-Technology' | \
+ sed '
+/<tbody>/,/<\/tbody>/!d
+s|<a href="/jobs/term/[^"]*">[^<]*</a>||g
+/<a href="\/jobs\//!d
+s|^ *<a href="\([^"]*\)">\(.*\)</a>.*|\2 (https://www.cam.ac.uk\1)|'
diff --git a/modules/vacancies/getters/glasgow.sh b/modules/vacancies/getters/glasgow.sh
new file mode 100755
index 0000000..c6beef5
--- /dev/null
+++ b/modules/vacancies/getters/glasgow.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+curl -sL 'https://www.jobs.gla.ac.uk/jobs/college-of-science-and-engineering/school-of-computing-science-1' \
+ | sed '
+/a href="\/job\// !d
+/>Read More</ d
+s|^ *<a href="\([^"]*\)">\(.*\)</a>.*|\2 (https://www.jobs.gla.ac.uk\1)|'
diff --git a/modules/vacancies/getters/mpg.sh b/modules/vacancies/getters/mpg.sh
new file mode 100755
index 0000000..bf7f222
--- /dev/null
+++ b/modules/vacancies/getters/mpg.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# <td headers="view-ocm-pp-project-title-table-column" class="views-field views-field-ocm-pp-project-title"><a href="/node/21090">Identifying novel ways of inducing effective angiogenesis and the development of arteries</a> </td>
+
+
+curl -s 'https://postdocprogram.mpg.de/all-postdoc-positions' \
+ | sed -n '/td.*project-title/ s/^ *<td[^>]*> *<a href="\([^"]*\)"[^>]*>\([^<]*\).*/\2 (https:\/\/postdocprogram.mpg.de\1)/p'
diff --git a/modules/vacancies/vacancies.js b/modules/vacancies/vacancies.js
new file mode 100644
index 0000000..be3c4b4
--- /dev/null
+++ b/modules/vacancies/vacancies.js
@@ -0,0 +1,84 @@
+const cmn = require("../$common.js");
+const fs = require("fs");
+const child_process = require("child_process");
+const persist = require("node-persist");
+const mkdirp = require("mkdirp");
+
+const FIRST_FETCH_DELAY = 10 * 60 * 1000; // 10 seconds
+const INTERVAL = 3 * 24 * 3600 * 1000; // 3 days
+// const FIRST_FETCH_DELAY = 2 * 1000;
+// const INTERVAL = 20 * 1000;
+
+let moddir = null;
+
+mkdirp.sync(cmn.persistdir + "/vacancies");
+const DB = persist.create({
+ dir: cmn.persistdir + "/vacancies",
+ continuous: false,
+ interval: false,
+});
+DB.initSync();
+
+function sendNotification(text, cb) {
+ cmn.statusbot.send("vacancies", text, cb);
+ // console.log("--> " + text);
+ // cb(200);
+}
+
+function refreshSite(name) {
+ child_process.execFile(moddir + "/getters/" + name + ".sh", (err, stdout, _stderr) => {
+ if (err != null) {
+ sendNotification("Error getting <" + name + ">: " + err, () => {});
+ return;
+ }
+
+ const lines = stdout.split("\n").filter(s => s.length > 0);
+ let news = [];
+
+ let prev = DB.getItemSync(name);
+ if (prev != null) {
+ prev = new Set(prev);
+ for (let line of lines) {
+ if (!prev.has(line)) news.push(line);
+ }
+ } else {
+ news = lines;
+ }
+
+ if (news.length == 0) {
+ console.log("[vacancies] No news for <" + name + ">");
+ return;
+ }
+ console.log("[vacancies] " + news.length + " news for <" + name + ">");
+
+ const message = news.map(s => "<" + name + "> " + s).join("\n");
+ sendNotification(message, status => {
+ if (status == 200) {
+ DB.setItemSync(name, lines);
+ } else {
+ console.log("[vacancies] Failed sending: [[[" + message + "]]]");
+ }
+ });
+ });
+}
+
+function refreshAll() {
+ console.log("[vacancies] Refreshing");
+ let i = 0;
+ for (let name of fs.readdirSync(moddir + "/getters")) {
+ if (!name.endsWith(".sh")) continue;
+ setTimeout(() => refreshSite(name.slice(0, -3)), 2000 * i);
+ i++;
+ }
+}
+
+module.exports = (app, io, _moddir) => {
+ moddir = _moddir;
+
+ setTimeout(() => {
+ refreshAll();
+ setInterval(() => {
+ refreshAll();
+ }, INTERVAL);
+ }, FIRST_FETCH_DELAY); // wait a while before the first fetch
+};
diff --git a/modules/zelfoverhoor/zelfoverhoor.js b/modules/zelfoverhoor/zelfoverhoor.js
index 0acb322..9474378 100644
--- a/modules/zelfoverhoor/zelfoverhoor.js
+++ b/modules/zelfoverhoor/zelfoverhoor.js
@@ -117,7 +117,7 @@ module.exports=function(app,io,_moddir){
persistDB();
}
- app.all("/zelfoverhoor*",function(req,res,next){
+ app.all("/zelfoverhoor*rest",function(req,res,next){
res.header("Cache-Control","private, no-cache, no-store, must-revalidate");
res.header("Expires","-1");
res.header("Pragma","no-cache");
@@ -161,7 +161,7 @@ module.exports=function(app,io,_moddir){
res.send(JSON.stringify(resset));
});
- app.use(["/zelfoverhoor/docent","/zelfoverhoor/docent/*"],cmn.authgen(accounts));
+ app.use(["/zelfoverhoor/docent","/zelfoverhoor/docent/*rest"],cmn.authgen(accounts));
app.get("/zelfoverhoor/docent",function(req,res){
fs.readFile(moddir+"/docent.html",function(err,data){
if(err)throw err;