├── .env.sample
├── .eslintrc.js
├── .github
└── FUNDING.yml
├── .gitignore
├── .nvmrc
├── LICENSE
├── README.md
├── app.js
├── config.js
├── helpers
├── admins.js
├── bot.js
├── db.js
├── goldenBorodutchSubCount.js
├── help.js
├── isRuChat.js
├── language.js
├── limit.js
├── localizations.js
├── lock.js
├── requests.js
├── strings.js
├── time.js
└── votekickWord.js
├── localize.js
├── models
├── chat.js
├── index.js
├── request.js
└── user.js
├── nixpacks.toml
├── package.json
├── scripts
├── download.js
└── upload.js
└── yarn.lock
/.env.sample:
--------------------------------------------------------------------------------
1 | TELEGRAM_API_KEY=123456789
2 | MONGO_DB_URL=mongodb://localhost:27017/banofbot
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "airbnb",
3 | "plugins": [
4 | "react",
5 | "jsx-a11y",
6 | "import",
7 | "promise",
8 | ],
9 | "rules": {
10 | "require-jsdoc": 2,
11 | "promise/catch-or-return": 2,
12 | "no-underscore-dangle": [2, { "allow": ["_id"] }], /** Mongoose uses _id, we can't have control over it */
13 | "no-use-before-define": 0,
14 | "no-console": ["error", { allow: ["info"] }],
15 | "global-require": 0,
16 | "no-param-reassign": 0,
17 | "no-restricted-syntax": 0,
18 | }
19 | };
20 |
21 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: backmeupplz
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.17.0
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Nikita Kolmogorov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [@banofbot](https://telegram.me/banofbot) and [@silent_banofbot](https://telegram.me/banofbot) code
2 | This repository contains the code for the democracy bots I've built. Readme is still in work, any contributions are welcome.
3 |
4 | # Installation and local launch
5 | 1. Clone this repo: `git clone https://github.com/backmeupplz/banofbot`
6 | 2. Launch the [mongo database](https://www.mongodb.com/) locally
7 | 3. Create `.env` file with `TELEGRAM_API_KEY` and `MONGO_DB_URL`
8 | 4. Run `npm i` in the root folder
9 | 5. Run `npm run start`
10 |
11 | And you should be good to go! Feel free to fork and submit pull requests. Thanks!
12 |
13 | # Continuous integration
14 | Any commit pushed to master gets deployed to both @banofbot and @silent_banofbot via [CI Ninja](https://github.com/backmeupplz/ci-ninja).
15 |
16 | # License
17 | MIT — use for any purpose. Would be great if you could leave a note about the original developers. Thanks!
18 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main app logic
3 | *
4 | * @module app
5 | * @license MIT
6 | */
7 |
8 | /** Dependencies */
9 | const path = require('path')
10 | require('dotenv').config({
11 | path: path.join(__dirname, '/.env'),
12 | })
13 | const mongoose = require('mongoose')
14 | const bot = require('./helpers/bot')
15 | const config = require('./config')
16 | const db = require('./helpers/db')
17 | const language = require('./helpers/language')
18 | const help = require('./helpers/help')
19 | const lock = require('./helpers/lock')
20 | const requests = require('./helpers/requests')
21 | const admins = require('./helpers/admins')
22 | const limit = require('./helpers/limit')
23 | const time = require('./helpers/time')
24 | const votekickWord = require('./helpers/votekickWord')
25 |
26 | global.Promise = require('bluebird')
27 |
28 | global.Promise.config({ cancellation: true })
29 |
30 | /** Setup mongoose */
31 | mongoose.Promise = require('bluebird')
32 |
33 | mongoose.connect(config.database, {
34 | socketTimeoutMS: 0,
35 | connectTimeoutMS: 0,
36 | useUnifiedTopology: true,
37 | useNewUrlParser: true,
38 | })
39 | mongoose.connection.on('disconnected', () => {
40 | mongoose.connect(config.database, {
41 | socketTimeoutMS: 0,
42 | connectTimeoutMS: 0,
43 | useUnifiedTopology: true,
44 | useNewUrlParser: true,
45 | })
46 | })
47 | mongoose.set('useCreateIndex', true)
48 | mongoose.set('useFindAndModify', false)
49 |
50 | let timeoutOver = false
51 | setTimeout(() => {
52 | timeoutOver = true
53 | }, 5000)
54 |
55 | bot.on('message', (msg) => {
56 | if (!timeoutOver) {
57 | return
58 | }
59 | handle(msg)
60 | })
61 |
62 | /**
63 | * Used to handle incoming message
64 | * @param {Telegram:Message} msg Message received
65 | */
66 | function handle(msg) {
67 | if (!msg) {
68 | return
69 | }
70 | if (msg.text && msg.text.includes('@') && !msg.text.includes('banofbot')) {
71 | return
72 | }
73 | const isPrivateChat =
74 | msg.chat.type === 'private' || msg.chat.type === 'channel'
75 | const isCommand =
76 | msg.text &&
77 | msg.entities &&
78 | msg.entities[0] &&
79 | msg.entities[0].type === 'bot_command'
80 | const isEntry =
81 | (msg.new_chat_participant &&
82 | msg.new_chat_participant.username &&
83 | msg.new_chat_participant.username === 'banofbot') ||
84 | msg.group_chat_created
85 | db.findChat(msg.chat)
86 | .then((chat) => {
87 | let isReply =
88 | msg.reply_to_message &&
89 | msg.text &&
90 | (msg.text.includes('banofbot') ||
91 | msg.text.includes('@ban') ||
92 | msg.text.includes('voteban') ||
93 | msg.text.includes('Voteban') ||
94 | msg.text.includes('/spam') ||
95 | (chat.votekickWord &&
96 | chat.votekickWord.split(', ').reduce((p, c) => {
97 | return (
98 | p ||
99 | new RegExp(
100 | `(?<=[\\s,.:;"']|^)${c}(?=[\\s,.:;"']|$)`,
101 | 'gum'
102 | ).test(msg.text)
103 | )
104 | }, false)))
105 | if (
106 | msg.reply_to_message &&
107 | msg.sticker &&
108 | msg.sticker.file_id === 'CAADAQADyQIAAgdEiQTkPSm3CRyNIQI'
109 | ) {
110 | isReply = true
111 | }
112 | if (isCommand) {
113 | if (isPrivateChat || !chat.admin_locked) {
114 | if (msg.text.includes('start')) {
115 | language.sendLanguage(bot, chat, false)
116 | } else if (msg.text.includes('help')) {
117 | help.sendHelp(bot, chat)
118 | } else if (msg.text.includes('language')) {
119 | language.sendLanguage(bot, chat, true)
120 | } else if (msg.text.includes('limit')) {
121 | if (!isPrivateChat) {
122 | limit.sendLimit(bot, chat, msg.text)
123 | }
124 | } else if (msg.text.includes('time')) {
125 | if (!isPrivateChat) {
126 | time.sendTime(bot, chat)
127 | }
128 | } else if (msg.text.includes('lock')) {
129 | if (!isPrivateChat) {
130 | lock.toggle(bot, chat)
131 | }
132 | } else if (msg.text.includes('filterNewcomers')) {
133 | if (!isPrivateChat) {
134 | bot.sendMessage(chat.id, 'Please, use @shieldy_bot instead.')
135 | }
136 | } else if (msg.text.includes('/banme')) {
137 | if (!isPrivateChat) {
138 | bot.kickChatMember(msg.chat.id, msg.from.id, {
139 | until_date: Math.floor(Date.now() / 1000) + 60,
140 | })
141 | }
142 | } else if (msg.text.includes('/votekickWord')) {
143 | if (!isPrivateChat) {
144 | votekickWord.check(bot, chat, msg.text)
145 | }
146 | }
147 | } else {
148 | admins
149 | .isAdmin(bot, chat.id, msg.from.id)
150 | .then((isAdmin) => {
151 | if (msg.text.includes('start')) {
152 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
153 | language.sendLanguage(bot, chat, false)
154 | } else if (msg.text.includes('help')) {
155 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
156 | help.sendHelp(bot, chat)
157 | } else if (msg.text.includes('language')) {
158 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
159 | language.sendLanguage(bot, chat, true)
160 | } else if (msg.text.includes('limit')) {
161 | if (!isPrivateChat) {
162 | if (!isAdmin)
163 | return deleteMessage(msg.chat.id, msg.message_id)
164 | limit.sendLimit(bot, chat, msg.text)
165 | }
166 | } else if (msg.text.includes('time')) {
167 | if (!isPrivateChat) {
168 | if (!isAdmin)
169 | return deleteMessage(msg.chat.id, msg.message_id)
170 | time.sendTime(bot, chat)
171 | }
172 | } else if (msg.text.includes('lock')) {
173 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
174 | if (!isPrivateChat) {
175 | lock.toggle(bot, chat)
176 | }
177 | } else if (msg.text.includes('filterNewcomers')) {
178 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
179 | bot.sendMessage(chat.id, 'Please, use @shieldy_bot instead.')
180 | } else if (msg.text.includes('/banme')) {
181 | if (!isPrivateChat) {
182 | bot.kickChatMember(msg.chat.id, msg.from.id, {
183 | until_date: Math.floor(Date.now() / 1000) + 60,
184 | })
185 | }
186 | } else if (msg.text.includes('/votekickWord')) {
187 | if (!isAdmin) return deleteMessage(msg.chat.id, msg.message_id)
188 | if (!isPrivateChat) {
189 | votekickWord.check(bot, chat, msg.text)
190 | }
191 | }
192 | })
193 | .catch(/** todo: handle error */)
194 | }
195 | } else if (isEntry) {
196 | language.sendLanguage(bot, chat, false)
197 | } else if (isReply) {
198 | try {
199 | requests.startRequest(bot, msg)
200 | } catch (err) {
201 | console.error(err)
202 | // Do nothing
203 | }
204 | }
205 | })
206 | .catch(/** todo: handle error */)
207 | }
208 |
209 | bot.on('callback_query', (msg) => {
210 | const options = msg.data.split('~')
211 | const inline = options[0]
212 | if (inline === 'li') {
213 | language.setLanguage(bot, msg)
214 | } else if (inline === 'vi') {
215 | try {
216 | requests.voteQuery(bot, msg)
217 | } catch (err) {
218 | // Do nothing
219 | }
220 | } else if (inline === 'lti') {
221 | limit.setLimit(bot, msg)
222 | } else if (inline === 'tlti') {
223 | time.setTime(bot, msg)
224 | }
225 | })
226 |
227 | console.info('Bot is up and running')
228 |
229 | function getUsername(member) {
230 | return `${
231 | member.user.username
232 | ? `@${member.user.username}`
233 | : `${member.user.first_name}${
234 | member.user.last_name ? ` ${member.user.last_name}` : ''
235 | }`
236 | }`
237 | }
238 |
239 | function deleteMessage(c, m) {
240 | try {
241 | bot.deleteMessage(c, m)
242 | } catch (err) {
243 | // Do nothing
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Configuration file
3 | *
4 | * @module config
5 | * @license MIT
6 | */
7 |
8 | module.exports = {
9 | token: process.env.TELEGRAM_API_KEY,
10 | database: process.env.MONGO_DB_URL,
11 | ssl_certificate_path: process.env.SSL_CERTIFICATE_PATH,
12 | ssl_key_path: process.env.SSL_KEY_PATH,
13 | should_use_webhooks: process.env.USE_WEBHOOKS || false,
14 | webhook_callback_url: process.env.WEBHOOK_CALLBACK_URL,
15 | botan: process.env.BOTAN_API_KEY
16 | };
17 |
--------------------------------------------------------------------------------
/helpers/admins.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to get chat admins
3 | *
4 | * @module admins
5 | * @license MIT
6 | */
7 |
8 | /**
9 | * Function to get chat admin ids
10 | * @param {Telegram:Bot} bot Bot that should perform the action
11 | * @param {Telegram:ChatId} chatId Id of the chat
12 | * @return {Promise([Telegram:UserId])} Ids of admins
13 | */
14 | function getAdminIds(bot, chatId) {
15 | return new Promise((resolve, reject) => {
16 | bot.getChatAdministrators(chatId)
17 | .then((data) => {
18 | resolve(data.map(v => v.user.id));
19 | })
20 | .catch(err => reject(err));
21 | });
22 | }
23 |
24 | /**
25 | * Function to check if user id is admin
26 | * @param {Telegram:Bot} bot Bot that should perform the action
27 | * @param {Telegram:ChatId} chatId Id of the chat to get admins
28 | * @param {Telegram:UserId} userId Id of the user to check if admin
29 | * @return {Promise(Boolean)} Promise that's called on completion
30 | */
31 | function isAdmin(bot, chatId, userId) {
32 | return new Promise((resolve, reject) => {
33 | getAdminIds(bot, chatId)
34 | .then((ids) => {
35 | resolve(ids.includes(userId));
36 | })
37 | .catch(err => reject(err));
38 | });
39 | }
40 |
41 | /**
42 | * Function to check if the bot is admin in the cat
43 | * @param {Telegram:Bot} bot Bot that needs to check that
44 | * @param {Telegram:ChatId} chatId Id of the chat that's checked
45 | * @return {Promise(Boolean)} Promise with true if bot is admin at the chat
46 | */
47 | function isBotAdmin(bot, chatId) {
48 | return bot.getMe()
49 | .then(me =>
50 | getAdminIds(bot, chatId)
51 | .then(admins => admins.includes(me.id)));
52 | }
53 |
54 | /** Exports */
55 | module.exports = {
56 | getAdminIds,
57 | isAdmin,
58 | isBotAdmin,
59 | };
60 |
--------------------------------------------------------------------------------
/helpers/bot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Telegam bot
3 | *
4 | * @module bot
5 | * @license MIT
6 | */
7 |
8 | /** Dependencies */
9 | const Telegram = require('node-telegram-bot-api');
10 | const config = require('../config');
11 | const path = require('path');
12 |
13 | let bot;
14 | if (config.should_use_webhooks) {
15 | const options = {
16 | webHook: {
17 | port: 5000,
18 | key: path.join(config.ssl_key_path),
19 | cert: path.join(config.ssl_certificate_path),
20 | },
21 | };
22 |
23 | bot = new Telegram(config.token, options);
24 | bot.setWebHook(
25 | `${config.webhook_callback_url}${config.token}`,
26 | path.join(config.ssl_certificate_path))
27 | .then(() => { console.info('Telegram webhook is active'); })
28 | .catch(/** todo: handle error */);
29 | } else {
30 | bot = new Telegram(config.token, {
31 | polling: true,
32 | });
33 | console.info('Telegram is using polling');
34 | }
35 |
36 | bot.on('polling_error', () => {
37 | console.error('Polling error');
38 | });
39 |
40 | /** Exports */
41 | module.exports = bot;
42 |
--------------------------------------------------------------------------------
/helpers/db.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module db
3 | * @license MIT
4 | */
5 |
6 | /** Get schemas **/
7 | const {
8 | Chat,
9 | User,
10 | Request,
11 | } = require('../models');
12 |
13 | /**
14 | * Function to get chat, creates if none exists yet
15 | * @param {Telegram:Chat} chat Chat object that was passed from Telegram
16 | * @return {Promise(Mongoose:Chat)} Chat that was created by mongoose
17 | */
18 | function findChat(chat) {
19 | return Chat.findOne({ id: chat.id })
20 | .then((dbchat) => {
21 | if (dbchat) {
22 | return dbchat;
23 | }
24 | return new Chat(chat).save();
25 | });
26 | }
27 |
28 | function findChatsWithNewcomers() {
29 | return Chat.find({ 'newcomers.0': { '$exists': true } });
30 | }
31 |
32 | /**
33 | * Function to get user, creates if none exists yet
34 | * @param {Telegram:User} user User object that was passed from Telegram
35 | * @return {Promise(Mongoose:User)} User that was created by mongoose
36 | */
37 | function findUser(user) {
38 | return User.findOne({ id: user.id })
39 | .then((dbuser) => {
40 | if (dbuser) {
41 | return dbuser;
42 | }
43 | return new User(user).save();
44 | });
45 | }
46 |
47 | /**
48 | * Function to get request from db
49 | * @param {Mongoose:ObjectId} id Id of the request
50 | * @return {Promise(Mongoose:Request)} Found request
51 | */
52 | function findRequest(id) {
53 | return Request.findById(id);
54 | }
55 |
56 | /**
57 | * Function to create a request
58 | * @param {Mongoose:Request} request Request object without _id
59 | * @return {Promise(Mongoose:Request)} Created request
60 | */
61 | function createRequest(request) {
62 | const req = new Request(request);
63 | return req.save();
64 | }
65 |
66 | /** Exports */
67 | module.exports = {
68 | findChat,
69 | findUser,
70 | findRequest,
71 | createRequest,
72 | findChatsWithNewcomers,
73 | };
74 |
--------------------------------------------------------------------------------
/helpers/goldenBorodutchSubCount.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 |
3 | let over10000 = true
4 |
5 | function isOver10000() {
6 | return false
7 | // return over10000
8 | }
9 |
10 | setInterval(async () => {
11 | try {
12 | const response = (await axios('https://stats.borodutch.com/stats')).data
13 | over10000 = response.goldenBorodutch.subCount > 10000
14 | } catch {
15 | // Do nothing
16 | }
17 | }, 60 * 1000)
18 |
19 | module.exports = { isOver10000 }
20 |
--------------------------------------------------------------------------------
/helpers/help.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to send help message
3 | *
4 | * @module help
5 | * @license MIT
6 | */
7 |
8 | /**
9 | * Sends help message to specified chat
10 | * @param {Telegam:Bot} bot Bot that should send help
11 | * @param {Mongoose:Chat} chat Chat where to send help
12 | */
13 | function sendHelp(bot, chat) {
14 | const strings = require('./strings')()
15 |
16 | const privateText =
17 | chat.type === 'private' || chat.type === 'channel'
18 | ? 'helpPrivate'
19 | : 'helpPublic'
20 |
21 | const text = strings.translate(privateText, chat.language)
22 | bot.sendMessage(chat.id, text, {
23 | parse_mode: 'Markdown',
24 | disable_web_page_preview: true,
25 | })
26 | }
27 |
28 | /** Exports */
29 | module.exports = {
30 | sendHelp,
31 | }
32 |
--------------------------------------------------------------------------------
/helpers/isRuChat.js:
--------------------------------------------------------------------------------
1 | function isRuChat(chat) {
2 | return chat.language === 'ru'
3 | }
4 |
5 | module.exports = {
6 | isRuChat,
7 | }
8 |
--------------------------------------------------------------------------------
/helpers/language.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | const db = require('./db')
3 | const help = require('./help')
4 |
5 | /**
6 | * Sends language message to specified chat
7 | * @param {Telegam:Bot} bot Bot that should send message
8 | * @param {Mongoose:Chat} chat Chat where to send message
9 | * @param {Boolean} isCommand Whether action was triggered by /language or start of the bot
10 | */
11 | function sendLanguage(bot, chat, isCommand) {
12 | const strings = require('./strings')()
13 |
14 | const text = strings.translate('selectLanguage', chat.language)
15 | const options = {
16 | parse_mode: 'Markdown',
17 | reply_markup: { inline_keyboard: languageKeyboard(isCommand) },
18 | }
19 | options.reply_markup = JSON.stringify(options.reply_markup)
20 | bot.sendMessage(chat.id, text, options)
21 | }
22 |
23 | /**
24 | * Called when inline button with language is touched
25 | * @param {Telegram:Bot} bot Bot that should respond
26 | * @param {Telegram:Message} msg Message of inline button that was touched
27 | */
28 | function setLanguage(bot, msg) {
29 | const options = msg.data.split('~')
30 | const isCommand = parseInt(options[1], 10) === 1
31 | const code = options[2]
32 |
33 | db.findChat(msg.message.chat)
34 | .then((chat) => {
35 | chat.language = code
36 | return chat.save().then((dbchat) => {
37 | updateMessagewithSuccess(bot, msg.message, dbchat)
38 | if (!isCommand) {
39 | help.sendHelp(bot, dbchat)
40 | }
41 | })
42 | })
43 | .catch((err) => updateMessagewithError(bot, msg.message, err))
44 | }
45 |
46 | /**
47 | * Updates passed message with error statement
48 | * @param {Telegram:Bot} bot Bot that should update the message
49 | * @param {Telegram:Message} msg Message to be updated
50 | * @param {Error} error Erorr to be displayed
51 | */
52 | function updateMessagewithError(bot, msg, error) {
53 | bot.editMessageText(`❗️ _${error.message}_`, {
54 | chat_id: msg.chat.id,
55 | message_id: msg.message_id,
56 | parse_mode: 'Markdown',
57 | })
58 | }
59 |
60 | /**
61 | * Updates passed message with success statement
62 | * @param {Telegram:Bot} bot Bot that should update the message
63 | * @param {Telegram:Message} msg Message to be updated
64 | * @param {Mongoose:Chat} chat Chat that had language updated
65 | */
66 | function updateMessagewithSuccess(bot, msg, chat) {
67 | const strings = require('./strings')()
68 |
69 | bot.editMessageText(
70 | strings.translate('languageSelectedBanofbot', chat.language),
71 | {
72 | chat_id: msg.chat.id,
73 | message_id: msg.message_id,
74 | parse_mode: 'Markdown',
75 | }
76 | )
77 | }
78 |
79 | /**
80 | * Returns an inline keyboard with all available languages
81 | * @param {Boolean} isCommand Whether action was triggered by /language or start of the bot
82 | * @return {Telegram:Inline} Inline keyboard with all available languages
83 | */
84 | function languageKeyboard(isCommand) {
85 | const list = languages()
86 | const keyboard = Object.keys(list).map((key) => {
87 | const code = list[key]
88 | return [
89 | {
90 | text: key,
91 | callback_data: `li~${isCommand ? 1 : 0}~${code}`,
92 | },
93 | ]
94 | })
95 | return keyboard
96 | }
97 |
98 | /**
99 | * Returns a list of supported languages
100 | * @return {[String]]} List of the supported languages
101 | */
102 | function languages() {
103 | return {
104 | Russian: 'ru',
105 | English: 'en',
106 | Ukrainian: 'uk',
107 | Uzbek: 'uz',
108 | Kazakh: 'kz',
109 | Português: 'pt',
110 | Turkish: 'tr',
111 | Azerbaijani: 'az',
112 | German: 'de',
113 | French: 'fr',
114 | }
115 | }
116 |
117 | // Exports
118 | module.exports = {
119 | sendLanguage,
120 | setLanguage,
121 | }
122 |
--------------------------------------------------------------------------------
/helpers/limit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to send limit picker
3 | *
4 | * @module limit
5 | * @license MIT
6 | */
7 |
8 | /** Dependencies */
9 | const db = require('./db')
10 |
11 | /**
12 | * Sends limit message to specified chat
13 | * @param {Telegam:Bot} bot Bot that should send message
14 | * @param {Mongoose:Chat} chat Chat where to send message
15 | */
16 | function sendLimit(bot, chat, text) {
17 | const strings = require('./strings')()
18 |
19 | // Check if limit is set
20 | const limitNumber = +text.substr(7).trim()
21 | if (!isNaN(limitNumber) && limitNumber > 0 && limitNumber < 100000) {
22 | chat.required_voters_count = limitNumber
23 | return chat
24 | .save()
25 | .then(() =>
26 | bot.sendMessage(
27 | chat.id,
28 | strings.translate(
29 | 'limitSuccess',
30 | chat.language,
31 | chat.required_voters_count
32 | ),
33 | {
34 | parse_mode: 'Markdown',
35 | }
36 | )
37 | )
38 | .catch((err) => bot.sendMessage(chat.id, `❗️ _${error.message}_`))
39 | }
40 |
41 | const replyText = strings.translate(
42 | 'limitMessage',
43 | chat.language,
44 | chat.required_voters_count
45 | )
46 | const options = {
47 | parse_mode: 'Markdown',
48 | reply_markup: { inline_keyboard: limitKeyboard() },
49 | }
50 | options.reply_markup = JSON.stringify(options.reply_markup)
51 | bot.sendMessage(chat.id, replyText, options)
52 | }
53 |
54 | /**
55 | * Called when inline button with limit is touched
56 | * @param {Telegram:Bot} bot Bot that should respond
57 | * @param {Telegram:Message} msg Message of inline button that was touched
58 | */
59 | function setLimit(bot, msg) {
60 | const options = msg.data.split('~')
61 | const limit = parseInt(options[1], 10)
62 |
63 | db.findChat(msg.message.chat)
64 | .then((chat) => {
65 | chat.required_voters_count = limit
66 | return chat
67 | .save()
68 | .then((dbchat) => updateMessagewithSuccess(bot, msg.message, dbchat))
69 | })
70 | .catch((err) => updateMessagewithError(bot, msg.message, err))
71 | }
72 |
73 | /**
74 | * Updates passed message with error statement
75 | * @param {Telegram:Bot} bot Bot that should update the message
76 | * @param {Telegram:Message} msg Message to be updated
77 | * @param {Error} error Erorr to be displayed
78 | */
79 | function updateMessagewithError(bot, msg, error) {
80 | bot.editMessageText(`❗️ _${error.message}_`, {
81 | chat_id: msg.chat.id,
82 | message_id: msg.message_id,
83 | parse_mode: 'Markdown',
84 | })
85 | }
86 |
87 | /**
88 | * Updates passed message with success statement
89 | * @param {Telegram:Bot} bot Bot that should update the message
90 | * @param {Telegram:Message} msg Message to be updated
91 | * @param {Mongoose:Chat} chat Chat that had limit updated
92 | */
93 | function updateMessagewithSuccess(bot, msg, chat) {
94 | const strings = require('./strings')()
95 |
96 | bot.editMessageText(
97 | strings.translate(
98 | 'limitSuccess',
99 | chat.language,
100 | chat.required_voters_count
101 | ),
102 | {
103 | parse_mode: 'Markdown',
104 | chat_id: msg.chat.id,
105 | message_id: msg.message_id,
106 | }
107 | )
108 | }
109 |
110 | /**
111 | * Returns an inline keyboard with all available limit options
112 | * @return {Telegram:Inline} Inline keyboard with all available limits
113 | */
114 | function limitKeyboard() {
115 | const list = [3, 5, 8, 10, 20, 30, 40, 50, 100]
116 | const keyboard = []
117 | let temp = []
118 | list.forEach((number) => {
119 | temp.push({
120 | text: `${number}`,
121 | callback_data: `lti~${number}`,
122 | })
123 | if (temp.length >= 3) {
124 | keyboard.push(temp)
125 | temp = []
126 | }
127 | })
128 | return keyboard
129 | }
130 |
131 | /** Exports */
132 | module.exports = {
133 | sendLimit,
134 | setLimit,
135 | }
136 |
--------------------------------------------------------------------------------
/helpers/localizations.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | selectLanguage: {
3 | uk: '👋 Будь ласка, оберіть свою мову.',
4 | en: '👋 Please, select your language.',
5 | pt: '👋 Por favor, selecione o idioma.',
6 | ru: '👋 Пожалуйста, выберите свой язык.',
7 | uz: '👋 Iltimos, tilni tanlang.',
8 | kz: '👋 Sіzdіń tіlіńіzdі tańdańyz.',
9 | tr: '👋 Lütfen dilinizi seçin.',
10 | az: '👋 Xahiş edirəm dilinizi seçin.',
11 | de: '👋 Bitte wähle deine Sprache.',
12 | fr: '👋 S\'il vous plaît, Veuillez sélectionner votre langue.',
13 | },
14 | helpPrivate: {
15 | uk:
16 | "😎 *Banofbot* дозволяє голосувати за бан учасників чату. З'явився спамер або ще якийсь негідник, а адмінів немає поруч? Просто надішліть `@banofbot` у відповідь на повідомлення порушника, і бот почне голосування.\n\n@banofbot чудово працює в групових чатах — сміливіше, додайте його в кілька! Не забудьте зробити бота адміном, інакше він не зможе працювати.\n\n/help — Показує це повідомлення 😱\n/language — Дозволяє обрати мову 📣\n\nВи хочете підтримати автора бота? Я написав наукову книгу про те, як жити здоровіше та щасливіше! Ви можете купити її на Амазоні — amazon.com/dp/B0CHL7WRYM або на сайті книги — wdlaty.com. Дякую!",
17 | en:
18 | "😎 *Banofbot* allows you to vote to ban users. Got a spammer or flamer but nobody is out there to ban one? Simply reply to the violator's message with the text `@banofbot`, and the bot will start the voting.\n\n@banofbot works well in group chats — so go on, add it to one of your precious chats! Don't forget to set it as an admin, otherwise it wouldn't work.\n\n/help — Shows this message 😱\n/language — Lets you pick the language 📣\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
19 | pt:
20 | "😎 *Banofbot* permite que você vote para banir usuários. Tem alguém fazendo spam ou flood e não tem um admin disponível para dar ban? Simplesmente responda à mensagem do infrator com o texto `@banofbot` e o bot iniciará a votação.\n\n@banofbot funciona bem em grupos — então o adicione a um de seus preciosos bate-papos! Não se esqueça de configurá-lo como administrador, caso contrário não vai adiantar nada.\n\n/help — Mostra esta mensagem 😱\n/language — Permite escolher o idioma 📣\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
21 | ru:
22 | '😎 *Banofbot* позволяет голосовать за бан участников чата. Появился спамер или ещё какой негодяй, а админов нет рядом? Просто ответьте на сообщение нарушителя текстом `@banofbot`, и бот начнет голосование.\n\n@banofbot отлично работает в групповых чатах — не бойтесь, добавьте его в парочку! Не забудьте сделать бота админом, иначе он не сможет работать.\n\n/help — Показывает это сообщение 😱\n/language — Позволяет выбрать язык 📣\n\nХотите поддержать автора бота? Я написал научную книгу о том, как жить здоровее и счастливее! Купить ее можно на Амазоне — amazon.com/dp/B0CHL7WRYM или на сайте книги — wdlaty.com. Спасибо!',
23 | uz:
24 | "😎 *Banofbot* guruh a’zolarini ban qilish uchun ovoz berishga yordam beradi. Spamer yoki qandaydir bezori paydo bo‘ldi, lekin adminlar bandmi? Shunchaki bezorining xabariga javob qilib `@banofbot` so‘zini yuboring va bot ban qilish uchun ovoz to‘plashni boshlaydi.\n\n@banofbot guruhlarda zo‘r ishlaydi — uni bir nechta guruhlarga qo‘shing va rivojlantirishga yordam bering! Botni admin qilib tayinlashni unutmang, aks holda u ishlamaydi.\n\n/help — Ushbu xabarni ko‘rsatadi 😱\n/language — Foydalanish tilini tanlashga yordam beradi 📣\n\nBot muallifini qo‘llab-quvvatlashni istaysizmi? Men qanday qilib sog‘lomroq va baxtliroq yashash haqida ilmiy kitob yozganman! Siz uni Amazonda - amazon.com/dp/B0CHL7WRYM yoki vebsaytda — wdlaty.com sotib olishingiz mumkin. Kattakon rahmat!",
25 | kz:
26 | "😎 *Banofbot* chattyń paıdalanýshylaryn ban etýge daýys berý múmkіndіgіn beredі. Spammer nemese basqa da qasqyr chatta otyr, bіraq jaqynda admın joq pa? Tek qana `@banofbot` mátіnі bar habarlamamen buzaqyǵa jaýap berіńіz de, bot daýys berýdі bastaıdy. \n\nBotty admın qyldyrýǵa umytpańyz, áıtpese ol jumys іstemeıdі! \n\n/help — Bul habardy kórsetedі 😱\n/language — Tіldі tańdaýǵa múmkіndіk beredі 📣\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
27 | tr:
28 | '😎 *Banofbot* kullanıcıları yasaklamak için oy kullanmanıza izin verir. İstenmeyen ileti gönderen bir kullanıcı var veya bir tane yasaklayacak kimse yok mu? İhlalci iletisine `@banofbot` yazıp cevaplamanız yeterli: Bot hemen oylamaya başlayacaktır.\n\n/help — Bu mesajı gösterir 😱\n/language — Dili seçmenizi sağlar 📣\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
29 | az:
30 | '😎 *Banofbot* istifadəçiləri qadağan etmək üçün səs verməyə imkan verir. Spammer və ya alovlandıran var, amma heç kim qadağan edə bilməz? Qanunu pozanın mesajına sadəcə `@banofbot` mətni ilə cavab verin və bot səsverməyə başlayacaq.\n\n@banofbot qrup söhbətlərində yaxşı işləyir — davam edin, dəyərli söhbətlərinizdən birinə əlavə edin! Admin olaraq qurmağı unutmayın, əks halda işə yaramayacaq.\n\n/help — Bu mesajı göndərir.😱\n/language — Dilinizi seçməyə imkan verir 📣\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
31 | de:
32 | '😎 *Banofbot* erlaubt es, über das Bannen von Benutzern abzustimmen. Ihr habt einen Spammer oder Beleidiger, und niemand ist hier um ihn zu bannen? Antworte einfach mit dem Text `@banofbot` auf die Nachricht und der Bot startet die Abstimmung.\n\n@banofbot funktioniert gut in Gruppenchats — also los, füge ihn einem deiner wertvollen Chats hinzu! Vergiss nicht ihm Admin-Rechte zu geben, sonst funktioniert es nicht.\n\n/help — Zeigt diese Nachricht 😱\n/language — Wähle die Sprache 📣\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
33 | fr:
34 | '😎 *Banofbot* vous permet de voter pour bannir des utilisateurs. Vous avez un spammeur ou un insulteur mais personne n\'est là pour le bannir ? Répondez simplement au message du contrevenant avec le texte `@banofbot` et le bot commencera le vote.\n\n@banofbot fonctionne bien dans les chats de groupe — alors allez-y, ajoutez-le à l\'un de vos précieux chats ! N\'oubliez pas de le configurer en tant qu\'administrateur, sinon il ne fonctionnera pas.\n\n/help — Affiche ce message 😱\n/language — Vous permet de choisir la langue 📣\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
35 | },
36 | helpPublic: {
37 | uk:
38 | "😎 *Banofbot* дозволяє голосувати за бан учасників чату. З'явився спамер або ще якийсь негідник, а адмінів немає поруч? Просто надішліть `@banofbot` у відповідь на повідомлення порушника, і бот почне голосування.\n\n/help — Відображає це повідомлення 😱\n/language — Дозволяє обрати мову 📣\n/lock — Увімкнути або вимкнути доступ неадмінів до команд бота 🔑\n/limit — Змінити мінімальну кількість голосів для кіку користувача ✌️\n/time — Змінити мінімальний проміжок часу між банами\n/votekickWord — дозволяє додати слова, які починають голосування. Спробуйте, наприклад, `/votekickWord кік, челендж, бійка` 🐸\n\nНе забудьте зробити @banofbot адміном, інакше він не зможе працювати.\n\nВи хочете підтримати автора бота? Я написав наукову книгу про те, як жити здоровіше та щасливіше! Ви можете купити її на Амазоні — amazon.com/dp/B0CHL7WRYM або на сайті книги — wdlaty.com. Дякую!",
39 | en:
40 | "😎 *Banofbot* allows you to vote to ban users. Got a spammer or flamer but nobody is out there to ban one? Simply reply to the violator's message with the text `@banofbot` and the bot will start the voting.\n\n/help — Shows this message 😱\n/language — Lets you pick the language 📣\n/lock — Toggles lock or unlock of non-admins using commands 🔑\n/limit — Lets you set minimal number of voters to kick a user ✌️\n/time — Allows you to select the minimum time between bans\n/votekickWord — allows you to set more votekick words. Use like `/votekickWord kick, trial, challenge` 🐸\n\nDon't forget to set @banofbot as an admin, otherwise it wouldn't work.\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
41 | pt:
42 | "😎 *Banofbot* permite que você vote para banir usuários. Tem alguém fazendo spam ou flood, mas não tem um admin disponível para dar ban? Simplesmente responda à mensagem do infrator com o texto `@banofbot` e o bot iniciará a votação.\n\n/help — Mostra esta mensagem 😱 \n/language — Permite escolher o idioma 📣\n/lock — Alterna o bloqueio de não-administradores poderem usar os comandos 🔑\n/limit — Permite que você defina um número mínimo de membros para expulsar um usuário ✌️\n/time — permite que você selecione o tempo mínimo entre banimentos\n/votekickWord — allows you to set more votekick words. Use like `/votekickWord kick, trial, challenge` 🐸\n\nNão se esqueça de configurar @banofbot como administrador, caso contrário não vai funcionar.\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
43 | ru:
44 | '😎 *Banofbot* позволяет голосовать за бан участников чата. Появился спамер или ещё какой негодяй, а админов нет рядом? Просто ответьте на сообщение нарушителя текстом `@banofbot`, и бот начнет голосование.\n\n/help — Показывает это сообщение 😱\n/language — Позволяет выбрать язык 📣\n/lock — Включить или выключить доступ неадминов к командам бота 🔑\n/limit — Изменить минимальное количество голосов для кика пользователя ✌️\n/time — Изменить минимальный промежуток времени между банами\n/votekickWord — Добавить слова для начала голосования. Попробуйте, например, `/votekickWord кик, челлендж, драка` 🐸\n\nНе забудьте сделать @banofbot админом, иначе он не сможет работать.\n\nХотите поддержать автора бота? Я написал научную книгу о том, как жить здоровее и счастливее! Купить ее можно на Амазоне — amazon.com/dp/B0CHL7WRYM или на сайте книги — wdlaty.com. Спасибо!',
45 | uz:
46 | "😎 *Banofbot* guruh a’zolarini ban qilish uchun ovoz berishga yordam beradi. Spamer yoki qandaydir bezori paydo bo‘ldi, lekin adminlar bandmi? Shunchaki bezorining xabariga javob qilib `@banofbot` so‘zini yuboring va bot ban qilish uchun ovoz to‘plashni boshlaydi.\n\n/help — Ushbu xabarni ko‘rsatadi 😱\n/language — Foydalanish tilini tanlashga yordam beradi 📣\n/lock — Oddiy foydalanuvchilarga (admin bo‘lmaganlarga) bot buyruqlarini ishlatishni ta’qiqlaydi 🔑\n/limit — Foydalanuvchini ban qilish uchun kerak bo‘lgan eng kam ovozlar sonini belgilaydi ✌️\n/time — Banlar orasidagi eng kam vaqtni belgilaydi\n/votekickWord — allows you to set more votekick words. Use like `/votekickWord kick, trial, challenge` 🐸\n\nBotni admin qilib tayinlashni unutmang, aks holda u ishlamaydi.\n\nBot muallifini qo‘llab-quvvatlashni istaysizmi? Men qanday qilib sog‘lomroq va baxtliroq yashash haqida ilmiy kitob yozganman! Siz uni Amazonda - amazon.com/dp/B0CHL7WRYM yoki vebsaytda — wdlaty.com sotib olishingiz mumkin. Kattakon rahmat!",
47 | kz:
48 | "😎 *Banofbot* chattyń paıdalanýshylaryn ban etýge daýys berý múmkіndіgіn beredі. Spammer nemese basqa da qasqyr chatta otyr, bіraq jaqynda admın joq pa? Tek qana `@banofbot` mátіnі bar habarlamamen buzaqyǵa jaýap berіńіz de, bot daýys berýdі bastaıdy.\n\n/help — Bul habardy kórsetedі 😱\n/language — Tіldі tańdaýǵa múmkіndіk beredі 📣\n/lock — Bot komandalaryna admın emes qoldanýshylarǵa qatynaý berý nemese alý. 🔑\n/limit — Shyǵaryp jіberý úshіn eń az daýys sanyn tańdaý ✌️\n/time — Banǵa daýys berý aralyǵyndagy en az sekýndtar sanyn tańdaý\n/votekickWord — allows you to set more votekick words. Use like `/votekickWord kick, trial, challenge` 🐸\n\nBotty admın qyldyrýǵa umytpańyz, áıtpese ol jumys іstemeıdі!\n\nDo you want to support the bot's author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book's website — wdlaty.com. Thank you!",
49 | tr:
50 | '😎 *Banofbot* kullanıcıları yasaklamak için oy kullanmanıza izin verir. İstenmeyen ileti gönderen bir kullanıcı var veya bir tane yasaklayacak kimse yok mu? İhlalci iletisine `@banofbot` yazıp cevaplamanız yeterli: Bot hemen oylamaya başlayacaktır.\n\n/help — Bu mesajı gösterir 😱\n/language — Dili seçmenizi sağlar 📣\n/lock — Komutları kullanarak yönetici olmayanların kilidini açar veya kapatır 🔑\n/limit — Bir kullanıcıyı kovmak için en az sayıda seçmen belirlemenizi sağlar ✌️\n/time — Yasaklamalar arasındaki minimum süreyi seçmenizi sağlar\n/votekickWord — allows you to set more votekick words. Use like `/votekickWord kick, trial, challenge` 🐸\n\n@banofbot botunu bir yönetici olarak ayarlamayı unutmayın, aksi halde işe yaramaz.\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
51 | az:
52 | '😎 *Banofbot* istifadəçiləri qadağan etmək üçün səs verməyə imkan verir. Spammer və ya alovlandıran var, amma heç kim qadağan edə bilməz? Qanunu pozanın mesajına sadəcə `@banofbot` mətni ilə cavab verin və bot səsverməyə başlayacaq.\n\n/help — Bu mesajı göstərir 😱 \n/language — Dili seçməyə imkan verir 📣 \n/lock — Kilidi açar və ya əmrlərdən istifadə edərək admin olmayanların kilidini açmaq 🔑 \n/limit — Bir istifadəçiyə təpik vurmaq üçün seçicilərin minimum sayını təyin etməyə imkan verir ✌️\n/time — arasında minimum vaxt seçməyə imkan verir qadağalar\n/votekickWord — daha çox səs seçmə sözləri təyin etməyə imkan verir. `/votekickWord kick, trial, challenge` kimi istifadə edin 🐸 \n\n@banofbot u admin olaraq təyin etməyi unutmayın, əks halda alınmayacaq.\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
53 | de:
54 | '😎 *Banofbot* erlaubt es, über das Bannen von Benutzern abzustimmen. Ihr habt einen Spammer oder Beleidiger, und niemand ist hier um ihn zu bannen? Antworte einfach mit dem Text `@banofbot` auf die Nachricht und der Bot startet die Abstimmung.\n\n/help — Zeigt diese Nachricht 😱\n/language — Wähle die Sprache 📣\n/lock — Aktiviert oder deaktiviert, dass nicht-Admins Kommandos nutzen können 🔑\n/limit — Wähle die anzahl benötigter Stimmen, um einen User zu bannen ✌️\n/time — Erlaubt dir, die Zeitspanne zwischen Bans zu setzen\n/votekickWord — Erlaubt dir, andere Wörter zu wählen um eine Abstimmung zu starten. Verwende es wie `/votekickWord entfernen, abstimmung, bannen` 🐸\n\nVergiss nicht, @banofbot Admin-Rechte zu geben, sonst funktioniert es nicht.\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
55 | fr:
56 | '😎 *Banofbot* vous permet de voter pour bannir des utilisateurs. Vous avez un spammeur ou un insulteur mais personne n\'est là pour le bannir ? Répondez simplement au message du contrevenant avec le texte `@banofbot` et le bot commencera le vote.\n\n/help — Affiche ce message 😱\n/language — Vous permet de choisir la langue 📣\n/lock — Permet de verrouiller ou de déverrouiller les utilisateurs non-admins utilisant des commandes 🔑\n/limit — Vous permet de définir le nombre minimal de votants pour expulser un utilisateur ✌️\n/time — Vous permet de sélectionner le temps minimum entre les bannissements `/votekickWord — vous permet de définir plus de mots de votekick. Utilisez comme `/votekickWord kick, trial, challenge` 🐸\n\nN\'oubliez pas de définir @banofbot en tant qu\'admin, sinon cela ne fonctionnera pas.\n\nDo you want to support the bot\'s author? I wrote a scientific book about how to live healthier and happier! You can buy it on Amazon — amazon.com/dp/B0CHL7WRYM or on the book\'s website — wdlaty.com. Thank you!',
57 | },
58 | languageSelectedBanofbot: {
59 | uk: '@banofbot тепер розмовляє українською. Дякую!',
60 | en: '@banofbot now speaks English. Thank you!',
61 | pt: '@banofbot agora fala português. Obrigado!',
62 | ru: '@banofbot теперь говорит по-русски. Спасибо!',
63 | uz: '@banofbot endi o‘zbekcha gapiradi. Rahmat!',
64 | kz: '@banofbot endі qazaqsha sóıleıdі. Rahmet!',
65 | tr: '@banofbot artık Türkçe konuşuyor. Teşekkür ederiz!',
66 | az: '@banofbot indi Azərbaycan dilində danışır. Çox sağ ol!',
67 | de: '@banofbot spricht nun Deutsch. Danke!',
68 | fr: '@banofbot parle désormais le français. Merci!',
69 | },
70 | lockOnBanofbot: {
71 | uk:
72 | '🔑 Чудово! *Banofbot* тепер реагує тільки на команди, надіслані *адмінами*, у цьому чаті.',
73 | en:
74 | '🔑 Great! *Banofbot* will now respond only to command calls sent by *admins* in this chat.',
75 | pt:
76 | '🔑 OK! *Banofbot* somente vai atender a comandos enviados por *admins".',
77 | ru:
78 | '🔑 Чудно! *Banofbot* теперь реагирует только на команды, отправленные *админами*, в этом чате.',
79 | uz:
80 | '🔑 Ajoyib! *Banofbot* endi faqat ushbu guruhdagi *adminlar* yuborgan buyruqlarga javob beradi.',
81 | kz:
82 | '🔑 Keremet! *Banofbot* endі osy chatta *admınnyń* komandalaryna ǵana jaýap beredі.',
83 | tr:
84 | '🔑 Harika! *Banofbot* artık bu sohbette yalnızca *yöneticiler* tarafından gönderilen komut çağrılarına yanıt verecek.',
85 | az:
86 | '🔑 Əla! *Banofbot* indi yalnız *söhbət* administratorları tərəfindən göndərilən komanda zənglərinə cavab verəcəkdir.',
87 | de:
88 | '🔑 Super! *Banofbot* antwortet nun nur auf Kommandos, die von *admins* in diesem Chat gesendet werden.',
89 | fr:
90 | '🔑 Super! *Banofbot* ne répondra désormais qu\'aux commandes envoyées par les *admins* dans ce chat.',
91 | },
92 | lockOffBanofbot: {
93 | uk:
94 | '🔑 Чудово! *Banofbot* тепер реагує на команди, надіслані *будь-якими користувачами*, у цьому чаті.',
95 | en:
96 | '🔑 Great! *Banofbot* will now respond to command calls from *anyone* in this chat.',
97 | pt:
98 | '🔑 OK! *Banofbot* vai atender a comandos de *qualquer membro*.',
99 | ru:
100 | '🔑 Чудно! *Banofbot* теперь реагирует на команды, отправленные *любыми пользователями*, в этом чате.',
101 | uz:
102 | '🔑 Ajoyib! *Banofbot* endi ushbu guruhdagi *istalgan foydalanuvchi* yuborgan buyruqlarga javob beradi.',
103 | kz:
104 | '🔑 Keremet! *Banofbot* endі osy chatta *kez kelgen paıdalanýshy* arqyly jіberіlgen komandalarǵa jaýap beredі.',
105 | tr:
106 | '🔑 Harika! *Banofbot* artık bu sohbette *herkes* tarafından gönderilen komut çağrılarına cevap verecek.',
107 | az:
108 | '🔑 Əla! *Banofbot* indi yalnız bu söhbətdəki *hər kəsdən* gələn komanda zənglərinə cavab verəcəkdir.',
109 | de:
110 | '🔑 Super! *Banofbot* antwortet nun auf Kommandos von *jedem/jeder* in diesem Chat.',
111 | fr:
112 | '🔑 Super! *Banofbot* répondra désormais aux commandes de *n\'importe qui* dans ce chat.',
113 | },
114 | kickRequest: {
115 | uk: '$[1] хоче кікнути $[2] з чату. Згодні?',
116 | en: '$[1] would like to kick $[2]. Do you agree?',
117 | pt: '$[1] acha que $[2] deveria ser retirado do grupo. Concordam?',
118 | ru: '$[1] хочет кикнуть $[2] из чата. Согласны?',
119 | uz: '$[1] guruhdan $[2]ni ban qilishni istayapti. Rozimisiz?',
120 | kz: '$[1] chattań $[2]-ti shyǵatynyn qalaıdy. Sіz kelіsesіz be?',
121 | tr: '$[1], $[2] kullanıcısını kovmak istiyor. Katılıyor musunuz?',
122 | az: '$[1] təpik vurmaq istəyir $[2]. Razısan?',
123 | de: '$[1] würde gerne $[2] aus dem Chat bannen. Stimmst du zu?',
124 | fr: '$[1] souhaite exclure $[2]. Est-tu d\'accord?',
125 | },
126 | kickAction: {
127 | uk: '🔫 Кікнути ($[1]/$[2])',
128 | en: '🔫 Kick ($[1]/$[2])',
129 | pt: '🔫 Retirar ($[1]/$[2])',
130 | ru: '🔫 Кикнуть ($[1]/$[2])',
131 | uz: '🔫 Ban qilish ($[1]/$[2])',
132 | kz: '🔫 Shyǵaryp jіberý ($[1]/$[2])',
133 | tr: '🔫 Kov ($[1]/$[2])',
134 | az: '🔫 Vurun ($[1]/$[2])',
135 | de: '🔫 Bannen ($[1]/$[2])',
136 | fr: '🔫 Exclure ($[1]/$[2])',
137 | },
138 | saveAction: {
139 | uk: '👼 Вибачити ($[1]/$[2])',
140 | en: '👼 Save ($[1]/$[2])',
141 | pt: '👼 Absolver ($[1]/$[2])',
142 | ru: '👼 Простить ($[1]/$[2])',
143 | uz: '👼 Kechirish ($[1]/$[2])',
144 | kz: '👼 Keshіrý',
145 | tr: '👼 Tut ($[1]/$[2])',
146 | az: '👼 Yadda saxla ($[1]/$[2])',
147 | de: '👼 Im Chat behalten ($[1]/$[2])',
148 | fr: '👼 Sauver ($[1]/$[2])',
149 | },
150 | resultSave: {
151 | uk:
152 | '👼 $[1] врятований — цього разу його не кікнули.\n\nЗа порятунок проголосували:\n$[2]',
153 | en:
154 | '👼 $[1] has been saved — no kick for you this time.\n\nVoters who chose to save:\n$[2]',
155 | pt:
156 | '👼 $[1] foi absolvido — sem expulsões por enquanto.\n\nQuem votou pela absolvição:\n$[2]',
157 | ru:
158 | '👼 $[1] спасён — в этот раз его не кикнули.\n\nЗа спасение проголосовали:\n$[2]',
159 | uz:
160 | '👼 $[1] guruhda qoldirildi — bu safar u ban qilinmadi.\n\nGuruhda qoldirishga berilgan ovozlar:\n$[2]',
161 | kz:
162 | '👼 $[1] qutqaryldy — bul joly ony shyǵaryp jіbermedі. \n\nQutqarý úshіn daýys berdі:\n$[2]',
163 | tr:
164 | '👼 $[1] grupta tutuldu — bu seferlik sizin için kovma eylemi yok.\n\nGrupta tutulmasını isteyenler:\n$[2]',
165 | az:
166 | '👼 $[1] Qeyd edildi — bu dəfə sizin üçün bir zərbə yoxdur. \\n\\nQənaəti seçən seçicilər:\\n$[2]',
167 | de:
168 | '👼 $[1] wurde im Chat behalten — diesmal kein Bannen aus dem Chat für dich.\n\nLeute die für im Chat behalten gestimmt haben:\n$[2]',
169 | fr:
170 | '👼 $[1] a été sauvé — pas d\'exclusion pour cette fois.\n\nLes votants qui ont choisi de sauver:\n$[2]',
171 | },
172 | resultKick: {
173 | uk:
174 | '🔫 $[1] кікнуто — повернути цього користувача можна тільки розбаном у налаштуваннях чату.\n\nЗа кік проголосували:\n$[2]',
175 | en:
176 | '🔫 $[1] has been kicked — the only way to get this user back is for admins to manualy unban in chat settings.\n\nVoters who chose to kick:\n$[2]',
177 | pt:
178 | '🔫 $[1] foi retirado do grupo — a única forma de retornar é um admin removendo o banimento do usuário nas configurações do grupo.\n\nQuem votou pela expulsão:\n$[2]',
179 | ru:
180 | '🔫 $[1] кикнут — вернуть этого пользователя можно только разбаном в настройках чата.\n\nЗа кик проголосовали:\n$[2]',
181 | uz:
182 | '🔫 $[1] ban qilindi — foydalanuvchini qaytarish uchun uni guruh qora ro‘yxatdan olib tashlash kerak.\n\nBan qilish uchun ovozlar soni:\n$[2]',
183 | kz:
184 | '🔫 $[1] shyǵaryp jіberіldі — bul paıdalanýshyny tek chat parametrlerі arqyly qaıtarylýy múmkіn.\n\nShyǵaryp jіberý úshіn daýys bergen:\n$[2]',
185 | tr:
186 | '🔫 $[1] kovuldu — bu kullanıcıyı geri almanın tek yolu, yöneticilerin sohbet ayarlarından yasağı elle kaldırmasıdır.\n\nGruptan kovulmasını isteyenler:\n$[2]',
187 | az:
188 | '🔫 $[1] təpik atıldı — bu istifadəçini geri qaytarmağın yeganə yolu administratorların sohbet ayarlarında unban elan etməsidir. \n\nDoğuşmağı seçən seçicilər:\n$[2]',
189 | de:
190 | '🔫 $[1] wurde aus dem Chat gebannt — Der einzige Weg, diesen User in den Chat zurück zu lassen ist für die Admins, den User in den Chat-Settings manuell zu entbannen.\n\nLeute die für Bannen haben:\n$[2]',
191 | fr:
192 | '🔫 $[1] a été exclu — le seul moyen d\'autoriser cet utilisasteur à revenir est que les admins l\'autorisent manuellement dans les paramètres du chat.\n\nLes votants qui ont choisi l\'exclusion:\n$[2]',
193 | },
194 | voteSave: {
195 | uk: 'Ви вже проголосували за 👼',
196 | en: 'You have already voted for 👼',
197 | pt: 'Você já vou em 👼',
198 | ru: 'Вы уже проголосовали за 👼',
199 | uz: 'Siz 👼 uchun ovoz berib bo‘ldingiz',
200 | kz: 'Sіz daýys berіp qoıdynyz 👼',
201 | tr: 'Zaten oy kullandınız 👼',
202 | az: 'Artıq səs vermisiniz 👼',
203 | de: 'Du hast bereits für 👼 gestimmt',
204 | fr: 'Tu as déjà voté pour 👼',
205 | },
206 | voteKick: {
207 | uk: 'Ви вже проголосували за 🔫',
208 | en: 'You have already voted for 🔫',
209 | pt: 'Você já votou em 🔫',
210 | ru: 'Вы уже проголосовали за 🔫',
211 | uz: 'Siz 🔫 uchun ovoz berib bo‘ldingiz',
212 | kz: 'Sіz daýys berіp qoıdynyz 🔫',
213 | tr: 'Zaten oy kullandınız 🔫',
214 | az: 'Artıq səs vermisiniz 🔫',
215 | de: 'Du hast bereits für 🔫 gestimmt',
216 | fr: 'Tu as déjà voté pour 🔫',
217 | },
218 | adminError: {
219 | uk:
220 | '🔥 Ой! Схоже, що @banofbot тут ще не адмін. Будь-ласка, попросіть адмінів зробити @banofbot також адміном, інакше він не буде працювати. Дякую!',
221 | en:
222 | '🔥 Oops! Looks like @banofbot is not an admin here yet. Please ask admins to set @banofbot as an admin as well, otherwise it will not work. Thanks!',
223 | pt:
224 | '🔥 Ops! Parece que o @banofbot ainda não é um administrador. Por favor, peça aos administradores que definam @banofbot como administrador também, senão não funcionará. Obrigado!',
225 | ru:
226 | '🔥 Ой! Похоже, что @banofbot здесь ещё не админ. Пожалуйста, попросите админов сделать @banofbot админом тоже, иначе он не будет работать. Спасибо!',
227 | uz:
228 | "🔥 Vay! @banofbot ushbu guruhda admin emasga o‘xshaydi. Iltimos, adminlardan @banofbot'ni guruh admini qilib tayinlashni so‘rang, aks holda u ishlamaydi. Rahmat!",
229 | kz:
230 | '🔥 Oı! @Banofbot álі admın emes sııaqty. Admınderden @banofbot-dі admin dep qosýdy surańyz, áıtpese ol jumys іstemeıdі. Rahmet!',
231 | tr:
232 | '🔥 Hay aksi! Görünüşe göre @banofbot henüz bir yönetici değil. Lütfen yöneticilerinizden @banofbot botunu bir yönetici olarak ayarlamasını isteyin, aksi takdirde çalışmaz. Teşekkür ederiz!',
233 | az:
234 | '🔥 Vay! Görünür @banofbot hələ burada admin deyil. Zəhmət olmasa, administratorlardan @banofbot-u da admin olaraq təyin etmələrini xahiş edin, əks halda işə yaramır. Təşəkkürlər!',
235 | de:
236 | '🔥 Ups! Sieht so aus als wäre @banofbot noch kein Admin hier. Bitte Admins darum, @banofbot ebenfalls zu einem Admin zu machen, ansonsten wird er nicht funktionieren. Danke!',
237 | fr:
238 | '🔥 Oops! On dirait que @banofbot n\'est pas admin ici. Veuillez demander aux admins de définir @banofbot en tant qu\'admin également, autrement cela ne fonctionnera pas. Merci!',
239 | },
240 | limitMessage: {
241 | uk:
242 | '✌️ Будь ласка, оберіть мінімальну кількість голосів для кіка користувача. Поточна кількість — *$[1]*',
243 | en:
244 | '✌️ Please, select the minimum number of votes to kick a user. Current number is *$[1]*',
245 | pt:
246 | '✌️ Por favor, selecione o número mínimo de votos para retirar um usuário. O número atual é *$[1]*',
247 | ru:
248 | '✌️ Пожалуйста, выберите минимальное количество голосов для кика пользователя. Текущее количество — *$[1]*',
249 | uz:
250 | '✌️ Iltimos, foydalanuvchilarni ban qilish uchun kerak bo‘lgan eng kam ovozlar sonini ayting. Hozir — *$[1]*',
251 | kz:
252 | '✌️ Paıdalanýshyny shyǵaryp jіberý úshіn eń az daýys sanyn tańdańyz. Aǵymdaǵy san — *$[1]*',
253 | tr:
254 | '✌️ Lütfen bir kullanıcıyı kovmak için minimum oy sayısını seçin. Mevcut sayı: *$[1]*',
255 | az:
256 | '✌️ Xahiş edirəm bir istifadəçiyə təpik vurmaq üçün minimum səs sayını seçin. Cari nömrə *$[1]*',
257 | de:
258 | '✌️ Bitte wähle die anzahl benötigter Stimmen, um einen User zu bannen. Die aktuelle Anzahl ist *$[1]*',
259 | fr:
260 | '✌️ Merci de définir un nombre de votants minimum pour exclure un utilisateur. Le nombre actuel est de *$[1]*',
261 | },
262 | limitSuccess: {
263 | uk:
264 | '@banofbot тепер буде кікати користувача, якщо *$[1]* людей проголосує за це. Дякую!',
265 | en:
266 | '@banofbot will now kick a user if *$[1]* people vote for it. Thanks!',
267 | pt:
268 | '@banofbot agora vai expulsar um usuário se *$[1]* pessoas votarem. Obrigado!',
269 | ru:
270 | '@banofbot теперь будет кикать пользователя, если *$[1]* людей проголосуют за это. Спасибо!',
271 | uz:
272 | '@banofbot endi foydalanuvchini *$[1]*ta qarshi ovozdan keyin kick qiladi. Rahmat!',
273 | kz:
274 | '@banofbot endі paıdalanýshyny *$[1]* adam daýys bergen kezde, ony shyǵaryp jіberedі. Rahmet!',
275 | tr:
276 | '@banofbot artık *$[1]* kişi oy verirse bir kullanıcıyı kovacak. Teşekkür ederiz!',
277 | az:
278 | '@banofbot indi *$[1]* nəfərin buna səs verməsi halında bir istifadəçini vuracaq. Təşəkkürlər!',
279 | de:
280 | '@banofbot bannt nun einen User wenn *$[1]* leute dafür abstimmen. Danke!',
281 | fr:
282 | '@banofbot va exclure un utilisateur si *$[1]* personnes votent. Merci!',
283 | },
284 | tooSoonError: {
285 | uk:
286 | '🔥 Схоже, ви намагаєтеся почати голосування за кік занадто рано. Мінімальний проміжок часу між голосуваннями можна змінити за допомогою команди /time. Дякую!',
287 | en:
288 | '🔥 Looks like you are trying to start a new ban request too soon. You can change the time limit for ban requests by using /time command. Thanks!',
289 | pt:
290 | '🔥 Parece que você está tentando iniciar uma nova solicitação cedo demais. Você pode alterar o limite de tempo para este tipo de solicitação usando o comando /time. Obrigado!',
291 | ru:
292 | '🔥 Похоже, вы пытаетесь начать голосование за кик слишком рано. Минимальный промежуток времени между голосованиями можно изменить при помощи команды /time. Спасибо!',
293 | uz:
294 | '🔥 Kick qilish uchun ovoz to‘plashni juda erta boshlashga urinyapsiz. Ban qilish uchun ovoz berish jarayonlari orasidagi vaqtni /time buyrug‘i orqali o‘zgartirishingiz mumkin. Rahmat!',
295 | kz:
296 | '🔥 Sіz tym erte shyǵaryp jіberýge daýys berýdі bastap jatyrsyz. Daýys berý arasyndaǵy ýaqyt aralyǵyn /time komandasymen ózgertýge bolady. Rahmet!',
297 | tr:
298 | '🔥 Çok kısa sürede yeni bir yasak isteği başlatmaya çalışıyor gibi görünüyorsun. /time komutunu kullanarak yasak isteklerinin zaman sınırını değiştirebilirsin. Teşekkür ederiz!',
299 | az:
300 | '🔥 Göründüyü kimi yeni bir qadağan tələbini çox tez başlamağa çalışırsınız. /time əmrini istifadə edərək qadağan istəkləri üçün vaxt məhdudiyyətini dəyişə bilərsiniz. Təşəkkürlər!',
301 | de:
302 | '🔥 Sieht so aus, als würdest du zu schnell eine neue Ban-Anfrage starten. Du kannst das Zeitlimit für Ban-Anfragen mit dem /time-Kommando ändern. Danke!',
303 | fr:
304 | '🔥 Il semble que vous essayez de lancer une nouvelle demande d\'exclusion trop tôt. Vous pouvez modifier la limite de temps pour les demandes d\'exclusions en utilisant la commande /time. Merci!',
305 | },
306 | timeLimitMessage: {
307 | uk:
308 | '✌️ Будь ласка, оберіть мінімальний проміжок часу між голосуваннями за бан. Поточне обмеженння — *$[1]* секунд.',
309 | en:
310 | '✌️ Please, select the minimum amount of time between ban requests. The current limit is *$[1]* seconds.',
311 | pt:
312 | '✌️ Por favor, selecione o tempo mínimo entre as solicitações. O limite atual é *$[1]* segundos.',
313 | ru:
314 | '✌️ Пожалуйста, выберите минимальный промежуток времени между голосованиями за бан. Текущее ограничение — *$[1]* секунд.',
315 | uz:
316 | '✌️ Iltimos, ban qilish uchun ovoz berish jarayonlari orasidagi eng kam vaqtni belgilang. Hozirgi cheklov — *$[1]* soniya.',
317 | kz:
318 | '✌️ Banǵa daýys berý aralyǵyndagy en az sekýndtar sanyn tańdanyz. Kazіrgі lımıt — *$[1]* sekýnd',
319 | tr:
320 | '✌️ Lütfen yasak istekleri arasındaki minimum süreyi seçin. Mevcut süre: *$[1]* saniye.',
321 | az:
322 | '✌️ Xahiş edirəm, qadağan istəkləri arasındakı minimum vaxtı seçin. Cari limit *$[1]* saniyədir.',
323 | de:
324 | '✌️ Bitte wähle das Zeitlimit zwischen zwei Ban-Anfragen. Das momentane Limit ist *$[1]* Sekunden.',
325 | fr:
326 | '✌️ Merci de définir un temps minimum entre les demandes d\'exclusions. La limite actuelle est de *$[1]* secondes.',
327 | },
328 | timeLimitSuccess: {
329 | uk:
330 | '@banofbot тепер дозволятиме голосування за бан через *$[1]* секунд після останнього бана. Дякую!',
331 | en:
332 | '@banofbot will now allow new ban requests *$[1]* seconds after the last ban. Thanks!',
333 | pt:
334 | '@banofbot agora permitirá novas solicitações *$[1]* segundos após a última. Obrigado!',
335 | ru:
336 | '@banofbot теперь будет разрешать голосования за бан спустя *$[1]* секунд после последнего бана. Спасибо!',
337 | uz:
338 | '@banofbot endi ban qilish uchun ovoz berishni oxirgi bandan *$[1]* soniyadan keyin boshlaydi. Rahmat!',
339 | kz:
340 | '@banofbot sońǵy bannan keıіn *$[1]* sekýnd ótkennen soń banǵa daýys berý bastaıdy. Rahmet!',
341 | tr:
342 | '@banofbot artık yeni yasaklama isteklerine son yasaklamadan *$[1]* saniye sonra izin verecek. Teşekkür ederiz!',
343 | az:
344 | '@banofbot indi son qadağadan *$[1]* saniyə sonra yeni qadağan istəklərinə icazə verəcəkdir. Təşəkkürlər!',
345 | de:
346 | '@banofbot erlaubt nun neue Ban-Anfragen *$[1]* Sekunden nach dem letzten Ban. Danke!',
347 | fr:
348 | '@banofbot autorise désormais de nouvelles exclusions *$[1]* secondes après la dernière exclusion. Merci!',
349 | },
350 | };
351 |
--------------------------------------------------------------------------------
/helpers/lock.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to handle /lock command
3 | *
4 | * @module lock
5 | * @license MIT
6 | */
7 |
8 | /**
9 | * Function to toggle adminLocked of the chat
10 | * @param {Telegram:Bot} bot Bot that should send success message
11 | * @param {Mongoose:Chat} chat Chat that should get adminLocked changed
12 | */
13 | function toggle(bot, chat) {
14 | const strings = require('./strings')()
15 |
16 | chat.admin_locked = !chat.admin_locked
17 | chat
18 | .save()
19 | .then((newChat) => {
20 | const text = newChat.admin_locked ? 'lockOnBanofbot' : 'lockOffBanofbot'
21 | bot.sendMessage(newChat.id, strings.translate(text, chat.language), {
22 | parse_mode: 'Markdown',
23 | })
24 | })
25 | .catch(/** todo: handle error */)
26 | }
27 |
28 | /** Exports */
29 | module.exports = {
30 | toggle,
31 | }
32 |
--------------------------------------------------------------------------------
/helpers/requests.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Requests logic
3 | *
4 | * @module requests
5 | * @license MIT
6 | */
7 |
8 | /** Dependencies */
9 | const db = require('./db')
10 | const _ = require('lodash')
11 | const admins = require('./admins')
12 | const { Lock } = require('semaphore-async-await')
13 | const { isRuChat } = require('./isRuChat')
14 | const { isOver10000 } = require('./goldenBorodutchSubCount')
15 |
16 | const promoAdditions = {
17 | ru: () =>
18 | isOver10000()
19 | ? 'Информация по переезду из 🇺🇦 в 🇨🇦'
20 | : 'Информация по переезду из 🇺🇦 в 🇨🇦',
21 | en: () =>
22 | 'Info on moving from 🇺🇦 to 🇨🇦',
23 | }
24 |
25 | /**
26 | * Starts ban request
27 | * @param {Telegram:Bot} bot Bot that should respond
28 | * @param {Telegram:Message} msg Message to start ban request
29 | */
30 | async function startRequest(bot, msg) {
31 | const chat = await db.findChat(msg.chat)
32 | const starter = await db.findUser(msg.from)
33 | const candidate = await db.findUser(msg.reply_to_message.from)
34 |
35 | // Check if it can create new ban request
36 | const now = new Date().getTime()
37 | const lastBan = chat.last_ban.getTime()
38 | const requiredMilliseconds = chat.seconds_between_bans * 1000
39 | if (now - lastBan < requiredMilliseconds) {
40 | return sendBanLimitError(bot, chat)
41 | }
42 |
43 | const isBotAdmin = await admins.isBotAdmin(bot, chat.id)
44 | if (!isBotAdmin) {
45 | sendAdminError(bot, chat)
46 | return
47 | }
48 |
49 | const isCandidateAdmin = await admins.isAdmin(bot, chat.id, candidate.id)
50 | if (isCandidateAdmin) {
51 | return
52 | }
53 |
54 | if (candidate.username === 'banofbot') {
55 | return
56 | }
57 |
58 | const mockRequest = {
59 | reply_chat_id: msg.reply_to_message.chat.id,
60 | reply_message_id: msg.reply_to_message.message_id,
61 | chat,
62 | candidate,
63 | starter,
64 | voters_ban: [starter],
65 | }
66 | const request = await db.createRequest(mockRequest)
67 |
68 | const strings = require('./strings')()
69 |
70 | const starterName = await request.starter.realNameWithHTML(bot, chat.id)
71 | const candidateName = await request.candidate.realNameWithHTML(bot, chat.id)
72 |
73 | const promoAddition = promoAdditions[isRuChat(chat) ? 'ru' : 'en']()
74 |
75 | const text = `${strings.translate(
76 | 'kickRequest',
77 | request.chat.language,
78 | starterName,
79 | candidateName
80 | )}\n${promoAddition}`
81 | const options = {
82 | parse_mode: 'HTML',
83 | disable_web_page_preview: true,
84 | reply_markup: {
85 | inline_keyboard: kickKeyboard(
86 | 1,
87 | 0,
88 | request._id,
89 | strings,
90 | request.chat.required_voters_count,
91 | request.chat.language
92 | ),
93 | },
94 | }
95 | options.reply_markup = JSON.stringify(options.reply_markup)
96 | const data = await bot.sendMessage(request.chat.id, text, options)
97 |
98 | request.inline_chat_id = data.chat.id
99 | request.inline_message_id = data.message_id
100 | await request.save()
101 | }
102 |
103 | /**
104 | * Function to be called when somebody votes for ban
105 | * @param {Telegram:Bot} bot Bot that should respond
106 | * @param {Teleram:Message} msg Message that triggered inline
107 | */
108 | async function voteQuery(bot, msg) {
109 | const lock = new Lock(1)
110 | await lock.acquire()
111 | try {
112 | const options = msg.data.split('~')
113 | const requestId = options[1]
114 | const against = parseInt(options[2], 10) === 1
115 |
116 | const member = await bot.getChatMember(msg.message.chat.id, msg.from.id)
117 |
118 | if (!['creator', 'administrator', 'member'].includes(member.status)) {
119 | return bot.answerCallbackQuery(msg.id)
120 | }
121 |
122 | let request = await db
123 | .findRequest(requestId)
124 | .populate('chat candidate starter voters_ban voters_noban')
125 | const voter = await db.findUser(msg.from)
126 |
127 | const strings = require('./strings')()
128 |
129 | if (against) {
130 | const alreadyThere = _.find(request.voters_noban, (arrayVoter) =>
131 | arrayVoter._id.equals(voter._id)
132 | )
133 | if (alreadyThere) {
134 | await bot.answerCallbackQuery(msg.id, {
135 | text: strings.translate('voteSave', request.chat.language),
136 | show_alert: true,
137 | })
138 | return
139 | } else {
140 | await bot.answerCallbackQuery(msg.id)
141 | }
142 | request.voters_ban = request.voters_ban.filter(
143 | (arrayVoter) => !arrayVoter._id.equals(voter._id)
144 | )
145 | request.voters_noban.push(voter)
146 | } else {
147 | const alreadyThere = _.find(request.voters_ban, (arrayVoter) =>
148 | arrayVoter._id.equals(voter._id)
149 | )
150 | if (alreadyThere) {
151 | await bot.answerCallbackQuery(msg.id, {
152 | text: strings.translate('voteKick', request.chat.language),
153 | show_alert: true,
154 | })
155 | return
156 | } else {
157 | await bot.answerCallbackQuery(msg.id)
158 | }
159 | request.voters_noban = request.voters_noban.filter(
160 | (arrayVoter) => !arrayVoter._id.equals(voter._id)
161 | )
162 | request.voters_ban.push(voter)
163 | }
164 | request = await request.save()
165 | await updateMessage(bot, request)
166 | } catch (err) {
167 | console.error(err)
168 | // Do nothing
169 | } finally {
170 | lock.release()
171 | }
172 | }
173 |
174 | /**
175 | * Function to update existing request message
176 | * @param {Telegram:Bot} bot Bot that should respond
177 | * @param {Mongoose:Request} request Request whos message to be updated
178 | */
179 | async function updateMessage(bot, request) {
180 | const finished =
181 | request.voters_noban.length >= request.chat.required_voters_count ||
182 | request.voters_ban.length >= request.chat.required_voters_count
183 | if (finished) {
184 | return await finishRequest(bot, request)
185 | }
186 | const strings = require('./strings')()
187 |
188 | const starterName = await request.starter.realNameWithHTML(
189 | bot,
190 | request.chat.id
191 | )
192 | const candidateName = await request.candidate.realNameWithHTML(
193 | bot,
194 | request.chat.id
195 | )
196 |
197 | const promoAddition = promoAdditions[isRuChat(request.chat) ? 'ru' : 'en']()
198 |
199 | const text = `${strings.translate(
200 | 'kickRequest',
201 | request.chat.language,
202 | starterName,
203 | candidateName
204 | )}\n${promoAddition}`
205 | const options = {
206 | parse_mode: 'HTML',
207 | chat_id: request.inline_chat_id,
208 | message_id: request.inline_message_id,
209 | disable_web_page_preview: true,
210 | reply_markup: {
211 | inline_keyboard: kickKeyboard(
212 | request.voters_ban.length,
213 | request.voters_noban.length,
214 | request._id,
215 | strings,
216 | request.chat.required_voters_count,
217 | request.chat.language
218 | ),
219 | },
220 | }
221 | options.reply_markup = JSON.stringify(options.reply_markup)
222 |
223 | return await bot.editMessageText(text, options)
224 | }
225 |
226 | /**
227 | * Finalizes request when there are enough people
228 | * @param {Telegram:Bot} bot Bot that should respond
229 | * @param {Mongoose:Request} request Request to be finalized
230 | */
231 | async function finishRequest(bot, request) {
232 | const strings = require('./strings')()
233 |
234 | const saved =
235 | request.voters_noban.length >= request.chat.required_voters_count
236 | let voters
237 | if (saved) {
238 | const votersArray = []
239 | for (const voter of request.voters_noban) {
240 | const realName = await voter.realNameWithHTML(bot, request.chat.id)
241 | votersArray.push(realName)
242 | }
243 | voters = votersArray.join(', ')
244 | } else {
245 | const votersArray = []
246 | for (const voter of request.voters_ban) {
247 | const realName = await voter.realNameWithHTML(bot, request.chat.id)
248 | votersArray.push(realName)
249 | }
250 | voters = votersArray.join(', ')
251 | }
252 |
253 | const candidateName = await request.candidate.realNameWithHTML(
254 | bot,
255 | request.chat.id
256 | )
257 |
258 | const promoAddition = promoAdditions[isRuChat(request.chat) ? 'ru' : 'en']()
259 |
260 | const text = `${
261 | saved
262 | ? strings.translate(
263 | 'resultSave',
264 | request.chat.language,
265 | candidateName,
266 | voters
267 | )
268 | : strings.translate(
269 | 'resultKick',
270 | request.chat.language,
271 | candidateName,
272 | voters
273 | )
274 | }\n${promoAddition}`
275 |
276 | if (!saved) {
277 | bot.kickChatMember(request.chat.id, request.candidate.id)
278 | if (request.reply_chat_id && request.reply_message_id) {
279 | bot.deleteMessage(request.reply_chat_id, request.reply_message_id)
280 | }
281 | try {
282 | request.chat.last_ban = new Date()
283 | await request.chat.save()
284 | } catch (err) {
285 | // Do nothing
286 | }
287 | }
288 |
289 | const options = {
290 | parse_mode: 'HTML',
291 | chat_id: request.inline_chat_id,
292 | message_id: request.inline_message_id,
293 | disable_web_page_preview: true,
294 | }
295 | return bot.editMessageText(text, options)
296 | }
297 |
298 | /**
299 | * Keyboard to kick user
300 | * @param {Number} forkick Number of users to kick
301 | * @param {Number} against Number of users against kick
302 | * @param {Mongoose:ObjectId} requestId Id of the request
303 | * @param {strings.js} strings Localization object
304 | * @param {Number} voteCount Minimal number of votes to kick or save
305 | * @return {Telegram:InlineKeyboard} Keyboard to kick or not to kick user
306 | */
307 | function kickKeyboard(
308 | forkick,
309 | against,
310 | requestId,
311 | strings,
312 | voteCount,
313 | language
314 | ) {
315 | return [
316 | [
317 | {
318 | text: strings.translate('kickAction', language, forkick, voteCount),
319 | callback_data: `vi~${String(requestId)}~0`,
320 | },
321 | ],
322 | [
323 | {
324 | text: strings.translate('saveAction', language, against, voteCount),
325 | callback_data: `vi~${String(requestId)}~1`,
326 | },
327 | ],
328 | ]
329 | }
330 |
331 | /**
332 | * Function to send error that bot is not an admin to the chat
333 | * @param {Telegram:Bot} bot Bot that should send message
334 | * @param {Mongoose:Chat} chat Chat that should receive the message
335 | */
336 | function sendAdminError(bot, chat) {
337 | const strings = require('./strings')()
338 |
339 | return bot.sendMessage(
340 | chat.id,
341 | chat.language,
342 | strings.translate('adminError')
343 | )
344 | }
345 |
346 | /**
347 | * Function to send error that ban limit is not sufficient
348 | * @param {Telegram:Bot} bot Bot that should send message
349 | * @param {Mongoose:Chat} chat Chat that should receive the message
350 | */
351 | function sendBanLimitError(bot, chat) {
352 | const strings = require('./strings')()
353 |
354 | return bot.sendMessage(
355 | chat.id,
356 | strings.translate('tooSoonError', chat.language)
357 | )
358 | }
359 |
360 | /** Exports */
361 | module.exports = {
362 | startRequest,
363 | voteQuery,
364 | }
365 |
--------------------------------------------------------------------------------
/helpers/strings.js:
--------------------------------------------------------------------------------
1 | const texts = require('./localizations')
2 |
3 | module.exports = () => ({
4 | translate: (key, language, ...theRest) => {
5 | let text = texts[key][language] || texts[key].en
6 | theRest.forEach((sub, i) => {
7 | text = text.replace(new RegExp(`\\$\\[${i + 1}\\]`, 'gum'), sub)
8 | })
9 | return text
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/helpers/time.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to send time limit picker
3 | *
4 | * @module limit
5 | * @license MIT
6 | */
7 |
8 | /** Dependencies */
9 | const db = require('./db')
10 |
11 | /**
12 | * Sends time limit message to specified chat
13 | * @param {Telegam:Bot} bot Bot that should send message
14 | * @param {Mongoose:Chat} chat Chat where to send message
15 | */
16 | function sendTime(bot, chat) {
17 | const strings = require('./strings')()
18 |
19 | const text = strings.translate(
20 | 'timeLimitMessage',
21 | chat.language,
22 | chat.seconds_between_bans
23 | )
24 | const options = {
25 | parse_mode: 'Markdown',
26 | reply_markup: { inline_keyboard: limitKeyboard() },
27 | }
28 | options.reply_markup = JSON.stringify(options.reply_markup)
29 | bot.sendMessage(chat.id, text, options)
30 | }
31 |
32 | /**
33 | * Called when inline button with time limit is touched
34 | * @param {Telegram:Bot} bot Bot that should respond
35 | * @param {Telegram:Message} msg Message of inline button that was touched
36 | */
37 | function setTime(bot, msg) {
38 | const options = msg.data.split('~')
39 | const limit = parseInt(options[1], 10)
40 |
41 | db.findChat(msg.message.chat)
42 | .then((chat) => {
43 | chat.seconds_between_bans = limit
44 | return chat
45 | .save()
46 | .then((dbchat) => updateMessagewithSuccess(bot, msg.message, dbchat))
47 | })
48 | .catch((err) => updateMessagewithError(bot, msg.message, err))
49 | }
50 |
51 | /**
52 | * Updates passed message with error statement
53 | * @param {Telegram:Bot} bot Bot that should update the message
54 | * @param {Telegram:Message} msg Message to be updated
55 | * @param {Error} error Erorr to be displayed
56 | */
57 | function updateMessagewithError(bot, msg, error) {
58 | bot.editMessageText(`❗️ _${error.message}_`, {
59 | chat_id: msg.chat.id,
60 | message_id: msg.message_id,
61 | parse_mode: 'Markdown',
62 | })
63 | }
64 |
65 | /**
66 | * Updates passed message with success statement
67 | * @param {Telegram:Bot} bot Bot that should update the message
68 | * @param {Telegram:Message} msg Message to be updated
69 | * @param {Mongoose:Chat} chat Chat that had limit updated
70 | */
71 | function updateMessagewithSuccess(bot, msg, chat) {
72 | const strings = require('./strings')()
73 |
74 | bot.editMessageText(
75 | strings.translate(
76 | 'timeLimitSuccess',
77 | chat.language,
78 | chat.seconds_between_bans
79 | ),
80 | {
81 | parse_mode: 'Markdown',
82 | chat_id: msg.chat.id,
83 | message_id: msg.message_id,
84 | }
85 | )
86 | }
87 |
88 | /**
89 | * Returns an inline keyboard with all available time limit options
90 | * @return {Telegram:Inline} Inline keyboard with all available limits
91 | */
92 | function limitKeyboard() {
93 | const list = [0, 30, 60, 120, 240, 300, 600, 1200, 5000]
94 | const keyboard = []
95 | let temp = []
96 | list.forEach((number) => {
97 | temp.push({
98 | text: `${number}`,
99 | callback_data: `tlti~${number}`,
100 | })
101 | if (temp.length >= 3) {
102 | keyboard.push(temp)
103 | temp = []
104 | }
105 | })
106 | return keyboard
107 | }
108 |
109 | /** Exports */
110 | module.exports = {
111 | sendTime,
112 | setTime,
113 | }
114 |
--------------------------------------------------------------------------------
/helpers/votekickWord.js:
--------------------------------------------------------------------------------
1 | function check(bot, chat, text) {
2 | const votekickWordString = text.substr('/votekickWord'.length).trim()
3 | if (!votekickWordString.length) {
4 | chat.votekickWord = undefined
5 | chat
6 | .save()
7 | .then((newChat) => {
8 | bot.sendMessage(newChat.id, `👍`)
9 | })
10 | .catch(() => {
11 | // Do nothing
12 | })
13 | return
14 | }
15 |
16 | chat.votekickWord = votekickWordString
17 | chat
18 | .save()
19 | .then((newChat) => {
20 | bot.sendMessage(newChat.id, `👍 ${votekickWordString}`)
21 | })
22 | .catch(() => {
23 | // Do nothing
24 | })
25 | }
26 |
27 | module.exports = {
28 | check,
29 | }
30 |
--------------------------------------------------------------------------------
/localize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * File to build localization files
3 | */
4 |
5 | /** Dependencies */
6 | const strings = require('./helpers/strings');
7 | const fm = require('easy-file-manager');
8 |
9 | const translations = strings().getTranslations();
10 |
11 | fm.upload('/lozalizations/', 'strings.js', new Buffer(JSON.stringify(translations, null, 2)).toString('base64'), () => {});
12 |
13 | const englishTranslations = Object.keys(translations).map(v => `${JSON.stringify(v)}`).join('\n\n');
14 |
15 | fm.upload('/lozalizations/', 'strings.txt', new Buffer(englishTranslations).toString('base64'), () => {});
16 |
--------------------------------------------------------------------------------
/models/chat.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | const mongoose = require('mongoose')
3 |
4 | // Schema
5 | const Schema = mongoose.Schema
6 | const chatSchema = new Schema(
7 | {
8 | id: {
9 | type: String,
10 | required: true,
11 | index: true,
12 | },
13 | type: {
14 | type: String,
15 | required: true,
16 | },
17 | username: {
18 | type: String,
19 | index: true,
20 | },
21 | first_name: String,
22 | last_name: String,
23 | all_members_are_administrators: Boolean,
24 | language: {
25 | type: String,
26 | required: true,
27 | default: 'en',
28 | },
29 | admin_locked: {
30 | type: Boolean,
31 | required: true,
32 | default: false,
33 | },
34 | required_voters_count: {
35 | type: Number,
36 | required: true,
37 | default: 5,
38 | },
39 | last_ban: {
40 | type: Date,
41 | required: true,
42 | default: new Date(0),
43 | index: true, // Add index for time-based queries
44 | },
45 | seconds_between_bans: {
46 | type: Number,
47 | required: true,
48 | default: 30,
49 | },
50 | votekickWord: {
51 | type: String,
52 | required: false,
53 | },
54 | },
55 | { timestamps: true, usePushEach: true }
56 | )
57 |
58 | // Create a unique index on the id field for faster lookups
59 | chatSchema.index({ id: 1 }, { unique: true });
60 |
61 | // Create a compound index for queries that might filter by type and sort by last_ban
62 | chatSchema.index({ type: 1, last_ban: -1 });
63 |
64 | // Create an index for the findChatsWithNewcomers function that looks for chats with newcomers
65 | chatSchema.index({ 'newcomers.0': 1 });
66 |
67 | // Exports
68 | module.exports = mongoose.model('chat', chatSchema)
69 |
--------------------------------------------------------------------------------
/models/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Includes all models in one place
3 | *
4 | * @module models
5 | * @license MIT
6 | */
7 |
8 | module.exports = {
9 | Chat: require('./chat'),
10 | User: require('./user'),
11 | Request: require('./request'),
12 | };
--------------------------------------------------------------------------------
/models/request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module models/request
3 | * @license MIT
4 | */
5 |
6 | /** Dependencies */
7 | const mongoose = require('mongoose')
8 |
9 | /** Schema */
10 | const Schema = mongoose.Schema
11 | const requestSchema = new Schema(
12 | {
13 | inline_chat_id: Number,
14 | inline_message_id: Number,
15 | reply_chat_id: Number,
16 | reply_message_id: Number,
17 | chat: {
18 | type: Schema.ObjectId,
19 | ref: 'chat',
20 | required: true,
21 | index: true, // Add index for faster lookups by chat
22 | },
23 | candidate: {
24 | type: Schema.ObjectId,
25 | ref: 'user',
26 | required: true,
27 | index: true, // Add index for faster lookups by candidate
28 | },
29 | starter: {
30 | type: Schema.ObjectId,
31 | ref: 'user',
32 | required: true,
33 | index: true, // Add index for faster lookups by request starter
34 | },
35 | voters_ban: [
36 | {
37 | type: Schema.ObjectId,
38 | ref: 'user',
39 | required: true,
40 | default: [],
41 | },
42 | ],
43 | voters_noban: [
44 | {
45 | type: Schema.ObjectId,
46 | ref: 'user',
47 | required: true,
48 | default: [],
49 | },
50 | ],
51 | },
52 | { timestamps: true, usePushEach: true }
53 | )
54 |
55 | // Create compound indexes for common query patterns
56 | // Index for queries that filter by chat and sort by createdAt (for finding recent requests in a chat)
57 | requestSchema.index({ chat: 1, createdAt: -1 });
58 |
59 | // Index for finding active requests (those with fewer votes than required)
60 | requestSchema.index({ 'voters_ban.0': 1 });
61 | requestSchema.index({ 'voters_noban.0': 1 });
62 |
63 | // Index for finding requests by inline message details (used when updating votes)
64 | requestSchema.index({ inline_chat_id: 1, inline_message_id: 1 });
65 |
66 | /** Exports */
67 | module.exports = mongoose.model('request', requestSchema)
68 |
--------------------------------------------------------------------------------
/models/user.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module models/user
3 | * @license MIT
4 | */
5 |
6 | /** Dependencies */
7 | const mongoose = require('mongoose')
8 |
9 | /** Schema */
10 | const Schema = mongoose.Schema
11 | const userSchema = new Schema(
12 | {
13 | id: {
14 | type: Number,
15 | required: true,
16 | index: true, // Add index for faster lookups by Telegram user ID
17 | },
18 | first_name: {
19 | type: String,
20 | required: true,
21 | },
22 | last_name: String,
23 | username: {
24 | type: String,
25 | index: true, // Add index for username lookups
26 | },
27 | },
28 | { timestamps: true, usePushEach: true }
29 | )
30 |
31 | // Create a unique index on the id field for faster lookups
32 | userSchema.index({ id: 1 }, { unique: true });
33 |
34 | // Create index for searching users by name
35 | userSchema.index({ first_name: 1, last_name: 1 });
36 |
37 | userSchema.methods.name = function name() {
38 | if (this.username) {
39 | return `@${this.username}`
40 | } else if (this.last_name) {
41 | return `${this.first_name} ${this.last_name}`
42 | }
43 | return this.first_name
44 | }
45 |
46 | userSchema.methods.realNameWithHTML = function(bot, chatId) {
47 | return bot.getChatMember(chatId, this.id).then(res => {
48 | const user = res.user
49 | if (user.username) {
50 | return `@${user.username}`
51 | }
52 | return `${(user.first_name || 'User')
53 | .replace('<', '')
54 | .replace('>', '')}${
55 | user.last_name
56 | ? ` ${user.last_name.replace('<', '').replace('>', '')}`
57 | : ''
58 | }`
59 | })
60 | }
61 |
62 | /** Exports */
63 | module.exports = mongoose.model('user', userSchema)
64 |
--------------------------------------------------------------------------------
/nixpacks.toml:
--------------------------------------------------------------------------------
1 | [phases.install]
2 | cmds = ["yarn install"]
3 |
4 | [start]
5 | cmd = "yarn start"
6 |
7 | [variables]
8 | NODE_ENV = "production"
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "banofbot",
3 | "version": "0.0.1",
4 | "description": "Telegram bot to kick and ban users",
5 | "main": "app.js",
6 | "private": true,
7 | "license": "MIT",
8 | "engines": {
9 | "node": ">=6.4"
10 | },
11 | "scripts": {
12 | "start": "node app.js",
13 | "localize": "node localize.js",
14 | "upload-translations": "node scripts/upload.js",
15 | "download-translations": "node scripts/download.js && yarn prettier --single-quote --no-semi --write ./helpers/localizations.js"
16 | },
17 | "dependencies": {
18 | "bluebird": "^3.5.5",
19 | "botanio": "0.0.6",
20 | "dotenv": "^8.1.0",
21 | "easy-file-manager": "^0.1.3",
22 | "lodash": "^4.17.21",
23 | "mongoose": "^5.7.0",
24 | "node-telegram-bot-api": "^0.30.0",
25 | "nodemon": "^1.19.2",
26 | "semaphore-async-await": "^1.5.1"
27 | },
28 | "devDependencies": {
29 | "axios": "^0.21.2",
30 | "eslint": "^6.3.0",
31 | "eslint-config-airbnb": "^18.0.1",
32 | "eslint-plugin-import": "^2.18.2",
33 | "eslint-plugin-jsx-a11y": "^6.2.3",
34 | "eslint-plugin-promise": "^4.2.1",
35 | "eslint-plugin-react": "^7.14.3",
36 | "flat": "^5.0.0",
37 | "prettier": "^2.0.5"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/scripts/download.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv')
2 | dotenv.config({ path: `${__dirname}/../.env` })
3 | const axios = require('axios')
4 | const unflatten = require('flat').unflatten
5 | const fs = require('fs')
6 |
7 | ;(async function getTranslations() {
8 | console.log('==== Getting localizations')
9 | const translations = (
10 | await axios.get('https://localizer.borodutch.com/localizations')
11 | ).data.filter((l) => {
12 | return l.tags.indexOf('banofbot') > -1
13 | })
14 | console.log('==== Got localizations:')
15 | console.log(JSON.stringify(translations, undefined, 2))
16 | // Get flattened map
17 | const flattenedMap = {} // { key: {en: '', ru: ''}}
18 | translations.forEach((t) => {
19 | const key = t.key
20 | const variants = t.variants.filter((v) => !!v.selected)
21 | flattenedMap[key] = variants.reduce((p, c) => {
22 | p[c.language] = c.text
23 | return p
24 | }, {})
25 | })
26 | console.log('==== Decoded response:')
27 | console.log(flattenedMap)
28 | const unflattened = unflatten(flattenedMap)
29 | console.log('==== Reversed and unflattened map')
30 | console.log(unflattened)
31 | fs.writeFileSync(
32 | `${__dirname}/../helpers/localizations.js`,
33 | `module.exports = ${JSON.stringify(unflattened, undefined, 2)}`
34 | )
35 | console.log('==== Saved object to the file')
36 | })()
37 |
--------------------------------------------------------------------------------
/scripts/upload.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv')
2 | dotenv.config({ path: `${__dirname}/../.env` })
3 | const axios = require('axios')
4 | const flatten = require('flat')
5 | const fs = require('fs')
6 | const localizations = require(`${__dirname}/../helpers/localizations.js`)
7 |
8 | const flattenedLocalizations = {}
9 | Object.keys(localizations).forEach((language) => {
10 | flattenedLocalizations[language] = flatten(localizations[language])
11 | })
12 | ;(async function postLocalizations() {
13 | console.log('==== Posting body:')
14 | console.log(JSON.stringify(flattenedLocalizations, undefined, 2))
15 | try {
16 | await axios.post(`https://localizer.borodutch.com/localizations`, {
17 | // await axios.post(`http://localhost:1337/localizations`, {
18 | localizations: flattenedLocalizations,
19 | password: process.env.PASSWORD,
20 | username: 'borodutch',
21 | tags: ['banofbot'],
22 | })
23 | console.error(`==== Body posted!`)
24 | } catch (err) {
25 | console.error(`==== Error posting: ${err.message}`)
26 | }
27 | })()
28 |
--------------------------------------------------------------------------------