Compare commits
26 commits
Author | SHA1 | Date | |
---|---|---|---|
73dac81f2d | |||
|
296cdc75a6 | ||
|
4cb8d1da7e | ||
|
17c7f819ac | ||
|
12c422c324 | ||
|
8e2ce18f26 | ||
|
5924009154 | ||
|
3122361c6c | ||
|
3b16a0495c | ||
|
35cec7751f | ||
|
9067ae600b | ||
|
25e49ffc78 | ||
|
ccdcf69bea | ||
|
dded657009 | ||
|
06e0ce26a9 | ||
|
6273452876 | ||
|
44e4138b80 | ||
|
be3e68f221 | ||
|
b7e73fc533 | ||
|
2f58d6bb84 | ||
|
472552c33b | ||
|
54631f5c2a | ||
|
c7e3f26f60 | ||
|
a371b7c501 | ||
|
39a88b2722 | ||
|
340fed6346 |
34 changed files with 1961 additions and 887 deletions
85
auth.js
85
auth.js
|
@ -1,12 +1,10 @@
|
||||||
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||||
|
|
||||||
const matrixTokenLogin = async () => {
|
const matrixTokenLogin = async () => {
|
||||||
matrixClient = sdk.createClient({
|
matrixClient = sdk.createClient({
|
||||||
baseUrl: config.matrix.domain,
|
baseUrl: config.matrix.domain,
|
||||||
accessToken: matrix.auth.access_token,
|
accessToken: matrix.auth.access_token,
|
||||||
userId: matrix.auth.user_id,
|
userId: matrix.auth.user_id,
|
||||||
deviceId: matrix.auth.device_id,
|
deviceId: matrix.auth.device_id,
|
||||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
|
||||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||||
});
|
});
|
||||||
matrixClient.initCrypto()
|
matrixClient.initCrypto()
|
||||||
|
@ -18,6 +16,7 @@ const matrixTokenLogin = async () => {
|
||||||
+ '====================================================',
|
+ '====================================================',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
matrixClient.setGlobalErrorOnUnknownDevices(config.matrix.manualVerify);
|
||||||
matrixClient.startClient();
|
matrixClient.startClient();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -25,7 +24,7 @@ const matrixTokenLogin = async () => {
|
||||||
module.exports.matrixTokenLogin = matrixTokenLogin;
|
module.exports.matrixTokenLogin = matrixTokenLogin;
|
||||||
|
|
||||||
module.exports.getMatrixToken = async () => {
|
module.exports.getMatrixToken = async () => {
|
||||||
matrixClient = sdk.createClient(config.matrix.domain);
|
matrixClient = sdk.createClient({ baseUrl: config.matrix.domain });
|
||||||
matrixClient.loginWithPassword(config.matrix.user, config.matrix.password)
|
matrixClient.loginWithPassword(config.matrix.user, config.matrix.password)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
matrix.auth = {
|
matrix.auth = {
|
||||||
|
@ -40,31 +39,75 @@ module.exports.getMatrixToken = async () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.registerFediverseApp = async () => {
|
const getFediverseLink = (domain,roomId) => {
|
||||||
axios.post(`${config.fediverse.domain}/api/v1/apps`,
|
let apps = {}
|
||||||
|
apps = JSON.parse(localStorage.getItem("apps"));
|
||||||
|
if(!apps[domain]){
|
||||||
|
axios.post(`https://${domain}/api/v1/apps`,
|
||||||
{
|
{
|
||||||
client_name: config.fediverse.client_name,
|
client_name: config.fediverse.client_name,
|
||||||
redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
|
redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
|
||||||
scopes: 'read write follow push',
|
scopes: 'read write follow push',
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
axios.post(`${config.fediverse.domain}/oauth/token`,
|
console.log(response.data)
|
||||||
{
|
if(!response.data.client_id || !response.data.client_secret) return false;
|
||||||
username: config.fediverse.username,
|
apps[domain] = {
|
||||||
password: config.fediverse.password,
|
client_id: response.data.client_id,
|
||||||
client_id: response.data.client_id,
|
client_secret: response.data.client_secret
|
||||||
client_secret: response.data.client_secret,
|
}
|
||||||
scope: 'read write follow push',
|
localStorage.setItem("apps",JSON.stringify(apps))
|
||||||
grant_type: 'password',
|
matrixClient.sendHtmlNotice(roomId,"Приложение зарегистрировано. Введите команду еще раз для создания ссылки")
|
||||||
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
// return getFediverseLink(domain)
|
||||||
})
|
|
||||||
.then((tokens) => {
|
|
||||||
localStorage.setItem('fediverse_auth', JSON.stringify(tokens.data, null, 2));
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.log(e);
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.log(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,5 +1,6 @@
|
||||||
const { JSDOM } = require('jsdom');
|
const { JSDOM } = require('jsdom');
|
||||||
const qs = require('qs');
|
const qs = require('qs');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ const arc3Str = str => `<em>Timed out <code>${str}</code></em>`;
|
||||||
const run = async (roomId, userInput, rearchive) => {
|
const run = async (roomId, userInput, rearchive) => {
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: `https://${config.archive.domain}`,
|
baseURL: `https://${config.archive.domain}`,
|
||||||
|
httpsAgent: https.Agent({ maxVersion: "TLSv1.2"}),
|
||||||
headers: headers(config.archive),
|
headers: headers(config.archive),
|
||||||
transformResponse: [],
|
transformResponse: [],
|
||||||
timeout: 10 * 1000
|
timeout: 10 * 1000
|
||||||
|
@ -42,7 +44,7 @@ const run = async (roomId, userInput, rearchive) => {
|
||||||
|
|
||||||
let reply = null;
|
let reply = null;
|
||||||
try {
|
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);
|
const { refresh, id, title, date } = await archive(instance, userInput, rearchive);
|
||||||
if (id)
|
if (id)
|
||||||
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
return await matrix.utils.editNoticeHTML(roomId, reply, arc2Str(`${config.archive.domain}${id}`, title, date));
|
||||||
|
|
9
commands/expand.js
Normal file
9
commands/expand.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
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(() => {});
|
||||||
|
};
|
||||||
|
|
12
commands/fediverse/auth.js
Normal file
12
commands/fediverse/auth.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
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}`)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
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) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unfavourite`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unfavourite`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/favourite`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/favourite`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/reblog`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/reblog`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,23 +1,36 @@
|
||||||
exports.runQuery = function (roomId) {
|
let intervalId = null;
|
||||||
setInterval(() => {
|
|
||||||
|
exports.runQuery = function (roomId, disable) {
|
||||||
|
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
intervalId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disable) return;
|
||||||
|
|
||||||
|
intervalId = setInterval(() => {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/timelines/home`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/timelines/home`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
let past = JSON.parse(localStorage.getItem('timeline'));
|
let timeline = JSON.parse(localStorage.getItem('timeline'));
|
||||||
|
past = timeline[event.getSender()] || {}
|
||||||
if (past.length === 0) past = res.data;
|
if (past.length === 0) past = res.data;
|
||||||
const events = res.data;
|
const events = res.data;
|
||||||
const len = events.length;
|
const len = events.length;
|
||||||
for (let i = len - 1; i >= 0; i--) {
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||||
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
const lastStored = past.slice(past.length - 1, past.length);
|
||||||
|
if (events[i].created_at < lastStored[0].created_at) return;
|
||||||
events[i].label = 'status';
|
events[i].label = 'status';
|
||||||
fediverse.utils.formatter(events[i], roomId);
|
fediverse.utils.formatter(events[i], roomId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
localStorage.setItem('timeline', JSON.stringify(events, null, 2));
|
timeline[event.getSender()] = events
|
||||||
|
localStorage.setItem('timeline', JSON.stringify(timeline, null, 2));
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrix.utils.sendError(null, roomId, e);
|
matrix.utils.sendError(null, roomId, e);
|
||||||
|
|
|
@ -5,8 +5,8 @@ exports.runQuery = async function (roomId, event, userInput) {
|
||||||
const suggest = [];
|
const suggest = [];
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
}).then((findUserId) => {
|
}).then((findUserId) => {
|
||||||
const results = findUserId.data.accounts;
|
const results = findUserId.data.accounts;
|
||||||
const len = results.length;
|
const len = results.length;
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
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>`)
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
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,23 +1,36 @@
|
||||||
exports.runQuery = function (roomId) {
|
let intervalId = null;
|
||||||
setInterval(() => {
|
|
||||||
|
exports.runQuery = function (roomId, disable) {
|
||||||
|
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
intervalId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disable) return;
|
||||||
|
|
||||||
|
intervalId = setInterval(() => {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/notifications`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/notifications`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
let past = JSON.parse(localStorage.getItem('notifications'));
|
let notifications = JSON.parse(localStorage.getItem('notifications'));
|
||||||
|
let past = notifications[event.getSender()] || {}
|
||||||
if (past.length === 0) past = res.data;
|
if (past.length === 0) past = res.data;
|
||||||
const events = res.data;
|
const events = res.data;
|
||||||
const len = events.length;
|
const len = events.length;
|
||||||
for (let i = len - 1; i >= 0; i--) {
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
if (past.findIndex((x) => x.created_at === events[i].created_at) === -1) {
|
||||||
if (events[i].created_at < past.slice(18, 19)[0].created_at) return;
|
const lastStored = past.slice(past.length - 1, past.length);
|
||||||
|
if (events[i].created_at < lastStored[0].created_at) return;
|
||||||
events[i].label = 'notifications';
|
events[i].label = 'notifications';
|
||||||
fediverse.utils.formatter(events[i], roomId);
|
fediverse.utils.formatter(events[i], roomId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
localStorage.setItem('notifications', JSON.stringify(events, null, 2));
|
notifications[event.getSender()] = events
|
||||||
|
localStorage.setItem('notifications', JSON.stringify(notifications, null, 2));
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
matrix.utils.sendError(null, roomId, e);
|
matrix.utils.sendError(null, roomId, e);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/pin`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/pin`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,9 +1,61 @@
|
||||||
const qs = require('qs');
|
const qs = require('qs');
|
||||||
|
const crypto = require('crypto');
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
|
|
||||||
const emojis = { public: '🌐', unlisted: '📝', private: '🔒️', direct: '✉️' };
|
const emojis = { public: '🌐', unlisted: '📝', private: '🔒️', direct: '✉️' };
|
||||||
exports.visibilityEmoji = (v) => emojis[v] || v;
|
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) => {
|
const getFilename = (header) => {
|
||||||
if (typeof header !== 'string') return null;
|
if (typeof header !== 'string') return null;
|
||||||
try {
|
try {
|
||||||
|
@ -14,15 +66,14 @@ const getFilename = (header) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const mediaDownload = async (url, { whitelist, blacklist }) => {
|
const mediaDownload = async (opts) => {
|
||||||
const media = await axios({ method: 'GET', url, responseType: 'arraybuffer' });
|
const { whitelist, blacklist } = config.fediverse.mimetypes;
|
||||||
if (media.statusText !== 'OK' || blacklist.includes(media.headers['content-type'])) throw media;
|
const media = await axios({ method: 'GET', url: opts.url, responseType: 'arraybuffer' });
|
||||||
if (whitelist.length && !whitelist.includes(media.headers['content-type'])) throw media;
|
const filename = opts.filename || getFilename(media.headers['content-disposition']);
|
||||||
return {
|
const mimetype = opts.mimetype || media.headers['content-type'];
|
||||||
data: media.data,
|
if (media.statusText !== 'OK' || blacklist.includes(mimetype)) throw media;
|
||||||
filename: getFilename(media.headers['content-disposition']),
|
if (whitelist.length && !whitelist.includes(mimetype)) throw media;
|
||||||
mimetype: media.headers['content-type'],
|
return { data: media.data, filename, mimetype };
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
||||||
|
@ -33,28 +84,29 @@ const mediaUpload = async ({ domain }, { data, filename, mimetype }) => {
|
||||||
});
|
});
|
||||||
const upload = await axios({
|
const upload = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${domain}/api/v1/media`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/media`,
|
||||||
headers: form.getHeaders({ Authorization: `Bearer ${fediverse.auth.access_token}` }),
|
headers: form.getHeaders({ Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` }),
|
||||||
data: form,
|
data: form,
|
||||||
});
|
});
|
||||||
if (upload.statusText !== 'OK') throw upload;
|
if (upload.statusText !== 'OK') throw upload;
|
||||||
return upload.data.id;
|
return upload.data.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
const run = async (roomId, event, content, replyId, mediaURL, subject) => {
|
const run = async (roomId, event, content, replyId, mediaURL, subject, visibility) => {
|
||||||
let mediaId = null;
|
let mediaId = null;
|
||||||
if (mediaURL) {
|
if (mediaURL) {
|
||||||
const media = await mediaDownload(mediaURL, config.fediverse.mimetypes);
|
const media = await getMedia(roomId, mediaURL);
|
||||||
mediaId = await mediaUpload(config.fediverse, media);
|
mediaId = await mediaUpload(config.fediverse, media);
|
||||||
}
|
}
|
||||||
if (replyId) content = await fediverse.utils.getStatusMentions(replyId, event).then(m => m.concat(content).join(' '));
|
if (replyId) content = await fediverse.utils.getStatusMentions(replyId, event).then(m => m.concat(content).join(' '));
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
data: qs.stringify({
|
data: qs.stringify({
|
||||||
status: content,
|
status: content,
|
||||||
content_type: 'text/markdown',
|
content_type: 'text/markdown',
|
||||||
|
visibility: visibility || undefined,
|
||||||
media_ids: mediaURL && [mediaId] || undefined,
|
media_ids: mediaURL && [mediaId] || undefined,
|
||||||
in_reply_to_id: replyId || undefined,
|
in_reply_to_id: replyId || undefined,
|
||||||
spoiler_text: subject || undefined,
|
spoiler_text: subject || undefined,
|
||||||
|
@ -63,29 +115,18 @@ const run = async (roomId, event, content, replyId, mediaURL, subject) => {
|
||||||
return fediverse.utils.sendEventWithMeta(roomId, `<a href="${response.data.url}">${response.data.id}</a>`, `redact ${response.data.id}`);
|
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 }) => {
|
exports.runQuery = async (roomId, event, userInput, { isReply, hasMedia, hasSubject, visibility }) => {
|
||||||
try {
|
try {
|
||||||
const chunks = userInput.trim().split(' ');
|
const chunks = userInput.trim().split(' ');
|
||||||
if (!chunks.length || chunks.length < !!isReply + !!hasMedia) throw '';
|
if (!chunks.length || chunks.length < !!isReply + !!hasMedia) throw '';
|
||||||
let replyId = null;
|
let replyId = null;
|
||||||
let mediaURL = null;
|
let mediaURL = null;
|
||||||
const subject = hasSubject ? config.fediverse.subject : null;
|
const subject = hasSubject ? config.fediverse.subject : null;
|
||||||
if (isReply) {
|
if (isReply) replyId = chunks.shift();
|
||||||
replyId = chunks[0];
|
if (hasMedia) mediaURL = chunks.shift();
|
||||||
chunks.shift();
|
return await run(roomId, event, chunks.join(' '), replyId, mediaURL, subject, visibility);
|
||||||
}
|
|
||||||
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) {
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
26
commands/fediverse/react.js
vendored
Normal file
26
commands/fediverse/react.js
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
response.label = 'status';
|
response.label = 'status';
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
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 = [];
|
const suggest = [];
|
||||||
axios({
|
axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v2/search?q=${userInput}&type=accounts`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v2/search?q=${userInput}&type=accounts`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
}).then((findUserId) => {
|
}).then((findUserId) => {
|
||||||
const results = findUserId.data.accounts;
|
const results = findUserId.data.accounts;
|
||||||
const len = results.length;
|
const len = results.length;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unpin`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unpin`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
exports.runQuery = function (roomId, event, userInput) {
|
exports.runQuery = function (roomId, event, userInput) {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${userInput}/unreblog`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${userInput}/unreblog`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
|
28
commands/fediverse/unroll.js
Normal file
28
commands/fediverse/unroll.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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,6 +8,20 @@ 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) => {
|
const hasAttachment = (res) => {
|
||||||
if (res.status) res = res.status;
|
if (res.status) res = res.status;
|
||||||
if (!res.media_attachments) return '<br>';
|
if (!res.media_attachments) return '<br>';
|
||||||
|
@ -48,7 +62,7 @@ const notifyFormatter = (res, roomId) => {
|
||||||
content = `${userDetails}
|
content = `${userDetails}
|
||||||
<font color="#03b381"><b>has <a href="${config.fediverse.domain}/notice/${res.status.id}">mentioned</a>
|
<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>
|
you:</font><blockquote><i>${res.status.content}</i><br>
|
||||||
${hasAttachment(res.status.id)}
|
${hasAttachment(res)}
|
||||||
<br>(id: ${res.status.id}) ${registrar.post.visibilityEmoji(res.status.visibility)}
|
<br>(id: ${res.status.id}) ${registrar.post.visibilityEmoji(res.status.visibility)}
|
||||||
</blockquote>`;
|
</blockquote>`;
|
||||||
sendEventWithMeta(roomId, content, meta);
|
sendEventWithMeta(roomId, content, meta);
|
||||||
|
@ -64,12 +78,24 @@ const notifyFormatter = (res, roomId) => {
|
||||||
</blockquote>`;
|
</blockquote>`;
|
||||||
sendEventWithMeta(roomId, content, meta);
|
sendEventWithMeta(roomId, content, meta);
|
||||||
break;
|
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:
|
default:
|
||||||
return console.log('Unknown notification type.');
|
return console.log('Unknown notification type.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOriginal = (res, roomId) => {
|
const isOriginal = (res, roomId, event) => {
|
||||||
if (res.data) res = res.data;
|
if (res.data) res = res.data;
|
||||||
userDetails = `<b><a href="${config.fediverse.domain}/notice/${res.id}">
|
userDetails = `<b><a href="${config.fediverse.domain}/notice/${res.id}">
|
||||||
${res.account.acct}</a>`;
|
${res.account.acct}</a>`;
|
||||||
|
@ -80,7 +106,8 @@ const isOriginal = (res, roomId) => {
|
||||||
${hasAttachment(res)}
|
${hasAttachment(res)}
|
||||||
<br>(id: ${res.id}) ${registrar.post.visibilityEmoji(res.visibility)}
|
<br>(id: ${res.id}) ${registrar.post.visibilityEmoji(res.visibility)}
|
||||||
</blockquote>`;
|
</blockquote>`;
|
||||||
sendEventWithMeta(roomId, content, meta);
|
if (res.label == 'thread') thread(roomId, event, content, meta);
|
||||||
|
else sendEventWithMeta(roomId, content, meta);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isReblog = (res, roomId) => {
|
const isReblog = (res, roomId) => {
|
||||||
|
@ -101,11 +128,13 @@ const isReblog = (res, roomId) => {
|
||||||
|
|
||||||
module.exports.sendEventWithMeta = sendEventWithMeta;
|
module.exports.sendEventWithMeta = sendEventWithMeta;
|
||||||
|
|
||||||
module.exports.formatter = (res, roomId) => {
|
module.exports.thread = thread;
|
||||||
|
|
||||||
|
module.exports.formatter = (res, roomId, event) => {
|
||||||
const filtered = (res.label === 'notifications')
|
const filtered = (res.label === 'notifications')
|
||||||
? notifyFormatter(res, roomId)
|
? notifyFormatter(res, roomId)
|
||||||
: (res.reblog == null)
|
: (res.reblog == null)
|
||||||
? isOriginal(res, roomId)
|
? isOriginal(res, roomId, event)
|
||||||
: isReblog(res, roomId);
|
: isReblog(res, roomId);
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
|
@ -113,8 +142,8 @@ module.exports.formatter = (res, roomId) => {
|
||||||
module.exports.follow = (roomId, account, event, original) => {
|
module.exports.follow = (roomId, account, event, original) => {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/follow`,
|
url: `https://${config.fediverse[event.getSender()].domain}/api/v1/accounts/${account[0].id}/follow`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
@ -129,8 +158,8 @@ module.exports.follow = (roomId, account, event, original) => {
|
||||||
module.exports.unfollow = (roomId, account, event, original) => {
|
module.exports.unfollow = (roomId, account, event, original) => {
|
||||||
axios({
|
axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${config.fediverse.domain}/api/v1/accounts/${account[0].id}/unfollow`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/accounts/${account[0].id}/unfollow`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
matrix.utils.addReact(event, '✅');
|
matrix.utils.addReact(event, '✅');
|
||||||
|
@ -145,8 +174,8 @@ module.exports.unfollow = (roomId, account, event, original) => {
|
||||||
module.exports.getStatusMentions = (notice, event) => {
|
module.exports.getStatusMentions = (notice, event) => {
|
||||||
const users = axios({
|
const users = axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${config.fediverse.domain}/api/v1/statuses/${notice}`,
|
url: `https://${fediverse.auth[event.getSender()].domain}/api/v1/statuses/${notice}`,
|
||||||
headers: { Authorization: `Bearer ${fediverse.auth.access_token}` },
|
headers: { Authorization: `Bearer ${fediverse.auth[event.getSender()].access_token}` },
|
||||||
}).then((notice) => {
|
}).then((notice) => {
|
||||||
const users = [];
|
const users = [];
|
||||||
users.push('@' + notice.data.account.acct);
|
users.push('@' + notice.data.account.acct);
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
exports.runQuery = function (roomId) {
|
exports.runQuery = function (roomId) {
|
||||||
matrixClient.sendHtmlNotice(roomId,
|
matrixClient.sendHtmlNotice(roomId,
|
||||||
'',
|
' ',
|
||||||
'<blockquote><b>fediverse commands<br>'
|
'<blockquote><b>fediverse commands<br>'
|
||||||
+ '+post [your message] : post<br>'
|
+ '+post [your message] : post<br>'
|
||||||
|
+ '+direct [@recipient] [message] : direct message<br>'
|
||||||
|
+ '+private [message] : follower-only message<br>'
|
||||||
+ '+redact [post id] : delete post<br>'
|
+ '+redact [post id] : delete post<br>'
|
||||||
+ '+follow [user id] : follow<br>'
|
+ '+follow [user id] : follow<br>'
|
||||||
+ '+unfollow [user id] : unfollow<br>'
|
+ '+unfollow [user id] : unfollow<br>'
|
||||||
+ '+media [homeserver image URL or MXC] [optional message] : post media<br>'
|
+ '+media [homeserver image URL or MXC] [optional message] : post media<br>'
|
||||||
+ '+copy [post id] : repeat/repost/retweet<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>'
|
+ '+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>'
|
+ '+clap [post id] : favorite<br>'
|
||||||
+ '+boo [post id] : unfavorite</blockquote>'
|
+ '+boo [post id] : unfavorite</blockquote>'
|
||||||
+ '<blockquote><b>channel commands<br>'
|
+ '<blockquote><b>channel commands<br>'
|
||||||
+ '+flood : turn on timeline in channel<br>'
|
+ '+flood : turn on timeline in channel<br>'
|
||||||
+ '+notify : show notifications 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>'
|
+ '+archive [URL] : archive content<br>'
|
||||||
+ '+rearchive [URL] : re-archive content<br>'
|
+ '+rearchive [URL] : re-archive content<br>'
|
||||||
+ '+nitter [status URL] : redirect twitter to nitter, also embed tweet<br>'
|
+ '+nitter [status URL] : redirect twitter to nitter, also embed tweet<br>'
|
||||||
|
@ -25,6 +27,7 @@ exports.runQuery = function (roomId) {
|
||||||
+ `<blockquote><b>ligh7hau5 version ${require('../package.json').version}</b><br>`
|
+ `<blockquote><b>ligh7hau5 version ${require('../package.json').version}</b><br>`
|
||||||
+ '<b>--- <i>Contributors🐱</i> ---</b><br>'
|
+ '<b>--- <i>Contributors🐱</i> ---</b><br>'
|
||||||
+ '<b>CRYPTOMOONERS</b><br>'
|
+ '<b>CRYPTOMOONERS</b><br>'
|
||||||
|
+ '<b>doesnm</b><br>'
|
||||||
+ '<b><i>docs by LINT</i></b></blockquote>'
|
+ '<b><i>docs by LINT</i></b></blockquote>'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
const headers = ({ domain, userAgent }) => ({
|
|
||||||
Host: `${domain}`,
|
|
||||||
'User-Agent': `${userAgent}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const invidious = async (instance, url) => {
|
const invidious = async (instance, url) => {
|
||||||
const req = await instance({ method: 'GET', url });
|
const req = await instance({ method: 'GET', url });
|
||||||
if (req.statusText !== 'OK') throw req;
|
if (req.statusText !== 'OK') throw req;
|
||||||
|
const { headers } = instance.defaults;
|
||||||
const video = JSON.parse(req.data);
|
const video = JSON.parse(req.data);
|
||||||
return {
|
return {
|
||||||
|
url: headers['Host'],
|
||||||
name: video.title,
|
name: video.title,
|
||||||
date: video.publishedText,
|
date: video.publishedText,
|
||||||
description: video.descriptionHtml,
|
description: video.descriptionHtml,
|
||||||
|
@ -18,8 +15,8 @@ const invidious = async (instance, url) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const card = (video, base, path) =>
|
const card = (video, path) =>
|
||||||
`<a href="${base}/${path}"><b>${video.name}</a></b><blockquote><b><i>` +
|
`<a href="https://${video.url}/${path}"><b>${video.name}</a></b><blockquote><b><i>` +
|
||||||
((video.description.length > 300) ? `${video.description.substr(0, 300)}…` : ``)+
|
((video.description.length > 300) ? `${video.description.substr(0, 300)}…` : ``)+
|
||||||
((video.description === '<p></p>') ? `No description.`: ``)+
|
((video.description === '<p></p>') ? `No description.`: ``)+
|
||||||
((video.description.length < 300 && video.description !== '<p></p>') ? `${video.description}` : ``)+
|
((video.description.length < 300 && video.description !== '<p></p>') ? `${video.description}` : ``)+
|
||||||
|
@ -29,21 +26,28 @@ const card = (video, base, path) =>
|
||||||
`<br />(${video.date})</b> <br />
|
`<br />(${video.date})</b> <br />
|
||||||
</blockquote>`;
|
</blockquote>`;
|
||||||
|
|
||||||
const run = async (roomId, userInput) => {
|
const getInstance = (domain, config) =>
|
||||||
const instance = axios.create({
|
axios.create({
|
||||||
baseURL: `https://${config.invidious.domain}/api/v1/videos/`,
|
baseURL: `https://${domain}/api/v1/videos`,
|
||||||
headers: headers(config.invidious),
|
headers: {
|
||||||
|
Host: `${domain}`,
|
||||||
|
'User-Agent': `${config.userAgent}`,
|
||||||
|
},
|
||||||
transformResponse: [],
|
transformResponse: [],
|
||||||
timeout: 10 * 1000,
|
timeout: 10 * 1000,
|
||||||
});
|
});
|
||||||
const video = await invidious(instance, userInput);
|
|
||||||
return await matrixClient.sendHtmlNotice(roomId, '', card(video, `https://${config.invidious.domain}`, userInput));
|
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));
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.runQuery = async (roomId, event, userInput) => {
|
exports.runQuery = async (roomId, event, userInput) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(userInput);
|
const url = new URL(userInput);
|
||||||
if (!config.invidious.domains.includes(url.hostname)) throw '';
|
const { redirect, original } = config.invidious.domains;
|
||||||
|
if (!redirect.includes(url.hostname) && !original.includes(url.hostname)) throw '';
|
||||||
if (/^\/[\w-]{11}$/.test(url.pathname)) return await run(roomId, url.pathname.slice(1));
|
if (/^\/[\w-]{11}$/.test(url.pathname)) return await run(roomId, url.pathname.slice(1));
|
||||||
const params = new URLSearchParams(url.search).get('v');
|
const params = new URLSearchParams(url.search).get('v');
|
||||||
if (!/^[\w-]{11}$/.test(params)) throw '';
|
if (!/^[\w-]{11}$/.test(params)) throw '';
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
const { JSDOM } = require('jsdom');
|
const { JSDOM } = require('jsdom');
|
||||||
|
|
||||||
const headers = ({ domain, userAgent }) => ({
|
|
||||||
Host: `${domain}`,
|
|
||||||
'User-Agent': `${userAgent}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
const nitter = async (instance, url) => {
|
const nitter = async (instance, url) => {
|
||||||
const req = await instance({ method: 'GET', url });
|
const req = await instance({ method: 'GET', url });
|
||||||
if (req.statusText !== 'OK') throw req;
|
if (req.statusText !== 'OK') throw req;
|
||||||
|
@ -15,7 +10,9 @@ const nitter = async (instance, url) => {
|
||||||
const quote = tweet.querySelector('.tweet-body > .quote');
|
const quote = tweet.querySelector('.tweet-body > .quote');
|
||||||
const isReply = tweet.querySelector('.tweet-body > .replying-to');
|
const isReply = tweet.querySelector('.tweet-body > .replying-to');
|
||||||
const replies = document.querySelectorAll('.main-thread > .before-tweet > .timeline-item');
|
const replies = document.querySelectorAll('.main-thread > .before-tweet > .timeline-item');
|
||||||
|
const { defaults } = instance;
|
||||||
return {
|
return {
|
||||||
|
url: defaults.baseURL,
|
||||||
text: tweet.querySelector('.tweet-body > .tweet-content').innerHTML,
|
text: tweet.querySelector('.tweet-body > .tweet-content').innerHTML,
|
||||||
date: tweet.querySelector('.tweet-body > .tweet-published').textContent,
|
date: tweet.querySelector('.tweet-body > .tweet-published').textContent,
|
||||||
name: tweet.querySelector('.tweet-body > div .fullname').textContent,
|
name: tweet.querySelector('.tweet-body > div .fullname').textContent,
|
||||||
|
@ -30,6 +27,10 @@ const nitter = async (instance, url) => {
|
||||||
path: replies[replies.length - 1].querySelector('a.tweet-link').href,
|
path: replies[replies.length - 1].querySelector('a.tweet-link').href,
|
||||||
text: replies[replies.length - 1].querySelector('.tweet-content').innerHTML,
|
text: replies[replies.length - 1].querySelector('.tweet-content').innerHTML,
|
||||||
} : null,
|
} : 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: {
|
stats: {
|
||||||
replies: stats[0].textContent.trim(),
|
replies: stats[0].textContent.trim(),
|
||||||
retweets: stats[1].textContent.trim(),
|
retweets: stats[1].textContent.trim(),
|
||||||
|
@ -38,35 +39,49 @@ const nitter = async (instance, url) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const card = (tweet, base, check, path) =>
|
const card = (tweet, check, path) =>
|
||||||
`<a href="${base}/${tweet.handle.replace(/^@/, '')}"><b>${tweet.name}</b></a> ` +
|
`<a href="${tweet.url}/${tweet.handle.replace(/^@/, '')}"><b>${tweet.name}</b></a> ` +
|
||||||
(tweet.check ? `${check} ` : '') +
|
(tweet.check ? `${check} ` : '') +
|
||||||
`<a href="${base}${path}"><b>${tweet.date}</b></a> ` +
|
`<a href="${tweet.url}${path}"><b>${tweet.date}</b></a> ` +
|
||||||
`<span>🗨️ ${tweet.stats.replies}</span> ` +
|
`<span>🗨️ ${tweet.stats.replies}</span> ` +
|
||||||
`<span>🔁 ${tweet.stats.retweets}</span> ` +
|
`<span>🔁 ${tweet.stats.retweets}</span> ` +
|
||||||
`<span>❤️ ${tweet.stats.favorites}</span> ` +
|
`<span>❤️ ${tweet.stats.favorites}</span> ` +
|
||||||
`<br /><blockquote><b><i>${tweet.text.replace('\n', '<br />')}</i></b></blockquote>` +
|
`<br /><blockquote><b><i>${tweet.text.replace('\n', '<br />')}</i></b></blockquote>` +
|
||||||
(tweet.hasAttachments ? '<blockquote><b>This tweet has attached media.</b></blockquote>' : '') +
|
(tweet.hasAttachments ? '<blockquote><b>This tweet has attached media.</b></blockquote>' : '') +
|
||||||
(tweet.isReply ? tweet.isReply === 'unavailable' ? '<blockquote>Replied Tweet is unavailable</blockquote>' : `<blockquote><b><a href="${base}${tweet.isReply.path}">Replied Tweet</a></b><br /><b><i>${tweet.isReply.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
(tweet.isReply ? tweet.isReply === 'unavailable' ? '<blockquote>Replied Tweet is unavailable</blockquote>' : `<blockquote><b><a href="${tweet.url}${tweet.isReply.path}">Replied Tweet</a></b><br /><b><i>${tweet.isReply.text.replace('\n', '<br />')}</i></b></blockquote>` : '') +
|
||||||
(tweet.quote ? `<blockquote><b><a href="${base}${tweet.quote.path}">Quoted Tweet</a></b><br /><b><i>${tweet.quote.text.replace('\n', '<br />')}</i></b></blockquote>` : '');
|
(tweet.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>` : '');
|
||||||
|
|
||||||
const run = async (roomId, userInput) => {
|
const getInstance = (domain, config) =>
|
||||||
const instance = axios.create({
|
axios.create({
|
||||||
baseURL: `https://${config.nitter.domain}`,
|
baseURL: `https://${domain}`,
|
||||||
headers: headers(config.nitter),
|
headers: {
|
||||||
|
Host: `${domain}`,
|
||||||
|
'User-Agent': `${config.userAgent}`,
|
||||||
|
},
|
||||||
transformResponse: [],
|
transformResponse: [],
|
||||||
timeout: 10 * 1000,
|
timeout: 10 * 1000,
|
||||||
});
|
});
|
||||||
const tweet = await nitter(instance, userInput);
|
|
||||||
return await matrixClient.sendHtmlNotice(roomId, '', card(tweet, `https://${config.nitter.domain}`, config.nitter.check, userInput));
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.runQuery = async (roomId, event, userInput) => {
|
exports.runQuery = async (roomId, event, userInput, fedi) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(userInput);
|
const url = new URL(userInput);
|
||||||
if (!config.nitter.domains.includes(url.hostname)) throw '';
|
const { redirect, original } = config.nitter.domains;
|
||||||
|
if (!redirect.includes(url.hostname) && !original.includes(url.hostname)) throw '';
|
||||||
if (!/^\/[^/]+\/status\/\d+\/?$/.test(url.pathname)) throw '';
|
if (!/^\/[^/]+\/status\/\d+\/?$/.test(url.pathname)) throw '';
|
||||||
return await run(roomId, url.pathname);
|
return await run(roomId, url.pathname, fedi);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
return matrixClient.sendHtmlNotice(roomId, 'Sad!', '<strong>Sad!</strong>').catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
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');
|
global.registrar = require('./registrar.js');
|
||||||
|
|
||||||
matrix.auth.access_token ? auth.matrixTokenLogin() : auth.getMatrixToken();
|
matrix.auth.access_token ? auth.matrixTokenLogin() : auth.getMatrixToken();
|
||||||
if (!fediverse.auth.access_token && config.fediverse.username) auth.registerFediverseApp();
|
//if (!fediverse.auth.access_token && config.fediverse.username) auth.registerFediverseApp();
|
||||||
|
|
||||||
matrixClient.on('RoomMember.membership', (event, member) => {
|
matrixClient.on('RoomMember.membership', (event, member) => {
|
||||||
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
|
if (member.membership === 'invite' && member.userId === matrixClient.credentials.userId) {
|
||||||
|
@ -18,22 +18,23 @@ matrixClient.on('RoomMember.membership', (event, member) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
matrixClient.on('event', async (event) => {
|
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.getSender() === matrixClient.credentials.userId) return matrix.utils.selfReact(event);
|
||||||
if (!event.getContent()['m.relates_to']) return;
|
if (!event.event.content['m.relates_to']) return;
|
||||||
if (event.event.unsigned.age > 10000) return;
|
if (event.event.unsigned.age > 10000) return;
|
||||||
return event.getType() === 'm.room.message'
|
return event.getType() === 'm.room.message'
|
||||||
? matrix.utils.handleReply(event) : matrix.utils.handleReact(event);
|
? matrix.utils.handleReply(event) : matrix.utils.handleReact(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
matrixClient.on('Room.timeline', async (event, member, toStartOfTimeline) => {
|
matrixClient.on('Room.timeline', async (event, member, toStartOfTimeline) => {
|
||||||
matrixClient.setGlobalErrorOnUnknownDevices(config.matrix.manualVerify);
|
|
||||||
if (toStartOfTimeline) return;
|
if (toStartOfTimeline) return;
|
||||||
if (event.isEncrypted()) await event._decryptionPromise;
|
if (event.isEncrypted()) await matrixClient.decryptEventIfNeeded(event, { emit: false, isRetry: false });
|
||||||
if (event.getType() !== 'm.room.message') return;
|
if (event.getType() !== 'm.room.message') return;
|
||||||
if (event.getSender() === matrixClient.credentials.userId) return;
|
if (event.getSender() === matrixClient.credentials.userId) return;
|
||||||
if (event.event.unsigned.age > 10000) return;
|
if (event.event.unsigned.age > 10000) return;
|
||||||
roomId = event.event.room_id;
|
roomId = event.event.room_id;
|
||||||
content = event.getContent().body;
|
content = event.getContent().body;
|
||||||
|
if (!typeof content === 'string') return;
|
||||||
if (content.charAt(0) === '+') {
|
if (content.charAt(0) === '+') {
|
||||||
const args = content.slice(1).trim().split(/ +/g);
|
const args = content.slice(1).trim().split(/ +/g);
|
||||||
const command = args.shift().toLowerCase();
|
const command = args.shift().toLowerCase();
|
||||||
|
|
1027
package-lock.json
generated
Normal file
1027
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
19
package.json
19
package.json
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "ligh7hau5",
|
"name": "ligh7hau5",
|
||||||
"version": "1.2.2",
|
"version": "3.0.1",
|
||||||
"description": "A Matrix to Fediverse client",
|
"description": "A Matrix to Fediverse client",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
@ -18,13 +21,13 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/vulet/lighthau5#readme",
|
"homepage": "https://github.com/vulet/lighthau5#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.25.0",
|
||||||
"form-data": "^3.0.0",
|
"form-data": "^4.0.0",
|
||||||
"jsdom": "^16.4.0",
|
"jsdom": "^19.0.0",
|
||||||
"matrix-js-sdk": "^9.7.0",
|
"matrix-js-sdk": "^27.2.0",
|
||||||
"node-localstorage": "^2.1.6",
|
"node-localstorage": "^2.2.1",
|
||||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
"olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
|
||||||
"qs": "^6.9.6"
|
"qs": "^6.11.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
|
|
19
registrar.js
19
registrar.js
|
@ -3,16 +3,16 @@ global.sdk = require('matrix-js-sdk');
|
||||||
global.axios = require('axios');
|
global.axios = require('axios');
|
||||||
global.config = require('./config.js');
|
global.config = require('./config.js');
|
||||||
global.auth = require('./auth.js');
|
global.auth = require('./auth.js');
|
||||||
|
global.authEvents = [];
|
||||||
const { LocalStorage } = require('node-localstorage');
|
const { LocalStorage } = require('node-localstorage');
|
||||||
global.localStorage = new LocalStorage('./keys');
|
global.localStorage = new LocalStorage('./keys');
|
||||||
if (!localStorage.getItem('matrix_auth')) {
|
if (!localStorage.getItem('matrix_auth')) {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
localStorage.setItem('matrix_auth', '[]');
|
localStorage.setItem('matrix_auth', '[]');
|
||||||
}
|
}
|
||||||
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', '[]');
|
if (!localStorage.getItem('fediverse_auth')) localStorage.setItem('fediverse_auth', '{}');
|
||||||
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', '[]');
|
if (!localStorage.getItem('timeline')) localStorage.setItem('timeline', '{}');
|
||||||
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', '[]');
|
if (!localStorage.getItem('notifications')) localStorage.setItem('notifications', '{}');
|
||||||
|
|
||||||
|
|
||||||
global.fediverse = {
|
global.fediverse = {
|
||||||
|
@ -27,24 +27,25 @@ global.matrix = {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
config: require('./config.js'),
|
config: require('./config.js'),
|
||||||
archive: require('./commands/archive.js'),
|
archive: require('./commands/archive.js'),
|
||||||
|
proxy: require('./commands/proxy.js'),
|
||||||
invidious: require('./commands/invidious.js'),
|
invidious: require('./commands/invidious.js'),
|
||||||
nitter: require('./commands/nitter.js'),
|
nitter: require('./commands/nitter.js'),
|
||||||
beg: require('./commands/fediverse/beg.js'),
|
|
||||||
boo: require('./commands/fediverse/boo.js'),
|
boo: require('./commands/fediverse/boo.js'),
|
||||||
clap: require('./commands/fediverse/clap.js'),
|
clap: require('./commands/fediverse/clap.js'),
|
||||||
copy: require('./commands/fediverse/copy.js'),
|
copy: require('./commands/fediverse/copy.js'),
|
||||||
flood: require('./commands/fediverse/flood.js'),
|
flood: require('./commands/fediverse/flood.js'),
|
||||||
follow: require('./commands/fediverse/follow.js'),
|
follow: require('./commands/fediverse/follow.js'),
|
||||||
help: require('./commands/help.js'),
|
help: require('./commands/help.js'),
|
||||||
makeitrain: require('./commands/fediverse/makeitrain.js'),
|
|
||||||
mordy: require('./commands/fediverse/mordy.js'),
|
|
||||||
notify: require('./commands/fediverse/notify.js'),
|
notify: require('./commands/fediverse/notify.js'),
|
||||||
pin: require('./commands/fediverse/pin.js'),
|
pin: require('./commands/fediverse/pin.js'),
|
||||||
post: require('./commands/fediverse/post.js'),
|
post: require('./commands/fediverse/post.js'),
|
||||||
redact: require('./commands/fediverse/redact.js'),
|
redact: require('./commands/fediverse/redact.js'),
|
||||||
status: require('./commands/fediverse/status.js'),
|
status: require('./commands/fediverse/status.js'),
|
||||||
tip: require('./commands/fediverse/tip.js'),
|
|
||||||
unfollow: require('./commands/fediverse/unfollow.js'),
|
unfollow: require('./commands/fediverse/unfollow.js'),
|
||||||
unpin: require('./commands/fediverse/unpin.js'),
|
unpin: require('./commands/fediverse/unpin.js'),
|
||||||
unreblog: require('./commands/fediverse/unreblog.js')
|
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")
|
||||||
};
|
};
|
||||||
|
|
136
utils.js
136
utils.js
|
@ -1,9 +1,13 @@
|
||||||
|
const { MatrixEvent } = require('matrix-js-sdk/lib/models/event');
|
||||||
|
const url = require("url")
|
||||||
|
const isEmoji = string => true;
|
||||||
|
|
||||||
const sendError = async (event, roomId, e) => {
|
const sendError = async (event, roomId, e) => {
|
||||||
e.response ? error = `Error(${e.response.status}): ${e.response.data.error}`
|
e.response ? error = `Error(${e.response.status}): ${e.response.data.error}`
|
||||||
: e.data ? error = `Error(${e.errcode}): ${e.data.error}`
|
: e.data ? error = `Error(${e.errcode}): ${e.data.error}`
|
||||||
: error = `Error: ${e.syscall}, ${e.code}`;
|
: error = `Error: ${e.syscall}, ${e.code}`;
|
||||||
return matrixClient.sendHtmlNotice(roomId,
|
return matrixClient.sendHtmlNotice(roomId,
|
||||||
'', error);
|
' ', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReact = async (event, key) => {
|
const addReact = async (event, key) => {
|
||||||
|
@ -23,6 +27,7 @@ const eventHandler = (args, roomId, command, event) => {
|
||||||
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
||||||
|
|
||||||
args = [];
|
args = [];
|
||||||
|
let visibility = null;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 'config':
|
case 'config':
|
||||||
|
@ -30,6 +35,14 @@ const eventHandler = (args, roomId, command, event) => {
|
||||||
case 'help': case 'flood': case 'notify':
|
case 'help': case 'flood': case 'notify':
|
||||||
args.push(roomId);
|
args.push(roomId);
|
||||||
break;
|
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':
|
case 'tip': case 'makeitrain':
|
||||||
args.push(roomId, event, address, flaggedInput);
|
args.push(roomId, event, address, flaggedInput);
|
||||||
break;
|
break;
|
||||||
|
@ -39,35 +52,52 @@ const eventHandler = (args, roomId, command, event) => {
|
||||||
break;
|
break;
|
||||||
case 'post': case 'reply': case 'media': case 'mediareply':
|
case 'post': case 'reply': case 'media': case 'mediareply':
|
||||||
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
|
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, {
|
args.push(roomId, event, userInput, {
|
||||||
isReply: !!~command.indexOf('reply'),
|
isReply: !!~command.indexOf('reply'),
|
||||||
hasMedia: !!~command.indexOf('media'),
|
hasMedia: !!~command.indexOf('media'),
|
||||||
hasSubject: !!~command.indexOf('random'),
|
hasSubject: !!~command.indexOf('random'),
|
||||||
|
visibility: visibility ? visibility[1] : null
|
||||||
});
|
});
|
||||||
command = 'post';
|
command = 'post';
|
||||||
break;
|
break;
|
||||||
case 'proxy':
|
case 'crossblog':
|
||||||
try {
|
args.push(roomId, event, userInput, true);
|
||||||
const url = new URL(userInput);
|
command = 'nitter'
|
||||||
command = config.invidious.domains.includes(url.hostname)
|
break;
|
||||||
? 'invidious'
|
|
||||||
: config.nitter.domains.includes(url.hostname)
|
|
||||||
? 'nitter'
|
|
||||||
: 'proxy';
|
|
||||||
} catch (e) { sendError(event, roomId, e); }
|
|
||||||
// fallthrough
|
// fallthrough
|
||||||
default:
|
default:
|
||||||
args.push(roomId, event, userInput);
|
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);
|
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.sendError = sendError;
|
||||||
|
|
||||||
module.exports.addReact = addReact;
|
module.exports.addReact = addReact;
|
||||||
|
|
||||||
module.exports.eventHandler = eventHandler;
|
module.exports.eventHandler = eventHandler;
|
||||||
|
|
||||||
|
module.exports.fetchEncryptedOrNot = fetchEncryptedOrNot
|
||||||
|
|
||||||
module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sendMessage(roomId, {
|
module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sendMessage(roomId, {
|
||||||
body: ` * ${plain || html.replace(/<[^<]+?>/g, '')}`,
|
body: ` * ${plain || html.replace(/<[^<]+?>/g, '')}`,
|
||||||
formatted_body: ` * ${html}`,
|
formatted_body: ` * ${html}`,
|
||||||
|
@ -86,31 +116,55 @@ module.exports.editNoticeHTML = (roomId, event, html, plain) => matrixClient.sen
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.handleReact = async (event) => {
|
module.exports.handleReact = async (event) => {
|
||||||
|
const reactions = config.matrix.reactions;
|
||||||
const roomId = event.event.room_id;
|
const roomId = event.event.room_id;
|
||||||
|
if (!event.getContent()['m.relates_to']) return;
|
||||||
const reaction = event.getContent()['m.relates_to'];
|
const reaction = event.getContent()['m.relates_to'];
|
||||||
if (!reaction) return;
|
const metaEvent = await fetchEncryptedOrNot(roomId, reaction);
|
||||||
const metaEvent = await matrixClient.fetchRoomEvent(roomId, reaction.event_id);
|
if (!metaEvent.getContent().meta || metaEvent.event.sender !== config.matrix.user) return;
|
||||||
if (!metaEvent.content.meta || metaEvent.sender !== config.matrix.user) return;
|
let args = metaEvent.getContent().meta.split(' ');
|
||||||
const args = metaEvent.content.meta.split(' ');
|
|
||||||
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
||||||
if (!isMeta.includes(args[0])) return;
|
if (!isMeta.includes(args[0])) return;
|
||||||
let command = [];
|
let command = [];
|
||||||
args.shift().toLowerCase();
|
args.shift().toLowerCase();
|
||||||
if (reaction.key === '🔁') command = 'copy';
|
switch (reaction.key) {
|
||||||
if (reaction.key === '👏') command = 'clap';
|
case reactions.copy: command = 'copy'; break;
|
||||||
if (reaction.key === '🗑️️') command = 'redact';
|
case reactions.clap: command = 'clap'; break;
|
||||||
if (reaction.key === '🌧️') command = 'makeitrain';
|
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;
|
||||||
|
}
|
||||||
eventHandler(args, roomId, command, event);
|
eventHandler(args, roomId, command, event);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.handleReply = async (event) => {
|
module.exports.handleReply = async (event) => {
|
||||||
const roomId = event.event.room_id;
|
const roomId = event.event.room_id;
|
||||||
const reply = event.getContent()['m.relates_to']['m.in_reply_to'];
|
if(!event.event.content['m.relates_to']['m.in_reply_to']) return;
|
||||||
if (!reply) return;
|
const reply = event.event.content['m.relates_to']['m.in_reply_to'];
|
||||||
const metaEvent = await matrixClient.fetchRoomEvent(roomId, reply.event_id);
|
const metaEvent = await fetchEncryptedOrNot(roomId, reply);
|
||||||
if (!metaEvent.content.meta || metaEvent.sender !== config.matrix.user) return;
|
if(authEvents.includes(metaEvent.event_id)){
|
||||||
const args = metaEvent.content.meta.split(' ');
|
const domain = metaEvent.event.content.body.match(/https?:\/\/[^\s]+/);
|
||||||
args.push(event.event.content.formatted_body.trim().split('</mx-reply>')[1]);
|
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]);
|
||||||
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
||||||
if (!isMeta.includes(args[0])) return;
|
if (!isMeta.includes(args[0])) return;
|
||||||
args.shift().toLowerCase();
|
args.shift().toLowerCase();
|
||||||
|
@ -119,15 +173,39 @@ module.exports.handleReply = async (event) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.selfReact = async (event) => {
|
module.exports.selfReact = async (event) => {
|
||||||
|
const reactions = config.matrix.reactions;
|
||||||
if (event.getType() !== 'm.room.message') return;
|
if (event.getType() !== 'm.room.message') return;
|
||||||
if (event.event.unsigned.age > 10000) 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();
|
const { meta } = event.getContent();
|
||||||
if (!meta) return;
|
|
||||||
const type = meta.split(' ')[0];
|
const type = meta.split(' ')[0];
|
||||||
if (type === 'redact' || type === 'unreblog') addReact(event, '🗑️️');
|
|
||||||
if (type === 'status' || type === 'reblog' || type === 'mention') {
|
if (type === 'status' || type === 'reblog' || type === 'mention') {
|
||||||
addReact(event, '🔁');
|
addReact(event, reactions.unroll);
|
||||||
addReact(event, '👏');
|
addReact(event, reactions.copy);
|
||||||
if (config.fediverse.tipping === true) addReact(event, '🌧️');
|
addReact(event, reactions.clap);
|
||||||
|
if (config.fediverse.tipping)
|
||||||
|
addReact(event, reactions.rain);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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