211 lines
7.6 KiB
JavaScript
211 lines
7.6 KiB
JavaScript
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);
|
|
};
|
|
|
|
const addReact = async (event, key) => {
|
|
const roomId = event.event.room_id;
|
|
return matrixClient.sendEvent(event.event.room_id, 'm.reaction', {
|
|
'm.relates_to': {
|
|
rel_type: 'm.annotation',
|
|
event_id: event.getId(),
|
|
key,
|
|
},
|
|
}).catch((e) => sendError(null, roomId, e));
|
|
};
|
|
|
|
const eventHandler = (args, roomId, command, event) => {
|
|
const userInput = args.join(' ');
|
|
const flaggedInput = userInput.substr(userInput.indexOf(' ') + 1);
|
|
const address = args.slice(0, 1).join(' ').replace(/"/g, '');
|
|
|
|
args = [];
|
|
let visibility = null;
|
|
|
|
switch (command) {
|
|
case 'config':
|
|
return;
|
|
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;
|
|
case 'archive': case 'rearchive':
|
|
args.push(roomId, userInput, !!~command.indexOf('re'));
|
|
command = 'archive';
|
|
break;
|
|
case 'post': case 'reply': case 'media': case 'mediareply':
|
|
case 'random': case 'randomreply': case 'randommedia': case 'randommediareply':
|
|
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;
|
|
// 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}`,
|
|
format: 'org.matrix.custom.html',
|
|
msgtype: 'm.notice',
|
|
'm.new_content': {
|
|
body: plain || html.replace(/<[^<]+?>/g, ''),
|
|
formatted_body: html,
|
|
format: 'org.matrix.custom.html',
|
|
msgtype: 'm.notice',
|
|
},
|
|
'm.relates_to': {
|
|
rel_type: 'm.replace',
|
|
event_id: event.event_id,
|
|
},
|
|
});
|
|
|
|
module.exports.handleReact = async (event) => {
|
|
const 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(' ');
|
|
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;
|
|
}
|
|
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]);
|
|
isMeta = ['status', 'reblog', 'mention', 'redact', 'unreblog'];
|
|
if (!isMeta.includes(args[0])) return;
|
|
args.shift().toLowerCase();
|
|
command = 'reply';
|
|
eventHandler(args, roomId, command, event);
|
|
};
|
|
|
|
module.exports.selfReact = async (event) => {
|
|
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();
|
|
const type = meta.split(' ')[0];
|
|
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);
|
|
}
|
|
};
|
|
|
|
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');
|
|
};
|