diff --git a/.gitmodules b/.gitmodules index 1eb51e4..cf3ce05 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "src/plugins/lib-spamwatch"] - path = src/plugins/lib-spamwatch +[submodule "src/spamwatch"] + path = src/spamwatch url = https://github.com/ABOCN/TelegramBot-SpamWatch diff --git a/Dockerfile b/Dockerfile index 56b5b32..473f9b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM node:20-slim # Install ffmpeg and other deps -RUN apt-get update && apt-get install -y ffmpeg && apt-get clean && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y ffmpeg git && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR /usr/src/app diff --git a/LICENSE b/LICENSE index ae3e4ae..2562540 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ BSD 3-Clause License -Copyright (c) 2024, Lucas Gabriel +Copyright (c) 2024-2025, Lucas Gabriel , +ABOCN and all contributors Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index a154166..44c2b74 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Kowalski (Node.js Telegram Bot) [![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) +[![GitHub License](https://img.shields.io/github/license/ABOCN/TelegramBot)](https://github.com/abocn/TelegramBot/blob/main/LICENSE) +[![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) Kowalski is a a simple Telegram bot made in Node.js. @@ -9,24 +11,20 @@ Kowalski is a a simple Telegram bot made in Node.js. ## Self-host requirements -- Node.js 20 or newer (you can also use [Bun](https://bun.sh)) +> [!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)) - 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) -## Run it yourself, develop or contribute with Kowalski +## Running locally (non-Docker setup) First, clone the repo with Git: ```bash -git clone https://github.com/ABOCN/TelegramBot -``` - -And now, init the submodules with these commands (this is very important): - -```bash -cd TelegramBot -git submodule update --init --recursive +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). @@ -41,6 +39,9 @@ After editing the file, save all changes and run the bot with ``npm start``. > [!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. + You can also run Kowalski using Docker, which simplifies the setup process. Make sure you have Docker and Docker Compose installed. ### Using Docker Compose @@ -53,9 +54,6 @@ You can also run Kowalski using Docker, which simplifies the setup process. Make docker compose up -d ``` -> [!NOTE] -> The `-d` flag causes Kowalski to run in the background. If you're just playing around, you may not want to use this flag. - ### Using Docker Run If you prefer to use Docker directly, you can use these instructions instead. @@ -74,21 +72,18 @@ If you prefer to use Docker directly, you can use these instructions instead. docker run -d --name kowalski --restart unless-stopped -v $(pwd)/config.env:/usr/src/app/config.env:ro kowalski ``` -> [!NOTE] -> The `-d` flag causes Kowalski to run in the background. If you're just playing around, you may not want to use this flag. - ## config.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! - **botSource**: Put the link to your bot source code. +- **botPrivacy**: Put the link to your bot privacy policy. +- **maxRetries**: Maximum number of retries for a failing command on Kowalski. Default is 5. If the limit is hit, the bot will crash past this number. - **botToken**: Put your bot token that you created at [@BotFather](https://t.me/botfather). - **botAdmins**: Put the ID of the people responsible for managing the bot. They can use some administrative + exclusive commands on any group. - **lastKey**: Last.fm API key, for use on `lastfm.js` functions, like see who is listening to what song and etc. - **weatherKey**: Weather.com API key, used for the `/weather` command. -## Note - -- 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! - ## Troubleshooting ### YouTube Downloading diff --git a/config.env.example b/config.env.example index 9773d37..452211b 100644 --- a/config.env.example +++ b/config.env.example @@ -1,5 +1,12 @@ -botSource = "https://github.com/change-this/to-your-repo/" -botToken = "InsertYourBotTokenHere" -botAdmins = 0000000000, 00000000, 00000000 +# links for source and privacy +botPrivacy = "https://blog.lucmsilva.com/posts/lynx-privacy-policy" +botSource = "https://github.com/ABOCN/TelegramBot" + +# insert token here +botToken = "" + +# misc (botAdmins isnt a array here!) +maxRetries = 9999 +botAdmins = 00000000, 00000000, 00000000 lastKey = "InsertYourLastFmApiKeyHere" weatherKey = "InsertYourWeatherDotComApiKeyHere" \ No newline at end of file diff --git a/src/bot.js b/src/bot.js index 472bc10..d66b8e2 100644 --- a/src/bot.js +++ b/src/bot.js @@ -1,7 +1,7 @@ const { Telegraf } = require('telegraf'); const path = require('path'); const fs = require('fs'); -const { isOnSpamWatch } = require('./plugins/lib-spamwatch/spamwatch.js'); +const { isOnSpamWatch } = require('./spamwatch/spamwatch.js'); require('@dotenvx/dotenvx').config({ path: "config.env" }); require('./plugins/ytdlp-wrapper.js'); // require('./plugins/termlogger.js'); diff --git a/src/commands/animal.js b/src/commands/animal.js index 5355df1..204015d 100644 --- a/src/commands/animal.js +++ b/src/commands/animal.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require("axios"); module.exports = (bot) => { diff --git a/src/commands/codename.js b/src/commands/codename.js index 277523c..390760a 100644 --- a/src/commands/codename.js +++ b/src/commands/codename.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require('axios'); const { verifyInput } = require('../plugins/verifyInput.js'); diff --git a/src/commands/crew.js b/src/commands/crew.js index 52a3703..f754843 100644 --- a/src/commands/crew.js +++ b/src/commands/crew.js @@ -1,6 +1,6 @@ const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +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'); diff --git a/src/commands/fun.js b/src/commands/fun.js index 6fc0bba..b80696d 100644 --- a/src/commands/fun.js +++ b/src/commands/fun.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +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); diff --git a/src/commands/gsmarena.js b/src/commands/gsmarena.js index 6f5a01a..23478c3 100644 --- a/src/commands/gsmarena.js +++ b/src/commands/gsmarena.js @@ -4,8 +4,8 @@ // 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('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require('axios'); const { parse } = require('node-html-parser'); @@ -193,14 +193,18 @@ function extractMetaData(meta, key) { return line ? line.split('"')[1] : ""; } +function getUsername(ctx){ + let userName = String(ctx.from.first_name); + if(userName.includes("<") && userName.includes(">")) { + userName = userName.replaceAll("<", "").replaceAll(">", ""); + } + return userName; +} + module.exports = (bot) => { bot.command(['d', 'device'], spamwatchMiddleware, async (ctx) => { const userId = ctx.from.id; - let userName = String(ctx.from.first_name); - - if(userName.includes("<") && userName.includes(">")) { - userName = userName.replaceAll("<", "").replaceAll(">", ""); - } + const userName = getUsername(ctx); const phone = ctx.message.text.split(" ").slice(1).join(" "); if (!phone) { @@ -228,7 +232,7 @@ module.exports = (bot) => { bot.action(/details:(.+):(.+)/, async (ctx) => { const url = ctx.match[1]; const userId = parseInt(ctx.match[2]); - const userName = ctx.from.first_name; + const userName = getUsername(ctx); const callbackQueryUserId = ctx.update.callback_query.from.id; diff --git a/src/commands/help.js b/src/commands/help.js index ab9e601..163af25 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -1,6 +1,6 @@ const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); async function sendHelpMessage(ctx, isEditing) { const Strings = getStrings(ctx.from.language_code); diff --git a/src/commands/http.js b/src/commands/http.js index da4dd1f..1ee383c 100644 --- a/src/commands/http.js +++ b/src/commands/http.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require('axios'); const { verifyInput } = require('../plugins/verifyInput.js'); diff --git a/src/commands/info.js b/src/commands/info.js index b1c2c0b..f0c6354 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,6 +1,6 @@ const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); async function getUserInfo(ctx) { const Strings = getStrings(ctx.from.language_code); diff --git a/src/commands/lastfm.js b/src/commands/lastfm.js index 1f15f5a..ec6d126 100644 --- a/src/commands/lastfm.js +++ b/src/commands/lastfm.js @@ -2,8 +2,8 @@ const Resources = require('../props/resources.json'); const fs = require('fs'); const axios = require('axios'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const scrobbler_url = Resources.lastFmApi; const api_key = process.env.lastKey; diff --git a/src/commands/main.js b/src/commands/main.js index 6333296..6f36d64 100644 --- a/src/commands/main.js +++ b/src/commands/main.js @@ -1,6 +1,6 @@ const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); module.exports = (bot) => { bot.start(spamwatchMiddleware, async (ctx) => { @@ -16,8 +16,9 @@ module.exports = (bot) => { bot.command('privacy', spamwatchMiddleware, async (ctx) => { const Strings = getStrings(ctx.from.language_code); - ctx.reply( - Strings.botPrivacy, { + const message = Strings.botPrivacy.replace("{botPrivacy}", process.env.botPrivacy); + + ctx.reply(message, { parse_mode: 'Markdown', disable_web_page_preview: true, reply_to_message_id: ctx.message.message_id diff --git a/src/commands/modarchive.js b/src/commands/modarchive.js index 86d3c99..35f2d7d 100644 --- a/src/commands/modarchive.js +++ b/src/commands/modarchive.js @@ -3,8 +3,8 @@ const axios = require('axios'); const fs = require('fs'); const path = require('path'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); async function downloadModule(moduleId) { try { diff --git a/src/commands/ponyapi.js b/src/commands/ponyapi.js index 83897b7..345d864 100644 --- a/src/commands/ponyapi.js +++ b/src/commands/ponyapi.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require("axios"); const { verifyInput } = require('../plugins/verifyInput.js'); diff --git a/src/commands/quotes.js b/src/commands/quotes.js index e73e70e..859af57 100644 --- a/src/commands/quotes.js +++ b/src/commands/quotes.js @@ -1,7 +1,7 @@ // const Resources = require('../props/resources.json'); // const { getStrings } = require('../plugins/checklang.js'); -// const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -// const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +// const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +// const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); // const escape = require('markdown-escape'); // const axios = require('axios'); diff --git a/src/commands/randompony.js b/src/commands/randompony.js index 814e41a..b935504 100644 --- a/src/commands/randompony.js +++ b/src/commands/randompony.js @@ -1,7 +1,7 @@ const Resources = require('../props/resources.json'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const axios = require("axios"); module.exports = (bot) => { diff --git a/src/commands/weather.js b/src/commands/weather.js index 1c40919..caf420b 100644 --- a/src/commands/weather.js +++ b/src/commands/weather.js @@ -5,9 +5,9 @@ const Resources = require('../props/resources.json'); const axios = require('axios'); const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); +const { isOnSpamWatch } = require('../spamwatch/spamwatch.js'); +const spamwatchMiddleware = require('../spamwatch/Middleware.js')(isOnSpamWatch); const { verifyInput } = require('../plugins/verifyInput.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); const statusEmojis = { 0: '⛈', 1: '⛈', 2: '⛈', 3: '⛈', 4: '⛈', 5: '🌨', 6: '🌨', 7: '🌨', diff --git a/src/commands/youtube.js b/src/commands/youtube.js index 2442f95..35be412 100644 --- a/src/commands/youtube.js +++ b/src/commands/youtube.js @@ -1,6 +1,6 @@ const { getStrings } = require('../plugins/checklang.js'); -const { isOnSpamWatch } = require('../plugins/lib-spamwatch/spamwatch.js'); -const spamwatchMiddleware = require('../plugins/lib-spamwatch/Middleware.js')(isOnSpamWatch); +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'); diff --git a/src/locales/english.json b/src/locales/english.json index b2fb10a..2a143b9 100644 --- a/src/locales/english.json +++ b/src/locales/english.json @@ -1,7 +1,7 @@ { "botWelcome": "*Hello! I'm {botName}!*\nI was made with love by some nerds who really love programming!\n\n*By using {botName}, you affirm that you have read to and agree with the privacy policy (/privacy). This helps you understand where your data goes when using this bot.*\n\nAlso, you can use /help to see the bot commands!", "botHelp": "*Hey, I'm {botName}, a simple bot made entirely from scratch in Telegraf and Node.js by some nerds who really love programming.*\n\nClick on the buttons below to see which commands you can use!\n", - "botPrivacy": "Check out [this link](https://blog.lucmsilva.com/posts/lynx-privacy-policy) to read the bot's privacy policy.", + "botPrivacy": "Check out [this link]({botPrivacy}) to read the bot's privacy policy.", "botAbout": "*About the bot*\n\nThe bot base was originally created by [Lucas Gabriel (lucmsilva)](https://github.com/lucmsilva651), now maintained by several people.\n\nThe bot's purpose is to bring fun to your groups here on Telegram in a relaxed and simple way. The bot also features some very useful commands, which you can see using the help command (/help).\n\nSpecial thanks to @givfnz2 for his many contributions to the bot!\n\nSee the source code: [Click here to go to GitHub]({sourceLink})", "aboutBot": "About the bot", "varStrings": { diff --git a/src/locales/portuguese.json b/src/locales/portuguese.json index 6b492f2..07872cc 100644 --- a/src/locales/portuguese.json +++ b/src/locales/portuguese.json @@ -1,7 +1,7 @@ { "botWelcome": "*Olá! Eu sou o {botName}!*\n\n*Ao usar o {botName}, você afirma que leu e concorda com a política de privacidade (/privacy). Isso ajuda você a entender onde seus dados vão ao usar este bot.*\n\nAlém disso, você pode usar /help para ver os meus comandos!", "botHelp": "*Oi, eu sou o {botName}, um bot simples feito do zero em Telegraf e Node.js por uns nerds que gostam de programação.*\n\nVeja o código fonte: [Clique aqui para ir ao GitHub]({sourceLink})\n\nClique nos botões abaixo para ver quais comandos você pode usar!\n", - "botPrivacy": "Acesse [este link](https://blog.lucmsilva.com/posts/lynx-privacy-policy) para ler a política de privacidade do bot.", + "botPrivacy": "Acesse [este link]({botPrivacy}) para ler a política de privacidade do bot.", "botAbout": "*Sobre o bot*\n\nA base deste bot foi feita originalmente por [Lucas Gabriel (lucmsilva)](https://github.com/lucmsilva651), agora sendo mantido por várias pessoas.\n\nA intenção do bot é trazer diversão para os seus grupos aqui no Telegram de uma maneira bem descontraida e simples. O bot também conta com alguns comandos bem úteis, que você consegue ver com o comando de ajuda (/help).\n\nAgradecimento especial ao @givfnz2 pelas suas várias contribuições ao bot!\n\nVeja o código fonte: [Clique aqui para ir ao GitHub]({sourceLink})", "aboutBot": "Sobre o bot", "varStrings": { diff --git a/src/plugins/lib-spamwatch b/src/plugins/lib-spamwatch deleted file mode 160000 index 8d35b7e..0000000 --- a/src/plugins/lib-spamwatch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d35b7ec4cffb48df8d1f59485b32e2484ae64e7 diff --git a/src/spamwatch b/src/spamwatch new file mode 160000 index 0000000..b71b3b9 --- /dev/null +++ b/src/spamwatch @@ -0,0 +1 @@ +Subproject commit b71b3b9eab0f172c038674fc6739fce9199ad3e0