From 94d695e0084fe974a0c2aa009a92595ce9f88b11 Mon Sep 17 00:00:00 2001 From: Danil Nikolaev Date: Sun, 10 May 2026 22:53:32 +0300 Subject: [PATCH] commands: localize generic bot commands --- src/callback_commands/cancel.ts | 5 +-- src/commands/admins-add.ts | 14 ++++---- src/commands/admins-list.ts | 10 +++--- src/commands/admins-remove.ts | 14 ++++---- src/commands/ae.ts | 11 ++++--- src/commands/ban.ts | 22 ++++++++----- src/commands/choice.ts | 18 +++++++--- src/commands/coin.ts | 12 ++++--- src/commands/debug.ts | 7 ++-- src/commands/dice.ts | 31 ++++++++++-------- src/commands/distort.ts | 32 +++++++++++------- src/commands/export-db.ts | 20 +++++++----- src/commands/help.ts | 13 ++++---- src/commands/id.ts | 18 +++++----- src/commands/ignore.ts | 14 ++++---- src/commands/leave.ts | 13 +++++--- src/commands/ping.ts | 26 +++++---------- src/commands/prefix-response.ts | 4 +-- src/commands/qr.ts | 54 +++++++++++++++++------------- src/commands/quote.ts | 47 +++++++++++++++----------- src/commands/random-int.ts | 32 +++++++++++------- src/commands/random-string.ts | 23 ++++++------- src/commands/shutdown.ts | 36 ++++++++++---------- src/commands/start.ts | 7 ++-- src/commands/system-info.ts | 16 +++++---- src/commands/test.ts | 8 ++--- src/commands/title.ts | 15 ++++++--- src/commands/transliteration.ts | 9 ++--- src/commands/unban.ts | 22 ++++++++----- src/commands/unignore.ts | 14 ++++---- src/commands/uptime.ts | 7 ++-- src/commands/what-better.ts | 8 ++--- src/commands/when.ts | 58 ++++++++------------------------- 33 files changed, 341 insertions(+), 299 deletions(-) diff --git a/src/callback_commands/cancel.ts b/src/callback_commands/cancel.ts index 313a35f..11deecc 100644 --- a/src/callback_commands/cancel.ts +++ b/src/callback_commands/cancel.ts @@ -1,8 +1,9 @@ import {CallbackCommand} from "../base/callback-command"; +import {Environment} from "../common/environment"; export class Cancel extends CallbackCommand { - text = "❌ Отменить"; + text = Environment.cancelText; data = ""; constructor(text?: string, data?: string) { @@ -19,4 +20,4 @@ export class Cancel extends CallbackCommand { async execute(): Promise { return Promise.resolve(); } -} \ No newline at end of file +} diff --git a/src/commands/admins-add.ts b/src/commands/admins-add.ts index 15413dc..b99901e 100644 --- a/src/commands/admins-add.ts +++ b/src/commands/admins-add.ts @@ -8,8 +8,8 @@ import {botUser} from "../index"; export class AdminsAdd extends Command { command = "addAdmin"; - title = "/addAdmin"; - description = "Add user to admins"; + title = Environment.commandTitles.adminsAdd; + description = Environment.commandDescriptions.adminsAdd; requirements = Requirements.Build( Requirement.BOT_CREATOR, @@ -24,19 +24,19 @@ export class AdminsAdd extends Command { const text = fullName(msg.reply_to_message.from); if (id === botUser.id) { - await oldSendMessage(msg, "Бот не может сам себя сделать админом").catch(logError); + await oldSendMessage(msg, Environment.botCannotMakeItselfAdminText).catch(logError); return; } if (id === Environment.CREATOR_ID) { - await oldSendMessage(msg, "Создатель бота и так является админом").catch(logError); + await oldSendMessage(msg, Environment.botCreatorAlreadyAdminText).catch(logError); return; } if (await Environment.addAdmin(id)) { - await oldSendMessage(msg, text + " теперь админ!").catch(logError); + await oldSendMessage(msg, Environment.getUserIsNowAdminText(text)).catch(logError); } else { - await oldSendMessage(msg, text + " и так уже админ 🤔").catch(logError); + await oldSendMessage(msg, Environment.getUserAlreadyAdminText(text)).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/admins-list.ts b/src/commands/admins-list.ts index e94a8ac..17eaed4 100644 --- a/src/commands/admins-list.ts +++ b/src/commands/admins-list.ts @@ -3,7 +3,7 @@ import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Environment} from "../common/environment"; -import {fullName, logError, replyToMessage, sendErrorPlaceholder} from "../util/utils"; +import {escapePlainMarkdownV2, fullName, logError, replyToMessage, sendErrorPlaceholder} from "../util/utils"; import {StoredUser} from "../model/stored-user"; import {UserStore} from "../common/user-store"; @@ -29,14 +29,14 @@ export class AdminsList extends Command { } } - let text = "*Администраторы*:\n\n"; + let text = Environment.administratorsHeaderText; users.forEach(user => { text += "\\* "; if (user) { - text += `[${fullName(user)}](tg://user?id=${user.id})`; + text += `[${escapePlainMarkdownV2(fullName(user))}](tg://user?id=${user.id})`; } else { - text += "Нет информации о пользователе"; + text += Environment.noUserInfoText; } text += "\n"; @@ -52,4 +52,4 @@ export class AdminsList extends Command { await sendErrorPlaceholder(msg).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/admins-remove.ts b/src/commands/admins-remove.ts index a452426..1b14629 100644 --- a/src/commands/admins-remove.ts +++ b/src/commands/admins-remove.ts @@ -8,8 +8,8 @@ import {botUser} from "../index"; export class AdminsRemove extends Command { command = "removeAdmin"; - title = "/removeAdmin"; - description = "Remove user from admins"; + title = Environment.commandTitles.adminsRemove; + description = Environment.commandDescriptions.adminsRemove; requirements = Requirements.Build( Requirement.BOT_CREATOR, @@ -24,19 +24,19 @@ export class AdminsRemove extends Command { const text = fullName(msg.reply_to_message.from); if (id === botUser.id) { - await oldSendMessage(msg, "Бот не может сам себя убрать из админов").catch(logError); + await oldSendMessage(msg, Environment.botCannotRemoveItselfFromAdminsText).catch(logError); return; } if (id === Environment.CREATOR_ID) { - await oldSendMessage(msg, "Создатель бота не может перестать быть админом").catch(logError); + await oldSendMessage(msg, Environment.botCreatorCannotStopBeingAdminText).catch(logError); return; } if (await Environment.removeAdmin(id)) { - await oldSendMessage(msg, text + " больше не админ!").catch(logError); + await oldSendMessage(msg, Environment.getUserNoLongerAdminText(text)).catch(logError); } else { - await oldSendMessage(msg, text + " и так не был админом 🤔").catch(logError); + await oldSendMessage(msg, Environment.getUserWasNotAdminText(text)).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/ae.ts b/src/commands/ae.ts index cb3c770..338b3ae 100644 --- a/src/commands/ae.ts +++ b/src/commands/ae.ts @@ -3,14 +3,15 @@ import {Message} from "typescript-telegram-bot-api"; import {errorPlaceholder, logError, oldSendMessage} from "../util/utils"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; +import {Environment} from "../common/environment"; export class Ae extends Command { argsMode = "required" as const; command = ["ae"]; - title = "/ae"; - description = "evaluation"; + title = Environment.commandTitles.ae; + description = Environment.commandDescriptions.ae; requirements = Requirements.Build(Requirement.BOT_CREATOR); @@ -24,7 +25,7 @@ export class Ae extends Command { const text = e.message.toString(); if (text.includes("is not defined")) { - await oldSendMessage(msg, "variable is not defined").catch(logError); + await oldSendMessage(msg, Environment.variableNotDefinedText).catch(logError); return; } @@ -46,7 +47,7 @@ export class Ae extends Command { const text = e.message.toString(); if (text.includes("is not defined")) { - return "Variable not defined"; + return Environment.evaluationVariableNotDefinedText; } logError(`${text} @@ -55,4 +56,4 @@ export class Ae extends Command { return text; } } -} \ No newline at end of file +} diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 890465a..cdd1663 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -5,10 +5,11 @@ import {Message} from "typescript-telegram-bot-api"; import {bot, botUser} from "../index"; import {fullName, logError, oldSendMessage, oldReplyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; export class Ban extends Command { - title = "/ban [reply]"; - description = "ban user from chat"; + title = Environment.commandTitles.ban; + description = Environment.commandDescriptions.ban; requirements = Requirements.Build( Requirement.BOT_ADMIN, @@ -25,26 +26,29 @@ export class Ban extends Command { const userId = user.id; if (userId === botUser.id) { - await oldReplyToMessage(msg, "Используй /leave").catch(logError); + await oldReplyToMessage(msg, Environment.useLeaveCommandText).catch(logError); return; } if (userId === Environment.CREATOR_ID) { - await oldReplyToMessage(msg, "Бот не будет банить своего создателя.").catch(logError); + await oldReplyToMessage(msg, Environment.botWillNotBanCreatorText).catch(logError); return; } if (msg.from.id !== Environment.CREATOR_ID && Environment.ADMIN_IDS.has(userId)) { - await oldReplyToMessage(msg, "Бот не будет банить своих администраторов.").catch(logError); + await oldReplyToMessage(msg, Environment.botWillNotBanAdminsText).catch(logError); return; } - bot.banChatMember({chat_id: msg.chat.id, user_id: userId}) + enqueueTelegramApiCall( + () => bot.banChatMember({chat_id: msg.chat.id, user_id: userId}), + {method: "banChatMember", chatId: msg.chat.id, chatType: msg.chat.type} + ) .then(async () => { - await oldSendMessage(msg, `${fullName(user)} забанен 🚫`).catch(logError); + await oldSendMessage(msg, Environment.getUserBannedText(fullName(user))).catch(logError); }) .catch(async () => { - await oldSendMessage(msg, `Не смог забанить ${fullName(user)} ☹️`).catch(logError); + await oldSendMessage(msg, Environment.getUserBanFailedText(fullName(user))).catch(logError); }); } -} \ No newline at end of file +} diff --git a/src/commands/choice.ts b/src/commands/choice.ts index e0a66c0..cad8f5b 100644 --- a/src/commands/choice.ts +++ b/src/commands/choice.ts @@ -1,13 +1,15 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage, randomValue} from "../util/utils"; +import {prepareTelegramMarkdownV2} from "../util/markdown-v2-renderer"; +import {Environment} from "../common/environment"; export class Choice extends Command { command = "choice"; argsMode = "required" as const; - title = "/choice a, b, ..., c"; - description = "Выбор случайного значения"; + title = Environment.commandTitles.choice; + description = Environment.commandDescriptions.choice; async execute(msg: Message, match?: RegExpExecArray): Promise { console.log("match", match); @@ -33,7 +35,15 @@ export class Choice extends Command { } const random = randomValue(out); + if (!random) { + await oldReplyToMessage(msg, Environment.noChoicesText).catch(logError); + return; + } - await oldReplyToMessage(msg, `Выбрал *${random}*`, "Markdown").catch(logError); + await oldReplyToMessage( + msg, + Environment.getChoiceText(prepareTelegramMarkdownV2(random, {mode: "final"})), + "MarkdownV2" + ).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/coin.ts b/src/commands/coin.ts index a71efb0..5f9d7ba 100644 --- a/src/commands/coin.ts +++ b/src/commands/coin.ts @@ -1,13 +1,15 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils"; +import {Environment} from "../common/environment"; export class Coin extends Command { - title = "/coin"; - description = "Heads or tails"; + title = Environment.commandTitles.coin; + description = Environment.commandDescriptions.coin; async execute(msg: Message): Promise { const random = getRangedRandomInt(0, 2); - const headsOrTails = random === 1 ? "Выпал *Орёл* 🪙" : "Выпала *Решка* 🪙"; - await oldReplyToMessage(msg, headsOrTails, "Markdown").catch(logError); } -} \ No newline at end of file + const headsOrTails = Environment.getCoinResultText(random === 1 ? Environment.coinHeadsText : Environment.coinTailsText) + " 🪙"; + await oldReplyToMessage(msg, headsOrTails, "Markdown").catch(logError); + } +} diff --git a/src/commands/debug.ts b/src/commands/debug.ts index d0cce74..1260d5c 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -3,10 +3,11 @@ import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {logError, replyToMessage} from "../util/utils"; +import {Environment} from "../common/environment"; export class Debug extends Command { - title = "/debug"; - description = "Returns msg (or reply) as json"; + title = Environment.commandTitles.debug; + description = Environment.commandDescriptions.debug; requirements = Requirements.Build(Requirement.BOT_ADMIN); @@ -17,4 +18,4 @@ export class Debug extends Command { const text = `\`\`\`json\n${json}\n\`\`\``; await replyToMessage({message: msg, text: text, parse_mode: "Markdown"}).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/dice.ts b/src/commands/dice.ts index 94b8902..77e40e7 100644 --- a/src/commands/dice.ts +++ b/src/commands/dice.ts @@ -2,26 +2,31 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, randomValue} from "../util/utils"; import {bot} from "../index"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; type DiceEmoji = "🎲" | "🎯" | "🏀" | "⚽" | "🎳" | "🎰"; -const emojis = ["🎲", "🎯", "🏀", "⚽", "🎳", "🎰"]; +const emojis: readonly DiceEmoji[] = ["🎲", "🎯", "🏀", "⚽", "🎳", "🎰"]; export class Dice extends Command { - title = "/dice"; - description = "Sends random or specific dice"; + title = Environment.commandTitles.dice; + description = Environment.commandDescriptions.dice; async execute(msg: Message): Promise { const split = msg.text?.split("/dice "); const secondPart = split?.[1]?.trim() || ""; - const emojiIndex = emojis.indexOf(secondPart); - const emojiToDice: DiceEmoji = (emojiIndex >= 0 ? emojis[emojiIndex] : randomValue(emojis)) as DiceEmoji; + const requestedEmoji = secondPart as DiceEmoji; + const emojiToDice = emojis.includes(requestedEmoji) ? requestedEmoji : randomValue(emojis) ?? "🎲"; - await bot.sendDice({ - chat_id: msg.chat.id, - emoji: emojiToDice, - reply_parameters: { - message_id: msg.message_id - } - }).catch(logError); + await enqueueTelegramApiCall( + () => bot.sendDice({ + chat_id: msg.chat.id, + emoji: emojiToDice, + reply_parameters: { + message_id: msg.message_id + } + }), + {method: "sendDice", chatId: msg.chat.id, chatType: msg.chat.type} + ).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/distort.ts b/src/commands/distort.ts index 7785458..65b8a22 100644 --- a/src/commands/distort.ts +++ b/src/commands/distort.ts @@ -2,13 +2,15 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {downloadTelegramFile, extractImageFileId, logError, oldReplyToMessage, waveDistortSharp} from "../util/utils"; import {bot} from "../index"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; export class Distort extends Command { command = "distort"; argsMode = "optional" as const; - title = "/distort [amp] [wavelength]"; - description = "Distortion of picture"; + title = Environment.commandTitles.distort; + description = Environment.commandDescriptions.distort; async execute(msg: Message, match?: RegExpExecArray): Promise { const chatId = msg.chat.id; @@ -17,7 +19,7 @@ export class Distort extends Command { if (!reply) { await oldReplyToMessage( msg, - "Ответь командой /distort на сообщение с картинкой (фото, документ или стикер).\n" + "Пример: /distort 16 80" + Environment.distortReplyInstructionText ); return; } @@ -26,7 +28,7 @@ export class Distort extends Command { if (!fileId) { await oldReplyToMessage( msg, - "В реплае не вижу картинку. Пришли фото или файл-изображение." + Environment.distortMissingImageText ); return; } @@ -37,7 +39,10 @@ export class Distort extends Command { const wavelength = b ? Number(b) : 72; try { - await bot.sendChatAction({chat_id: chatId, action: "upload_photo"}); + await enqueueTelegramApiCall( + () => bot.sendChatAction({chat_id: chatId, action: "upload_photo"}), + {method: "sendChatAction", chatId, chatType: msg.chat.type} + ); const file = await bot.getFile({file_id: fileId}); if (!file.file_path) { @@ -49,15 +54,18 @@ export class Distort extends Command { const outBuf = await waveDistortSharp(inputBuf, amp, wavelength); - await bot.sendPhoto({ - chat_id: chatId, - photo: outBuf, - caption: `Искажение готово ✅ (amp=${amp}, wavelength=${wavelength})`, - }); + await enqueueTelegramApiCall( + () => bot.sendPhoto({ + chat_id: chatId, + photo: outBuf, + caption: Environment.getDistortionReadyCaption(amp, wavelength), + }), + {method: "sendPhoto", chatId, chatType: msg.chat.type} + ); } catch (e: any) { await oldReplyToMessage( - msg, `Не получилось исказить изображение: ${e?.message ?? String(e)}` + msg, Environment.getDistortFailedText(e) ).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/export-db.ts b/src/commands/export-db.ts index 83cf2ad..3b73a19 100644 --- a/src/commands/export-db.ts +++ b/src/commands/export-db.ts @@ -6,6 +6,7 @@ import {Environment} from "../common/environment"; import fs from "node:fs"; import {logError, replyToMessage, sendErrorPlaceholder} from "../util/utils"; import {bot} from "../index"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; export class ExportDb extends Command { @@ -23,20 +24,21 @@ export class ExportDb extends Command { } try { - const buffer = fs.readFileSync(fullPath); - - await bot.sendDocument({ - chat_id: Environment.CREATOR_ID, - document: new FileOptions(buffer, {filename: "database.db", contentType: "application/sql"}), - caption: "Бэкап базы данных", - }); + await enqueueTelegramApiCall( + () => bot.sendDocument({ + chat_id: Environment.CREATOR_ID, + document: new FileOptions(fs.createReadStream(fullPath), {filename: "database.db", contentType: "application/sql"}), + caption: Environment.databaseBackupCaption, + }), + {method: "sendDocument", chatId: Environment.CREATOR_ID, chatType: "private"} + ); if (msg.chat.id !== Environment.CREATOR_ID) { - await replyToMessage({message: msg, text: "Успешно отправлено в ЛС создателю!"}); + await replyToMessage({message: msg, text: Environment.databaseBackupSentText}); } } catch (e) { logError(e); await sendErrorPlaceholder(msg); } } -} \ No newline at end of file +} diff --git a/src/commands/help.ts b/src/commands/help.ts index df362d4..16572eb 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -3,16 +3,17 @@ import {chatCommandToString, delay, logError, sendMessage} from "../util/utils"; import {Command} from "../base/command"; import {commands} from "../index"; import {TelegramError} from "typescript-telegram-bot-api/dist/errors"; +import {Environment} from "../common/environment"; export class Help extends Command { command = ["h", "help"]; - title = "/help"; - description = "Show list of commands"; + title = Environment.commandTitles.help; + description = Environment.commandDescriptions.help; async execute(msg: Message) { if (!msg.from) return; - let text = "Commands:\n\n"; + let text = Environment.commandsHeaderText; commands.forEach(c => { text += `${chatCommandToString(c)}\n`; @@ -21,7 +22,7 @@ export class Help extends Command { await sendMessage({chat_id: msg.from.id, text: text}) .then(async () => { if (msg.chat.type !== "private") { - await sendMessage({message: msg, text: "Отправил команды в ЛС 😎"}).catch(logError); + await sendMessage({message: msg, text: Environment.sentCommandsInDmText}).catch(logError); } }) .catch(async (e) => { @@ -29,7 +30,7 @@ export class Help extends Command { if (e.response?.error_code === 403) { await sendMessage({ message: msg, - text: "Не смог отправить команды в ЛС ☹️\nТогда отправлю сюда" + text: Environment.couldNotSendCommandsInDmText }).catch(logError); await delay(1000); @@ -38,4 +39,4 @@ export class Help extends Command { } }); } -} \ No newline at end of file +} diff --git a/src/commands/id.ts b/src/commands/id.ts index 911f496..b3eb296 100644 --- a/src/commands/id.ts +++ b/src/commands/id.ts @@ -1,17 +1,17 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage} from "../util/utils"; +import {Environment} from "../common/environment"; export class Id extends Command { - title = "/id"; - description = "ID of chat, user and reply (if replied to any message)"; + title = Environment.commandTitles.id; + description = Environment.commandDescriptions.id; async execute(msg: Message): Promise { - let text = `chat id: \n\`\`\`${msg.chat.id}\`\`\` \nfrom id: \n\`\`\`${msg.from?.id}\`\`\``; - if (msg.reply_to_message) { - text += ` \nreply id: \n\`\`\`${msg.reply_to_message.from?.id}\`\`\``; - } - - await oldReplyToMessage(msg, text, "MarkdownV2").catch(logError); + await oldReplyToMessage( + msg, + Environment.getIdText(msg.chat.id, msg.from?.id, msg.reply_to_message?.from?.id), + "MarkdownV2", + ).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index ab20492..2a6edf5 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -7,8 +7,8 @@ import {botUser} from "../index"; import {Environment} from "../common/environment"; export class Ignore extends Command { - title = "/ignore"; - description = "Bot will ignore user"; + title = Environment.commandTitles.ignore; + description = Environment.commandDescriptions.ignore; requirements = Requirements.Build( Requirement.BOT_ADMIN, @@ -25,19 +25,19 @@ export class Ignore extends Command { const text = fullName(msg.reply_to_message.from); if (id === botUser.id) { - await oldSendMessage(msg, "Бот не может сам себя игнорировать").catch(logError); + await oldSendMessage(msg, Environment.botWillNotIgnoreItselfText).catch(logError); return; } if (id === Environment.CREATOR_ID) { - await oldSendMessage(msg, "Бот не будет игнорировать своего создателя").catch(logError); + await oldSendMessage(msg, Environment.botWillNotIgnoreCreatorText).catch(logError); return; } if (await Environment.addMute(id)) { - await oldSendMessage(msg, text + " в муте! 🔇").catch(logError); + await oldSendMessage(msg, Environment.getUserIgnoredText(text)).catch(logError); } else { - await oldSendMessage(msg, text + " уже в муте 🤔").catch(logError); + await oldSendMessage(msg, Environment.getUserAlreadyIgnoredText(text)).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/leave.ts b/src/commands/leave.ts index 40446aa..a8cfd1c 100644 --- a/src/commands/leave.ts +++ b/src/commands/leave.ts @@ -3,10 +3,12 @@ import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {bot} from "../index"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; export class Leave extends Command { - title = "/leave"; - description = "Bot will leave current chat"; + title = Environment.commandTitles.leave; + description = Environment.commandDescriptions.leave; requirements = Requirements.Build( Requirement.BOT_ADMIN, @@ -14,6 +16,9 @@ export class Leave extends Command { ); async execute(msg: Message): Promise { - await bot.leaveChat({chat_id: msg.chat.id}); + await enqueueTelegramApiCall( + () => bot.leaveChat({chat_id: msg.chat.id}), + {method: "leaveChat", chatId: msg.chat.id, chatType: msg.chat.type} + ); } -} \ No newline at end of file +} diff --git a/src/commands/ping.ts b/src/commands/ping.ts index ed453c6..5504086 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,46 +1,38 @@ import {logError, sendMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; import {Command} from "../base/command"; +import {Environment} from "../common/environment"; export class Ping extends Command { - title = "/ping"; - description = "Ping between received and sent message"; + title = Environment.commandTitles.ping; + description = Environment.commandDescriptions.ping; async execute(msg: Message) { let d = new Date(); const u = (n: number): string => n > 9 ? n.toString() : `0${n}`; - const date = `${u(d.getDay())}.${u(d.getMonth() + 1)}.${d.getFullYear()}`; + const date = `${u(d.getDate())}.${u(d.getMonth() + 1)}.${d.getFullYear()}`; const time = `${u(d.getHours())}:${u(d.getMinutes())}:${u(d.getSeconds())}:${u(d.getMilliseconds())}`; const mDate = msg.date; const nowDate = new Date().getTime() / 1000; const diff = nowDate - mDate; - const tgPing = diff.toFixed(2); + const tgPing = (diff * 1000).toFixed(0); d = new Date(mDate * 1000); - const msgDate = `${u(d.getDay())}.${u(d.getMonth() + 1)}.${d.getFullYear()}`; + const msgDate = `${u(d.getDate())}.${u(d.getMonth() + 1)}.${d.getFullYear()}`; const msgTime = `${u(d.getHours())}:${u(d.getMinutes())}:${u(d.getSeconds())}:${u(d.getMilliseconds())}`; const then = Date.now(); - await sendMessage({message: msg, text: "pong"}).catch(logError); + await sendMessage({message: msg, text: Environment.pongText}).catch(logError); const now = Date.now(); const msgSendDiff = (now - then).toFixed(2); await sendMessage( { message: msg, - text: - "```ping\n" + - `TG: ${tgPing}ms\n` + - `API ${msgSendDiff}ms\n\n` + - - `🗓️ Message date: ${msgDate}\n` + - `🕒 Message time: ${msgTime}\n\n` + - `🗓️ Local date : ${date}\n` + - `🕒 Local time: ${time}` + - "```", + text: Environment.getPingReportText(tgPing, msgSendDiff, msgDate, msgTime, date, time), parse_mode: "Markdown" } ).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/prefix-response.ts b/src/commands/prefix-response.ts index f4f7c3b..5ebf860 100644 --- a/src/commands/prefix-response.ts +++ b/src/commands/prefix-response.ts @@ -5,6 +5,6 @@ import {Environment} from "../common/environment"; export class PrefixResponse extends Command { async execute(msg: Message): Promise { - await replyToMessage({message: msg, text: randomValue(Environment.ANSWERS.prefix)}).catch(logError); + await replyToMessage({message: msg, text: randomValue(Environment.ANSWERS.prefix) ?? Environment.prefixFallbackText}).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/qr.ts b/src/commands/qr.ts index 851036c..c3ddc75 100644 --- a/src/commands/qr.ts +++ b/src/commands/qr.ts @@ -1,15 +1,17 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; -import {extractMessagePayload, logError, replyToMessage} from "../util/utils"; +import {escapeHtml, extractMessagePayload, logError, replyToMessage} from "../util/utils"; import {bot, botUser} from "../index"; import QRCode from "qrcode"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; export class Qr extends Command { argsMode = "optional" as const; - title = "/qr"; - description = "Generates QR-code from text you sent or replied to."; + title = Environment.commandTitles.qr; + description = Environment.commandDescriptions.qr; async execute(msg: Message, match?: RegExpExecArray): Promise { const chatId = msg.chat.id; @@ -19,27 +21,29 @@ export class Qr extends Command { await replyToMessage( { message: msg, - text: "Не найден текст для генерации QR-кода." + text: Environment.qrCodeMissingTextText } ); return; } - // TODO: 16/02/2026, Danil Nikolaev: escape html symbols in payload - - if (payload.length > 1500) { - payload = payload.slice(0, 1500); + const maxQrPayloadLength = 1500; + if (payload.length > maxQrPayloadLength) { + payload = payload.slice(0, maxQrPayloadLength); await replyToMessage( { message: msg, - text: `Слишком длинный текст для QR (${payload.length} символов). Текст будет обрезан до 1500 символов.` + text: Environment.getQrCodeTextTooLongText(payload.length, maxQrPayloadLength) } ); } try { - await bot.sendChatAction({chat_id: chatId, action: "upload_photo"}); + await enqueueTelegramApiCall( + () => bot.sendChatAction({chat_id: chatId, action: "upload_photo"}), + {method: "sendChatAction", chatId, chatType: msg.chat.type} + ); const pngBuffer = await QRCode.toBuffer(payload, { type: "png", @@ -49,23 +53,27 @@ export class Qr extends Command { }); const maxCaptionLength = botUser.is_premium ? 4096 : 1024; + const visiblePayload = payload.length > maxCaptionLength - 80 + ? payload.slice(0, maxCaptionLength - 83) + "..." + : payload; - await bot.sendPhoto({ - chat_id: chatId, - photo: pngBuffer, - caption: "QR-код готов ✅\nСодержимое:\n
" + - `${payload.length > maxCaptionLength ? payload.slice(0, maxCaptionLength - 40) + "..." : payload}` + - "
", - reply_parameters: { - message_id: msg.message_id, - }, - parse_mode: "HTML" - }); + await enqueueTelegramApiCall( + () => bot.sendPhoto({ + chat_id: chatId, + photo: pngBuffer, + caption: Environment.getQrCodeReadyText(escapeHtml(visiblePayload)), + reply_parameters: { + message_id: msg.message_id, + }, + parse_mode: "HTML" + }), + {method: "sendPhoto", chatId, chatType: msg.chat.type} + ); } catch (e: any) { await replyToMessage({ message: msg, - text: `Не получилось сгенерировать QR: ${e?.message ?? String(e)}` + text: Environment.getQrCodeFailedText(e) }).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/quote.ts b/src/commands/quote.ts index e75ac7f..32a9399 100644 --- a/src/commands/quote.ts +++ b/src/commands/quote.ts @@ -17,9 +17,12 @@ import { import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import twemoji from "twemoji"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {AsyncSemaphore} from "../util/async-lock"; +import {Environment} from "../common/environment"; +import {getLruMapValue, setLruMapValue} from "../util/lru-map"; try { - GlobalFonts.registerFromPath("./assets/Inter_18pt-ExtraThin.ttf", "InterExtraThin"); GlobalFonts.registerFromPath("./assets/Inter_18pt-Thin.ttf", "InterThin"); GlobalFonts.registerFromPath("./assets/Inter_18pt-Light.ttf", "InterLight"); GlobalFonts.registerFromPath("./assets/Inter_18pt-Regular.ttf", "Inter"); @@ -40,8 +43,8 @@ export class Quote extends Command { command = ["cit", "citation", "q", "quote"]; argsMode = "none" as const; - title = "/quote"; - description = "Make quote from text (or quote)"; + title = Environment.commandTitles.quote; + description = Environment.commandDescriptions.quote; requirements = Requirements.Build(Requirement.REPLY); @@ -53,31 +56,37 @@ export class Quote extends Command { try { const quoteRaw = (msg.quote?.text ?? reply.text ?? reply.caption ?? "").trim(); if (quoteRaw.length === 0) { - await replyToMessage({message: msg, text: "Не нашёл в сообщении текста 😢"}).catch(logError); + await replyToMessage({message: msg, text: Environment.quoteMissingTextText}).catch(logError); return; } const quote = quoteRaw.length ? quoteRaw : "…"; - const entities = msg.quote ? msg.quote.entities : reply.entities ?? reply.caption_entities ?? []; + const entities = msg.quote ? msg.quote.entities ?? [] : reply.entities ?? reply.caption_entities ?? []; - const png = await renderQuoteCard(msg, quote, reply, entities); - await bot.sendPhoto({ - chat_id: chatId, - photo: png, - reply_parameters: { - message_id: msg.message_id, - }, - }).catch(logError); + const png = await quoteRenderSemaphore.runExclusive(() => renderQuoteCard(msg, quote, reply, entities)); + await enqueueTelegramApiCall( + () => bot.sendPhoto({ + chat_id: chatId, + photo: png, + reply_parameters: { + message_id: msg.message_id, + }, + }), + {method: "sendPhoto", chatId, chatType: msg.chat.type} + ).catch(logError); } catch (e) { logError(e); - await replyToMessage({message: msg, text: "Не смог собрать цитату 😢"}).catch(logError); + await replyToMessage({message: msg, text: Environment.quoteBuildFailedText}).catch(logError); } } } const emojiCache = new Map(); const customEmojiCache = new Map(); +const quoteRenderSemaphore = new AsyncSemaphore(2); +const EMOJI_CACHE_MAX_ENTRIES = 256; +const CUSTOM_EMOJI_CACHE_MAX_ENTRIES = 512; function appleEmojiUrl(emoji: string): string { const codePoints = [...emoji] @@ -104,13 +113,13 @@ async function loadEmoji(emoji: string | undefined): Promise const downloadAndCache = async (url: string): Promise => { const res = await axios.get(url, {responseType: "arraybuffer"}); const img = await loadImage(Buffer.from(res.data)); - emojiCache.set(url, img); + setLruMapValue(emojiCache, url, img, EMOJI_CACHE_MAX_ENTRIES); return img; }; const checkIfCached = async (emoji: string, emojiToUrl: (emoji: string) => string): Promise => { const url = emojiToUrl(emoji); - const cached = emojiCache.get(url); + const cached = getLruMapValue(emojiCache, url); if (cached) return cached; return await downloadAndCache(emojiToUrl(emoji)); }; @@ -128,7 +137,7 @@ async function loadEmoji(emoji: string | undefined): Promise } async function loadCustomEmoji(customEmojiId: string): Promise { - const cached = customEmojiCache.get(customEmojiId); + const cached = getLruMapValue(customEmojiCache, customEmojiId); if (cached) return cached; try { @@ -159,7 +168,7 @@ async function loadCustomEmoji(customEmojiId: string): Promise Number(value)) + .filter(value => Number.isSafeInteger(value)); + const min = values.length === 1 ? 1 : values[0]; + const max = values.length === 1 ? values[0] : values[1]; - const good = max > min; - const sufficient = !!(min && max) && good; + const sufficient = Number.isSafeInteger(min) && Number.isSafeInteger(max); + if (sufficient && min === max) { + await oldSendMessage(msg, Environment.getRandomIntRangeText(min, max, min)).catch(logError); + return; + } - const random = !sufficient ? getRandomInt(Math.pow(2, 60)) : getRangedRandomInt(min, max); + const from = sufficient ? Math.min(min, max) : 0; + const to = sufficient ? Math.max(min, max) : 1_000_000_000; + const random = getRandomInt(to - from + 1) + from; - const randomText = !sufficient ? random.toString() : `[${min}; ${max}]: ${random}`; + const randomText = !sufficient ? random.toString() : Environment.getRandomIntRangeText(from, to, random); await oldSendMessage(msg, randomText).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/random-string.ts b/src/commands/random-string.ts index 31dacc2..174a077 100644 --- a/src/commands/random-string.ts +++ b/src/commands/random-string.ts @@ -1,34 +1,35 @@ import {Command} from "../base/command"; import {getRandomInt, logError, replyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; +import {Environment} from "../common/environment"; export class RandomString extends Command { argsMode = "optional" as const; - title = "/randomString"; - description = "literally random string (up to 4096 symbols)"; + title = Environment.commandTitles.randomString; + description = Environment.commandDescriptions.randomString; async execute(msg: Message) { - // TODO: 01/05/2026, Danil Nikolaev: improve if (!msg.text) return; - const split = msg.text.split(" "); - const l = parseInt(split.length > 1 ? split[1] : "1"); + const [, lengthArg] = msg.text.trim().split(/\s+/); + const requestedLength = Number(lengthArg ?? 1); - const length = (l <= 0 || l > 4096) ? 1 : l; + const length = Number.isSafeInteger(requestedLength) + ? Math.min(4096, Math.max(1, requestedLength)) + : 1; + const characters = Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789"); let result = ""; - const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789"; - for (let i = 0; i < length; i++) { - result += characters.charAt(getRandomInt(characters.length)); + result += characters[getRandomInt(characters.length)]; } await replyToMessage({ message: msg, - text: "
" + result + "
", + text: Environment.getExpandableBlockquoteText(result), parse_mode: "HTML" }).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/shutdown.ts b/src/commands/shutdown.ts index ca57c7a..0bc5af1 100644 --- a/src/commands/shutdown.ts +++ b/src/commands/shutdown.ts @@ -4,45 +4,47 @@ import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {bot} from "../index"; import {delay, logError, randomValue} from "../util/utils"; - -const texts = [ - "ну что-же, господа", - "приятно было с вами пообщаться", - "но мне пора на покой", - "всего хорошего" -]; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; const timings = [1500, 2500]; const timer = [3, 2, 1]; export class Shutdown extends Command { - title = "/shutdown"; - description = "Self-destruction sequence for bot (shutdown)"; + title = Environment.commandTitles.shutdown; + description = Environment.commandDescriptions.shutdown; argsMode = "optional" as const; requirements = Requirements.Build(Requirement.BOT_CREATOR); async execute(msg: Message, match?: RegExpExecArray): Promise { - await bot.sendMessage({chat_id: msg.chat.id, text: "..."}).catch(logError); + const send = async (text: string) => { + await enqueueTelegramApiCall( + () => bot.sendMessage({chat_id: msg.chat.id, text}), + {method: "sendMessage", chatId: msg.chat.id, chatType: msg.chat.type} + ).catch(logError); + }; + + await send(Environment.shutdownFallbackText); const now = match?.[3]?.toLowerCase() === "now"; if (msg.chat.type !== "private" && !now) { - for (const text of texts) { - await delay(randomValue(timings)); - await bot.sendMessage({chat_id: msg.chat.id, text: text}).catch(logError); + for (const text of Environment.shutdownSequenceTexts) { + await delay(randomValue(timings) ?? 1500); + await send(text); } - await delay(randomValue(timings)); + await delay(randomValue(timings) ?? 1500); for (const t of timer) { - await bot.sendMessage({chat_id: msg.chat.id, text: `${t}`}).catch(logError); + await send(`${t}`); await delay(1000); } } - await bot.sendMessage({chat_id: msg.chat.id, text: "*R.I.P*"}).catch(logError); + await send(Environment.shutdownDoneText); delay(2000).then(() => process.exit(0)); } -} \ No newline at end of file +} diff --git a/src/commands/start.ts b/src/commands/start.ts index e224b85..0c4d679 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -2,12 +2,13 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {commands} from "../index"; import {Help} from "./help"; +import {Environment} from "../common/environment"; export class Start extends Command { - title = "/start"; - description = "Start the bot"; + title = Environment.commandTitles.start; + description = Environment.commandDescriptions.start; async execute(msg: Message): Promise { await commands.find(e => e instanceof Help)?.execute(msg); } -} \ No newline at end of file +} diff --git a/src/commands/system-info.ts b/src/commands/system-info.ts index 53a7809..b55b983 100644 --- a/src/commands/system-info.ts +++ b/src/commands/system-info.ts @@ -1,18 +1,20 @@ import {Command} from "../base/command"; import {logError, replyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; +import {Environment} from "../common/environment"; export class SystemInfo extends Command { - title = "/systemInfo"; - description = "System information"; + title = Environment.commandTitles.systemInfo; + description = Environment.commandDescriptions.systemInfo; - private static systemInfoText: string; + private static systemInfoParams: Parameters[0] | null = null; - static setSystemInfo(info: string) { - SystemInfo.systemInfoText = info; + static setSystemInfo(params: Parameters[0]) { + SystemInfo.systemInfoParams = params; } async execute(msg: Message) { - await replyToMessage({message: msg, text: SystemInfo.systemInfoText}).catch(logError); + if (!SystemInfo.systemInfoParams) return; + await replyToMessage({message: msg, text: Environment.getSystemSpecsText(SystemInfo.systemInfoParams)}).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/test.ts b/src/commands/test.ts index ff9eddb..a720203 100644 --- a/src/commands/test.ts +++ b/src/commands/test.ts @@ -5,10 +5,10 @@ import {Environment} from "../common/environment"; export class Test extends Command { regexp = /^(test|тест|еуые|ntcn|инноке(нтий|ш|нтич))$/i; - title = "тест"; - description = "System functionality check"; + title = Environment.commandTitles.test; + description = Environment.commandDescriptions.test; async execute(msg: Message) { - await oldReplyToMessage(msg, randomValue(Environment.ANSWERS.test) || "а").catch(logError); + await oldReplyToMessage(msg, randomValue(Environment.ANSWERS.test) || Environment.defaultTestAnswerText).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/title.ts b/src/commands/title.ts index 2b76b1a..49dc4ac 100644 --- a/src/commands/title.ts +++ b/src/commands/title.ts @@ -4,13 +4,15 @@ import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {logError, oldReplyToMessage} from "../util/utils"; import {bot} from "../index"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; +import {Environment} from "../common/environment"; export class Title extends Command { command = "title"; argsMode = "required" as const; - title = "/title"; - description = "Change group title"; + title = Environment.commandTitles.title; + description = Environment.commandDescriptions.title; requirements = Requirements.Build( Requirement.BOT_ADMIN, @@ -22,10 +24,13 @@ export class Title extends Command { async execute(msg: Message, match?: RegExpExecArray): Promise { const title = (match?.[3] ?? "").trim(); if (title.length === 0) { - await oldReplyToMessage(msg, "Не нашёл название...").catch(logError); + await oldReplyToMessage(msg, Environment.titleMissingText).catch(logError); return; } - await bot.setChatTitle({chat_id: msg.chat.id, title: title}).catch(logError); + await enqueueTelegramApiCall( + () => bot.setChatTitle({chat_id: msg.chat.id, title: title}), + {method: "setChatTitle", chatId: msg.chat.id, chatType: msg.chat.type} + ).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/transliteration.ts b/src/commands/transliteration.ts index 9b29b17..c94c217 100644 --- a/src/commands/transliteration.ts +++ b/src/commands/transliteration.ts @@ -1,6 +1,7 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage, randomValue} from "../util/utils"; +import {Environment} from "../common/environment"; const EN = "`qwertyuiop[]asdfghjkl;'zxcvbnm,./" + @@ -60,7 +61,7 @@ export function fixLayoutAuto( ): string { let guess = detectScript(text); if (guess === "mixed") { - guess = randomValue([true, false]) ? "ru" : "en"; + guess = (randomValue([true, false]) ?? false) ? "ru" : "en"; } if (guess === "en") { @@ -77,8 +78,8 @@ export function fixLayoutAuto( export class Transliteration extends Command { command = ["transliteration", "tr"]; - title = "/tr [text or reply]"; - description = "Transliteration EN <--> RU"; + title = Environment.commandTitles.transliteration; + description = Environment.commandDescriptions.transliteration; async execute(msg: Message): Promise { if (!msg.text && !msg.caption) return; @@ -102,4 +103,4 @@ export class Transliteration extends Command { await oldReplyToMessage(msg, newText).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/unban.ts b/src/commands/unban.ts index a00a4bb..f985816 100644 --- a/src/commands/unban.ts +++ b/src/commands/unban.ts @@ -5,10 +5,11 @@ import {Message} from "typescript-telegram-bot-api"; import {bot, botUser} from "../index"; import {fullName, logError, oldReplyToMessage, oldSendMessage} from "../util/utils"; import {Environment} from "../common/environment"; +import {enqueueTelegramApiCall} from "../util/telegram-api-queue"; export class Unban extends Command { - title = "/unban [reply]"; - description = "unban user from chat"; + title = Environment.commandTitles.unban; + description = Environment.commandDescriptions.unban; requirements = Requirements.Build( Requirement.BOT_ADMIN, @@ -25,26 +26,29 @@ export class Unban extends Command { const userId = user.id; if (userId === botUser.id) { - await oldReplyToMessage(msg, "Бот и так не в бане сам у себя.").catch(logError); + await oldReplyToMessage(msg, Environment.botIsNotBannedByItselfText).catch(logError); return; } if (userId === Environment.CREATOR_ID) { - await oldReplyToMessage(msg, "Создатель бота и так не в бане и никогда не будет.").catch(logError); + await oldReplyToMessage(msg, Environment.botCreatorNeverBannedText).catch(logError); return; } if (msg.from?.id !== Environment.CREATOR_ID && Environment.ADMIN_IDS.has(userId)) { - await oldReplyToMessage(msg, "Админимтраторы бота и так не в бане.").catch(logError); + await oldReplyToMessage(msg, Environment.botAdminsNotBannedText).catch(logError); return; } - bot.unbanChatMember({chat_id: msg.chat.id, user_id: userId}) + enqueueTelegramApiCall( + () => bot.unbanChatMember({chat_id: msg.chat.id, user_id: userId}), + {method: "unbanChatMember", chatId: msg.chat.id, chatType: msg.chat.type} + ) .then(async () => { - await oldSendMessage(msg, `${fullName(user)} разбанен ⛓️‍💥`).catch(logError); + await oldSendMessage(msg, Environment.getUserUnbannedText(fullName(user))).catch(logError); }) .catch(async () => { - await oldSendMessage(msg, `Не смог разбанить ${fullName(user)} ☹️`).catch(logError); + await oldSendMessage(msg, Environment.getUserUnbanFailedText(fullName(user))).catch(logError); }); } -} \ No newline at end of file +} diff --git a/src/commands/unignore.ts b/src/commands/unignore.ts index 085d7f3..ba47a88 100644 --- a/src/commands/unignore.ts +++ b/src/commands/unignore.ts @@ -7,8 +7,8 @@ import {botUser} from "../index"; import {Environment} from "../common/environment"; export class Unignore extends Command { - title = "/unignore"; - description = "Bot will start responding to the user"; + title = Environment.commandTitles.unignore; + description = Environment.commandDescriptions.unignore; requirements = Requirements.Build( Requirement.BOT_ADMIN, Requirement.CHAT, @@ -24,19 +24,19 @@ export class Unignore extends Command { const text = fullName(msg.reply_to_message.from); if (id === botUser.id) { - await oldSendMessage(msg, "Бот и так всегда к себе прислушивается").catch(logError); + await oldSendMessage(msg, Environment.botAlreadyAlwaysListensToItselfText).catch(logError); return; } if (id === Environment.CREATOR_ID) { - await oldSendMessage(msg, "Бот всегда слушает своего создателя").catch(logError); + await oldSendMessage(msg, Environment.botAlwaysListensToCreatorText).catch(logError); return; } if (await Environment.removeMute(id)) { - await oldSendMessage(msg, text + " больше не в муте! 🔈").catch(logError); + await oldSendMessage(msg, Environment.getUserUnignoredText(text)).catch(logError); } else { - await oldSendMessage(msg, text + " не был в муте 🤔").catch(logError); + await oldSendMessage(msg, Environment.getUserWasNotIgnoredText(text)).catch(logError); } } -} \ No newline at end of file +} diff --git a/src/commands/uptime.ts b/src/commands/uptime.ts index c5aa0ea..403c8a3 100644 --- a/src/commands/uptime.ts +++ b/src/commands/uptime.ts @@ -1,12 +1,13 @@ import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {getUptime, logError, oldSendMessage} from "../util/utils"; +import {Environment} from "../common/environment"; export class Uptime extends Command { - title = "/uptime"; - description = "Bot's uptime"; + title = Environment.commandTitles.uptime; + description = Environment.commandDescriptions.uptime; async execute(msg: Message): Promise { await oldSendMessage(msg, getUptime()).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/what-better.ts b/src/commands/what-better.ts index e559c1a..642a910 100644 --- a/src/commands/what-better.ts +++ b/src/commands/what-better.ts @@ -7,8 +7,8 @@ export class WhatBetter extends Command { command = ["what", "что"]; argsMode = "required" as const; - title = "/what better [a] or [b]"; - description = "either a or b randomly (50% chance)"; + title = Environment.commandTitles.whatBetter; + description = Environment.commandDescriptions.whatBetter; private argsRe = /^(better|лучше)\s+([\s\S]+?)\s+(or|или)\s+([\s\S]+)$/i; @@ -19,8 +19,8 @@ export class WhatBetter extends Command { const a = m[2].trim(); const b = m[4].trim(); - const text = `${randomValue(Environment.ANSWERS.better)} ${randomValue([a, b])}`; + const text = `${randomValue(Environment.ANSWERS.better) ?? Environment.betterFallbackText} ${randomValue([a, b]) ?? a}`; await oldSendMessage(msg, text).catch(logError); } -} \ No newline at end of file +} diff --git a/src/commands/when.ts b/src/commands/when.ts index 9a43523..45a39fa 100644 --- a/src/commands/when.ts +++ b/src/commands/when.ts @@ -1,92 +1,60 @@ import {Command} from "../base/command"; import {getRandomInt, getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; +import {Environment} from "../common/environment"; export class When extends Command { command = ["when", "когда"]; argsMode = "required" as const; - title = "/when [value]"; - description = "random date"; + title = Environment.commandTitles.when; + description = Environment.commandDescriptions.when; async execute(msg: Message) { - let text = "через "; + let text = Environment.getWhenPrefixText(); const type = getRandomInt(8); switch (type) { case 0: - text = "сейчас"; + text = Environment.whenNowText; break; case 1: - text = "никогда"; + text = Environment.whenNeverText; break; case 2: //seconds { const seconds = getRangedRandomInt(1, 60); - - text += `${seconds} `; - - text += ( - (seconds == 1 || seconds % 10 == 1) ? "секунду" : - ((seconds > 1 && seconds < 5) || (seconds % 10 > 1 && seconds % 10 < 5)) ? "секунды" : "секунд" - ); + text = Environment.getWhenDurationText(seconds, Environment.whenSecondUnitText); break; } case 3: { const minutes = getRangedRandomInt(1, 60); - - text += `${minutes} `; - - text += ( - (minutes == 1 || minutes % 10 == 1) ? "минуту" : - ((minutes > 1 && minutes < 5) || (minutes % 10 > 1 && minutes % 10 < 5)) ? "минуты" : "минут" - ); + text = Environment.getWhenDurationText(minutes, Environment.whenMinuteUnitText); break; } case 4: { const hours = getRangedRandomInt(1, 24); - - text += `${hours} `; - - text += ( - (hours == 1 || hours % 10 == 1) ? "час" : - ((hours > 1 && hours < 5) || (hours % 10 > 1 && hours % 10 < 5)) ? "часа" : "часов" - ); + text = Environment.getWhenDurationText(hours, Environment.whenHourUnitText); break; } case 5: { const weeks = getRangedRandomInt(1, 4); - - text += `${weeks} `; - - text += (weeks == 1 ? "неделю" : "недель"); + text = Environment.getWhenDurationText(weeks, Environment.whenWeekUnitText); break; } case 6: { const months = getRandomInt(12); - - text += `${months} `; - - text += ( - (months == 1 || months % 10 == 1) ? "месяц" : - ((months > 1 && months < 5) || (months % 10 > 1 && months % 10 < 5)) ? "месяца" : "месяцев" - ); + text = Environment.getWhenDurationText(months, Environment.whenMonthUnitText); break; } case 7: { const years = getRangedRandomInt(1, 100); - - text += `${years} `; - - text += ( - (years == 1 || years % 10 == 1) ? "год" : - ((years > 1 && years < 5) || (years % 10 > 1 && years % 10 < 5)) ? "года" : "лет" - ); + text = Environment.getWhenDurationText(years, Environment.whenYearUnitText); break; } } await oldReplyToMessage(msg, text).catch(logError); } -} \ No newline at end of file +}