mirror of
https://github.com/civsocit/olgram.git
synced 2025-12-16 11:46:17 +00:00
Compare commits
3 Commits
103618cbd2
...
mailing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58f551c77d | ||
|
|
696bc5368b | ||
|
|
340246b937 |
@@ -1,25 +0,0 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
|
||||
# Required
|
||||
|
||||
version: 2
|
||||
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.8"
|
||||
jobs:
|
||||
post_create_environment:
|
||||
- python -m pip install sphinx_rtd_theme
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
|
||||
sphinx:
|
||||
|
||||
configuration: docs/source/conf.py
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM python:3.8-buster
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
POETRY_VERSION=1.5.1 \
|
||||
POETRY_VERSION=1.1.12 \
|
||||
POETRY_VIRTUALENVS_CREATE="false"
|
||||
|
||||
RUN apt-get update && \
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
О проекте
|
||||
===================================
|
||||
|
||||
|
||||
Зачем нужен Olgram
|
||||
------------
|
||||
|
||||
|
||||
@@ -4,30 +4,21 @@
|
||||
Донаты
|
||||
----------------
|
||||
|
||||
На аренду сервера для этого проекта
|
||||
На рекламу проекта, аренду сервера и пиццу
|
||||
|
||||
Bitcoin:
|
||||
``bc1qlq7cm5chc8flr3fy8ewk967aknq3dwmxtwn9hl``
|
||||
|
||||
Litecoin:
|
||||
``ltc1qxajsvz0lw44aa5nytuch8cp2g8x7a4cdase4y7``
|
||||
|
||||
Monero:
|
||||
``84ymMfpw3vxFxsgmYbFURMiZLgQCmhKsZNiZiqZRbpH2WRka2UDjyDVZpX8XH1cZ9d5EghvPXrF5hEuzvK5NvHGE8za4Gmk``
|
||||
``886AQ8tCVcQKp21xsuSLkfDdTAdtCFH1jR58Tw9MsaxFXoZ7YRHXx1cQcUfUnDX6hySzPsQEVt6RWPn3sXH9QUmwCr3oVqB``
|
||||
|
||||
Как убрать "Этот бот создан с помощью ..."
|
||||
----------------
|
||||
Напишите нам на `@civsocit_feedback_bot <https://t.me/civsocit_feedback_bot>`_.
|
||||
Dash:
|
||||
``XqxetfWzr5n4Ms1TxMbdEEeHGe8CaMdmb6``
|
||||
|
||||
|
||||
История изменений
|
||||
----------------
|
||||
|
||||
- `2024-03-15` Тэги
|
||||
- `2024-03-02` Рассылки
|
||||
- `2024-03-01` Непрерывные потоки сообщений (опция)
|
||||
- `2024-02-17` Опция смены режима работы автоответчика: автоответчик отвечает на КАЖДОЕ сообщение
|
||||
- `2024-01-12` Мультиязычность (стартовое сообщение и автоответчик)
|
||||
- `2022-08-01` Защита от флуда
|
||||
- `2022-07-23` Автоответчик не пишет сообщение лишний раз
|
||||
- `2022-07-04` Поддержка двух ботов в одном чате
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# -- Project information
|
||||
|
||||
project = 'Olgram'
|
||||
copyright = '2024, Civsocit'
|
||||
copyright = '2022, Civsocit'
|
||||
author = 'civsocit'
|
||||
|
||||
release = '0.1'
|
||||
|
||||
@@ -50,25 +50,6 @@ Olgram пересылает сообщения так, чтобы сообщен
|
||||
При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
|
||||
не успеваете обрабатывать входящие сообщения.
|
||||
|
||||
|
||||
.. _always_second_message:
|
||||
|
||||
Использовать автоответчик всегда
|
||||
--------------------------------
|
||||
|
||||
По-умолчанию автоответчик отвечает только на первое сообщение в диалоге с пользователем. Чтобы автоответчик отвечал на
|
||||
КАЖДОЕ входящее сообщение, включите эту опцию.
|
||||
|
||||
|
||||
.. thread_interrupt:
|
||||
|
||||
Прерывать поток
|
||||
--------------------------------
|
||||
|
||||
По-умолчанию поток сообщений от одного пользователя прерывается каждые 24 часа. Без этой опции поток сообщений не
|
||||
прерывается никогда.
|
||||
|
||||
|
||||
.. _mailing:
|
||||
|
||||
Рассылка
|
||||
|
||||
@@ -68,13 +68,6 @@ BotFather - это официальный бот Telegram, создающий д
|
||||
|
||||
Теперь просто отправьте новый текст приветствия.
|
||||
|
||||
.. note::
|
||||
|
||||
Чтобы настроить особый текст приветствия для, например, русскоязычных пользователей (т.е. тех пользователей, у
|
||||
которых в настройках Telegram выставлена русская локализация), нажмите кнопку "Руссикй 🇷🇺" и только потом отправьте
|
||||
текст приветствия. Чтобы отредактировать текст приветствия для всех остальных языков, нажмите "[все языки]".
|
||||
|
||||
|
||||
Как привязать бота к групповому чату
|
||||
------------------------------------
|
||||
|
||||
@@ -115,14 +108,3 @@ BotFather - это официальный бот Telegram, создающий д
|
||||
|
||||
Если у вас возникли вопросы по использованию бота, или вы нашли ошибку - напишите
|
||||
нам `@civsocit_feedback_bot <https://t.me/civsocit_feedback_bot>`_.
|
||||
|
||||
Тэги пользователей
|
||||
------------------
|
||||
|
||||
Пользователям можно проставлять теги. Например, в ответ на сообщение пользователя написать:
|
||||
|
||||
```
|
||||
/tag #important
|
||||
```
|
||||
|
||||
Тогда в user info (см. раздел опции) помимо информации о пользователе будет тег #important
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2024-03-02 19:47+0400\n"
|
||||
"PO-Revision-Date: 2024-03-02 19:48+0400\n"
|
||||
"POT-Creation-Date: 2022-09-02 05:02+0400\n"
|
||||
"PO-Revision-Date: 2022-09-02 05:07+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.4.2\n"
|
||||
"X-Generator: Poedit 3.1\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:27
|
||||
#: olgram/commands/bot_actions.py:22
|
||||
msgid "Бот удалён"
|
||||
msgstr "Bot removed"
|
||||
|
||||
#: olgram/commands/bot_actions.py:49 olgram/commands/bot_actions.py:67
|
||||
#: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50
|
||||
msgid "Текст сброшен"
|
||||
msgstr "Text is reset"
|
||||
|
||||
#: olgram/commands/bot_actions.py:81
|
||||
#: olgram/commands/bot_actions.py:64
|
||||
msgid "Выбран личный чат"
|
||||
msgstr "Personal chat selected"
|
||||
|
||||
#: olgram/commands/bot_actions.py:94
|
||||
#: olgram/commands/bot_actions.py:77
|
||||
msgid "Бот вышел из чатов"
|
||||
msgstr "Bot leaved chats"
|
||||
|
||||
#: olgram/commands/bot_actions.py:100
|
||||
#: olgram/commands/bot_actions.py:83
|
||||
msgid "Нельзя привязать бота к этому чату"
|
||||
msgstr "You can't bind a bot to this chat room"
|
||||
|
||||
#: olgram/commands/bot_actions.py:104
|
||||
#: olgram/commands/bot_actions.py:87
|
||||
msgid "Выбран чат {0}"
|
||||
msgstr "Selected chat {0}"
|
||||
|
||||
@@ -195,11 +195,7 @@ msgstr "All bots have outgoing messages: {0}\n"
|
||||
msgid "Промо-кодов выдано: {0}\n"
|
||||
msgstr "Promo codes issued: {0}\n"
|
||||
|
||||
#: olgram/commands/info.py:40
|
||||
msgid "Рекламную плашку выключили: {0}\n"
|
||||
msgstr "Ad disabled:: {0}\n"
|
||||
|
||||
#: olgram/commands/menu.py:33
|
||||
#: olgram/commands/menu.py:31
|
||||
msgid ""
|
||||
"\n"
|
||||
" У вас нет добавленных ботов.\n"
|
||||
@@ -213,25 +209,25 @@ msgstr ""
|
||||
" Send the command /addbot to add a bot.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:48
|
||||
#: olgram/commands/menu.py:46
|
||||
msgid "Ваши боты"
|
||||
msgstr "Your bots"
|
||||
|
||||
#: olgram/commands/menu.py:69
|
||||
#: olgram/commands/menu.py:67
|
||||
msgid "Личные сообщения"
|
||||
msgstr "Personal messages"
|
||||
|
||||
#: olgram/commands/menu.py:74
|
||||
#: olgram/commands/menu.py:72
|
||||
msgid "❗️ Выйти из всех чатов"
|
||||
msgstr "❗️ Leave all chats"
|
||||
|
||||
#: 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
|
||||
#: 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 "<< Назад"
|
||||
msgstr "<< Back"
|
||||
|
||||
#: olgram/commands/menu.py:85
|
||||
#: olgram/commands/menu.py:83
|
||||
msgid ""
|
||||
"\n"
|
||||
" Этот бот не добавлен в чаты, поэтому все сообщения будут приходить "
|
||||
@@ -253,7 +249,7 @@ msgstr ""
|
||||
" again.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:92
|
||||
#: olgram/commands/menu.py:90
|
||||
msgid ""
|
||||
"\n"
|
||||
" В этом разделе вы можете привязать бота @{0} к чату.\n"
|
||||
@@ -265,31 +261,27 @@ msgstr ""
|
||||
" Select the chat room where the bot will forward messages.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:104
|
||||
#: olgram/commands/menu.py:102
|
||||
msgid "Текст"
|
||||
msgstr "Text"
|
||||
|
||||
#: olgram/commands/menu.py:109
|
||||
#: olgram/commands/menu.py:107
|
||||
msgid "Чат"
|
||||
msgstr "Chat"
|
||||
|
||||
#: olgram/commands/menu.py:114
|
||||
#: olgram/commands/menu.py:112
|
||||
msgid "Удалить бот"
|
||||
msgstr "Delete bot"
|
||||
|
||||
#: olgram/commands/menu.py:119
|
||||
#: olgram/commands/menu.py:117
|
||||
msgid "Статистика"
|
||||
msgstr "Statistics"
|
||||
|
||||
#: olgram/commands/menu.py:128
|
||||
#: olgram/commands/menu.py:126
|
||||
msgid "Опции"
|
||||
msgstr "Options"
|
||||
|
||||
#: olgram/commands/menu.py:134 olgram/commands/menu.py:190
|
||||
msgid "Рассылка"
|
||||
msgstr "Mailing"
|
||||
|
||||
#: olgram/commands/menu.py:139
|
||||
#: olgram/commands/menu.py:131
|
||||
msgid ""
|
||||
"\n"
|
||||
" Управление ботом @{0}.\n"
|
||||
@@ -307,11 +299,11 @@ msgstr ""
|
||||
" @civsocit_feedback_bot\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:151
|
||||
#: olgram/commands/menu.py:143
|
||||
msgid "Да, удалить бот"
|
||||
msgstr "Yes, delete the bot"
|
||||
|
||||
#: olgram/commands/menu.py:160
|
||||
#: olgram/commands/menu.py:152
|
||||
msgid ""
|
||||
"\n"
|
||||
" Вы уверены, что хотите удалить бота @{0}?\n"
|
||||
@@ -321,73 +313,43 @@ msgstr ""
|
||||
" Are you sure you want to delete the bot @{0}?\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:169
|
||||
#: olgram/commands/menu.py:161
|
||||
msgid "Потоки сообщений"
|
||||
msgstr "Message threads"
|
||||
|
||||
#: olgram/commands/menu.py:174
|
||||
#: olgram/commands/menu.py:166
|
||||
msgid "Данные пользователя"
|
||||
msgstr "User data"
|
||||
|
||||
#: olgram/commands/menu.py:179
|
||||
#: olgram/commands/menu.py:171
|
||||
msgid "Антифлуд"
|
||||
msgstr "Antiflood"
|
||||
|
||||
#: olgram/commands/menu.py:184
|
||||
msgid "Автоответчик всегда"
|
||||
msgstr "Autorespond always"
|
||||
|
||||
#: olgram/commands/menu.py:195
|
||||
msgid "Прерывать поток"
|
||||
msgstr "Inteeupt thread"
|
||||
|
||||
#: olgram/commands/menu.py:203
|
||||
#: olgram/commands/menu.py:178
|
||||
msgid "Olgram подпись"
|
||||
msgstr "Olgram signature"
|
||||
|
||||
#: olgram/commands/menu.py:214 olgram/commands/menu.py:215
|
||||
#: olgram/commands/menu.py:189 olgram/commands/menu.py:190
|
||||
msgid "включены"
|
||||
msgstr "enabled"
|
||||
|
||||
#: olgram/commands/menu.py:214 olgram/commands/menu.py:215
|
||||
#: olgram/commands/menu.py:189 olgram/commands/menu.py:190
|
||||
msgid "выключены"
|
||||
msgstr "disabled"
|
||||
|
||||
#: olgram/commands/menu.py:216
|
||||
#: olgram/commands/menu.py:191
|
||||
#, fuzzy
|
||||
#| msgid "включены"
|
||||
msgid "включен"
|
||||
msgstr "enabled"
|
||||
|
||||
#: olgram/commands/menu.py:216 olgram/commands/menu.py:217
|
||||
#: olgram/commands/menu.py:191
|
||||
#, fuzzy
|
||||
#| msgid "выключены"
|
||||
msgid "выключен"
|
||||
msgstr "disabled"
|
||||
|
||||
#: 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
|
||||
#: olgram/commands/menu.py:192
|
||||
msgid ""
|
||||
"\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
@@ -396,12 +358,6 @@ msgid ""
|
||||
"info\">Данные пользователя</a>: <b>{1}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#antiflood\">Антифлуд</a>: <b>{2}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#always_second_message\">Автоответчик всегда</a>: <b>{3}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#thread_interrupt\">Прерывать поток</a>: <b>{4}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#mailing\">Рассылка</a>: <b>{5}</b>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
@@ -410,86 +366,60 @@ msgstr ""
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
|
||||
"info\">User data</a>: <b>{1}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#antiflood\">Anti-flood</a>: <b>{2}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#always_second_message\">Autorespond always</a>: <b>{3}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#thread_interrupt\">Interrupt threads</a>: <b>{4}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#mailing\">Mailing</a>: <b>{5}</b>\n"
|
||||
" "
|
||||
"html#antiflood\">Antiflood</a>: <b>{2}</b>"
|
||||
|
||||
#: olgram/commands/menu.py:232
|
||||
#: olgram/commands/menu.py:199
|
||||
msgid "включена"
|
||||
msgstr "enabled"
|
||||
|
||||
#: olgram/commands/menu.py:199
|
||||
msgid "выключена"
|
||||
msgstr "disabled"
|
||||
|
||||
#: olgram/commands/menu.py:200
|
||||
msgid "Olgram подпись: <b>{0}</b>"
|
||||
msgstr "Olgram signature: <b>{0}</b>"
|
||||
|
||||
#: olgram/commands/menu.py:259 olgram/commands/menu.py:421
|
||||
#: olgram/commands/menu.py:480
|
||||
#: olgram/commands/menu.py:210 olgram/commands/menu.py:272
|
||||
#: olgram/commands/menu.py:314
|
||||
msgid "<< Завершить редактирование"
|
||||
msgstr "<< Finish editing"
|
||||
|
||||
#: olgram/commands/menu.py:263
|
||||
#: olgram/commands/menu.py:214
|
||||
msgid "Автоответчик"
|
||||
msgstr "Autoresponder"
|
||||
|
||||
#: olgram/commands/menu.py:268 olgram/commands/menu.py:435
|
||||
#: olgram/commands/menu.py:219 olgram/commands/menu.py:286
|
||||
msgid "Сбросить текст"
|
||||
msgstr "Reset text"
|
||||
|
||||
#: olgram/commands/menu.py:273 olgram/commands/menu.py:440
|
||||
msgid "[все языки]"
|
||||
msgstr "[all languages]"
|
||||
|
||||
#: olgram/commands/menu.py:290
|
||||
#: olgram/commands/menu.py:224
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете текст, который отправляется после того, как "
|
||||
"пользователь отправит вашему боту @{0}\n"
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Текущий текст{2}:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\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"
|
||||
" Напишите сообщение, которое нужно разослать всем подписчикам вашего бота "
|
||||
"@{0}. \n"
|
||||
" У сообщения будет до {1} получателей. \n"
|
||||
" Учтите, что\n"
|
||||
" 1. Рассылается только одно сообщение за раз (в т.ч. только одна "
|
||||
"картинка)\n"
|
||||
" 2. Когда рассылка запущена, её нельзя отменить \n"
|
||||
" "
|
||||
msgstr ""
|
||||
" You are now editing the text that is sent after the user sends your bot "
|
||||
"@{0}\n"
|
||||
" /start command.\n"
|
||||
"\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"
|
||||
" Current text:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Send a message to change the text.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:367
|
||||
msgid "Не удалось загрузить файл (слишком большой размер?)"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:374
|
||||
msgid "Да, начать рассылку"
|
||||
msgstr "Yes, start mailing"
|
||||
|
||||
#: olgram/commands/menu.py:394
|
||||
#: olgram/commands/menu.py:251
|
||||
msgid ""
|
||||
"\n"
|
||||
" Статистика по боту @{0}\n"
|
||||
@@ -509,32 +439,45 @@ msgstr ""
|
||||
" Banned users: <b>{4}</b>\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:425
|
||||
#: olgram/commands/menu.py:276
|
||||
msgid "Предыдущий текст"
|
||||
msgstr "Previous text"
|
||||
|
||||
#: olgram/commands/menu.py:430
|
||||
#: olgram/commands/menu.py:281
|
||||
msgid "Шаблоны ответов..."
|
||||
msgstr "Answer templates..."
|
||||
|
||||
#: olgram/commands/menu.py:457
|
||||
#: olgram/commands/menu.py:291
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в "
|
||||
"ответ на все входящие сообщения @{0} автоматически. По умолчанию оно "
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст{2}:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\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"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Send a message to change the text.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:466
|
||||
msgid "отключено"
|
||||
msgstr "disabled"
|
||||
#: olgram/commands/menu.py:301
|
||||
msgid "(отключено)"
|
||||
msgstr "(disabled)"
|
||||
|
||||
#: olgram/commands/menu.py:484
|
||||
#: olgram/commands/menu.py:318
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n"
|
||||
@@ -560,50 +503,30 @@ msgstr ""
|
||||
" To remove a template from the list, send its number in the list (for "
|
||||
"example, 4) "
|
||||
|
||||
#: olgram/commands/menu.py:503
|
||||
#: olgram/commands/menu.py:337
|
||||
msgid "(нет шаблонов)"
|
||||
msgstr "(no templates)"
|
||||
|
||||
#: olgram/commands/menu.py:565
|
||||
#: olgram/commands/menu.py:376
|
||||
msgid "У вас нет шаблонов, чтобы их удалять"
|
||||
msgstr "You don't have templates to delete them"
|
||||
|
||||
#: olgram/commands/menu.py:567
|
||||
#: olgram/commands/menu.py:378
|
||||
msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}"
|
||||
msgstr "To delete a template, enter a number between 0 and {0}"
|
||||
|
||||
#: olgram/commands/menu.py:575
|
||||
#: olgram/commands/menu.py:386
|
||||
msgid "У вашего бота уже слишком много шаблонов"
|
||||
msgstr "Your bot already has too many templates"
|
||||
|
||||
#: olgram/commands/menu.py:579
|
||||
#: olgram/commands/menu.py:390
|
||||
msgid "Такой текст уже есть в списке шаблонов"
|
||||
msgstr "This text is already in the list of templates"
|
||||
|
||||
#: olgram/commands/menu.py:597
|
||||
#: olgram/commands/menu.py:408
|
||||
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"
|
||||
@@ -628,8 +551,8 @@ msgstr "Promotion code withdrawn"
|
||||
msgid ""
|
||||
"Укажите аргумент: промокод. Например: <pre>/setpromo my-promo-code</pre>"
|
||||
msgstr ""
|
||||
"Specify the argument: promo code. For example: <pre>/setpromo my-promo-code</"
|
||||
"pre>"
|
||||
"Specify the argument: promo code. For example: <pre>/setpromo my-promo-"
|
||||
"code</pre>"
|
||||
|
||||
#: olgram/commands/promo.py:78 olgram/commands/promo.py:82
|
||||
msgid "Промокод не найден"
|
||||
@@ -696,102 +619,87 @@ msgstr ""
|
||||
" Write your question and we will answer you shortly.\n"
|
||||
" "
|
||||
|
||||
#: olgram/utils/permissions.py:41
|
||||
#: olgram/utils/permissions.py:40
|
||||
msgid "Владелец бота ограничил доступ к этому функционалу 😞"
|
||||
msgstr "The bot owner has restricted access to this functionality 😞"
|
||||
|
||||
#: olgram/utils/permissions.py:53
|
||||
#: olgram/utils/permissions.py:52
|
||||
msgid "Владелец бота ограничил доступ к этому функционалу😞"
|
||||
msgstr "The owner of the bot has restricted access to this function😞"
|
||||
|
||||
#: server/custom.py:57
|
||||
#: server/custom.py:55
|
||||
msgid ""
|
||||
"<b>Политика конфиденциальности</b>\n"
|
||||
"\n"
|
||||
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При "
|
||||
"отправке сообщения (кроме команд /start и /security_policy) ваш "
|
||||
"идентификатор пользователя записывается в кеш на некоторое время и потом "
|
||||
"удаляется из кеша. Этот идентификатор используется для общения с "
|
||||
"оператором.\n"
|
||||
"удаляется из кеша. Этот идентификатор используется только для общения с "
|
||||
"оператором; боты Olgram не делают массовых рассылок.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"<b>Privacy Policy</b>.\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 used for "
|
||||
"communicating with the operator\n"
|
||||
"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"
|
||||
"\n"
|
||||
|
||||
#: server/custom.py:62
|
||||
#: server/custom.py:61
|
||||
msgid ""
|
||||
"При отправке сообщения (кроме команд /start и /security_policy) оператор "
|
||||
"<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в "
|
||||
"силу настроек, которые оператор указал при создании бота.\n"
|
||||
"\n"
|
||||
"силу настроек, которые оператор указал при создании бота."
|
||||
msgstr ""
|
||||
"When sending a message (except /start and /security_policy), the operator "
|
||||
"<b>sees</b> your username, @username and user ID by virtue of the settings "
|
||||
"that the operator specified when creating the bot.\n"
|
||||
"\n"
|
||||
"that the operator specified when creating the bot."
|
||||
|
||||
#: server/custom.py:66
|
||||
#: server/custom.py:65
|
||||
msgid ""
|
||||
"В зависимости от ваших настроек конфиденциальности Telegram, оператор может "
|
||||
"видеть ваш username, имя пользователя и другую информацию.\n"
|
||||
"\n"
|
||||
"видеть ваш username, имя пользователя и другую информацию."
|
||||
msgstr ""
|
||||
"Depending on your Telegram privacy settings, the operator may see your "
|
||||
"username, username and other information.\n"
|
||||
"\n"
|
||||
"username, username and other information."
|
||||
|
||||
#: 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
|
||||
#: server/custom.py:76
|
||||
msgid "Сообщение от пользователя "
|
||||
msgstr "Message from the user "
|
||||
|
||||
#: server/custom.py:157
|
||||
#: server/custom.py:135
|
||||
msgid "Вы заблокированы в этом боте"
|
||||
msgstr "You are blocked in this bot"
|
||||
|
||||
#: server/custom.py:163
|
||||
#: server/custom.py:141
|
||||
msgid "Слишком много сообщений, подождите одну минуту"
|
||||
msgstr "Too many messages, wait one minute"
|
||||
|
||||
#: server/custom.py:170
|
||||
#: server/custom.py:148
|
||||
msgid "Не удаётся связаться с владельцем бота"
|
||||
msgstr "Cannot contact the owner of the bot"
|
||||
|
||||
#: server/custom.py:202
|
||||
#: server/custom.py:179
|
||||
msgid ""
|
||||
"<i>Невозможно переслать сообщение: автор не найден (сообщение слишком "
|
||||
"старое?)</i>"
|
||||
msgstr ""
|
||||
"<i>Cannot forward this message: author not found (message too old?)</i>"
|
||||
|
||||
#: server/custom.py:210
|
||||
#: server/custom.py:187
|
||||
msgid "Пользователь заблокирован"
|
||||
msgstr "User is blocked"
|
||||
|
||||
#: server/custom.py:215
|
||||
#: server/custom.py:192
|
||||
msgid "Пользователь не был забанен"
|
||||
msgstr "The user was not banned"
|
||||
|
||||
#: server/custom.py:218
|
||||
#: server/custom.py:195
|
||||
msgid "Пользователь разбанен"
|
||||
msgstr "A user has been unlocked"
|
||||
|
||||
#: server/custom.py:223
|
||||
#: server/custom.py:200
|
||||
msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>"
|
||||
msgstr "<i>Cannot forward the message (has the author blocked the bot?)</i>"
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2024-03-02 19:47+0400\n"
|
||||
"PO-Revision-Date: 2024-03-02 19:48+0400\n"
|
||||
"POT-Creation-Date: 2022-09-02 05:07+0400\n"
|
||||
"PO-Revision-Date: 2022-09-02 05:12+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.4.2\n"
|
||||
"X-Generator: Poedit 3.1\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:27
|
||||
#: olgram/commands/bot_actions.py:22
|
||||
msgid "Бот удалён"
|
||||
msgstr "Бот видалений"
|
||||
|
||||
#: olgram/commands/bot_actions.py:49 olgram/commands/bot_actions.py:67
|
||||
#: olgram/commands/bot_actions.py:38 olgram/commands/bot_actions.py:50
|
||||
msgid "Текст сброшен"
|
||||
msgstr "Текст скинутий"
|
||||
|
||||
#: olgram/commands/bot_actions.py:81
|
||||
#: olgram/commands/bot_actions.py:64
|
||||
msgid "Выбран личный чат"
|
||||
msgstr "Вибраний особистий чат"
|
||||
|
||||
#: olgram/commands/bot_actions.py:94
|
||||
#: olgram/commands/bot_actions.py:77
|
||||
msgid "Бот вышел из чатов"
|
||||
msgstr "Бот вийшов із чатів"
|
||||
|
||||
#: olgram/commands/bot_actions.py:100
|
||||
#: olgram/commands/bot_actions.py:83
|
||||
msgid "Нельзя привязать бота к этому чату"
|
||||
msgstr "Не можна прив'язати робота до цього чату"
|
||||
|
||||
#: olgram/commands/bot_actions.py:104
|
||||
#: olgram/commands/bot_actions.py:87
|
||||
msgid "Выбран чат {0}"
|
||||
msgstr "Вибраний чат {0}"
|
||||
|
||||
@@ -198,11 +198,7 @@ msgstr "Вихідних повідомлень у всіх роботів: {0}\
|
||||
msgid "Промо-кодов выдано: {0}\n"
|
||||
msgstr "Промо-кодів видано: {0}\n"
|
||||
|
||||
#: olgram/commands/info.py:40
|
||||
msgid "Рекламную плашку выключили: {0}\n"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:33
|
||||
#: olgram/commands/menu.py:31
|
||||
msgid ""
|
||||
"\n"
|
||||
" У вас нет добавленных ботов.\n"
|
||||
@@ -217,25 +213,25 @@ msgstr ""
|
||||
" \n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:48
|
||||
#: olgram/commands/menu.py:46
|
||||
msgid "Ваши боты"
|
||||
msgstr "Ваші боти"
|
||||
|
||||
#: olgram/commands/menu.py:69
|
||||
#: olgram/commands/menu.py:67
|
||||
msgid "Личные сообщения"
|
||||
msgstr "Особисті повідомлення"
|
||||
|
||||
#: olgram/commands/menu.py:74
|
||||
#: olgram/commands/menu.py:72
|
||||
msgid "❗️ Выйти из всех чатов"
|
||||
msgstr "❗️ Вийти зі всіх чатів"
|
||||
|
||||
#: 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
|
||||
#: 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 "<< Назад"
|
||||
msgstr "<< Назад"
|
||||
|
||||
#: olgram/commands/menu.py:85
|
||||
#: olgram/commands/menu.py:83
|
||||
msgid ""
|
||||
"\n"
|
||||
" Этот бот не добавлен в чаты, поэтому все сообщения будут приходить "
|
||||
@@ -258,7 +254,7 @@ msgstr ""
|
||||
" \n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:92
|
||||
#: olgram/commands/menu.py:90
|
||||
msgid ""
|
||||
"\n"
|
||||
" В этом разделе вы можете привязать бота @{0} к чату.\n"
|
||||
@@ -271,31 +267,27 @@ msgstr ""
|
||||
" \n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:104
|
||||
#: olgram/commands/menu.py:102
|
||||
msgid "Текст"
|
||||
msgstr "Текст"
|
||||
|
||||
#: olgram/commands/menu.py:109
|
||||
#: olgram/commands/menu.py:107
|
||||
msgid "Чат"
|
||||
msgstr "Чат"
|
||||
|
||||
#: olgram/commands/menu.py:114
|
||||
#: olgram/commands/menu.py:112
|
||||
msgid "Удалить бот"
|
||||
msgstr "Видалити бот"
|
||||
|
||||
#: olgram/commands/menu.py:119
|
||||
#: olgram/commands/menu.py:117
|
||||
msgid "Статистика"
|
||||
msgstr "Статистика"
|
||||
|
||||
#: olgram/commands/menu.py:128
|
||||
#: olgram/commands/menu.py:126
|
||||
msgid "Опции"
|
||||
msgstr "Опції"
|
||||
|
||||
#: olgram/commands/menu.py:134 olgram/commands/menu.py:190
|
||||
msgid "Рассылка"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:139
|
||||
#: olgram/commands/menu.py:131
|
||||
msgid ""
|
||||
"\n"
|
||||
" Управление ботом @{0}.\n"
|
||||
@@ -313,11 +305,11 @@ msgstr ""
|
||||
" @civsocit_feedback_bot\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:151
|
||||
#: olgram/commands/menu.py:143
|
||||
msgid "Да, удалить бот"
|
||||
msgstr "Так, видалити бот"
|
||||
|
||||
#: olgram/commands/menu.py:160
|
||||
#: olgram/commands/menu.py:152
|
||||
msgid ""
|
||||
"\n"
|
||||
" Вы уверены, что хотите удалить бота @{0}?\n"
|
||||
@@ -327,75 +319,43 @@ msgstr ""
|
||||
" Ви впевнені, що хочете видалити бота @{0}?\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:169
|
||||
#: olgram/commands/menu.py:161
|
||||
msgid "Потоки сообщений"
|
||||
msgstr "Потоки повідомлень"
|
||||
|
||||
#: olgram/commands/menu.py:174
|
||||
#: olgram/commands/menu.py:166
|
||||
msgid "Данные пользователя"
|
||||
msgstr "Дані користувача"
|
||||
|
||||
#: olgram/commands/menu.py:179
|
||||
#: olgram/commands/menu.py:171
|
||||
msgid "Антифлуд"
|
||||
msgstr "Антифлуд"
|
||||
|
||||
#: olgram/commands/menu.py:184
|
||||
#, fuzzy
|
||||
#| msgid "Автоответчик"
|
||||
msgid "Автоответчик всегда"
|
||||
msgstr "Автовідповідач"
|
||||
|
||||
#: olgram/commands/menu.py:195
|
||||
msgid "Прерывать поток"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:203
|
||||
#: olgram/commands/menu.py:178
|
||||
msgid "Olgram подпись"
|
||||
msgstr "Olgram підпис"
|
||||
|
||||
#: olgram/commands/menu.py:214 olgram/commands/menu.py:215
|
||||
#: olgram/commands/menu.py:189 olgram/commands/menu.py:190
|
||||
msgid "включены"
|
||||
msgstr "включені"
|
||||
|
||||
#: olgram/commands/menu.py:214 olgram/commands/menu.py:215
|
||||
#: olgram/commands/menu.py:189 olgram/commands/menu.py:190
|
||||
msgid "выключены"
|
||||
msgstr "вимкнені"
|
||||
|
||||
#: olgram/commands/menu.py:216
|
||||
#: olgram/commands/menu.py:191
|
||||
#, fuzzy
|
||||
#| msgid "включены"
|
||||
msgid "включен"
|
||||
msgstr "включені"
|
||||
|
||||
#: olgram/commands/menu.py:216 olgram/commands/menu.py:217
|
||||
#: olgram/commands/menu.py:191
|
||||
#, fuzzy
|
||||
#| msgid "выключены"
|
||||
msgid "выключен"
|
||||
msgstr "вимкнені"
|
||||
|
||||
#: 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
|
||||
#: olgram/commands/menu.py:192
|
||||
msgid ""
|
||||
"\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
@@ -404,81 +364,69 @@ msgid ""
|
||||
"info\">Данные пользователя</a>: <b>{1}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#antiflood\">Антифлуд</a>: <b>{2}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#always_second_message\">Автоответчик всегда</a>: <b>{3}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#thread_interrupt\">Прерывать поток</a>: <b>{4}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#mailing\">Рассылка</a>: <b>{5}</b>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options."
|
||||
"html#threads\">Потоки повідомлень</a>: <b>{0}</b>\n"
|
||||
" <a href=\"https://olgram.readthedocs.io/ru/latest/options.html#user-"
|
||||
"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:232
|
||||
#: olgram/commands/menu.py:199
|
||||
msgid "включена"
|
||||
msgstr "включена"
|
||||
|
||||
#: olgram/commands/menu.py:199
|
||||
msgid "выключена"
|
||||
msgstr "вимкнена"
|
||||
|
||||
#: olgram/commands/menu.py:200
|
||||
msgid "Olgram подпись: <b>{0}</b>"
|
||||
msgstr "Olgram підпис: <b>{0}</b>"
|
||||
|
||||
#: olgram/commands/menu.py:259 olgram/commands/menu.py:421
|
||||
#: olgram/commands/menu.py:480
|
||||
#: olgram/commands/menu.py:210 olgram/commands/menu.py:272
|
||||
#: olgram/commands/menu.py:314
|
||||
msgid "<< Завершить редактирование"
|
||||
msgstr "<< Завершити редагування"
|
||||
|
||||
#: olgram/commands/menu.py:263
|
||||
#: olgram/commands/menu.py:214
|
||||
msgid "Автоответчик"
|
||||
msgstr "Автовідповідач"
|
||||
|
||||
#: olgram/commands/menu.py:268 olgram/commands/menu.py:435
|
||||
#: olgram/commands/menu.py:219 olgram/commands/menu.py:286
|
||||
msgid "Сбросить текст"
|
||||
msgstr "Скинути текст"
|
||||
|
||||
#: olgram/commands/menu.py:273 olgram/commands/menu.py:440
|
||||
msgid "[все языки]"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:290
|
||||
#: olgram/commands/menu.py:224
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете текст, который отправляется после того, как "
|
||||
"пользователь отправит вашему боту @{0}\n"
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Текущий текст{2}:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: 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"
|
||||
" Зараз ви редагуєте текст, який надсилається після того, як користувач "
|
||||
"надішле вашому боту @{0}\n"
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Поточний текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Надішліть повідомлення, щоб змінити текст.\n"
|
||||
" \n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:367
|
||||
msgid "Не удалось загрузить файл (слишком большой размер?)"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:374
|
||||
msgid "Да, начать рассылку"
|
||||
msgstr ""
|
||||
|
||||
#: olgram/commands/menu.py:394
|
||||
#: olgram/commands/menu.py:251
|
||||
msgid ""
|
||||
"\n"
|
||||
" Статистика по боту @{0}\n"
|
||||
@@ -498,32 +446,45 @@ msgstr ""
|
||||
" Забанено користувачів: <b>{4}</b>\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:425
|
||||
#: olgram/commands/menu.py:276
|
||||
msgid "Предыдущий текст"
|
||||
msgstr "Попередній текст"
|
||||
|
||||
#: olgram/commands/menu.py:430
|
||||
#: olgram/commands/menu.py:281
|
||||
msgid "Шаблоны ответов..."
|
||||
msgstr "Шаблони відповідей..."
|
||||
|
||||
#: olgram/commands/menu.py:457
|
||||
#: olgram/commands/menu.py:291
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в "
|
||||
"ответ на все входящие сообщения @{0} автоматически. По умолчанию оно "
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст{2}:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Зараз ви редагуєте текст автовідповідача. Це повідомлення надсилається у "
|
||||
"відповідь на всі вхідні повідомлення @{0} автоматично. За замовчуванням його "
|
||||
"вимкнено.\n"
|
||||
"\n"
|
||||
" Поточний текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Надішліть повідомлення, щоб змінити текст.\n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:466
|
||||
msgid "отключено"
|
||||
msgstr "відключено"
|
||||
#: olgram/commands/menu.py:301
|
||||
msgid "(отключено)"
|
||||
msgstr "(відключено)"
|
||||
|
||||
#: olgram/commands/menu.py:484
|
||||
#: olgram/commands/menu.py:318
|
||||
msgid ""
|
||||
"\n"
|
||||
" Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:\n"
|
||||
@@ -549,50 +510,30 @@ msgstr ""
|
||||
" \n"
|
||||
" "
|
||||
|
||||
#: olgram/commands/menu.py:503
|
||||
#: olgram/commands/menu.py:337
|
||||
msgid "(нет шаблонов)"
|
||||
msgstr "(Немає шаблонів)"
|
||||
|
||||
#: olgram/commands/menu.py:565
|
||||
#: olgram/commands/menu.py:376
|
||||
msgid "У вас нет шаблонов, чтобы их удалять"
|
||||
msgstr "У вас немає шаблонів, щоб їх видаляти"
|
||||
|
||||
#: olgram/commands/menu.py:567
|
||||
#: olgram/commands/menu.py:378
|
||||
msgid "Неправильное число. Чтобы удалить шаблон, введите число от 0 до {0}"
|
||||
msgstr "Неправильне число. Щоб видалити шаблон, введіть число від 0 до {0}"
|
||||
|
||||
#: olgram/commands/menu.py:575
|
||||
#: olgram/commands/menu.py:386
|
||||
msgid "У вашего бота уже слишком много шаблонов"
|
||||
msgstr "У вашого бота вже дуже багато шаблонів"
|
||||
|
||||
#: olgram/commands/menu.py:579
|
||||
#: olgram/commands/menu.py:390
|
||||
msgid "Такой текст уже есть в списке шаблонов"
|
||||
msgstr "Такий текст вже є у списку шаблонів"
|
||||
|
||||
#: olgram/commands/menu.py:597
|
||||
#: olgram/commands/menu.py:408
|
||||
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"
|
||||
@@ -688,23 +629,23 @@ msgstr ""
|
||||
" \n"
|
||||
" "
|
||||
|
||||
#: olgram/utils/permissions.py:41
|
||||
#: olgram/utils/permissions.py:40
|
||||
msgid "Владелец бота ограничил доступ к этому функционалу 😞"
|
||||
msgstr "Власник бота обмежив доступ до цього функціоналу 😞"
|
||||
|
||||
#: olgram/utils/permissions.py:53
|
||||
#: olgram/utils/permissions.py:52
|
||||
msgid "Владелец бота ограничил доступ к этому функционалу😞"
|
||||
msgstr "Власник бота обмежив доступ до цього функціоналу 😞"
|
||||
|
||||
#: server/custom.py:57
|
||||
#: server/custom.py:55
|
||||
msgid ""
|
||||
"<b>Политика конфиденциальности</b>\n"
|
||||
"\n"
|
||||
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При "
|
||||
"отправке сообщения (кроме команд /start и /security_policy) ваш "
|
||||
"идентификатор пользователя записывается в кеш на некоторое время и потом "
|
||||
"удаляется из кеша. Этот идентификатор используется для общения с "
|
||||
"оператором.\n"
|
||||
"удаляется из кеша. Этот идентификатор используется только для общения с "
|
||||
"оператором; боты Olgram не делают массовых рассылок.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"<b>Політика конфіденційності</b>\n"
|
||||
@@ -712,63 +653,45 @@ msgstr ""
|
||||
"Цей бот не зберігає ваші повідомлення, ім'я користувача та @username. При "
|
||||
"надсиланні повідомлення (крім команд /start та /security_policy) ваш "
|
||||
"ідентифікатор користувача записується в кеш на деякий час і потім "
|
||||
"видаляється з кеша. Цей ідентифікатор використовується для спілкування з "
|
||||
"оператором.\n"
|
||||
"видаляється з кеша. Цей ідентифікатор використовується лише для спілкування "
|
||||
"з оператором; боти Olgram не роблять масових розсилок.\n"
|
||||
"\n"
|
||||
|
||||
#: server/custom.py:62
|
||||
#: server/custom.py:61
|
||||
msgid ""
|
||||
"При отправке сообщения (кроме команд /start и /security_policy) оператор "
|
||||
"<b>видит</b> ваши имя пользователя, @username и идентификатор пользователя в "
|
||||
"силу настроек, которые оператор указал при создании бота.\n"
|
||||
"\n"
|
||||
"силу настроек, которые оператор указал при создании бота."
|
||||
msgstr ""
|
||||
"При надсиланні повідомлення (крім команд /start та /security_policy) "
|
||||
"оператор <b>бачить</b> ваше ім'я користувача, @username та ідентифікатор "
|
||||
"користувача через налаштування, які оператор вказав при створенні бота.\n"
|
||||
"\n"
|
||||
"користувача через налаштування, які оператор вказав при створенні бота."
|
||||
|
||||
#: server/custom.py:66
|
||||
#: server/custom.py:65
|
||||
msgid ""
|
||||
"В зависимости от ваших настроек конфиденциальности Telegram, оператор может "
|
||||
"видеть ваш username, имя пользователя и другую информацию.\n"
|
||||
"\n"
|
||||
"видеть ваш username, имя пользователя и другую информацию."
|
||||
msgstr ""
|
||||
"Залежно від ваших налаштувань конфіденційності Telegram оператор може бачити "
|
||||
"ваш username, ім'я користувача та іншу інформацію.\n"
|
||||
"\n"
|
||||
"ваш username, ім'я користувача та іншу інформацію."
|
||||
|
||||
#: server/custom.py:70
|
||||
msgid ""
|
||||
"В этом боте включена массовая рассылка в силу настроек, которые оператор "
|
||||
"указал при создании бота. Ваш идентификатор пользователя может быть записан "
|
||||
"в базу данных на долгое время"
|
||||
msgstr ""
|
||||
"У цьому роботі включено масове розсилання в силу налаштувань, які оператор "
|
||||
"вказав при створенні робота. Ваш ідентифікатор користувача може бути "
|
||||
"записаний до бази даних на довгий час"
|
||||
|
||||
#: server/custom.py:73
|
||||
msgid "В этом боте нет массовой рассылки сообщений"
|
||||
msgstr "У цьому роботі немає масової розсилки повідомлень"
|
||||
|
||||
#: server/custom.py:83
|
||||
#: server/custom.py:76
|
||||
msgid "Сообщение от пользователя "
|
||||
msgstr "Допис від користувача "
|
||||
|
||||
#: server/custom.py:157
|
||||
#: server/custom.py:135
|
||||
msgid "Вы заблокированы в этом боте"
|
||||
msgstr "Ви заблоковані у цьому боті"
|
||||
|
||||
#: server/custom.py:163
|
||||
#: server/custom.py:141
|
||||
msgid "Слишком много сообщений, подождите одну минуту"
|
||||
msgstr "Забагато повідомлень, зачекайте одну хвилину"
|
||||
|
||||
#: server/custom.py:170
|
||||
#: server/custom.py:148
|
||||
msgid "Не удаётся связаться с владельцем бота"
|
||||
msgstr "Не вдається зв'язатися з власником бота"
|
||||
|
||||
#: server/custom.py:202
|
||||
#: server/custom.py:179
|
||||
msgid ""
|
||||
"<i>Невозможно переслать сообщение: автор не найден (сообщение слишком "
|
||||
"старое?)</i>"
|
||||
@@ -776,19 +699,19 @@ msgstr ""
|
||||
"<i>Неможливо надіслати повідомлення: автора не знайдено (повідомлення "
|
||||
"занадто старе?)</i>"
|
||||
|
||||
#: server/custom.py:210
|
||||
#: server/custom.py:187
|
||||
msgid "Пользователь заблокирован"
|
||||
msgstr "Користувач заблоковано"
|
||||
|
||||
#: server/custom.py:215
|
||||
#: server/custom.py:192
|
||||
msgid "Пользователь не был забанен"
|
||||
msgstr "Користувач не був забанений"
|
||||
|
||||
#: server/custom.py:218
|
||||
#: server/custom.py:195
|
||||
msgid "Пользователь разбанен"
|
||||
msgstr "Користувач розбанений"
|
||||
|
||||
#: server/custom.py:223
|
||||
#: server/custom.py:200
|
||||
msgid "<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>"
|
||||
msgstr "<i>Неможливо надіслати повідомлення (автор заблокував робота?)</i>"
|
||||
|
||||
|
||||
@@ -306,7 +306,9 @@ msgid ""
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -315,7 +317,9 @@ msgstr ""
|
||||
" /start\n"
|
||||
"\n"
|
||||
" 目前的文本。\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" 发送消息,改变文本。\n"
|
||||
" "
|
||||
|
||||
@@ -355,7 +359,9 @@ msgid ""
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -364,7 +370,9 @@ msgstr ""
|
||||
"默认情况下,它是禁用的。\n"
|
||||
"\n"
|
||||
" 目前的文本。\n"
|
||||
" <pre>{1}</pre>。\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>。\n"
|
||||
" 发送消息,改变文本。\n"
|
||||
" "
|
||||
|
||||
|
||||
2
main.py
2
main.py
@@ -7,9 +7,9 @@ from olgram.settings import TORTOISE_ORM, OlgramSettings
|
||||
from olgram.utils.permissions import AccessMiddleware
|
||||
from server.custom import init_redis
|
||||
|
||||
import olgram.commands.menu # noqa: F401
|
||||
import olgram.commands.bots # noqa: F401
|
||||
import olgram.commands.start # noqa: F401
|
||||
import olgram.commands.menu # noqa: F401
|
||||
import olgram.commands.bot_actions # noqa: F401
|
||||
import olgram.commands.info # noqa: F401
|
||||
import olgram.commands.promo # noqa: F401
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
"""
|
||||
Здесь работа с конкретным ботом
|
||||
"""
|
||||
import logging
|
||||
|
||||
from aiogram import types
|
||||
from asyncio import sleep
|
||||
from datetime import datetime
|
||||
from olgram.utils.mix import send_stored_message
|
||||
from aiogram import types
|
||||
from aiogram.utils import exceptions
|
||||
from aiogram import Bot as AioBot
|
||||
from olgram.models.models import Bot, BotStartMessage, BotSecondMessage
|
||||
from olgram.models.models import Bot
|
||||
from olgram.utils.mix import send_stored_message
|
||||
from server.server import unregister_token
|
||||
from locales.locale import _
|
||||
|
||||
@@ -31,39 +29,27 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery):
|
||||
pass
|
||||
|
||||
|
||||
async def reset_bot_text(bot: Bot, call: types.CallbackQuery, state):
|
||||
async def reset_bot_text(bot: Bot, call: types.CallbackQuery):
|
||||
"""
|
||||
Пользователь решил сбросить текст бота к default
|
||||
:param bot:
|
||||
:param call:
|
||||
:return:
|
||||
"""
|
||||
async with state.proxy() as proxy:
|
||||
lang = proxy.get("lang", "none")
|
||||
if lang == "none":
|
||||
await BotStartMessage.filter(bot=bot).delete()
|
||||
bot.start_text = bot._meta.fields_map['start_text'].default
|
||||
await bot.save(update_fields=["start_text"])
|
||||
else:
|
||||
await BotStartMessage.filter(bot=bot, locale=lang).delete()
|
||||
bot.start_text = bot._meta.fields_map['start_text'].default
|
||||
await bot.save()
|
||||
await call.answer(_("Текст сброшен"))
|
||||
|
||||
|
||||
async def reset_bot_second_text(bot: Bot, call: types.CallbackQuery, state):
|
||||
async def reset_bot_second_text(bot: Bot, call: types.CallbackQuery):
|
||||
"""
|
||||
Пользователь решил сбросить second text бота
|
||||
:param bot:
|
||||
:param call:
|
||||
:return:
|
||||
"""
|
||||
async with state.proxy() as proxy:
|
||||
lang = proxy.get("lang", "none")
|
||||
if lang == "none":
|
||||
await BotSecondMessage.filter(bot=bot).delete()
|
||||
bot.second_text = bot._meta.fields_map['second_text'].default
|
||||
await bot.save(update_fields=["second_text"])
|
||||
else:
|
||||
await BotSecondMessage.filter(bot=bot, locale=lang).delete()
|
||||
bot.second_text = bot._meta.fields_map['second_text'].default
|
||||
await bot.save()
|
||||
await call.answer(_("Текст сброшен"))
|
||||
|
||||
|
||||
@@ -114,16 +100,6 @@ async def additional_info(bot: Bot, call: types.CallbackQuery):
|
||||
await bot.save(update_fields=["enable_additional_info"])
|
||||
|
||||
|
||||
async def always_second_message(bot: Bot, call: types.CallbackQuery):
|
||||
bot.enable_always_second_message = not bot.enable_always_second_message
|
||||
await bot.save(update_fields=["enable_always_second_message"])
|
||||
|
||||
|
||||
async def thread_interrupt(bot: Bot, call: types.CallbackQuery):
|
||||
bot.enable_thread_interrupt = not bot.enable_thread_interrupt
|
||||
await bot.save(update_fields=["enable_thread_interrupt"])
|
||||
|
||||
|
||||
async def olgram_text(bot: Bot, call: types.CallbackQuery):
|
||||
if await bot.is_promo():
|
||||
bot.enable_olgram_text = not bot.enable_olgram_text
|
||||
@@ -140,36 +116,28 @@ async def mailing(bot: Bot, call: types.CallbackQuery):
|
||||
await bot.save(update_fields=["enable_mailing"])
|
||||
|
||||
|
||||
async def tags(bot: Bot, call: types.CallbackQuery):
|
||||
bot.enable_tags = not bot.enable_tags
|
||||
await bot.save(update_fields=["enable_tags"])
|
||||
|
||||
|
||||
async def go_mailing(bot: Bot, context: dict) -> int:
|
||||
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:
|
||||
file_id = await send_stored_message(context, a_bot, user.telegram_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
|
||||
await send_stored_message(context, a_bot, user.telegram_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)
|
||||
except exceptions.TelegramAPIError:
|
||||
pass
|
||||
|
||||
return count
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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 olgram.models.models import Bot, User, DefaultAnswer
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from datetime import datetime, timedelta
|
||||
from textwrap import dedent
|
||||
from olgram.utils.mix import edit_or_create, button_text_limit, wrap, send_stored_message
|
||||
from olgram.commands import bot_actions
|
||||
@@ -13,7 +12,6 @@ from locales.locale import _
|
||||
|
||||
import typing as ty
|
||||
|
||||
|
||||
menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat')
|
||||
|
||||
empty = "0"
|
||||
@@ -180,29 +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",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Автоответчик всегда"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="always_second_message",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Рассылка"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="mailing",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Прерывать поток"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="thread_interrupt",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Теги"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="tags",
|
||||
chat=empty))
|
||||
)
|
||||
is_promo = await bot.is_promo()
|
||||
if is_promo:
|
||||
keyboard.insert(
|
||||
@@ -220,20 +200,13 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
||||
thread_turn = _("включены") if bot.enable_threads else _("выключены")
|
||||
info_turn = _("включены") if bot.enable_additional_info else _("выключены")
|
||||
antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен")
|
||||
enable_always_second_message = _("включён") if bot.enable_always_second_message else _("выключен")
|
||||
thread_interrupt = _("да") if bot.enable_thread_interrupt else _("нет")
|
||||
mailing_turn = _("включена") if bot.enable_mailing else _("выключена")
|
||||
tags_turn = _("включены") if bot.enable_tags else _("выключены")
|
||||
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#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#always_second_message">Автоответчик всегда</a>: <b>{3}</b>
|
||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#thread_interrupt">Прерывать поток</a>: <b>{4}</b>
|
||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{5}</b>
|
||||
Теги: <b>{6}</b>
|
||||
""")).format(thread_turn, info_turn, antiflood_turn, enable_always_second_message, thread_interrupt,
|
||||
mailing_turn, tags_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:
|
||||
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
|
||||
@@ -242,26 +215,9 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
||||
await edit_or_create(call, text, reply_markup=keyboard, parse_mode="HTML")
|
||||
|
||||
|
||||
languages = {
|
||||
"en": "English 🇺🇸",
|
||||
"ru": "Русский 🇷🇺",
|
||||
"uk": "Український 🇺🇦",
|
||||
"tr": "Türkçe 🇹🇷",
|
||||
"hy": "հայերեն 🇦🇲",
|
||||
"ka": "ქართული ენა 🇬🇪"
|
||||
}
|
||||
|
||||
|
||||
async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, chat_id: ty.Optional[int] = None,
|
||||
state=None):
|
||||
async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, chat_id: ty.Optional[int] = None):
|
||||
if call:
|
||||
await call.answer()
|
||||
|
||||
async with state.proxy() as proxy:
|
||||
lang = proxy.get("lang", "none")
|
||||
|
||||
prepared_languages = {ln.locale: ln.text for ln in await bot.start_texts}
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(row_width=2)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("<< Завершить редактирование"),
|
||||
@@ -272,40 +228,23 @@ async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] =
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="next_text",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.row(
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Сбросить текст"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="reset_text",
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.add(
|
||||
types.InlineKeyboardButton(text=("🟢 " if lang == "none" else "") + _("[все языки]"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="slang_none", chat=empty))
|
||||
)
|
||||
for code, name in languages.items():
|
||||
prefix = ""
|
||||
if code == lang:
|
||||
prefix = "🟢 "
|
||||
elif code in prepared_languages:
|
||||
prefix = "✔️ "
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=prefix + name,
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation=f"slang_{code}",
|
||||
chat=empty))
|
||||
)
|
||||
|
||||
text = dedent(_("""
|
||||
Сейчас вы редактируете текст, который отправляется после того, как пользователь отправит вашему боту @{0}
|
||||
команду /start
|
||||
|
||||
Текущий текст{2}:
|
||||
<pre>{1}</pre>
|
||||
Текущий текст:
|
||||
<pre>
|
||||
{1}
|
||||
</pre>
|
||||
Отправьте сообщение, чтобы изменить текст.
|
||||
"""))
|
||||
text = text.format(bot.name,
|
||||
prepared_languages.get(lang, bot.start_text),
|
||||
_(" (для языка {0})").format(languages[lang]) if lang != "none" else "")
|
||||
text = text.format(bot.name, bot.start_text)
|
||||
if call:
|
||||
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||
else:
|
||||
@@ -336,65 +275,6 @@ async def send_bot_mailing_menu(bot: Bot, call: ty.Optional[types.CallbackQuery]
|
||||
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
|
||||
if obj.file_size and obj.file_size > 4194304:
|
||||
return await message.answer(_("Слишком большой файл (4 Мб максимум)"))
|
||||
|
||||
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", None)
|
||||
_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=1, bot_id=bot_id, operation=empty, chat=empty))
|
||||
)
|
||||
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:
|
||||
@@ -421,15 +301,9 @@ async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuer
|
||||
|
||||
|
||||
async def send_bot_second_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
|
||||
chat_id: ty.Optional[int] = None, state=None):
|
||||
chat_id: ty.Optional[int] = None):
|
||||
if call:
|
||||
await call.answer()
|
||||
|
||||
async with state.proxy() as proxy:
|
||||
lang = proxy.get("lang", "none")
|
||||
|
||||
prepared_languages = {ln.locale: ln.text for ln in await bot.second_texts}
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(row_width=2)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("<< Завершить редактирование"),
|
||||
@@ -450,35 +324,18 @@ async def send_bot_second_text_menu(bot: Bot, call: ty.Optional[types.CallbackQu
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="reset_second_text", chat=empty))
|
||||
)
|
||||
keyboard.add(
|
||||
types.InlineKeyboardButton(text=("🟢 " if lang == "none" else "") + _("[все языки]"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation="alang_none", chat=empty))
|
||||
)
|
||||
for code, name in languages.items():
|
||||
prefix = ""
|
||||
if code == lang:
|
||||
prefix = "🟢 "
|
||||
elif code in prepared_languages:
|
||||
prefix = "✔️ "
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=prefix + name,
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id,
|
||||
operation=f"alang_{code}",
|
||||
chat=empty))
|
||||
)
|
||||
|
||||
text = dedent(_("""
|
||||
Сейчас вы редактируете текст автоответчика. Это сообщение отправляется в ответ на все входящие сообщения @{0} \
|
||||
автоматически. По умолчанию оно отключено.
|
||||
|
||||
Текущий текст{2}:
|
||||
<pre>{1}</pre>
|
||||
Текущий текст:
|
||||
<pre>
|
||||
{1}
|
||||
</pre>
|
||||
Отправьте сообщение, чтобы изменить текст.
|
||||
"""))
|
||||
text = text.format(bot.name,
|
||||
prepared_languages.get(lang, bot.second_text or _("отключено")),
|
||||
_(" (для языка {0})").format(languages[lang]) if lang != "none" else "")
|
||||
text = text.format(bot.name, bot.second_text if bot.second_text else _("(отключено)"))
|
||||
if call:
|
||||
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||
else:
|
||||
@@ -526,43 +383,63 @@ async def send_bot_templates_menu(bot: Bot, call: ty.Optional[types.CallbackQuer
|
||||
async def start_text_received(message: types.Message, state: FSMContext):
|
||||
async with state.proxy() as proxy:
|
||||
bot_id = proxy.get("bot_id")
|
||||
lang = proxy.get("lang", "none")
|
||||
|
||||
bot = await Bot.get_or_none(pk=bot_id)
|
||||
if lang == "none":
|
||||
bot.start_text = message.html_text
|
||||
await bot.save(update_fields=["start_text"])
|
||||
else:
|
||||
obj, created = await BotStartMessage.get_or_create(bot=bot,
|
||||
locale=lang,
|
||||
defaults={"text": message.html_text})
|
||||
if not created:
|
||||
obj.text = message.html_text
|
||||
await obj.save(update_fields=["text"])
|
||||
await send_bot_text_menu(bot, chat_id=message.chat.id, state=state)
|
||||
bot.start_text = message.html_text
|
||||
await bot.save()
|
||||
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
|
||||
async def second_text_received(message: types.Message, state: FSMContext):
|
||||
async with state.proxy() as proxy:
|
||||
bot_id = proxy.get("bot_id")
|
||||
lang = proxy.get("lang", "none")
|
||||
|
||||
bot = await Bot.get_or_none(pk=bot_id)
|
||||
if lang == "none":
|
||||
bot.second_text = message.html_text
|
||||
await bot.save(update_fields=["second_text"])
|
||||
else:
|
||||
obj, created = await BotSecondMessage.get_or_create(bot=bot,
|
||||
locale=lang,
|
||||
defaults={"text": message.html_text})
|
||||
if not created:
|
||||
obj.text = message.html_text
|
||||
await obj.save(update_fields=["text"])
|
||||
if not bot.second_text:
|
||||
bot.second_text = message.html_text
|
||||
await bot.save(update_fields=["second_text"])
|
||||
await send_bot_second_text_menu(bot, chat_id=message.chat.id, state=state)
|
||||
bot.second_text = message.html_text
|
||||
await bot.save()
|
||||
await send_bot_second_text_menu(bot, chat_id=message.chat.id)
|
||||
|
||||
|
||||
@dp.message_handler(state="wait_template", content_types="text", regexp="^[^/](.+)?") # Not command
|
||||
@@ -627,7 +504,7 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
||||
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):
|
||||
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(_("Нет пользователей для рассылки"))
|
||||
@@ -639,30 +516,11 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
||||
await state.set_state("wait_start_text")
|
||||
async with state.proxy() as proxy:
|
||||
proxy["bot_id"] = bot.id
|
||||
return await send_bot_text_menu(bot, call, state=state)
|
||||
return await send_bot_text_menu(bot, call)
|
||||
|
||||
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":
|
||||
@@ -671,47 +529,40 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
||||
if operation == "antiflood":
|
||||
await bot_actions.antiflood(bot, call)
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "additional_info":
|
||||
await bot_actions.additional_info(bot, call)
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "always_second_message":
|
||||
await bot_actions.always_second_message(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 == "tags":
|
||||
await bot_actions.tags(bot, call)
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "thread_interrupt":
|
||||
await bot_actions.thread_interrupt(bot, call)
|
||||
if operation == "additional_info":
|
||||
await bot_actions.additional_info(bot, call)
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "olgram_text":
|
||||
await bot_actions.olgram_text(bot, call)
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "reset_text":
|
||||
await bot_actions.reset_bot_text(bot, call, state)
|
||||
return await send_bot_text_menu(bot, call, state=state)
|
||||
if operation.startswith("slang_"):
|
||||
async with state.proxy() as proxy:
|
||||
lang = operation.replace("slang_", "")
|
||||
if lang == "none" or lang in languages:
|
||||
proxy["lang"] = lang
|
||||
return await send_bot_text_menu(bot, call, state=state)
|
||||
await bot_actions.reset_bot_text(bot, call)
|
||||
return await send_bot_text_menu(bot, call)
|
||||
if operation == "next_text":
|
||||
await state.set_state("wait_second_text")
|
||||
async with state.proxy() as proxy:
|
||||
proxy["bot_id"] = bot.id
|
||||
return await send_bot_second_text_menu(bot, call, state=state)
|
||||
if operation.startswith("alang_"):
|
||||
async with state.proxy() as proxy:
|
||||
lang = operation.replace("alang_", "")
|
||||
if lang == "none" or lang in languages:
|
||||
proxy["lang"] = lang
|
||||
return await send_bot_second_text_menu(bot, call, state=state)
|
||||
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":
|
||||
await bot_actions.reset_bot_second_text(bot, call, state)
|
||||
return await send_bot_second_text_menu(bot, call, state=state)
|
||||
await bot_actions.reset_bot_second_text(bot, call)
|
||||
return await send_bot_second_text_menu(bot, call)
|
||||
if operation == "templates":
|
||||
await state.set_state("wait_template")
|
||||
async with state.proxy() as proxy:
|
||||
|
||||
@@ -1,10 +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");
|
||||
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";
|
||||
@@ -1,9 +0,0 @@
|
||||
-- upgrade --
|
||||
CREATE TABLE IF NOT EXISTS "bot_start_message" (
|
||||
"id" SERIAL NOT NULL PRIMARY KEY,
|
||||
"locale" VARCHAR(5) NOT NULL,
|
||||
"bot_id" INT NOT NULL REFERENCES "bot" ("id") ON DELETE CASCADE,
|
||||
CONSTRAINT "uid_bot_start_m_bot_id_871cd1" UNIQUE ("bot_id", "locale")
|
||||
);
|
||||
-- downgrade --
|
||||
DROP TABLE IF EXISTS "bot_start_message";
|
||||
@@ -1,4 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot_start_message" ADD "text" TEXT NOT NULL;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot_start_message" DROP COLUMN "text";
|
||||
@@ -1,10 +0,0 @@
|
||||
-- upgrade --
|
||||
CREATE TABLE IF NOT EXISTS "bot_second_message" (
|
||||
"id" SERIAL NOT NULL PRIMARY KEY,
|
||||
"locale" VARCHAR(5) NOT NULL,
|
||||
"text" TEXT NOT NULL,
|
||||
"bot_id" INT NOT NULL REFERENCES "bot" ("id") ON DELETE CASCADE,
|
||||
CONSTRAINT "uid_bot_second__bot_id_432892" UNIQUE ("bot_id", "locale")
|
||||
);
|
||||
-- downgrade --
|
||||
DROP TABLE IF EXISTS "bot_second_message";
|
||||
@@ -1,4 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_always_second_message" BOOL NOT NULL DEFAULT False;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_always_second_message";
|
||||
@@ -1,4 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_thread_interrupt" BOOL NOT NULL DEFAULT True;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_thread_interrupt";
|
||||
@@ -1,6 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_mailing" BOOL NOT NULL DEFAULT False;
|
||||
ALTER TABLE "bot" ADD "last_mailing_at" TIMESTAMPTZ;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_mailing";
|
||||
ALTER TABLE "bot" DROP COLUMN "last_mailing_at";
|
||||
@@ -1,4 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot_start_message" ALTER COLUMN "locale" TYPE VARCHAR(15) USING "locale"::VARCHAR(15);
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot_start_message" ALTER COLUMN "locale" TYPE VARCHAR(5) USING "locale"::VARCHAR(5);
|
||||
@@ -1,4 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_tags" BOOL NOT NULL DEFAULT False;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_tags";
|
||||
@@ -46,10 +46,7 @@ class Bot(Model):
|
||||
enable_additional_info = fields.BooleanField(default=False)
|
||||
enable_olgram_text = fields.BooleanField(default=True)
|
||||
enable_antiflood = fields.BooleanField(default=False)
|
||||
enable_always_second_message = fields.BooleanField(default=False)
|
||||
enable_thread_interrupt = fields.BooleanField(default=True)
|
||||
enable_mailing = fields.BooleanField(default=False)
|
||||
enable_tags = fields.BooleanField(default=False)
|
||||
last_mailing_at = fields.DatetimeField(null=True, default=None)
|
||||
|
||||
def decrypted_token(self):
|
||||
@@ -75,26 +72,15 @@ class Bot(Model):
|
||||
table = 'bot'
|
||||
|
||||
|
||||
class BotStartMessage(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
bot = fields.ForeignKeyField("models.Bot", related_name="start_texts", on_delete=fields.CASCADE)
|
||||
locale = fields.CharField(max_length=15)
|
||||
text = fields.TextField()
|
||||
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:
|
||||
unique_together = ("bot", "locale")
|
||||
table = 'bot_start_message'
|
||||
|
||||
|
||||
class BotSecondMessage(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
bot = fields.ForeignKeyField("models.Bot", related_name="second_texts", on_delete=fields.CASCADE)
|
||||
locale = fields.CharField(max_length=5)
|
||||
text = fields.TextField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ("bot", "locale")
|
||||
table = 'bot_second_message'
|
||||
table = 'mailinguser'
|
||||
unique_together = (("bot", "telegram_id"), )
|
||||
|
||||
|
||||
class User(Model):
|
||||
@@ -109,17 +95,6 @@ class User(Model):
|
||||
table = 'user'
|
||||
|
||||
|
||||
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 GroupChat(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
chat_id = fields.BigIntField(index=True, unique=True)
|
||||
|
||||
@@ -7,6 +7,7 @@ from datetime import timedelta
|
||||
import typing as ty
|
||||
from olgram.utils.crypto import Cryptor
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
@@ -40,7 +41,7 @@ class OlgramSettings(AbstractSettings):
|
||||
|
||||
@classmethod
|
||||
def version(cls):
|
||||
return "0.7.3"
|
||||
return "0.5.0"
|
||||
|
||||
@classmethod
|
||||
@lru_cache
|
||||
@@ -160,6 +161,4 @@ TORTOISE_ORM = {
|
||||
"default_connection": "default",
|
||||
},
|
||||
},
|
||||
"use_tz": False,
|
||||
"timezone": "UTC"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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
|
||||
from aiogram.utils.exceptions import TelegramAPIError
|
||||
|
||||
from typing import Optional
|
||||
|
||||
@@ -41,23 +39,15 @@ 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 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")
|
||||
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")
|
||||
1374
poetry.lock
generated
1374
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
/usr/lib/python3.11/Tools/i18n/pygettext.py -d chinese -o locales/olgram.pot olgram/ server/
|
||||
/usr/lib/python3.10/Tools/i18n/pygettext.py -d chinese -o locales/olgram.pot olgram/ server/
|
||||
|
||||
141
server/custom.py
141
server/custom.py
@@ -1,5 +1,3 @@
|
||||
import asyncio
|
||||
|
||||
from aiogram import Bot as AioBot, Dispatcher
|
||||
from aiogram.dispatcher.webhook import WebhookRequestHandler
|
||||
from aiogram.dispatcher.webhook import SendMessage
|
||||
@@ -13,7 +11,7 @@ from tortoise.expressions import F
|
||||
import logging
|
||||
import typing as ty
|
||||
from olgram.settings import ServerSettings
|
||||
from olgram.models.models import Bot, GroupChat, BannedUser, BotStartMessage, BotSecondMessage, MailingUser
|
||||
from olgram.models.models import Bot, GroupChat, BannedUser, MailingUser
|
||||
from locales.locale import _, translators
|
||||
from server.inlines import inline_handler
|
||||
|
||||
@@ -26,12 +24,9 @@ _redis: ty.Optional[Redis] = None
|
||||
|
||||
|
||||
def _get_translator(message: types.Message) -> ty.Callable:
|
||||
try:
|
||||
if not message.from_user.locale:
|
||||
return _
|
||||
return translators.get(message.from_user.locale.language, _)
|
||||
except Exception:
|
||||
if not message.from_user.locale:
|
||||
return _
|
||||
return translators.get(message.from_user.locale.language, _)
|
||||
|
||||
|
||||
async def init_redis():
|
||||
@@ -43,11 +38,7 @@ def _message_unique_id(bot_id: int, message_id: int) -> str:
|
||||
return f"{bot_id}_{message_id}"
|
||||
|
||||
|
||||
def _tag_uid(bot_id: int, user_id: int) -> str:
|
||||
return f"tag_{bot_id}_{user_id}"
|
||||
|
||||
|
||||
def _thread_unique_id(bot_id: int, chat_id: int) -> str:
|
||||
def _thread_uniqie_id(bot_id: int, chat_id: int) -> str:
|
||||
return f"thread_{bot_id}_{chat_id}"
|
||||
|
||||
|
||||
@@ -84,7 +75,7 @@ def _on_security_policy(message: types.Message, bot):
|
||||
parse_mode="HTML")
|
||||
|
||||
|
||||
async def send_user_message(message: types.Message, super_chat_id: int, bot, tag: str = ""):
|
||||
async def send_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
"""Переслать сообщение от пользователя, добавлять к нему user info при необходимости"""
|
||||
if bot.enable_additional_info:
|
||||
user_info = _("Сообщение от пользователя ")
|
||||
@@ -92,97 +83,49 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot, tag
|
||||
if message.from_user.username:
|
||||
user_info += " | @" + message.from_user.username
|
||||
user_info += f" | #ID{message.from_user.id}"
|
||||
try:
|
||||
if message.from_user.locale:
|
||||
user_info += f" | lang: {message.from_user.locale}"
|
||||
except Exception:
|
||||
pass
|
||||
if message.forward_sender_name:
|
||||
user_info += f" | fwd: {message.forward_sender_name}"
|
||||
tag = await _redis.get(_tag_uid(bot.pk, message.from_user.id), encoding="utf-8")
|
||||
if tag:
|
||||
user_info += f" | tag: {tag}"
|
||||
|
||||
# Добавлять информацию в конец текста
|
||||
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
|
||||
new_message = await message.bot.send_message(super_chat_id, message.text + "\n\n" + user_info)
|
||||
else: # Не добавлять информацию в конец текста, информация отдельным сообщением
|
||||
new_message = await message.bot.send_message(super_chat_id, text=user_info)
|
||||
new_message_2 = await message.copy_to(super_chat_id, reply_to_message_id=new_message.message_id)
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
elif tag:
|
||||
# добавлять тег в конец текста
|
||||
if message.content_type == types.ContentType.TEXT and len(message.text) + len(tag) < 4093:
|
||||
new_message = await message.bot.send_message(super_chat_id, message.text + "\n\n" + tag)
|
||||
else:
|
||||
new_message = await message.bot.send_message(super_chat_id, text=tag)
|
||||
new_message_2 = await message.copy_to(super_chat_id, reply_to_message_id=new_message.message_id)
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id,
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
return new_message
|
||||
else:
|
||||
try:
|
||||
new_message = await message.forward(super_chat_id)
|
||||
except exceptions.MessageCantBeForwarded:
|
||||
new_message = await message.copy_to(super_chat_id)
|
||||
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id,
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
return new_message
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id,
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
return new_message
|
||||
|
||||
|
||||
async def send_to_superchat(is_super_group: bool, message: types.Message, super_chat_id: int, bot):
|
||||
"""Пересылка сообщения от пользователя оператору (логика потоков сообщений)"""
|
||||
if bot.enable_tags:
|
||||
tag = await _redis.get(_tag_uid(bot.pk, message.chat.id), encoding="utf-8")
|
||||
else:
|
||||
tag = ""
|
||||
if tag:
|
||||
tag = str(tag)
|
||||
|
||||
if is_super_group and bot.enable_threads:
|
||||
if bot.enable_thread_interrupt:
|
||||
thread_timeout = ServerSettings.thread_timeout_ms()
|
||||
else:
|
||||
thread_timeout = ServerSettings.redis_timeout_ms()
|
||||
thread_first_message = await _redis.get(_thread_unique_id(bot.pk, message.chat.id))
|
||||
thread_first_message = await _redis.get(_thread_uniqie_id(bot.pk, message.chat.id))
|
||||
if thread_first_message:
|
||||
# переслать в супер-чат, отвечая на предыдущее сообщение
|
||||
try:
|
||||
if tag:
|
||||
if message.content_type == types.ContentType.TEXT and len(message.text) + len(tag) < 4093:
|
||||
new_message = await message.bot.send_message(
|
||||
super_chat_id,
|
||||
message.text + "\n\n" + tag,
|
||||
reply_to_message_id=int(thread_first_message))
|
||||
else:
|
||||
new_message = await message.copy_to(super_chat_id,
|
||||
reply_to_message_id=int(thread_first_message))
|
||||
new_message_2 = await message.bot.send_message(
|
||||
super_chat_id, reply_to_message_id=new_message.message_id, text=tag)
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
|
||||
pexpire=thread_timeout)
|
||||
else:
|
||||
new_message = await message.copy_to(super_chat_id, reply_to_message_id=int(thread_first_message))
|
||||
new_message = await message.copy_to(super_chat_id, reply_to_message_id=int(thread_first_message))
|
||||
await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id,
|
||||
pexpire=thread_timeout)
|
||||
pexpire=ServerSettings.redis_timeout_ms())
|
||||
except exceptions.BadRequest:
|
||||
new_message = await send_user_message(message, super_chat_id, bot, tag)
|
||||
await _redis.set(
|
||||
_thread_unique_id(bot.pk, message.chat.id), new_message.message_id, pexpire=thread_timeout)
|
||||
new_message = await send_user_message(message, super_chat_id, bot)
|
||||
await _redis.set(_thread_uniqie_id(bot.pk, message.chat.id), new_message.message_id,
|
||||
pexpire=ServerSettings.thread_timeout_ms())
|
||||
else:
|
||||
# переслать супер-чат
|
||||
new_message = await send_user_message(message, super_chat_id, bot, tag)
|
||||
await _redis.set(_thread_unique_id(bot.pk, message.chat.id), new_message.message_id,
|
||||
pexpire=thread_timeout)
|
||||
new_message = await send_user_message(message, super_chat_id, bot)
|
||||
await _redis.set(_thread_uniqie_id(bot.pk, message.chat.id), new_message.message_id,
|
||||
pexpire=ServerSettings.thread_timeout_ms())
|
||||
else: # личные сообщения не поддерживают потоки сообщений: просто отправляем сообщение
|
||||
await send_user_message(message, super_chat_id, bot, tag)
|
||||
|
||||
|
||||
async def _increase_count(_bot):
|
||||
_bot.incoming_messages_count = F("incoming_messages_count") + 1
|
||||
await _bot.save(update_fields=["incoming_messages_count"])
|
||||
await send_user_message(message, super_chat_id, bot)
|
||||
|
||||
|
||||
async def handle_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
@@ -190,8 +133,9 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
_ = _get_translator(message)
|
||||
is_super_group = super_chat_id < 0
|
||||
|
||||
# Записать пользователя для рассылки, если она включена
|
||||
if bot.enable_mailing:
|
||||
asyncio.create_task(MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot))
|
||||
_, __ = await MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot)
|
||||
|
||||
# Проверить, не забанен ли пользователь
|
||||
banned = await bot.banned_users.filter(telegram_id=message.chat.id)
|
||||
@@ -211,26 +155,19 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
await send_to_superchat(is_super_group, message, super_chat_id, bot)
|
||||
except (exceptions.Unauthorized, exceptions.ChatNotFound):
|
||||
return SendMessage(chat_id=message.chat.id, text=_("Не удаётся связаться с владельцем бота"))
|
||||
except exceptions.RetryAfter:
|
||||
return SendMessage(chat_id=message.chat.id, text=_("Слишком много сообщений, подождите одну минуту"),
|
||||
reply_to_message_id=message.message_id)
|
||||
except exceptions.TelegramAPIError as err:
|
||||
_logger.error(f"(exception on forwarding) {err}")
|
||||
return
|
||||
|
||||
asyncio.create_task(_increase_count(bot))
|
||||
bot.incoming_messages_count = F("incoming_messages_count") + 1
|
||||
await bot.save(update_fields=["incoming_messages_count"])
|
||||
|
||||
# И отправить пользователю специальный текст, если он указан и если давно не отправляли
|
||||
if bot.second_text:
|
||||
send_auto = not await _redis.get(_last_message_uid(bot.pk, message.chat.id))
|
||||
await _redis.setex(_last_message_uid(bot.pk, message.chat.id), 60 * 60 * 3, 1)
|
||||
if send_auto or bot.enable_always_second_message:
|
||||
try:
|
||||
text_obj = await BotSecondMessage.get_or_none(bot=bot, locale=str(message.from_user.locale))
|
||||
except Exception:
|
||||
text_obj = None
|
||||
return SendMessage(chat_id=message.chat.id, text=text_obj.text if text_obj else bot.second_text,
|
||||
parse_mode="HTML")
|
||||
if send_auto:
|
||||
return SendMessage(chat_id=message.chat.id, text=bot.second_text, parse_mode="HTML")
|
||||
|
||||
|
||||
async def handle_operator_message(message: types.Message, super_chat_id: int, bot):
|
||||
@@ -265,15 +202,6 @@ async def handle_operator_message(message: types.Message, super_chat_id: int, bo
|
||||
else:
|
||||
await banned_user.delete()
|
||||
return SendMessage(chat_id=message.chat.id, text=_("Пользователь разбанен"))
|
||||
if bot.enable_tags:
|
||||
if message.text and message.text.startswith("/tag "):
|
||||
tag = message.text.replace("/tag ", "")[:20].strip()
|
||||
if tag:
|
||||
await _redis.set(_tag_uid(bot.pk, chat_id), tag, pexpire=ServerSettings.redis_timeout_ms())
|
||||
return SendMessage(chat_id=message.chat.id, text=_("Тег выставлен"))
|
||||
else:
|
||||
await _redis.delete(_tag_uid(bot.pk, chat_id))
|
||||
return SendMessage(chat_id=message.chat.id, text=_("Тег убран"))
|
||||
|
||||
try:
|
||||
await message.copy_to(chat_id)
|
||||
@@ -287,12 +215,7 @@ 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))
|
||||
try:
|
||||
await message.forward(super_chat_id)
|
||||
except exceptions.MessageCantBeForwarded:
|
||||
await message.copy_to(super_chat_id)
|
||||
await message.forward(super_chat_id)
|
||||
# И отправить пользователю специальный текст, если он указан
|
||||
if bot.second_text:
|
||||
return SendMessage(chat_id=message.chat.id, text=bot.second_text, parse_mode="HTML")
|
||||
@@ -304,11 +227,7 @@ async def message_handler(message: types.Message, *args, **kwargs):
|
||||
|
||||
if message.text and message.text == "/start":
|
||||
# На команду start нужно ответить, не пересылая сообщение никуда
|
||||
try:
|
||||
text_obj = await BotStartMessage.get_or_none(bot=bot, locale=str(message.from_user.locale))
|
||||
except Exception:
|
||||
text_obj = None
|
||||
text = text_obj.text if text_obj else bot.start_text
|
||||
text = bot.start_text
|
||||
if bot.enable_olgram_text:
|
||||
text += _(ServerSettings.append_text())
|
||||
return SendMessage(chat_id=message.chat.id, text=text, parse_mode="HTML")
|
||||
|
||||
Reference in New Issue
Block a user