Migrate to TypeScript, minor changes and fixes (#46)

* docs: linting, require bun for ts

* rf: js -> ts

* chore: bump

* docs: add ts badge

* chore: bump types

* fix: add types for context to animal commands

* [m] hf: add bot type

* fix/types: add bot, ctx types, fix emoji on /dice cmd, add todo

* fix/types: bot admin checking fixes, other misc fixes, add types

---------

Co-authored-by: Lucas Gabriel <lucmsilva651@gmail.com>
This commit is contained in:
Aidan 2025-04-29 15:39:10 -04:00 committed by GitHub
parent 6fd5652afa
commit 07045d8e09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 550 additions and 325 deletions

View File

@ -3,6 +3,5 @@ npm-debug.log
.git .git
.gitignore .gitignore
.env .env
config.env
*.md *.md
!README.md !README.md

5
.gitignore vendored
View File

@ -136,11 +136,12 @@ dist
lastfm.json lastfm.json
sw-blocklist.txt sw-blocklist.txt
package-lock.json package-lock.json
bun.lock
bun.lockb
tmp/ tmp/
# Executables # Executables
*.exe *.exe
yt-dlp yt-dlp
ffmpeg ffmpeg
# Bun
bun.lock*

View File

@ -13,6 +13,6 @@ COPY . .
RUN chmod +x /usr/src/app/src/plugins/yt-dlp/yt-dlp 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"] CMD ["npm", "start"]

View File

@ -2,6 +2,7 @@
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![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) [![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) [![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) [![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] > [!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. > 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)) - A Telegram bot (create one at [@BotFather](https://t.me/botfather))
- FFmpeg (only for the `/yt` command) - FFmpeg (only for the `/yt` command)
- Docker and Docker Compose (only required for Docker setup) - 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 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] > [!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 ## Running with Docker
> [!IMPORTANT] > [!IMPORTANT]
> Please complete the above steps to prepare your local copy for building. You do not need to install FFmpeg on your host system. > Please complete the above steps to prepare your local copy for building. You do not need to install FFmpeg on your host system.
---
> [!NOTE] > [!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. > 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 ### 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** 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. 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** 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** 3. **Run the container**
```bash ```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] > [!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. - **botSource**: Put the link to your bot source code.
- **botPrivacy**: Put the link to your bot privacy policy. - **botPrivacy**: Put the link to your bot privacy policy.
@ -99,7 +103,7 @@ chmod +x src/plugins/yt-dlp/yt-dlp
## Contributors ## Contributors
<a href="https://github.com/abocn/TelegramBot/graphs/contributors"> <a href="https://github.com/abocn/TelegramBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=abocn/TelegramBot" /> <img src="https://contrib.rocks/image?repo=abocn/TelegramBot" alt="Profile pictures of Kowalski contributors" />
</a> </a>
Made with [contrib.rocks](https://contrib.rocks). Made with [contrib.rocks](https://contrib.rocks).

View File

@ -4,6 +4,6 @@ services:
container_name: kowalski container_name: kowalski
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ./config.env:/usr/src/app/config.env:ro - ./.env:/usr/src/app/.env:ro
environment: environment:
- NODE_ENV=production - NODE_ENV=production

View File

@ -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"
} }

View File

@ -1,12 +1,13 @@
{ {
"scripts": { "scripts": {
"start": "nodemon src/bot.js" "start": "nodemon src/bot.ts"
}, },
"dependencies": { "dependencies": {
"@dotenvx/dotenvx": "^1.28.0", "@dotenvx/dotenvx": "^1.41.0",
"axios": "^1.7.9", "@types/node": "^22.15.2",
"axios": "^1.9.0",
"node-html-parser": "^7.0.1", "node-html-parser": "^7.0.1",
"nodemon": "^3.1.7", "nodemon": "^3.1.10",
"telegraf": "^4.16.3", "telegraf": "^4.16.3",
"winston": "^3.17.0" "winston": "^3.17.0"
} }

View File

@ -1,13 +1,13 @@
const { Telegraf } = require('telegraf'); import { Telegraf } from 'telegraf';
const path = require('path'); import path from 'path';
const fs = require('fs'); import fs from 'fs';
const { isOnSpamWatch } = require('./spamwatch/spamwatch.js'); import { isOnSpamWatch } from './spamwatch/spamwatch';
require('@dotenvx/dotenvx').config({ path: "config.env" }); import '@dotenvx/dotenvx';
require('./plugins/ytDlpWrapper.js'); import './plugins/ytDlpWrapper';
// Ensures bot token is set, and not default value // Ensures bot token is set, and not default value
if (!process.env.botToken || process.env.botToken === 'InsertYourBotTokenHere') { 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) process.exit(1)
} }
@ -19,10 +19,13 @@ const loadCommands = () => {
const commandsPath = path.join(__dirname, 'commands'); const commandsPath = path.join(__dirname, 'commands');
try { try {
const files = fs.readdirSync(commandsPath); const files = fs.readdirSync(commandsPath)
.filter(file => file.endsWith('.ts') || file.endsWith('.js'));
files.forEach((file) => { files.forEach((file) => {
try { 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') { if (typeof command === 'function') {
command(bot, isOnSpamWatch); command(bot, isOnSpamWatch);
} }
@ -43,7 +46,7 @@ const startBot = async () => {
restartCount = 0; restartCount = 0;
} catch (error) { } catch (error) {
console.error('Failed to start bot:', error.message); console.error('Failed to start bot:', error.message);
if (restartCount < maxRetries) { if (restartCount < Number(maxRetries)) {
restartCount++; restartCount++;
console.log(`Retrying to start bot... Attempt ${restartCount}`); console.log(`Retrying to start bot... Attempt ${restartCount}`);
setTimeout(startBot, 5000); setTimeout(startBot, 5000);

View File

@ -1,66 +1,76 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const axios = require("axios"); import axios from 'axios';
import { Context, Telegraf } from 'telegraf';
module.exports = (bot) => { const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
bot.command("duck", spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); export default (bot: Telegraf<Context>) => {
bot.command("duck", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from?.language_code);
try { try {
const response = await axios(Resources.duckApi); const response = await axios(Resources.duckApi);
ctx.replyWithPhoto(response.data.url, { ctx.replyWithPhoto(response.data.url, {
caption: "🦆", caption: "🦆",
// reply_to_message_id works fine, using this for now to avoid errors
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
const message = Strings.duckApiErr.replace('{error}', error.message); const message = Strings.duckApiErr.replace('{error}', error.message);
ctx.reply(message, { ctx.reply(message, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
return; return;
} }
}); });
bot.command("fox", spamwatchMiddleware, async (ctx) => { bot.command("fox", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
try { try {
const response = await axios(Resources.foxApi); const response = await axios(Resources.foxApi);
ctx.replyWithPhoto(response.data.image, { ctx.replyWithPhoto(response.data.image, {
caption: "🦊", caption: "🦊",
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
const message = Strings.foxApiErr.replace('{error}', error.message); const message = Strings.foxApiErr.replace('{error}', error.message);
ctx.reply(message, { ctx.reply(message, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
return; return;
} }
}); });
bot.command("dog", spamwatchMiddleware, async (ctx) => { bot.command("dog", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
try { try {
const response = await axios(Resources.dogApi); const response = await axios(Resources.dogApi);
ctx.replyWithPhoto(response.data.message, { ctx.replyWithPhoto(response.data.message, {
caption: "🐶", caption: "🐶",
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
const message = Strings.foxApiErr.replace('{error}', error.message); const message = Strings.foxApiErr.replace('{error}', error.message);
ctx.reply(message, { ctx.reply(message, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
return; return;
} }
}); });
bot.command("cat", spamwatchMiddleware, async (ctx) => { bot.command("cat", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
const apiUrl = `${Resources.catApi}?json=true`; const apiUrl = `${Resources.catApi}?json=true`;
const response = await axios.get(apiUrl); const response = await axios.get(apiUrl);
const data = response.data; const data = response.data;
@ -70,17 +80,19 @@ module.exports = (bot) => {
await ctx.replyWithPhoto(imageUrl, { await ctx.replyWithPhoto(imageUrl, {
caption: `🐱`, caption: `🐱`,
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
ctx.reply(Strings.catImgErr, { ctx.reply(Strings.catImgErr, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id 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]; const userInput = ctx.message.text.split(' ')[1];
switch (true) { switch (true) {
@ -89,6 +101,7 @@ module.exports = (bot) => {
Resources.soggyCat2, { Resources.soggyCat2, {
caption: Resources.soggyCat2, caption: Resources.soggyCat2,
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
break; break;
@ -96,6 +109,7 @@ module.exports = (bot) => {
case (userInput === "3" || userInput === "sticker"): case (userInput === "3" || userInput === "sticker"):
ctx.replyWithSticker( ctx.replyWithSticker(
Resources.soggyCatSticker, { Resources.soggyCatSticker, {
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
break; break;
@ -105,6 +119,7 @@ module.exports = (bot) => {
Resources.soggyCatAlt, { Resources.soggyCatAlt, {
caption: Resources.soggyCatAlt, caption: Resources.soggyCatAlt,
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
break; break;
@ -114,6 +129,7 @@ module.exports = (bot) => {
Resources.soggyCat, { Resources.soggyCat, {
caption: Resources.soggyCat, caption: Resources.soggyCat,
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
break; break;

View File

@ -1,11 +1,14 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const axios = require('axios'); import axios from 'axios';
const { verifyInput } = require('../plugins/verifyInput.js'); 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 { try {
const response = await axios.get(Resources.codenameApi); const response = await axios.get(Resources.codenameApi);
return response.data return response.data
@ -15,27 +18,29 @@ async function getDeviceList() {
return ctx.reply(message, { return ctx.reply(message, {
parse_mode: "Markdown", parse_mode: "Markdown",
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
} }
module.exports = (bot) => { export default (bot: Telegraf<Context>) => {
bot.command(['codename', 'whatis'], spamwatchMiddleware, async (ctx) => { bot.command(['codename', 'whatis'], spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const userInput = ctx.message.text.split(" ").slice(1).join(" "); 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 const { noCodename } = Strings.codenameCheck
if(verifyInput(ctx, userInput, noCodename)){ if(verifyInput(ctx, userInput, noCodename)){
return; return;
} }
const jsonRes = await getDeviceList() const jsonRes = await getDeviceList({ Strings, ctx })
const phoneSearch = Object.keys(jsonRes).find((codename) => codename === userInput); const phoneSearch = Object.keys(jsonRes).find((codename) => codename === userInput);
if (!phoneSearch) { if (!phoneSearch) {
return ctx.reply(Strings.codenameCheck.notFound, { return ctx.reply(Strings.codenameCheck.notFound, {
parse_mode: "Markdown", parse_mode: "Markdown",
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
@ -50,6 +55,7 @@ module.exports = (bot) => {
return ctx.reply(message, { return ctx.reply(message, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
}) })

View File

@ -1,9 +1,12 @@
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const os = require('os'); import os from 'os';
const { exec } = require('child_process'); import { exec } from 'child_process';
const { error } = require('console'); import { error } from 'console';
import { Context, Telegraf } from 'telegraf';
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
function getGitCommitHash() { function getGitCommitHash() {
return new Promise((resolve, reject) => { 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 hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60); const minutes = Math.floor((uptime % 3600) / 60);
const seconds = Math.floor(uptime % 60); const seconds = Math.floor(uptime % 60);
@ -50,152 +53,190 @@ function getSystemInfo() {
`*Uptime:* \`${formatUptime(uptime())}\`\n\n`; `*Uptime:* \`${formatUptime(uptime())}\`\n\n`;
} }
async function handleAdminCommand(ctx, action, successMessage, errorMessage) { async function handleAdminCommand(ctx: Context & { message: { text: string } }, action: () => Promise<void>, successMessage: string, errorMessage: string) {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
const userId = ctx.from.id; const userId = ctx.from?.id;
const adminArray = JSON.parse("[" + process.env.botAdmins + "]"); const adminArray = process.env.botAdmins ? process.env.botAdmins.split(',').map(id => parseInt(id.trim())) : [];
if (adminArray.includes(userId)) { if (userId && adminArray.includes(userId)) {
try { try {
await action(); await action();
ctx.reply(successMessage, { if (successMessage) {
parse_mode: 'Markdown', ctx.reply(successMessage, {
reply_to_message_id: ctx.message.message_id parse_mode: 'Markdown',
}); // @ts-ignore
reply_to_message_id: ctx.message.message_id
});
}
} catch (error) { } catch (error) {
ctx.reply(errorMessage.replace(/{error}/g, error.message), { ctx.reply(errorMessage.replace(/{error}/g, error.message), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
} else { } else {
ctx.reply(Strings.noPermission, { ctx.reply(Strings.noPermission, {
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
} }
module.exports = (bot) => { export default (bot: Telegraf<Context>) => {
bot.command('getbotstats', spamwatchMiddleware, async (ctx) => { bot.command('getbotstats', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
const stats = getSystemInfo(); const stats = getSystemInfo();
await ctx.reply(stats, { await ctx.reply(stats, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
}, '', Strings.errorRetrievingStats); }, '', Strings.errorRetrievingStats);
}); });
bot.command('getbotcommit', spamwatchMiddleware, async (ctx) => { bot.command('getbotcommit', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
try { try {
const commitHash = await getGitCommitHash(); const commitHash = await getGitCommitHash();
await ctx.reply(Strings.gitCurrentCommit.replace(/{commitHash}/g, commitHash), { await ctx.reply(Strings.gitCurrentCommit.replace(/{commitHash}/g, commitHash), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
ctx.reply(Strings.gitErrRetrievingCommit.replace(/{error}/g, error), { ctx.reply(Strings.gitErrRetrievingCommit.replace(/{error}/g, error), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
}, '', Strings.gitErrRetrievingCommit); }, '', Strings.gitErrRetrievingCommit);
}); });
bot.command('updatebot', spamwatchMiddleware, async (ctx) => { bot.command('updatebot', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
try { try {
const result = await updateBot(); const result = await updateBot();
await ctx.reply(Strings.botUpdated.replace(/{result}/g, result), { await ctx.reply(Strings.botUpdated.replace(/{result}/g, result), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
ctx.reply(Strings.errorUpdatingBot.replace(/{error}/g, error), { ctx.reply(Strings.errorUpdatingBot.replace(/{error}/g, error), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
}, '', Strings.errorUpdatingBot); }, '', Strings.errorUpdatingBot);
}); });
bot.command('setbotname', spamwatchMiddleware, async (ctx) => { bot.command('setbotname', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
const botName = ctx.message.text.split(' ').slice(1).join(' '); const botName = ctx.message.text.split(' ').slice(1).join(' ');
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
await ctx.telegram.setMyName(botName); await ctx.telegram.setMyName(botName);
}, Strings.botNameChanged.replace(/{botName}/g, botName), Strings.botNameErr.replace(/{error}/g, error)); }, Strings.botNameChanged.replace(/{botName}/g, botName), Strings.botNameErr.replace(/{error}/g, error));
}); });
bot.command('setbotdesc', spamwatchMiddleware, async (ctx) => { bot.command('setbotdesc', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
const botDesc = ctx.message.text.split(' ').slice(1).join(' '); const botDesc = ctx.message.text.split(' ').slice(1).join(' ');
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
await ctx.telegram.setMyDescription(botDesc); await ctx.telegram.setMyDescription(botDesc);
}, Strings.botDescChanged.replace(/{botDesc}/g, botDesc), Strings.botDescErr.replace(/{error}/g, error)); }, Strings.botDescChanged.replace(/{botDesc}/g, botDesc), Strings.botDescErr.replace(/{error}/g, error));
}); });
bot.command('botkickme', spamwatchMiddleware, async (ctx) => { bot.command('botkickme', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
handleAdminCommand(ctx, async () => { 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, { ctx.reply(Strings.kickingMyself, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
await ctx.telegram.leaveChat(ctx.chat.id); await ctx.telegram.leaveChat(ctx.chat.id);
}, '', Strings.kickingMyselfErr); }, '', Strings.kickingMyselfErr);
}); });
bot.command('getfile', spamwatchMiddleware, async (ctx) => { bot.command('getfile', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from?.language_code);
const botFile = ctx.message.text.split(' ').slice(1).join(' '); 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 () => { handleAdminCommand(ctx, async () => {
try { try {
await ctx.replyWithDocument({ await ctx.replyWithDocument({
// @ts-ignore
source: botFile, source: botFile,
caption: botFile caption: botFile
}, {
// @ts-ignore
reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
ctx.reply(Strings.unexpectedErr.replace(/{error}/g, error.message), { ctx.reply(Strings.unexpectedErr.replace(/{error}/g, error.message), {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
}, '', Strings.unexpectedErr); }, '', 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(' '); const command = ctx.message.text.split(' ').slice(1).join(' ');
handleAdminCommand(ctx, async () => { handleAdminCommand(ctx, async () => {
if (!command) { 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) => { exec(command, (error, stdout, stderr) => {
if (error) { if (error) {
return ctx.reply(`\`${error.message}\``, { return ctx.reply(`\`${error.message}\``, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
if (stderr) { if (stderr) {
return ctx.reply(`\`${stderr}\``, { return ctx.reply(`\`${stderr}\``, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} }
ctx.reply(`\`${stdout}\``, { ctx.reply(`\`${stdout}\``, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
}); });
}, '', "Nope!"); }, '', "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(' '); const code = ctx.message.text.split(' ').slice(1).join(' ');
if (!code) { if (!code) {
return ctx.reply('Por favor, forneça um código para avaliar.'); return ctx.reply('Por favor, forneça um código para avaliar.');
@ -205,19 +246,21 @@ module.exports = (bot) => {
const result = eval(code); const result = eval(code);
ctx.reply(`Result: ${result}`, { ctx.reply(`Result: ${result}`, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id
}); });
} catch (error) { } catch (error) {
ctx.reply(`Error: ${error.message}`, { ctx.reply(`Error: ${error.message}`, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
// @ts-ignore
reply_to_message_id: ctx.message.message_id 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 () => { handleAdminCommand(ctx, async () => {
ctx.reply(null); ctx.reply('Crashed!');
}, '', "Nope!"); }, '', "Nope!");
}); });
}; };

View File

@ -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');
});
};

112
src/commands/fun.ts Normal file
View File

@ -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<Context>) => {
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');
});
};

View File

@ -4,18 +4,23 @@
// With some help from GPT (I don't really like AI but whatever) // 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! // If this were a kang, I would not be giving credits to him!
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
import axios from 'axios';
import { parse } from 'node-html-parser';
const axios = require('axios'); const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
const { parse } = require('node-html-parser');
class PhoneSearchResult { interface PhoneSearchResult {
constructor(name, url) { name: string;
this.name = name; url: string;
this.url = url; }
Object.freeze(this);
} interface PhoneDetails {
specs: Record<string, Record<string, string>>;
name?: string;
url?: string;
picture?: string;
} }
const HEADERS = { const HEADERS = {
@ -32,7 +37,7 @@ function getDataFromSpecs(specsData, category, attributes) {
.join("\n"); .join("\n");
} }
function parseSpecs(specsData) { function parseSpecs(specsData: PhoneDetails): PhoneDetails {
const categories = { const categories = {
"status": ["Launch", ["Status"]], "status": ["Launch", ["Status"]],
"network": ["Network", ["Technology"]], "network": ["Network", ["Technology"]],
@ -69,7 +74,7 @@ function parseSpecs(specsData) {
const [cat, attrs] = categories[key]; const [cat, attrs] = categories[key];
acc[key] = getDataFromSpecs(specsData, cat, attrs) || ""; acc[key] = getDataFromSpecs(specsData, cat, attrs) || "";
return acc; return acc;
}, {}); }, { specs: {} } as PhoneDetails);
parsedData["name"] = specsData.name || ""; parsedData["name"] = specsData.name || "";
parsedData["url"] = specsData.url || ""; parsedData["url"] = specsData.url || "";
@ -77,7 +82,7 @@ function parseSpecs(specsData) {
return parsedData; return parsedData;
} }
function formatPhone(phone) { function formatPhone(phone: PhoneDetails) {
const formattedPhone = parseSpecs(phone); const formattedPhone = parseSpecs(phone);
const attributesDict = { const attributesDict = {
"Status": "status", "Status": "status",
@ -132,7 +137,7 @@ async function fetchHtml(url) {
} }
} }
async function searchPhone(phone) { async function searchPhone(phone: string): Promise<PhoneSearchResult[]> {
try { try {
const searchUrl = `https://m.gsmarena.com/results.php3?sQuickSearch=yes&sName=${encodeURIComponent(phone)}`; const searchUrl = `https://m.gsmarena.com/results.php3?sQuickSearch=yes&sName=${encodeURIComponent(phone)}`;
const htmlContent = await fetchHtml(searchUrl); const htmlContent = await fetchHtml(searchUrl);
@ -142,7 +147,7 @@ async function searchPhone(phone) {
return foundPhones.map((phoneTag) => { return foundPhones.map((phoneTag) => {
const name = phoneTag.querySelector('img')?.getAttribute('title') || ""; const name = phoneTag.querySelector('img')?.getAttribute('title') || "";
const url = phoneTag.querySelector('a')?.getAttribute('href') || ""; const url = phoneTag.querySelector('a')?.getAttribute('href') || "";
return new PhoneSearchResult(name, url); return { name, url };
}); });
} catch (error) { } catch (error) {
console.error("Error searching for phone:", 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}` }; return { ...specsData, name, picture, url: `https://www.gsmarena.com/${url}` };
} catch (error) { } catch (error) {
console.error("Error fetching phone details:", error); console.error("Error fetching phone details:", error);
return {}; return { specs: {}, name: "", url: "", picture: "" };
} }
} }
@ -201,7 +206,7 @@ function getUsername(ctx){
return userName; return userName;
} }
module.exports = (bot) => { export default (bot) => {
bot.command(['d', 'device'], spamwatchMiddleware, async (ctx) => { bot.command(['d', 'device'], spamwatchMiddleware, async (ctx) => {
const userId = ctx.from.id; const userId = ctx.from.id;
const userName = getUsername(ctx); const userName = getUsername(ctx);

View File

@ -1,6 +1,17 @@
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); 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) { async function sendHelpMessage(ctx, isEditing) {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
@ -11,8 +22,8 @@ async function sendHelpMessage(ctx, isEditing) {
function getMessageId(ctx) { function getMessageId(ctx) {
return ctx.message?.message_id || ctx.callbackQuery?.message?.message_id; return ctx.message?.message_id || ctx.callbackQuery?.message?.message_id;
}; };
const createOptions = (ctx, includeReplyTo = false) => { const createOptions = (ctx, includeReplyTo = false): MessageOptions => {
const options = { const options: MessageOptions = {
parse_mode: 'Markdown', parse_mode: 'Markdown',
disable_web_page_preview: true, disable_web_page_preview: true,
reply_markup: { reply_markup: {
@ -39,9 +50,9 @@ async function sendHelpMessage(ctx, isEditing) {
}; };
} }
module.exports = (bot) => { export default (bot) => {
bot.help(spamwatchMiddleware, async (ctx) => { bot.help(spamwatchMiddleware, async (ctx) => {
await sendHelpMessage(ctx); await sendHelpMessage(ctx, false);
}); });
bot.command("about", spamwatchMiddleware, async (ctx) => { bot.command("about", spamwatchMiddleware, async (ctx) => {

View File

@ -1,11 +1,13 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const axios = require('axios'); import axios from 'axios';
const { verifyInput } = require('../plugins/verifyInput.js'); import verifyInput from '../plugins/verifyInput';
module.exports = (bot) => { const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
export default (bot) => {
bot.command("http", spamwatchMiddleware, async (ctx) => { bot.command("http", spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
const userInput = ctx.message.text.split(' ')[1]; const userInput = ctx.message.text.split(' ')[1];

View File

@ -1,6 +1,8 @@
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
async function getUserInfo(ctx) { async function getUserInfo(ctx) {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
@ -9,7 +11,7 @@ async function getUserInfo(ctx) {
lastName = " "; lastName = " ";
} }
userInfo = Strings.userInfo const userInfo = Strings.userInfo
.replace('{userName}', `${ctx.from.first_name} ${lastName}` || Strings.varStrings.varUnknown) .replace('{userName}', `${ctx.from.first_name} ${lastName}` || Strings.varStrings.varUnknown)
.replace('{userId}', ctx.from.id || Strings.varStrings.varUnknown) .replace('{userId}', ctx.from.id || Strings.varStrings.varUnknown)
.replace('{userHandle}', ctx.from.username ? `@${ctx.from.username}` : Strings.varStrings.varNone) .replace('{userHandle}', ctx.from.username ? `@${ctx.from.username}` : Strings.varStrings.varNone)
@ -22,7 +24,7 @@ async function getUserInfo(ctx) {
async function getChatInfo(ctx) { async function getChatInfo(ctx) {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
if (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup') { 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('{chatId}', ctx.chat.id || Strings.varStrings.varUnknown)
.replace('{chatName}', ctx.chat.title || Strings.varStrings.varUnknown) .replace('{chatName}', ctx.chat.title || Strings.varStrings.varUnknown)
.replace('{chatHandle}', ctx.chat.username ? `@${ctx.chat.username}` : Strings.varStrings.varNone) .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) => { bot.command('chatinfo', spamwatchMiddleware, async (ctx) => {
const chatInfo = await getChatInfo(ctx); const chatInfo = await getChatInfo(ctx);
ctx.reply( ctx.reply(

View File

@ -1,9 +1,11 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const fs = require('fs'); import fs from 'fs';
const axios = require('axios'); import axios from 'axios';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
const scrobbler_url = Resources.lastFmApi; const scrobbler_url = Resources.lastFmApi;
const api_key = process.env.lastKey; const api_key = process.env.lastKey;
@ -35,7 +37,7 @@ function saveUsers() {
} }
} }
async function getFromMusicBrainz(mbid) { async function getFromMusicBrainz(mbid: string) {
try { try {
const response = await axios.get(`${Resources.musicBrainzApi}${mbid}`); const response = await axios.get(`${Resources.musicBrainzApi}${mbid}`);
const imgObjLarge = response.data.images[0]?.thumbnails?.['1200']; const imgObjLarge = response.data.images[0]?.thumbnails?.['1200'];
@ -58,7 +60,7 @@ function getFromLast(track) {
return imageUrl; return imageUrl;
} }
module.exports = (bot) => { export default (bot) => {
loadUsers(); loadUsers();
bot.command('setuser', (ctx) => { bot.command('setuser', (ctx) => {
@ -149,7 +151,7 @@ module.exports = (bot) => {
const artistUrl = `https://www.last.fm/music/${encodeURIComponent(artistName)}`; const artistUrl = `https://www.last.fm/music/${encodeURIComponent(artistName)}`;
const userUrl = `https://www.last.fm/user/${encodeURIComponent(lastfmUser)}`; const userUrl = `https://www.last.fm/user/${encodeURIComponent(lastfmUser)}`;
let num_plays = ''; let num_plays = 0;
try { try {
const response_plays = await axios.get(scrobbler_url, { const response_plays = await axios.get(scrobbler_url, {
params: { params: {
@ -164,11 +166,8 @@ module.exports = (bot) => {
'User-Agent': `@${botInfo.username}-node-telegram-bot` 'User-Agent': `@${botInfo.username}-node-telegram-bot`
} }
}); });
num_plays = response_plays.data.track.userplaycount;
if (!num_plays || num_plays === undefined) { num_plays = response_plays.data.track.userplaycount;
num_plays = 0;
};
} catch (err) { } catch (err) {
console.log(err) console.log(err)
const message = Strings.lastFm.apiErr const message = Strings.lastFm.apiErr

View File

@ -1,9 +1,11 @@
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
module.exports = (bot) => { const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
bot.start(spamwatchMiddleware, async (ctx) => {
export default (bot: any) => {
bot.start(spamwatchMiddleware, async (ctx: any) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
const botInfo = await ctx.telegram.getMe(); const botInfo = await ctx.telegram.getMe();
const startMsg = Strings.botWelcome.replace(/{botName}/g, botInfo.first_name); 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 Strings = getStrings(ctx.from.language_code);
const message = Strings.botPrivacy.replace("{botPrivacy}", process.env.botPrivacy); const message = Strings.botPrivacy.replace("{botPrivacy}", process.env.botPrivacy);

View File

@ -1,12 +1,19 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const axios = require('axios'); import axios from 'axios';
const fs = require('fs'); import fs from 'fs';
const path = require('path'); import path from 'path';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); 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<ModuleResult | null> {
try { try {
const downloadUrl = `${Resources.modArchiveApi}${moduleId}`; const downloadUrl = `${Resources.modArchiveApi}${moduleId}`;
const response = await axios({ 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) => { bot.command(['modarchive', 'tma'], spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
const moduleId = ctx.message.text.split(' ')[1]; const moduleId = ctx.message.text.split(' ')[1];
if (moduleId == NaN || null) { if (Number.isNaN(moduleId) || null) {
return ctx.reply(Strings.maInvalidModule, { return ctx.reply(Strings.maInvalidModule, {
parse_mode: "Markdown", parse_mode: "Markdown",
reply_to_message_id: ctx.message.message_id reply_to_message_id: ctx.message.message_id

View File

@ -1,15 +1,56 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const axios = require("axios"); import axios from 'axios';
const { verifyInput } = require('../plugins/verifyInput.js'); 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) { function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1); return string.charAt(0).toUpperCase() + string.slice(1);
} }
module.exports = (bot) => { export default (bot) => {
bot.command("mlp", spamwatchMiddleware, async (ctx) => { bot.command("mlp", spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
@ -34,11 +75,11 @@ module.exports = (bot) => {
try { try {
const response = await axios(apiUrl); const response = await axios(apiUrl);
const charactersArray = []; const charactersArray: Character[] = [];
if (Array.isArray(response.data.data)) { if (Array.isArray(response.data.data)) {
response.data.data.forEach(character => { response.data.data.forEach(character => {
let aliases = []; let aliases: string[] = [];
if (character.alias) { if (character.alias) {
if (typeof character.alias === 'string') { if (typeof character.alias === 'string') {
aliases.push(character.alias); aliases.push(character.alias);
@ -107,7 +148,7 @@ module.exports = (bot) => {
try { try {
const response = await axios(apiUrl); const response = await axios(apiUrl);
const episodeArray = []; const episodeArray: Episode[] = [];
if (Array.isArray(response.data.data)) { if (Array.isArray(response.data.data)) {
response.data.data.forEach(episode => { response.data.data.forEach(episode => {
@ -175,15 +216,15 @@ module.exports = (bot) => {
try { try {
const response = await axios(apiUrl); const response = await axios(apiUrl);
const comicArray = []; const comicArray: Comic[] = [];
if (Array.isArray(response.data.data)) { if (Array.isArray(response.data.data)) {
response.data.data.forEach(comic => { response.data.data.forEach(comic => {
let letterers = []; let letterers: string[] = [];
if (comic.letterer) { if (comic.letterer) {
if (typeof comic.letterer === 'string') { if (typeof comic.letterer === 'string') {
letterers.push(comic.letterer); letterers.push(comic.letterer);
} else if (Array.isArray(comic.letterer)) { } else if (Array.isArray(comic.letterer)) {
letterers = aliases.concat(comic.letterer); letterers = letterers.concat(comic.letterer);
} }
} }
comicArray.push({ comicArray.push({

32
src/commands/quotes.ts Normal file
View File

@ -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'
});
};
});
};
*/

View File

@ -1,15 +1,17 @@
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const axios = require("axios"); import axios from 'axios';
module.exports = (bot) => { const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
export default (bot) => {
bot.command(["rpony", "randompony", "mlpart"], spamwatchMiddleware, async (ctx) => { bot.command(["rpony", "randompony", "mlpart"], spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
try { try {
const response = await axios(Resources.randomPonyApi); const response = await axios(Resources.randomPonyApi);
let tags = []; let tags: string[] = [];
if (response.data.pony.tags) { if (response.data.pony.tags) {
if (typeof response.data.pony.tags === 'string') { if (typeof response.data.pony.tags === 'string') {

View File

@ -2,12 +2,14 @@
// Copyright (c) 2024 BubbalooTeam. (https://github.com/BubbalooTeam) // Copyright (c) 2024 BubbalooTeam. (https://github.com/BubbalooTeam)
// Minor code changes by lucmsilva (https://github.com/lucmsilva651) // Minor code changes by lucmsilva (https://github.com/lucmsilva651)
const Resources = require('../props/resources.json'); import Resources from '../props/resources.json';
const axios = require('axios'); import axios from 'axios';
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const { verifyInput } = require('../plugins/verifyInput.js'); import verifyInput from '../plugins/verifyInput';
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
const statusEmojis = { const statusEmojis = {
0: '⛈', 1: '⛈', 2: '⛈', 3: '⛈', 4: '⛈', 5: '🌨', 6: '🌨', 7: '🌨', 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) => { bot.command(['clima', 'weather'], spamwatchMiddleware, async (ctx) => {
const userLang = ctx.from.language_code || "en-US"; const userLang = ctx.from.language_code || "en-US";
const Strings = getStrings(userLang); const Strings = getStrings(userLang);

38
src/commands/wiki.ts Normal file
View File

@ -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 });
});
};
*/

View File

@ -1,10 +1,12 @@
const { getStrings } = require('../plugins/checkLang.js'); import { getStrings } from '../plugins/checklang';
const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); import { isOnSpamWatch } from '../spamwatch/spamwatch';
const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); import spamwatchMiddlewareModule from '../spamwatch/Middleware';
const { execFile } = require('child_process'); import { execFile } from 'child_process';
const os = require('os'); import os from 'os';
const fs = require('fs'); import fs from 'fs';
const path = require('path'); import path from 'path';
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
const ytDlpPaths = { const ytDlpPaths = {
linux: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp'), linux: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp'),
@ -28,7 +30,7 @@ const getFfmpegPath = () => {
return ffmpegPaths[platform] || ffmpegPaths.linux; 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) => { return new Promise((resolve, reject) => {
execFile(command, args, (error, stdout, stderr) => { execFile(command, args, (error, stdout, stderr) => {
if (error) { if (error) {
@ -40,8 +42,8 @@ const downloadFromYoutube = async (command, args) => {
}); });
}; };
const getApproxSize = async (command, videoUrl) => { const getApproxSize = async (command: string, videoUrl: string): Promise<number> => {
let args = []; let args: string[] = [];
if (fs.existsSync(path.resolve(__dirname, "../props/cookies.txt"))) { 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")]; args = [videoUrl, '--compat-opt', 'manifest-filesize-approx', '-O', 'filesize_approx', '--cookies', path.resolve(__dirname, "../props/cookies.txt")];
} else { } 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) => { bot.command(['yt', 'ytdl', 'sdl', 'video', 'dl'], spamwatchMiddleware, async (ctx) => {
const Strings = getStrings(ctx.from.language_code); const Strings = getStrings(ctx.from.language_code);
const ytDlpPath = getYtDlpPath(); const ytDlpPath = getYtDlpPath();
@ -198,11 +200,8 @@ module.exports = (bot) => {
} }
} catch (error) { } catch (error) {
const errMsg = Strings.ytDownload.uploadErr.replace("{error}", error) const errMsg = Strings.ytDownload.uploadErr.replace("{error}", error)
await ctx.telegram.editMessageText( // will no longer edit the message as the message context is not outside the try block
ctx.chat.id, await ctx.reply(errMsg, {
downloadingMessage.message_id,
null,
errMsg, {
parse_mode: 'Markdown', parse_mode: 'Markdown',
reply_to_message_id: ctx.message.message_id, reply_to_message_id: ctx.message.message_id,
}, },

View File

@ -110,5 +110,7 @@
"notFound": "Phone not found.", "notFound": "Phone not found.",
"resultMsg": "*Name:* `{name}`\n*Brand:* `{brand}`\n*Model:* `{model}`\n*Codename:* `{codename}`", "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}`" "apiErr": "An error occurred while fetching data from the API.\n\n`{err}`"
} },
"chatNotFound": "Chat not found.",
"noFileProvided": "Please provide a file to send."
} }

View File

@ -17,6 +17,4 @@ function getStrings(languageCode) {
} }
} }
module.exports = { export { getStrings };
getStrings
};

View File

@ -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
};

View File

@ -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;
}

View File

@ -1,7 +1,7 @@
const axios = require('axios'); import axios from 'axios';
const fs = require('fs'); import fs from 'fs';
const path = require('path'); import path from 'path';
const os = require('os'); import os from 'os';
const downloadDir = path.resolve(__dirname, 'yt-dlp'); const downloadDir = path.resolve(__dirname, 'yt-dlp');