Migrate to TypeScript, minor changes and fixes #46

Merged
ihatenodejs merged 10 commits from typescript into main 2025-04-29 19:39:11 +00:00
32 changed files with 550 additions and 325 deletions

View file

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

7
.gitignore vendored
View file

@ -136,11 +136,12 @@ dist
lastfm.json
sw-blocklist.txt
package-lock.json
bun.lock
bun.lockb
tmp/
# Executables
*.exe
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
VOLUME /usr/src/app/config.env
VOLUME /usr/src/app/.env
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)
[![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
<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>
Made with [contrib.rocks](https://contrib.rocks).

View file

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

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": {
"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"
}

View file

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

View file

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

View file

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

View file

@ -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<void>, 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<Context>) => {
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!");
});
};

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)
// 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<string, Record<string, string>>;
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<PhoneSearchResult[]> {
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);

View file

@ -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) => {

View file

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

View file

@ -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(

View file

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

View file

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

View file

@ -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<ModuleResult | null> {
try {
const downloadUrl = `${Resources.modArchiveApi}${moduleId}`;
const response = await axios({
GiovaniFZ commented 2025-04-24 23:09:54 +00:00 (Migrated from github.com)

instead of using an interface like this, my recommendation is to try to use Zod to make validations better

instead of using an interface like this, my recommendation is to try to use Zod to make validations better
ihatenodejs commented 2025-04-24 23:12:39 +00:00 (Migrated from github.com)

@lucmsilva651 What do you think about this? Using Zod might make things more complex for you to maintain in terms of modifying types.

@lucmsilva651 What do you think about this? Using [Zod](https://zod.dev) might make things more complex for you to maintain in terms of modifying types.
GiovaniFZ commented 2025-04-24 23:17:47 +00:00 (Migrated from github.com)

@lucmsilva651 we discussed that if you know what is zod and how to use it, @ihatenodejs will do, otherwise not

@lucmsilva651 we discussed that if you know what is zod and how to use it, @ihatenodejs will do, otherwise not
@ -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

View file

@ -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({

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');
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') {

View file

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

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');
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<number> => {
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,
},

View file

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

View file

@ -17,6 +17,4 @@ function getStrings(languageCode) {
}
}
module.exports = {
getStrings
};
export { 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');
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');