commit 7d8ea3446a67daa1f597bae547bf39a9a4e73b8d Author: DanielMowitz Date: Wed Aug 7 15:44:12 2019 +0200 initial commit diff --git a/bridge.js b/bridge.js new file mode 100644 index 0000000..a917f77 --- /dev/null +++ b/bridge.js @@ -0,0 +1,292 @@ +const ssbClient = require('ssb-client'); +const pull = require('pull-stream'); + +const allowed_message_types = [ //valid activitypub message types + 'create', + 'update', + 'delete', + 'follow', + 'update', + 'reject', + 'add', + 'remove', + 'like', + 'announce', + 'undo' +]; + +async function createBlobObject(type, objectId, otherData) { + + /* + * Turns an activitypub object into a json object with it's id, type and a link + * to the blob store with the rest of the data. This is done to enable deletion + * and updating of objects. + */ + + let out = new Promise((resolve, reject) => { + ssbClient((err, sbot) => { + if (err) throw err; + pull( + pull.values(JSON.stringify(otherData)), + sbot.blobs.add((err, blobId) => { + sbot.close(); + if (err) reject(err); + else { + resolve({ + type: type, + id: objectId, + otherData: blobId + }); + } + }) + ); + }); + }); + + return await out; +} + +async function get_last_by_object_id(id) { + + /* + * Returns the last Object with the wanted id in the ssb log. + */ + + let last = null; + + let out = new Promise((resolve, reject) => { + ssbClient((err, sbot) => { + pull( + sbot.createLogStream(), + pull.collect(function (err, array) { + sbot.close(); + if (err) reject(err); + for (let i in array) { + if (array[i].value.content.type === 'ap-create' || + array[i].value.content.type === 'ap-update') { + if (array[i].value.content.object.id === id) { + last = array[i]; + } + } + } + if (last) { + resolve(last); + } else { + reject('no object found.'); + } + }), + ); + }); + }); + + return await out; +} + +async function get_last_by_activity_id(id) { + + /* + * Returns the last Activity with the wanted id in the ssb log. + */ + + let last = null; + + let out = new Promise((resolve, reject) => { + ssbClient((err, sbot) => { + pull( + sbot.createLogStream(), + pull.collect(function (err, array) { + sbot.close(); + if (err) reject(err); + for (let i in array) { + if (array[i].value.content.type.length > 3 && + array[i].value.content.type.substr(0,3) === 'ap-' && + allowed_message_types.indexOf(array[i].value.content.type.substr(3)) >= 0) { + if (array[i].value.content.id === id) { + last = array[i]; + } + } + } + if (last){ + resolve(last); + }else { + reject('no activity found'); + } + + }), + ); + }); + }); + + return await out; +} + +function delete_last_by_object_id(id) { + + /* + * Deletes the last Object with the wanted id in the ssb log. + */ + + let p = get_last_by_object_id(id); + + p.then((data) => { + ssbClient((err, sbot) => { + sbot.blobs.rm(data.value.content.object.otherData); + }); + }) +} + +function add_ssb_message(type, id, actor, summary, object, origin = null, target = null) { + + /* + * Takes: + * type: string, + * id: string, + * actor: json object, + * summary: string, + * object: json object, + * origin: json object, + * target: json object + * + * Turns the json object into the appropriate blob-objects, + * acts out any side-effects (for update and delete) and + * adds the message to the ssb log. + */ + + let actor_type = actor.type; + delete actor.type; + let actor_id = actor.id; + delete actor.id; + let actor_promise = createBlobObject(actor_type, actor_id, actor); + + let object_type = object.type; + delete object.type; + let object_id = object.id; + delete object.id; + let object_promise = createBlobObject(object_type, object_id, object); + + Promise.all([actor_promise, object_promise]).then(([actor_blob, object_blob]) => { + ssbClient((err, sbot) => { + if (err) throw err; + + let msg = { + type: 'ap-' + type, + id: id, + notes: summary, + actor: actor_blob, + }; + + if (type === 'delete') { + delete_last_by_object_id(object_id); + } else if (type === 'update') { + delete_last_by_object_id(object_id); + msg.object = object_blob; + } else { + msg.object = object_blob; + } + + if (origin) { + msg.origin = origin; + } + + if (target) { + msg.target = target; + } + + sbot.publish(msg); + sbot.close(); + }); + }) + +} + +async function get_json_from_blob(blob_id){ + + /* + * Gets the Object with the specified id from the + * blob-store and returns it as a restored json object. + */ + + let out = new Promise((resolve, reject) => { + ssbClient((err, sbot) => { + pull( + sbot.blobs.get(blob_id), + pull.collect((err, array) => { + try { + resolve(JSON.parse(decodeURIComponent(escape(array)))); + } catch (e) { + reject(e); + } + }) + ); + sbot.close(); + }) + }); + + return await out; +} + +async function restore_ssb_message(id){ + + /* + * Gets the Message with the specified id from the + * ssb log, restores all contained objects and returns + * it as a json object. + */ + + let activity_promise = get_last_by_activity_id(id); + + let out = new Promise((resolve, reject) => { + activity_promise.then((activity) => { + let msg = activity.value.content; + + let actor_promise = get_json_from_blob(msg.actor.otherData); + let object_promise = get_json_from_blob(msg.object.otherData); + + Promise.all([actor_promise, object_promise]).then(([actor_data, object_data]) => { + msg.type = msg.type.substr(3); + + delete msg.actor.otherData; + for (let key in actor_data){ + msg.actor[key] = actor_data[key]; + } + + delete msg.object.otherData; + for (let key in object_data){ + msg.object[key] = object_data[key]; + } + + resolve(msg); + }) + }); + activity_promise.catch(() => { + reject('id did not work'); + }); + }); + + return await out; +} + +exports.bridge = { + save : (message) => { + if (message.@context === "https://www.w3.org/ns/activitystreams") { + + add_ssb_message( + message.type, + message.id, + message.actor, + message.summary || "", + message.object, + message.origin || null, + message.target || null + ) + + } else { + throw ("Invalid message context."); + } + }, + restore: (id) => { + let p = restore_ssb_message(id); + + return p; + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..d06f622 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "scuttlebutt_to_ap", + "version": "1.0.0", + "dependencies": { + "ssb-client": "latest", + "pull-stream": "latest", + "stream-to-pull-stream": "latest", + "node-fetch": "latest" + } +}