mailing first iteration

This commit is contained in:
walker 2022-11-06 04:32:32 +04:00
parent bb1456dda1
commit 340246b937
6 changed files with 70 additions and 6 deletions

View File

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

View File

@ -106,3 +106,8 @@ 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"])

View File

@ -172,6 +172,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="mailing",
chat=empty))
)
is_promo = await bot.is_promo()
if is_promo:
keyboard.insert(
@ -189,11 +194,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 _("выключен")
mailing_turn = _("включена") if bot.enable_mailing 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>
""")).format(thread_turn, info_turn, antiflood_turn)
<a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{3}</b>
""")).format(thread_turn, info_turn, antiflood_turn, mailing_turn)
if is_promo:
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
@ -440,6 +447,9 @@ 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)

View File

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

View File

@ -46,6 +46,8 @@ class Bot(Model):
enable_additional_info = fields.BooleanField(default=False)
enable_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)
def decrypted_token(self):
cryptor = DatabaseSettings.cryptor()
@ -70,6 +72,17 @@ 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 Meta:
table = 'mailinguser'
unique_together = (("bot", "telegram_id"), )
class User(Model):
id = fields.IntField(pk=True)
telegram_id = fields.BigIntField(index=True, unique=True)

View File

@ -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
from olgram.models.models import Bot, GroupChat, BannedUser, MailingUser
from locales.locale import _, translators
from server.inlines import inline_handler
@ -55,15 +55,20 @@ def _on_security_policy(message: types.Message, bot):
text = _("<b>Политика конфиденциальности</b>\n\n"
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд "
"/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом "
"удаляется из кеша. Этот идентификатор используется только для общения с оператором; боты Olgram "
"не делают массовых рассылок.\n\n")
"удаляется из кеша. Этот идентификатор используется для общения с оператором.\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,
@ -128,6 +133,10 @@ 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: