diff --git a/.dockerignore b/.dockerignore index ba231c9..33e390a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,5 @@ npm-debug.log .git .gitignore .env -config.env *.md !README.md \ No newline at end of file diff --git a/config.env.example b/.env.example similarity index 100% rename from config.env.example rename to .env.example diff --git a/.gitignore b/.gitignore index ba85ba8..6b42f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -136,11 +136,12 @@ dist lastfm.json sw-blocklist.txt package-lock.json -bun.lock -bun.lockb tmp/ # Executables *.exe yt-dlp -ffmpeg \ No newline at end of file +ffmpeg + +# Bun +bun.lock* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 473f9b7..7971682 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,6 @@ COPY . . RUN chmod +x /usr/src/app/src/plugins/yt-dlp/yt-dlp -VOLUME /usr/src/app/config.env +VOLUME /usr/src/app/.env CMD ["npm", "start"] \ No newline at end of file diff --git a/README.md b/README.md index 71e5cff..37e61a7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![GitHub License](https://img.shields.io/github/license/abocn/TelegramBot)](https://github.com/abocn/TelegramBot/blob/main/LICENSE) +[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=fff)](https://www.typescriptlang.org) [![CodeQL](https://github.com/abocn/TelegramBot/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/abocn/TelegramBot/actions/workflows/github-code-scanning/codeql) [![Dependabot Updates](https://github.com/abocn/TelegramBot/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/abocn/TelegramBot/actions/workflows/dependabot/dependabot-updates) @@ -14,7 +15,7 @@ Kowalski is a a simple Telegram bot made in Node.js. > [!IMPORTANT] > You will only need all of them if you are not running it dockerized. Read ["Running with Docker"](#running-with-docker) for more information. -- Node.js 23 or newer (you can also use [Bun](https://bun.sh)) +- [Bun](https://bun.sh) (latest is suggested) - A Telegram bot (create one at [@BotFather](https://t.me/botfather)) - FFmpeg (only for the `/yt` command) - Docker and Docker Compose (only required for Docker setup) @@ -27,18 +28,20 @@ First, clone the repo with Git: git clone --recurse-submodules https://github.com/ABOCN/TelegramBot ``` -Next, inside the repository directory, create a `config.env` file with some content, which you can see the [example .env file](config.env.example) to fill info with. To see the meaning of each one, see [the Functions section](#configenv-functions). +Next, inside the repository directory, create an `.env` file with some content, which you can see the [example .env file](.env.example) to fill info with. To see the meaning of each one, see [the Functions section](#env-functions). -After editing the file, save all changes and run the bot with ``npm start``. +After editing the file, save all changes and run the bot with ``bun start``. > [!TIP] -> To deal with dependencies, just run ``npm install`` or ``npm i`` at any moment to install all of them. +> To deal with dependencies, just run ``bun install`` or ``bun i`` at any moment to install all of them. ## Running with Docker > [!IMPORTANT] > Please complete the above steps to prepare your local copy for building. You do not need to install FFmpeg on your host system. +--- + > [!NOTE] > Using the `-d` flag when running causes Kowalski to run in the background. If you're just playing around or testing, you may not want to use this flag. @@ -46,7 +49,7 @@ You can also run Kowalski using Docker, which simplifies the setup process. Make ### Using Docker Compose -1. **Make sure to setup your `config.env` file first!** +1. **Make sure to setup your `.env` file first!** 2. **Run the container** @@ -58,7 +61,7 @@ You can also run Kowalski using Docker, which simplifies the setup process. Make If you prefer to use Docker directly, you can use these instructions instead. -1. **Make sure to setup your `config.env` file first!** +1. **Make sure to setup your `.env` file first!** 2. **Build the image** @@ -69,12 +72,13 @@ If you prefer to use Docker directly, you can use these instructions instead. 3. **Run the container** ```bash - docker run -d --name kowalski --restart unless-stopped -v $(pwd)/config.env:/usr/src/app/config.env:ro kowalski + docker run -d --name kowalski --restart unless-stopped -v $(pwd)/.env:/usr/src/app/.env:ro kowalski ``` -## config.env Functions +## .env Functions + > [!IMPORTANT] -> Take care of your ``config.env`` file, as it is so much important and needs to be secret (like your passwords), as anyone can do whatever they want to the bot with this token! +> Take care of your ``.env`` file, as it is so much important and needs to be secret (like your passwords), as anyone can do whatever they want to the bot with this token! - **botSource**: Put the link to your bot source code. - **botPrivacy**: Put the link to your bot privacy policy. @@ -99,7 +103,7 @@ chmod +x src/plugins/yt-dlp/yt-dlp ## Contributors - + Profile pictures of Kowalski contributors Made with [contrib.rocks](https://contrib.rocks). diff --git a/docker-compose.yml b/docker-compose.yml index 981d90a..0aab44a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,6 @@ services: container_name: kowalski restart: unless-stopped volumes: - - ./config.env:/usr/src/app/config.env:ro + - ./.env:/usr/src/app/.env:ro environment: - NODE_ENV=production \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index d7508b0..918bcb8 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,3 +1,6 @@ { - "ignore": ["src/props/*.json", "src/props/*.txt"] + "ignore": ["src/props/*.json", "src/props/*.txt"], + "watch": ["src"], + "ext": "ts,js", + "exec": "bun src/bot.ts" } \ No newline at end of file diff --git a/package.json b/package.json index 379cc96..db30850 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "scripts": { - "start": "nodemon src/bot.js" + "start": "nodemon src/bot.ts" }, "dependencies": { - "@dotenvx/dotenvx": "^1.28.0", - "axios": "^1.7.9", + "@dotenvx/dotenvx": "^1.41.0", + "@types/node": "^22.15.2", + "axios": "^1.9.0", "node-html-parser": "^7.0.1", - "nodemon": "^3.1.7", + "nodemon": "^3.1.10", "telegraf": "^4.16.3", "winston": "^3.17.0" } diff --git a/src/bot.js b/src/bot.ts similarity index 77% rename from src/bot.js rename to src/bot.ts index f053de7..3422e56 100644 --- a/src/bot.js +++ b/src/bot.ts @@ -1,13 +1,13 @@ -const { Telegraf } = require('telegraf'); -const path = require('path'); -const fs = require('fs'); -const { isOnSpamWatch } = require('./spamwatch/spamwatch.js'); -require('@dotenvx/dotenvx').config({ path: "config.env" }); -require('./plugins/ytDlpWrapper.js'); +import { Telegraf } from 'telegraf'; +import path from 'path'; +import fs from 'fs'; +import { isOnSpamWatch } from './spamwatch/spamwatch'; +import '@dotenvx/dotenvx'; +import './plugins/ytDlpWrapper'; // Ensures bot token is set, and not default value if (!process.env.botToken || process.env.botToken === 'InsertYourBotTokenHere') { - console.error('Bot token is not set. Please set the bot token in the config.env file.') + console.error('Bot token is not set. Please set the bot token in the .env file.') process.exit(1) } @@ -19,10 +19,13 @@ const loadCommands = () => { const commandsPath = path.join(__dirname, 'commands'); try { - const files = fs.readdirSync(commandsPath); + const files = fs.readdirSync(commandsPath) + .filter(file => file.endsWith('.ts') || file.endsWith('.js')); + files.forEach((file) => { try { - const command = require(path.join(commandsPath, file)); + const commandPath = path.join(commandsPath, file); + const command = require(commandPath).default || require(commandPath); if (typeof command === 'function') { command(bot, isOnSpamWatch); } @@ -43,7 +46,7 @@ const startBot = async () => { restartCount = 0; } catch (error) { console.error('Failed to start bot:', error.message); - if (restartCount < maxRetries) { + if (restartCount < Number(maxRetries)) { restartCount++; console.log(`Retrying to start bot... Attempt ${restartCount}`); setTimeout(startBot, 5000); diff --git a/src/commands/animal.js b/src/commands/animal.ts similarity index 67% rename from src/commands/animal.js rename to src/commands/animal.ts index 332e0e3..89eecba 100644 --- a/src/commands/animal.js +++ b/src/commands/animal.ts @@ -1,66 +1,76 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const axios = require("axios"); +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; +import { Context, Telegraf } from 'telegraf'; -module.exports = (bot) => { - bot.command("duck", spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +export default (bot: Telegraf) => { + bot.command("duck", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); try { const response = await axios(Resources.duckApi); ctx.replyWithPhoto(response.data.url, { caption: "🦆", + // reply_to_message_id works fine, using this for now to avoid errors + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { const message = Strings.duckApiErr.replace('{error}', error.message); ctx.reply(message, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); return; } }); - bot.command("fox", spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command("fox", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); try { const response = await axios(Resources.foxApi); ctx.replyWithPhoto(response.data.image, { caption: "🦊", + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { const message = Strings.foxApiErr.replace('{error}', error.message); ctx.reply(message, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); return; } }); - bot.command("dog", spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command("dog", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); try { const response = await axios(Resources.dogApi); ctx.replyWithPhoto(response.data.message, { caption: "🐶", + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { const message = Strings.foxApiErr.replace('{error}', error.message); ctx.reply(message, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); return; } }); - bot.command("cat", spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command("cat", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); const apiUrl = `${Resources.catApi}?json=true`; const response = await axios.get(apiUrl); const data = response.data; @@ -70,17 +80,19 @@ module.exports = (bot) => { await ctx.replyWithPhoto(imageUrl, { caption: `🐱`, parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { ctx.reply(Strings.catImgErr, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); }; }); - bot.command(['soggy', 'soggycat'], spamwatchMiddleware, async (ctx) => { + bot.command(['soggy', 'soggycat'], spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { const userInput = ctx.message.text.split(' ')[1]; switch (true) { @@ -89,6 +101,7 @@ module.exports = (bot) => { Resources.soggyCat2, { caption: Resources.soggyCat2, parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); break; @@ -96,6 +109,7 @@ module.exports = (bot) => { case (userInput === "3" || userInput === "sticker"): ctx.replyWithSticker( Resources.soggyCatSticker, { + // @ts-ignore reply_to_message_id: ctx.message.message_id }); break; @@ -105,6 +119,7 @@ module.exports = (bot) => { Resources.soggyCatAlt, { caption: Resources.soggyCatAlt, parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); break; @@ -114,6 +129,7 @@ module.exports = (bot) => { Resources.soggyCat, { caption: Resources.soggyCat, parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); break; diff --git a/src/commands/codename.js b/src/commands/codename.ts similarity index 62% rename from src/commands/codename.js rename to src/commands/codename.ts index 138ae51..7f4a3e5 100644 --- a/src/commands/codename.js +++ b/src/commands/codename.ts @@ -1,11 +1,14 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const axios = require('axios'); -const { verifyInput } = require('../plugins/verifyInput.js'); +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; +import verifyInput from '../plugins/verifyInput'; +import { Context, Telegraf } from 'telegraf'; -async function getDeviceList() { +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +async function getDeviceList({ Strings, ctx }: { Strings: any, ctx: Context & { message: { text: string } } }) { try { const response = await axios.get(Resources.codenameApi); return response.data @@ -15,27 +18,29 @@ async function getDeviceList() { return ctx.reply(message, { parse_mode: "Markdown", + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } } -module.exports = (bot) => { - bot.command(['codename', 'whatis'], spamwatchMiddleware, async (ctx) => { +export default (bot: Telegraf) => { + bot.command(['codename', 'whatis'], spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { const userInput = ctx.message.text.split(" ").slice(1).join(" "); - const Strings = getStrings(ctx.from.language_code); + const Strings = getStrings(ctx.from?.language_code); const { noCodename } = Strings.codenameCheck if(verifyInput(ctx, userInput, noCodename)){ return; } - const jsonRes = await getDeviceList() + const jsonRes = await getDeviceList({ Strings, ctx }) const phoneSearch = Object.keys(jsonRes).find((codename) => codename === userInput); if (!phoneSearch) { return ctx.reply(Strings.codenameCheck.notFound, { parse_mode: "Markdown", + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } @@ -50,6 +55,7 @@ module.exports = (bot) => { return ctx.reply(message, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); }) diff --git a/src/commands/crew.js b/src/commands/crew.ts similarity index 62% rename from src/commands/crew.js rename to src/commands/crew.ts index e2282ea..c55b774 100644 --- a/src/commands/crew.js +++ b/src/commands/crew.ts @@ -1,9 +1,12 @@ -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const os = require('os'); -const { exec } = require('child_process'); -const { error } = require('console'); +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import os from 'os'; +import { exec } from 'child_process'; +import { error } from 'console'; +import { Context, Telegraf } from 'telegraf'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); function getGitCommitHash() { return new Promise((resolve, reject) => { @@ -29,7 +32,7 @@ function updateBot() { }); } -function formatUptime(uptime) { +function formatUptime(uptime: number) { const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); const seconds = Math.floor(uptime % 60); @@ -50,152 +53,190 @@ function getSystemInfo() { `*Uptime:* \`${formatUptime(uptime())}\`\n\n`; } -async function handleAdminCommand(ctx, action, successMessage, errorMessage) { - const Strings = getStrings(ctx.from.language_code); - const userId = ctx.from.id; - const adminArray = JSON.parse("[" + process.env.botAdmins + "]"); - if (adminArray.includes(userId)) { +async function handleAdminCommand(ctx: Context & { message: { text: string } }, action: () => Promise, successMessage: string, errorMessage: string) { + const Strings = getStrings(ctx.from?.language_code); + const userId = ctx.from?.id; + const adminArray = process.env.botAdmins ? process.env.botAdmins.split(',').map(id => parseInt(id.trim())) : []; + if (userId && adminArray.includes(userId)) { try { await action(); - ctx.reply(successMessage, { - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }); + if (successMessage) { + ctx.reply(successMessage, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + } } catch (error) { ctx.reply(errorMessage.replace(/{error}/g, error.message), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } } else { ctx.reply(Strings.noPermission, { + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } } -module.exports = (bot) => { - bot.command('getbotstats', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); +export default (bot: Telegraf) => { + bot.command('getbotstats', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); handleAdminCommand(ctx, async () => { const stats = getSystemInfo(); await ctx.reply(stats, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); }, '', Strings.errorRetrievingStats); }); - bot.command('getbotcommit', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('getbotcommit', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); handleAdminCommand(ctx, async () => { try { const commitHash = await getGitCommitHash(); await ctx.reply(Strings.gitCurrentCommit.replace(/{commitHash}/g, commitHash), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { ctx.reply(Strings.gitErrRetrievingCommit.replace(/{error}/g, error), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } }, '', Strings.gitErrRetrievingCommit); }); - bot.command('updatebot', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('updatebot', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); handleAdminCommand(ctx, async () => { try { const result = await updateBot(); await ctx.reply(Strings.botUpdated.replace(/{result}/g, result), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { ctx.reply(Strings.errorUpdatingBot.replace(/{error}/g, error), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } }, '', Strings.errorUpdatingBot); }); - bot.command('setbotname', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('setbotname', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); const botName = ctx.message.text.split(' ').slice(1).join(' '); handleAdminCommand(ctx, async () => { await ctx.telegram.setMyName(botName); }, Strings.botNameChanged.replace(/{botName}/g, botName), Strings.botNameErr.replace(/{error}/g, error)); }); - bot.command('setbotdesc', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('setbotdesc', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); const botDesc = ctx.message.text.split(' ').slice(1).join(' '); handleAdminCommand(ctx, async () => { await ctx.telegram.setMyDescription(botDesc); }, Strings.botDescChanged.replace(/{botDesc}/g, botDesc), Strings.botDescErr.replace(/{error}/g, error)); }); - bot.command('botkickme', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('botkickme', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); handleAdminCommand(ctx, async () => { + if (!ctx.chat) { + ctx.reply(Strings.chatNotFound, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + return; + } ctx.reply(Strings.kickingMyself, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); await ctx.telegram.leaveChat(ctx.chat.id); }, '', Strings.kickingMyselfErr); }); - bot.command('getfile', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); + bot.command('getfile', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); const botFile = ctx.message.text.split(' ').slice(1).join(' '); + + if (!botFile) { + ctx.reply(Strings.noFileProvided, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + return; + } + handleAdminCommand(ctx, async () => { try { await ctx.replyWithDocument({ + // @ts-ignore source: botFile, caption: botFile + }, { + // @ts-ignore + reply_to_message_id: ctx.message.message_id }); } catch (error) { ctx.reply(Strings.unexpectedErr.replace(/{error}/g, error.message), { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } }, '', Strings.unexpectedErr); }); - bot.command('run', spamwatchMiddleware, async (ctx) => { + bot.command('run', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { const command = ctx.message.text.split(' ').slice(1).join(' '); handleAdminCommand(ctx, async () => { if (!command) { - return ctx.reply('Por favor, forneça um comando para executar.'); + ctx.reply('Por favor, forneça um comando para executar.'); + return; } exec(command, (error, stdout, stderr) => { if (error) { return ctx.reply(`\`${error.message}\``, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } if (stderr) { return ctx.reply(`\`${stderr}\``, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } ctx.reply(`\`${stdout}\``, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); }); }, '', "Nope!"); }); - bot.command('eval', spamwatchMiddleware, async (ctx) => { + bot.command('eval', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { const code = ctx.message.text.split(' ').slice(1).join(' '); if (!code) { return ctx.reply('Por favor, forneça um código para avaliar.'); @@ -205,19 +246,21 @@ module.exports = (bot) => { const result = eval(code); ctx.reply(`Result: ${result}`, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } catch (error) { ctx.reply(`Error: ${error.message}`, { parse_mode: 'Markdown', + // @ts-ignore reply_to_message_id: ctx.message.message_id }); } }); - bot.command('crash', spamwatchMiddleware, async (ctx) => { + bot.command('crash', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { handleAdminCommand(ctx, async () => { - ctx.reply(null); + ctx.reply('Crashed!'); }, '', "Nope!"); }); }; diff --git a/src/commands/fun.js b/src/commands/fun.js deleted file mode 100644 index 0c64bbe..0000000 --- a/src/commands/fun.js +++ /dev/null @@ -1,101 +0,0 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); - -function sendRandomReply(ctx, gifUrl, textKey) { - const Strings = getStrings(ctx.from.language_code); - const randomNumber = Math.floor(Math.random() * 100); - const shouldSendGif = randomNumber > 50; - - const caption = Strings[textKey].replace('{randomNum}', randomNumber) - - if (shouldSendGif) { - ctx.replyWithAnimation(gifUrl, { - caption, - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }).catch(err => { - gifErr = gifErr.replace('{err}', err); - ctx.reply(Strings.gifErr, { - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }); - }); - } else { - ctx.reply(caption, { - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }); - } -} - - -async function handleDiceCommand(ctx, emoji, delay) { - const Strings = getStrings(ctx.from.language_code); - - const result = await ctx.sendDice({ emoji, reply_to_message_id: ctx.message.message_id }); - const botResponse = Strings.funEmojiResult - .replace('{emoji}', result.dice.emoji) - .replace('{value}', result.dice.value); - - setTimeout(() => { - ctx.reply(botResponse, { - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }); - }, delay); -} - -function getRandomInt(max) { - return Math.floor(Math.random() * (max + 1)); -} - -module.exports = (bot) => { - bot.command('random', spamwatchMiddleware, async (ctx) => { - const Strings = getStrings(ctx.from.language_code); - const randomValue = getRandomInt(11); - const randomVStr = Strings.randomNum.replace('{number}', randomValue); - - ctx.reply( - randomVStr, { - parse_mode: 'Markdown', - reply_to_message_id: ctx.message.message_id - }); - }); - - bot.command('dice', spamwatchMiddleware, async (ctx) => { - await handleDiceCommand(ctx, undefined, 4000); - }); - - bot.command('slot', spamwatchMiddleware, async (ctx) => { - await handleDiceCommand(ctx, '🎰', 3000); - }); - - bot.command('ball', spamwatchMiddleware, async (ctx) => { - await handleDiceCommand(ctx, '⚽', 3000); - }); - - bot.command('dart', spamwatchMiddleware, async (ctx) => { - await handleDiceCommand(ctx, '🎯', 3000); - }); - - bot.command('bowling', spamwatchMiddleware, async (ctx) => { - await handleDiceCommand(ctx, '🎳', 3000); - }); - - bot.command('idice', spamwatchMiddleware, async (ctx) => { - ctx.replyWithSticker( - Resources.infiniteDice, { - reply_to_message_id: ctx.message.message_id - }); - }); - - bot.command('furry', spamwatchMiddleware, async (ctx) => { - sendRandomReply(ctx, Resources.furryGif, 'furryAmount'); - }); - - bot.command('gay', spamwatchMiddleware, async (ctx) => { - sendRandomReply(ctx, Resources.gayFlag, 'gayAmount'); - }); -}; \ No newline at end of file diff --git a/src/commands/fun.ts b/src/commands/fun.ts new file mode 100644 index 0000000..b1437bf --- /dev/null +++ b/src/commands/fun.ts @@ -0,0 +1,112 @@ +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import { Context, Telegraf } from 'telegraf'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +function sendRandomReply(ctx: Context & { message: { text: string } }, gifUrl: string, textKey: string) { + const Strings = getStrings(ctx.from?.language_code); + const randomNumber = Math.floor(Math.random() * 100); + const shouldSendGif = randomNumber > 50; + + const caption = Strings[textKey].replace('{randomNum}', randomNumber) + + if (shouldSendGif) { + ctx.replyWithAnimation(gifUrl, { + caption, + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }).catch(err => { + const gifErr = Strings.gifErr.replace('{err}', err); + ctx.reply(gifErr, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + }); + } else { + ctx.reply(caption, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + } +} + + +async function handleDiceCommand(ctx: Context & { message: { text: string } }, emoji: string, delay: number) { + const Strings = getStrings(ctx.from?.language_code); + + // @ts-ignore + const result = await ctx.sendDice({ emoji, reply_to_message_id: ctx.message.message_id }); + const botResponse = Strings.funEmojiResult + .replace('{emoji}', result.dice.emoji) + .replace('{value}', result.dice.value); + + setTimeout(() => { + ctx.reply(botResponse, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + }, delay); +} + +function getRandomInt(max) { + return Math.floor(Math.random() * (max + 1)); +} + +export default (bot: Telegraf) => { + bot.command('random', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + const Strings = getStrings(ctx.from?.language_code); + const randomValue = getRandomInt(11); + const randomVStr = Strings.randomNum.replace('{number}', randomValue); + + ctx.reply( + randomVStr, { + parse_mode: 'Markdown', + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + }); + + // TODO: maybe send custom stickers to match result of the roll? i think there are pre-existing ones + bot.command('dice', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + await handleDiceCommand(ctx, '🎲', 4000); + }); + + bot.command('slot', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + await handleDiceCommand(ctx, '🎰', 3000); + }); + + bot.command('ball', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + await handleDiceCommand(ctx, '⚽', 3000); + }); + + bot.command('dart', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + await handleDiceCommand(ctx, '🎯', 3000); + }); + + bot.command('bowling', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + await handleDiceCommand(ctx, '🎳', 3000); + }); + + bot.command('idice', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + ctx.replyWithSticker( + Resources.infiniteDice, { + // @ts-ignore + reply_to_message_id: ctx.message.message_id + }); + }); + + bot.command('furry', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + sendRandomReply(ctx, Resources.furryGif, 'furryAmount'); + }); + + bot.command('gay', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => { + sendRandomReply(ctx, Resources.gayFlag, 'gayAmount'); + }); +}; \ No newline at end of file diff --git a/src/commands/gsmarena.js b/src/commands/gsmarena.ts similarity index 91% rename from src/commands/gsmarena.js rename to src/commands/gsmarena.ts index 23478c3..43a20ed 100644 --- a/src/commands/gsmarena.js +++ b/src/commands/gsmarena.ts @@ -4,18 +4,23 @@ // With some help from GPT (I don't really like AI but whatever) // If this were a kang, I would not be giving credits to him! -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; +import { parse } from 'node-html-parser'; -const axios = require('axios'); -const { parse } = require('node-html-parser'); +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); -class PhoneSearchResult { - constructor(name, url) { - this.name = name; - this.url = url; - Object.freeze(this); - } +interface PhoneSearchResult { + name: string; + url: string; +} + +interface PhoneDetails { + specs: Record>; + name?: string; + url?: string; + picture?: string; } const HEADERS = { @@ -32,7 +37,7 @@ function getDataFromSpecs(specsData, category, attributes) { .join("\n"); } -function parseSpecs(specsData) { +function parseSpecs(specsData: PhoneDetails): PhoneDetails { const categories = { "status": ["Launch", ["Status"]], "network": ["Network", ["Technology"]], @@ -69,7 +74,7 @@ function parseSpecs(specsData) { const [cat, attrs] = categories[key]; acc[key] = getDataFromSpecs(specsData, cat, attrs) || ""; return acc; - }, {}); + }, { specs: {} } as PhoneDetails); parsedData["name"] = specsData.name || ""; parsedData["url"] = specsData.url || ""; @@ -77,7 +82,7 @@ function parseSpecs(specsData) { return parsedData; } -function formatPhone(phone) { +function formatPhone(phone: PhoneDetails) { const formattedPhone = parseSpecs(phone); const attributesDict = { "Status": "status", @@ -132,7 +137,7 @@ async function fetchHtml(url) { } } -async function searchPhone(phone) { +async function searchPhone(phone: string): Promise { try { const searchUrl = `https://m.gsmarena.com/results.php3?sQuickSearch=yes&sName=${encodeURIComponent(phone)}`; const htmlContent = await fetchHtml(searchUrl); @@ -142,7 +147,7 @@ async function searchPhone(phone) { return foundPhones.map((phoneTag) => { const name = phoneTag.querySelector('img')?.getAttribute('title') || ""; const url = phoneTag.querySelector('a')?.getAttribute('href') || ""; - return new PhoneSearchResult(name, url); + return { name, url }; }); } catch (error) { console.error("Error searching for phone:", error); @@ -164,7 +169,7 @@ async function checkPhoneDetails(url) { return { ...specsData, name, picture, url: `https://www.gsmarena.com/${url}` }; } catch (error) { console.error("Error fetching phone details:", error); - return {}; + return { specs: {}, name: "", url: "", picture: "" }; } } @@ -201,7 +206,7 @@ function getUsername(ctx){ return userName; } -module.exports = (bot) => { +export default (bot) => { bot.command(['d', 'device'], spamwatchMiddleware, async (ctx) => { const userId = ctx.from.id; const userName = getUsername(ctx); diff --git a/src/commands/help.js b/src/commands/help.ts similarity index 85% rename from src/commands/help.js rename to src/commands/help.ts index b4cc44b..95ea804 100644 --- a/src/commands/help.js +++ b/src/commands/help.ts @@ -1,6 +1,17 @@ -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +interface MessageOptions { + parse_mode: string; + disable_web_page_preview: boolean; + reply_markup: { + inline_keyboard: { text: any; callback_data: string; }[][]; + }; + reply_to_message_id?: number; +} async function sendHelpMessage(ctx, isEditing) { const Strings = getStrings(ctx.from.language_code); @@ -11,8 +22,8 @@ async function sendHelpMessage(ctx, isEditing) { function getMessageId(ctx) { return ctx.message?.message_id || ctx.callbackQuery?.message?.message_id; }; - const createOptions = (ctx, includeReplyTo = false) => { - const options = { + const createOptions = (ctx, includeReplyTo = false): MessageOptions => { + const options: MessageOptions = { parse_mode: 'Markdown', disable_web_page_preview: true, reply_markup: { @@ -39,9 +50,9 @@ async function sendHelpMessage(ctx, isEditing) { }; } -module.exports = (bot) => { +export default (bot) => { bot.help(spamwatchMiddleware, async (ctx) => { - await sendHelpMessage(ctx); + await sendHelpMessage(ctx, false); }); bot.command("about", spamwatchMiddleware, async (ctx) => { diff --git a/src/commands/http.js b/src/commands/http.ts similarity index 84% rename from src/commands/http.js rename to src/commands/http.ts index 1382ad6..5e47a49 100644 --- a/src/commands/http.js +++ b/src/commands/http.ts @@ -1,11 +1,13 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const axios = require('axios'); -const { verifyInput } = require('../plugins/verifyInput.js'); +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; +import verifyInput from '../plugins/verifyInput'; -module.exports = (bot) => { +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +export default (bot) => { bot.command("http", spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); const userInput = ctx.message.text.split(' ')[1]; diff --git a/src/commands/info.js b/src/commands/info.ts similarity index 85% rename from src/commands/info.js rename to src/commands/info.ts index eed4874..a19651c 100644 --- a/src/commands/info.js +++ b/src/commands/info.ts @@ -1,6 +1,8 @@ -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); async function getUserInfo(ctx) { const Strings = getStrings(ctx.from.language_code); @@ -9,7 +11,7 @@ async function getUserInfo(ctx) { lastName = " "; } - userInfo = Strings.userInfo + const userInfo = Strings.userInfo .replace('{userName}', `${ctx.from.first_name} ${lastName}` || Strings.varStrings.varUnknown) .replace('{userId}', ctx.from.id || Strings.varStrings.varUnknown) .replace('{userHandle}', ctx.from.username ? `@${ctx.from.username}` : Strings.varStrings.varNone) @@ -22,7 +24,7 @@ async function getUserInfo(ctx) { async function getChatInfo(ctx) { const Strings = getStrings(ctx.from.language_code); if (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup') { - chatInfo = Strings.chatInfo + const chatInfo = Strings.chatInfo .replace('{chatId}', ctx.chat.id || Strings.varStrings.varUnknown) .replace('{chatName}', ctx.chat.title || Strings.varStrings.varUnknown) .replace('{chatHandle}', ctx.chat.username ? `@${ctx.chat.username}` : Strings.varStrings.varNone) @@ -40,7 +42,7 @@ async function getChatInfo(ctx) { } } -module.exports = (bot) => { +export default (bot) => { bot.command('chatinfo', spamwatchMiddleware, async (ctx) => { const chatInfo = await getChatInfo(ctx); ctx.reply( diff --git a/src/commands/lastfm.js b/src/commands/lastfm.ts similarity index 92% rename from src/commands/lastfm.js rename to src/commands/lastfm.ts index 1b1976f..39d6c8d 100644 --- a/src/commands/lastfm.js +++ b/src/commands/lastfm.ts @@ -1,9 +1,11 @@ -const Resources = require('../props/resources.json'); -const fs = require('fs'); -const axios = require('axios'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import Resources from '../props/resources.json'; +import fs from 'fs'; +import axios from 'axios'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); const scrobbler_url = Resources.lastFmApi; const api_key = process.env.lastKey; @@ -35,7 +37,7 @@ function saveUsers() { } } -async function getFromMusicBrainz(mbid) { +async function getFromMusicBrainz(mbid: string) { try { const response = await axios.get(`${Resources.musicBrainzApi}${mbid}`); const imgObjLarge = response.data.images[0]?.thumbnails?.['1200']; @@ -58,7 +60,7 @@ function getFromLast(track) { return imageUrl; } -module.exports = (bot) => { +export default (bot) => { loadUsers(); bot.command('setuser', (ctx) => { @@ -149,7 +151,7 @@ module.exports = (bot) => { const artistUrl = `https://www.last.fm/music/${encodeURIComponent(artistName)}`; const userUrl = `https://www.last.fm/user/${encodeURIComponent(lastfmUser)}`; - let num_plays = ''; + let num_plays = 0; try { const response_plays = await axios.get(scrobbler_url, { params: { @@ -164,11 +166,8 @@ module.exports = (bot) => { 'User-Agent': `@${botInfo.username}-node-telegram-bot` } }); + num_plays = response_plays.data.track.userplaycount; - - if (!num_plays || num_plays === undefined) { - num_plays = 0; - }; } catch (err) { console.log(err) const message = Strings.lastFm.apiErr diff --git a/src/commands/main.js b/src/commands/main.ts similarity index 60% rename from src/commands/main.js rename to src/commands/main.ts index 975762d..2729173 100644 --- a/src/commands/main.js +++ b/src/commands/main.ts @@ -1,9 +1,11 @@ -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; -module.exports = (bot) => { - bot.start(spamwatchMiddleware, async (ctx) => { +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +export default (bot: any) => { + bot.start(spamwatchMiddleware, async (ctx: any) => { const Strings = getStrings(ctx.from.language_code); const botInfo = await ctx.telegram.getMe(); const startMsg = Strings.botWelcome.replace(/{botName}/g, botInfo.first_name); @@ -14,7 +16,7 @@ module.exports = (bot) => { }); }); - bot.command('privacy', spamwatchMiddleware, async (ctx) => { + bot.command('privacy', spamwatchMiddleware, async (ctx: any) => { const Strings = getStrings(ctx.from.language_code); const message = Strings.botPrivacy.replace("{botPrivacy}", process.env.botPrivacy); diff --git a/src/commands/modarchive.js b/src/commands/modarchive.ts similarity index 73% rename from src/commands/modarchive.js rename to src/commands/modarchive.ts index 4e89370..eac76a8 100644 --- a/src/commands/modarchive.js +++ b/src/commands/modarchive.ts @@ -1,12 +1,19 @@ -const Resources = require('../props/resources.json'); -const axios = require('axios'); -const fs = require('fs'); -const path = require('path'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); +import Resources from '../props/resources.json'; +import axios from 'axios'; +import fs from 'fs'; +import path from 'path'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; -async function downloadModule(moduleId) { +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +interface ModuleResult { + filePath: string; + fileName: string; +} + +async function downloadModule(moduleId: string): Promise { try { const downloadUrl = `${Resources.modArchiveApi}${moduleId}`; const response = await axios({ @@ -39,12 +46,12 @@ async function downloadModule(moduleId) { } } -module.exports = (bot) => { +export default (bot) => { bot.command(['modarchive', 'tma'], spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); const moduleId = ctx.message.text.split(' ')[1]; - if (moduleId == NaN || null) { + if (Number.isNaN(moduleId) || null) { return ctx.reply(Strings.maInvalidModule, { parse_mode: "Markdown", reply_to_message_id: ctx.message.message_id diff --git a/src/commands/ponyapi.js b/src/commands/ponyapi.ts similarity index 86% rename from src/commands/ponyapi.js rename to src/commands/ponyapi.ts index e5fbf94..dfa2e1c 100644 --- a/src/commands/ponyapi.js +++ b/src/commands/ponyapi.ts @@ -1,15 +1,56 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const axios = require("axios"); -const { verifyInput } = require('../plugins/verifyInput.js'); +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; +import verifyInput from '../plugins/verifyInput'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +interface Character { + id: string; + name: string; + alias: string; + url: string; + sex: string; + residence: string; + occupation: string; + kind: string; + image: string[]; +} + +interface Episode { + id: string; + name: string; + image: string; + url: string; + season: string; + episode: string; + overall: string; + airdate: string; + storyby: string; + writtenby: string; + storyboard: string; +} + +interface Comic { + id: string; + name: string; + series: string; + image: string; + url: string; + writer: string; + artist: string; + colorist: string; + letterer: string; + editor: string; +} function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } -module.exports = (bot) => { +export default (bot) => { bot.command("mlp", spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); @@ -34,11 +75,11 @@ module.exports = (bot) => { try { const response = await axios(apiUrl); - const charactersArray = []; + const charactersArray: Character[] = []; if (Array.isArray(response.data.data)) { response.data.data.forEach(character => { - let aliases = []; + let aliases: string[] = []; if (character.alias) { if (typeof character.alias === 'string') { aliases.push(character.alias); @@ -107,7 +148,7 @@ module.exports = (bot) => { try { const response = await axios(apiUrl); - const episodeArray = []; + const episodeArray: Episode[] = []; if (Array.isArray(response.data.data)) { response.data.data.forEach(episode => { @@ -175,15 +216,15 @@ module.exports = (bot) => { try { const response = await axios(apiUrl); - const comicArray = []; + const comicArray: Comic[] = []; if (Array.isArray(response.data.data)) { response.data.data.forEach(comic => { - let letterers = []; + let letterers: string[] = []; if (comic.letterer) { if (typeof comic.letterer === 'string') { letterers.push(comic.letterer); } else if (Array.isArray(comic.letterer)) { - letterers = aliases.concat(comic.letterer); + letterers = letterers.concat(comic.letterer); } } comicArray.push({ diff --git a/src/commands/quotes.ts b/src/commands/quotes.ts new file mode 100644 index 0000000..4c59f2a --- /dev/null +++ b/src/commands/quotes.ts @@ -0,0 +1,32 @@ +/* +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import escape from 'markdown-escape'; +import axios from 'axios'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +export default (bot) => { + bot.command("quote", spamwatchMiddleware, async (ctx) => { + const Strings = getStrings(ctx.from.language_code); + + try { + const response = await axios.get(Resources.quoteApi); + const data = response.data; + + ctx.reply(escape(`${escape(Strings.quoteResult)}\n> *${escape(data.quote)}*\n_${escape(data.author)}_`), { + reply_to_message_id: ctx.message.message_id, + parse_mode: 'Markdown' + }); + } catch (error) { + console.error(error); + ctx.reply(Strings.quoteErr, { + reply_to_message_id: ctx.message.id, + parse_mode: 'MarkdownV2' + }); + }; + }); +}; +*/ \ No newline at end of file diff --git a/src/commands/randompony.js b/src/commands/randompony.ts similarity index 72% rename from src/commands/randompony.js rename to src/commands/randompony.ts index 1be1e73..0ca140d 100644 --- a/src/commands/randompony.js +++ b/src/commands/randompony.ts @@ -1,15 +1,17 @@ -const Resources = require('../props/resources.json'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const axios = require("axios"); +import Resources from '../props/resources.json'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import axios from 'axios'; -module.exports = (bot) => { +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); + +export default (bot) => { bot.command(["rpony", "randompony", "mlpart"], spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); try { const response = await axios(Resources.randomPonyApi); - let tags = []; + let tags: string[] = []; if (response.data.pony.tags) { if (typeof response.data.pony.tags === 'string') { diff --git a/src/commands/weather.js b/src/commands/weather.ts similarity index 90% rename from src/commands/weather.js rename to src/commands/weather.ts index be1ebe9..716726c 100644 --- a/src/commands/weather.js +++ b/src/commands/weather.ts @@ -2,12 +2,14 @@ // Copyright (c) 2024 BubbalooTeam. (https://github.com/BubbalooTeam) // Minor code changes by lucmsilva (https://github.com/lucmsilva651) -const Resources = require('../props/resources.json'); -const axios = require('axios'); -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const { verifyInput } = require('../plugins/verifyInput.js'); +import Resources from '../props/resources.json'; +import axios from 'axios'; +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import verifyInput from '../plugins/verifyInput'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); const statusEmojis = { 0: '⛈', 1: '⛈', 2: '⛈', 3: '⛈', 4: '⛈', 5: '🌨', 6: '🌨', 7: '🌨', @@ -31,7 +33,7 @@ function getLocaleUnit(countryCode) { } } -module.exports = (bot) => { +export default (bot) => { bot.command(['clima', 'weather'], spamwatchMiddleware, async (ctx) => { const userLang = ctx.from.language_code || "en-US"; const Strings = getStrings(userLang); diff --git a/src/commands/wiki.ts b/src/commands/wiki.ts new file mode 100644 index 0000000..f6ca6d0 --- /dev/null +++ b/src/commands/wiki.ts @@ -0,0 +1,38 @@ +/* +import axios from "axios"; + +function capitalizeFirstLetter(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +function mediaWikiToMarkdown(input: string) { + input = input.replace(/===(.*?)===/g, '*$1*'); + input = input.replace(/==(.*?)==/g, '*$1*'); + input = input.replace(/=(.*?)=/g, '*$1*'); + input = input.replace(/'''(.*?)'''/g, '**$1**'); + input = input.replace(/''(.*?)''/g, '_$1_'); + input = input.replace(/^\*\s/gm, '- '); + input = input.replace(/^\#\s/gm, '1. '); + input = input.replace(/{{Quote(.*?)}}/g, "```\n$1```\n"); + input = input.replace(/\[\[(.*?)\|?(.*?)\]\]/g, (_, link, text) => { + const sanitizedLink = link.replace(/ /g, '_'); + return text ? `[${text}](${sanitizedLink})` : `[${sanitizedLink}](${sanitizedLink})`; + }); + input = input.replace(/\[\[File:(.*?)\|.*?\]\]/g, '![$1](https://en.wikipedia.org/wiki/File:$1)'); + + return input; +} + +export default (bot) => { + bot.command("wiki", async (ctx) => { + const userInput = capitalizeFirstLetter(ctx.message.text.split(' ')[1]); + const apiUrl = `https://en.wikipedia.org/w/index.php?title=${userInput}&action=raw`; + const response = await axios(apiUrl, { headers: { 'Accept': "text/plain" } }); + const convertedResponse = response.data.replace(/<\/?div>/g, "").replace(/{{Infobox.*?}}/s, ""); + + const result = mediaWikiToMarkdown(convertedResponse).slice(0, 2048); + + ctx.reply(result, { parse_mode: 'Markdown', disable_web_page_preview: true, reply_to_message_id: ctx.message.message_id }); + }); +}; +*/ \ No newline at end of file diff --git a/src/commands/youtube.js b/src/commands/youtube.ts similarity index 89% rename from src/commands/youtube.js rename to src/commands/youtube.ts index d3fa755..9fa6364 100644 --- a/src/commands/youtube.js +++ b/src/commands/youtube.ts @@ -1,10 +1,12 @@ -const { getStrings } = require('../plugins/checkLang.js'); -const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); -const { execFile } = require('child_process'); -const os = require('os'); -const fs = require('fs'); -const path = require('path'); +import { getStrings } from '../plugins/checklang'; +import { isOnSpamWatch } from '../spamwatch/spamwatch'; +import spamwatchMiddlewareModule from '../spamwatch/Middleware'; +import { execFile } from 'child_process'; +import os from 'os'; +import fs from 'fs'; +import path from 'path'; + +const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch); const ytDlpPaths = { linux: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp'), @@ -28,7 +30,7 @@ const getFfmpegPath = () => { return ffmpegPaths[platform] || ffmpegPaths.linux; }; -const downloadFromYoutube = async (command, args) => { +const downloadFromYoutube = async (command: string, args: string[]): Promise<{ stdout: string; stderr: string }> => { return new Promise((resolve, reject) => { execFile(command, args, (error, stdout, stderr) => { if (error) { @@ -40,8 +42,8 @@ const downloadFromYoutube = async (command, args) => { }); }; -const getApproxSize = async (command, videoUrl) => { - let args = []; +const getApproxSize = async (command: string, videoUrl: string): Promise => { + let args: string[] = []; if (fs.existsSync(path.resolve(__dirname, "../props/cookies.txt"))) { args = [videoUrl, '--compat-opt', 'manifest-filesize-approx', '-O', 'filesize_approx', '--cookies', path.resolve(__dirname, "../props/cookies.txt")]; } else { @@ -60,7 +62,7 @@ const getApproxSize = async (command, videoUrl) => { } }; -module.exports = (bot) => { +export default (bot) => { bot.command(['yt', 'ytdl', 'sdl', 'video', 'dl'], spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); const ytDlpPath = getYtDlpPath(); @@ -198,11 +200,8 @@ module.exports = (bot) => { } } catch (error) { const errMsg = Strings.ytDownload.uploadErr.replace("{error}", error) - await ctx.telegram.editMessageText( - ctx.chat.id, - downloadingMessage.message_id, - null, - errMsg, { + // will no longer edit the message as the message context is not outside the try block + await ctx.reply(errMsg, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id, }, diff --git a/src/locales/english.json b/src/locales/english.json index 26e1244..5e21a15 100644 --- a/src/locales/english.json +++ b/src/locales/english.json @@ -110,5 +110,7 @@ "notFound": "Phone not found.", "resultMsg": "*Name:* `{name}`\n*Brand:* `{brand}`\n*Model:* `{model}`\n*Codename:* `{codename}`", "apiErr": "An error occurred while fetching data from the API.\n\n`{err}`" - } + }, + "chatNotFound": "Chat not found.", + "noFileProvided": "Please provide a file to send." } \ No newline at end of file diff --git a/src/plugins/checklang.js b/src/plugins/checklang.ts similarity index 94% rename from src/plugins/checklang.js rename to src/plugins/checklang.ts index bd8f126..a8d1b79 100644 --- a/src/plugins/checklang.js +++ b/src/plugins/checklang.ts @@ -17,6 +17,4 @@ function getStrings(languageCode) { } } -module.exports = { - getStrings -}; \ No newline at end of file +export { getStrings }; \ No newline at end of file diff --git a/src/plugins/verifyInput.js b/src/plugins/verifyInput.js deleted file mode 100644 index feae352..0000000 --- a/src/plugins/verifyInput.js +++ /dev/null @@ -1,14 +0,0 @@ -function verifyInput(ctx, userInput, message, verifyNaN = false) { - if (!userInput || (verifyNaN && isNaN(userInput))) { - ctx.reply(message, { - parse_mode: "Markdown", - reply_to_message_id: ctx.message.message_id - }); - return true; - } - return false; -} - -module.exports = { - verifyInput -}; \ No newline at end of file diff --git a/src/plugins/verifyInput.ts b/src/plugins/verifyInput.ts new file mode 100644 index 0000000..b1355fd --- /dev/null +++ b/src/plugins/verifyInput.ts @@ -0,0 +1,10 @@ +export default function verifyInput(ctx: any, userInput: string, message: string, verifyNaN = false) { + if (!userInput || (verifyNaN && isNaN(Number(userInput)))) { // not sure why isNaN is used here, but the input should be a number + ctx.reply(message, { + parse_mode: "Markdown", + reply_to_message_id: ctx.message.message_id + }); + return true; + } + return false; +} diff --git a/src/plugins/ytDlpWrapper.js b/src/plugins/ytDlpWrapper.ts similarity index 91% rename from src/plugins/ytDlpWrapper.js rename to src/plugins/ytDlpWrapper.ts index 8d2298e..2313b4c 100644 --- a/src/plugins/ytDlpWrapper.js +++ b/src/plugins/ytDlpWrapper.ts @@ -1,7 +1,7 @@ -const axios = require('axios'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); +import axios from 'axios'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; const downloadDir = path.resolve(__dirname, 'yt-dlp');