mirror of
https://github.com/civsocit/olgram.git
synced 2025-12-16 21:56:18 +00:00
Compare commits
3 Commits
bb1456dda1
...
mailing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58f551c77d | ||
|
|
696bc5368b | ||
|
|
340246b937 |
@@ -49,3 +49,16 @@ Olgram пересылает сообщения так, чтобы сообщен
|
|||||||
|
|
||||||
При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
|
При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
|
||||||
не успеваете обрабатывать входящие сообщения.
|
не успеваете обрабатывать входящие сообщения.
|
||||||
|
|
||||||
|
.. _mailing:
|
||||||
|
|
||||||
|
Рассылка
|
||||||
|
---------------
|
||||||
|
|
||||||
|
После включения этой опции ваш бот будет запоминать всех пользователей, которые пишут в ваш бот.
|
||||||
|
Вы сможете запустить рассылку по этим пользователям.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Включение этой опции меняет текст политики конфиденциальности вашего feedback бота (команда /security_policy)
|
||||||
|
и может отпугнуть некоторых пользователей. Не включайте эту опцию без необходимости.
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
"""
|
"""
|
||||||
Здесь работа с конкретным ботом
|
Здесь работа с конкретным ботом
|
||||||
"""
|
"""
|
||||||
|
from asyncio import sleep
|
||||||
|
from datetime import datetime
|
||||||
from aiogram import types
|
from aiogram import types
|
||||||
from aiogram.utils.exceptions import TelegramAPIError, Unauthorized
|
from aiogram.utils import exceptions
|
||||||
from aiogram import Bot as AioBot
|
from aiogram import Bot as AioBot
|
||||||
from olgram.models.models import Bot
|
from olgram.models.models import Bot
|
||||||
|
from olgram.utils.mix import send_stored_message
|
||||||
from server.server import unregister_token
|
from server.server import unregister_token
|
||||||
from locales.locale import _
|
from locales.locale import _
|
||||||
|
|
||||||
@@ -15,14 +18,14 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await unregister_token(bot.decrypted_token())
|
await unregister_token(bot.decrypted_token())
|
||||||
except Unauthorized:
|
except exceptions.Unauthorized:
|
||||||
# Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы
|
# Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы
|
||||||
pass
|
pass
|
||||||
await bot.delete()
|
await bot.delete()
|
||||||
await call.answer(_("Бот удалён"))
|
await call.answer(_("Бот удалён"))
|
||||||
try:
|
try:
|
||||||
await call.message.delete()
|
await call.message.delete()
|
||||||
except TelegramAPIError:
|
except exceptions.TelegramAPIError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +75,7 @@ async def select_chat(bot: Bot, call: types.CallbackQuery, chat: str):
|
|||||||
try:
|
try:
|
||||||
await chat.delete()
|
await chat.delete()
|
||||||
await a_bot.leave_chat(chat.chat_id)
|
await a_bot.leave_chat(chat.chat_id)
|
||||||
except TelegramAPIError:
|
except exceptions.TelegramAPIError:
|
||||||
pass
|
pass
|
||||||
await call.answer(_("Бот вышел из чатов"))
|
await call.answer(_("Бот вышел из чатов"))
|
||||||
await a_bot.session.close()
|
await a_bot.session.close()
|
||||||
@@ -106,3 +109,35 @@ async def olgram_text(bot: Bot, call: types.CallbackQuery):
|
|||||||
async def antiflood(bot: Bot, call: types.CallbackQuery):
|
async def antiflood(bot: Bot, call: types.CallbackQuery):
|
||||||
bot.enable_antiflood = not bot.enable_antiflood
|
bot.enable_antiflood = not bot.enable_antiflood
|
||||||
await bot.save(update_fields=["enable_antiflood"])
|
await bot.save(update_fields=["enable_antiflood"])
|
||||||
|
|
||||||
|
|
||||||
|
async def mailing(bot: Bot, call: types.CallbackQuery):
|
||||||
|
bot.enable_mailing = not bot.enable_mailing
|
||||||
|
await bot.save(update_fields=["enable_mailing"])
|
||||||
|
|
||||||
|
|
||||||
|
async def go_mailing(bot: Bot, context) -> int:
|
||||||
|
users = await bot.mailing_users
|
||||||
|
a_bot = AioBot(bot.decrypted_token())
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
print(f"start mailing {context}")
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
bot.last_mailing_at = datetime.now()
|
||||||
|
await bot.save(update_fields=["last_mailing_at"])
|
||||||
|
try:
|
||||||
|
await sleep(0.05)
|
||||||
|
try:
|
||||||
|
await send_stored_message(context, a_bot, user.telegram_id)
|
||||||
|
except exceptions.RetryAfter as err:
|
||||||
|
await sleep(err.timeout)
|
||||||
|
await send_stored_message(context, a_bot, user.telegram_id)
|
||||||
|
count += 1
|
||||||
|
except (exceptions.ChatNotFound, exceptions.BotBlocked, exceptions.UserDeactivated):
|
||||||
|
await user.delete()
|
||||||
|
except exceptions.TelegramAPIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return count
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ async def info(message: types.Message, state: FSMContext):
|
|||||||
_("Входящих сообщений у всех ботов: {0}\n").format(income_messages) +
|
_("Входящих сообщений у всех ботов: {0}\n").format(income_messages) +
|
||||||
_("Исходящих сообщений у всех ботов: {0}\n").format(outgoing_messages) +
|
_("Исходящих сообщений у всех ботов: {0}\n").format(outgoing_messages) +
|
||||||
_("Промо-кодов выдано: {0}\n").format(promo_count) +
|
_("Промо-кодов выдано: {0}\n").format(promo_count) +
|
||||||
_("Рекламную плашку выключили: {0}\n".format(olgram_text_disabled)))
|
_("Рекламную плашку выключили: {0}\n").format(olgram_text_disabled))
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ from aiogram import types, Bot as AioBot
|
|||||||
from olgram.models.models import Bot, User, DefaultAnswer
|
from olgram.models.models import Bot, User, DefaultAnswer
|
||||||
from aiogram.dispatcher import FSMContext
|
from aiogram.dispatcher import FSMContext
|
||||||
from aiogram.utils.callback_data import CallbackData
|
from aiogram.utils.callback_data import CallbackData
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from olgram.utils.mix import edit_or_create, button_text_limit, wrap
|
from olgram.utils.mix import edit_or_create, button_text_limit, wrap, send_stored_message
|
||||||
from olgram.commands import bot_actions
|
from olgram.commands import bot_actions
|
||||||
from locales.locale import _
|
from locales.locale import _
|
||||||
|
|
||||||
import typing as ty
|
import typing as ty
|
||||||
|
|
||||||
|
|
||||||
menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat')
|
menu_callback = CallbackData('menu', 'level', 'bot_id', 'operation', 'chat')
|
||||||
|
|
||||||
empty = "0"
|
empty = "0"
|
||||||
@@ -127,6 +127,12 @@ async def send_bot_menu(bot: Bot, call: types.CallbackQuery):
|
|||||||
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings",
|
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings",
|
||||||
chat=empty))
|
chat=empty))
|
||||||
)
|
)
|
||||||
|
if bot.enable_mailing:
|
||||||
|
keyboard.insert(
|
||||||
|
types.InlineKeyboardButton(text=_("Рассылка"),
|
||||||
|
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="go_mailing",
|
||||||
|
chat=empty))
|
||||||
|
)
|
||||||
|
|
||||||
await edit_or_create(call, dedent(_("""
|
await edit_or_create(call, dedent(_("""
|
||||||
Управление ботом @{0}.
|
Управление ботом @{0}.
|
||||||
@@ -172,6 +178,11 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
|||||||
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="antiflood",
|
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="antiflood",
|
||||||
chat=empty))
|
chat=empty))
|
||||||
)
|
)
|
||||||
|
keyboard.insert(
|
||||||
|
types.InlineKeyboardButton(text=_("Рассылка"),
|
||||||
|
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="mailing",
|
||||||
|
chat=empty))
|
||||||
|
)
|
||||||
is_promo = await bot.is_promo()
|
is_promo = await bot.is_promo()
|
||||||
if is_promo:
|
if is_promo:
|
||||||
keyboard.insert(
|
keyboard.insert(
|
||||||
@@ -189,11 +200,13 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
|
|||||||
thread_turn = _("включены") if bot.enable_threads else _("выключены")
|
thread_turn = _("включены") if bot.enable_threads else _("выключены")
|
||||||
info_turn = _("включены") if bot.enable_additional_info else _("выключены")
|
info_turn = _("включены") if bot.enable_additional_info else _("выключены")
|
||||||
antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен")
|
antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен")
|
||||||
|
mailing_turn = _("включена") if bot.enable_mailing else _("выключена")
|
||||||
text = dedent(_("""
|
text = dedent(_("""
|
||||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b>
|
<a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b>
|
||||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b>
|
<a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b>
|
||||||
<a href="https://olgram.readthedocs.io/ru/latest/options.html#antiflood">Антифлуд</a>: <b>{2}</b>
|
<a href="https://olgram.readthedocs.io/ru/latest/options.html#antiflood">Антифлуд</a>: <b>{2}</b>
|
||||||
""")).format(thread_turn, info_turn, antiflood_turn)
|
<a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{3}</b>
|
||||||
|
""")).format(thread_turn, info_turn, antiflood_turn, mailing_turn)
|
||||||
|
|
||||||
if is_promo:
|
if is_promo:
|
||||||
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
|
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
|
||||||
@@ -238,6 +251,30 @@ async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] =
|
|||||||
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
|
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
|
||||||
|
|
||||||
|
|
||||||
|
async def send_bot_mailing_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
|
||||||
|
chat_id: ty.Optional[int] = None):
|
||||||
|
if call:
|
||||||
|
await call.answer()
|
||||||
|
keyboard = types.InlineKeyboardMarkup(row_width=1)
|
||||||
|
keyboard.insert(
|
||||||
|
types.InlineKeyboardButton(text=_("<< Отменить рассылку"),
|
||||||
|
callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty, chat=empty))
|
||||||
|
)
|
||||||
|
|
||||||
|
text = dedent(_("""
|
||||||
|
Напишите сообщение, которое нужно разослать всем подписчикам вашего бота @{0}.
|
||||||
|
У сообщения будет до {1} получателей.
|
||||||
|
Учтите, что
|
||||||
|
1. Рассылается только одно сообщение за раз (в т.ч. только одна картинка)
|
||||||
|
2. Когда рассылка запущена, её нельзя отменить
|
||||||
|
"""))
|
||||||
|
text = text.format(bot.name, len(await bot.mailing_users))
|
||||||
|
if call:
|
||||||
|
await edit_or_create(call, text, keyboard, parse_mode="HTML")
|
||||||
|
else:
|
||||||
|
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
|
||||||
|
|
||||||
|
|
||||||
async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
|
async def send_bot_statistic_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
|
||||||
chat_id: ty.Optional[int] = None):
|
chat_id: ty.Optional[int] = None):
|
||||||
if call:
|
if call:
|
||||||
@@ -352,6 +389,49 @@ async def start_text_received(message: types.Message, state: FSMContext):
|
|||||||
await send_bot_text_menu(bot, chat_id=message.chat.id)
|
await send_bot_text_menu(bot, chat_id=message.chat.id)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message_handler(state="wait_mailing_text",
|
||||||
|
content_types=[types.ContentType.TEXT,
|
||||||
|
types.ContentType.LOCATION,
|
||||||
|
types.ContentType.DOCUMENT,
|
||||||
|
types.ContentType.PHOTO,
|
||||||
|
types.ContentType.AUDIO,
|
||||||
|
types.ContentType.VIDEO]) # TODO: not command
|
||||||
|
async def mailing_text_received(message: types.Message, state: FSMContext):
|
||||||
|
async with state.proxy() as proxy:
|
||||||
|
bot_id = proxy["bot_id"]
|
||||||
|
proxy["mailing_content_type"] = message.content_type
|
||||||
|
|
||||||
|
if message.content_type == types.ContentType.TEXT:
|
||||||
|
proxy["mailing_text"] = message.html_text
|
||||||
|
elif message.content_type == types.ContentType.LOCATION:
|
||||||
|
proxy["mailing_location"] = message.location
|
||||||
|
elif message.content_type == types.ContentType.PHOTO:
|
||||||
|
proxy["mailing_photo"] = message.photo[0].file_id
|
||||||
|
proxy["mailing_caption"] = message.caption
|
||||||
|
elif message.content_type == types.ContentType.DOCUMENT:
|
||||||
|
proxy["mailing_document"] = message.document.file_id
|
||||||
|
proxy["mailing_caption"] = message.caption
|
||||||
|
elif message.content_type == types.ContentType.AUDIO:
|
||||||
|
proxy["mailing_audio"] = message.audio.file_id
|
||||||
|
proxy["mailing_caption"] = message.caption
|
||||||
|
elif message.content_type == types.ContentType.VIDEO:
|
||||||
|
proxy["mailing_video"] = message.video.file_id
|
||||||
|
proxy["mailing_video"] = message.caption
|
||||||
|
|
||||||
|
_message_id = await send_stored_message(proxy, AioBot.get_current(), message.chat.id)
|
||||||
|
|
||||||
|
keyboard = types.InlineKeyboardMarkup(row_width=1)
|
||||||
|
keyboard.insert(
|
||||||
|
types.InlineKeyboardButton(text=_("Да, начать рассылку"),
|
||||||
|
callback_data=menu_callback.new(level=3, bot_id=bot_id, operation="go_go_mailing",
|
||||||
|
chat=empty))
|
||||||
|
)
|
||||||
|
|
||||||
|
await AioBot.get_current().send_message(message.chat.id, reply_to_message_id=_message_id.message_id,
|
||||||
|
text="Вы уверены, что хотите разослать это сообщение всем пользователям?",
|
||||||
|
reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
@dp.message_handler(state="wait_second_text", content_types="text", regexp="^[^/].+") # Not command
|
@dp.message_handler(state="wait_second_text", content_types="text", regexp="^[^/].+") # Not command
|
||||||
async def second_text_received(message: types.Message, state: FSMContext):
|
async def second_text_received(message: types.Message, state: FSMContext):
|
||||||
async with state.proxy() as proxy:
|
async with state.proxy() as proxy:
|
||||||
@@ -423,6 +503,15 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
|||||||
return await send_bot_statistic_menu(bot, call)
|
return await send_bot_statistic_menu(bot, call)
|
||||||
if operation == "settings":
|
if operation == "settings":
|
||||||
return await send_bot_settings_menu(bot, call)
|
return await send_bot_settings_menu(bot, call)
|
||||||
|
if operation == "go_mailing":
|
||||||
|
if bot.last_mailing_at and bot.last_mailing_at >= datetime.now() - timedelta(minutes=5):
|
||||||
|
return await call.answer(_("Рассылка была совсем недавно, подождите немного"), show_alert=True)
|
||||||
|
if not await bot.mailing_users:
|
||||||
|
return await call.answer(_("Нет пользователей для рассылки"))
|
||||||
|
await state.set_state("wait_mailing_text")
|
||||||
|
async with state.proxy() as proxy:
|
||||||
|
proxy["bot_id"] = bot.id
|
||||||
|
return await send_bot_mailing_menu(bot, call)
|
||||||
if operation == "text":
|
if operation == "text":
|
||||||
await state.set_state("wait_start_text")
|
await state.set_state("wait_start_text")
|
||||||
async with state.proxy() as proxy:
|
async with state.proxy() as proxy:
|
||||||
@@ -440,6 +529,9 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
|||||||
if operation == "antiflood":
|
if operation == "antiflood":
|
||||||
await bot_actions.antiflood(bot, call)
|
await bot_actions.antiflood(bot, call)
|
||||||
return await send_bot_settings_menu(bot, call)
|
return await send_bot_settings_menu(bot, call)
|
||||||
|
if operation == "mailing":
|
||||||
|
await bot_actions.mailing(bot, call)
|
||||||
|
return await send_bot_settings_menu(bot, call)
|
||||||
if operation == "additional_info":
|
if operation == "additional_info":
|
||||||
await bot_actions.additional_info(bot, call)
|
await bot_actions.additional_info(bot, call)
|
||||||
return await send_bot_settings_menu(bot, call)
|
return await send_bot_settings_menu(bot, call)
|
||||||
@@ -454,6 +546,20 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
|
|||||||
async with state.proxy() as proxy:
|
async with state.proxy() as proxy:
|
||||||
proxy["bot_id"] = bot.id
|
proxy["bot_id"] = bot.id
|
||||||
return await send_bot_second_text_menu(bot, call)
|
return await send_bot_second_text_menu(bot, call)
|
||||||
|
if operation == "go_go_mailing":
|
||||||
|
if (await state.get_state()) == "wait_mailing_text":
|
||||||
|
async with state.proxy() as proxy:
|
||||||
|
mailing_data = dict(proxy)
|
||||||
|
await state.reset_state()
|
||||||
|
|
||||||
|
if bot.last_mailing_at and bot.last_mailing_at >= datetime.now() - timedelta(minutes=5):
|
||||||
|
return await call.answer(_("Рассылка была совсем недавно, подождите немного"), show_alert=True)
|
||||||
|
if not await bot.mailing_users:
|
||||||
|
return await call.answer(_("Нет пользователей для рассылки"))
|
||||||
|
|
||||||
|
await call.answer(_("Рассылка запущена"))
|
||||||
|
count = await bot_actions.go_mailing(bot, mailing_data)
|
||||||
|
await call.message.answer(_("Рассылка завершена, отправлено {0} сообщений").format(count))
|
||||||
if operation == "reset_second_text":
|
if operation == "reset_second_text":
|
||||||
await bot_actions.reset_bot_second_text(bot, call)
|
await bot_actions.reset_bot_second_text(bot, call)
|
||||||
return await send_bot_second_text_menu(bot, call)
|
return await send_bot_second_text_menu(bot, call)
|
||||||
|
|||||||
14
olgram/migrations/models/15_20221106042712_update.sql
Normal file
14
olgram/migrations/models/15_20221106042712_update.sql
Normal 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";
|
||||||
@@ -46,6 +46,8 @@ class Bot(Model):
|
|||||||
enable_additional_info = fields.BooleanField(default=False)
|
enable_additional_info = fields.BooleanField(default=False)
|
||||||
enable_olgram_text = fields.BooleanField(default=True)
|
enable_olgram_text = fields.BooleanField(default=True)
|
||||||
enable_antiflood = fields.BooleanField(default=False)
|
enable_antiflood = fields.BooleanField(default=False)
|
||||||
|
enable_mailing = fields.BooleanField(default=False)
|
||||||
|
last_mailing_at = fields.DatetimeField(null=True, default=None)
|
||||||
|
|
||||||
def decrypted_token(self):
|
def decrypted_token(self):
|
||||||
cryptor = DatabaseSettings.cryptor()
|
cryptor = DatabaseSettings.cryptor()
|
||||||
@@ -70,6 +72,17 @@ class Bot(Model):
|
|||||||
table = 'bot'
|
table = 'bot'
|
||||||
|
|
||||||
|
|
||||||
|
class MailingUser(Model):
|
||||||
|
id = fields.BigIntField(pk=True)
|
||||||
|
telegram_id = fields.BigIntField(index=True)
|
||||||
|
|
||||||
|
bot = fields.ForeignKeyField("models.Bot", related_name="mailing_users", on_delete=fields.relational.CASCADE)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table = 'mailinguser'
|
||||||
|
unique_together = (("bot", "telegram_id"), )
|
||||||
|
|
||||||
|
|
||||||
class User(Model):
|
class User(Model):
|
||||||
id = fields.IntField(pk=True)
|
id = fields.IntField(pk=True)
|
||||||
telegram_id = fields.BigIntField(index=True, unique=True)
|
telegram_id = fields.BigIntField(index=True, unique=True)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup
|
||||||
|
from aiogram import types, Bot as AioBot
|
||||||
from aiogram.utils.exceptions import TelegramAPIError
|
from aiogram.utils.exceptions import TelegramAPIError
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -30,3 +31,23 @@ def wrap(data: str, max_len: int) -> str:
|
|||||||
|
|
||||||
def button_text_limit(data: str) -> str:
|
def button_text_limit(data: str) -> str:
|
||||||
return wrap(data, 30)
|
return wrap(data, 30)
|
||||||
|
|
||||||
|
|
||||||
|
async def send_stored_message(storage: dict, bot: AioBot, chat_id: int):
|
||||||
|
content_type = storage["mailing_content_type"]
|
||||||
|
if content_type == types.ContentType.TEXT:
|
||||||
|
return await bot.send_message(chat_id, storage["mailing_text"], parse_mode="HTML")
|
||||||
|
if content_type == types.ContentType.LOCATION:
|
||||||
|
return await bot.send_location(chat_id, storage["mailing_location"][0], storage["mailing_location"][1])
|
||||||
|
if content_type == types.ContentType.AUDIO:
|
||||||
|
return await bot.send_audio(chat_id, audio=storage["mailing_audio"], caption=storage.get("mailing_caption"))
|
||||||
|
if content_type == types.ContentType.DOCUMENT:
|
||||||
|
return await bot.send_document(chat_id, document=storage["mailing_document"],
|
||||||
|
caption=storage.get("mailing_caption"))
|
||||||
|
if content_type == types.ContentType.PHOTO:
|
||||||
|
return await bot.send_photo(chat_id, photo=storage["mailing_photo"],
|
||||||
|
caption=storage.get("mailing_caption"))
|
||||||
|
if content_type == types.ContentType.VIDEO:
|
||||||
|
return await bot.send_video(chat_id, video=storage["mailing_video"],
|
||||||
|
caption=storage.get("mailing_caption"))
|
||||||
|
raise NotImplementedError("Mailing, unknown content type")
|
||||||
@@ -11,7 +11,7 @@ from tortoise.expressions import F
|
|||||||
import logging
|
import logging
|
||||||
import typing as ty
|
import typing as ty
|
||||||
from olgram.settings import ServerSettings
|
from olgram.settings import ServerSettings
|
||||||
from olgram.models.models import Bot, GroupChat, BannedUser
|
from olgram.models.models import Bot, GroupChat, BannedUser, MailingUser
|
||||||
from locales.locale import _, translators
|
from locales.locale import _, translators
|
||||||
from server.inlines import inline_handler
|
from server.inlines import inline_handler
|
||||||
|
|
||||||
@@ -55,15 +55,20 @@ def _on_security_policy(message: types.Message, bot):
|
|||||||
text = _("<b>Политика конфиденциальности</b>\n\n"
|
text = _("<b>Политика конфиденциальности</b>\n\n"
|
||||||
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд "
|
"Этот бот не хранит ваши сообщения, имя пользователя и @username. При отправке сообщения (кроме команд "
|
||||||
"/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом "
|
"/start и /security_policy) ваш идентификатор пользователя записывается в кеш на некоторое время и потом "
|
||||||
"удаляется из кеша. Этот идентификатор используется только для общения с оператором; боты Olgram "
|
"удаляется из кеша. Этот идентификатор используется для общения с оператором.\n\n")
|
||||||
"не делают массовых рассылок.\n\n")
|
|
||||||
if bot.enable_additional_info:
|
if bot.enable_additional_info:
|
||||||
text += _("При отправке сообщения (кроме команд /start и /security_policy) оператор <b>видит</b> ваши имя "
|
text += _("При отправке сообщения (кроме команд /start и /security_policy) оператор <b>видит</b> ваши имя "
|
||||||
"пользователя, @username и идентификатор пользователя в силу настроек, которые оператор указал при "
|
"пользователя, @username и идентификатор пользователя в силу настроек, которые оператор указал при "
|
||||||
"создании бота.")
|
"создании бота.\n\n")
|
||||||
else:
|
else:
|
||||||
text += _("В зависимости от ваших настроек конфиденциальности Telegram, оператор может видеть ваш username, "
|
text += _("В зависимости от ваших настроек конфиденциальности Telegram, оператор может видеть ваш username, "
|
||||||
"имя пользователя и другую информацию.")
|
"имя пользователя и другую информацию.\n\n")
|
||||||
|
|
||||||
|
if bot.enable_mailing:
|
||||||
|
text += _("В этом боте включена массовая рассылка в силу настроек, которые оператор указал при создании бота. "
|
||||||
|
"Ваш идентификатор пользователя может быть записан в базу данных на долгое время")
|
||||||
|
else:
|
||||||
|
text += _("В этом боте нет массовой рассылки сообщений")
|
||||||
|
|
||||||
return SendMessage(chat_id=message.chat.id,
|
return SendMessage(chat_id=message.chat.id,
|
||||||
text=text,
|
text=text,
|
||||||
@@ -128,6 +133,10 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
|
|||||||
_ = _get_translator(message)
|
_ = _get_translator(message)
|
||||||
is_super_group = super_chat_id < 0
|
is_super_group = super_chat_id < 0
|
||||||
|
|
||||||
|
# Записать пользователя для рассылки, если она включена
|
||||||
|
if bot.enable_mailing:
|
||||||
|
_, __ = await MailingUser.get_or_create(telegram_id=message.chat.id, bot=bot)
|
||||||
|
|
||||||
# Проверить, не забанен ли пользователь
|
# Проверить, не забанен ли пользователь
|
||||||
banned = await bot.banned_users.filter(telegram_id=message.chat.id)
|
banned = await bot.banned_users.filter(telegram_id=message.chat.id)
|
||||||
if banned:
|
if banned:
|
||||||
|
|||||||
Reference in New Issue
Block a user