mirror of
https://github.com/civsocit/olgram.git
synced 2025-12-17 15:26:17 +00:00
Compare commits
9 Commits
mailing
...
acb62fb644
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb62fb644 | ||
|
|
ae45374490 | ||
|
|
0881c86349 | ||
|
|
82b68d1d9f | ||
|
|
a5e6fbce34 | ||
|
|
28ed36ffeb | ||
|
|
601c16622d | ||
|
|
9e46041d0f | ||
|
|
f41e17a15c |
25
.readthedocs.yaml
Normal file
25
.readthedocs.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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,6 +1,7 @@
|
||||
О проекте
|
||||
===================================
|
||||
|
||||
|
||||
Зачем нужен Olgram
|
||||
------------
|
||||
|
||||
|
||||
@@ -4,21 +4,25 @@
|
||||
Донаты
|
||||
----------------
|
||||
|
||||
На рекламу проекта, аренду сервера и пиццу
|
||||
На аренду сервера для этого проекта
|
||||
|
||||
Bitcoin:
|
||||
``bc1qlq7cm5chc8flr3fy8ewk967aknq3dwmxtwn9hl``
|
||||
|
||||
Monero:
|
||||
``886AQ8tCVcQKp21xsuSLkfDdTAdtCFH1jR58Tw9MsaxFXoZ7YRHXx1cQcUfUnDX6hySzPsQEVt6RWPn3sXH9QUmwCr3oVqB``
|
||||
Litecoin:
|
||||
``LTC1QXAJSVZ0LW44AA5NYTUCH8CP2G8X7A4CDASE4Y7``
|
||||
|
||||
Dash:
|
||||
``XqxetfWzr5n4Ms1TxMbdEEeHGe8CaMdmb6``
|
||||
Как убрать "Этот бот создан с помощью ..."
|
||||
----------------
|
||||
Напишите нам на `@civsocit_feedback_bot <https://t.me/civsocit_feedback_bot>`_.
|
||||
|
||||
|
||||
История изменений
|
||||
----------------
|
||||
|
||||
- `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 = '2022, Civsocit'
|
||||
copyright = '2024, Civsocit'
|
||||
author = 'civsocit'
|
||||
|
||||
release = '0.1'
|
||||
|
||||
@@ -50,15 +50,20 @@ Olgram пересылает сообщения так, чтобы сообщен
|
||||
При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
|
||||
не успеваете обрабатывать входящие сообщения.
|
||||
|
||||
.. _mailing:
|
||||
|
||||
Рассылка
|
||||
---------------
|
||||
.. _always_second_message:
|
||||
|
||||
После включения этой опции ваш бот будет запоминать всех пользователей, которые пишут в ваш бот.
|
||||
Вы сможете запустить рассылку по этим пользователям.
|
||||
Использовать автоответчик всегда
|
||||
--------------------------------
|
||||
|
||||
.. note::
|
||||
По-умолчанию автоответчик отвечает только на первое сообщение в диалоге с пользователем. Чтобы автоответчик отвечал на
|
||||
КАЖДОЕ входящее сообщение, включите эту опцию.
|
||||
|
||||
Включение этой опции меняет текст политики конфиденциальности вашего feedback бота (команда /security_policy)
|
||||
и может отпугнуть некоторых пользователей. Не включайте эту опцию без необходимости.
|
||||
|
||||
.. thread_interrupt:
|
||||
|
||||
Прерывать поток
|
||||
--------------------------------
|
||||
|
||||
По-умолчанию поток сообщений от одного пользователя прерывается каждые 24 часа. Без этой опции поток сообщений не
|
||||
прерывается никогда.
|
||||
|
||||
@@ -68,6 +68,13 @@ BotFather - это официальный бот Telegram, создающий д
|
||||
|
||||
Теперь просто отправьте новый текст приветствия.
|
||||
|
||||
.. note::
|
||||
|
||||
Чтобы настроить особый текст приветствия для, например, русскоязычных пользователей (т.е. тех пользователей, у
|
||||
которых в настройках Telegram выставлена русская локализация), нажмите кнопку "Руссикй 🇷🇺" и только потом отправьте
|
||||
текст приветствия. Чтобы отредактировать текст приветствия для всех остальных языков, нажмите "[все языки]".
|
||||
|
||||
|
||||
Как привязать бота к групповому чату
|
||||
------------------------------------
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ msgid ""
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" <pre>"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
@@ -711,11 +711,11 @@ msgstr "(Re)launch the bot"
|
||||
msgid "Политика конфиденциальности"
|
||||
msgstr "Privacy Policy"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "Этот бот создан с помощью @OlgramBot"
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "This bot was created using @OlgramBot"
|
||||
msgid ""
|
||||
"\n"
|
||||
"\n"
|
||||
"Этот бот создан с помощью @OlgramBot"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"\n"
|
||||
"This bot was created using @OlgramBot"
|
||||
|
||||
@@ -407,9 +407,7 @@ msgid ""
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -419,9 +417,7 @@ msgstr ""
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Поточний текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Надішліть повідомлення, щоб змінити текст.\n"
|
||||
" \n"
|
||||
" "
|
||||
@@ -462,9 +458,7 @@ msgid ""
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -723,11 +717,11 @@ msgstr "(Пере) запустити бота"
|
||||
msgid "Политика конфиденциальности"
|
||||
msgstr "Політика конфіденційності"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "Этот бот создан с помощью @OlgramBot"
|
||||
#~ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "\n"
|
||||
#~ "Цей бот створено за допомогою @OlgramBot"
|
||||
msgid ""
|
||||
"\n"
|
||||
"\n"
|
||||
"Этот бот создан с помощью @OlgramBot"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"\n"
|
||||
"Цей бот створено за допомогою @OlgramBot"
|
||||
|
||||
@@ -306,9 +306,7 @@ msgid ""
|
||||
" команду /start\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -317,9 +315,7 @@ msgstr ""
|
||||
" /start\n"
|
||||
"\n"
|
||||
" 目前的文本。\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" 发送消息,改变文本。\n"
|
||||
" "
|
||||
|
||||
@@ -359,9 +355,7 @@ msgid ""
|
||||
"отключено.\n"
|
||||
"\n"
|
||||
" Текущий текст:\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>\n"
|
||||
" <pre>{1}</pre>\n"
|
||||
" Отправьте сообщение, чтобы изменить текст.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
@@ -370,9 +364,7 @@ msgstr ""
|
||||
"默认情况下,它是禁用的。\n"
|
||||
"\n"
|
||||
" 目前的文本。\n"
|
||||
" <pre>\n"
|
||||
" {1}\n"
|
||||
" </pre>。\n"
|
||||
" <pre>{1}</pre>。\n"
|
||||
" 发送消息,改变文本。\n"
|
||||
" "
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"""
|
||||
Здесь работа с конкретным ботом
|
||||
"""
|
||||
from asyncio import sleep
|
||||
from datetime import datetime
|
||||
from aiogram import types
|
||||
from aiogram.utils import exceptions
|
||||
from aiogram.utils.exceptions import TelegramAPIError, Unauthorized
|
||||
from aiogram import Bot as AioBot
|
||||
from olgram.models.models import Bot
|
||||
from olgram.utils.mix import send_stored_message
|
||||
from olgram.models.models import Bot, BotStartMessage, BotSecondMessage
|
||||
from server.server import unregister_token
|
||||
from locales.locale import _
|
||||
|
||||
@@ -18,38 +15,50 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery):
|
||||
"""
|
||||
try:
|
||||
await unregister_token(bot.decrypted_token())
|
||||
except exceptions.Unauthorized:
|
||||
except Unauthorized:
|
||||
# Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы
|
||||
pass
|
||||
await bot.delete()
|
||||
await call.answer(_("Бот удалён"))
|
||||
try:
|
||||
await call.message.delete()
|
||||
except exceptions.TelegramAPIError:
|
||||
except TelegramAPIError:
|
||||
pass
|
||||
|
||||
|
||||
async def reset_bot_text(bot: Bot, call: types.CallbackQuery):
|
||||
async def reset_bot_text(bot: Bot, call: types.CallbackQuery, state):
|
||||
"""
|
||||
Пользователь решил сбросить текст бота к default
|
||||
:param bot:
|
||||
:param call:
|
||||
:return:
|
||||
"""
|
||||
bot.start_text = bot._meta.fields_map['start_text'].default
|
||||
await bot.save()
|
||||
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()
|
||||
await call.answer(_("Текст сброшен"))
|
||||
|
||||
|
||||
async def reset_bot_second_text(bot: Bot, call: types.CallbackQuery):
|
||||
async def reset_bot_second_text(bot: Bot, call: types.CallbackQuery, state):
|
||||
"""
|
||||
Пользователь решил сбросить second text бота
|
||||
:param bot:
|
||||
:param call:
|
||||
:return:
|
||||
"""
|
||||
bot.second_text = bot._meta.fields_map['second_text'].default
|
||||
await bot.save()
|
||||
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()
|
||||
await call.answer(_("Текст сброшен"))
|
||||
|
||||
|
||||
@@ -75,7 +84,7 @@ async def select_chat(bot: Bot, call: types.CallbackQuery, chat: str):
|
||||
try:
|
||||
await chat.delete()
|
||||
await a_bot.leave_chat(chat.chat_id)
|
||||
except exceptions.TelegramAPIError:
|
||||
except TelegramAPIError:
|
||||
pass
|
||||
await call.answer(_("Бот вышел из чатов"))
|
||||
await a_bot.session.close()
|
||||
@@ -100,6 +109,16 @@ 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
|
||||
@@ -109,35 +128,3 @@ async def olgram_text(bot: Bot, call: types.CallbackQuery):
|
||||
async def antiflood(bot: Bot, call: types.CallbackQuery):
|
||||
bot.enable_antiflood = not bot.enable_antiflood
|
||||
await bot.save(update_fields=["enable_antiflood"])
|
||||
|
||||
|
||||
async def mailing(bot: Bot, call: types.CallbackQuery):
|
||||
bot.enable_mailing = not bot.enable_mailing
|
||||
await bot.save(update_fields=["enable_mailing"])
|
||||
|
||||
|
||||
async def go_mailing(bot: Bot, context) -> int:
|
||||
users = await bot.mailing_users
|
||||
a_bot = AioBot(bot.decrypted_token())
|
||||
|
||||
count = 0
|
||||
|
||||
print(f"start mailing {context}")
|
||||
|
||||
for user in users:
|
||||
bot.last_mailing_at = datetime.now()
|
||||
await bot.save(update_fields=["last_mailing_at"])
|
||||
try:
|
||||
await sleep(0.05)
|
||||
try:
|
||||
await send_stored_message(context, a_bot, user.telegram_id)
|
||||
except exceptions.RetryAfter as err:
|
||||
await sleep(err.timeout)
|
||||
await send_stored_message(context, a_bot, user.telegram_id)
|
||||
count += 1
|
||||
except (exceptions.ChatNotFound, exceptions.BotBlocked, exceptions.UserDeactivated):
|
||||
await user.delete()
|
||||
except exceptions.TelegramAPIError:
|
||||
pass
|
||||
|
||||
return count
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
from olgram.router import dp
|
||||
|
||||
from aiogram import types, Bot as AioBot
|
||||
from olgram.models.models import Bot, User, DefaultAnswer
|
||||
from olgram.models.models import Bot, User, DefaultAnswer, BotStartMessage, BotSecondMessage
|
||||
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.utils.mix import edit_or_create, button_text_limit, wrap
|
||||
from olgram.commands import bot_actions
|
||||
from locales.locale import _
|
||||
|
||||
import typing as ty
|
||||
|
||||
|
||||
menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat')
|
||||
|
||||
empty = "0"
|
||||
@@ -127,12 +127,6 @@ async def send_bot_menu(bot: Bot, call: types.CallbackQuery):
|
||||
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings",
|
||||
chat=empty))
|
||||
)
|
||||
if bot.enable_mailing:
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Рассылка"),
|
||||
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="go_mailing",
|
||||
chat=empty))
|
||||
)
|
||||
|
||||
await edit_or_create(call, dedent(_("""
|
||||
Управление ботом @{0}.
|
||||
@@ -179,8 +173,15 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
||||
chat=empty))
|
||||
)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("Рассылка"),
|
||||
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="mailing",
|
||||
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="thread_interrupt",
|
||||
chat=empty))
|
||||
)
|
||||
is_promo = await bot.is_promo()
|
||||
@@ -200,13 +201,16 @@ 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 _("выключен")
|
||||
mailing_turn = _("включена") if bot.enable_mailing else _("выключена")
|
||||
enable_always_second_message = _("включён") if bot.enable_always_second_message else _("выключен")
|
||||
thread_interrupt = _("да") if bot.enable_thread_interrupt 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#mailing">Рассылка</a>: <b>{3}</b>
|
||||
""")).format(thread_turn, info_turn, antiflood_turn, mailing_turn)
|
||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#always_second_message">Автоответчик всегда</a>: <b>{3}
|
||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#thread_interrupt">Прерывать поток</a>: <b>{4}
|
||||
</b>
|
||||
""")).format(thread_turn, info_turn, antiflood_turn, enable_always_second_message, thread_interrupt)
|
||||
|
||||
if is_promo:
|
||||
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
|
||||
@@ -215,9 +219,26 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
||||
await edit_or_create(call, text, reply_markup=keyboard, parse_mode="HTML")
|
||||
|
||||
|
||||
async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, chat_id: ty.Optional[int] = None):
|
||||
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):
|
||||
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=_("<< Завершить редактирование"),
|
||||
@@ -228,47 +249,40 @@ 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.insert(
|
||||
keyboard.row(
|
||||
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
|
||||
|
||||
Текущий текст:
|
||||
<pre>
|
||||
{1}
|
||||
</pre>
|
||||
Текущий текст{2}:
|
||||
<pre>{1}</pre>
|
||||
Отправьте сообщение, чтобы изменить текст.
|
||||
"""))
|
||||
text = text.format(bot.name, bot.start_text)
|
||||
if call:
|
||||
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||
else:
|
||||
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
|
||||
|
||||
|
||||
async def send_bot_mailing_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
|
||||
chat_id: ty.Optional[int] = None):
|
||||
if call:
|
||||
await call.answer()
|
||||
keyboard = types.InlineKeyboardMarkup(row_width=1)
|
||||
keyboard.insert(
|
||||
types.InlineKeyboardButton(text=_("<< Отменить рассылку"),
|
||||
callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty, chat=empty))
|
||||
)
|
||||
|
||||
text = dedent(_("""
|
||||
Напишите сообщение, которое нужно разослать всем подписчикам вашего бота @{0}.
|
||||
У сообщения будет до {1} получателей.
|
||||
Учтите, что
|
||||
1. Рассылается только одно сообщение за раз (в т.ч. только одна картинка)
|
||||
2. Когда рассылка запущена, её нельзя отменить
|
||||
"""))
|
||||
text = text.format(bot.name, len(await bot.mailing_users))
|
||||
text = text.format(bot.name,
|
||||
prepared_languages.get(lang, bot.start_text),
|
||||
_(" (для языка {0})").format(languages[lang]) if lang != "none" else "")
|
||||
if call:
|
||||
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||
else:
|
||||
@@ -301,9 +315,15 @@ 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):
|
||||
chat_id: ty.Optional[int] = None, state=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=_("<< Завершить редактирование"),
|
||||
@@ -324,18 +344,35 @@ 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} \
|
||||
автоматически. По умолчанию оно отключено.
|
||||
|
||||
Текущий текст:
|
||||
<pre>
|
||||
{1}
|
||||
</pre>
|
||||
Текущий текст{2}:
|
||||
<pre>{1}</pre>
|
||||
Отправьте сообщение, чтобы изменить текст.
|
||||
"""))
|
||||
text = text.format(bot.name, bot.second_text if bot.second_text else _("(отключено)"))
|
||||
text = text.format(bot.name,
|
||||
prepared_languages.get(lang, bot.second_text or _("отключено")),
|
||||
_(" (для языка {0})").format(languages[lang]) if lang != "none" else "")
|
||||
if call:
|
||||
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||
else:
|
||||
@@ -383,63 +420,43 @@ 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)
|
||||
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)
|
||||
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)
|
||||
|
||||
|
||||
@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)
|
||||
bot.second_text = message.html_text
|
||||
await bot.save()
|
||||
await send_bot_second_text_menu(bot, chat_id=message.chat.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)
|
||||
|
||||
|
||||
@dp.message_handler(state="wait_template", content_types="text", regexp="^[^/](.+)?") # Not command
|
||||
@@ -503,20 +520,11 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
||||
return await send_bot_statistic_menu(bot, call)
|
||||
if operation == "settings":
|
||||
return await send_bot_settings_menu(bot, call)
|
||||
if operation == "go_mailing":
|
||||
if bot.last_mailing_at and bot.last_mailing_at >= datetime.now() - timedelta(minutes=5):
|
||||
return await call.answer(_("Рассылка была совсем недавно, подождите немного"), show_alert=True)
|
||||
if not await bot.mailing_users:
|
||||
return await call.answer(_("Нет пользователей для рассылки"))
|
||||
await state.set_state("wait_mailing_text")
|
||||
async with state.proxy() as proxy:
|
||||
proxy["bot_id"] = bot.id
|
||||
return await send_bot_mailing_menu(bot, call)
|
||||
if operation == "text":
|
||||
await state.set_state("wait_start_text")
|
||||
async with state.proxy() as proxy:
|
||||
proxy["bot_id"] = bot.id
|
||||
return await send_bot_text_menu(bot, call)
|
||||
return await send_bot_text_menu(bot, call, state=state)
|
||||
|
||||
if level == "3":
|
||||
if operation == "delete_yes":
|
||||
@@ -529,40 +537,41 @@ 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 == "mailing":
|
||||
await bot_actions.mailing(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 == "thread_interrupt":
|
||||
await bot_actions.thread_interrupt(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)
|
||||
return await send_bot_text_menu(bot, call)
|
||||
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)
|
||||
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)
|
||||
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))
|
||||
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)
|
||||
if operation == "reset_second_text":
|
||||
await bot_actions.reset_bot_second_text(bot, call)
|
||||
return await send_bot_second_text_menu(bot, call)
|
||||
await bot_actions.reset_bot_second_text(bot, call, state)
|
||||
return await send_bot_second_text_menu(bot, call, state=state)
|
||||
if operation == "templates":
|
||||
await state.set_state("wait_template")
|
||||
async with state.proxy() as proxy:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "last_mailing_at" TIMESTAMPTZ;
|
||||
ALTER TABLE "bot" ADD "enable_mailing" BOOL NOT NULL DEFAULT False;
|
||||
CREATE TABLE IF NOT EXISTS "mailinguser" (
|
||||
"id" BIGSERIAL NOT NULL PRIMARY KEY,
|
||||
"telegram_id" BIGINT NOT NULL,
|
||||
"bot_id" INT NOT NULL REFERENCES "bot" ("id") ON DELETE CASCADE,
|
||||
CONSTRAINT "uid_mailinguser_bot_id_906a76" UNIQUE ("bot_id", "telegram_id")
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS "idx_mailinguser_telegra_55de60" ON "mailinguser" ("telegram_id");;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "last_mailing_at";
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_mailing";
|
||||
DROP TABLE IF EXISTS "mailinguser";
|
||||
9
olgram/migrations/models/15_20240112035625_update.sql
Normal file
9
olgram/migrations/models/15_20240112035625_update.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- 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";
|
||||
4
olgram/migrations/models/16_20240112040146_update.sql
Normal file
4
olgram/migrations/models/16_20240112040146_update.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot_start_message" ADD "text" TEXT NOT NULL;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot_start_message" DROP COLUMN "text";
|
||||
10
olgram/migrations/models/17_20240112045126_update.sql
Normal file
10
olgram/migrations/models/17_20240112045126_update.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- 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";
|
||||
4
olgram/migrations/models/18_20240217035146_update.sql
Normal file
4
olgram/migrations/models/18_20240217035146_update.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_always_second_message" BOOL NOT NULL DEFAULT False;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_always_second_message";
|
||||
4
olgram/migrations/models/19_20240301193152_update.sql
Normal file
4
olgram/migrations/models/19_20240301193152_update.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- upgrade --
|
||||
ALTER TABLE "bot" ADD "enable_thread_interrupt" BOOL NOT NULL DEFAULT True;
|
||||
-- downgrade --
|
||||
ALTER TABLE "bot" DROP COLUMN "enable_thread_interrupt";
|
||||
@@ -46,8 +46,8 @@ class Bot(Model):
|
||||
enable_additional_info = fields.BooleanField(default=False)
|
||||
enable_olgram_text = fields.BooleanField(default=True)
|
||||
enable_antiflood = fields.BooleanField(default=False)
|
||||
enable_mailing = fields.BooleanField(default=False)
|
||||
last_mailing_at = fields.DatetimeField(null=True, default=None)
|
||||
enable_always_second_message = fields.BooleanField(default=False)
|
||||
enable_thread_interrupt = fields.BooleanField(default=True)
|
||||
|
||||
def decrypted_token(self):
|
||||
cryptor = DatabaseSettings.cryptor()
|
||||
@@ -72,15 +72,26 @@ class Bot(Model):
|
||||
table = 'bot'
|
||||
|
||||
|
||||
class MailingUser(Model):
|
||||
id = fields.BigIntField(pk=True)
|
||||
telegram_id = fields.BigIntField(index=True)
|
||||
|
||||
bot = fields.ForeignKeyField("models.Bot", related_name="mailing_users", on_delete=fields.relational.CASCADE)
|
||||
class 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=5)
|
||||
text = fields.TextField()
|
||||
|
||||
class Meta:
|
||||
table = 'mailinguser'
|
||||
unique_together = (("bot", "telegram_id"), )
|
||||
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'
|
||||
|
||||
|
||||
class User(Model):
|
||||
|
||||
@@ -41,7 +41,7 @@ class OlgramSettings(AbstractSettings):
|
||||
|
||||
@classmethod
|
||||
def version(cls):
|
||||
return "0.5.0"
|
||||
return "0.7.1"
|
||||
|
||||
@classmethod
|
||||
@lru_cache
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup
|
||||
from aiogram import types, Bot as AioBot
|
||||
from aiogram.utils.exceptions import TelegramAPIError
|
||||
|
||||
from typing import Optional
|
||||
@@ -31,23 +30,3 @@ def wrap(data: str, max_len: int) -> str:
|
||||
|
||||
def button_text_limit(data: str) -> str:
|
||||
return wrap(data, 30)
|
||||
|
||||
|
||||
async def send_stored_message(storage: dict, bot: AioBot, chat_id: int):
|
||||
content_type = storage["mailing_content_type"]
|
||||
if content_type == types.ContentType.TEXT:
|
||||
return await bot.send_message(chat_id, storage["mailing_text"], parse_mode="HTML")
|
||||
if content_type == types.ContentType.LOCATION:
|
||||
return await bot.send_location(chat_id, storage["mailing_location"][0], storage["mailing_location"][1])
|
||||
if content_type == types.ContentType.AUDIO:
|
||||
return await bot.send_audio(chat_id, audio=storage["mailing_audio"], caption=storage.get("mailing_caption"))
|
||||
if content_type == types.ContentType.DOCUMENT:
|
||||
return await bot.send_document(chat_id, document=storage["mailing_document"],
|
||||
caption=storage.get("mailing_caption"))
|
||||
if content_type == types.ContentType.PHOTO:
|
||||
return await bot.send_photo(chat_id, photo=storage["mailing_photo"],
|
||||
caption=storage.get("mailing_caption"))
|
||||
if content_type == types.ContentType.VIDEO:
|
||||
return await bot.send_video(chat_id, video=storage["mailing_video"],
|
||||
caption=storage.get("mailing_caption"))
|
||||
raise NotImplementedError("Mailing, unknown content type")
|
||||
1378
poetry.lock
generated
1378
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
/usr/lib/python3.10/Tools/i18n/pygettext.py -d chinese -o locales/olgram.pot olgram/ server/
|
||||
/usr/lib/python3.11/Tools/i18n/pygettext.py -d chinese -o locales/olgram.pot olgram/ server/
|
||||
|
||||
@@ -11,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, MailingUser
|
||||
from olgram.models.models import Bot, GroupChat, BannedUser, BotStartMessage, BotSecondMessage
|
||||
from locales.locale import _, translators
|
||||
from server.inlines import inline_handler
|
||||
|
||||
@@ -55,20 +55,15 @@ def _on_security_policy(message: types.Message, bot):
|
||||
text = _("<b>Политика конфиденциальности</b>\n\n"
|
||||
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд "
|
||||
"/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом "
|
||||
"удаляется из кеша. Этот идентификатор используется для общения с оператором.\n\n")
|
||||
"удаляется из кеша. Этот идентификатор используется только для общения с оператором; боты Olgram "
|
||||
"не делают массовых рассылок.\n\n")
|
||||
if bot.enable_additional_info:
|
||||
text += _("При отправке сообщения (кроме команд /start и /security_policy) оператор <b>видит</b> ваши имя "
|
||||
"пользователя, @username и идентификатор пользователя в силу настроек, которые оператор указал при "
|
||||
"создании бота.\n\n")
|
||||
"создании бота.")
|
||||
else:
|
||||
text += _("В зависимости от ваших настроек конфиденциальности Telegram, оператор может видеть ваш username, "
|
||||
"имя пользователя и другую информацию.\n\n")
|
||||
|
||||
if bot.enable_mailing:
|
||||
text += _("В этом боте включена массовая рассылка в силу настроек, которые оператор указал при создании бота. "
|
||||
"Ваш идентификатор пользователя может быть записан в базу данных на долгое время")
|
||||
else:
|
||||
text += _("В этом боте нет массовой рассылки сообщений")
|
||||
"имя пользователя и другую информацию.")
|
||||
|
||||
return SendMessage(chat_id=message.chat.id,
|
||||
text=text,
|
||||
@@ -83,47 +78,54 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
if message.from_user.username:
|
||||
user_info += " | @" + message.from_user.username
|
||||
user_info += f" | #ID{message.from_user.id}"
|
||||
if message.from_user.locale:
|
||||
user_info += f" | lang: {message.from_user.locale}"
|
||||
if message.forward_sender_name:
|
||||
user_info += f" | fwd: {message.forward_sender_name}"
|
||||
|
||||
# Добавлять информацию в конец текста
|
||||
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())
|
||||
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 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_uniqie_id(bot.pk, message.chat.id))
|
||||
if thread_first_message:
|
||||
# переслать в супер-чат, отвечая на предыдущее сообщение
|
||||
try:
|
||||
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=ServerSettings.redis_timeout_ms())
|
||||
pexpire=thread_timeout)
|
||||
except exceptions.BadRequest:
|
||||
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())
|
||||
await _redis.set(
|
||||
_thread_uniqie_id(bot.pk, message.chat.id), new_message.message_id, pexpire=thread_timeout)
|
||||
else:
|
||||
# переслать супер-чат
|
||||
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())
|
||||
pexpire=thread_timeout)
|
||||
else: # личные сообщения не поддерживают потоки сообщений: просто отправляем сообщение
|
||||
await send_user_message(message, super_chat_id, bot)
|
||||
|
||||
@@ -133,10 +135,6 @@ 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:
|
||||
_, __ = await MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot)
|
||||
|
||||
# Проверить, не забанен ли пользователь
|
||||
banned = await bot.banned_users.filter(telegram_id=message.chat.id)
|
||||
if banned:
|
||||
@@ -166,8 +164,10 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
|
||||
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:
|
||||
return SendMessage(chat_id=message.chat.id, text=bot.second_text, parse_mode="HTML")
|
||||
if send_auto or bot.enable_always_second_message:
|
||||
text_obj = await BotSecondMessage.get_or_none(bot=bot, locale=str(message.from_user.locale))
|
||||
return SendMessage(chat_id=message.chat.id, text=text_obj.text if text_obj else bot.second_text,
|
||||
parse_mode="HTML")
|
||||
|
||||
|
||||
async def handle_operator_message(message: types.Message, super_chat_id: int, bot):
|
||||
@@ -227,7 +227,8 @@ async def message_handler(message: types.Message, *args, **kwargs):
|
||||
|
||||
if message.text and message.text == "/start":
|
||||
# На команду start нужно ответить, не пересылая сообщение никуда
|
||||
text = bot.start_text
|
||||
text_obj = await BotStartMessage.get_or_none(bot=bot, locale=str(message.from_user.locale))
|
||||
text = text_obj.text if text_obj else 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