1
0
mirror of https://github.com/civsocit/olgram.git synced 2025-12-19 20:56:19 +00:00

17 Commits

Author SHA1 Message Date
walker
58f551c77d mailing, more mailing 2022-11-07 02:03:00 +04:00
walker
696bc5368b mailing second iteration 2022-11-06 06:09:15 +04:00
walker
340246b937 mailing first iteration 2022-11-06 04:32:32 +04:00
walker
bb1456dda1 fix location forwarding 2022-11-05 00:42:11 +04:00
walker
756f0bd89a minor fixes 2022-11-05 00:40:26 +04:00
walker
6acc2068de fix for prev 2022-10-29 20:14:50 +04:00
walker
d478e9d8e9 version bump 2022-10-29 19:34:13 +04:00
walker
52864ed729 fix #19 2022-10-29 19:32:56 +04:00
mihalin
ac09e42f94 Merge pull request #24 from arcxio/multiple_admins
support multiple comma-separated values in ADMIN_ID
2022-10-29 19:20:27 +04:00
arĉi
afc5389520 support multiple comma-separated values in ADMIN_ID 2022-10-29 18:52:08 +06:00
mihalin
30ab7c84b4 update translations 2022-09-02 05:12:53 +04:00
mihalin
9d8f5a97f7 Revert "debug print"
This reverts commit 16e944707f.
2022-09-02 04:59:18 +04:00
mihalin
16e944707f debug print 2022-09-02 04:48:16 +04:00
mihalin
9723c70deb leave chat button first iteration 2022-09-02 04:28:51 +04:00
mihalin
6e2ee437ba more nice menu 2022-09-02 04:09:28 +04:00
mihalin
6789d23c28 #ID more useful tag in user info 2022-08-03 00:02:32 +03:00
mihalin
0fd8d541f7 add SUPERVISOR_ID to env example 2022-08-02 23:58:41 +03:00
15 changed files with 652 additions and 275 deletions

View File

@@ -49,3 +49,16 @@ Olgram пересылает сообщения так, чтобы сообщен
При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
не успеваете обрабатывать входящие сообщения. не успеваете обрабатывать входящие сообщения.
.. _mailing:
Рассылка
---------------
После включения этой опции ваш бот будет запоминать всех пользователей, которые пишут в ваш бот.
Вы сможете запустить рассылку по этим пользователям.
.. note::
Включение этой опции меняет текст политики конфиденциальности вашего feedback бота (команда /security_policy)
и может отпугнуть некоторых пользователей. Не включайте эту опцию без необходимости.

View File

@@ -15,6 +15,9 @@ TOKEN_ENCRYPTION_KEY=SOME_RANDOM_PASSWORD_HERE
# use your user id or group chat id to restrict access to the bot # use your user id or group chat id to restrict access to the bot
# ADMIN_ID=223453418 # ADMIN_ID=223453418
# use your user id or group chat id to give selected users access to the bot's general statistics (/info command)
# SUPERVISOR_ID=223453419
# example: 11.143.142.140 or my_domain.com (without quotes, without 'https://' prefix!) # example: 11.143.142.140 or my_domain.com (without quotes, without 'https://' prefix!)
WEBHOOK_HOST=YOUR_HOST_HERE WEBHOOK_HOST=YOUR_HOST_HERE
@@ -28,5 +31,5 @@ REDIS_PATH=redis://redis
# Set log level, can be CRITICAL, ERROR, WARNING, INFO, DEBUG. By default it set to WARNING. # Set log level, can be CRITICAL, ERROR, WARNING, INFO, DEBUG. By default it set to WARNING.
LOGLEVEL= LOGLEVEL=
# Uncomment this to switch bot language to Chinese # Uncomment this to switch bot language to English
# O_LANG=zh # O_LANG=en

View File

@@ -5,39 +5,88 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2022-04-11 17:53+0300\n" "POT-Creation-Date: 2022-09-02 05:02+0400\n"
"PO-Revision-Date: 2022-04-11 18:14+0300\n" "PO-Revision-Date: 2022-09-02 05:07+0400\n"
"Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: en_US\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: en_US\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.1\n"
#: olgram/commands/bot_actions.py:21 #: olgram/commands/admin.py:21 olgram/commands/info.py:21
#: olgram/commands/promo.py:23 olgram/commands/promo.py:39
msgid "Недостаточно прав"
msgstr "Not enough permissions"
#: olgram/commands/admin.py:27
msgid "Нужно указать имя бота"
msgstr "You need to specify the bot's name"
#: olgram/commands/admin.py:33
msgid "Такого бота нет в системе"
msgstr "There is no such bot"
#: olgram/commands/admin.py:39 olgram/commands/admin.py:53
msgid "Пропустить"
msgstr "Skip"
#: olgram/commands/admin.py:42
msgid ""
"Введите текст, который будет отправлен владельцу бота {0}. Напишите "
"'Пропустить' чтобы отменить"
msgstr ""
"Enter the text that will be sent to the owner of the bot {0}. Write 'Skip' "
"to cancel"
#: olgram/commands/admin.py:50
msgid "Поддерживается только текст"
msgstr "Only text is supported"
#: olgram/commands/admin.py:55 olgram/commands/admin.py:71
msgid "Отменено"
msgstr "Cancelled"
#: olgram/commands/admin.py:61 olgram/commands/admin.py:69
msgid "Отправить"
msgstr "Send"
#: olgram/commands/admin.py:62
msgid "Отменить"
msgstr "Cancel"
#: olgram/commands/admin.py:81
msgid "Отправлено"
msgstr "Sent"
#: olgram/commands/bot_actions.py:22
msgid "Бот удалён" msgid "Бот удалён"
msgstr "Bot removed" msgstr "Bot removed"
#: olgram/commands/bot_actions.py:37 olgram/commands/bot_actions.py:49 #: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50
msgid "Текст сброшен" msgid "Текст сброшен"
msgstr "" msgstr "Text is reset"
#: olgram/commands/bot_actions.py:63 #: olgram/commands/bot_actions.py:64
msgid "Выбран личный чат" msgid "Выбран личный чат"
msgstr "Personal chat selected" msgstr "Personal chat selected"
#: olgram/commands/bot_actions.py:68 #: olgram/commands/bot_actions.py:77
msgid "Бот вышел из чатов"
msgstr "Bot leaved chats"
#: olgram/commands/bot_actions.py:83
msgid "Нельзя привязать бота к этому чату" msgid "Нельзя привязать бота к этому чату"
msgstr "You can't bind a bot to this chat room" msgstr "You can't bind a bot to this chat room"
#: olgram/commands/bot_actions.py:72 #: olgram/commands/bot_actions.py:87
msgid "Выбран чат {0}" msgid "Выбран чат {0}"
msgstr "Selected chat {0}" msgstr "Selected chat {0}"
#: olgram/commands/bots.py:42 #: olgram/commands/bots.py:46
msgid "" msgid ""
"У вас уже слишком много ботов. Удалите какой-нибудь свой бот из Olgram(/" "У вас уже слишком много ботов. Удалите какой-нибудь свой бот из Olgram(/"
"mybots -> (Выбрать бота) -> Удалить бот)" "mybots -> (Выбрать бота) -> Удалить бот)"
@@ -45,7 +94,7 @@ msgstr ""
"You already have too many bots. Remove any of your bots from Olgram(/mybots -" "You already have too many bots. Remove any of your bots from Olgram(/mybots -"
"> (Select bot) -> Remove bot)" "> (Select bot) -> Remove bot)"
#: olgram/commands/bots.py:46 #: olgram/commands/bots.py:50
msgid "" msgid ""
"\n" "\n"
" Чтобы подключить бот, вам нужно выполнить три действия:\n" " Чтобы подключить бот, вам нужно выполнить три действия:\n"
@@ -72,7 +121,7 @@ msgstr ""
"Chatfuel, Livegram and others).\n" "Chatfuel, Livegram and others).\n"
" " " "
#: olgram/commands/bots.py:66 #: olgram/commands/bots.py:70
msgid "" msgid ""
"\n" "\n"
" Это не токен бота.\n" " Это не токен бота.\n"
@@ -88,7 +137,7 @@ msgstr ""
"abc123_AbcdEFghijKLMnopqrstu12\n" "abc123_AbcdEFghijKLMnopqrstu12\n"
" " " "
#: olgram/commands/bots.py:73 #: olgram/commands/bots.py:77
msgid "" msgid ""
"\n" "\n"
" Не удалось запустить этого бота: неверный токен\n" " Не удалось запустить этого бота: неверный токен\n"
@@ -98,7 +147,7 @@ msgstr ""
" Failed to start this bot: Wrong token\n" " Failed to start this bot: Wrong token\n"
" " " "
#: olgram/commands/bots.py:78 #: olgram/commands/bots.py:82
msgid "" msgid ""
"\n" "\n"
" Не удалось запустить этого бота: непредвиденная ошибка\n" " Не удалось запустить этого бота: непредвиденная ошибка\n"
@@ -108,7 +157,7 @@ msgstr ""
" Failed to start this bot: unexpected error\n" " Failed to start this bot: unexpected error\n"
" " " "
#: olgram/commands/bots.py:83 #: olgram/commands/bots.py:87
msgid "" msgid ""
"\n" "\n"
" Такой бот уже есть в базе данных\n" " Такой бот уже есть в базе данных\n"
@@ -118,35 +167,34 @@ msgstr ""
" Such a bot is already in the database\n" " Such a bot is already in the database\n"
" " " "
#: olgram/commands/bots.py:115 #: olgram/commands/bots.py:122
msgid "Бот добавлен! Список ваших ботов: /mybots" msgid "Бот добавлен! Список ваших ботов: /mybots"
msgstr "Bot added! List of your bots: /mybots" msgstr "Bot added! List of your bots: /mybots"
#: olgram/commands/info.py:21 olgram/commands/promo.py:23 #: olgram/commands/info.py:34
#: olgram/commands/promo.py:39
msgid "Недостаточно прав"
msgstr "Not enough permissions"
#: olgram/commands/info.py:32
msgid "Количество ботов: {0}\n" msgid "Количество ботов: {0}\n"
msgstr "Number of bots: {0}\n" msgstr "Number of bots: {0}\n"
#: olgram/commands/info.py:33 #: olgram/commands/info.py:35
msgid "Количество пользователей (у конструктора): {0}\n" msgid "Количество пользователей (у конструктора): {0}\n"
msgstr "Number of users (at the constructor): {0}\n" msgstr "Number of users (at the constructor): {0}\n"
#: olgram/commands/info.py:34 #: olgram/commands/info.py:36
msgid "Шаблонов ответов: {0}\n" msgid "Шаблонов ответов: {0}\n"
msgstr "Answer templates: {0}\n" msgstr "Answer templates: {0}\n"
#: olgram/commands/info.py:35 #: olgram/commands/info.py:37
msgid "Входящих сообщений у всех ботов: {0}\n" msgid "Входящих сообщений у всех ботов: {0}\n"
msgstr "Incoming messages from all bots: {0}\n" msgstr "Incoming messages from all bots: {0}\n"
#: olgram/commands/info.py:36 #: olgram/commands/info.py:38
msgid "Исходящих сообщений у всех ботов: {0}\n" msgid "Исходящих сообщений у всех ботов: {0}\n"
msgstr "All bots have outgoing messages: {0}\n" msgstr "All bots have outgoing messages: {0}\n"
#: olgram/commands/info.py:39
msgid "Промо-кодов выдано: {0}\n"
msgstr "Promo codes issued: {0}\n"
#: olgram/commands/menu.py:31 #: olgram/commands/menu.py:31
msgid "" msgid ""
"\n" "\n"
@@ -167,15 +215,19 @@ msgstr "Your bots"
#: olgram/commands/menu.py:67 #: olgram/commands/menu.py:67
msgid "Личные сообщения" msgid "Личные сообщения"
msgstr "Личные сообщения" msgstr "Personal messages"
#: olgram/commands/menu.py:72 olgram/commands/menu.py:117 #: olgram/commands/menu.py:72
#: olgram/commands/menu.py:143 olgram/commands/menu.py:174 msgid "❗️ Выйти из всех чатов"
#: olgram/commands/menu.py:235 msgstr "❗️ Leave all chats"
#: olgram/commands/menu.py:77 olgram/commands/menu.py:122
#: olgram/commands/menu.py:148 olgram/commands/menu.py:184
#: olgram/commands/menu.py:247
msgid "<< Назад" msgid "<< Назад"
msgstr "<< Back" msgstr "<< Back"
#: olgram/commands/menu.py:78 #: olgram/commands/menu.py:83
msgid "" msgid ""
"\n" "\n"
" Этот бот не добавлен в чаты, поэтому все сообщения будут приходить " " Этот бот не добавлен в чаты, поэтому все сообщения будут приходить "
@@ -197,7 +249,7 @@ msgstr ""
" again.\n" " again.\n"
" " " "
#: olgram/commands/menu.py:85 #: olgram/commands/menu.py:90
msgid "" msgid ""
"\n" "\n"
" В этом разделе вы можете привязать бота @{0} к чату.\n" " В этом разделе вы можете привязать бота @{0} к чату.\n"
@@ -209,27 +261,27 @@ msgstr ""
" Select the chat room where the bot will forward messages.\n" " Select the chat room where the bot will forward messages.\n"
" " " "
#: olgram/commands/menu.py:97 #: olgram/commands/menu.py:102
msgid "Текст" msgid "Текст"
msgstr "Text" msgstr "Text"
#: olgram/commands/menu.py:102 #: olgram/commands/menu.py:107
msgid "Чат" msgid "Чат"
msgstr "Chat" msgstr "Chat"
#: olgram/commands/menu.py:107 #: olgram/commands/menu.py:112
msgid "Удалить бот" msgid "Удалить бот"
msgstr "Delete bot" msgstr "Delete bot"
#: olgram/commands/menu.py:112 #: olgram/commands/menu.py:117
msgid "Статистика" msgid "Статистика"
msgstr "Statistics" msgstr "Statistics"
#: olgram/commands/menu.py:121 #: olgram/commands/menu.py:126
msgid "Опции" msgid "Опции"
msgstr "Options" msgstr "Options"
#: olgram/commands/menu.py:126 #: olgram/commands/menu.py:131
msgid "" msgid ""
"\n" "\n"
" Управление ботом @{0}.\n" " Управление ботом @{0}.\n"
@@ -247,11 +299,11 @@ msgstr ""
" @civsocit_feedback_bot\n" " @civsocit_feedback_bot\n"
" " " "
#: olgram/commands/menu.py:138 #: olgram/commands/menu.py:143
msgid "Да, удалить бот" msgid "Да, удалить бот"
msgstr "Yes, delete the bot" msgstr "Yes, delete the bot"
#: olgram/commands/menu.py:147 #: olgram/commands/menu.py:152
msgid "" msgid ""
"\n" "\n"
" Вы уверены, что хотите удалить бота @{0}?\n" " Вы уверены, что хотите удалить бота @{0}?\n"
@@ -261,68 +313,87 @@ msgstr ""
" Are you sure you want to delete the bot @{0}?\n" " Are you sure you want to delete the bot @{0}?\n"
" " " "
#: olgram/commands/menu.py:156 #: olgram/commands/menu.py:161
msgid "Потоки сообщений" msgid "Потоки сообщений"
msgstr "Message threads" msgstr "Message threads"
#: olgram/commands/menu.py:161 #: olgram/commands/menu.py:166
msgid "Данные пользователя" msgid "Данные пользователя"
msgstr "User data" msgstr "User data"
#: olgram/commands/menu.py:168 #: olgram/commands/menu.py:171
msgid "Антифлуд"
msgstr "Antiflood"
#: olgram/commands/menu.py:178
msgid "Olgram подпись" msgid "Olgram подпись"
msgstr "Olgram signature" msgstr "Olgram signature"
#: olgram/commands/menu.py:179 olgram/commands/menu.py:180 #: olgram/commands/menu.py:189 olgram/commands/menu.py:190
msgid "включены" msgid "включены"
msgstr "enabled" msgstr "enabled"
#: olgram/commands/menu.py:179 olgram/commands/menu.py:180 #: olgram/commands/menu.py:189 olgram/commands/menu.py:190
msgid "выключены" msgid "выключены"
msgstr "disabled" msgstr "disabled"
#: olgram/commands/menu.py:181 #: olgram/commands/menu.py:191
#, fuzzy
#| msgid "включены"
msgid "включен"
msgstr "enabled"
#: olgram/commands/menu.py:191
#, fuzzy
#| msgid "выключены"
msgid "выключен"
msgstr "disabled"
#: olgram/commands/menu.py:192
msgid "" msgid ""
"\n" "\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#threads" " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"\">Потоки сообщений</a>: <b>{0}</b>\n" "html#threads\">Потоки сообщений</a>: <b>{0}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-info" " <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
"\">Данные пользователя</a>: <b>{1}</b>\n" "info\">Данные пользователя</a>: <b>{1}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"html#antiflood\">Антифлуд</a>: <b>{2}</b>\n"
" " " "
msgstr "" msgstr ""
"\n" "\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#threads" " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"\">Потоки сообщений</a>: <b>{0}</b>\n" "html#threads\">Threads</a>: <b>{0}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-info" " <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
"\">Данные пользователя</a>: <b>{1}</b>\n" "info\">User data</a>: <b>{1}</b>\n"
" " " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"html#antiflood\">Antiflood</a>: <b>{2}</b>"
#: olgram/commands/menu.py:187 #: olgram/commands/menu.py:199
msgid "включена" msgid "включена"
msgstr "enabled" msgstr "enabled"
#: olgram/commands/menu.py:187 #: olgram/commands/menu.py:199
msgid "выключена" msgid "выключена"
msgstr "disabled" msgstr "disabled"
#: olgram/commands/menu.py:188 #: olgram/commands/menu.py:200
msgid "Olgram подпись: <b>{0}</b>" msgid "Olgram подпись: <b>{0}</b>"
msgstr "Olgram signature: <b>{0}</b>" msgstr "Olgram signature: <b>{0}</b>"
#: olgram/commands/menu.py:198 olgram/commands/menu.py:260 #: olgram/commands/menu.py:210 olgram/commands/menu.py:272
#: olgram/commands/menu.py:302 #: olgram/commands/menu.py:314
msgid "<< Завершить редактирование" msgid "<< Завершить редактирование"
msgstr "<< Finish editing" msgstr "<< Finish editing"
#: olgram/commands/menu.py:202 #: olgram/commands/menu.py:214
msgid "Автоответчик" msgid "Автоответчик"
msgstr "Autoresponder" msgstr "Autoresponder"
#: olgram/commands/menu.py:207 olgram/commands/menu.py:274 #: olgram/commands/menu.py:219 olgram/commands/menu.py:286
msgid "Сбросить текст" msgid "Сбросить текст"
msgstr "Reset text" msgstr "Reset text"
#: olgram/commands/menu.py:212 #: olgram/commands/menu.py:224
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете текст, который отправляется после того, как " " Сейчас вы редактируете текст, который отправляется после того, как "
@@ -348,7 +419,7 @@ msgstr ""
" Send a message to change the text.\n" " Send a message to change the text.\n"
" " " "
#: olgram/commands/menu.py:239 #: olgram/commands/menu.py:251
msgid "" msgid ""
"\n" "\n"
" Статистика по боту @{0}\n" " Статистика по боту @{0}\n"
@@ -368,15 +439,15 @@ msgstr ""
" Banned users: <b>{4}</b>\n" " Banned users: <b>{4}</b>\n"
" " " "
#: olgram/commands/menu.py:264 #: olgram/commands/menu.py:276
msgid "Предыдущий текст" msgid "Предыдущий текст"
msgstr "Previous text" msgstr "Previous text"
#: olgram/commands/menu.py:269 #: olgram/commands/menu.py:281
msgid "Шаблоны ответов..." msgid "Шаблоны ответов..."
msgstr "Answer templates..." msgstr "Answer templates..."
#: olgram/commands/menu.py:279 #: olgram/commands/menu.py:291
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в " " Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в "
@@ -402,11 +473,11 @@ msgstr ""
" Send a message to change the text.\n" " Send a message to change the text.\n"
" " " "
#: olgram/commands/menu.py:289 #: olgram/commands/menu.py:301
msgid "(отключено)" msgid "(отключено)"
msgstr "(disabled)" msgstr "(disabled)"
#: olgram/commands/menu.py:306 #: olgram/commands/menu.py:318
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n" " Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n"
@@ -432,27 +503,27 @@ msgstr ""
" To remove a template from the list, send its number in the list (for " " To remove a template from the list, send its number in the list (for "
"example, 4) " "example, 4) "
#: olgram/commands/menu.py:325 #: olgram/commands/menu.py:337
msgid "(нет шаблонов)" msgid "(нет шаблонов)"
msgstr "(no templates)" msgstr "(no templates)"
#: olgram/commands/menu.py:364 #: olgram/commands/menu.py:376
msgid "У вас нет шаблонов, чтобы их удалять" msgid "У вас нет шаблонов, чтобы их удалять"
msgstr "You don't have templates to delete them" msgstr "You don't have templates to delete them"
#: olgram/commands/menu.py:366 #: olgram/commands/menu.py:378
msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}" msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}"
msgstr "To delete a template, enter a number between 0 and {0}" msgstr "To delete a template, enter a number between 0 and {0}"
#: olgram/commands/menu.py:374 #: olgram/commands/menu.py:386
msgid "У вашего бота уже слишком много шаблонов" msgid "У вашего бота уже слишком много шаблонов"
msgstr "Your bot already has too many templates" msgstr "Your bot already has too many templates"
#: olgram/commands/menu.py:378 #: olgram/commands/menu.py:390
msgid "Такой текст уже есть в списке шаблонов" msgid "Такой текст уже есть в списке шаблонов"
msgstr "This text is already in the list of templates" msgstr "This text is already in the list of templates"
#: olgram/commands/menu.py:396 #: olgram/commands/menu.py:408
msgid "У вас нет прав на этого бота" msgid "У вас нет прав на этого бота"
msgstr "You have no permissions to this bot" msgstr "You have no permissions to this bot"
@@ -472,34 +543,35 @@ msgstr "Incorrect token"
msgid "Такого кода не существует" msgid "Такого кода не существует"
msgstr "There is no such code" msgstr "There is no such code"
#: olgram/commands/promo.py:53 #: olgram/commands/promo.py:59
msgid "Промокод отозван" msgid "Промокод отозван"
msgstr "Promotion code withdrawn" msgstr "Promotion code withdrawn"
#: olgram/commands/promo.py:64 #: olgram/commands/promo.py:70
msgid "" msgid ""
"Укажите аргумент: промокод. Например: <pre>/set_promo my-promo-code</pre>" "Укажите аргумент: промокод. Например: <pre>/setpromo my-promo-code</pre>"
msgstr "" msgstr ""
"Specify the argument: promo code. For example: <pre>/set_promo my-promo-" "Specify the argument: promo code. For example: <pre>/setpromo my-promo-"
"code</pre>" "code</pre>"
#: olgram/commands/promo.py:72 olgram/commands/promo.py:76 #: olgram/commands/promo.py:78 olgram/commands/promo.py:82
msgid "Промокод не найден" msgid "Промокод не найден"
msgstr "Promo code not found" msgstr "Promo code not found"
#: olgram/commands/promo.py:79 #: olgram/commands/promo.py:85
msgid "Промокод уже использован" msgid "Промокод уже использован"
msgstr "Promo code has already been used" msgstr "Promo code has already been used"
#: olgram/commands/promo.py:85 #: olgram/commands/promo.py:91
msgid "Промокод активирован! Спасибо 🙌" msgid "Промокод активирован! Спасибо 🙌"
msgstr "Promo code activated! Thank you 🙌" msgstr "Promo code activated! Thank you 🙌"
#: olgram/commands/start.py:25 #: olgram/commands/start.py:23
msgid "" msgid ""
"\n" "\n"
" Olgram Bot — это конструктор ботов обратной связи в Telegram. Подробнее " " Olgram Bot — это конструктор ботов обратной связи в Telegram. Подробнее "
"<a href=\"https://olgram.readthedocs.io\">читайте здесь</a>.\n" "<a href=\"https://olgram.readthedocs.io\">читайте здесь</a>. Следите за "
"обновлениями <a href=\"https://t.me/civsoc_it\">здесь</a>.\n"
"\n" "\n"
" Используйте эти команды, чтобы управлять этим ботом:\n" " Используйте эти команды, чтобы управлять этим ботом:\n"
"\n" "\n"
@@ -510,18 +582,18 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
" Olgram Bot — это конструктор ботов обратной связи в Telegram. Подробнее " " Olgram Bot is a feedback bot contructor for Telegram. More info <a "
"<a href=\"https://olgram.readthedocs.io\">читайте здесь</a>.\n" "href=\"https://olgram.readthedocs.io\">here</a>.\n"
"\n" "\n"
" Используйте эти команды, чтобы управлять этим ботом:\n" " Use that commands to control bot:\n"
"\n" "\n"
" /addbot - добавить бот\n" " /addbot - add bot\n"
" /mybots - управление ботами\n" " /mybots - bot control\n"
"\n" "\n"
" /help - помощь\n" " /help - help\n"
" " " "
#: olgram/commands/start.py:44 #: olgram/commands/start.py:43
msgid "" msgid ""
"\n" "\n"
" Читайте инструкции на нашем сайте https://olgram.readthedocs.io\n" " Читайте инструкции на нашем сайте https://olgram.readthedocs.io\n"
@@ -555,7 +627,7 @@ msgstr "The bot owner has restricted access to this functionality 😞"
msgid "Владелец бота ограничил доступ к этому функционалу😞" msgid "Владелец бота ограничил доступ к этому функционалу😞"
msgstr "The owner of the bot has restricted access to this function😞" msgstr "The owner of the bot has restricted access to this function😞"
#: server/custom.py:47 #: server/custom.py:55
msgid "" msgid ""
"<b>Политика конфиденциальности</b>\n" "<b>Политика конфиденциальности</b>\n"
"\n" "\n"
@@ -574,7 +646,7 @@ msgstr ""
"communicating with the operator; Olgram bots do not do mass mailings.\n" "communicating with the operator; Olgram bots do not do mass mailings.\n"
"\n" "\n"
#: server/custom.py:53 #: server/custom.py:61
msgid "" msgid ""
"При отправке сообщения (кроме команд /start и /security_policy) оператор " "При отправке сообщения (кроме команд /start и /security_policy) оператор "
"<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в " "<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в "
@@ -584,7 +656,7 @@ msgstr ""
"<b>sees</b> your username, @username and user ID by virtue of the settings " "<b>sees</b> your username, @username and user ID by virtue of the settings "
"that the operator specified when creating the bot." "that the operator specified when creating the bot."
#: server/custom.py:57 #: server/custom.py:65
msgid "" msgid ""
"В зависимости от ваших настроек конфиденциальности Telegram, оператор может " "В зависимости от ваших настроек конфиденциальности Telegram, оператор может "
"видеть ваш username, имя пользователя и другую информацию." "видеть ваш username, имя пользователя и другую информацию."
@@ -592,34 +664,42 @@ msgstr ""
"Depending on your Telegram privacy settings, the operator may see your " "Depending on your Telegram privacy settings, the operator may see your "
"username, username and other information." "username, username and other information."
#: server/custom.py:68 #: server/custom.py:76
msgid "Сообщение от пользователя " msgid "Сообщение от пользователя "
msgstr "Message from the user " msgstr "Message from the user "
#: server/custom.py:96 #: server/custom.py:135
msgid "Вы заблокированы в этом боте" msgid "Вы заблокированы в этом боте"
msgstr "You are blocked in this bot" msgstr "You are blocked in this bot"
#: server/custom.py:142 #: server/custom.py:141
msgid "Слишком много сообщений, подождите одну минуту"
msgstr "Too many messages, wait one minute"
#: server/custom.py:148
msgid "Не удаётся связаться с владельцем бота"
msgstr "Cannot contact the owner of the bot"
#: server/custom.py:179
msgid "" msgid ""
"<i>Невозможно переслать сообщение: автор не найден (сообщение слишком " "<i>Невозможно переслать сообщение: автор не найден (сообщение слишком "
"старое?)</i>" "старое?)</i>"
msgstr "" msgstr ""
"<i>Cannot forward this message: author not found (message too old?)</i>" "<i>Cannot forward this message: author not found (message too old?)</i>"
#: server/custom.py:150 #: server/custom.py:187
msgid "Пользователь заблокирован" msgid "Пользователь заблокирован"
msgstr "User is blocked" msgstr "User is blocked"
#: server/custom.py:155 #: server/custom.py:192
msgid "Пользователь не был забанен" msgid "Пользователь не был забанен"
msgstr "The user was not banned" msgstr "The user was not banned"
#: server/custom.py:158 #: server/custom.py:195
msgid "Пользователь разбанен" msgid "Пользователь разбанен"
msgstr "A user has been unlocked" msgstr "A user has been unlocked"
#: server/custom.py:163 #: server/custom.py:200
msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>" msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>"
msgstr "<i>Cannot forward the message (has the author blocked the bot?)</i>" msgstr "<i>Cannot forward the message (has the author blocked the bot?)</i>"
@@ -631,21 +711,11 @@ msgstr "(Re)launch the bot"
msgid "Политика конфиденциальности" msgid "Политика конфиденциальности"
msgstr "Privacy Policy" msgstr "Privacy Policy"
msgid "" #~ msgid ""
"\n" #~ "\n"
"\n" #~ "\n"
"Этот бот создан с помощью @OlgramBot" #~ "Этот бот создан с помощью @OlgramBot"
msgstr "" #~ msgstr ""
"\n" #~ "\n"
"\n" #~ "\n"
"This bot was created using @OlgramBot" #~ "This bot was created using @OlgramBot"
msgid "Не удаётся связаться с владельцем бота"
msgstr "Cannot contact the owner of the bot"
msgid "Слишком много сообщений, подождите одну минуту"
msgstr "Too many messages, wait one minute"
msgid "Антифлуд"
msgstr "Antiflood"

View File

@@ -5,40 +5,89 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2022-04-09 06:24+0300\n" "POT-Creation-Date: 2022-09-02 05:07+0400\n"
"PO-Revision-Date: 2022-04-09 07:11+0300\n" "PO-Revision-Date: 2022-09-02 05:12+0400\n"
"Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: uk_UA\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0\n" "X-Generator: Poedit 3.1\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
"Language: uk_UA\n"
#: olgram/commands/bot_actions.py:21 #: olgram/commands/admin.py:21 olgram/commands/info.py:21
#: olgram/commands/promo.py:23 olgram/commands/promo.py:39
msgid "Недостаточно прав"
msgstr "Недостатньо прав"
#: olgram/commands/admin.py:27
msgid "Нужно указать имя бота"
msgstr "Потрібно вказати ім'я бота"
#: olgram/commands/admin.py:33
msgid "Такого бота нет в системе"
msgstr "Такого бота немає в системі"
#: olgram/commands/admin.py:39 olgram/commands/admin.py:53
msgid "Пропустить"
msgstr "Пропустити"
#: olgram/commands/admin.py:42
msgid ""
"Введите текст, который будет отправлен владельцу бота {0}. Напишите "
"'Пропустить' чтобы отменить"
msgstr ""
"Введіть текст, який буде надіслано власнику бота {0}. Напишіть 'Пропустити', "
"щоб скасувати"
#: olgram/commands/admin.py:50
msgid "Поддерживается только текст"
msgstr "Підтримується лише текст"
#: olgram/commands/admin.py:55 olgram/commands/admin.py:71
msgid "Отменено"
msgstr "Скасовано"
#: olgram/commands/admin.py:61 olgram/commands/admin.py:69
msgid "Отправить"
msgstr "Надіслати"
#: olgram/commands/admin.py:62
msgid "Отменить"
msgstr "Скасувати"
#: olgram/commands/admin.py:81
msgid "Отправлено"
msgstr "Надіслано"
#: olgram/commands/bot_actions.py:22
msgid "Бот удалён" msgid "Бот удалён"
msgstr "Бот видалений" msgstr "Бот видалений"
#: olgram/commands/bot_actions.py:37 olgram/commands/bot_actions.py:49 #: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50
msgid "Текст сброшен" msgid "Текст сброшен"
msgstr "Текст скинутий" msgstr "Текст скинутий"
#: olgram/commands/bot_actions.py:63 #: olgram/commands/bot_actions.py:64
msgid "Выбран личный чат" msgid "Выбран личный чат"
msgstr "Вибраний особистий чат" msgstr "Вибраний особистий чат"
#: olgram/commands/bot_actions.py:68 #: olgram/commands/bot_actions.py:77
msgid "Нельзя привязать бота к этому чату" msgid "Бот вышел из чатов"
msgstr "Нельзя привязать бота к этому чату" msgstr "Бот вийшов із чатів"
#: olgram/commands/bot_actions.py:72 #: olgram/commands/bot_actions.py:83
msgid "Нельзя привязать бота к этому чату"
msgstr "Не можна прив'язати робота до цього чату"
#: olgram/commands/bot_actions.py:87
msgid "Выбран чат {0}" msgid "Выбран чат {0}"
msgstr "Вибраний чат {0}" msgstr "Вибраний чат {0}"
#: olgram/commands/bots.py:42 #: olgram/commands/bots.py:46
msgid "" msgid ""
"У вас уже слишком много ботов. Удалите какой-нибудь свой бот из Olgram(/" "У вас уже слишком много ботов. Удалите какой-нибудь свой бот из Olgram(/"
"mybots -> (Выбрать бота) -> Удалить бот)" "mybots -> (Выбрать бота) -> Удалить бот)"
@@ -46,7 +95,7 @@ msgstr ""
"У вас вже надто багато роботів. Видаліть якийсь свій бот з Olgram(/mybots -> " "У вас вже надто багато роботів. Видаліть якийсь свій бот з Olgram(/mybots -> "
"(Вибрати бота) -> Видалити бот)" "(Вибрати бота) -> Видалити бот)"
#: olgram/commands/bots.py:46 #: olgram/commands/bots.py:50
msgid "" msgid ""
"\n" "\n"
" Чтобы подключить бот, вам нужно выполнить три действия:\n" " Чтобы подключить бот, вам нужно выполнить три действия:\n"
@@ -75,7 +124,7 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/bots.py:66 #: olgram/commands/bots.py:70
msgid "" msgid ""
"\n" "\n"
" Это не токен бота.\n" " Это не токен бота.\n"
@@ -91,7 +140,7 @@ msgstr ""
"abc123_AbcdEFghijKLMnopqrstu12\n" "abc123_AbcdEFghijKLMnopqrstu12\n"
" " " "
#: olgram/commands/bots.py:73 #: olgram/commands/bots.py:77
msgid "" msgid ""
"\n" "\n"
" Не удалось запустить этого бота: неверный токен\n" " Не удалось запустить этого бота: неверный токен\n"
@@ -101,7 +150,7 @@ msgstr ""
" Не вдалося запустити цього бота: неправильний токен\n" " Не вдалося запустити цього бота: неправильний токен\n"
" " " "
#: olgram/commands/bots.py:78 #: olgram/commands/bots.py:82
msgid "" msgid ""
"\n" "\n"
" Не удалось запустить этого бота: непредвиденная ошибка\n" " Не удалось запустить этого бота: непредвиденная ошибка\n"
@@ -111,7 +160,7 @@ msgstr ""
" Не вдалося запустити цього бота: непередбачена помилка\n" " Не вдалося запустити цього бота: непередбачена помилка\n"
" " " "
#: olgram/commands/bots.py:83 #: olgram/commands/bots.py:87
msgid "" msgid ""
"\n" "\n"
" Такой бот уже есть в базе данных\n" " Такой бот уже есть в базе данных\n"
@@ -121,35 +170,34 @@ msgstr ""
" Такий бот вже є у базі даних\n" " Такий бот вже є у базі даних\n"
" " " "
#: olgram/commands/bots.py:115 #: olgram/commands/bots.py:122
msgid "Бот добавлен! Список ваших ботов: /mybots" msgid "Бот добавлен! Список ваших ботов: /mybots"
msgstr "Бот доданий! Список ваших роботів: /mybots" msgstr "Бот доданий! Список ваших роботів: /mybots"
#: olgram/commands/info.py:21 olgram/commands/promo.py:23 #: olgram/commands/info.py:34
#: olgram/commands/promo.py:39
msgid "Недостаточно прав"
msgstr "Недостатньо прав"
#: olgram/commands/info.py:32
msgid "Количество ботов: {0}\n" msgid "Количество ботов: {0}\n"
msgstr "Кількість ботів: {0}\n" msgstr "Кількість ботів: {0}\n"
#: olgram/commands/info.py:33 #: olgram/commands/info.py:35
msgid "Количество пользователей (у конструктора): {0}\n" msgid "Количество пользователей (у конструктора): {0}\n"
msgstr "Кількість користувачів (у конструктора): {0}\n" msgstr "Кількість користувачів (у конструктора): {0}\n"
#: olgram/commands/info.py:34 #: olgram/commands/info.py:36
msgid "Шаблонов ответов: {0}\n" msgid "Шаблонов ответов: {0}\n"
msgstr "Шаблонів відповідей: {0}\n" msgstr "Шаблонів відповідей: {0}\n"
#: olgram/commands/info.py:35 #: olgram/commands/info.py:37
msgid "Входящих сообщений у всех ботов: {0}\n" msgid "Входящих сообщений у всех ботов: {0}\n"
msgstr "Вхідних повідомлень у всіх роботів: {0}\n" msgstr "Вхідних повідомлень у всіх роботів: {0}\n"
#: olgram/commands/info.py:36 #: olgram/commands/info.py:38
msgid "Исходящих сообщений у всех ботов: {0}\n" msgid "Исходящих сообщений у всех ботов: {0}\n"
msgstr "Вихідних повідомлень у всіх роботів: {0}\n" msgstr "Вихідних повідомлень у всіх роботів: {0}\n"
#: olgram/commands/info.py:39
msgid "Промо-кодов выдано: {0}\n"
msgstr "Промо-кодів видано: {0}\n"
#: olgram/commands/menu.py:31 #: olgram/commands/menu.py:31
msgid "" msgid ""
"\n" "\n"
@@ -173,13 +221,17 @@ msgstr "Ваші боти"
msgid "Личные сообщения" msgid "Личные сообщения"
msgstr "Особисті повідомлення" msgstr "Особисті повідомлення"
#: olgram/commands/menu.py:72 olgram/commands/menu.py:117 #: olgram/commands/menu.py:72
#: olgram/commands/menu.py:143 olgram/commands/menu.py:174 msgid "❗️ Выйти из всех чатов"
#: olgram/commands/menu.py:235 msgstr "❗️ Вийти зі всіх чатів"
#: olgram/commands/menu.py:77 olgram/commands/menu.py:122
#: olgram/commands/menu.py:148 olgram/commands/menu.py:184
#: olgram/commands/menu.py:247
msgid "<< Назад" msgid "<< Назад"
msgstr "<< Назад" msgstr "<< Назад"
#: olgram/commands/menu.py:78 #: olgram/commands/menu.py:83
msgid "" msgid ""
"\n" "\n"
" Этот бот не добавлен в чаты, поэтому все сообщения будут приходить " " Этот бот не добавлен в чаты, поэтому все сообщения будут приходить "
@@ -202,7 +254,7 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/menu.py:85 #: olgram/commands/menu.py:90
msgid "" msgid ""
"\n" "\n"
" В этом разделе вы можете привязать бота @{0} к чату.\n" " В этом разделе вы можете привязать бота @{0} к чату.\n"
@@ -215,27 +267,27 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/menu.py:97 #: olgram/commands/menu.py:102
msgid "Текст" msgid "Текст"
msgstr "Текст" msgstr "Текст"
#: olgram/commands/menu.py:102 #: olgram/commands/menu.py:107
msgid "Чат" msgid "Чат"
msgstr "Чат" msgstr "Чат"
#: olgram/commands/menu.py:107 #: olgram/commands/menu.py:112
msgid "Удалить бот" msgid "Удалить бот"
msgstr "Видалити бот" msgstr "Видалити бот"
#: olgram/commands/menu.py:112 #: olgram/commands/menu.py:117
msgid "Статистика" msgid "Статистика"
msgstr "Статистика" msgstr "Статистика"
#: olgram/commands/menu.py:121 #: olgram/commands/menu.py:126
msgid "Опции" msgid "Опции"
msgstr "Опції" msgstr "Опції"
#: olgram/commands/menu.py:126 #: olgram/commands/menu.py:131
msgid "" msgid ""
"\n" "\n"
" Управление ботом @{0}.\n" " Управление ботом @{0}.\n"
@@ -253,11 +305,11 @@ msgstr ""
" @civsocit_feedback_bot\n" " @civsocit_feedback_bot\n"
" " " "
#: olgram/commands/menu.py:138 #: olgram/commands/menu.py:143
msgid "Да, удалить бот" msgid "Да, удалить бот"
msgstr "Так, видалити бот" msgstr "Так, видалити бот"
#: olgram/commands/menu.py:147 #: olgram/commands/menu.py:152
msgid "" msgid ""
"\n" "\n"
" Вы уверены, что хотите удалить бота @{0}?\n" " Вы уверены, что хотите удалить бота @{0}?\n"
@@ -267,68 +319,87 @@ msgstr ""
" Ви впевнені, що хочете видалити бота @{0}?\n" " Ви впевнені, що хочете видалити бота @{0}?\n"
" " " "
#: olgram/commands/menu.py:156 #: olgram/commands/menu.py:161
msgid "Потоки сообщений" msgid "Потоки сообщений"
msgstr "Потоки повідомлень" msgstr "Потоки повідомлень"
#: olgram/commands/menu.py:161 #: olgram/commands/menu.py:166
msgid "Данные пользователя" msgid "Данные пользователя"
msgstr "Дані користувача" msgstr "Дані користувача"
#: olgram/commands/menu.py:168 #: olgram/commands/menu.py:171
msgid "Антифлуд"
msgstr "Антифлуд"
#: olgram/commands/menu.py:178
msgid "Olgram подпись" msgid "Olgram подпись"
msgstr "Olgram підпис" msgstr "Olgram підпис"
#: olgram/commands/menu.py:179 olgram/commands/menu.py:180 #: olgram/commands/menu.py:189 olgram/commands/menu.py:190
msgid "включены" msgid "включены"
msgstr "включені" msgstr "включені"
#: olgram/commands/menu.py:179 olgram/commands/menu.py:180 #: olgram/commands/menu.py:189 olgram/commands/menu.py:190
msgid "выключены" msgid "выключены"
msgstr "вимкнені" msgstr "вимкнені"
#: olgram/commands/menu.py:181 #: olgram/commands/menu.py:191
#, fuzzy
#| msgid "включены"
msgid "включен"
msgstr "включені"
#: olgram/commands/menu.py:191
#, fuzzy
#| msgid "выключены"
msgid "выключен"
msgstr "вимкнені"
#: olgram/commands/menu.py:192
msgid "" msgid ""
"\n" "\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#threads" " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"\">Потоки сообщений</a>: <b>{0}</b>\n" "html#threads\">Потоки сообщений</a>: <b>{0}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-info" " <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
"\">Данные пользователя</a>: <b>{1}</b>\n" "info\">Данные пользователя</a>: <b>{1}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"html#antiflood\">Антифлуд</a>: <b>{2}</b>\n"
" " " "
msgstr "" msgstr ""
"\n" "\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#threads" " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"\">Потоки повідомлень</a>: <b>{0}</b>\n" "html#threads\">Потоки повідомлень</a>: <b>{0}</b>\n"
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-info" " <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
"\">Дані користувача</a>: <b>{1}</b>\n" "info\">Дані користувача</a>: <b>{1}</b>\n"
" " " <a href=\"https://olgram.readthedocs.io/ru/latest/options."
"html#antiflood\">Anti-flood</a>: <b>{2}</b>"
#: olgram/commands/menu.py:187 #: olgram/commands/menu.py:199
msgid "включена" msgid "включена"
msgstr "включена" msgstr "включена"
#: olgram/commands/menu.py:187 #: olgram/commands/menu.py:199
msgid "выключена" msgid "выключена"
msgstr "выключена" msgstr "вимкнена"
#: olgram/commands/menu.py:188 #: olgram/commands/menu.py:200
msgid "Olgram подпись: <b>{0}</b>" msgid "Olgram подпись: <b>{0}</b>"
msgstr "Olgram підпис: <b>{0}</b>" msgstr "Olgram підпис: <b>{0}</b>"
#: olgram/commands/menu.py:198 olgram/commands/menu.py:260 #: olgram/commands/menu.py:210 olgram/commands/menu.py:272
#: olgram/commands/menu.py:302 #: olgram/commands/menu.py:314
msgid "<< Завершить редактирование" msgid "<< Завершить редактирование"
msgstr "<< Завершити редагування" msgstr "<< Завершити редагування"
#: olgram/commands/menu.py:202 #: olgram/commands/menu.py:214
msgid "Автоответчик" msgid "Автоответчик"
msgstr "Автовідповідач" msgstr "Автовідповідач"
#: olgram/commands/menu.py:207 olgram/commands/menu.py:274 #: olgram/commands/menu.py:219 olgram/commands/menu.py:286
msgid "Сбросить текст" msgid "Сбросить текст"
msgstr "Скинути текст" msgstr "Скинути текст"
#: olgram/commands/menu.py:212 #: olgram/commands/menu.py:224
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете текст, который отправляется после того, как " " Сейчас вы редактируете текст, который отправляется после того, как "
@@ -355,7 +426,7 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/menu.py:239 #: olgram/commands/menu.py:251
msgid "" msgid ""
"\n" "\n"
" Статистика по боту @{0}\n" " Статистика по боту @{0}\n"
@@ -375,15 +446,15 @@ msgstr ""
" Забанено користувачів: <b>{4}</b>\n" " Забанено користувачів: <b>{4}</b>\n"
" " " "
#: olgram/commands/menu.py:264 #: olgram/commands/menu.py:276
msgid "Предыдущий текст" msgid "Предыдущий текст"
msgstr "Попередній текст" msgstr "Попередній текст"
#: olgram/commands/menu.py:269 #: olgram/commands/menu.py:281
msgid "Шаблоны ответов..." msgid "Шаблоны ответов..."
msgstr "Шаблони відповідей..." msgstr "Шаблони відповідей..."
#: olgram/commands/menu.py:279 #: olgram/commands/menu.py:291
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в " " Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в "
@@ -409,11 +480,11 @@ msgstr ""
" Надішліть повідомлення, щоб змінити текст.\n" " Надішліть повідомлення, щоб змінити текст.\n"
" " " "
#: olgram/commands/menu.py:289 #: olgram/commands/menu.py:301
msgid "(отключено)" msgid "(отключено)"
msgstr "(відключено)" msgstr "(відключено)"
#: olgram/commands/menu.py:306 #: olgram/commands/menu.py:318
msgid "" msgid ""
"\n" "\n"
" Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n" " Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n"
@@ -439,27 +510,27 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/menu.py:325 #: olgram/commands/menu.py:337
msgid "(нет шаблонов)" msgid "(нет шаблонов)"
msgstr "(Немає шаблонів)" msgstr "(Немає шаблонів)"
#: olgram/commands/menu.py:364 #: olgram/commands/menu.py:376
msgid "У вас нет шаблонов, чтобы их удалять" msgid "У вас нет шаблонов, чтобы их удалять"
msgstr "У вас немає шаблонів, щоб їх видаляти" msgstr "У вас немає шаблонів, щоб їх видаляти"
#: olgram/commands/menu.py:366 #: olgram/commands/menu.py:378
msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}" msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}"
msgstr "Неправильне число. Щоб видалити шаблон, введіть число від 0 до {0}" msgstr "Неправильне число. Щоб видалити шаблон, введіть число від 0 до {0}"
#: olgram/commands/menu.py:374 #: olgram/commands/menu.py:386
msgid "У вашего бота уже слишком много шаблонов" msgid "У вашего бота уже слишком много шаблонов"
msgstr "У вашого бота вже дуже багато шаблонів" msgstr "У вашого бота вже дуже багато шаблонів"
#: olgram/commands/menu.py:378 #: olgram/commands/menu.py:390
msgid "Такой текст уже есть в списке шаблонов" msgid "Такой текст уже есть в списке шаблонов"
msgstr "Такий текст вже є у списку шаблонів" msgstr "Такий текст вже є у списку шаблонів"
#: olgram/commands/menu.py:396 #: olgram/commands/menu.py:408
msgid "У вас нет прав на этого бота" msgid "У вас нет прав на этого бота"
msgstr "У вас немає прав на цього бота" msgstr "У вас немає прав на цього бота"
@@ -479,25 +550,25 @@ msgstr "Неправильний токен"
msgid "Такого кода не существует" msgid "Такого кода не существует"
msgstr "Такого коду не існує" msgstr "Такого коду не існує"
#: olgram/commands/promo.py:53 #: olgram/commands/promo.py:59
msgid "Промокод отозван" msgid "Промокод отозван"
msgstr "Промокод відкликаний" msgstr "Промокод відкликаний"
#: olgram/commands/promo.py:64 #: olgram/commands/promo.py:70
msgid "" msgid ""
"Укажите аргумент: промокод. Например: <pre>/set_promo my-promo-code</pre>" "Укажите аргумент: промокод. Например: <pre>/setpromo my-promo-code</pre>"
msgstr "" msgstr ""
"Зазначте аргумент: промокод. Наприклад: <pre>/set_promo my-promo-code</pre>" "Зазначте аргумент: промокод. Наприклад: <pre>/setpromo my-promo-code</pre>"
#: olgram/commands/promo.py:72 olgram/commands/promo.py:76 #: olgram/commands/promo.py:78 olgram/commands/promo.py:82
msgid "Промокод не найден" msgid "Промокод не найден"
msgstr "Промокод не знайдено" msgstr "Промокод не знайдено"
#: olgram/commands/promo.py:79 #: olgram/commands/promo.py:85
msgid "Промокод уже использован" msgid "Промокод уже использован"
msgstr "Промокод уже використаний" msgstr "Промокод уже використаний"
#: olgram/commands/promo.py:85 #: olgram/commands/promo.py:91
msgid "Промокод активирован! Спасибо 🙌" msgid "Промокод активирован! Спасибо 🙌"
msgstr "Промокод активовано! Дякую 🙌" msgstr "Промокод активовано! Дякую 🙌"
@@ -505,7 +576,8 @@ msgstr "Промокод активовано! Дякую 🙌"
msgid "" msgid ""
"\n" "\n"
" Olgram Bot — это конструктор ботов обратной связи в Telegram. Подробнее " " Olgram Bot — это конструктор ботов обратной связи в Telegram. Подробнее "
"<a href=\"https://olgram.readthedocs.io\">читайте здесь</a>.\n" "<a href=\"https://olgram.readthedocs.io\">читайте здесь</a>. Следите за "
"обновлениями <a href=\"https://t.me/civsoc_it\">здесь</a>.\n"
"\n" "\n"
" Используйте эти команды, чтобы управлять этим ботом:\n" " Используйте эти команды, чтобы управлять этим ботом:\n"
"\n" "\n"
@@ -517,7 +589,8 @@ msgid ""
msgstr "" msgstr ""
"\n" "\n"
" Olgram Bot - це конструктор роботів зворотного зв'язку в Telegram. " " Olgram Bot - це конструктор роботів зворотного зв'язку в Telegram. "
"Докладніше <a href=\"https://olgram.readthedocs.io\">читайте тут</a>.\n" "Докладніше <a href=\"https://olgram.readthedocs.io\">читайте тут</a>. "
"Слідкуйте за оновленнями <a href=\"https://t.me/civsoc_it\">тут</a>.\n"
"\n" "\n"
" Використовуйте ці команди, щоб керувати цим ботом:\n" " Використовуйте ці команди, щоб керувати цим ботом:\n"
"\n" "\n"
@@ -528,7 +601,7 @@ msgstr ""
" \n" " \n"
" " " "
#: olgram/commands/start.py:42 #: olgram/commands/start.py:43
msgid "" msgid ""
"\n" "\n"
" Читайте инструкции на нашем сайте https://olgram.readthedocs.io\n" " Читайте инструкции на нашем сайте https://olgram.readthedocs.io\n"
@@ -564,7 +637,7 @@ msgstr "Власник бота обмежив доступ до цього фу
msgid "Владелец бота ограничил доступ к этому функционалу😞" msgid "Владелец бота ограничил доступ к этому функционалу😞"
msgstr "Власник бота обмежив доступ до цього функціоналу 😞" msgstr "Власник бота обмежив доступ до цього функціоналу 😞"
#: server/custom.py:40 #: server/custom.py:55
msgid "" msgid ""
"<b>Политика конфиденциальности</b>\n" "<b>Политика конфиденциальности</b>\n"
"\n" "\n"
@@ -584,7 +657,7 @@ msgstr ""
"з оператором; боти Olgram не роблять масових розсилок.\n" "з оператором; боти Olgram не роблять масових розсилок.\n"
"\n" "\n"
#: server/custom.py:46 #: server/custom.py:61
msgid "" msgid ""
"При отправке сообщения (кроме команд /start и /security_policy) оператор " "При отправке сообщения (кроме команд /start и /security_policy) оператор "
"<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в " "<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в "
@@ -594,7 +667,7 @@ msgstr ""
"оператор <b>бачить</b> ваше ім'я користувача, @username та ідентифікатор " "оператор <b>бачить</b> ваше ім'я користувача, @username та ідентифікатор "
"користувача через налаштування, які оператор вказав при створенні бота." "користувача через налаштування, які оператор вказав при створенні бота."
#: server/custom.py:50 #: server/custom.py:65
msgid "" msgid ""
"В зависимости от ваших настроек конфиденциальности Telegram, оператор может " "В зависимости от ваших настроек конфиденциальности Telegram, оператор может "
"видеть ваш username, имя пользователя и другую информацию." "видеть ваш username, имя пользователя и другую информацию."
@@ -602,15 +675,23 @@ msgstr ""
"Залежно від ваших налаштувань конфіденційності Telegram оператор може бачити " "Залежно від ваших налаштувань конфіденційності Telegram оператор може бачити "
"ваш username, ім'я користувача та іншу інформацію." "ваш username, ім'я користувача та іншу інформацію."
#: server/custom.py:61 #: server/custom.py:76
msgid "Сообщение от пользователя " msgid "Сообщение от пользователя "
msgstr "Допис від користувача " msgstr "Допис від користувача "
#: server/custom.py:88 #: server/custom.py:135
msgid "Вы заблокированы в этом боте" msgid "Вы заблокированы в этом боте"
msgstr "Ви заблоковані у цьому боті" msgstr "Ви заблоковані у цьому боті"
#: server/custom.py:132 #: server/custom.py:141
msgid "Слишком много сообщений, подождите одну минуту"
msgstr "Забагато повідомлень, зачекайте одну хвилину"
#: server/custom.py:148
msgid "Не удаётся связаться с владельцем бота"
msgstr "Не вдається зв'язатися з власником бота"
#: server/custom.py:179
msgid "" msgid ""
"<i>Невозможно переслать сообщение: автор не найден (сообщение слишком " "<i>Невозможно переслать сообщение: автор не найден (сообщение слишком "
"старое?)</i>" "старое?)</i>"
@@ -618,19 +699,19 @@ msgstr ""
"<i>Неможливо надіслати повідомлення: автора не знайдено (повідомлення " "<i>Неможливо надіслати повідомлення: автора не знайдено (повідомлення "
"занадто старе?)</i>" "занадто старе?)</i>"
#: server/custom.py:140 #: server/custom.py:187
msgid "Пользователь заблокирован" msgid "Пользователь заблокирован"
msgstr "Користувач заблоковано" msgstr "Користувач заблоковано"
#: server/custom.py:145 #: server/custom.py:192
msgid "Пользователь не был забанен" msgid "Пользователь не был забанен"
msgstr "Користувач не був забанений" msgstr "Користувач не був забанений"
#: server/custom.py:148 #: server/custom.py:195
msgid "Пользователь разбанен" msgid "Пользователь разбанен"
msgstr "Користувач розбанений" msgstr "Користувач розбанений"
#: server/custom.py:153 #: server/custom.py:200
msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>" msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>"
msgstr "<i>Неможливо надіслати повідомлення (автор заблокував робота?)</i>" msgstr "<i>Неможливо надіслати повідомлення (автор заблокував робота?)</i>"
@@ -642,21 +723,11 @@ msgstr "(Пере) запустити бота"
msgid "Политика конфиденциальности" msgid "Политика конфиденциальности"
msgstr "Політика конфіденційності" msgstr "Політика конфіденційності"
msgid "" #~ msgid ""
"\n" #~ "\n"
"\n" #~ "\n"
"Этот бот создан с помощью @OlgramBot" #~ "Этот бот создан с помощью @OlgramBot"
msgstr "" #~ msgstr ""
"\n" #~ "\n"
"\n" #~ "\n"
"Цей бот створено за допомогою @OlgramBot" #~ "Цей бот створено за допомогою @OlgramBot"
msgid "Не удаётся связаться с владельцем бота"
msgstr "Не вдається зв'язатися з власником бота"
msgid "Слишком много сообщений, подождите одну минуту"
msgstr "Забагато повідомлень, зачекайте одну хвилину"
msgid "Антифлуд"
msgstr "Антифлуд"

View File

@@ -25,7 +25,7 @@ async def init_database():
async def init_olgram(): async def init_olgram():
from olgram.router import bot, dp from olgram.router import bot, dp
dp.setup_middleware(AccessMiddleware(OlgramSettings.admin_id())) dp.setup_middleware(AccessMiddleware(OlgramSettings.admin_ids()))
from aiogram.types import BotCommand from aiogram.types import BotCommand
await bot.set_my_commands( await bot.set_my_commands(
[ [

View File

@@ -1,9 +1,13 @@
""" """
Здесь работа с конкретным ботом Здесь работа с конкретным ботом
""" """
from asyncio import sleep
from datetime import datetime
from aiogram import types from aiogram import types
from aiogram.utils.exceptions import TelegramAPIError, Unauthorized from aiogram.utils import exceptions
from aiogram import Bot as AioBot
from olgram.models.models import Bot from olgram.models.models import Bot
from olgram.utils.mix import send_stored_message
from server.server import unregister_token from server.server import unregister_token
from locales.locale import _ from locales.locale import _
@@ -14,14 +18,14 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery):
""" """
try: try:
await unregister_token(bot.decrypted_token()) await unregister_token(bot.decrypted_token())
except Unauthorized: except exceptions.Unauthorized:
# Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы # Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы
pass pass
await bot.delete() await bot.delete()
await call.answer(_("Бот удалён")) await call.answer(_("Бот удалён"))
try: try:
await call.message.delete() await call.message.delete()
except TelegramAPIError: except exceptions.TelegramAPIError:
pass pass
@@ -62,6 +66,20 @@ async def select_chat(bot: Bot, call: types.CallbackQuery, chat: str):
await bot.save() await bot.save()
await call.answer(_("Выбран личный чат")) await call.answer(_("Выбран личный чат"))
return return
if chat == "leave":
bot.group_chat = None
await bot.save()
chats = await bot.group_chats.all()
a_bot = AioBot(bot.decrypted_token())
for chat in chats:
try:
await chat.delete()
await a_bot.leave_chat(chat.chat_id)
except exceptions.TelegramAPIError:
pass
await call.answer(_("Бот вышел из чатов"))
await a_bot.session.close()
return
chat_obj = await bot.group_chats.filter(id=chat).first() chat_obj = await bot.group_chats.filter(id=chat).first()
if not chat_obj: if not chat_obj:
@@ -91,3 +109,35 @@ async def olgram_text(bot: Bot, call: types.CallbackQuery):
async def antiflood(bot: Bot, call: types.CallbackQuery): async def antiflood(bot: Bot, call: types.CallbackQuery):
bot.enable_antiflood = not bot.enable_antiflood bot.enable_antiflood = not bot.enable_antiflood
await bot.save(update_fields=["enable_antiflood"]) await bot.save(update_fields=["enable_antiflood"])
async def mailing(bot: Bot, call: types.CallbackQuery):
bot.enable_mailing = not bot.enable_mailing
await bot.save(update_fields=["enable_mailing"])
async def go_mailing(bot: Bot, context) -> int:
users = await bot.mailing_users
a_bot = AioBot(bot.decrypted_token())
count = 0
print(f"start mailing {context}")
for user in users:
bot.last_mailing_at = datetime.now()
await bot.save(update_fields=["last_mailing_at"])
try:
await sleep(0.05)
try:
await send_stored_message(context, a_bot, user.telegram_id)
except exceptions.RetryAfter as err:
await sleep(err.timeout)
await send_stored_message(context, a_bot, user.telegram_id)
count += 1
except (exceptions.ChatNotFound, exceptions.BotBlocked, exceptions.UserDeactivated):
await user.delete()
except exceptions.TelegramAPIError:
pass
return count

View File

@@ -37,4 +37,4 @@ async def info(message: types.Message, state: FSMContext):
_("Входящих сообщений у всех ботов: {0}\n").format(income_messages) + _("Входящих сообщений у всех ботов: {0}\n").format(income_messages) +
_("Исходящих сообщений у всех ботов: {0}\n").format(outgoing_messages) + _("Исходящих сообщений у всех ботов: {0}\n").format(outgoing_messages) +
_("Промо-кодов выдано: {0}\n").format(promo_count) + _("Промо-кодов выдано: {0}\n").format(promo_count) +
_("Рекламную плашку выключили: {0}\n".format(olgram_text_disabled))) _("Рекламную плашку выключили: {0}\n").format(olgram_text_disabled))

View File

@@ -4,14 +4,14 @@ from aiogram import types, Bot as AioBot
from olgram.models.models import Bot, User, DefaultAnswer from olgram.models.models import Bot, User, DefaultAnswer
from aiogram.dispatcher import FSMContext from aiogram.dispatcher import FSMContext
from aiogram.utils.callback_data import CallbackData from aiogram.utils.callback_data import CallbackData
from datetime import datetime, timedelta
from textwrap import dedent from textwrap import dedent
from olgram.utils.mix import edit_or_create, button_text_limit, wrap from olgram.utils.mix import edit_or_create, button_text_limit, wrap, send_stored_message
from olgram.commands import bot_actions from olgram.commands import bot_actions
from locales.locale import _ from locales.locale import _
import typing as ty import typing as ty
menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat') menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat')
empty = "0" empty = "0"
@@ -68,6 +68,11 @@ async def send_chats_menu(bot: Bot, call: types.CallbackQuery):
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="chat", callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="chat",
chat="personal")) chat="personal"))
) )
keyboard.insert(
types.InlineKeyboardButton(text=_("❗️ Выйти из всех чатов"),
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="chat",
chat="leave"))
)
keyboard.insert( keyboard.insert(
types.InlineKeyboardButton(text=_("<< Назад"), types.InlineKeyboardButton(text=_("<< Назад"),
callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty, callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty,
@@ -122,6 +127,12 @@ async def send_bot_menu(bot: Bot, call: types.CallbackQuery):
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings", callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings",
chat=empty)) chat=empty))
) )
if bot.enable_mailing:
keyboard.insert(
types.InlineKeyboardButton(text=_("Рассылка"),
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="go_mailing",
chat=empty))
)
await edit_or_create(call, dedent(_(""" await edit_or_create(call, dedent(_("""
Управление ботом @{0}. Управление ботом @{0}.
@@ -151,7 +162,7 @@ async def send_bot_delete_menu(bot: Bot, call: types.CallbackQuery):
async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery): async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
await call.answer() await call.answer()
keyboard = types.InlineKeyboardMarkup(row_width=1) keyboard = types.InlineKeyboardMarkup(row_width=2)
keyboard.insert( keyboard.insert(
types.InlineKeyboardButton(text=_("Потоки сообщений"), types.InlineKeyboardButton(text=_("Потоки сообщений"),
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="threads", callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="threads",
@@ -167,6 +178,11 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="antiflood", callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="antiflood",
chat=empty)) chat=empty))
) )
keyboard.insert(
types.InlineKeyboardButton(text=_("Рассылка"),
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="mailing",
chat=empty))
)
is_promo = await bot.is_promo() is_promo = await bot.is_promo()
if is_promo: if is_promo:
keyboard.insert( keyboard.insert(
@@ -184,11 +200,13 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
thread_turn = _("включены") if bot.enable_threads else _("выключены") thread_turn = _("включены") if bot.enable_threads else _("выключены")
info_turn = _("включены") if bot.enable_additional_info else _("выключены") info_turn = _("включены") if bot.enable_additional_info else _("выключены")
antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен") antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен")
mailing_turn = _("включена") if bot.enable_mailing else _("выключена")
text = dedent(_(""" text = dedent(_("""
<a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b>
<a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b>
<a href="https://olgram.readthedocs.io/ru/latest/options.html#antiflood">Антифлуд</a>: <b>{2}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#antiflood">Антифлуд</a>: <b>{2}</b>
""")).format(thread_turn, info_turn, antiflood_turn) <a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{3}</b>
""")).format(thread_turn, info_turn, antiflood_turn, mailing_turn)
if is_promo: if is_promo:
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена") olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
@@ -233,6 +251,30 @@ async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] =
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML") await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
async def send_bot_mailing_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
chat_id: ty.Optional[int] = None):
if call:
await call.answer()
keyboard = types.InlineKeyboardMarkup(row_width=1)
keyboard.insert(
types.InlineKeyboardButton(text=_("<< Отменить рассылку"),
callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty, chat=empty))
)
text = dedent(_("""
Напишите сообщение, которое нужно разослать всем подписчикам вашего бота @{0}.
У сообщения будет до {1} получателей.
Учтите, что
1. Рассылается только одно сообщение за раз (в т.ч. только одна картинка)
2. Когда рассылка запущена, её нельзя отменить
"""))
text = text.format(bot.name, len(await bot.mailing_users))
if call:
await edit_or_create(call, text, keyboard, parse_mode="HTML")
else:
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
chat_id: ty.Optional[int] = None): chat_id: ty.Optional[int] = None):
if call: if call:
@@ -347,6 +389,49 @@ async def start_text_received(message: types.Message, state: FSMContext):
await send_bot_text_menu(bot, chat_id=message.chat.id) await send_bot_text_menu(bot, chat_id=message.chat.id)
@dp.message_handler(state="wait_mailing_text",
content_types=[types.ContentType.TEXT,
types.ContentType.LOCATION,
types.ContentType.DOCUMENT,
types.ContentType.PHOTO,
types.ContentType.AUDIO,
types.ContentType.VIDEO]) # TODO: not command
async def mailing_text_received(message: types.Message, state: FSMContext):
async with state.proxy() as proxy:
bot_id = proxy["bot_id"]
proxy["mailing_content_type"] = message.content_type
if message.content_type == types.ContentType.TEXT:
proxy["mailing_text"] = message.html_text
elif message.content_type == types.ContentType.LOCATION:
proxy["mailing_location"] = message.location
elif message.content_type == types.ContentType.PHOTO:
proxy["mailing_photo"] = message.photo[0].file_id
proxy["mailing_caption"] = message.caption
elif message.content_type == types.ContentType.DOCUMENT:
proxy["mailing_document"] = message.document.file_id
proxy["mailing_caption"] = message.caption
elif message.content_type == types.ContentType.AUDIO:
proxy["mailing_audio"] = message.audio.file_id
proxy["mailing_caption"] = message.caption
elif message.content_type == types.ContentType.VIDEO:
proxy["mailing_video"] = message.video.file_id
proxy["mailing_video"] = message.caption
_message_id = await send_stored_message(proxy, AioBot.get_current(), message.chat.id)
keyboard = types.InlineKeyboardMarkup(row_width=1)
keyboard.insert(
types.InlineKeyboardButton(text=_("Да, начать рассылку"),
callback_data=menu_callback.new(level=3, bot_id=bot_id, operation="go_go_mailing",
chat=empty))
)
await AioBot.get_current().send_message(message.chat.id, reply_to_message_id=_message_id.message_id,
text="Вы уверены, что хотите разослать это сообщение всем пользователям?",
reply_markup=keyboard)
@dp.message_handler(state="wait_second_text", content_types="text", regexp="^[^/].+") # Not command @dp.message_handler(state="wait_second_text", content_types="text", regexp="^[^/].+") # Not command
async def second_text_received(message: types.Message, state: FSMContext): async def second_text_received(message: types.Message, state: FSMContext):
async with state.proxy() as proxy: async with state.proxy() as proxy:
@@ -418,6 +503,15 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
return await send_bot_statistic_menu(bot, call) return await send_bot_statistic_menu(bot, call)
if operation == "settings": if operation == "settings":
return await send_bot_settings_menu(bot, call) return await send_bot_settings_menu(bot, call)
if operation == "go_mailing":
if bot.last_mailing_at and bot.last_mailing_at >= datetime.now() - timedelta(minutes=5):
return await call.answer(_("Рассылка была совсем недавно, подождите немного"), show_alert=True)
if not await bot.mailing_users:
return await call.answer(_("Нет пользователей для рассылки"))
await state.set_state("wait_mailing_text")
async with state.proxy() as proxy:
proxy["bot_id"] = bot.id
return await send_bot_mailing_menu(bot, call)
if operation == "text": if operation == "text":
await state.set_state("wait_start_text") await state.set_state("wait_start_text")
async with state.proxy() as proxy: async with state.proxy() as proxy:
@@ -435,6 +529,9 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
if operation == "antiflood": if operation == "antiflood":
await bot_actions.antiflood(bot, call) await bot_actions.antiflood(bot, call)
return await send_bot_settings_menu(bot, call) return await send_bot_settings_menu(bot, call)
if operation == "mailing":
await bot_actions.mailing(bot, call)
return await send_bot_settings_menu(bot, call)
if operation == "additional_info": if operation == "additional_info":
await bot_actions.additional_info(bot, call) await bot_actions.additional_info(bot, call)
return await send_bot_settings_menu(bot, call) return await send_bot_settings_menu(bot, call)
@@ -449,6 +546,20 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
async with state.proxy() as proxy: async with state.proxy() as proxy:
proxy["bot_id"] = bot.id proxy["bot_id"] = bot.id
return await send_bot_second_text_menu(bot, call) return await send_bot_second_text_menu(bot, call)
if operation == "go_go_mailing":
if (await state.get_state()) == "wait_mailing_text":
async with state.proxy() as proxy:
mailing_data = dict(proxy)
await state.reset_state()
if bot.last_mailing_at and bot.last_mailing_at >= datetime.now() - timedelta(minutes=5):
return await call.answer(_("Рассылка была совсем недавно, подождите немного"), show_alert=True)
if not await bot.mailing_users:
return await call.answer(_("Нет пользователей для рассылки"))
await call.answer(_("Рассылка запущена"))
count = await bot_actions.go_mailing(bot, mailing_data)
await call.message.answer(_("Рассылка завершена, отправлено {0} сообщений").format(count))
if operation == "reset_second_text": if operation == "reset_second_text":
await bot_actions.reset_bot_second_text(bot, call) await bot_actions.reset_bot_second_text(bot, call)
return await send_bot_second_text_menu(bot, call) return await send_bot_second_text_menu(bot, call)

View File

@@ -60,14 +60,14 @@ async def del_promo(message: types.Message, state: FSMContext):
@dp.message_handler(commands=["setpromo"], state="*") @dp.message_handler(commands=["setpromo"], state="*")
async def set_promo(message: types.Message, state: FSMContext): async def setpromo(message: types.Message, state: FSMContext):
""" """
Команда /setpromo Команда /setpromo
""" """
arg = message.get_args() arg = message.get_args()
if not arg: if not arg:
return await message.answer(_("Укажите аргумент: промокод. Например: <pre>/set_promo my-promo-code</pre>"), return await message.answer(_("Укажите аргумент: промокод. Например: <pre>/setpromo my-promo-code</pre>"),
parse_mode="HTML") parse_mode="HTML")
arg = arg.strip() arg = arg.strip()

View File

@@ -0,0 +1,14 @@
-- upgrade --
ALTER TABLE "bot" ADD "last_mailing_at" TIMESTAMPTZ;
ALTER TABLE "bot" ADD "enable_mailing" BOOL NOT NULL DEFAULT False;
CREATE TABLE IF NOT EXISTS "mailinguser" (
"id" BIGSERIAL NOT NULL PRIMARY KEY,
"telegram_id" BIGINT NOT NULL,
"bot_id" INT NOT NULL REFERENCES "bot" ("id") ON DELETE CASCADE,
CONSTRAINT "uid_mailinguser_bot_id_906a76" UNIQUE ("bot_id", "telegram_id")
);
CREATE INDEX IF NOT EXISTS "idx_mailinguser_telegra_55de60" ON "mailinguser" ("telegram_id");;
-- downgrade --
ALTER TABLE "bot" DROP COLUMN "last_mailing_at";
ALTER TABLE "bot" DROP COLUMN "enable_mailing";
DROP TABLE IF EXISTS "mailinguser";

View File

@@ -46,6 +46,8 @@ class Bot(Model):
enable_additional_info = fields.BooleanField(default=False) enable_additional_info = fields.BooleanField(default=False)
enable_olgram_text = fields.BooleanField(default=True) enable_olgram_text = fields.BooleanField(default=True)
enable_antiflood = fields.BooleanField(default=False) enable_antiflood = fields.BooleanField(default=False)
enable_mailing = fields.BooleanField(default=False)
last_mailing_at = fields.DatetimeField(null=True, default=None)
def decrypted_token(self): def decrypted_token(self):
cryptor = DatabaseSettings.cryptor() cryptor = DatabaseSettings.cryptor()
@@ -70,6 +72,17 @@ class Bot(Model):
table = 'bot' table = 'bot'
class MailingUser(Model):
id = fields.BigIntField(pk=True)
telegram_id = fields.BigIntField(index=True)
bot = fields.ForeignKeyField("models.Bot", related_name="mailing_users", on_delete=fields.relational.CASCADE)
class Meta:
table = 'mailinguser'
unique_together = (("bot", "telegram_id"), )
class User(Model): class User(Model):
id = fields.IntField(pk=True) id = fields.IntField(pk=True)
telegram_id = fields.BigIntField(index=True, unique=True) telegram_id = fields.BigIntField(index=True, unique=True)

View File

@@ -41,13 +41,13 @@ class OlgramSettings(AbstractSettings):
@classmethod @classmethod
def version(cls): def version(cls):
return "0.4.3" return "0.5.0"
@classmethod @classmethod
@lru_cache @lru_cache
def admin_id(cls): def admin_ids(cls):
_id = cls._get_env("ADMIN_ID", True) _ids = cls._get_env("ADMIN_ID", True)
return int(_id) if _id else None return set(map(int, _ids.split(","))) if _ids else None
@classmethod @classmethod
@lru_cache @lru_cache
@@ -105,7 +105,7 @@ class ServerSettings(AbstractSettings):
return int(timedelta(days=1).total_seconds() * 1000.0) return int(timedelta(days=1).total_seconds() * 1000.0)
logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING"), logging.basicConfig(level=os.environ.get("LOGLEVEL") or "WARNING",
format='%(asctime)s %(levelname)-8s %(message)s') format='%(asctime)s %(levelname)-8s %(message)s')

View File

@@ -1,4 +1,5 @@
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup
from aiogram import types, Bot as AioBot
from aiogram.utils.exceptions import TelegramAPIError from aiogram.utils.exceptions import TelegramAPIError
from typing import Optional from typing import Optional
@@ -30,3 +31,23 @@ def wrap(data: str, max_len: int) -> str:
def button_text_limit(data: str) -> str: def button_text_limit(data: str) -> str:
return wrap(data, 30) return wrap(data, 30)
async def send_stored_message(storage: dict, bot: AioBot, chat_id: int):
content_type = storage["mailing_content_type"]
if content_type == types.ContentType.TEXT:
return await bot.send_message(chat_id, storage["mailing_text"], parse_mode="HTML")
if content_type == types.ContentType.LOCATION:
return await bot.send_location(chat_id, storage["mailing_location"][0], storage["mailing_location"][1])
if content_type == types.ContentType.AUDIO:
return await bot.send_audio(chat_id, audio=storage["mailing_audio"], caption=storage.get("mailing_caption"))
if content_type == types.ContentType.DOCUMENT:
return await bot.send_document(chat_id, document=storage["mailing_document"],
caption=storage.get("mailing_caption"))
if content_type == types.ContentType.PHOTO:
return await bot.send_photo(chat_id, photo=storage["mailing_photo"],
caption=storage.get("mailing_caption"))
if content_type == types.ContentType.VIDEO:
return await bot.send_video(chat_id, video=storage["mailing_video"],
caption=storage.get("mailing_caption"))
raise NotImplementedError("Mailing, unknown content type")

View File

@@ -1,6 +1,7 @@
import aiogram.types as types import aiogram.types as types
from aiogram.dispatcher.handler import CancelHandler, current_handler from aiogram.dispatcher.handler import CancelHandler, current_handler
from aiogram.dispatcher.middlewares import BaseMiddleware from aiogram.dispatcher.middlewares import BaseMiddleware
import typing as ty
from locales.locale import _ from locales.locale import _
@@ -19,8 +20,8 @@ def public():
class AccessMiddleware(BaseMiddleware): class AccessMiddleware(BaseMiddleware):
def __init__(self, access_chat_id: int): def __init__(self, access_chat_ids: ty.Iterable[int]):
self._access_chat_id = access_chat_id self._access_chat_ids = access_chat_ids
super(AccessMiddleware, self).__init__() super(AccessMiddleware, self).__init__()
@classmethod @classmethod
@@ -29,25 +30,25 @@ class AccessMiddleware(BaseMiddleware):
return handler and getattr(handler, "access_public", False) return handler and getattr(handler, "access_public", False)
async def on_process_message(self, message: types.Message, data: dict): async def on_process_message(self, message: types.Message, data: dict):
admin_id = self._access_chat_id admin_ids = self._access_chat_ids
if not admin_id: if not admin_ids:
return # Администратор бота вообще не указан return # Администраторы бота вообще не указаны
if self._is_public_command(): # Эта команда разрешена всем пользователям if self._is_public_command(): # Эта команда разрешена всем пользователям
return return
if message.chat.id != admin_id: if message.chat.id not in admin_ids:
await message.answer(_("Владелец бота ограничил доступ к этому функционалу 😞")) await message.answer(_("Владелец бота ограничил доступ к этому функционалу 😞"))
raise CancelHandler() raise CancelHandler()
async def on_process_callback_query(self, call: types.CallbackQuery, data: dict): async def on_process_callback_query(self, call: types.CallbackQuery, data: dict):
admin_id = self._access_chat_id admin_ids = self._access_chat_ids
if not admin_id: if not admin_ids:
return # Администратор бота вообще не указан return # Администраторы бота вообще не указаны
if self._is_public_command(): # Эта команда разрешена всем пользователям if self._is_public_command(): # Эта команда разрешена всем пользователям
return return
if call.message.chat.id != admin_id: if call.message.chat.id not in admin_ids:
await call.answer(_("Владелец бота ограничил доступ к этому функционалу😞")) await call.answer(_("Владелец бота ограничил доступ к этому функционалу😞"))
raise CancelHandler() raise CancelHandler()

View File

@@ -11,7 +11,7 @@ from tortoise.expressions import F
import logging import logging
import typing as ty import typing as ty
from olgram.settings import ServerSettings from olgram.settings import ServerSettings
from olgram.models.models import Bot, GroupChat, BannedUser from olgram.models.models import Bot, GroupChat, BannedUser, MailingUser
from locales.locale import _, translators from locales.locale import _, translators
from server.inlines import inline_handler from server.inlines import inline_handler
@@ -55,15 +55,20 @@ def _on_security_policy(message: types.Message, bot):
text = _("<b>Политика конфиденциальности</b>\n\n" text = _("<b>Политика конфиденциальности</b>\n\n"
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд " "Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд "
"/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом " "/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом "
"удаляется из кеша. Этот идентификатор используется только для общения с оператором; боты Olgram " "удаляется из кеша. Этот идентификатор используется для общения с оператором.\n\n")
"не делают массовых рассылок.\n\n")
if bot.enable_additional_info: if bot.enable_additional_info:
text += _("При отправке сообщения (кроме команд /start и /security_policy) оператор <b>видит</b> ваши имя " text += _("При отправке сообщения (кроме команд /start и /security_policy) оператор <b>видит</b> ваши имя "
"пользователя, @username и идентификатор пользователя в силу настроек, которые оператор указал при " "пользователя, @username и идентификатор пользователя в силу настроек, которые оператор указал при "
"создании бота.") "создании бота.\n\n")
else: else:
text += _("В зависимости от ваших настроек конфиденциальности Telegram, оператор может видеть ваш username, " text += _("В зависимости от ваших настроек конфиденциальности Telegram, оператор может видеть ваш username, "
"имя пользователя и другую информацию.") "имя пользователя и другую информацию.\n\n")
if bot.enable_mailing:
text += _("В этом боте включена массовая рассылка в силу настроек, которые оператор указал при создании бота. "
"Ваш идентификатор пользователя может быть записан в базу данных на долгое время")
else:
text += _("В этом боте нет массовой рассылки сообщений")
return SendMessage(chat_id=message.chat.id, return SendMessage(chat_id=message.chat.id,
text=text, text=text,
@@ -77,7 +82,7 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot):
user_info += message.from_user.full_name user_info += message.from_user.full_name
if message.from_user.username: if message.from_user.username:
user_info += " | @" + message.from_user.username user_info += " | @" + message.from_user.username
user_info += f" | #{message.from_user.id}" user_info += f" | #ID{message.from_user.id}"
# Добавлять информацию в конец текста # Добавлять информацию в конец текста
if message.content_type == types.ContentType.TEXT and len(message.text) + len(user_info) < 4093: # noqa:E721 if message.content_type == types.ContentType.TEXT and len(message.text) + len(user_info) < 4093: # noqa:E721
@@ -128,6 +133,10 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
_ = _get_translator(message) _ = _get_translator(message)
is_super_group = super_chat_id < 0 is_super_group = super_chat_id < 0
# Записать пользователя для рассылки, если она включена
if bot.enable_mailing:
_, __ = await MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot)
# Проверить, не забанен ли пользователь # Проверить, не забанен ли пользователь
banned = await bot.banned_users.filter(telegram_id=message.chat.id) banned = await bot.banned_users.filter(telegram_id=message.chat.id)
if banned: if banned:
@@ -319,7 +328,8 @@ class CustomRequestHandler(WebhookRequestHandler):
types.ContentType.PHOTO, types.ContentType.PHOTO,
types.ContentType.STICKER, types.ContentType.STICKER,
types.ContentType.VIDEO, types.ContentType.VIDEO,
types.ContentType.VOICE] types.ContentType.VOICE,
types.ContentType.LOCATION]
dp.register_message_handler(message_handler, content_types=supported_messages) dp.register_message_handler(message_handler, content_types=supported_messages)
dp.register_edited_message_handler(edited_message_handler, content_types=supported_messages) dp.register_edited_message_handler(edited_message_handler, content_types=supported_messages)