Introduce meta field (1.2.0)
This introduces a `content.meta` field to our Fediverse related `m.room.message` events on Matrix. The field is attached to events that are related to notifications, and timeline. The data includes: `['status', 'reblog', 'mention', 'redact', 'unreblog', 'account']` then followed by a notice ID or an account ID. The meta field is listened for, and once encountered, a self-reaction occurs with the related available commands. After the self-reaction, we then listen for a second reaction or (`m.annotation`), and if given, act on the command specified. The commands which are currently supported include: favorite (👏), reblog (🔃), and redact (🗑). A reply is also listened for, or `m.in_reply_to`. If a reply is encountered on an event with meta, then a reply is carried out splitting at [`</mx-reply>`](https://matrix.org/docs/spec/client_server/r0.6.1#m-room-message-msgtypes) , with reaction handling done at MSC2677.
This commit is contained in:
commit
58fe0c19d2
28 changed files with 619 additions and 482 deletions
51
auth.js
51
auth.js
|
@ -1,46 +1,45 @@
|
||||||
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||||
|
|
||||||
module.exports.getMatrixToken = async () => {
|
const matrixTokenLogin = async () => {
|
||||||
matrixClient = sdk.createClient(config.matrix.domain);
|
|
||||||
matrixClient.loginWithPassword(config.matrix.user, config.matrix.password)
|
|
||||||
.then((response) => {
|
|
||||||
matrix_auth = {
|
|
||||||
user_id: response.user_id,
|
|
||||||
access_token: response.access_token,
|
|
||||||
device_id: response.device_id,
|
|
||||||
};
|
|
||||||
localStorage.setItem('matrix_auth', JSON.stringify(response, null, 2));
|
|
||||||
}).then(() => {
|
|
||||||
matrixTokenLogin();
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.log(e);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
matrixTokenLogin = async () => {
|
|
||||||
matrixClient = sdk.createClient({
|
matrixClient = sdk.createClient({
|
||||||
baseUrl: config.matrix.domain,
|
baseUrl: config.matrix.domain,
|
||||||
accessToken: matrix_auth.access_token,
|
accessToken: matrix.auth.access_token,
|
||||||
userId: matrix_auth.user_id,
|
userId: matrix.auth.user_id,
|
||||||
deviceId: matrix_auth.device_id,
|
deviceId: matrix.auth.device_id,
|
||||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
||||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||||
});
|
});
|
||||||
matrixClient.initCrypto()
|
matrixClient.initCrypto()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if(!localStorage.getItem('crypto.device_data'))
|
if (!localStorage.getItem('crypto.device_data')) {
|
||||||
return console.log(
|
return console.log(
|
||||||
'====================================================\n'+
|
'====================================================\n'
|
||||||
'New OLM Encryption Keys created, please restart ligh7hau5.\n'+
|
+ 'New OLM Encryption Keys created, please restart ligh7hau5.\n'
|
||||||
'===================================================='
|
+ '====================================================',
|
||||||
);
|
);
|
||||||
|
}
|
||||||
matrixClient.startClient();
|
matrixClient.startClient();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.matrixTokenLogin = matrixTokenLogin;
|
module.exports.matrixTokenLogin = matrixTokenLogin;
|
||||||
|
|
||||||
|
module.exports.getMatrixToken = async () => {
|
||||||
|
matrixClient = sdk.createClient(config.matrix.domain);
|
||||||
|
matrixClient.loginWithPassword(config.matrix.user, config.matrix.password)
|
||||||
|
.then((response) => {
|
||||||
|
matrix.auth = {
|
||||||
|
user_id: response.user_id,
|
||||||
|
access_token: response.access_token,
|
||||||
|
device_id: response.device_id,
|
||||||
|
};
|
||||||
|
localStorage.setItem('matrix_auth', JSON.stringify(response, null, 2));
|
||||||
|
}).then(() => matrixTokenLogin())
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.registerFediverseApp = async () => {
|
module.exports.registerFediverseApp = async () => {
|
||||||
axios.post(`${config.fediverse.domain}/api/v1/apps`,
|
axios.post(`${config.fediverse.domain}/api/v1/apps`,
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,23 +3,6 @@ const qs = require('qs');
|
||||||
|
|
||||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||||
|
|
||||||
const editNoticeHTML = (client, roomId, event, html, plain) => client.sendMessage(roomId, {
|
|
||||||
body: ` * ${plain || html.replace(/<[^<]+?>/g, '')}`,
|
|
||||||
formatted_body: ` * ${html}`,
|
|
||||||
format: 'org.matrix.custom.html',
|
|
||||||
msgtype: 'm.notice',
|
|
||||||
'm.new_content': {
|
|
||||||
body: plain || html.replace(/<[^<]+?>/g, ''),
|
|
||||||
formatted_body: html,
|
|
||||||
format: 'org.matrix.custom.html',
|
|
||||||
msgtype: 'm.notice'
|
|
||||||
},
|
|
||||||
'm.relates_to': {
|
|
||||||
rel_type: 'm.replace',
|
|
||||||
event_id: event.event_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const headers = ({ domain, userAgent }) => ({
|
const headers = ({ domain, userAgent }) => ({
|
||||||
'Host': `${domain}`,
|
'Host': `${domain}`,
|
||||||
'User-Agent': `${userAgent}`
|
'User-Agent': `${userAgent}`
|
||||||
|
@ -49,7 +32,7 @@ const arc1Str = str => `<em>Archiving page <code>${str}</code></em>`;
|
||||||
const arc2Str = (str, title, date) => `<em>Archived page <code><a href="https://${str}">${str}</code> [${date}]</em><br /><b>${title}</b>`;
|
const arc2Str = (str, title, date) => `<em>Archived page <code><a href="https://${str}">${str}</code> [${date}]</em><br /><b>${title}</b>`;
|
||||||
const arc3Str = str => `<em>Timed out <code>${str}</code></em>`;
|
const arc3Str = str => `<em>Timed out <code>${str}</code></em>`;
|
||||||
|
|
||||||
const run = async (matrixClient, { roomId }, userInput, rearchive) => {
|
const run = async (roomId, userInput, rearchive) => {
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: `https://${config.archive.domain}`,
|
baseURL: `https://${config.archive.domain}`,
|
||||||
headers: headers(config.archive),
|
headers: headers(config.archive),
|
||||||
|
@ -62,29 +45,29 @@ const run = async (matrixClient, { roomId }, userInput, rearchive) => {
|
||||||
reply = await matrixClient.sendHtmlNotice(roomId, '', reqStr(userInput));
|
reply = await matrixClient.sendHtmlNotice(roomId, '', reqStr(userInput));
|
||||||
const { refresh, id, title, date } = await archive(instance, userInput, rearchive);
|
const { refresh, id, title, date } = await archive(instance, userInput, rearchive);
|
||||||
if (id)
|
if (id)
|
||||||
return await editNoticeHTML(matrixClient, roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
const path = refresh.split(`https://${config.archive.domain}`);
|
const path = refresh.split(`https://${config.archive.domain}`);
|
||||||
if (!path[1]) throw refresh;
|
if (!path[1]) throw refresh;
|
||||||
await editNoticeHTML(matrixClient, roomId, reply, arc1Str(refresh));
|
await matrix.utils.editNoticeHTML(roomId, reply, arc1Str(refresh));
|
||||||
let tries = 30;
|
let tries = 30;
|
||||||
while (tries--) {
|
while (tries--) {
|
||||||
await sleep(10000);
|
await sleep(10000);
|
||||||
const { title, date, id } = await archive(instance, userInput);
|
const { title, date, id } = await archive(instance, userInput);
|
||||||
if (rearchive == false && title !== undefined)
|
if (rearchive == false && title !== undefined)
|
||||||
return await editNoticeHTML(matrixClient, roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
||||||
const { request: { path: reqPath }, headers: { 'memento-datetime': rearchiveDate } } = await instance({ method: 'HEAD', url: path[1] })
|
const { request: { path: reqPath }, headers: { 'memento-datetime': rearchiveDate } } = await instance({ method: 'HEAD', url: path[1] })
|
||||||
.catch(e => ({ request: { path: path[1] } }));
|
.catch(e => ({ request: { path: path[1] } }));
|
||||||
if (rearchive == true && reqPath !== path[1])
|
if (rearchive == true && reqPath !== path[1])
|
||||||
return await editNoticeHTML(matrixClient, roomId, reply, arc2Str(`${config.archive.domain}${reqPath}`, title, rearchiveDate));
|
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${reqPath}`, title, rearchiveDate));
|
||||||
}
|
}
|
||||||
return await editNoticeHTML(matrixClient, roomId, reply, arc3Str(refresh));
|
return await matrix.utils.editNoticeHTML(roomId, reply, arc3Str(refresh));
|
||||||
}
|
}
|
||||||
throw 'sad';
|
throw 'sad';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const sad = `<strong>Sad!</strong><br /><code>${`${e}`.replace(/<[^<]+?>/g, '').substr(0, 100)}</code>`;
|
const sad = `<strong>Sad!</strong><br /><code>${`${e}`.replace(/<[^<]+?>/g, '').substr(0, 100)}</code>`;
|
||||||
if (reply)
|
if (reply)
|
||||||
editNoticeHTML(matrixClient, roomId, reply, sad, 'sad').catch(() => {});
|
matrix.utils.editNoticeHTML(roomId, reply, sad, 'sad').catch(() => {});
|
||||||
else
|
else
|
||||||
matrixClient.sendHtmlNotice(roomId, 'sad', sad).catch(() => {});
|
matrixClient.sendHtmlNotice(roomId, 'sad', sad).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
exports.runQuery = function (matrixClient, room) {
|
exports.runQuery = function (roomId, event) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
data: { status: `@10grans@fedi.cc beg` },
|
data: { status: '@10grans@fedi.cc beg' },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`<b>
|
|
||||||
<blockquote><i>You have begged for 10grans.<br>
|
|
||||||
(id: ${response.data.id}</a>)
|
|
||||||
</blockquote><br>`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`You have boo'd: <a href="${response.data.url}">${response.data.account.acct}</a>
|
|
||||||
<blockquote>${response.data.content}`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`You have clapped: <a href="${response.data.url}">${response.data.account.acct}</a>:
|
|
||||||
<blockquote>${response.data.content}`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`You have repeated:
|
|
||||||
<blockquote>${response.data.content}`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,39 +1,26 @@
|
||||||
exports.runQuery = function (matrixClient, room) {
|
exports.runQuery = function (roomId) {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/timelines/home`,
|
url: `${config.fediverse.domain}/api/v1/timelines/home`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((events) => {
|
})
|
||||||
let lastEvent = JSON.parse(localStorage.getItem('timeline'));
|
.then((res) => {
|
||||||
localStorage.setItem('timeline', JSON.stringify(events.data[0].created_at, null, 2));
|
let past = JSON.parse(localStorage.getItem('timeline'));
|
||||||
|
if (past.length === 0) past = res.data;
|
||||||
if (lastEvent !== events.data[0].created_at) {
|
const events = res.data;
|
||||||
if (events.data[0].reblog === null) {
|
const len = events.length;
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
'',
|
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||||
`<b><a href="${config.fediverse.domain}/notice/${events.data[0].id}">${events.data[0].account.acct}</a>
|
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
||||||
<blockquote><i>${events.data[0].content}</i><br>
|
events[i].label = 'status';
|
||||||
${events.data[0].media_attachments.map(media =>
|
fediverse.utils.formatter(events[i], roomId);
|
||||||
`<a href="${media.remote_url}">`+`${media.description}`+'</a>'
|
|
||||||
).join('<br>')}
|
|
||||||
(id: ${events.data[0].id}) ${registrar.media.visibilityEmoji(events.data[0].visibility)}
|
|
||||||
</blockquote>`);
|
|
||||||
} else {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'',
|
|
||||||
`<b><a href="${config.fediverse.domain}/${events.data[0].account.id}">
|
|
||||||
${events.data[0].account.acct}</a>
|
|
||||||
<font color="#7886D7">has <a href="${config.fediverse.domain}/notice/${events.data[0].id}">repeated</a>:
|
|
||||||
<blockquote><a href="${events.data[0].reblog.account.url}">${events.data[0].reblog.account.acct}</a></blockquote>
|
|
||||||
<blockquote>${events.data[0].content}<br>
|
|
||||||
${events.data[0].media_attachments.map(media =>
|
|
||||||
`<a href="${media.remote_url}">`+`Proxied image, no description available.`+'</a>'
|
|
||||||
).join('<br>')}
|
|
||||||
<br>(id: ${events.data[0].id}) ${registrar.media.visibilityEmoji(events.data[0].visibility)}
|
|
||||||
</blockquote>`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
localStorage.setItem('timeline', JSON.stringify(events, null, 2));
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
matrix.utils.sendError(null, roomId, e);
|
||||||
});
|
});
|
||||||
}, 8000);
|
}, 30000);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
const axios = require('axios');
|
exports.runQuery = async function (roomId, event, userInput) {
|
||||||
const fediverse_auth = JSON.parse(localStorage.getItem('fediverse_auth'));
|
const loadingString = `Searching for ${userInput}...`;
|
||||||
|
const original = await matrixClient.sendHtmlNotice(roomId, `${loadingString}`, `<code>${loadingString}</code>`);
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
const found = [];
|
||||||
axios.get(`${config.fediverse.domain}/api/v1/accounts/${userInput}`).then((findUID) => {
|
const suggest = [];
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/follow`,
|
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
})
|
}).then((findUserId) => {
|
||||||
.then((response) => {
|
const results = findUserId.data.accounts;
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
const len = results.length;
|
||||||
'',
|
for (let i = 0; i < len; i++) results[i].acct !== userInput ? suggest.push(results[i].acct) : found.push(results[i]);
|
||||||
`Subscribed:
|
if (found.length > 0) return fediverse.utils.follow(roomId, found, event, original);
|
||||||
<blockquote>${config.fediverse.domain}/${response.data.id}`);
|
if (suggest.length > 0) msg = `<code>${userInput} was not found, suggesting:</code><blockquote>${suggest.join('<br>')}</blockquote>`;
|
||||||
});
|
if (suggest.length === 0) msg = `<code>No results found for: ${userInput}.</code>`;
|
||||||
}).catch((e) => {
|
return matrix.utils.editNoticeHTML(roomId, original, msg);
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'', `${e}`);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
const qs = require('qs');
|
|
||||||
const FormData = require('form-data');
|
|
||||||
|
|
||||||
const emojis = { public: '🌐', unlisted: '📝', private: '🔒️', direct: '✉️' };
|
|
||||||
exports.visibilityEmoji = v => emojis[v] || v;
|
|
||||||
|
|
||||||
const getFilename = header => {
|
|
||||||
if(typeof header !== 'string') return null;
|
|
||||||
try {
|
|
||||||
const m = header.match(/inline; filename(?:=(.+)|\*=utf-8''(.+))/);
|
|
||||||
return !m ? null : m[2] && decodeURIComponent(m[2]) || m[1];
|
|
||||||
} catch(e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const mediaDownload = async (url, { whitelist, blacklist }) => {
|
|
||||||
const media = await axios({ method: 'GET', url, responseType: 'arraybuffer' });
|
|
||||||
if (media.statusText !== 'OK' || blacklist.includes(media.headers['content-type'])) throw media;
|
|
||||||
if(whitelist.length && !whitelist.includes(media.headers['content-type'])) throw media;
|
|
||||||
return {
|
|
||||||
data: media.data,
|
|
||||||
filename: getFilename(media.headers['content-disposition']),
|
|
||||||
mimetype: media.headers['content-type']
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
|
||||||
const form = new FormData();
|
|
||||||
form.append('file', data, {
|
|
||||||
filename: filename || 'upload',
|
|
||||||
contentType: mimetype,
|
|
||||||
});
|
|
||||||
const upload = await axios({
|
|
||||||
method: 'POST',
|
|
||||||
url: `${domain}/api/v1/media`,
|
|
||||||
headers: form.getHeaders({ Authorization: `Bearer ${fediverse_auth.access_token}` }),
|
|
||||||
data: form,
|
|
||||||
});
|
|
||||||
if(upload.statusText !== 'OK') throw upload;
|
|
||||||
return upload.data.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const run = async (matrixClient, { roomId }, content, replyId, mediaURL, subject) => {
|
|
||||||
let mediaId = null;
|
|
||||||
const fediverse = config.fediverse;
|
|
||||||
if(mediaURL) {
|
|
||||||
const media = await mediaDownload(mediaURL, fediverse.mimetypes);
|
|
||||||
mediaId = await mediaUpload(fediverse, media);
|
|
||||||
}
|
|
||||||
const response = await axios({
|
|
||||||
method: 'POST',
|
|
||||||
url: `${fediverse.domain}/api/v1/statuses`,
|
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
||||||
data : qs.stringify({
|
|
||||||
status: content,
|
|
||||||
content_type: `text/markdown`,
|
|
||||||
media_ids: mediaURL && [ mediaId ] || undefined,
|
|
||||||
in_reply_to_id: replyId || undefined,
|
|
||||||
spoiler_text: subject || undefined
|
|
||||||
}, { arrayFormat: 'brackets' })
|
|
||||||
});
|
|
||||||
return matrixClient.sendHtmlNotice(roomId, '', `<a href="${response.data.url}">${response.data.id}</a>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.runQuery = async (client, room, userInput, { isReply, hasMedia, hasSubject }) => {
|
|
||||||
try {
|
|
||||||
const chunks = userInput.trim().split(' ');
|
|
||||||
if(!chunks.length || chunks.length < !!isReply + !!hasMedia) throw '';
|
|
||||||
let replyId = null;
|
|
||||||
let mediaURL = null;
|
|
||||||
const subject = hasSubject ? fediverse.subject : null;
|
|
||||||
if(isReply) {
|
|
||||||
replyId = chunks[0];
|
|
||||||
chunks.shift();
|
|
||||||
}
|
|
||||||
if(hasMedia) {
|
|
||||||
let url = new URL(chunks[0]);
|
|
||||||
chunks.shift();
|
|
||||||
if(url.protocol === 'mxc:' && url.hostname && url.pathname)
|
|
||||||
url = new URL(`${config.matrix.domain}/_matrix/media/r0/download/${url.hostname}${url.pathname}`);
|
|
||||||
if(url.protocol !== 'https:') throw '';
|
|
||||||
if(!config.matrix.domains.includes(url.hostname)) throw '';
|
|
||||||
if(!/^\/_matrix\/media\/r0\/download\/[^/]+\/[^/]+\/?$/.test(url.pathname)) throw '';
|
|
||||||
mediaURL = url.toString();
|
|
||||||
}
|
|
||||||
return await run(client, room, chunks.join(' '), replyId, mediaURL, subject);
|
|
||||||
} catch(e) {
|
|
||||||
return client.sendHtmlNotice(room.roomId, 'Sad!', `<strong>Sad!</strong>`).catch(()=>{});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,24 +1,20 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
data: {
|
data: {
|
||||||
status: `@mordekai ${userInput}`,
|
status: `@mordekai ${userInput}`,
|
||||||
content_type: `text/markdown`,
|
content_type: 'text/markdown',
|
||||||
visibility: 'unlisted',
|
visibility: 'unlisted',
|
||||||
expires_in: '7200'
|
expires_in: '7200',
|
||||||
},
|
},
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`<b>
|
|
||||||
<blockquote><i>${response.data.content}<br>
|
|
||||||
(id: ${response.data.id}</a>)
|
|
||||||
</blockquote><br>`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,47 +1,26 @@
|
||||||
exports.runQuery = function (matrixClient, room) {
|
exports.runQuery = function (roomId) {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/notifications`,
|
url: `${config.fediverse.domain}/api/v1/notifications`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((events) => {
|
})
|
||||||
let lastEvent = JSON.parse(localStorage.getItem('notifications'));
|
.then((res) => {
|
||||||
localStorage.setItem('notifications', JSON.stringify(events.data[0].created_at, null, 2));
|
let past = JSON.parse(localStorage.getItem('notifications'));
|
||||||
if (lastEvent !== events.data[0].created_at) {
|
if (past.length === 0) past = res.data;
|
||||||
if (events.data[0].type === 'follow') {
|
const events = res.data;
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
const len = events.length;
|
||||||
'',
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
`<b><a href="${config.fediverse.domain}/${events.data[0].account.id}">
|
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||||
${events.data[0].account.acct}</a></b>
|
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
||||||
<font color="#03b381"><b>has followed you.</b></font>
|
events[i].label = 'notifications';
|
||||||
<br><i>${events.data[0].account.note}</i>`);
|
fediverse.utils.formatter(events[i], roomId);
|
||||||
} else if (events.data[0].type === 'favourite') {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'',
|
|
||||||
`<b><a href="${config.fediverse.domain}/${events.data[0].account.id}">
|
|
||||||
${events.data[0].account.acct}</a></b>
|
|
||||||
<font color="#03b381"><b>has <a href="${events.data[0].status.uri}">favorited</a>
|
|
||||||
your post:</b></font>
|
|
||||||
<br><blockquote><i><b>${events.data[0].status.content}</i></b></blockquote>`);
|
|
||||||
} else if (events.data[0].type === 'mention') {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'',
|
|
||||||
`<b><a href="${config.fediverse.domain}/${events.data[0].account.id}">
|
|
||||||
${events.data[0].account.acct}</a></b>
|
|
||||||
<font color="#03b381"><b>has <a href="${events.data[0].status.uri}">mentioned</a>
|
|
||||||
you:</b></font><br><blockquote><i><b>${events.data[0].status.content}
|
|
||||||
<br>(id: ${events.data[0].status.id}) ${registrar.media.visibilityEmoji(events.data[0].status.visibility)}</i></b>
|
|
||||||
</blockquote>`);
|
|
||||||
} else if (events.data[0].type === 'reblog') {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'',
|
|
||||||
`<b><a href="${config.fediverse.domain}/${events.data[0].account.id}">
|
|
||||||
${events.data[0].account.acct}</a></b>
|
|
||||||
<font color="#03b381"><b>has <a href="${events.data[0].status.uri}">repeated</a>
|
|
||||||
your post:</b></font><br>
|
|
||||||
<blockquote><i><b>${events.data[0].status.content}</i></b></blockquote>`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
localStorage.setItem('notifications', JSON.stringify(events, null, 2));
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
matrix.utils.sendError(null, roomId, e);
|
||||||
});
|
});
|
||||||
}, 8000);
|
}, 30000);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/pin`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/pin`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`Pinned:
|
|
||||||
<blockquote><i><a href="${config.fediverse.domain}/notice/${response.data.id}">
|
|
||||||
${response.data.content}</a></i>
|
|
||||||
</blockquote>`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,19 +1,90 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
const qs = require('qs');
|
||||||
axios({
|
const FormData = require('form-data');
|
||||||
|
|
||||||
|
const emojis = { public: '🌐', unlisted: '📝', private: '🔒️', direct: '✉️' };
|
||||||
|
exports.visibilityEmoji = (v) => emojis[v] || v;
|
||||||
|
|
||||||
|
const getFilename = (header) => {
|
||||||
|
if (typeof header !== 'string') return null;
|
||||||
|
try {
|
||||||
|
const m = header.match(/inline; filename(?:=(.+)|\*=utf-8''(.+))/);
|
||||||
|
return !m ? null : m[2] && decodeURIComponent(m[2]) || m[1];
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mediaDownload = async (url, { whitelist, blacklist }) => {
|
||||||
|
const media = await axios({ method: 'GET', url, responseType: 'arraybuffer' });
|
||||||
|
if (media.statusText !== 'OK' || blacklist.includes(media.headers['content-type'])) throw media;
|
||||||
|
if (whitelist.length && !whitelist.includes(media.headers['content-type'])) throw media;
|
||||||
|
return {
|
||||||
|
data: media.data,
|
||||||
|
filename: getFilename(media.headers['content-disposition']),
|
||||||
|
mimetype: media.headers['content-type'],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', data, {
|
||||||
|
filename: filename || 'upload',
|
||||||
|
contentType: mimetype,
|
||||||
|
});
|
||||||
|
const upload = await axios({
|
||||||
|
method: 'POST',
|
||||||
|
url: `${domain}/api/v1/media`,
|
||||||
|
headers: form.getHeaders({ Authorization: `Bearer ${fediverse.auth.access_token}` }),
|
||||||
|
data: form,
|
||||||
|
});
|
||||||
|
if (upload.statusText !== 'OK') throw upload;
|
||||||
|
return upload.data.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const run = async (roomId, content, replyId, mediaURL, subject) => {
|
||||||
|
let mediaId = null;
|
||||||
|
if (mediaURL) {
|
||||||
|
const media = await mediaDownload(mediaURL, config.fediverse.mimetypes);
|
||||||
|
mediaId = await mediaUpload(config.fediverse, media);
|
||||||
|
}
|
||||||
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
data: { status: userInput, content_type: `text/markdown` },
|
data: qs.stringify({
|
||||||
}).then((response) => {
|
status: content,
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
content_type: 'text/markdown',
|
||||||
'',
|
media_ids: mediaURL && [mediaId] || undefined,
|
||||||
`<b>
|
in_reply_to_id: replyId || undefined,
|
||||||
<blockquote><i>${response.data.content}<br>
|
spoiler_text: subject || undefined,
|
||||||
(id: ${response.data.id}</a>)
|
}, { arrayFormat: 'brackets' }),
|
||||||
</blockquote><br>`);
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'', `${e}`);
|
|
||||||
});
|
});
|
||||||
|
return fediverse.utils.sendEventWithMeta(roomId, `<a href="${response.data.url}">${response.data.id}</a>`, `redact ${response.data.id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.runQuery = async (roomId, userInput, { isReply, hasMedia, hasSubject }) => {
|
||||||
|
try {
|
||||||
|
const chunks = userInput.trim().split(' ');
|
||||||
|
if (!chunks.length || chunks.length < !!isReply + !!hasMedia) throw '';
|
||||||
|
let replyId = null;
|
||||||
|
let mediaURL = null;
|
||||||
|
const subject = hasSubject ? config.fediverse.subject : null;
|
||||||
|
if (isReply) {
|
||||||
|
replyId = chunks[0];
|
||||||
|
chunks.shift();
|
||||||
|
}
|
||||||
|
if (hasMedia) {
|
||||||
|
let url = new URL(chunks[0]);
|
||||||
|
chunks.shift();
|
||||||
|
if (url.protocol === 'mxc:' && url.hostname && url.pathname)
|
||||||
|
url = new URL(`${config.matrix.domain}/_matrix/media/r0/download/${url.hostname}${url.pathname}`);
|
||||||
|
if (url.protocol !== 'https:') throw '';
|
||||||
|
if (!config.matrix.domains.includes(url.hostname)) throw '';
|
||||||
|
if (!/^\/_matrix\/media\/r0\/download\/[^/]+\/[^/]+\/?$/.test(url.pathname)) throw '';
|
||||||
|
mediaURL = url.toString();
|
||||||
|
}
|
||||||
|
return await run(roomId, chunks.join(' '), replyId, mediaURL, subject);
|
||||||
|
} catch (e) {
|
||||||
|
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
'<blockquote>Redacted.</blockquote');
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
exports.runQuery = function (matrixClient, room, address, flaggedInput) {
|
|
||||||
axios({
|
|
||||||
method: 'POST',
|
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
|
||||||
data: { status: flaggedInput, in_reply_to_id: address, content_type: `text/markdown` },
|
|
||||||
}).then((response) => {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'',
|
|
||||||
`${response.data.content} ${response.data.url}`);
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'', `${e}`);
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,17 +1,15 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput, ) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then((response) => {
|
||||||
'',
|
response.label = 'status';
|
||||||
`<b><a href="${config.fediverse.domain}/notice/${response.data.id}">${response.data.account.acct}</a>
|
fediverse.utils.formatter(response, roomId);
|
||||||
<blockquote><i>${response.data.content}<br>
|
})
|
||||||
${response.data.media_attachments.map(media =>
|
.catch((e) => {
|
||||||
`<a href="${media.remote_url}"><b>${media.description}</b></a>`)
|
matrix.utils.addReact(event, '❌');
|
||||||
.join('<br>')}
|
matrix.utils.sendError(event, roomId, e);
|
||||||
(id: ${response.data.id}</a>)
|
|
||||||
</blockquote>`);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
exports.runQuery = function (matrixClient, room, address, flaggedInput) {
|
exports.runQuery = function (roomId, address, flaggedInput, event) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
data: { status: `@10grans@fedi.cc tip `+ flaggedInput + ` to `+address },
|
data: { status: `@10grans@fedi.cc tip ${flaggedInput} to ${address}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`<b>
|
|
||||||
<blockquote><i>Tipping ${response.data.content}<br>
|
|
||||||
(id: ${response.data.id}</a>)
|
|
||||||
</blockquote><br>`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = async function (roomId, event, userInput) {
|
||||||
axios.get(`${config.fediverse.domain}/api/v1/accounts/${userInput}`).then((findUID) => {
|
const loadingString = `Searching for ${userInput}...`;
|
||||||
|
const original = await matrixClient.sendHtmlNotice(roomId, `${loadingString}`, `<code>${loadingString}</code>`);
|
||||||
|
const found = [];
|
||||||
|
const suggest = [];
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/accounts/${findUID.data.id}/unfollow`,
|
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
})
|
}).then((findUserId) => {
|
||||||
.then((response) => {
|
const results = findUserId.data.accounts;
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
const len = results.length;
|
||||||
'',
|
for (let i = 0; i < len; i++) results[i].acct !== userInput ? suggest.push(results[i].acct) : found.push(results[i]);
|
||||||
`Unsubscribed:
|
if (found.length > 0) return fediverse.utils.unfollow(roomId, found, event, original);
|
||||||
<blockquote>${config.fediverse.domain}/${response.data.id}`);
|
if (suggest.length > 0) msg = `<code>${userInput} was not found, suggesting:</code><blockquote>${suggest.join('<br>')}</blockquote>`;
|
||||||
});
|
if (suggest.length === 0) msg = `<code>No results found for: ${userInput}.</code>`;
|
||||||
}).catch((e) => {
|
return matrix.utils.editNoticeHTML(roomId, original, msg);
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
|
||||||
'', `${e}`);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
exports.runQuery = function (matrixClient, room, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`,
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`,
|
||||||
headers: { Authorization: `Bearer ${fediverse_auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
}).then((response) => {
|
})
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
.then(() => {
|
||||||
'',
|
matrix.utils.addReact(event, '✅');
|
||||||
`Unpinned:
|
|
||||||
<blockquote><i><a href="${config.fediverse.domain}/notice/${response.data.id}">
|
|
||||||
${response.data.content}</a></i>
|
|
||||||
</blockquote>`);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrix.utils.addReact(event, '❌');
|
||||||
'', `${e}`);
|
matrix.utils.sendError(event, roomId, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
14
commands/fediverse/unreblog.js
Normal file
14
commands/fediverse/unreblog.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
|
axios({
|
||||||
|
method: 'POST',
|
||||||
|
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unreblog`,
|
||||||
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
matrix.utils.addReact(event, '✅');
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
matrix.utils.addReact(event, '❌');
|
||||||
|
matrix.utils.sendError(event, roomId, e);
|
||||||
|
});
|
||||||
|
};
|
136
commands/fediverse/utils.js
Normal file
136
commands/fediverse/utils.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
const sendEventWithMeta = async (roomId, content, meta) => {
|
||||||
|
await matrixClient.sendEvent(roomId, 'm.room.message', {
|
||||||
|
body: content.replace(/<[^<]+?>/g, ''),
|
||||||
|
msgtype: 'm.notice',
|
||||||
|
formatted_body: content,
|
||||||
|
meta: meta,
|
||||||
|
format: 'org.matrix.custom.html',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasAttachment = (res) => {
|
||||||
|
if (!res.media_attachments) return '<br>';
|
||||||
|
return res.media_attachments.map((media) => {
|
||||||
|
const mediaURL = new URL(media.remote_url);
|
||||||
|
media.name = new URLSearchParams(mediaURL.search).get('name') || 'Unknown file name.';
|
||||||
|
return `File attachment: <a href="${media.remote_url}">${media.name}</a><br>`;
|
||||||
|
}).join('<br>');
|
||||||
|
};
|
||||||
|
|
||||||
|
const notifyFormatter = (res, roomId) => {
|
||||||
|
userDetails = `<b><a href="${config.fediverse.domain}/${res.account.id}">
|
||||||
|
${res.account.acct}</a></b>`;
|
||||||
|
switch (res.type) {
|
||||||
|
case 'follow':
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'follow' : res.meta = 'redact';
|
||||||
|
meta = `${res.meta} ${res.account.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<font color="#03b381"><b>has followed you.</b></font>
|
||||||
|
<br><blockquote><i>${res.account.note}</i></blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
break;
|
||||||
|
case 'favourite':
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'favourite' : res.meta = 'redact';
|
||||||
|
meta = `${res.meta} ${res.status.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<font color="#03b381"><b>has <a href="${res.status.uri}">favorited</a>
|
||||||
|
your post:</b></font>
|
||||||
|
<br><blockquote><i><b>${res.status.content}</i></b></blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, res.meta);
|
||||||
|
break;
|
||||||
|
case 'mention':
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'mention' : res.meta = 'redact';
|
||||||
|
meta = `${res.meta} ${res.status.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<font color="#03b381"><b>has <a href="${res.status.uri}">mentioned</a>
|
||||||
|
you:</b></font><br><blockquote><i><b>${res.status.content}
|
||||||
|
<br>(id: ${res.status.id}) ${registrar.post.visibilityEmoji(res.status.visibility)}</i></b>
|
||||||
|
</blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
break;
|
||||||
|
case 'reblog':
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'reblog' : res.meta = 'redact';
|
||||||
|
meta = `${res.meta} ${res.status.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<font color="#03b381"><b>has <a href="${res.status.uri}">repeated</a>
|
||||||
|
your post:</b></font><br>
|
||||||
|
<blockquote><i><b>${res.status.content}</i></b></blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw 'Unknown notification type.';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isOriginal = (res, roomId) => {
|
||||||
|
if (res.data) res = res.data;
|
||||||
|
userDetails = `<b><a href="${config.fediverse.domain}/${res.account.id}">
|
||||||
|
${res.account.acct}</a>`;
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'status' : res.meta = 'redact';
|
||||||
|
meta = `${res.meta} ${res.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<blockquote><i>${res.content}</i><br>
|
||||||
|
${hasAttachment(res)}
|
||||||
|
<br>(id: ${res.id}) ${registrar.post.visibilityEmoji(res.visibility)}
|
||||||
|
</blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isReblog = (res, roomId) => {
|
||||||
|
if (res.data) res = res.data;
|
||||||
|
userDetails = `<b><a href="${config.fediverse.domain}/${res.account.id}">
|
||||||
|
${res.account.acct}</a>`;
|
||||||
|
fediverse.auth.me !== res.account.url ? res.meta = 'status' : res.meta = 'unreblog';
|
||||||
|
meta = `${res.meta} ${res.reblog.id}`;
|
||||||
|
content = `${userDetails}
|
||||||
|
<font color="#7886D7"><b>has <a href="${config.fediverse.domain}/${res.reblog.id}">repeated</a>
|
||||||
|
${res.reblog.account.acct}'s post:</b></font>
|
||||||
|
<blockquote><i>${res.content}</i><br>
|
||||||
|
${hasAttachment(res)}
|
||||||
|
<br>(id: ${res.reblog.id}) ${registrar.post.visibilityEmoji(res.visibility)}
|
||||||
|
</blockquote>`;
|
||||||
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.sendEventWithMeta = sendEventWithMeta;
|
||||||
|
|
||||||
|
module.exports.formatter = (res, roomId) => {
|
||||||
|
const filtered = (res.label === 'notifications')
|
||||||
|
? notifyFormatter(res, roomId)
|
||||||
|
: (res.reblog == null)
|
||||||
|
? isOriginal(res, roomId)
|
||||||
|
: isReblog(res, roomId);
|
||||||
|
return filtered;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.follow = (roomId, account, event, original) => {
|
||||||
|
axios({
|
||||||
|
method: 'POST',
|
||||||
|
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/follow`,
|
||||||
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
matrix.utils.addReact(event, '✅');
|
||||||
|
matrix.utils.editNoticeHTML(roomId, original, `<code>Followed ${account[0].acct}.</code>`);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
matrix.utils.addReact(event, '❌');
|
||||||
|
matrix.utils.sendError(event, roomId, e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.unfollow = (roomId, account, event, original) => {
|
||||||
|
axios({
|
||||||
|
method: 'POST',
|
||||||
|
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/unfollow`,
|
||||||
|
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
matrix.utils.addReact(event, '✅');
|
||||||
|
matrix.utils.editNoticeHTML(roomId, original, `<code>Unfollowed ${account[0].acct}.</code>`);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
matrix.utils.addReact(event, '❌');
|
||||||
|
matrix.utils.sendError(event, roomId, e);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
exports.runQuery = function (matrixClient, room) {
|
exports.runQuery = function (roomId) {
|
||||||
matrixClient.sendHtmlNotice(room.roomId,
|
matrixClient.sendHtmlNotice(roomId,
|
||||||
'',
|
'',
|
||||||
'<blockquote><b>fediverse commands<br>'
|
'<blockquote><b>fediverse commands<br>'
|
||||||
+ '+post [your message] : post<br>'
|
+ '+post [your message] : post<br>'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const headers = ({ domain, userAgent }) => ({
|
const headers = ({ domain, userAgent }) => ({
|
||||||
'Host': `${domain}`,
|
Host: `${domain}`,
|
||||||
'User-Agent': `${userAgent}`
|
'User-Agent': `${userAgent}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const invidious = async (instance, url) => {
|
const invidious = async (instance, url) => {
|
||||||
|
@ -14,7 +14,7 @@ const invidious = async (instance, url) => {
|
||||||
author: video.author,
|
author: video.author,
|
||||||
views: video.viewCount,
|
views: video.viewCount,
|
||||||
likes: video.likeCount,
|
likes: video.likeCount,
|
||||||
dislikes: video.dislikeCount
|
dislikes: video.dislikeCount,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,28 +29,26 @@ const card = (video, base, path) =>
|
||||||
`<br />(${video.date})</b> <br />
|
`<br />(${video.date})</b> <br />
|
||||||
</blockquote>`;
|
</blockquote>`;
|
||||||
|
|
||||||
const run = async (matrixClient, { roomId }, userInput) => {
|
const run = async (roomId, userInput) => {
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: `https://${config.invidious.domain}/api/v1/videos/`,
|
baseURL: `https://${config.invidious.domain}/api/v1/videos/`,
|
||||||
headers: headers(config.invidious),
|
headers: headers(config.invidious),
|
||||||
transformResponse: [],
|
transformResponse: [],
|
||||||
timeout: 10 * 1000
|
timeout: 10 * 1000,
|
||||||
});
|
});
|
||||||
const video = await invidious(instance, userInput);
|
const video = await invidious(instance, userInput);
|
||||||
return await matrixClient.sendHtmlNotice(roomId, '', card(video, `https://${config.invidious.domain}`, userInput));
|
return await matrixClient.sendHtmlNotice(roomId, '', card(video, `https://${config.invidious.domain}`, userInput));
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.runQuery = async (client, room, userInput) => {
|
exports.runQuery = async (roomId, event, userInput) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(userInput);
|
const url = new URL(userInput);
|
||||||
if (!config.invidious.domains.includes(url.hostname)) throw '';
|
if (!config.invidious.domains.includes(url.hostname)) throw '';
|
||||||
if(/^\/[\w-]{11}$/.test(url.pathname))
|
if (/^\/[\w-]{11}$/.test(url.pathname)) return await run(roomId, url.pathname.slice(1));
|
||||||
return await run(client, room, url.pathname.slice(1));
|
const params = new URLSearchParams(url.search).get('v');
|
||||||
const params = new URLSearchParams(url.search).get("v");
|
|
||||||
if (!/^[\w-]{11}$/.test(params)) throw '';
|
if (!/^[\w-]{11}$/.test(params)) throw '';
|
||||||
return await run(client, room, params);
|
return await run(roomId, params);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||||
return client.sendHtmlNotice(room.roomId, 'Sad!', `<strong>Sad!</strong>`).catch(()=>{});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
const { JSDOM } = require("jsdom");
|
const { JSDOM } = require('jsdom');
|
||||||
|
|
||||||
const headers = ({ domain, userAgent }) => ({
|
const headers = ({ domain, userAgent }) => ({
|
||||||
'Host': `${domain}`,
|
Host: `${domain}`,
|
||||||
'User-Agent': `${userAgent}`
|
'User-Agent': `${userAgent}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const nitter = async (instance, url) => {
|
const nitter = async (instance, url) => {
|
||||||
const req = await instance({ method: 'GET', url });
|
const req = await instance({ method: 'GET', url });
|
||||||
if (req.statusText !== 'OK') throw req;
|
if (req.statusText !== 'OK') throw req;
|
||||||
const dom = new JSDOM(req.data);
|
const dom = new JSDOM(req.data);
|
||||||
const document = dom.window.document;
|
const { document } = dom.window;
|
||||||
const tweet = document.querySelector('#m');
|
const tweet = document.querySelector('#m');
|
||||||
const stats = tweet.querySelectorAll('.tweet-body > .tweet-stats .icon-container');
|
const stats = tweet.querySelectorAll('.tweet-body > .tweet-stats .icon-container');
|
||||||
const quote = tweet.querySelector('.tweet-body > .quote');
|
const quote = tweet.querySelector('.tweet-body > .quote');
|
||||||
|
@ -33,8 +33,8 @@ const nitter = async (instance, url) => {
|
||||||
stats: {
|
stats: {
|
||||||
replies: stats[0].textContent.trim(),
|
replies: stats[0].textContent.trim(),
|
||||||
retweets: stats[1].textContent.trim(),
|
retweets: stats[1].textContent.trim(),
|
||||||
favorites: stats[2].textContent.trim()
|
favorites: stats[2].textContent.trim(),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,24 +49,25 @@ const card = (tweet, base, check, path) =>
|
||||||
(tweet.hasAttachments ? '<blockquote><b>This tweet has attached media.</b></blockquote>' : '') +
|
(tweet.hasAttachments ? '<blockquote><b>This tweet has attached media.</b></blockquote>' : '') +
|
||||||
(tweet.isReply ? tweet.isReply === 'unavailable' ? '<blockquote>Replied Tweet is unavailable</blockquote>' : `<blockquote><b><a href="${base}${tweet.isReply.path}">Replied Tweet</a></b><br /><b><i>${tweet.isReply.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
(tweet.isReply ? tweet.isReply === 'unavailable' ? '<blockquote>Replied Tweet is unavailable</blockquote>' : `<blockquote><b><a href="${base}${tweet.isReply.path}">Replied Tweet</a></b><br /><b><i>${tweet.isReply.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
||||||
(tweet.quote ? `<blockquote><b><a href="${base}${tweet.quote.path}">Quoted Tweet</a></b><br /><b><i>${tweet.quote.text.replace('\n', '<br />')}</i></b></blockquote>` : '');
|
(tweet.quote ? `<blockquote><b><a href="${base}${tweet.quote.path}">Quoted Tweet</a></b><br /><b><i>${tweet.quote.text.replace('\n', '<br />')}</i></b></blockquote>` : '');
|
||||||
const run = async (matrixClient, { roomId }, userInput) => {
|
|
||||||
|
const run = async (roomId, userInput) => {
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: `https://${config.nitter.domain}`,
|
baseURL: `https://${config.nitter.domain}`,
|
||||||
headers: headers(config.nitter),
|
headers: headers(config.nitter),
|
||||||
transformResponse: [],
|
transformResponse: [],
|
||||||
timeout: 10 * 1000
|
timeout: 10 * 1000,
|
||||||
});
|
});
|
||||||
const tweet = await nitter(instance, userInput);
|
const tweet = await nitter(instance, userInput);
|
||||||
return await matrixClient.sendHtmlNotice(roomId, '', card(tweet, `https://${config.nitter.domain}`, config.nitter.check, userInput));
|
return await matrixClient.sendHtmlNotice(roomId, '', card(tweet, `https://${config.nitter.domain}`, config.nitter.check, userInput));
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.runQuery = async (client, room, userInput) => {
|
exports.runQuery = async (roomId, event, userInput) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(userInput);
|
const url = new URL(userInput);
|
||||||
if (!config.nitter.domains.includes(url.hostname)) throw '';
|
if (!config.nitter.domains.includes(url.hostname)) throw '';
|
||||||
if (!/^\/[^/]+\/status\/\d+\/?$/.test(url.pathname)) throw '';
|
if (!/^\/[^/]+\/status\/\d+\/?$/.test(url.pathname)) throw '';
|
||||||
return await run(client, room, url.pathname);
|
return await run(roomId, url.pathname);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return client.sendHtmlNotice(room.roomId, 'Sad!', `<strong>Sad!</strong>`).catch(()=>{});
|
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
67
main.js
67
main.js
|
@ -1,7 +1,7 @@
|
||||||
global.registrar = require('./registrar.js');
|
global.registrar = require('./registrar.js');
|
||||||
|
|
||||||
matrix_auth.access_token ? auth.matrixTokenLogin() : auth.getMatrixToken();
|
matrix.auth.access_token ? auth.matrixTokenLogin() : auth.getMatrixToken();
|
||||||
if (!fediverse_auth.access_token && config.fediverse.username) auth.registerFediverseApp();
|
if (!fediverse.auth.access_token && config.fediverse.username) auth.registerFediverseApp();
|
||||||
|
|
||||||
matrixClient.on('RoomMember.membership', (event, member) => {
|
matrixClient.on('RoomMember.membership', (event, member) => {
|
||||||
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
|
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
|
||||||
|
@ -17,57 +17,26 @@ matrixClient.on('RoomMember.membership', (event, member) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
matrixClient.on('Room.timeline', async function (event, room, member, toStartOfTimeline) {
|
matrixClient.on('event', async (event) => {
|
||||||
|
if (event.getSender() === matrixClient.credentials.userId) return matrix.utils.selfReact(event);
|
||||||
|
if (!event.getContent()['m.relates_to']) return;
|
||||||
|
if (event.event.unsigned.age > 10000) return;
|
||||||
|
return event.getType() === 'm.room.message'
|
||||||
|
? matrix.utils.handleReply(event) : matrix.utils.handleReact(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
matrixClient.on('Room.timeline', async (event, member, toStartOfTimeline) => {
|
||||||
if (toStartOfTimeline) return;
|
if (toStartOfTimeline) return;
|
||||||
if (event.isEncrypted()) await event._decryptionPromise;
|
if (event.isEncrypted()) await event._decryptionPromise;
|
||||||
if (event.getType() !== 'm.room.message') return;
|
if (event.getType() !== 'm.room.message') return;
|
||||||
if (event.getSender() === matrixClient.credentials.userId) return;
|
if (event.getSender() === matrixClient.credentials.userId) return;
|
||||||
if (event.event.unsigned.age > 10000) return;
|
if (event.event.unsigned.age > 10000) return;
|
||||||
if (event.getContent().body.charAt(0) === '+') {
|
roomId = event.event.room_id;
|
||||||
console.log(`Logs: ${event.event.sender} - ${event.getContent().body}`);
|
content = event.getContent().body;
|
||||||
let args = event.getContent().body.slice(1).trim().split(/ +/g);
|
if (content.charAt(0) === '+') {
|
||||||
let command = args.shift().toLowerCase();
|
const args = content.slice(1).trim().split(/ +/g);
|
||||||
const userInput = args.join(' ');
|
const command = args.shift().toLowerCase();
|
||||||
const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1);
|
console.log(`Logs: ${event.event.sender} - ${content}`);
|
||||||
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
matrix.utils.eventHandler(args, roomId, command, event);
|
||||||
|
|
||||||
args = [];
|
|
||||||
|
|
||||||
switch(command) {
|
|
||||||
case 'config':
|
|
||||||
return;
|
|
||||||
case 'help': case 'beg': case 'flood': case 'asdf':
|
|
||||||
args.push(matrixClient, room);
|
|
||||||
break;
|
|
||||||
case 'tip':
|
|
||||||
args.push(matrixClient, room, address, flaggedInput);
|
|
||||||
break;
|
|
||||||
case 'archive': case 'rearchive':
|
|
||||||
args.push(matrixClient, room, userInput, !!~command.indexOf('re'));
|
|
||||||
command = 'archive';
|
|
||||||
break;
|
|
||||||
case 'post': case 'reply': case 'media': case 'mediareply':
|
|
||||||
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
|
|
||||||
args.push(matrixClient, room, userInput, {
|
|
||||||
isReply: !!~command.indexOf('reply'),
|
|
||||||
hasMedia: !!~command.indexOf('media'),
|
|
||||||
hasSubject: !!~command.indexOf('random'),
|
|
||||||
});
|
|
||||||
command = 'media';
|
|
||||||
break;
|
|
||||||
case 'proxy':
|
|
||||||
try {
|
|
||||||
const url = new URL(userInput);
|
|
||||||
command = config.invidious.domains.includes(url.hostname)
|
|
||||||
? 'invidious'
|
|
||||||
: config.nitter.domains.includes(url.hostname)
|
|
||||||
? 'nitter'
|
|
||||||
: 'proxy';
|
|
||||||
} catch(e) {}
|
|
||||||
//fallthrough
|
|
||||||
default:
|
|
||||||
args.push(matrixClient, room, userInput);
|
|
||||||
}
|
|
||||||
registrar[command] && registrar[command].runQuery.apply(null, args);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ligh7hau5",
|
"name": "ligh7hau5",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"description": "A Matrix to Fediverse client",
|
"description": "A Matrix to Fediverse client",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
24
registrar.js
24
registrar.js
|
@ -8,14 +8,21 @@ const { LocalStorage } = require('node-localstorage');
|
||||||
global.localStorage = new LocalStorage('./keys');
|
global.localStorage = new LocalStorage('./keys');
|
||||||
if (!localStorage.getItem('matrix_auth')) {
|
if (!localStorage.getItem('matrix_auth')) {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
localStorage.setItem('matrix_auth', "{}");
|
localStorage.setItem('matrix_auth', '[]');
|
||||||
}
|
}
|
||||||
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', "{}");
|
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', '[]');
|
||||||
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', "{}");
|
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', '[]');
|
||||||
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', "{}");
|
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', '[]');
|
||||||
|
|
||||||
global.matrix_auth = JSON.parse(localStorage.getItem('matrix_auth'));
|
|
||||||
global.fediverse_auth = JSON.parse(localStorage.getItem('fediverse_auth'));
|
global.fediverse = {
|
||||||
|
auth: JSON.parse(localStorage.getItem('fediverse_auth')),
|
||||||
|
utils: require('./commands/fediverse/utils.js'),
|
||||||
|
};
|
||||||
|
global.matrix = {
|
||||||
|
auth: JSON.parse(localStorage.getItem('matrix_auth')),
|
||||||
|
utils: require('./utils.js'),
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
config: require('./config.js'),
|
config: require('./config.js'),
|
||||||
|
@ -29,15 +36,14 @@ module.exports = {
|
||||||
flood: require('./commands/fediverse/flood.js'),
|
flood: require('./commands/fediverse/flood.js'),
|
||||||
follow: require('./commands/fediverse/follow.js'),
|
follow: require('./commands/fediverse/follow.js'),
|
||||||
help: require('./commands/help.js'),
|
help: require('./commands/help.js'),
|
||||||
media: require('./commands/fediverse/media.js'),
|
|
||||||
mordy: require('./commands/fediverse/mordy.js'),
|
mordy: require('./commands/fediverse/mordy.js'),
|
||||||
notify: require('./commands/fediverse/notify.js'),
|
notify: require('./commands/fediverse/notify.js'),
|
||||||
pin: require('./commands/fediverse/pin.js'),
|
pin: require('./commands/fediverse/pin.js'),
|
||||||
post: require('./commands/fediverse/post.js'),
|
post: require('./commands/fediverse/post.js'),
|
||||||
redact: require('./commands/fediverse/redact.js'),
|
redact: require('./commands/fediverse/redact.js'),
|
||||||
reply: require('./commands/fediverse/reply.js'),
|
|
||||||
status: require('./commands/fediverse/status.js'),
|
status: require('./commands/fediverse/status.js'),
|
||||||
tip: require('./commands/fediverse/tip.js'),
|
tip: require('./commands/fediverse/tip.js'),
|
||||||
unfollow: require('./commands/fediverse/unfollow.js'),
|
unfollow: require('./commands/fediverse/unfollow.js'),
|
||||||
unpin: require('./commands/fediverse/unpin.js')
|
unpin: require('./commands/fediverse/unpin.js'),
|
||||||
|
unreblog: require('./commands/fediverse/unreblog.js')
|
||||||
};
|
};
|
||||||
|
|
131
utils.js
Normal file
131
utils.js
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
const sendError = async (event, roomId, e) => {
|
||||||
|
e.response ? error = `Error(${e.response.status}): ${e.response.data.error}`
|
||||||
|
: e.data ? error = `Error(${e.errcode}): ${e.data.error}`
|
||||||
|
: error = `Error: ${e.syscall}, ${e.code}`;
|
||||||
|
return matrixClient.sendHtmlNotice(roomId,
|
||||||
|
'', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addReact = async (event, key) => {
|
||||||
|
const roomId = event.event.room_id;
|
||||||
|
return matrixClient.sendEvent(event.event.room_id, 'm.reaction', {
|
||||||
|
'm.relates_to': {
|
||||||
|
rel_type: 'm.annotation',
|
||||||
|
event_id: event.getId(),
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
}).catch((e) => sendError(null, roomId, e));
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventHandler = (args, roomId, command, event) => {
|
||||||
|
const userInput = args.join(' ');
|
||||||
|
const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1);
|
||||||
|
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
||||||
|
|
||||||
|
args = [];
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 'config':
|
||||||
|
return;
|
||||||
|
case 'help': case 'flood': case 'notify':
|
||||||
|
args.push(roomId);
|
||||||
|
break;
|
||||||
|
case 'tip':
|
||||||
|
args.push(roomId, address, flaggedInput);
|
||||||
|
break;
|
||||||
|
case 'archive': case 'rearchive':
|
||||||
|
args.push(roomId, userInput, !!~command.indexOf('re'));
|
||||||
|
command = 'archive';
|
||||||
|
break;
|
||||||
|
case 'post': case 'reply': case 'media': case 'mediareply':
|
||||||
|
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
|
||||||
|
args.push(roomId, userInput, {
|
||||||
|
isReply: !!~command.indexOf('reply'),
|
||||||
|
hasMedia: !!~command.indexOf('media'),
|
||||||
|
hasSubject: !!~command.indexOf('random'),
|
||||||
|
});
|
||||||
|
command = 'post';
|
||||||
|
break;
|
||||||
|
case 'proxy':
|
||||||
|
try {
|
||||||
|
const url = new URL(userInput);
|
||||||
|
command = config.invidious.domains.includes(url.hostname)
|
||||||
|
? 'invidious'
|
||||||
|
: config.nitter.domains.includes(url.hostname)
|
||||||
|
? 'nitter'
|
||||||
|
: 'proxy';
|
||||||
|
} catch (e) { sendError(event, roomId, e); }
|
||||||
|
// fallthrough
|
||||||
|
default:
|
||||||
|
args.push(roomId, event, userInput);
|
||||||
|
}
|
||||||
|
registrar[command] && registrar[command].runQuery.apply(null, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.sendError = sendError;
|
||||||
|
|
||||||
|
module.exports.addReact = addReact;
|
||||||
|
|
||||||
|
module.exports.eventHandler = eventHandler;
|
||||||
|
|
||||||
|
module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sendMessage(roomId, {
|
||||||
|
body: ` * ${plain || html.replace(/<[^<]+?>/g, '')}`,
|
||||||
|
formatted_body: ` * ${html}`,
|
||||||
|
format: 'org.matrix.custom.html',
|
||||||
|
msgtype: 'm.notice',
|
||||||
|
'm.new_content': {
|
||||||
|
body: plain || html.replace(/<[^<]+?>/g, ''),
|
||||||
|
formatted_body: html,
|
||||||
|
format: 'org.matrix.custom.html',
|
||||||
|
msgtype: 'm.notice',
|
||||||
|
},
|
||||||
|
'm.relates_to': {
|
||||||
|
rel_type: 'm.replace',
|
||||||
|
event_id: event.event_id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.handleReact = async (event) => {
|
||||||
|
const roomId = event.event.room_id;
|
||||||
|
const reaction = event.getContent()['m.relates_to'];
|
||||||
|
if (!reaction) return;
|
||||||
|
const metaEvent = await matrixClient.fetchRoomEvent(roomId, reaction.event_id);
|
||||||
|
if (!metaEvent.content.meta || metaEvent.sender !== config.matrix.user) return;
|
||||||
|
const args = metaEvent.content.meta.split(' ');
|
||||||
|
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
||||||
|
if (!isMeta.includes(args[0])) return;
|
||||||
|
let command = [];
|
||||||
|
args.shift().toLowerCase();
|
||||||
|
if (reaction.key === '🔃') command = 'copy';
|
||||||
|
if (reaction.key === '👏') command = 'clap';
|
||||||
|
if (reaction.key === '🗑') command = 'redact';
|
||||||
|
eventHandler(args, roomId, command, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.handleReply = async (event) => {
|
||||||
|
const roomId = event.event.room_id;
|
||||||
|
const reply = event.getContent()['m.relates_to']['m.in_reply_to'];
|
||||||
|
if (!reply) return;
|
||||||
|
const metaEvent = await matrixClient.fetchRoomEvent(roomId, reply.event_id);
|
||||||
|
if (!metaEvent.content.meta || metaEvent.sender !== config.matrix.user) return;
|
||||||
|
const args = metaEvent.content.meta.split(' ');
|
||||||
|
args.push(event.event.content.formatted_body.trim().split('</mx-reply>')[1]);
|
||||||
|
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
||||||
|
if (!isMeta.includes(args[0])) return;
|
||||||
|
args.shift().toLowerCase();
|
||||||
|
command = 'reply';
|
||||||
|
eventHandler(args, roomId, command, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.selfReact = async (event) => {
|
||||||
|
if (event.getType() !== 'm.room.message') return;
|
||||||
|
if (event.event.unsigned.age > 10000) return;
|
||||||
|
const { meta } = event.getContent();
|
||||||
|
if (!meta) return;
|
||||||
|
const type = meta.split(' ')[0];
|
||||||
|
if (type === 'redact' || type === 'unreblog') addReact(event, '🗑️');
|
||||||
|
if (type === 'status' || type === 'reblog' || type === 'mention') {
|
||||||
|
addReact(event, '🔃');
|
||||||
|
addReact(event, '👏');
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue