From ab39f741f3c5e583002bfd1a9ef780b1a0c63143 Mon Sep 17 00:00:00 2001
From: Niko
Date: Mon, 21 Feb 2022 23:54:59 +0100
Subject: [PATCH] Intercept AP Create.Note and check if contains #fedilovequiz
to parse
* FediLove Quiz is a standar text-based (parsed) protocol wrapped inside the object "Note"
that can create a Quiz inside FediLove instance so there can be an interaction between non-FediLove
instances and FediLove instances.
* TODO: implement the response from this side, so it goes parsed as a regular response to the original "Note"
but parsed so if the target instance is FediLove, it parses it as a quiz result and can be interpreted on any AP software
---
api/src/activity/federation.js | 10 +++++
api/src/api-utils.js | 5 +++
api/src/api/auth.js | 4 +-
api/src/api/me.js | 76 ++++++++++++++++++++++++++++++++++
api/src/auth.js | 1 -
api/src/db.js | 1 +
api/src/random.js | 14 +++++++
api/src/utils.js | 6 ---
8 files changed, 108 insertions(+), 9 deletions(-)
create mode 100644 api/src/random.js
delete mode 100644 api/src/utils.js
diff --git a/api/src/activity/federation.js b/api/src/activity/federation.js
index daef13b..7301a5e 100644
--- a/api/src/activity/federation.js
+++ b/api/src/activity/federation.js
@@ -37,6 +37,16 @@ module.exports = {
return true
}
}
+ if (msg.activity.type === 'Create')
+ {
+ if (msg.object.type === 'Note') {
+ var content = utils.firstIfArray(msg.object.content)
+ if (api.me.isFediloveQuiz(content))
+ await api.me.quizCreate(content, msg.actor.id,
+ msg.recipient.id, msg.object.id)
+ return true
+ }
+ }
if (msg.activity.type === 'Delete')
{
if (msg.object.type === 'Note') {
diff --git a/api/src/api-utils.js b/api/src/api-utils.js
index 36d7611..927fabe 100644
--- a/api/src/api-utils.js
+++ b/api/src/api-utils.js
@@ -14,6 +14,11 @@ module.exports = {
return false
}
},
+ md5sum: (str, size) => {
+ size = size || 32
+ const hasher = crypto.createHmac('md5','FediLove')
+ return hasher.update(str).digest('hex').slice(0, size)
+ },
firstIfArray: (o) => {
if (o === undefined)
return null
diff --git a/api/src/api/auth.js b/api/src/api/auth.js
index 22357f7..9bb6a16 100644
--- a/api/src/api/auth.js
+++ b/api/src/api/auth.js
@@ -1,6 +1,6 @@
const api = require('../api-utils.js')
const pwd = require('../passwd.js')
-const utils = require('../utils.js')
+const random = require('../random.js')
module.exports = {
login: {
@@ -16,7 +16,7 @@ module.exports = {
if (!pwd.comparePassword(req.body.password, user.password))
return res.status(403).json({ error: 'invalid_password' })
- const session_id = utils.randomString(128)
+ const session_id = random.base64(128)
const session = await db.table.sessions().insertOne({
id_user: user._id,
session: session_id
diff --git a/api/src/api/me.js b/api/src/api/me.js
index 7eae5c3..e74d5aa 100644
--- a/api/src/api/me.js
+++ b/api/src/api/me.js
@@ -1,6 +1,82 @@
const auth = require('../auth.js')
+const utils = require('../api-utils.js')
+const random = require('../random.js')
+
+function quizParse(html, check) {
+ check = check === undefined ? false : check
+ if (html === undefined || html === null)
+ return false
+ html = decodeURIComponent(html)
+ html = html.replaceAll('>','>').replaceAll('<','<')
+ .replaceAll('"','"').replaceAll('&','&')
+ html = html.replaceAll(/
/g, '
')
+ html = html.replaceAll('
', '\n')
+ html = html.replaceAll('
', '\n')
+ html = html.replaceAll(/<[^<>]+?>/g, '')
+ if (!html.toLowerCase().includes('#fedilovequiz'))
+ return false
+ if (check)
+ return true
+ var it = {}
+ var sq = false
+ var items = []
+ html = html+'\n999: end'
+ const lines = html.split('\n')
+ for (var i in lines) {
+ var ln = lines[i].trim()
+ if (ln === '')
+ continue
+ if (ln.toLowerCase().includes('#fedilovequiz')) {
+ sq = true
+ continue
+ }
+ if (sq) {
+ if (ln.match(/^\d+\:.*$/)) {
+ if (it.question !== undefined)
+ items.push(it)
+ ln = ln.substr(ln.indexOf(':')+1)
+ it = { question: ln.trim(), options: [] }
+ } else {
+ ln = ln.replaceAll(/\s?\|\s?/g,'|')
+ .replaceAll(/\|+/g, '|')
+ if (!ln.includes('|'))
+ continue
+ ps = ln.split('|')
+ for (var j in ps)
+ ps[j] = ps[j].trim()
+ it.options = ps
+ }
+ }
+ }
+ return items
+}
+
+async function quizCreate(content, from, to, replyTo) {
+ var quiz = quizParse(content)
+ if (quiz === false)
+ return false
+ if (from === undefined || from === null)
+ return false
+ if (to === undefined || to === null)
+ return false
+ if (replyTo === undefined || replyTo === null)
+ return false
+ const payload = {
+ id: utils.md5sum(replyTo, 16).toUpperCase(),
+ replyTo, from, to, content: quiz,
+ }
+ await db.table.quizs().insertOne(payload)
+ return payload
+}
+
+function isFediloveQuiz(html) {
+ return quizParse(html, true) !== false
+}
module.exports = {
+ quizParse,
+ quizCreate,
+ isFediloveQuiz,
pending_follows: {
get: [auth.enforceSession, async (req, res) => {
let follows
diff --git a/api/src/auth.js b/api/src/auth.js
index 9b1e314..3d20c8b 100644
--- a/api/src/auth.js
+++ b/api/src/auth.js
@@ -1,5 +1,4 @@
const pwd = require('./passwd.js')
-const utils = require('./utils.js')
module.exports = {
enforceSession: async (req, res, next) => {
diff --git a/api/src/db.js b/api/src/db.js
index c752f10..1afe9a7 100644
--- a/api/src/db.js
+++ b/api/src/db.js
@@ -21,6 +21,7 @@ module.exports = {
objects: () => { return mdb.collection('objects') },
users: () => { return mdb.collection('u__users') },
sessions: () => { return mdb.collection('u__sessions') },
+ quizs: () => { return mdb.collection('u__quizs') },
},
getAPObject: async (objID, checkType) => {
const object = await module.exports.table
diff --git a/api/src/random.js b/api/src/random.js
new file mode 100644
index 0000000..c3ea6a4
--- /dev/null
+++ b/api/src/random.js
@@ -0,0 +1,14 @@
+const Crypto = require('crypto')
+
+module.exports = {
+ base64: (size) => {
+ return Crypto.randomBytes(size)
+ .toString('base64').slice(0, size)
+ },
+ md5: (size) => {
+ size = size || 32
+ const rnd = module.exports.base64(32)
+ const hasher = Crypto.createHmac("md5", rnd.substr(16))
+ return hasher.update(rnd.substr(0,16)).digest("hex").slice(0, size)
+ },
+}
diff --git a/api/src/utils.js b/api/src/utils.js
deleted file mode 100644
index d7db5f1..0000000
--- a/api/src/utils.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const Crypto = require('crypto')
-
-exports.randomString = (size) => {
- return Crypto.randomBytes(size)
- .toString('base64').slice(0, size)
-}