diff --git a/Dockerfile b/Dockerfile index 6b4d454..b82ca41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.8-buster ENV PYTHONUNBUFFERED=1 \ - POETRY_VERSION=1.1.12 \ + POETRY_VERSION=1.5.1 \ POETRY_VIRTUALENVS_CREATE="false" RUN apt-get update && \ diff --git a/locales/en/LC_MESSAGES/olgram.po b/locales/en/LC_MESSAGES/olgram.po index 4d0ca73..fd62a2a 100644 --- a/locales/en/LC_MESSAGES/olgram.po +++ b/locales/en/LC_MESSAGES/olgram.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2022-09-02 05:02+0400\n" -"PO-Revision-Date: 2022-09-02 05:07+0400\n" +"POT-Creation-Date: 2024-03-02 19:47+0400\n" +"PO-Revision-Date: 2024-03-02 19:48+0400\n" "Last-Translator: \n" "Language-Team: \n" "Language: en_US\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: pygettext.py 1.5\n" -"X-Generator: Poedit 3.1\n" +"X-Generator: Poedit 3.4.2\n" #: olgram/commands/admin.py:21 olgram/commands/info.py:21 #: olgram/commands/promo.py:23 olgram/commands/promo.py:39 @@ -62,27 +62,27 @@ msgstr "Cancel" msgid "Отправлено" msgstr "Sent" -#: olgram/commands/bot_actions.py:22 +#: olgram/commands/bot_actions.py:27 msgid "Бот удалён" msgstr "Bot removed" -#: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50 +#: olgram/commands/bot_actions.py:49 olgram/commands/bot_actions.py:67 msgid "Текст сброшен" msgstr "Text is reset" -#: olgram/commands/bot_actions.py:64 +#: olgram/commands/bot_actions.py:81 msgid "Выбран личный чат" msgstr "Personal chat selected" -#: olgram/commands/bot_actions.py:77 +#: olgram/commands/bot_actions.py:94 msgid "Бот вышел из чатов" msgstr "Bot leaved chats" -#: olgram/commands/bot_actions.py:83 +#: olgram/commands/bot_actions.py:100 msgid "Нельзя привязать бота к этому чату" msgstr "You can't bind a bot to this chat room" -#: olgram/commands/bot_actions.py:87 +#: olgram/commands/bot_actions.py:104 msgid "Выбран чат {0}" msgstr "Selected chat {0}" @@ -195,7 +195,11 @@ msgstr "All bots have outgoing messages: {0}\n" msgid "Промо-кодов выдано: {0}\n" msgstr "Promo codes issued: {0}\n" -#: olgram/commands/menu.py:31 +#: olgram/commands/info.py:40 +msgid "Рекламную плашку выключили: {0}\n" +msgstr "Ad disabled:: {0}\n" + +#: olgram/commands/menu.py:33 msgid "" "\n" " У вас нет добавленных ботов.\n" @@ -209,25 +213,25 @@ msgstr "" " Send the command /addbot to add a bot.\n" " " -#: olgram/commands/menu.py:46 +#: olgram/commands/menu.py:48 msgid "Ваши боты" msgstr "Your bots" -#: olgram/commands/menu.py:67 +#: olgram/commands/menu.py:69 msgid "Личные сообщения" msgstr "Personal messages" -#: olgram/commands/menu.py:72 +#: olgram/commands/menu.py:74 msgid "❗️ Выйти из всех чатов" 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 +#: olgram/commands/menu.py:79 olgram/commands/menu.py:124 +#: olgram/commands/menu.py:156 olgram/commands/menu.py:209 +#: olgram/commands/menu.py:390 msgid "<< Назад" msgstr "<< Back" -#: olgram/commands/menu.py:83 +#: olgram/commands/menu.py:85 msgid "" "\n" " Этот бот не добавлен в чаты, поэтому все сообщения будут приходить " @@ -249,7 +253,7 @@ msgstr "" " again.\n" " " -#: olgram/commands/menu.py:90 +#: olgram/commands/menu.py:92 msgid "" "\n" " В этом разделе вы можете привязать бота @{0} к чату.\n" @@ -261,27 +265,31 @@ msgstr "" " Select the chat room where the bot will forward messages.\n" " " -#: olgram/commands/menu.py:102 +#: olgram/commands/menu.py:104 msgid "Текст" msgstr "Text" -#: olgram/commands/menu.py:107 +#: olgram/commands/menu.py:109 msgid "Чат" msgstr "Chat" -#: olgram/commands/menu.py:112 +#: olgram/commands/menu.py:114 msgid "Удалить бот" msgstr "Delete bot" -#: olgram/commands/menu.py:117 +#: olgram/commands/menu.py:119 msgid "Статистика" msgstr "Statistics" -#: olgram/commands/menu.py:126 +#: olgram/commands/menu.py:128 msgid "Опции" msgstr "Options" -#: olgram/commands/menu.py:131 +#: olgram/commands/menu.py:134 olgram/commands/menu.py:190 +msgid "Рассылка" +msgstr "Mailing" + +#: olgram/commands/menu.py:139 msgid "" "\n" " Управление ботом @{0}.\n" @@ -299,11 +307,11 @@ msgstr "" " @civsocit_feedback_bot\n" " " -#: olgram/commands/menu.py:143 +#: olgram/commands/menu.py:151 msgid "Да, удалить бот" msgstr "Yes, delete the bot" -#: olgram/commands/menu.py:152 +#: olgram/commands/menu.py:160 msgid "" "\n" " Вы уверены, что хотите удалить бота @{0}?\n" @@ -313,43 +321,73 @@ msgstr "" " Are you sure you want to delete the bot @{0}?\n" " " -#: olgram/commands/menu.py:161 +#: olgram/commands/menu.py:169 msgid "Потоки сообщений" msgstr "Message threads" -#: olgram/commands/menu.py:166 +#: olgram/commands/menu.py:174 msgid "Данные пользователя" msgstr "User data" -#: olgram/commands/menu.py:171 +#: olgram/commands/menu.py:179 msgid "Антифлуд" msgstr "Antiflood" -#: olgram/commands/menu.py:178 +#: olgram/commands/menu.py:184 +msgid "Автоответчик всегда" +msgstr "Autorespond always" + +#: olgram/commands/menu.py:195 +msgid "Прерывать поток" +msgstr "Inteeupt thread" + +#: olgram/commands/menu.py:203 msgid "Olgram подпись" msgstr "Olgram signature" -#: olgram/commands/menu.py:189 olgram/commands/menu.py:190 +#: olgram/commands/menu.py:214 olgram/commands/menu.py:215 msgid "включены" msgstr "enabled" -#: olgram/commands/menu.py:189 olgram/commands/menu.py:190 +#: olgram/commands/menu.py:214 olgram/commands/menu.py:215 msgid "выключены" msgstr "disabled" -#: olgram/commands/menu.py:191 +#: olgram/commands/menu.py:216 #, fuzzy #| msgid "включены" msgid "включен" msgstr "enabled" -#: olgram/commands/menu.py:191 +#: olgram/commands/menu.py:216 olgram/commands/menu.py:217 #, fuzzy #| msgid "выключены" msgid "выключен" msgstr "disabled" -#: olgram/commands/menu.py:192 +#: olgram/commands/menu.py:217 +#, fuzzy +#| msgid "включены" +msgid "включён" +msgstr "enabled" + +#: olgram/commands/menu.py:218 +msgid "да" +msgstr "yes" + +#: olgram/commands/menu.py:218 +msgid "нет" +msgstr "no" + +#: olgram/commands/menu.py:219 olgram/commands/menu.py:231 +msgid "включена" +msgstr "enabled" + +#: olgram/commands/menu.py:219 olgram/commands/menu.py:231 +msgid "выключена" +msgstr "disabled" + +#: olgram/commands/menu.py:220 msgid "" "\n" " Данные пользователя: {1}\n" " Антифлуд: {2}\n" +" Автоответчик всегда: {3}\n" +" Прерывать поток: {4}\n" +" Рассылка: {5}\n" " " msgstr "" "\n" @@ -366,60 +410,86 @@ msgstr "" " User data: {1}\n" " Antiflood: {2}" +"html#antiflood\">Anti-flood: {2}\n" +" Autorespond always: {3}\n" +" Interrupt threads: {4}\n" +" Mailing: {5}\n" +" " -#: olgram/commands/menu.py:199 -msgid "включена" -msgstr "enabled" - -#: olgram/commands/menu.py:199 -msgid "выключена" -msgstr "disabled" - -#: olgram/commands/menu.py:200 +#: olgram/commands/menu.py:232 msgid "Olgram подпись: {0}" msgstr "Olgram signature: {0}" -#: olgram/commands/menu.py:210 olgram/commands/menu.py:272 -#: olgram/commands/menu.py:314 +#: olgram/commands/menu.py:259 olgram/commands/menu.py:421 +#: olgram/commands/menu.py:480 msgid "<< Завершить редактирование" msgstr "<< Finish editing" -#: olgram/commands/menu.py:214 +#: olgram/commands/menu.py:263 msgid "Автоответчик" msgstr "Autoresponder" -#: olgram/commands/menu.py:219 olgram/commands/menu.py:286 +#: olgram/commands/menu.py:268 olgram/commands/menu.py:435 msgid "Сбросить текст" msgstr "Reset text" -#: olgram/commands/menu.py:224 +#: olgram/commands/menu.py:273 olgram/commands/menu.py:440 +msgid "[все языки]" +msgstr "[all languages]" + +#: olgram/commands/menu.py:290 msgid "" "\n" " Сейчас вы редактируете текст, который отправляется после того, как " "пользователь отправит вашему боту @{0}\n" " команду /start\n" "\n" -" Текущий текст:\n" -"
\n" -" {1}\n" -"\n" +" Текущий текст{2}:\n" +"
{1}\n" " Отправьте сообщение, чтобы изменить текст.\n" " " msgstr "" + +#: olgram/commands/menu.py:300 olgram/commands/menu.py:467 +msgid " (для языка {0})" +msgstr " (for language {0})" + +#: olgram/commands/menu.py:313 +msgid "<< Отменить рассылку" +msgstr "<< Cancel" + +#: olgram/commands/menu.py:317 +msgid "" "\n" -" You are now editing the text that is sent after the user sends your bot " -"@{0}\n" -" /start command.\n" +" Напишите сообщение, которое нужно разослать всем подписчикам вашего бота " +"@{0}. \n" +" У сообщения будет до {1} получателей. \n" +" Учтите, что\n" +" 1. Рассылается только одно сообщение за раз (в т.ч. только одна " +"картинка)\n" +" 2. Когда рассылка запущена, её нельзя отменить \n" +" " +msgstr "" "\n" -" Current text:\n" -"
\n" -" {1}\n" -"\n" -" Send a message to change the text.\n" +" Please send mailing message to send all @{0} subscribers. \n" +" Message will have up to {1} recipients. \n" +" Take note:\n" +" 1. Only one message per mailing\n" +" 2.Mailing cant be interrupted \n" " " -#: olgram/commands/menu.py:251 +#: olgram/commands/menu.py:367 +msgid "Не удалось загрузить файл (слишком большой размер?)" +msgstr "" + +#: olgram/commands/menu.py:374 +msgid "Да, начать рассылку" +msgstr "Yes, start mailing" + +#: olgram/commands/menu.py:394 msgid "" "\n" " Статистика по боту @{0}\n" @@ -439,45 +509,32 @@ msgstr "" " Banned users: {4}\n" " " -#: olgram/commands/menu.py:276 +#: olgram/commands/menu.py:425 msgid "Предыдущий текст" msgstr "Previous text" -#: olgram/commands/menu.py:281 +#: olgram/commands/menu.py:430 msgid "Шаблоны ответов..." msgstr "Answer templates..." -#: olgram/commands/menu.py:291 +#: olgram/commands/menu.py:457 msgid "" "\n" " Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в " "ответ на все входящие сообщения @{0} автоматически. По умолчанию оно " "отключено.\n" "\n" -" Текущий текст:\n" -"
" -" {1}\n" -"\n" +" Текущий текст{2}:\n" +"
{1}\n" " Отправьте сообщение, чтобы изменить текст.\n" " " msgstr "" -"\n" -" You are now editing the autoresponder text. This message is sent in " -"response to all incoming @{0} messages automatically. It is disabled by " -"default.\n" -"\n" -" Current text:\n" -"
\n" -" {1}\n" -"\n" -" Send a message to change the text.\n" -" " -#: olgram/commands/menu.py:301 -msgid "(отключено)" -msgstr "(disabled)" +#: olgram/commands/menu.py:466 +msgid "отключено" +msgstr "disabled" -#: olgram/commands/menu.py:318 +#: olgram/commands/menu.py:484 msgid "" "\n" " Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n" @@ -503,30 +560,50 @@ msgstr "" " To remove a template from the list, send its number in the list (for " "example, 4) " -#: olgram/commands/menu.py:337 +#: olgram/commands/menu.py:503 msgid "(нет шаблонов)" msgstr "(no templates)" -#: olgram/commands/menu.py:376 +#: olgram/commands/menu.py:565 msgid "У вас нет шаблонов, чтобы их удалять" msgstr "You don't have templates to delete them" -#: olgram/commands/menu.py:378 +#: olgram/commands/menu.py:567 msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}" msgstr "To delete a template, enter a number between 0 and {0}" -#: olgram/commands/menu.py:386 +#: olgram/commands/menu.py:575 msgid "У вашего бота уже слишком много шаблонов" msgstr "Your bot already has too many templates" -#: olgram/commands/menu.py:390 +#: olgram/commands/menu.py:579 msgid "Такой текст уже есть в списке шаблонов" msgstr "This text is already in the list of templates" -#: olgram/commands/menu.py:408 +#: olgram/commands/menu.py:597 msgid "У вас нет прав на этого бота" msgstr "You have no permissions to this bot" +#: olgram/commands/menu.py:617 olgram/commands/menu.py:643 +msgid "Рассылка была совсем недавно, подождите немного" +msgstr "Mailing was recently, wait a bit please" + +#: olgram/commands/menu.py:619 olgram/commands/menu.py:645 +msgid "Нет пользователей для рассылки" +msgstr "No users for mailing" + +#: olgram/commands/menu.py:647 +msgid "Рассылка запущена" +msgstr "Mailing started" + +#: olgram/commands/menu.py:649 +msgid "Рассылка завершена, отправлено {0} сообщений" +msgstr "Mailing completed, {0} messages sent" + +#: olgram/commands/menu.py:651 +msgid "Устарело, создайте новую рассылку" +msgstr "Expired, please create new mailing" + #: olgram/commands/promo.py:27 msgid "" "Новый промокод\n" @@ -551,8 +628,8 @@ msgstr "Promotion code withdrawn" msgid "" "Укажите аргумент: промокод. Например:
/setpromo my-promo-code" msgstr "" -"Specify the argument: promo code. For example:
/setpromo my-promo-" -"code" +"Specify the argument: promo code. For example:
/setpromo my-promo-code" +"pre>" #: olgram/commands/promo.py:78 olgram/commands/promo.py:82 msgid "Промокод не найден" @@ -619,87 +696,102 @@ msgstr "" " Write your question and we will answer you shortly.\n" " " -#: olgram/utils/permissions.py:40 +#: olgram/utils/permissions.py:41 msgid "Владелец бота ограничил доступ к этому функционалу 😞" msgstr "The bot owner has restricted access to this functionality 😞" -#: olgram/utils/permissions.py:52 +#: olgram/utils/permissions.py:53 msgid "Владелец бота ограничил доступ к этому функционалу😞" msgstr "The owner of the bot has restricted access to this function😞" -#: server/custom.py:55 +#: server/custom.py:57 msgid "" "Политика конфиденциальности\n" "\n" "Этот бот не хранит ваши сообщения, имя пользователя и @username. При " "отправке сообщения (кроме команд /start и /security_policy) ваш " "идентификатор пользователя записывается в кеш на некоторое время и потом " -"удаляется из кеша. Этот идентификатор используется только для общения с " -"оператором; боты Olgram не делают массовых рассылок.\n" +"удаляется из кеша. Этот идентификатор используется для общения с " +"оператором.\n" "\n" msgstr "" "Privacy Policy.\n" "\n" "This bot does not store your messages, username and @username. When you send " "a message (except for /start and /security_policy), your username is cached " -"for a while and then deleted from the cache. This ID is only used for " -"communicating with the operator; Olgram bots do not do mass mailings.\n" +"for a while and then deleted from the cache. This ID is used for " +"communicating with the operator\n" "\n" -#: server/custom.py:61 +#: server/custom.py:62 msgid "" "При отправке сообщения (кроме команд /start и /security_policy) оператор " "видит ваши имя пользователя, @username и идентификатор пользователя в " -"силу настроек, которые оператор указал при создании бота." +"силу настроек, которые оператор указал при создании бота.\n" +"\n" msgstr "" "When sending a message (except /start and /security_policy), the operator " "sees 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.\n" +"\n" -#: server/custom.py:65 +#: server/custom.py:66 msgid "" "В зависимости от ваших настроек конфиденциальности Telegram, оператор может " -"видеть ваш username, имя пользователя и другую информацию." +"видеть ваш username, имя пользователя и другую информацию.\n" +"\n" msgstr "" "Depending on your Telegram privacy settings, the operator may see your " -"username, username and other information." +"username, username and other information.\n" +"\n" -#: server/custom.py:76 +#: server/custom.py:70 +msgid "" +"В этом боте включена массовая рассылка в силу настроек, которые оператор " +"указал при создании бота. Ваш идентификатор пользователя может быть записан " +"в базу данных на долгое время" +msgstr "Mailing enabled for this bot" + +#: server/custom.py:73 +msgid "В этом боте нет массовой рассылки сообщений" +msgstr "Mailing disabled for this bot" + +#: server/custom.py:83 msgid "Сообщение от пользователя " msgstr "Message from the user " -#: server/custom.py:135 +#: server/custom.py:157 msgid "Вы заблокированы в этом боте" msgstr "You are blocked in this bot" -#: server/custom.py:141 +#: server/custom.py:163 msgid "Слишком много сообщений, подождите одну минуту" msgstr "Too many messages, wait one minute" -#: server/custom.py:148 +#: server/custom.py:170 msgid "Не удаётся связаться с владельцем бота" msgstr "Cannot contact the owner of the bot" -#: server/custom.py:179 +#: server/custom.py:202 msgid "" "Невозможно переслать сообщение: автор не найден (сообщение слишком " "старое?)" msgstr "" "Cannot forward this message: author not found (message too old?)" -#: server/custom.py:187 +#: server/custom.py:210 msgid "Пользователь заблокирован" msgstr "User is blocked" -#: server/custom.py:192 +#: server/custom.py:215 msgid "Пользователь не был забанен" msgstr "The user was not banned" -#: server/custom.py:195 +#: server/custom.py:218 msgid "Пользователь разбанен" msgstr "A user has been unlocked" -#: server/custom.py:200 +#: server/custom.py:223 msgid "Невозможно переслать сообщение (автор заблокировал бота?)" msgstr "Cannot forward the message (has the author blocked the bot?)" @@ -711,11 +803,11 @@ msgstr "(Re)launch the bot" msgid "Политика конфиденциальности" msgstr "Privacy Policy" -msgid "" -"\n" -"\n" -"Этот бот создан с помощью @OlgramBot" -msgstr "" -"\n" -"\n" -"This bot was created using @OlgramBot" +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "Этот бот создан с помощью @OlgramBot" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "This bot was created using @OlgramBot" diff --git a/locales/uk/LC_MESSAGES/olgram.po b/locales/uk/LC_MESSAGES/olgram.po index 303676e..d610f0b 100644 --- a/locales/uk/LC_MESSAGES/olgram.po +++ b/locales/uk/LC_MESSAGES/olgram.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2022-09-02 05:07+0400\n" -"PO-Revision-Date: 2022-09-02 05:12+0400\n" +"POT-Creation-Date: 2024-03-02 19:47+0400\n" +"PO-Revision-Date: 2024-03-02 19:48+0400\n" "Last-Translator: \n" "Language-Team: \n" "Language: uk_UA\n" @@ -16,7 +16,7 @@ msgstr "" "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" -"X-Generator: Poedit 3.1\n" +"X-Generator: Poedit 3.4.2\n" #: olgram/commands/admin.py:21 olgram/commands/info.py:21 #: olgram/commands/promo.py:23 olgram/commands/promo.py:39 @@ -63,27 +63,27 @@ msgstr "Скасувати" msgid "Отправлено" msgstr "Надіслано" -#: olgram/commands/bot_actions.py:22 +#: olgram/commands/bot_actions.py:27 msgid "Бот удалён" msgstr "Бот видалений" -#: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50 +#: olgram/commands/bot_actions.py:49 olgram/commands/bot_actions.py:67 msgid "Текст сброшен" msgstr "Текст скинутий" -#: olgram/commands/bot_actions.py:64 +#: olgram/commands/bot_actions.py:81 msgid "Выбран личный чат" msgstr "Вибраний особистий чат" -#: olgram/commands/bot_actions.py:77 +#: olgram/commands/bot_actions.py:94 msgid "Бот вышел из чатов" msgstr "Бот вийшов із чатів" -#: olgram/commands/bot_actions.py:83 +#: olgram/commands/bot_actions.py:100 msgid "Нельзя привязать бота к этому чату" msgstr "Не можна прив'язати робота до цього чату" -#: olgram/commands/bot_actions.py:87 +#: olgram/commands/bot_actions.py:104 msgid "Выбран чат {0}" msgstr "Вибраний чат {0}" @@ -198,7 +198,11 @@ msgstr "Вихідних повідомлень у всіх роботів: {0}\ msgid "Промо-кодов выдано: {0}\n" msgstr "Промо-кодів видано: {0}\n" -#: olgram/commands/menu.py:31 +#: olgram/commands/info.py:40 +msgid "Рекламную плашку выключили: {0}\n" +msgstr "" + +#: olgram/commands/menu.py:33 msgid "" "\n" " У вас нет добавленных ботов.\n" @@ -213,25 +217,25 @@ msgstr "" " \n" " " -#: olgram/commands/menu.py:46 +#: olgram/commands/menu.py:48 msgid "Ваши боты" msgstr "Ваші боти" -#: olgram/commands/menu.py:67 +#: olgram/commands/menu.py:69 msgid "Личные сообщения" msgstr "Особисті повідомлення" -#: olgram/commands/menu.py:72 +#: olgram/commands/menu.py:74 msgid "❗️ Выйти из всех чатов" 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 +#: olgram/commands/menu.py:79 olgram/commands/menu.py:124 +#: olgram/commands/menu.py:156 olgram/commands/menu.py:209 +#: olgram/commands/menu.py:390 msgid "<< Назад" msgstr "<< Назад" -#: olgram/commands/menu.py:83 +#: olgram/commands/menu.py:85 msgid "" "\n" " Этот бот не добавлен в чаты, поэтому все сообщения будут приходить " @@ -254,7 +258,7 @@ msgstr "" " \n" " " -#: olgram/commands/menu.py:90 +#: olgram/commands/menu.py:92 msgid "" "\n" " В этом разделе вы можете привязать бота @{0} к чату.\n" @@ -267,27 +271,31 @@ msgstr "" " \n" " " -#: olgram/commands/menu.py:102 +#: olgram/commands/menu.py:104 msgid "Текст" msgstr "Текст" -#: olgram/commands/menu.py:107 +#: olgram/commands/menu.py:109 msgid "Чат" msgstr "Чат" -#: olgram/commands/menu.py:112 +#: olgram/commands/menu.py:114 msgid "Удалить бот" msgstr "Видалити бот" -#: olgram/commands/menu.py:117 +#: olgram/commands/menu.py:119 msgid "Статистика" msgstr "Статистика" -#: olgram/commands/menu.py:126 +#: olgram/commands/menu.py:128 msgid "Опции" msgstr "Опції" -#: olgram/commands/menu.py:131 +#: olgram/commands/menu.py:134 olgram/commands/menu.py:190 +msgid "Рассылка" +msgstr "" + +#: olgram/commands/menu.py:139 msgid "" "\n" " Управление ботом @{0}.\n" @@ -305,11 +313,11 @@ msgstr "" " @civsocit_feedback_bot\n" " " -#: olgram/commands/menu.py:143 +#: olgram/commands/menu.py:151 msgid "Да, удалить бот" msgstr "Так, видалити бот" -#: olgram/commands/menu.py:152 +#: olgram/commands/menu.py:160 msgid "" "\n" " Вы уверены, что хотите удалить бота @{0}?\n" @@ -319,43 +327,75 @@ msgstr "" " Ви впевнені, що хочете видалити бота @{0}?\n" " " -#: olgram/commands/menu.py:161 +#: olgram/commands/menu.py:169 msgid "Потоки сообщений" msgstr "Потоки повідомлень" -#: olgram/commands/menu.py:166 +#: olgram/commands/menu.py:174 msgid "Данные пользователя" msgstr "Дані користувача" -#: olgram/commands/menu.py:171 +#: olgram/commands/menu.py:179 msgid "Антифлуд" msgstr "Антифлуд" -#: olgram/commands/menu.py:178 +#: olgram/commands/menu.py:184 +#, fuzzy +#| msgid "Автоответчик" +msgid "Автоответчик всегда" +msgstr "Автовідповідач" + +#: olgram/commands/menu.py:195 +msgid "Прерывать поток" +msgstr "" + +#: olgram/commands/menu.py:203 msgid "Olgram подпись" msgstr "Olgram підпис" -#: olgram/commands/menu.py:189 olgram/commands/menu.py:190 +#: olgram/commands/menu.py:214 olgram/commands/menu.py:215 msgid "включены" msgstr "включені" -#: olgram/commands/menu.py:189 olgram/commands/menu.py:190 +#: olgram/commands/menu.py:214 olgram/commands/menu.py:215 msgid "выключены" msgstr "вимкнені" -#: olgram/commands/menu.py:191 +#: olgram/commands/menu.py:216 #, fuzzy #| msgid "включены" msgid "включен" msgstr "включені" -#: olgram/commands/menu.py:191 +#: olgram/commands/menu.py:216 olgram/commands/menu.py:217 #, fuzzy #| msgid "выключены" msgid "выключен" msgstr "вимкнені" -#: olgram/commands/menu.py:192 +#: olgram/commands/menu.py:217 +#, fuzzy +#| msgid "включены" +msgid "включён" +msgstr "включені" + +#: olgram/commands/menu.py:218 +msgid "да" +msgstr "Ча" + +#: olgram/commands/menu.py:218 +msgid "нет" +msgstr "" + +#: olgram/commands/menu.py:219 olgram/commands/menu.py:231 +msgid "включена" +msgstr "включена" + +#: olgram/commands/menu.py:219 olgram/commands/menu.py:231 +msgid "выключена" +msgstr "вимкнена" + +#: olgram/commands/menu.py:220 msgid "" "\n" " Данные пользователя: {1}\n" " Антифлуд: {2}\n" +" Автоответчик всегда: {3}\n" +" Прерывать поток: {4}\n" +" Рассылка: {5}\n" " " msgstr "" -"\n" -" Потоки повідомлень: {0}\n" -" Дані користувача: {1}\n" -" Anti-flood: {2}" -#: olgram/commands/menu.py:199 -msgid "включена" -msgstr "включена" - -#: olgram/commands/menu.py:199 -msgid "выключена" -msgstr "вимкнена" - -#: olgram/commands/menu.py:200 +#: olgram/commands/menu.py:232 msgid "Olgram подпись: {0}" msgstr "Olgram підпис: {0}" -#: olgram/commands/menu.py:210 olgram/commands/menu.py:272 -#: olgram/commands/menu.py:314 +#: olgram/commands/menu.py:259 olgram/commands/menu.py:421 +#: olgram/commands/menu.py:480 msgid "<< Завершить редактирование" msgstr "<< Завершити редагування" -#: olgram/commands/menu.py:214 +#: olgram/commands/menu.py:263 msgid "Автоответчик" msgstr "Автовідповідач" -#: olgram/commands/menu.py:219 olgram/commands/menu.py:286 +#: olgram/commands/menu.py:268 olgram/commands/menu.py:435 msgid "Сбросить текст" msgstr "Скинути текст" -#: olgram/commands/menu.py:224 +#: olgram/commands/menu.py:273 olgram/commands/menu.py:440 +msgid "[все языки]" +msgstr "" + +#: olgram/commands/menu.py:290 msgid "" "\n" " Сейчас вы редактируете текст, который отправляется после того, как " "пользователь отправит вашему боту @{0}\n" " команду /start\n" "\n" -" Текущий текст:\n" +" Текущий текст{2}:\n" "{1}\n" " Отправьте сообщение, чтобы изменить текст.\n" " " msgstr "" -"\n" -" Зараз ви редагуєте текст, який надсилається після того, як користувач " -"надішле вашому боту @{0}\n" -" команду /start\n" -"\n" -" Поточний текст:\n" -"{1}\n" -" Надішліть повідомлення, щоб змінити текст.\n" -" \n" -" " -#: olgram/commands/menu.py:251 +#: olgram/commands/menu.py:300 olgram/commands/menu.py:467 +msgid " (для языка {0})" +msgstr "" + +#: olgram/commands/menu.py:313 +#, fuzzy +#| msgid "Отменить" +msgid "<< Отменить рассылку" +msgstr "Скасувати" + +#: olgram/commands/menu.py:317 +msgid "" +"\n" +" Напишите сообщение, которое нужно разослать всем подписчикам вашего бота " +"@{0}. \n" +" У сообщения будет до {1} получателей. \n" +" Учтите, что\n" +" 1. Рассылается только одно сообщение за раз (в т.ч. только одна " +"картинка)\n" +" 2. Когда рассылка запущена, её нельзя отменить \n" +" " +msgstr "" + +#: olgram/commands/menu.py:367 +msgid "Не удалось загрузить файл (слишком большой размер?)" +msgstr "" + +#: olgram/commands/menu.py:374 +msgid "Да, начать рассылку" +msgstr "" + +#: olgram/commands/menu.py:394 msgid "" "\n" " Статистика по боту @{0}\n" @@ -442,43 +498,32 @@ msgstr "" " Забанено користувачів: {4}\n" " " -#: olgram/commands/menu.py:276 +#: olgram/commands/menu.py:425 msgid "Предыдущий текст" msgstr "Попередній текст" -#: olgram/commands/menu.py:281 +#: olgram/commands/menu.py:430 msgid "Шаблоны ответов..." msgstr "Шаблони відповідей..." -#: olgram/commands/menu.py:291 +#: olgram/commands/menu.py:457 msgid "" "\n" " Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в " "ответ на все входящие сообщения @{0} автоматически. По умолчанию оно " "отключено.\n" "\n" -" Текущий текст:\n" +" Текущий текст{2}:\n" "{1}\n" " Отправьте сообщение, чтобы изменить текст.\n" " " msgstr "" -"\n" -" Зараз ви редагуєте текст автовідповідача. Це повідомлення надсилається у " -"відповідь на всі вхідні повідомлення @{0} автоматично. За замовчуванням його " -"вимкнено.\n" -"\n" -" Поточний текст:\n" -"\n" -" {1}\n" -"\n" -" Надішліть повідомлення, щоб змінити текст.\n" -" " -#: olgram/commands/menu.py:301 -msgid "(отключено)" -msgstr "(відключено)" +#: olgram/commands/menu.py:466 +msgid "отключено" +msgstr "відключено" -#: olgram/commands/menu.py:318 +#: olgram/commands/menu.py:484 msgid "" "\n" " Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n" @@ -504,30 +549,50 @@ msgstr "" " \n" " " -#: olgram/commands/menu.py:337 +#: olgram/commands/menu.py:503 msgid "(нет шаблонов)" msgstr "(Немає шаблонів)" -#: olgram/commands/menu.py:376 +#: olgram/commands/menu.py:565 msgid "У вас нет шаблонов, чтобы их удалять" msgstr "У вас немає шаблонів, щоб їх видаляти" -#: olgram/commands/menu.py:378 +#: olgram/commands/menu.py:567 msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}" msgstr "Неправильне число. Щоб видалити шаблон, введіть число від 0 до {0}" -#: olgram/commands/menu.py:386 +#: olgram/commands/menu.py:575 msgid "У вашего бота уже слишком много шаблонов" msgstr "У вашого бота вже дуже багато шаблонів" -#: olgram/commands/menu.py:390 +#: olgram/commands/menu.py:579 msgid "Такой текст уже есть в списке шаблонов" msgstr "Такий текст вже є у списку шаблонів" -#: olgram/commands/menu.py:408 +#: olgram/commands/menu.py:597 msgid "У вас нет прав на этого бота" msgstr "У вас немає прав на цього бота" +#: olgram/commands/menu.py:617 olgram/commands/menu.py:643 +msgid "Рассылка была совсем недавно, подождите немного" +msgstr "" + +#: olgram/commands/menu.py:619 olgram/commands/menu.py:645 +msgid "Нет пользователей для рассылки" +msgstr "" + +#: olgram/commands/menu.py:647 +msgid "Рассылка запущена" +msgstr "Розсилка запущена" + +#: olgram/commands/menu.py:649 +msgid "Рассылка завершена, отправлено {0} сообщений" +msgstr "Розсилка завершена, надіслано {0} повідомлень" + +#: olgram/commands/menu.py:651 +msgid "Устарело, создайте новую рассылку" +msgstr "Застаріло, створіть нову розсилку" + #: olgram/commands/promo.py:27 msgid "" "Новый промокод\n" @@ -623,23 +688,23 @@ msgstr "" " \n" " " -#: olgram/utils/permissions.py:40 +#: olgram/utils/permissions.py:41 msgid "Владелец бота ограничил доступ к этому функционалу 😞" msgstr "Власник бота обмежив доступ до цього функціоналу 😞" -#: olgram/utils/permissions.py:52 +#: olgram/utils/permissions.py:53 msgid "Владелец бота ограничил доступ к этому функционалу😞" msgstr "Власник бота обмежив доступ до цього функціоналу 😞" -#: server/custom.py:55 +#: server/custom.py:57 msgid "" "Политика конфиденциальности\n" "\n" "Этот бот не хранит ваши сообщения, имя пользователя и @username. При " "отправке сообщения (кроме команд /start и /security_policy) ваш " "идентификатор пользователя записывается в кеш на некоторое время и потом " -"удаляется из кеша. Этот идентификатор используется только для общения с " -"оператором; боты Olgram не делают массовых рассылок.\n" +"удаляется из кеша. Этот идентификатор используется для общения с " +"оператором.\n" "\n" msgstr "" "Політика конфіденційності\n" @@ -647,45 +712,63 @@ msgstr "" "Цей бот не зберігає ваші повідомлення, ім'я користувача та @username. При " "надсиланні повідомлення (крім команд /start та /security_policy) ваш " "ідентифікатор користувача записується в кеш на деякий час і потім " -"видаляється з кеша. Цей ідентифікатор використовується лише для спілкування " -"з оператором; боти Olgram не роблять масових розсилок.\n" +"видаляється з кеша. Цей ідентифікатор використовується для спілкування з " +"оператором.\n" "\n" -#: server/custom.py:61 +#: server/custom.py:62 msgid "" "При отправке сообщения (кроме команд /start и /security_policy) оператор " "видит ваши имя пользователя, @username и идентификатор пользователя в " -"силу настроек, которые оператор указал при создании бота." +"силу настроек, которые оператор указал при создании бота.\n" +"\n" msgstr "" "При надсиланні повідомлення (крім команд /start та /security_policy) " "оператор бачить ваше ім'я користувача, @username та ідентифікатор " -"користувача через налаштування, які оператор вказав при створенні бота." +"користувача через налаштування, які оператор вказав при створенні бота.\n" +"\n" -#: server/custom.py:65 +#: server/custom.py:66 msgid "" "В зависимости от ваших настроек конфиденциальности Telegram, оператор может " -"видеть ваш username, имя пользователя и другую информацию." +"видеть ваш username, имя пользователя и другую информацию.\n" +"\n" msgstr "" "Залежно від ваших налаштувань конфіденційності Telegram оператор може бачити " -"ваш username, ім'я користувача та іншу інформацію." +"ваш username, ім'я користувача та іншу інформацію.\n" +"\n" -#: server/custom.py:76 +#: server/custom.py:70 +msgid "" +"В этом боте включена массовая рассылка в силу настроек, которые оператор " +"указал при создании бота. Ваш идентификатор пользователя может быть записан " +"в базу данных на долгое время" +msgstr "" +"У цьому роботі включено масове розсилання в силу налаштувань, які оператор " +"вказав при створенні робота. Ваш ідентифікатор користувача може бути " +"записаний до бази даних на довгий час" + +#: server/custom.py:73 +msgid "В этом боте нет массовой рассылки сообщений" +msgstr "У цьому роботі немає масової розсилки повідомлень" + +#: server/custom.py:83 msgid "Сообщение от пользователя " msgstr "Допис від користувача " -#: server/custom.py:135 +#: server/custom.py:157 msgid "Вы заблокированы в этом боте" msgstr "Ви заблоковані у цьому боті" -#: server/custom.py:141 +#: server/custom.py:163 msgid "Слишком много сообщений, подождите одну минуту" msgstr "Забагато повідомлень, зачекайте одну хвилину" -#: server/custom.py:148 +#: server/custom.py:170 msgid "Не удаётся связаться с владельцем бота" msgstr "Не вдається зв'язатися з власником бота" -#: server/custom.py:179 +#: server/custom.py:202 msgid "" "Невозможно переслать сообщение: автор не найден (сообщение слишком " "старое?)" @@ -693,19 +776,19 @@ msgstr "" "Неможливо надіслати повідомлення: автора не знайдено (повідомлення " "занадто старе?)" -#: server/custom.py:187 +#: server/custom.py:210 msgid "Пользователь заблокирован" msgstr "Користувач заблоковано" -#: server/custom.py:192 +#: server/custom.py:215 msgid "Пользователь не был забанен" msgstr "Користувач не був забанений" -#: server/custom.py:195 +#: server/custom.py:218 msgid "Пользователь разбанен" msgstr "Користувач розбанений" -#: server/custom.py:200 +#: server/custom.py:223 msgid "Невозможно переслать сообщение (автор заблокировал бота?)" msgstr "Неможливо надіслати повідомлення (автор заблокував робота?)" @@ -717,11 +800,11 @@ msgstr "(Пере) запустити бота" msgid "Политика конфиденциальности" msgstr "Політика конфіденційності" -msgid "" -"\n" -"\n" -"Этот бот создан с помощью @OlgramBot" -msgstr "" -"\n" -"\n" -"Цей бот створено за допомогою @OlgramBot" +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "Этот бот создан с помощью @OlgramBot" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "Цей бот створено за допомогою @OlgramBot" diff --git a/olgram/commands/bot_actions.py b/olgram/commands/bot_actions.py index a4e043e..1eeee75 100644 --- a/olgram/commands/bot_actions.py +++ b/olgram/commands/bot_actions.py @@ -1,8 +1,13 @@ """ Здесь работа с конкретным ботом """ +import logging + from aiogram import types -from aiogram.utils.exceptions import TelegramAPIError, Unauthorized +from asyncio import sleep +from datetime import datetime +from olgram.utils.mix import send_stored_message +from aiogram.utils import exceptions from aiogram import Bot as AioBot from olgram.models.models import Bot, BotStartMessage, BotSecondMessage from server.server import unregister_token @@ -15,14 +20,14 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery): """ try: await unregister_token(bot.decrypted_token()) - except Unauthorized: + except exceptions.Unauthorized: # Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы pass await bot.delete() await call.answer(_("Бот удалён")) try: await call.message.delete() - except TelegramAPIError: + except exceptions.TelegramAPIError: pass @@ -84,7 +89,7 @@ async def select_chat(bot: Bot, call: types.CallbackQuery, chat: str): try: await chat.delete() await a_bot.leave_chat(chat.chat_id) - except TelegramAPIError: + except exceptions.TelegramAPIError: pass await call.answer(_("Бот вышел из чатов")) await a_bot.session.close() @@ -133,3 +138,33 @@ async def antiflood(bot: Bot, call: types.CallbackQuery): 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: dict) -> int: + users = await bot.mailing_users + a_bot = AioBot(bot.decrypted_token()) + + count = 0 + + for user in users: + bot.last_mailing_at = datetime.now() + await bot.save(update_fields=["last_mailing_at"]) + try: + await sleep(0.05) + try: + file_id = await send_stored_message(context, a_bot, user.telegram_id) + except exceptions.RetryAfter as err: + await sleep(err.timeout) + file_id = await send_stored_message(context, a_bot, user.telegram_id) + + if file_id: + context["mailing_id"] = file_id + count += 1 + except (exceptions.ChatNotFound, exceptions.BotBlocked, exceptions.UserDeactivated): + await user.delete() + except Exception as err: + logging.error("mailing error") + logging.error(err, exc_info=True) + pass + + return count diff --git a/olgram/commands/menu.py b/olgram/commands/menu.py index 9e8e087..fcd75df 100644 --- a/olgram/commands/menu.py +++ b/olgram/commands/menu.py @@ -1,11 +1,13 @@ +import logging +from io import BytesIO from olgram.router import dp - +from datetime import datetime, timedelta, timezone from aiogram import types, Bot as AioBot from olgram.models.models import Bot, User, DefaultAnswer, BotStartMessage, BotSecondMessage from aiogram.dispatcher import FSMContext from aiogram.utils.callback_data import CallbackData 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 locales.locale import _ @@ -127,6 +129,12 @@ async def send_bot_menu(bot: Bot, call: types.CallbackQuery): callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings", 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(_(""" Управление ботом @{0}. @@ -296,6 +304,83 @@ 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") +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") + + +@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 + + buffer = BytesIO() + + 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 in (types.ContentType.PHOTO, types.ContentType.DOCUMENT, types.ContentType.AUDIO, + types.ContentType.VIDEO): + proxy["mailing_caption"] = message.caption + + if message.content_type == types.ContentType.PHOTO: + obj = message.photo[-1] + elif message.content_type == types.ContentType.DOCUMENT: + obj = message.document + elif message.content_type == types.ContentType.AUDIO: + obj = message.audio + elif message.content_type == types.ContentType.VIDEO: + obj = message.video + + try: + await obj.download(buffer, timeout=5) + except Exception as err: + logging.error("Error downloading file") + logging.error(err, exc_info=True) + return await message.answer(_("Не удалось загрузить файл (слишком большой размер?)")) + proxy["mailing_data"] = buffer.getvalue() + proxy["mailing_file_name"] = getattr(obj, "file_name") + _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, + text="Вы уверены, что хотите разослать это сообщение всем пользователям?", + reply_markup=keyboard) + + async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, chat_id: ty.Optional[int] = None): if call: @@ -527,6 +612,15 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon return await send_bot_statistic_menu(bot, call) if operation == "settings": return await send_bot_settings_menu(bot, call) + if operation == "go_mailing": + if bot.last_mailing_at and bot.last_mailing_at >= datetime.now(tz=timezone.utc) - 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": await state.set_state("wait_start_text") async with state.proxy() as proxy: @@ -536,6 +630,25 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon if level == "3": if operation == "delete_yes": return await bot_actions.delete_bot(bot, call) + if operation == "mailing": + await bot_actions.mailing(bot, call) + return await send_bot_settings_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(tz=timezone.utc) - 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) + return await call.message.answer(_("Рассылка завершена, отправлено {0} сообщений").format(count)) + else: + return await call.answer(_("Устарело, создайте новую рассылку")) if operation == "chat": return await bot_actions.select_chat(bot, call, callback_data.get("chat")) if operation == "threads": diff --git a/olgram/migrations/models/21_20240301222228_update.sql b/olgram/migrations/models/21_20240301222228_update.sql new file mode 100644 index 0000000..c2fdc47 --- /dev/null +++ b/olgram/migrations/models/21_20240301222228_update.sql @@ -0,0 +1,10 @@ +-- upgrade -- +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 -- +DROP TABLE IF EXISTS "mailinguser"; diff --git a/olgram/settings.py b/olgram/settings.py index 653a1f5..eae63bc 100644 --- a/olgram/settings.py +++ b/olgram/settings.py @@ -7,7 +7,6 @@ from datetime import timedelta import typing as ty from olgram.utils.crypto import Cryptor - load_dotenv() @@ -161,4 +160,6 @@ TORTOISE_ORM = { "default_connection": "default", }, }, + "use_tz": False, + "timezone": "UTC" } diff --git a/olgram/utils/mix.py b/olgram/utils/mix.py index 2a39adb..8800c08 100644 --- a/olgram/utils/mix.py +++ b/olgram/utils/mix.py @@ -1,3 +1,5 @@ +import logging +from io import BytesIO from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup from aiogram.utils.exceptions import TelegramAPIError from aiogram import types, Bot as AioBot @@ -39,15 +41,23 @@ async def send_stored_message(storage: dict, bot: AioBot, chat_id: int): 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")) + if content_type in (types.ContentType.AUDIO, types.ContentType.VIDEO, types.ContentType.DOCUMENT, + types.ContentType.PHOTO): + caption = storage.get("mailing_caption") + if storage.get("mailing_id"): + logging.info("Mailing use file id") + obj = storage["mailing_id"] + else: + logging.info("Mailing upload file") + obj = types.InputFile(BytesIO(storage["mailing_data"]), filename=storage.get("mailing_file_name")) + + if content_type == types.ContentType.AUDIO: + return (await bot.send_audio(chat_id, audio=obj, caption=caption)).audio.file_id + if content_type == types.ContentType.PHOTO: + return (await bot.send_photo(chat_id, photo=obj, caption=caption)).photo[-1].file_id + if content_type == types.ContentType.VIDEO: + return (await bot.send_video(chat_id, video=obj, caption=caption)).video.file_id + if content_type == types.ContentType.DOCUMENT: + return (await bot.send_document(chat_id, document=obj, caption=caption)).document.file_id + raise NotImplementedError("Mailing, unknown content type") diff --git a/server/custom.py b/server/custom.py index 06df638..c5a2f69 100644 --- a/server/custom.py +++ b/server/custom.py @@ -229,6 +229,8 @@ async def handle_operator_message(message: types.Message, super_chat_id: int, bo elif super_chat_id > 0: # в супер-чате кто-то пишет сообщение сам себе, только для личных сообщений + if bot.enable_mailing: + asyncio.create_task(MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot)) await message.forward(super_chat_id) # И отправить пользователю специальный текст, если он указан if bot.second_text: