feat: refactor and new features (#7)

* feat: refactor and new features

+ Refactored the codebase
+ OCR support in bots
+ Server sends training data every minute
+ Not using collectors for Discord feedback buttons anymore
+ Fixed grammar mistakes
+ Configs are now seperated
+ Tokens are no longer in configs
- Like feedback doesn't work for Discord yet

* feat: remove feedback button once voted

* feat: role blacklist

* feat: thread name check

* feat: error handler for training

* fix: bot crashing when a webhook msg is sent

* refactor: remove debugging lines

* feat: allow fixing mistake at votes in discord bot
This commit is contained in:
reis
2023-06-23 21:29:00 +03:00
committed by GitHub
parent f5214a6ace
commit 8b9f45dc22
60 changed files with 1962 additions and 1591 deletions

View File

@@ -0,0 +1,61 @@
export default {
command: /\/train/,
async execute(bot, config, msg) {
const admins = await bot.getChatAdministrators(msg.chat.id);
const isAdmin = admins.find((admin) => admin.user.id === msg.from.id);
if (!isAdmin)
return bot.sendMessage(msg.chat.id, 'You\'re not an admin.', {
message_thread_id: msg.message_thread_id,
reply_to_message_id: msg.message_id
});
if (msg.reply_to_message.message_id === msg.message_thread_id)
return bot.sendMessage(msg.chat.id, 'Please reply to a message!', {
message_thread_id: msg.message_thread_id,
reply_to_message_id: msg.message_id
});
const options = [];
let arrI = 0;
let i = 0;
for (const { label } of config.responses) {
if (arrI === 0 && i === 0) {
options.push([
{
text: label,
callback_data: `label_${label.toLowerCase()}`
}
]);
i++;
} else if (i === 2) {
options.push([
{
text: label,
callback_data: `label_${label.toLowerCase()}`
}
]);
i = 0;
arrI++;
} else {
options[arrI].push({
text: label,
callback_data: `label_${label.toLowerCase()}`
});
i++;
}
}
bot.sendMessage(
msg.chat.id,
'Please select the corresponding label to train the bot.',
{
message_thread_id: msg.message_thread_id,
reply_to_message_id: msg.reply_to_message.message_id,
reply_markup: {
inline_keyboard: options
}
}
);
}
};

View File

@@ -0,0 +1,26 @@
{
"server": {
"port": 3000,
"host": "192.168.1.6"
},
"responses": [
{
"label": "revanced_download",
"threshold": 0.85,
"reply": {
"title": "How to download ReVanced?",
"desc": "You don't."
}
}
],
"ocrResponses": [
{
"regex": "is not installed",
"reply": {
"title": "Why can't I download videos",
"desc": "Because you didn't install the app."
}
}
]
}

View File

@@ -0,0 +1,25 @@
{
"server": {
"port": 3000,
"host": "127.0.0.1"
},
"responses": [
{
"label": "revanced_download",
"threshold": 0.85,
"reply": {
"title": "How to download ReVanced?",
"desc": "You don't."
}
}
],
"ocrResponses": [
{
"regex": "is not installed",
"reply": {
"title": "Why can't I download videos",
"desc": "Because you didn't install the app."
}
}
]
}

View File

@@ -0,0 +1,26 @@
export default {
name: 'callback_query',
once: false,
async execute(bot, helper, cb) {
const admins = await bot.getChatAdministrators(cb.message.chat.id);
const isAdmin = admins.find((admin) => admin.user.id === cb.from.id);
if (!isAdmin)
return bot.sendMessage(cb.message.chat.id, 'You\'re not an admin.', {
message_thread_id: cb.message.message_thread_id,
reply_to_message_id: cb.message.message_id
});
helper.sendTrainData(
cb.message.reply_to_message.text.toLowerCase(),
cb.data.replace('label_', '').toUpperCase()
);
bot.sendMessage(cb.message.chat.id, 'Sent training data to server.', {
message_thread_id: cb.message.message_thread_id,
reply_to_message_id: cb.message.message_id
});
bot.deleteMessage(cb.message.chat.id, cb.message.message_id);
}
};

View File

@@ -0,0 +1,15 @@
export default {
name: 'message',
once: false,
async execute(bot, helper, msg) {
if (msg.photo) {
const fileLink = await bot.getFileLink(msg.photo.at(-1).file_id);
helper.scanImage(fileLink, `${msg.chat.id}/${msg.message_thread_id}/${msg.message_id}`)
}
if (!msg.text) return;
helper.scanText(
msg.text.toLowerCase(),
`${msg.chat.id}/${msg.message_thread_id}/${msg.message_id}`
);
}
};

View File

@@ -0,0 +1,29 @@
export default {
name: 'aiResponse',
once: false,
async execute(bot, config, aiRes) {
if (!aiRes.response) return;
if (!aiRes.response[0]) return;
const ids = aiRes.id.split('/');
const intent = aiRes.response.reduce((a, b) =>
a.confidence > b.confidence ? a : b
);
const response = config.responses.find((res) => res.label === intent.name);
if (!response) return;
if (response.threshold > intent.confidence) return;
if (!response.reply) return;
// Because for some reason the markdown parser in TG is a pain in the ass,
// there won't be markdown support for now.
bot.sendMessage(
ids[0],
`## ${response.reply.title}\n\n${response.reply.desc}\n\n_Confidence: ${intent.confidence}_\n\nThis bot is currently being tested in production. Ignore it, if it's wrong.`,
{
message_thread_id: ids[1],
reply_to_message_id: ids[2]
}
);
return;
}
};

View File

@@ -0,0 +1,24 @@
export default {
name: 'ocrResponse',
once: false,
async execute(bot, config, ocrRes) {
const ids = ocrRes.id.split('/');
for (const ocrReply of config.ocrResponses) {
if (ocrRes.ocrText.match(ocrReply.regex)) {
// Because for some reason the markdown parser in TG is a pain in the ass,
// there won't be markdown support for now.
bot.sendMessage(
ids[0],
`## ${ocrReply.reply.title}\n\n${ocrReply.reply.desc}\n\nThis bot is currently being tested in production. Ignore it, if it's wrong.`,
{
message_thread_id: ids[1],
reply_to_message_id: ids[2]
}
);
break;
}
}
return;
}
};

View File

@@ -0,0 +1,66 @@
import TelegramBot from 'node-telegram-bot-api';
import { readFileSync, readdirSync } from 'node:fs';
// Fix __dirname not being defined in ES modules. (https://stackoverflow.com/a/64383997)
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import HelperClient from '@revanced-helper/helper-client';
import config from './config.json' assert { type: 'json' };
const helper = new HelperClient(config);
helper.connect();
const bot = new TelegramBot(process.env.TELEGRAM_TOKEN, { polling: true });
const commandsPath = join(__dirname, 'commands');
const commandFiles = readdirSync(commandsPath).filter((file) =>
file.endsWith('.js')
);
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = (await import(`file://${filePath}`)).default;
if ('command' in command && 'execute' in command) {
bot.onText(command.command, (...args) =>
command.execute(bot, config, ...args)
);
} else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
);
}
}
const tgEventsPath = join(__dirname, 'events');
const tgEventFiles = readdirSync(tgEventsPath).filter((file) =>
file.endsWith('.js')
);
for (const file of tgEventFiles) {
const filePath = join(tgEventsPath, file);
const event = (await import(`file://${filePath}`)).default;
if (event.once) {
bot.once(event.name, (...args) => event.execute(bot, helper, ...args));
} else {
bot.on(event.name, (...args) => event.execute(bot, helper, ...args));
}
}
// The ReVanced Helper events.
const helperEventsPath = join(__dirname, 'helperEvents');
const helperEventFiles = readdirSync(helperEventsPath).filter((file) =>
file.endsWith('.js')
);
for (const file of helperEventFiles) {
const filePath = join(helperEventsPath, file);
const event = (await import(`file://${filePath}`)).default;
if (event.once) {
helper.once(event.name, (...args) => event.execute(bot, config, ...args));
} else {
helper.on(event.name, (...args) => event.execute(bot, config, ...args));
}
}

2302
apps/bot-telegram/src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
{
"name": "bot-telegram",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Reis Can",
"license": "GPL-3.0-or-later",
"dependencies": {
"@revanced-helper/helper-client": "file:../../../packages/client",
"node-telegram-bot-api": "^0.61.0"
}
}