Compare commits

..

No commits in common. "master" and "1.3.0" have entirely different histories.

35 changed files with 858 additions and 1824 deletions

85
auth.js
View file

@ -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;

View file

@ -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));

View file

@ -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(() => {});
};

View file

@ -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
View 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);
});
};

View file

@ -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, '✅');

View file

@ -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, '✅');

View file

@ -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, '✅');

View file

@ -1,23 +1,12 @@
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;
@ -29,8 +18,7 @@ exports.runQuery = function (roomId, disable) {
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);

View file

@ -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;

View 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>`)
});
};

View 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);
});
};

View file

@ -1,23 +1,12 @@
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;
@ -29,8 +18,7 @@ exports.runQuery = function (roomId, disable) {
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);

View file

@ -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, '✅');

View file

@ -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(() => {});
}
};

View file

@ -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(() => {});
}
};

View file

@ -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, '✅');

View file

@ -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
View 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);
});
};

View file

@ -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;

View file

@ -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, '✅');

View file

@ -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, '✅');

View file

@ -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);
});
};

View file

@ -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>';
@ -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);

View file

@ -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>'
);
};

View file

@ -40,7 +40,7 @@ const getInstance = (domain, config) =>
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));
return matrixClient.sendHtmlNotice(roomId, '', card(video, userInput));
};
exports.runQuery = async (roomId, event, userInput) => {

View file

@ -63,25 +63,19 @@ const getInstance = (domain, config) =>
timeout: 10 * 1000,
});
const run = async (roomId, userInput, fedi) => {
const run = async (roomId, userInput) => {
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);
return matrixClient.sendHtmlNotice(roomId, '', card(tweet, cfg.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 (!/^\/[^/]+\/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(() => {});
}

4
commands/proxy.js Normal file
View file

@ -0,0 +1,4 @@
const msg = 'Invalid proxy domain!';
exports.runQuery = (roomId, event, userInput) =>
matrixClient.sendHtmlNotice(roomId, msg, `<b>${msg}</b>`).catch(() => {});

40
config.example.js Normal file
View file

@ -0,0 +1,40 @@
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: {
userAgent: 'Mozilla/4.0 (compatible; Beep Boop)',
domains: {
redirect: [ 'nitter.fdn.fr', 'nitter.snopyta.org', 'nitter.net' ],
original: [ 'nitter.net', 'www.nitter.net', 'twitter.com', 'www.twitter.com', 'mobile.twitter.com', 'm.twitter.com' ]
},
check: '(✅)',
},
invidious: {
userAgent: 'Mozilla/4.0 (compatible; Beep Boop)',
domains: {
redirect: [ 'invidious.fdn.fr', 'invidious.snopyta.org', 'invidious.xyz', 'inv.riverside.rocks', 'vid.puffyan.us', 'y.com.cm' ],
original: [ 'youtube.com', 'www.youtube.com', 'youtu.be', 'm.youtube.com' ]
}
}
};

10
main.js
View file

@ -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,23 @@ matrixClient.on('RoomMember.membership', (event, member) => {
});
matrixClient.on('event', async (event) => {
if (event.isEncrypted()) await matrixClient.decryptEventIfNeeded(event, { emit: false, isRetry: false });
if (event.isEncrypted()) await event._decryptionPromise;
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

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,7 @@
{
"name": "ligh7hau5",
"version": "3.0.1",
"version": "2.0.0",
"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",
"axios": "^0.21.1",
"form-data": "^3.0.0",
"jsdom": "^16.6.0",
"matrix-js-sdk": "^9.11.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"
"olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.4.tgz",
"qs": "^6.10.1"
},
"devDependencies": {}
}

View file

@ -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 = {
@ -30,22 +30,22 @@ module.exports = {
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')
};

128
utils.js
View file

@ -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,37 @@ 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': case 'p':
try {
const url = new URL(userInput);
const invidio = config.invidious.domains;
const nitter = config.nitter.domains;
command = invidio.redirect.includes(url.hostname) || invidio.original.includes(url.hostname)
? 'invidious'
: nitter.redirect.includes(url.hostname) || nitter.original.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 +88,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,30 +121,16 @@ 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, '🌧️');
}
};

925
yarn.lock

File diff suppressed because it is too large Load diff