Compare commits
No commits in common. "master" and "1.2.2" have entirely different histories.
34 changed files with 888 additions and 1962 deletions
85
auth.js
85
auth.js
|
@ -1,10 +1,12 @@
|
|||
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||
|
||||
const matrixTokenLogin = async () => {
|
||||
matrixClient = sdk.createClient({
|
||||
baseUrl: config.matrix.domain,
|
||||
accessToken: matrix.auth.access_token,
|
||||
userId: matrix.auth.user_id,
|
||||
deviceId: matrix.auth.device_id,
|
||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||
});
|
||||
matrixClient.initCrypto()
|
||||
|
@ -16,7 +18,6 @@ const matrixTokenLogin = async () => {
|
|||
+ '====================================================',
|
||||
);
|
||||
}
|
||||
matrixClient.setGlobalErrorOnUnknownDevices(config.matrix.manualVerify);
|
||||
matrixClient.startClient();
|
||||
});
|
||||
};
|
||||
|
@ -24,7 +25,7 @@ const matrixTokenLogin = async () => {
|
|||
module.exports.matrixTokenLogin = matrixTokenLogin;
|
||||
|
||||
module.exports.getMatrixToken = async () => {
|
||||
matrixClient = sdk.createClient({ baseUrl: config.matrix.domain });
|
||||
matrixClient = sdk.createClient(config.matrix.domain);
|
||||
matrixClient.loginWithPassword(config.matrix.user, config.matrix.password)
|
||||
.then((response) => {
|
||||
matrix.auth = {
|
||||
|
@ -39,75 +40,31 @@ module.exports.getMatrixToken = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
const getFediverseLink = (domain,roomId) => {
|
||||
let apps = {}
|
||||
apps = JSON.parse(localStorage.getItem("apps"));
|
||||
if(!apps[domain]){
|
||||
axios.post(`https://${domain}/api/v1/apps`,
|
||||
module.exports.registerFediverseApp = async () => {
|
||||
axios.post(`${config.fediverse.domain}/api/v1/apps`,
|
||||
{
|
||||
client_name: config.fediverse.client_name,
|
||||
redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
|
||||
scopes: 'read write follow push',
|
||||
})
|
||||
.then((response) => {
|
||||
console.log(response.data)
|
||||
if(!response.data.client_id || !response.data.client_secret) return false;
|
||||
apps[domain] = {
|
||||
client_id: response.data.client_id,
|
||||
client_secret: response.data.client_secret
|
||||
}
|
||||
localStorage.setItem("apps",JSON.stringify(apps))
|
||||
matrixClient.sendHtmlNotice(roomId,"Приложение зарегистрировано. Введите команду еще раз для создания ссылки")
|
||||
// return getFediverseLink(domain)
|
||||
axios.post(`${config.fediverse.domain}/oauth/token`,
|
||||
{
|
||||
username: config.fediverse.username,
|
||||
password: config.fediverse.password,
|
||||
client_id: response.data.client_id,
|
||||
client_secret: response.data.client_secret,
|
||||
scope: 'read write follow push',
|
||||
grant_type: 'password',
|
||||
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
||||
})
|
||||
.then((tokens) => {
|
||||
localStorage.setItem('fediverse_auth', JSON.stringify(tokens.data, null, 2));
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}else{
|
||||
const app = apps[domain]
|
||||
const uri = "urn:ietf:wg:oauth:2.0:oob".replace(/:/g,"%3A")
|
||||
const scope = "read write follow push".replace(/ /g,"%20")
|
||||
return `https://${domain}/oauth/authorize?client_id=${app.client_id}&response_type=code&redirect_uri=${uri}&scope=${scope}`
|
||||
}
|
||||
return "nothing"
|
||||
};
|
||||
|
||||
const obtainAccessToken = (domain,code,event) => {
|
||||
const apps = JSON.parse(localStorage.getItem("apps"));
|
||||
console.log(domain,code)
|
||||
const app = apps[domain];
|
||||
axios.post(`https://${domain}/oauth/token`, {
|
||||
client_id: app.client_id,
|
||||
client_secret: app.client_secret,
|
||||
redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
|
||||
grant_type: "authorization_code",
|
||||
code: code,
|
||||
scopes: "read write follow push"
|
||||
}).then(response => {
|
||||
if(!response.data.access_token) return console.log(response.data)
|
||||
fediverse.auth[event.getSender()] = {
|
||||
domain: domain,
|
||||
access_token: response.data.access_token
|
||||
}
|
||||
localStorage.setItem("fediverse_auth", JSON.stringify(fediverse.auth))
|
||||
getFediverseUserInfo(event)
|
||||
}).catch(e => console.error(e))
|
||||
}
|
||||
|
||||
const getFediverseUserInfo = (event) => {
|
||||
const user = event.getSender()
|
||||
axios({
|
||||
method: "GET",
|
||||
url: `https://${fediverse.auth[user].domain}/api/v1/accounts/verify_credentials`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${fediverse.auth[user].access_token}`
|
||||
}
|
||||
}).then(response => {
|
||||
if(response.data.username){
|
||||
matrixClient.sendHtmlNotice(event.getRoomId(), `Успешный вход в аккаунт ${response.data.display_name || response.data.username} (@${response.data.username}@${fediverse.auth[user].domain})`)
|
||||
}else{
|
||||
console.log(response.data)
|
||||
}
|
||||
}).catch(e => console.error(e))
|
||||
}
|
||||
module.exports.getFediverseLink = getFediverseLink;
|
||||
module.exports.obtainAccessToken = obtainAccessToken;
|
|
@ -1,6 +1,5 @@
|
|||
const { JSDOM } = require('jsdom');
|
||||
const qs = require('qs');
|
||||
const https = require('https');
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
|
@ -36,7 +35,6 @@ const arc3Str = str => `<em>Timed out <code>${str}</code></em>`;
|
|||
const run = async (roomId, userInput, rearchive) => {
|
||||
const instance = axios.create({
|
||||
baseURL: `https://${config.archive.domain}`,
|
||||
httpsAgent: https.Agent({ maxVersion: "TLSv1.2"}),
|
||||
headers: headers(config.archive),
|
||||
transformResponse: [],
|
||||
timeout: 10 * 1000
|
||||
|
@ -44,7 +42,7 @@ const run = async (roomId, userInput, rearchive) => {
|
|||
|
||||
let reply = null;
|
||||
try {
|
||||
reply = await matrixClient.sendHtmlNotice(roomId, ' ', reqStr(userInput));
|
||||
reply = await matrixClient.sendHtmlNotice(roomId, '', reqStr(userInput));
|
||||
const { refresh, id, title, date } = await archive(instance, userInput, rearchive);
|
||||
if (id)
|
||||
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
exports.runQuery = async (roomId, event, userInput) => {
|
||||
return matrix.utils.fetchEncryptedOrNot(roomId, { event_id: userInput })
|
||||
.then(event => matrix.utils.expandReact(event))
|
||||
.catch(e => {
|
||||
matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>')
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
exports.runQuery = (roomId,event,userInput) => {
|
||||
|
||||
//matrixClient.sendHtmlNotice(roomId,"Проверка связи","Проверка связи");
|
||||
const link = auth.getFediverseLink(userInput)
|
||||
if(!link){
|
||||
matrixClient.sendHtmlNotice(roomId,"Не удалось получить ссылку")
|
||||
}else if(link == "nothing"){
|
||||
}else{
|
||||
authEvents.push(event.event_id)
|
||||
matrixClient.sendHtmlNotice(roomId,`Перейдите по ссылке для входа в аккаунт. Для завершения ответьте на это сообщение кодом (еще не готово, пж не переходе по ссылке): ${link}`)
|
||||
}
|
||||
}
|
15
commands/fediverse/beg.js
Normal file
15
commands/fediverse/beg.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
exports.runQuery = function (roomId, event) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
data: { status: '@10grans@fedi.cc beg' },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
matrix.utils.sendError(event, roomId, e);
|
||||
});
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unfavourite`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/favourite`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/reblog`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,36 +1,23 @@
|
|||
let intervalId = null;
|
||||
|
||||
exports.runQuery = function (roomId, disable) {
|
||||
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
|
||||
if (disable) return;
|
||||
|
||||
intervalId = setInterval(() => {
|
||||
exports.runQuery = function (roomId) {
|
||||
setInterval(() => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/timelines/home`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/timelines/home`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then((res) => {
|
||||
let timeline = JSON.parse(localStorage.getItem('timeline'));
|
||||
past = timeline[event.getSender()] || {}
|
||||
let past = JSON.parse(localStorage.getItem('timeline'));
|
||||
if (past.length === 0) past = res.data;
|
||||
const events = res.data;
|
||||
const len = events.length;
|
||||
for (let i = len - 1; i >= 0; i--) {
|
||||
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||
const lastStored = past.slice(past.length - 1, past.length);
|
||||
if (events[i].created_at < lastStored[0].created_at) return;
|
||||
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
||||
events[i].label = 'status';
|
||||
fediverse.utils.formatter(events[i], roomId);
|
||||
}
|
||||
}
|
||||
timeline[event.getSender()] = events
|
||||
localStorage.setItem('timeline', JSON.stringify(timeline, null, 2));
|
||||
localStorage.setItem('timeline', JSON.stringify(events, null, 2));
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.sendError(null, roomId, e);
|
||||
|
|
|
@ -5,8 +5,8 @@ exports.runQuery = async function (roomId, event, userInput) {
|
|||
const suggest = [];
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
}).then((findUserId) => {
|
||||
const results = findUserId.data.accounts;
|
||||
const len = results.length;
|
||||
|
|
31
commands/fediverse/makeitrain.js
Normal file
31
commands/fediverse/makeitrain.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
exports.runQuery = async function (roomId, event, notice) {
|
||||
if (config.fediverse.tipping === false) return matrixClient.sendHtmlNotice(roomId, `Tipping is not enabled.`, `<code>Tipping is not enabled.</code>`);
|
||||
const loadingString = `Making it rain for notice: ${notice}...`;
|
||||
const original = await matrixClient.sendHtmlNotice(roomId, `${loadingString}`, `<code>${loadingString}</code>`);
|
||||
const findAllUsers = await fediverse.utils.getStatusMentions(notice, event, original);
|
||||
const exclude = "@10grans@fedi.cc";
|
||||
const users = findAllUsers.filter(users => !exclude.includes(findAllUsers))
|
||||
if (!users) return matrix.utils.editNoticeHTML(roomId, original, `<code>No eligible users found.</code>`);
|
||||
const rain = (users) => {
|
||||
amount = users.length * 0.00000001337 // by per user:
|
||||
if (users.length === 1) return amount * 100000 // 0.001337
|
||||
if (users.length <= 5) return amount * 10000 // 0.0001337
|
||||
if (users.length <= 10) return amount * 1000 // 0.00001337
|
||||
if (users.length <= 100) return amount * 100 // 0.000001337
|
||||
return amount
|
||||
}
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
data: { status: `@10grans@fedi.cc makeitrain ${rain(users)} to ${users.join(' ')}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
return matrix.utils.editNoticeHTML(roomId, original, `<code>Raining ${rain(users)} 10grans on: 🌧${users.join(', ')}🌧</code>`);
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
return matrix.utils.editNoticeHTML(roomId, original, `<code>${e}</code>`)
|
||||
});
|
||||
};
|
20
commands/fediverse/mordy.js
Normal file
20
commands/fediverse/mordy.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
data: {
|
||||
status: `@mordekai ${userInput}`,
|
||||
content_type: 'text/markdown',
|
||||
visibility: 'unlisted',
|
||||
expires_in: '7200',
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
matrix.utils.sendError(event, roomId, e);
|
||||
});
|
||||
};
|
|
@ -1,36 +1,23 @@
|
|||
let intervalId = null;
|
||||
|
||||
exports.runQuery = function (roomId, disable) {
|
||||
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
|
||||
if (disable) return;
|
||||
|
||||
intervalId = setInterval(() => {
|
||||
exports.runQuery = function (roomId) {
|
||||
setInterval(() => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/notifications`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/notifications`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then((res) => {
|
||||
let notifications = JSON.parse(localStorage.getItem('notifications'));
|
||||
let past = notifications[event.getSender()] || {}
|
||||
let past = JSON.parse(localStorage.getItem('notifications'));
|
||||
if (past.length === 0) past = res.data;
|
||||
const events = res.data;
|
||||
const len = events.length;
|
||||
for (let i = len - 1; i >= 0; i--) {
|
||||
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||
const lastStored = past.slice(past.length - 1, past.length);
|
||||
if (events[i].created_at < lastStored[0].created_at) return;
|
||||
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
||||
events[i].label = 'notifications';
|
||||
fediverse.utils.formatter(events[i], roomId);
|
||||
}
|
||||
}
|
||||
notifications[event.getSender()] = events
|
||||
localStorage.setItem('notifications', JSON.stringify(notifications, null, 2));
|
||||
localStorage.setItem('notifications', JSON.stringify(events, null, 2));
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.sendError(null, roomId, e);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/pin`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/pin`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,61 +1,9 @@
|
|||
const qs = require('qs');
|
||||
const crypto = require('crypto');
|
||||
const FormData = require('form-data');
|
||||
|
||||
const emojis = { public: '🌐', unlisted: '📝', private: '🔒️', direct: '✉️' };
|
||||
exports.visibilityEmoji = (v) => emojis[v] || v;
|
||||
|
||||
const mediaPathRegex = /^\/_matrix\/media\/r0\/download\/[^/]+\/[^/]+\/?$/;
|
||||
|
||||
const decryptMedia = (media, file) => {
|
||||
const { v, key: { alg, ext, k, }, iv } = file;
|
||||
|
||||
if (v !== 'v2' || ext !== true || alg !== 'A256CTR')
|
||||
throw new Error('Unsupported file encryption');
|
||||
|
||||
const key = Buffer.from(k, 'base64');
|
||||
const _iv = Buffer.from(iv, 'base64');
|
||||
const cipher = crypto.createDecipheriv('aes-256-ctr', key, _iv);
|
||||
const data = Buffer.concat([ cipher.update(media.data), cipher.final() ]);
|
||||
return Object.assign({}, media, { data });
|
||||
};
|
||||
|
||||
const getMediaInfoFromEvent = async (roomId, event_id) => {
|
||||
const event = await matrix.utils.fetchEncryptedOrNot(roomId, { event_id });
|
||||
if (event.getType() !== 'm.room.message') throw new Error('Invalid type');
|
||||
const content = event.getContent();
|
||||
if (content.msgtype !== 'm.image') throw new Error('Invalid msgtype');
|
||||
if (content.url) return { url: getMediaUrl(content.url) };
|
||||
if (content.file) return {
|
||||
url: getMediaUrl(content.file.url),
|
||||
filename: content.body,
|
||||
mimetype: content.info ? content.info.mimetype : null,
|
||||
file: content.file
|
||||
};
|
||||
throw new Error('Invalid event');
|
||||
};
|
||||
|
||||
const getMediaUrl = string => {
|
||||
let url = new URL(string);
|
||||
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:' ||
|
||||
!config.matrix.domains.includes(url.hostname) ||
|
||||
!mediaPathRegex.test(url.pathname))
|
||||
throw new Error('Invalid URL');
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
const getMedia = async (roomId, string) => {
|
||||
let opts = {};
|
||||
if (string.startsWith('mxe://'))
|
||||
opts = await getMediaInfoFromEvent(roomId, string.substring(6));
|
||||
else
|
||||
opts.url = getMediaUrl(string);
|
||||
const media = await mediaDownload(opts);
|
||||
return opts.file ? decryptMedia(media, opts.file) : media;
|
||||
};
|
||||
|
||||
const getFilename = (header) => {
|
||||
if (typeof header !== 'string') return null;
|
||||
try {
|
||||
|
@ -66,14 +14,15 @@ const getFilename = (header) => {
|
|||
}
|
||||
};
|
||||
|
||||
const mediaDownload = async (opts) => {
|
||||
const { whitelist, blacklist } = config.fediverse.mimetypes;
|
||||
const media = await axios({ method: 'GET', url: opts.url, responseType: 'arraybuffer' });
|
||||
const filename = opts.filename || getFilename(media.headers['content-disposition']);
|
||||
const mimetype = opts.mimetype || media.headers['content-type'];
|
||||
if (media.statusText !== 'OK' || blacklist.includes(mimetype)) throw media;
|
||||
if (whitelist.length && !whitelist.includes(mimetype)) throw media;
|
||||
return { data: media.data, filename, mimetype };
|
||||
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 }) => {
|
||||
|
@ -84,29 +33,28 @@ const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
|||
});
|
||||
const upload = await axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/media`,
|
||||
headers: form.getHeaders({ Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` }),
|
||||
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, event, content, replyId, mediaURL, subject, visibility) => {
|
||||
const run = async (roomId, event, content, replyId, mediaURL, subject) => {
|
||||
let mediaId = null;
|
||||
if (mediaURL) {
|
||||
const media = await getMedia(roomId, mediaURL);
|
||||
const media = await mediaDownload(mediaURL, config.fediverse.mimetypes);
|
||||
mediaId = await mediaUpload(config.fediverse, media);
|
||||
}
|
||||
if (replyId) content = await fediverse.utils.getStatusMentions(replyId, event).then(m => m.concat(content).join(' '));
|
||||
const response = await axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
url: `${config.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',
|
||||
visibility: visibility || undefined,
|
||||
media_ids: mediaURL && [mediaId] || undefined,
|
||||
in_reply_to_id: replyId || undefined,
|
||||
spoiler_text: subject || undefined,
|
||||
|
@ -115,18 +63,29 @@ const run = async (roomId, event, content, replyId, mediaURL, subject, visibilit
|
|||
return fediverse.utils.sendEventWithMeta(roomId, `<a href="${response.data.url}">${response.data.id}</a>`, `redact ${response.data.id}`);
|
||||
};
|
||||
|
||||
exports.runQuery = async (roomId, event, userInput, { isReply, hasMedia, hasSubject, visibility }) => {
|
||||
exports.runQuery = async (roomId, event, 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.shift();
|
||||
if (hasMedia) mediaURL = chunks.shift();
|
||||
return await run(roomId, event, chunks.join(' '), replyId, mediaURL, subject, visibility);
|
||||
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, event, chunks.join(' '), replyId, mediaURL, subject);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||
}
|
||||
};
|
||||
|
|
26
commands/fediverse/react.js
vendored
26
commands/fediverse/react.js
vendored
|
@ -1,26 +0,0 @@
|
|||
const run = async (roomId, event, id, emoji, remove) => {
|
||||
axios({
|
||||
method: remove ? 'DELETE' : 'PUT',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/pleroma/statuses/${id}/reactions/${emoji}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
matrix.utils.sendError(event, roomId, e);
|
||||
});
|
||||
};
|
||||
|
||||
exports.runQuery = async (roomId, event, userInput, remove) => {
|
||||
try {
|
||||
const chunks = userInput.trim().split(' ');
|
||||
if (chunks.length !== 2) throw '';
|
||||
const id = encodeURIComponent(chunks[0]);
|
||||
const emoji = encodeURIComponent(chunks[1]);
|
||||
return run(roomId, event, id, emoji, remove);
|
||||
} catch (e) {
|
||||
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||
}
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'DELETE',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then((response) => {
|
||||
response.label = 'status';
|
||||
|
|
16
commands/fediverse/tip.js
Normal file
16
commands/fediverse/tip.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
exports.runQuery = function (roomId, event, address, flaggedInput) {
|
||||
if (config.fediverse.tipping === false) return matrixClient.sendHtmlNotice(roomId, `Tipping is not enabled.`, `<code>Tipping is not enabled.</code>`);
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
data: { status: `@10grans@fedi.cc tip ${flaggedInput} to ${address}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
matrix.utils.sendError(event, roomId, e);
|
||||
});
|
||||
};
|
|
@ -5,8 +5,8 @@ exports.runQuery = async function (roomId, event, userInput) {
|
|||
const suggest = [];
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
}).then((findUserId) => {
|
||||
const results = findUserId.data.accounts;
|
||||
const len = results.length;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unpin`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unreblog`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unreblog`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
exports.runQuery = function (roomId, event, userInput) {
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://' + fediverse.auth[event.getSender()].domain,
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
});
|
||||
instance.get(`/api/v1/statuses/${userInput}/context`)
|
||||
.then(async (response) => {
|
||||
let story = [];
|
||||
const rel = event.getContent()['m.relates_to'];
|
||||
const eventId = rel && rel.event_id ? rel.event_id : event.getId();
|
||||
const original = await instance.get(`/api/v1/statuses/${userInput}`);
|
||||
const ancestors = response.data.ancestors;
|
||||
const descendants = response.data.descendants;
|
||||
story = [...story, ancestors, original.data, descendants];
|
||||
const book = story.flat();
|
||||
await fediverse.utils.thread(roomId, eventId, '<br><hr><h3>...Beginning thread...</h3><hr><br>');
|
||||
for (const [i, entry] of book.entries()) {
|
||||
entry.label = 'thread';
|
||||
fediverse.utils.formatter(entry, roomId, eventId);
|
||||
}
|
||||
await fediverse.utils.thread(roomId, eventId, '<br><hr><h3>...Thread ended...</h3><hr><br>');
|
||||
})
|
||||
.catch((e) => {
|
||||
matrix.utils.addReact(event, '❌');
|
||||
matrix.utils.sendError(event, roomId, e);
|
||||
});
|
||||
};
|
|
@ -8,20 +8,6 @@ const sendEventWithMeta = async (roomId, content, meta) => {
|
|||
});
|
||||
};
|
||||
|
||||
const thread = async (roomId, eventId, 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',
|
||||
'm.relates_to': {
|
||||
rel_type: 'm.thread',
|
||||
event_id: eventId,
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
const hasAttachment = (res) => {
|
||||
if (res.status) res = res.status;
|
||||
if (!res.media_attachments) return '<br>';
|
||||
|
@ -62,7 +48,7 @@ const notifyFormatter = (res, roomId) => {
|
|||
content = `${userDetails}
|
||||
<font color="#03b381"><b>has <a href="${config.fediverse.domain}/notice/${res.status.id}">mentioned</a>
|
||||
you:</font><blockquote><i>${res.status.content}</i><br>
|
||||
${hasAttachment(res)}
|
||||
${hasAttachment(res.status.id)}
|
||||
<br>(id: ${res.status.id}) ${registrar.post.visibilityEmoji(res.status.visibility)}
|
||||
</blockquote>`;
|
||||
sendEventWithMeta(roomId, content, meta);
|
||||
|
@ -78,24 +64,12 @@ const notifyFormatter = (res, roomId) => {
|
|||
</blockquote>`;
|
||||
sendEventWithMeta(roomId, content, meta);
|
||||
break;
|
||||
case 'pleroma:emoji_reaction':
|
||||
fediverse.auth.me !== res.account.url ? res.meta = 'react' : res.meta = 'redact';
|
||||
meta = `${res.meta} ${res.status.id}`;
|
||||
content = `${userDetails}
|
||||
<font color="#03b381"><b>has <a href="${config.fediverse.domain}/notice/${res.status.id}">reacted</a> with
|
||||
${ res.emoji_url ? `<a href="${res.emoji_url}">${res.emoji}</a>` : `<span>${res.emoji}</span>` }
|
||||
to your post:</font><blockquote><i>${res.status.content}</i><br>
|
||||
${hasAttachment(res)}
|
||||
<br>(id: ${res.status.id}) ${registrar.post.visibilityEmoji(res.status.visibility)}
|
||||
</blockquote>`;
|
||||
sendEventWithMeta(roomId, content, meta);
|
||||
break;
|
||||
default:
|
||||
return console.log('Unknown notification type.');
|
||||
}
|
||||
};
|
||||
|
||||
const isOriginal = (res, roomId, event) => {
|
||||
const isOriginal = (res, roomId) => {
|
||||
if (res.data) res = res.data;
|
||||
userDetails = `<b><a href="${config.fediverse.domain}/notice/${res.id}">
|
||||
${res.account.acct}</a>`;
|
||||
|
@ -106,8 +80,7 @@ const isOriginal = (res, roomId, event) => {
|
|||
${hasAttachment(res)}
|
||||
<br>(id: ${res.id}) ${registrar.post.visibilityEmoji(res.visibility)}
|
||||
</blockquote>`;
|
||||
if (res.label == 'thread') thread(roomId, event, content, meta);
|
||||
else sendEventWithMeta(roomId, content, meta);
|
||||
sendEventWithMeta(roomId, content, meta);
|
||||
};
|
||||
|
||||
const isReblog = (res, roomId) => {
|
||||
|
@ -128,13 +101,11 @@ const isReblog = (res, roomId) => {
|
|||
|
||||
module.exports.sendEventWithMeta = sendEventWithMeta;
|
||||
|
||||
module.exports.thread = thread;
|
||||
|
||||
module.exports.formatter = (res, roomId, event) => {
|
||||
module.exports.formatter = (res, roomId) => {
|
||||
const filtered = (res.label === 'notifications')
|
||||
? notifyFormatter(res, roomId)
|
||||
: (res.reblog == null)
|
||||
? isOriginal(res, roomId, event)
|
||||
? isOriginal(res, roomId)
|
||||
: isReblog(res, roomId);
|
||||
return filtered;
|
||||
};
|
||||
|
@ -142,8 +113,8 @@ module.exports.formatter = (res, roomId, event) => {
|
|||
module.exports.follow = (roomId, account, event, original) => {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${config.fediverse[event.getSender()].domain}/api/v1/accounts/${account[0].id}/follow`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/follow`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
@ -158,8 +129,8 @@ module.exports.follow = (roomId, account, event, original) => {
|
|||
module.exports.unfollow = (roomId, account, event, original) => {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/accounts/${account[0].id}/unfollow`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/unfollow`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
})
|
||||
.then(() => {
|
||||
matrix.utils.addReact(event, '✅');
|
||||
|
@ -174,8 +145,8 @@ module.exports.unfollow = (roomId, account, event, original) => {
|
|||
module.exports.getStatusMentions = (notice, event) => {
|
||||
const users = axios({
|
||||
method: 'GET',
|
||||
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${notice}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||
url: `${config.fediverse.domain}/api/v1/statuses/${notice}`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
}).then((notice) => {
|
||||
const users = [];
|
||||
users.push('@' + notice.data.account.acct);
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
exports.runQuery = function (roomId) {
|
||||
matrixClient.sendHtmlNotice(roomId,
|
||||
' ',
|
||||
'',
|
||||
'<blockquote><b>fediverse commands<br>'
|
||||
+ '+post [your message] : post<br>'
|
||||
+ '+direct [@recipient] [message] : direct message<br>'
|
||||
+ '+private [message] : follower-only message<br>'
|
||||
+ '+redact [post id] : delete post<br>'
|
||||
+ '+follow [user id] : follow<br>'
|
||||
+ '+unfollow [user id] : unfollow<br>'
|
||||
+ '+media [homeserver image URL or MXC] [optional message] : post media<br>'
|
||||
+ '+copy [post id] : repeat/repost/retweet<br>'
|
||||
+ '+crossblog [status URL]: cross blog twitter post to fediverse post<br>'
|
||||
+ '+reply [post id] [content] : reply to post<br>'
|
||||
+ '+tip [@user@fedi.url] [amount] : tip 10grans<br>'
|
||||
+ '+makeitrain [post id] : make it rain 10grans<br>'
|
||||
+ '+beg : beg for 10grans<br>'
|
||||
+ '+clap [post id] : favorite<br>'
|
||||
+ '+boo [post id] : unfavorite</blockquote>'
|
||||
+ '<blockquote><b>channel commands<br>'
|
||||
+ '+flood : turn on timeline in channel<br>'
|
||||
+ '+notify : show notifications in channel<br>'
|
||||
+ '+unflood : stop timeline in channel<br>'
|
||||
+ '+unnotify : stop notifications in channel<br>'
|
||||
+ '+archive [URL] : archive content<br>'
|
||||
+ '+rearchive [URL] : re-archive content<br>'
|
||||
+ '+nitter [status URL] : redirect twitter to nitter, also embed tweet<br>'
|
||||
|
@ -27,7 +25,6 @@ exports.runQuery = function (roomId) {
|
|||
+ `<blockquote><b>ligh7hau5 version ${require('../package.json').version}</b><br>`
|
||||
+ '<b>--- <i>Contributors🐱</i> ---</b><br>'
|
||||
+ '<b>CRYPTOMOONERS</b><br>'
|
||||
+ '<b>doesnm</b><br>'
|
||||
+ '<b><i>docs by LINT</i></b></blockquote>'
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
const headers = ({ domain, userAgent }) => ({
|
||||
Host: `${domain}`,
|
||||
'User-Agent': `${userAgent}`,
|
||||
});
|
||||
|
||||
const invidious = async (instance, url) => {
|
||||
const req = await instance({ method: 'GET', url });
|
||||
if (req.statusText !== 'OK') throw req;
|
||||
const { headers } = instance.defaults;
|
||||
const video = JSON.parse(req.data);
|
||||
return {
|
||||
url: headers['Host'],
|
||||
name: video.title,
|
||||
date: video.publishedText,
|
||||
description: video.descriptionHtml,
|
||||
|
@ -15,8 +18,8 @@ const invidious = async (instance, url) => {
|
|||
};
|
||||
};
|
||||
|
||||
const card = (video, path) =>
|
||||
`<a href="https://${video.url}/${path}"><b>${video.name}</a></b><blockquote><b><i>` +
|
||||
const card = (video, base, path) =>
|
||||
`<a href="${base}/${path}"><b>${video.name}</a></b><blockquote><b><i>` +
|
||||
((video.description.length > 300) ? `${video.description.substr(0, 300)}…` : ``)+
|
||||
((video.description === '<p></p>') ? `No description.`: ``)+
|
||||
((video.description.length < 300 && video.description !== '<p></p>') ? `${video.description}` : ``)+
|
||||
|
@ -26,28 +29,21 @@ const card = (video, path) =>
|
|||
`<br />(${video.date})</b> <br />
|
||||
</blockquote>`;
|
||||
|
||||
const getInstance = (domain, config) =>
|
||||
axios.create({
|
||||
baseURL: `https://${domain}/api/v1/videos`,
|
||||
headers: {
|
||||
Host: `${domain}`,
|
||||
'User-Agent': `${config.userAgent}`,
|
||||
},
|
||||
const run = async (roomId, userInput) => {
|
||||
const instance = axios.create({
|
||||
baseURL: `https://${config.invidious.domain}/api/v1/videos/`,
|
||||
headers: headers(config.invidious),
|
||||
transformResponse: [],
|
||||
timeout: 10 * 1000,
|
||||
});
|
||||
|
||||
const run = async (roomId, userInput) => {
|
||||
const cfg = config.invidious;
|
||||
const video = await matrix.utils.retryPromise(cfg.domains.redirect, domain => invidious(getInstance(domain, cfg), userInput));
|
||||
return matrixClient.sendHtmlNotice(roomId, ' ', card(video, userInput));
|
||||
const video = await invidious(instance, userInput);
|
||||
return await matrixClient.sendHtmlNotice(roomId, '', card(video, `https://${config.invidious.domain}`, userInput));
|
||||
};
|
||||
|
||||
exports.runQuery = async (roomId, event, userInput) => {
|
||||
try {
|
||||
const url = new URL(userInput);
|
||||
const { redirect, original } = config.invidious.domains;
|
||||
if (!redirect.includes(url.hostname) && !original.includes(url.hostname)) throw '';
|
||||
if (!config.invidious.domains.includes(url.hostname)) throw '';
|
||||
if (/^\/[\w-]{11}$/.test(url.pathname)) return await run(roomId, url.pathname.slice(1));
|
||||
const params = new URLSearchParams(url.search).get('v');
|
||||
if (!/^[\w-]{11}$/.test(params)) throw '';
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
const { JSDOM } = require('jsdom');
|
||||
|
||||
const headers = ({ domain, userAgent }) => ({
|
||||
Host: `${domain}`,
|
||||
'User-Agent': `${userAgent}`,
|
||||
});
|
||||
|
||||
const nitter = async (instance, url) => {
|
||||
const req = await instance({ method: 'GET', url });
|
||||
if (req.statusText !== 'OK') throw req;
|
||||
|
@ -10,9 +15,7 @@ const nitter = async (instance, url) => {
|
|||
const quote = tweet.querySelector('.tweet-body > .quote');
|
||||
const isReply = tweet.querySelector('.tweet-body > .replying-to');
|
||||
const replies = document.querySelectorAll('.main-thread > .before-tweet > .timeline-item');
|
||||
const { defaults } = instance;
|
||||
return {
|
||||
url: defaults.baseURL,
|
||||
text: tweet.querySelector('.tweet-body > .tweet-content').innerHTML,
|
||||
date: tweet.querySelector('.tweet-body > .tweet-published').textContent,
|
||||
name: tweet.querySelector('.tweet-body > div .fullname').textContent,
|
||||
|
@ -27,10 +30,6 @@ const nitter = async (instance, url) => {
|
|||
path: replies[replies.length - 1].querySelector('a.tweet-link').href,
|
||||
text: replies[replies.length - 1].querySelector('.tweet-content').innerHTML,
|
||||
} : null,
|
||||
isThread: !isReply && replies.length > 0 ? replies[replies.length - 1].classList.contains('unavailable') ? 'unavailable' : {
|
||||
path: replies[replies.length - 1].querySelector('a.tweet-link').href,
|
||||
text: replies[replies.length - 1].querySelector('.tweet-content').innerHTML,
|
||||
} : null,
|
||||
stats: {
|
||||
replies: stats[0].textContent.trim(),
|
||||
retweets: stats[1].textContent.trim(),
|
||||
|
@ -39,49 +38,35 @@ const nitter = async (instance, url) => {
|
|||
};
|
||||
};
|
||||
|
||||
const card = (tweet, check, path) =>
|
||||
`<a href="${tweet.url}/${tweet.handle.replace(/^@/, '')}"><b>${tweet.name}</b></a> ` +
|
||||
const card = (tweet, base, check, path) =>
|
||||
`<a href="${base}/${tweet.handle.replace(/^@/, '')}"><b>${tweet.name}</b></a> ` +
|
||||
(tweet.check ? `${check} ` : '') +
|
||||
`<a href="${tweet.url}${path}"><b>${tweet.date}</b></a> ` +
|
||||
`<a href="${base}${path}"><b>${tweet.date}</b></a> ` +
|
||||
`<span>🗨️ ${tweet.stats.replies}</span> ` +
|
||||
`<span>🔁 ${tweet.stats.retweets}</span> ` +
|
||||
`<span>❤️ ${tweet.stats.favorites}</span> ` +
|
||||
`<br /><blockquote><b><i>${tweet.text.replace('\n', '<br />')}</i></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="${tweet.url}${tweet.isReply.path}">Replied Tweet</a></b><br /><b><i>${tweet.isReply.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
||||
(tweet.isThread ? tweet.isThread === 'unavailable' ? '<blockquote>Previous Tweet is unavailable</blockquote>' : `<blockquote><b><a href="${tweet.url}${tweet.isThread.path}">Previous Tweet</a></b><br /><b><i>${tweet.isThread.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
||||
(tweet.quote ? `<blockquote><b><a href="${tweet.url}${tweet.quote.path}">Quoted Tweet</a></b><br /><b><i>${tweet.quote.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>` : '');
|
||||
|
||||
const getInstance = (domain, config) =>
|
||||
axios.create({
|
||||
baseURL: `https://${domain}`,
|
||||
headers: {
|
||||
Host: `${domain}`,
|
||||
'User-Agent': `${config.userAgent}`,
|
||||
},
|
||||
const run = async (roomId, userInput) => {
|
||||
const instance = axios.create({
|
||||
baseURL: `https://${config.nitter.domain}`,
|
||||
headers: headers(config.nitter),
|
||||
transformResponse: [],
|
||||
timeout: 10 * 1000,
|
||||
});
|
||||
|
||||
const run = async (roomId, userInput, fedi) => {
|
||||
const cfg = config.nitter;
|
||||
const tweet = await matrix.utils.retryPromise(cfg.domains.redirect, domain => nitter(getInstance(domain, cfg), userInput));
|
||||
const tweetCard = card(tweet, cfg.check, userInput);
|
||||
return fedi ? axios({
|
||||
method: 'POST',
|
||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
||||
data: { status: tweetCard, content_type: 'text/html' }
|
||||
}) : matrixClient.sendHtmlNotice(roomId, ' ', tweetCard);
|
||||
const tweet = await nitter(instance, userInput);
|
||||
return await matrixClient.sendHtmlNotice(roomId, '', card(tweet, `https://${config.nitter.domain}`, config.nitter.check, userInput));
|
||||
};
|
||||
|
||||
exports.runQuery = async (roomId, event, userInput, fedi) => {
|
||||
exports.runQuery = async (roomId, event, userInput) => {
|
||||
try {
|
||||
const url = new URL(userInput);
|
||||
const { redirect, original } = config.nitter.domains;
|
||||
if (!redirect.includes(url.hostname) && !original.includes(url.hostname)) throw '';
|
||||
if (!config.nitter.domains.includes(url.hostname)) throw '';
|
||||
if (!/^\/[^/]+\/status\/\d+\/?$/.test(url.pathname)) throw '';
|
||||
return await run(roomId, url.pathname, fedi);
|
||||
return await run(roomId, url.pathname);
|
||||
} catch (e) {
|
||||
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||
}
|
||||
|
|
36
config.example.js
Normal file
36
config.example.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
module.exports = {
|
||||
matrix: {
|
||||
domain: 'https://your_homeserver.com',
|
||||
user: '@your_user:your_homeserver.com',
|
||||
password: 'your_password',
|
||||
domains: [ 'your_homeserver.com' ],
|
||||
manualVerify: false,
|
||||
},
|
||||
fediverse: {
|
||||
domain: 'https://your_federation.com',
|
||||
username: '',
|
||||
password: '',
|
||||
client_name: 'ligh7hau5',
|
||||
subject: '',
|
||||
tipping: false,
|
||||
mimetypes: {
|
||||
whitelist: [],
|
||||
blacklist: []
|
||||
}
|
||||
},
|
||||
archive: {
|
||||
domain: 'archive.is',
|
||||
userAgent: 'Mozilla/4.0 (compatible; Beep Boop)'
|
||||
},
|
||||
nitter: {
|
||||
domain: 'nitter.fdn.fr',
|
||||
userAgent: 'Mozilla/4.0 (compatible; Beep Boop)',
|
||||
domains: [ 'nitter.net', 'www.nitter.net', 'twitter.com', 'www.twitter.com', 'mobile.twitter.com', 'm.twitter.com' ],
|
||||
check: '(✅)'
|
||||
},
|
||||
invidious: {
|
||||
domain: 'invidious.fdn.fr',
|
||||
userAgent: 'Mozilla/4.0 (compatible; Beep Boop)',
|
||||
domains: [ 'invidious.snopyta.org', 'invidious.xyz', 'youtube.com', 'www.youtube.com', 'youtu.be', 'm.youtube.com' ]
|
||||
}
|
||||
};
|
9
main.js
9
main.js
|
@ -1,7 +1,7 @@
|
|||
global.registrar = require('./registrar.js');
|
||||
|
||||
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) => {
|
||||
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
|
||||
|
@ -18,23 +18,22 @@ matrixClient.on('RoomMember.membership', (event, member) => {
|
|||
});
|
||||
|
||||
matrixClient.on('event', async (event) => {
|
||||
if (event.isEncrypted()) await matrixClient.decryptEventIfNeeded(event, { emit: false, isRetry: false });
|
||||
if (event.getSender() === matrixClient.credentials.userId) return matrix.utils.selfReact(event);
|
||||
if (!event.event.content['m.relates_to']) return;
|
||||
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) => {
|
||||
matrixClient.setGlobalErrorOnUnknownDevices(config.matrix.manualVerify);
|
||||
if (toStartOfTimeline) return;
|
||||
if (event.isEncrypted()) await matrixClient.decryptEventIfNeeded(event, { emit: false, isRetry: false });
|
||||
if (event.isEncrypted()) await event._decryptionPromise;
|
||||
if (event.getType() !== 'm.room.message') return;
|
||||
if (event.getSender() === matrixClient.credentials.userId) return;
|
||||
if (event.event.unsigned.age > 10000) return;
|
||||
roomId = event.event.room_id;
|
||||
content = event.getContent().body;
|
||||
if (!typeof content === 'string') return;
|
||||
if (content.charAt(0) === '+') {
|
||||
const args = content.slice(1).trim().split(/ +/g);
|
||||
const command = args.shift().toLowerCase();
|
||||
|
|
1027
package-lock.json
generated
1027
package-lock.json
generated
File diff suppressed because it is too large
Load diff
19
package.json
19
package.json
|
@ -1,10 +1,7 @@
|
|||
{
|
||||
"name": "ligh7hau5",
|
||||
"version": "3.0.1",
|
||||
"version": "1.2.2",
|
||||
"description": "A Matrix to Fediverse client",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
|
@ -21,13 +18,13 @@
|
|||
},
|
||||
"homepage": "https://github.com/vulet/lighthau5#readme",
|
||||
"dependencies": {
|
||||
"axios": "^0.25.0",
|
||||
"form-data": "^4.0.0",
|
||||
"jsdom": "^19.0.0",
|
||||
"matrix-js-sdk": "^27.2.0",
|
||||
"node-localstorage": "^2.2.1",
|
||||
"olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
|
||||
"qs": "^6.11.2"
|
||||
"axios": "^0.21.1",
|
||||
"form-data": "^3.0.0",
|
||||
"jsdom": "^16.4.0",
|
||||
"matrix-js-sdk": "^9.7.0",
|
||||
"node-localstorage": "^2.1.6",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"qs": "^6.9.6"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
19
registrar.js
19
registrar.js
|
@ -3,16 +3,16 @@ global.sdk = require('matrix-js-sdk');
|
|||
global.axios = require('axios');
|
||||
global.config = require('./config.js');
|
||||
global.auth = require('./auth.js');
|
||||
global.authEvents = [];
|
||||
|
||||
const { LocalStorage } = require('node-localstorage');
|
||||
global.localStorage = new LocalStorage('./keys');
|
||||
if (!localStorage.getItem('matrix_auth')) {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('matrix_auth', '[]');
|
||||
}
|
||||
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', '{}');
|
||||
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', '{}');
|
||||
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', '{}');
|
||||
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', '[]');
|
||||
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', '[]');
|
||||
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', '[]');
|
||||
|
||||
|
||||
global.fediverse = {
|
||||
|
@ -27,25 +27,24 @@ global.matrix = {
|
|||
module.exports = {
|
||||
config: require('./config.js'),
|
||||
archive: require('./commands/archive.js'),
|
||||
proxy: require('./commands/proxy.js'),
|
||||
invidious: require('./commands/invidious.js'),
|
||||
nitter: require('./commands/nitter.js'),
|
||||
beg: require('./commands/fediverse/beg.js'),
|
||||
boo: require('./commands/fediverse/boo.js'),
|
||||
clap: require('./commands/fediverse/clap.js'),
|
||||
copy: require('./commands/fediverse/copy.js'),
|
||||
flood: require('./commands/fediverse/flood.js'),
|
||||
follow: require('./commands/fediverse/follow.js'),
|
||||
help: require('./commands/help.js'),
|
||||
makeitrain: require('./commands/fediverse/makeitrain.js'),
|
||||
mordy: require('./commands/fediverse/mordy.js'),
|
||||
notify: require('./commands/fediverse/notify.js'),
|
||||
pin: require('./commands/fediverse/pin.js'),
|
||||
post: require('./commands/fediverse/post.js'),
|
||||
redact: require('./commands/fediverse/redact.js'),
|
||||
status: require('./commands/fediverse/status.js'),
|
||||
tip: require('./commands/fediverse/tip.js'),
|
||||
unfollow: require('./commands/fediverse/unfollow.js'),
|
||||
unpin: require('./commands/fediverse/unpin.js'),
|
||||
unreblog: require('./commands/fediverse/unreblog.js'),
|
||||
unroll: require('./commands/fediverse/unroll.js'),
|
||||
react: require('./commands/fediverse/react.js'),
|
||||
expand: require('./commands/expand.js'),
|
||||
auth: require("./commands/fediverse/auth.js")
|
||||
unreblog: require('./commands/fediverse/unreblog.js')
|
||||
};
|
||||
|
|
136
utils.js
136
utils.js
|
@ -1,13 +1,9 @@
|
|||
const { MatrixEvent } = require('matrix-js-sdk/lib/models/event');
|
||||
const url = require("url")
|
||||
const isEmoji = string => true;
|
||||
|
||||
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);
|
||||
'', error);
|
||||
};
|
||||
|
||||
const addReact = async (event, key) => {
|
||||
|
@ -27,7 +23,6 @@ const eventHandler = (args, roomId, command, event) => {
|
|||
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
||||
|
||||
args = [];
|
||||
let visibility = null;
|
||||
|
||||
switch (command) {
|
||||
case 'config':
|
||||
|
@ -35,14 +30,6 @@ const eventHandler = (args, roomId, command, event) => {
|
|||
case 'help': case 'flood': case 'notify':
|
||||
args.push(roomId);
|
||||
break;
|
||||
case 'unflood': case 'unnotify':
|
||||
args.push(roomId, true);
|
||||
command = command.substring(2);
|
||||
break;
|
||||
case 'unreact':
|
||||
args.push(roomId, event, userInput, true);
|
||||
command = 'react';
|
||||
break;
|
||||
case 'tip': case 'makeitrain':
|
||||
args.push(roomId, event, address, flaggedInput);
|
||||
break;
|
||||
|
@ -52,52 +39,35 @@ const eventHandler = (args, roomId, command, event) => {
|
|||
break;
|
||||
case 'post': case 'reply': case 'media': case 'mediareply':
|
||||
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
|
||||
case 'direct': case 'directreply': case 'directmedia': case 'directmediareply':
|
||||
case 'private': case 'privatereply': case 'privatemedia': case 'privatemediareply':
|
||||
case 'unlisted': case 'unlistedreply': case 'unlistedmedia': case 'unlistedmediareply':
|
||||
visibility = command.match(/^(direct|private|unlisted)/);
|
||||
args.push(roomId, event, userInput, {
|
||||
isReply: !!~command.indexOf('reply'),
|
||||
hasMedia: !!~command.indexOf('media'),
|
||||
hasSubject: !!~command.indexOf('random'),
|
||||
visibility: visibility ? visibility[1] : null
|
||||
});
|
||||
command = 'post';
|
||||
break;
|
||||
case 'crossblog':
|
||||
args.push(roomId, event, userInput, true);
|
||||
command = 'nitter'
|
||||
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);
|
||||
}
|
||||
if(["boo","clap","copy","flood","follow","notify","pin","unpin","post","react","redact","status","unfollow","unreblog","unroll"].includes(command) && !fediverse.auth[event.getSender()]) return matrixClient.sendHtmlNotice(roomId, ' ',`${event.getSender()}, для использования команды ${command} нужно привязать аккаунт Fediverse. Используйте для этого команду +auth <имя сервера>`)
|
||||
registrar[command] && registrar[command].runQuery.apply(null, args);
|
||||
};
|
||||
|
||||
/**
|
||||
matrixClient.fetchRoomEvent() does not return an Event class
|
||||
however, this class is necessary for decryption, so reinstate it.
|
||||
afterwards, decrypt.
|
||||
*/
|
||||
const fetchEncryptedOrNot = async (roomId, event) => {
|
||||
const fetchedEvent = await matrixClient.fetchRoomEvent(roomId, event.event_id)
|
||||
const realEvent = new MatrixEvent(fetchedEvent);
|
||||
if (realEvent.isEncrypted()) {
|
||||
await matrixClient.decryptEventIfNeeded(realEvent, { emit: false, isRetry: false });
|
||||
}
|
||||
return realEvent;
|
||||
}
|
||||
|
||||
module.exports.sendError = sendError;
|
||||
|
||||
module.exports.addReact = addReact;
|
||||
|
||||
module.exports.eventHandler = eventHandler;
|
||||
|
||||
module.exports.fetchEncryptedOrNot = fetchEncryptedOrNot
|
||||
|
||||
module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sendMessage(roomId, {
|
||||
body: ` * ${plain || html.replace(/<[^<]+?>/g, '')}`,
|
||||
formatted_body: ` * ${html}`,
|
||||
|
@ -116,55 +86,31 @@ module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sen
|
|||
});
|
||||
|
||||
module.exports.handleReact = async (event) => {
|
||||
const reactions = config.matrix.reactions;
|
||||
const roomId = event.event.room_id;
|
||||
if (!event.getContent()['m.relates_to']) return;
|
||||
const reaction = event.getContent()['m.relates_to'];
|
||||
const metaEvent = await fetchEncryptedOrNot(roomId, reaction);
|
||||
if (!metaEvent.getContent().meta || metaEvent.event.sender !== config.matrix.user) return;
|
||||
let args = metaEvent.getContent().meta.split(' ');
|
||||
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();
|
||||
switch (reaction.key) {
|
||||
case reactions.copy: command = 'copy'; break;
|
||||
case reactions.clap: command = 'clap'; break;
|
||||
case reactions.redact: command = 'redact'; break;
|
||||
case reactions.rain: command = 'makeitrain'; break;
|
||||
case reactions.unroll: command = 'unroll'; break;
|
||||
case reactions.expand:
|
||||
command = 'expand';
|
||||
args = [ reaction.event_id ];
|
||||
break;
|
||||
default:
|
||||
if (isEmoji(reaction.key)) {
|
||||
command = 'react';
|
||||
args.push(reaction.key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (reaction.key === '🔁') command = 'copy';
|
||||
if (reaction.key === '👏') command = 'clap';
|
||||
if (reaction.key === '🗑️️') command = 'redact';
|
||||
if (reaction.key === '🌧️') command = 'makeitrain';
|
||||
eventHandler(args, roomId, command, event);
|
||||
};
|
||||
|
||||
module.exports.handleReply = async (event) => {
|
||||
const roomId = event.event.room_id;
|
||||
if(!event.event.content['m.relates_to']['m.in_reply_to']) return;
|
||||
const reply = event.event.content['m.relates_to']['m.in_reply_to'];
|
||||
const metaEvent = await fetchEncryptedOrNot(roomId, reply);
|
||||
if(authEvents.includes(metaEvent.event_id)){
|
||||
const domain = metaEvent.event.content.body.match(/https?:\/\/[^\s]+/);
|
||||
if(domain && domain[0]){
|
||||
domain[0] = url.parse(domain[0]).host
|
||||
let code = event.getContent().body.split("\n");
|
||||
code = code[code.length-1].trim()
|
||||
auth.obtainAccessToken(domain[0],code,event)
|
||||
authEvents = authEvents.filter(f => f != event.event_id)
|
||||
}
|
||||
}
|
||||
if (!metaEvent.getContent().meta || metaEvent.event.sender !== config.matrix.user) return;
|
||||
const args = metaEvent.getContent().meta.split(' ');
|
||||
args.push(event.getContent().formatted_body.trim().split('</mx-reply>')[1]);
|
||||
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();
|
||||
|
@ -173,39 +119,15 @@ if(domain && domain[0]){
|
|||
};
|
||||
|
||||
module.exports.selfReact = async (event) => {
|
||||
const reactions = config.matrix.reactions;
|
||||
if (event.getType() !== 'm.room.message') return;
|
||||
if (event.event.unsigned.age > 10000) return;
|
||||
if (!event.getContent().meta) return;
|
||||
const { meta } = event.getContent();
|
||||
const type = meta.split(' ')[0];
|
||||
if (type === 'redact' || type === 'unreblog')
|
||||
addReact(event, reactions.redact);
|
||||
if (type === 'status' || type === 'reblog' || type === 'mention')
|
||||
addReact(event, reactions.expand);
|
||||
};
|
||||
|
||||
module.exports.expandReact = async (event) => {
|
||||
const reactions = config.matrix.reactions;
|
||||
if (event.getSender() !== matrixClient.credentials.userId) return;
|
||||
if (!event.getContent().meta) 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, reactions.unroll);
|
||||
addReact(event, reactions.copy);
|
||||
addReact(event, reactions.clap);
|
||||
if (config.fediverse.tipping)
|
||||
addReact(event, reactions.rain);
|
||||
addReact(event, '🔁');
|
||||
addReact(event, '👏');
|
||||
if (config.fediverse.tipping === true) addReact(event, '🌧️');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.retryPromise = async (argList, promiseFn) => {
|
||||
let err;
|
||||
for(var arg of argList) {
|
||||
try {
|
||||
return await promiseFn(arg);
|
||||
} catch(e) { err = e; }
|
||||
}
|
||||
throw err || new Error('retryPromise error');
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue