diff --git a/README.md b/README.md index 0869303..a6f5bec 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# spellbinder +# plerama A Matrix(https://matrix.org/docs/spec/) client for the fediverse. diff --git a/commands/boo.js b/commands/boo.js new file mode 100644 index 0000000..0801ac2 --- /dev/null +++ b/commands/boo.js @@ -0,0 +1,18 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/statuses/${userInput}/unfavourite`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `You have boo'd: ${response.data.account.acct} +
${response.data.content}`); + }) + .catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/clap.js b/commands/clap.js new file mode 100644 index 0000000..ea47753 --- /dev/null +++ b/commands/clap.js @@ -0,0 +1,18 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/statuses/${userInput}/favourite`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `You have clapped: ${response.data.account.acct}: +
${response.data.content}`); + }) + .catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/copy.js b/commands/copy.js new file mode 100644 index 0000000..1768cb6 --- /dev/null +++ b/commands/copy.js @@ -0,0 +1,18 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/statuses/${userInput}/reblog`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `You have repeated: +
${response.data.content}`); + }) + .catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/flood.js b/commands/flood.js new file mode 100644 index 0000000..3e81ad2 --- /dev/null +++ b/commands/flood.js @@ -0,0 +1,35 @@ +const axios = require('axios'); +const fs = require('fs'); + +exports.runQuery = function (matrixClient, room, registrar) { + setInterval(() => { + axios({ + method: 'GET', + url: `${registrar.config.fediverse}/api/v1/timelines/home`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((events) => { + const event = fs.readFileSync('timeline.json', 'utf8'); + fs.writeFileSync('timeline.json', events.data[0].created_at, 'utf8'); + if (event !== events.data[0].created_at) { + if (events.data[0].reblog === null) { + matrixClient.sendHtmlNotice(room.roomId, + '', + `${events.data[0].account.acct} +
${events.data[0].content}
+ (id: ${events.data[0].id}) +

`); + } else { + matrixClient.sendHtmlNotice(room.roomId, + '', + ` + ${events.data[0].account.acct} + has repeated: +
${events.data[0].reblog.account.acct}
+
${events.data[0].content}
+ (id: ${events.data[0].id}) +

`); + } + } + }); + }, 8000); +}; diff --git a/commands/fren.js b/commands/fren.js new file mode 100644 index 0000000..9e3faae --- /dev/null +++ b/commands/fren.js @@ -0,0 +1,20 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios.get(`${registrar.config.fediverse}/api/v1/accounts/${userInput}`).then((findUID) => { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/accounts/${findUID.data.id}/follow`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }) + .then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `Subscribed: +
${registrar.config.fediverse}}/${response.data.id}`); + }); + }).catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/help.js b/commands/help.js new file mode 100644 index 0000000..6339c58 --- /dev/null +++ b/commands/help.js @@ -0,0 +1,15 @@ +exports.runQuery = function (matrixClient, room) { + matrixClient.sendHtmlNotice(room.roomId, + '', + '
+plemara [your message] : post
' + + '+fren [user id] : follow
' + + '+unfren [user id] : unfollow
' + + '+copy [post id] : repeat/repost/retweet
' + + '+reply [post id] : reply to post
' + + '+clap [post id] : favorite
' + + '+boo [post id] : unfavorite
' + + '
channel commands
' + + '+flood : turn on timeline
' + + '+notify : show notifications
' + + '
--- docs by lint ---
'); +}; diff --git a/commands/notify.js b/commands/notify.js new file mode 100644 index 0000000..6667a0c --- /dev/null +++ b/commands/notify.js @@ -0,0 +1,51 @@ +const axios = require('axios'); +const fs = require('fs'); + +exports.runQuery = function (matrixClient, room, registrar) { + setInterval(() => { + axios({ + method: 'GET', + url: `${registrar.config.fediverse}/api/v1/notifications`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((notifications) => { + const event = fs.readFileSync('notification.json', 'utf8'); + fs.writeFileSync('notification.json', notifications.data[0].created_at, 'utf8'); + + if (event !== notifications.data[0].created_at) { + if (notifications.data[0].type === 'follow') { + matrixClient.sendHtmlNotice(room.roomId, + '', + `
+ ${notifications.data[0].account.acct} + has followed you. +
${notifications.data[0].account.note}
`); + } else if (notifications.data[0].type === 'favourite') { + matrixClient.sendHtmlNotice(room.roomId, + '', + `
+ ${notifications.data[0].account.acct} + has favorited + your post: +
${notifications.data[0].status.content}
`); + } else if (notifications.data[0].type === 'mention') { + matrixClient.sendHtmlNotice(room.roomId, + '', + `
+ ${notifications.data[0].account.acct} + has mentioned + you:
+ ${notifications.data[0].status.content} +
[id: ${notifications.data[0].status.id}]

`); + } else if (notifications.data[0].type === 'reblog') { + matrixClient.sendHtmlNotice(room.roomId, + '', + `
+ ${notifications.data[0].account.acct} + has repeated + your post:
+ ${notifications.data[0].status.content}
`); + } + } + }); + }, 8000); +}; diff --git a/commands/plemara.js b/commands/plemara.js new file mode 100644 index 0000000..85719a8 --- /dev/null +++ b/commands/plemara.js @@ -0,0 +1,21 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/statuses`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + data: { status: userInput }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + ` +
${response.data.content}
+ (id: ${response.data.id}) +

`); + }) + .catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/redact.js b/commands/redact.js new file mode 100644 index 0000000..52f6b35 --- /dev/null +++ b/commands/redact.js @@ -0,0 +1,17 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios({ + method: 'DELETE', + url: `${registrar.config.fediverse}/api/v1/statuses/${userInput}`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + '
Redacted. { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/reply.js b/commands/reply.js new file mode 100644 index 0000000..5c66849 --- /dev/null +++ b/commands/reply.js @@ -0,0 +1,18 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, address, flaggedInput, registrar) { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/statuses`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + data: { status: flaggedInput, in_reply_to_id: address }, + }).then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `${response.data.content} ${response.data.url}`); + }) + .catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/commands/unfren.js b/commands/unfren.js new file mode 100644 index 0000000..c18a671 --- /dev/null +++ b/commands/unfren.js @@ -0,0 +1,20 @@ +const axios = require('axios'); + +exports.runQuery = function (matrixClient, room, userInput, registrar) { + axios.get(`${registrar.config.fediverse}/api/v1/accounts/${userInput}`).then((findUID) => { + axios({ + method: 'POST', + url: `${registrar.config.fediverse}/api/v1/accounts/${findUID.data.id}/unfollow`, + headers: { Authorization: `Bearer ${registrar.config.fediverseToken}` }, + }) + .then((response) => { + matrixClient.sendHtmlNotice(room.roomId, + '', + `Unsubscribed: +
${registrar.config.fediverse}/${response.data.id}`); + }); + }).catch((e) => { + matrixClient.sendHtmlNotice(room.roomId, + '', `${e}`); + }); +}; diff --git a/config.js b/config.js new file mode 100644 index 0000000..e6cff93 --- /dev/null +++ b/config.js @@ -0,0 +1,8 @@ +module.exports = { + matrixServer: 'https://server.com', + userId: '@matrixUser:server.com', + matrixUser: 'hello', + matrixPass: 'password', + fediverse: 'https://server.com', + fediverseToken: 'access_token', +}; diff --git a/main.js b/main.js new file mode 100644 index 0000000..e143cf9 --- /dev/null +++ b/main.js @@ -0,0 +1,95 @@ +const sdk = require('matrix-js-sdk'); +const axios = require('axios'); +const registrar = require('./registrar.js'); + +const homeServer = 'https://civseed.com/_matrix/client/r0/login'; +const auth = { + type: 'm.login.password', + user: registrar.config.matrixUser, + password: registrar.config.matrixPass, +}; + +axios.post(homeServer, auth).then((response) => { + CreateClient(response.data.access_token); +}).catch((e) => { + console.log(e); +}); + +let CreateClient = (token) => { + const matrixClient = sdk.createClient({ + baseUrl: registrar.config.matrixServer, + accessToken: token, + userId: registrar.config.userId, + timelineSupport: true, + }); + + matrixClient.on('RoomMember.membership', (event, member) => { + if (member.membership === 'invite' && member.userId === registrar.config.userId) { + matrixClient.joinRoom(member.roomId).done(() => { + console.log('Auto-joined %s', member.roomId); + }); + } + }); + + matrixClient.on('Room.timeline', (event, room, toStartOfTimeline) => { + if (toStartOfTimeline) return; + if (event.getType() !== 'm.room.message') return; + if (event.getSender() === registrar.config.userId) return; + if (event.event.unsigned.age > 10000) return; + if (event.event.content.body.charAt(0) === '+') { + console.log(`Logs: ${event.event.sender} - ${event.event.content.body}`); + const args = event.event.content.body.slice(1).trim().split(/ +/g); + const command = args.shift().toLowerCase(); + const userInput = args.join(' '); + const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1); + const address = args.slice(0, 1).join(' ').replace(/"/g, ''); + + if (command === 'boo') { + registrar.boo.runQuery(matrixClient, room, userInput, registrar); + } + + if (command === 'clap') { + registrar.clap.runQuery(matrixClient, room, userInput, registrar); + } + + if (command === 'copy') { + registrar.copy.runQuery(matrixClient, room, userInput, registrar); + } + + if (command === 'flood') { + registrar.flood.runQuery(matrixClient, room, registrar); + } + + if (command === 'fren') { + registrar.fren.runQuery(matrixClient, room, registrar); + } + + if (command === 'help') { + registrar.help.runQuery(matrixClient, room); + } + + if (command === 'plemara') { + registrar.plemara.runQuery(matrixClient, room, userInput, registrar); + } + + if (command === 'notify') { + registrar.notify.runQuery(matrixClient, room, registrar); + } + + if (command === 'redact') { + registrar.redact.runQuery(matrixClient, room, userInput, registrar); + } + + if (command === 'reply') { + registrar.reply.runQuery(matrixClient, room, address, flaggedInput, registrar); + } + + if (command === 'unfren') { + registrar.unfren.runQuery(matrixClient, room, userInput, registrar); + } + } + }); + + matrixClient.startClient(); + module.exports = matrixClient; +}; diff --git a/notification.json b/notification.json new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..d33ec32 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "spellbinder", + "version": "0.1.0", + "description": "A Matrix to Fediverse client", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "run": "node main.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vulet/spellbinder.git" + }, + "author": "vul", + "license": "AGPL-3.0-only", + "bugs": { + "url": "https://github.com/vulet/spellbinder/issues" + }, + "homepage": "https://github.com/vulet/spellbinder#readme", + "dependencies": { + "axios": "^0.19.0", + "file-system": "^2.2.2", + "matrix-js-sdk": "^2.0.1-rc.1" + }, + "devDependencies": { + "eslint": "^5.16.0", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-plugin-import": "^2.17.3" + } +} diff --git a/registrar.js b/registrar.js new file mode 100644 index 0000000..87c845b --- /dev/null +++ b/registrar.js @@ -0,0 +1,14 @@ +module.exports = { + config: require('./config.js'), + boo: require('./commands/boo.js'), + clap: require('./commands/clap.js'), + copy: require('./commands/copy.js'), + flood: require('./commands/flood.js'), + fren: require('./commands/fren.js'), + help: require('./commands/help.js'), + plemara: require('./commands/plemara.js'), + redact: require('./commands/redact.js'), + notify: require('./commands/notify.js'), + reply: require('./commands/reply.js'), + unfren: require('./commands/unfren.js'), +}; diff --git a/timeline.json b/timeline.json new file mode 100644 index 0000000..e69de29