add broadcasting

This commit is contained in:
arĉi 2022-10-09 20:23:45 +06:00
parent 30ab7c84b4
commit 992daa5b30
5 changed files with 93 additions and 11 deletions

View File

@ -1,11 +1,13 @@
"""
Здесь работа с конкретным ботом
"""
import asyncio
from aiogram import types
from aiogram.utils.exceptions import TelegramAPIError, Unauthorized
from aiogram import Bot as AioBot
from olgram.models.models import Bot
from olgram.models.models import Bot, User
from server.server import unregister_token
from typing import Optional
from locales.locale import _
@ -87,6 +89,28 @@ async def select_chat(bot: Bot, call: types.CallbackQuery, chat: str):
await call.answer(_("Выбран чат {0}").format(chat_obj.name))
async def start_broadcast(bot: Bot, call: types.CallbackQuery, text: Optional[str]):
if not text:
return await call.answer(_("Отправьте текст для рассылки"))
user_chat_ids = await User.all().values_list("telegram_id", flat=True)
a_bot = AioBot(bot.decrypted_token())
count = 0
await call.answer(_("Рассылка начата"))
try:
for telegram_id in user_chat_ids:
try:
if await a_bot.send_message(telegram_id, text, parse_mode="HTML"):
count += 1
except Unauthorized:
continue
else:
await asyncio.sleep(0.05) # 20 messages per second (Limit: 30 messages per second)
finally:
await call.bot.send_message(call.from_user.id, _("Рассылка закончена. Сообщений отправлено: {0}").format(count))
await a_bot.session.close()
async def threads(bot: Bot, call: types.CallbackQuery):
bot.enable_threads = not bot.enable_threads
await bot.save(update_fields=["enable_threads"])

View File

@ -37,4 +37,4 @@ async def info(message: types.Message, state: FSMContext):
_("Входящих сообщений у всех ботов: {0}\n").format(income_messages) +
_("Исходящих сообщений у всех ботов: {0}\n").format(outgoing_messages) +
_("Промо-кодов выдано: {0}\n").format(promo_count) +
_("Рекламную плашку выключили: {0}\n".format(olgram_text_disabled)))
_("Рекламную плашку выключили: {0}\n").format(olgram_text_disabled))

View File

@ -119,14 +119,19 @@ async def send_bot_menu(bot: Bot, call: types.CallbackQuery):
chat=empty))
)
keyboard.insert(
types.InlineKeyboardButton(text=_("<< Назад"),
callback_data=menu_callback.new(level=0, bot_id=empty, operation=empty, chat=empty))
types.InlineKeyboardButton(text=_("Рассылка"),
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="broadcast",
chat=empty))
)
keyboard.insert(
types.InlineKeyboardButton(text=_("Опции"),
callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="settings",
chat=empty))
)
keyboard.insert(
types.InlineKeyboardButton(text=_("<< Назад"),
callback_data=menu_callback.new(level=0, bot_id=empty, operation=empty, chat=empty))
)
await edit_or_create(call, dedent(_("""
Управление ботом @{0}.
@ -202,6 +207,41 @@ 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_broadcast_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
chat_id: ty.Optional[int] = None, text: ty.Optional[str] = None):
if call:
await call.answer()
keyboard = types.InlineKeyboardMarkup(row_width=2)
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="start_broadcast",
chat=empty))
)
if text:
text = dedent(_("""
Сейчас вы редактируете текст, который будет отправлен всем пользователям бота @{0} после начала рассылки.
Текущий текст:
<pre>
{1}
</pre>
Отправьте сообщение, чтобы изменить текст.
""")).format(bot.name, text)
else:
text = _(
"Отправьте сообщение с текстом, который будет отправлен всем пользователям бота @{0} после начала рассылки."
).format(bot.name)
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_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None, chat_id: ty.Optional[int] = None):
if call:
await call.answer()
@ -342,7 +382,17 @@ async def send_bot_templates_menu(bot: Bot, call: ty.Optional[types.CallbackQuer
await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
@dp.message_handler(state="wait_start_text", content_types="text", regexp="^[^/].+") # Not command
@dp.message_handler(state="wait_broadcast_text", content_types="text", regexp="^[^/].*") # Not command
async def broadcast_text_received(message: types.Message, state: FSMContext):
broadcast_text = message.html_text
async with state.proxy() as proxy:
bot_id = proxy.get("bot_id")
proxy["broadcast_text"] = broadcast_text
bot = await Bot.get_or_none(pk=bot_id)
await send_bot_broadcast_menu(bot, chat_id=message.chat.id, text=broadcast_text)
@dp.message_handler(state="wait_start_text", content_types="text", regexp="^[^/].*") # Not command
async def start_text_received(message: types.Message, state: FSMContext):
async with state.proxy() as proxy:
bot_id = proxy.get("bot_id")
@ -352,7 +402,7 @@ async def start_text_received(message: types.Message, state: FSMContext):
await send_bot_text_menu(bot, chat_id=message.chat.id)
@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 with state.proxy() as proxy:
bot_id = proxy.get("bot_id")
@ -362,7 +412,7 @@ async def second_text_received(message: types.Message, state: FSMContext):
await send_bot_second_text_menu(bot, chat_id=message.chat.id)
@dp.message_handler(state="wait_template", content_types="text", regexp="^[^/](.+)?") # Not command
@dp.message_handler(state="wait_template", content_types="text", regexp="^[^/].*") # Not command
async def template_received(message: types.Message, state: FSMContext):
async with state.proxy() as proxy:
bot_id = proxy.get("bot_id")
@ -423,6 +473,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 == "broadcast":
await state.set_state("wait_broadcast_text")
async with state.proxy() as proxy:
proxy["bot_id"] = bot.id
return await send_bot_broadcast_menu(bot, call)
if operation == "text":
await state.set_state("wait_start_text")
async with state.proxy() as proxy:
@ -457,6 +512,10 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
if operation == "reset_second_text":
await bot_actions.reset_bot_second_text(bot, call)
return await send_bot_second_text_menu(bot, call)
if operation == "start_broadcast":
async with state.proxy() as proxy:
text = proxy.get("broadcast_text")
return await bot_actions.start_broadcast(bot, call, text)
if operation == "templates":
await state.set_state("wait_template")
async with state.proxy() as proxy:

View File

@ -91,8 +91,8 @@ class ServerSettings(AbstractSettings):
return "/cert/public.pem"
@classmethod
def append_text(cls) -> str:
return "\n\nЭтот бот создан с помощью @OlgramBot"
def append_text(cls, _: ty.Callable) -> str:
return _("\n\nЭтот бот создан с помощью @OlgramBot")
@classmethod
@lru_cache

View File

@ -213,14 +213,13 @@ async def handle_operator_message(message: types.Message, super_chat_id: int, bo
async def message_handler(message: types.Message, *args, **kwargs):
_ = _get_translator(message)
bot = db_bot_instance.get()
if message.text and message.text == "/start":
# На команду start нужно ответить, не пересылая сообщение никуда
text = bot.start_text
if bot.enable_olgram_text:
text += _(ServerSettings.append_text())
text += ServerSettings.append_text(_get_translator(message))
return SendMessage(chat_id=message.chat.id, text=text, parse_mode="HTML")
if message.text and message.text == "/security_policy":