diff --git a/olgram/commands/bot_actions.py b/olgram/commands/bot_actions.py index ed3d114..6b8946a 100644 --- a/olgram/commands/bot_actions.py +++ b/olgram/commands/bot_actions.py @@ -2,8 +2,9 @@ Здесь работа с конкретным ботом """ from asyncio import sleep +from datetime import datetime from aiogram import types -from aiogram.utils.exceptions import TelegramAPIError, Unauthorized, BotKicked, BotBlocked +from aiogram.utils import exceptions from aiogram import Bot as AioBot from olgram.models.models import Bot from olgram.utils.mix import send_stored_message @@ -17,14 +18,14 @@ async def delete_bot(bot: Bot, call: types.CallbackQuery): """ try: await unregister_token(bot.decrypted_token()) - except Unauthorized: + except exceptions.Unauthorized: # Вероятно пользователь сбросил токен или удалил бот, это уже не наши проблемы pass await bot.delete() await call.answer(_("Бот удалён")) try: await call.message.delete() - except TelegramAPIError: + except exceptions.TelegramAPIError: pass @@ -74,7 +75,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 TelegramAPIError: + except exceptions.TelegramAPIError: pass await call.answer(_("Бот вышел из чатов")) await a_bot.session.close() @@ -119,16 +120,24 @@ async def go_mailing(bot: Bot, context) -> int: users = await bot.mailing_users a_bot = AioBot(bot.decrypted_token()) - sended = 0 + 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.2) - await send_stored_message(context, a_bot, user.telegram_id) - sended += 1 - except TelegramAPIError: - # TODO: - # delete user - # check error source, check bot, break if bot deleted - continue - return sended + 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 diff --git a/olgram/commands/info.py b/olgram/commands/info.py index fdce6ee..b1766dd 100644 --- a/olgram/commands/info.py +++ b/olgram/commands/info.py @@ -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)) diff --git a/olgram/commands/menu.py b/olgram/commands/menu.py index 72200f5..582697e 100644 --- a/olgram/commands/menu.py +++ b/olgram/commands/menu.py @@ -420,15 +420,10 @@ async def mailing_text_received(message: types.Message, state: FSMContext): _message_id = await send_stored_message(proxy, AioBot.get_current(), message.chat.id) - keyboard = types.InlineKeyboardMarkup(row_width=2) - keyboard.insert( - types.InlineKeyboardButton(text=_("<< Нет, отменить"), # TODO: don't move menu back - callback_data=menu_callback.new(level=1, bot_id=bot_id, operation=empty, - chat=empty)) - ) + keyboard = types.InlineKeyboardMarkup(row_width=1) keyboard.insert( types.InlineKeyboardButton(text=_("Да, начать рассылку"), - callback_data=menu_callback.new(level=2, bot_id=bot_id, operation="go_go_mailing", + callback_data=menu_callback.new(level=3, bot_id=bot_id, operation="go_go_mailing", chat=empty)) ) @@ -508,20 +503,15 @@ 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 in ("go_mailing", "go_go_mailing"): + 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 operation == "go_mailing": + 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 == "go_go_mailing": - async with state.proxy() as proxy: - mailing_data = dict(proxy) - await state.reset_state() # TODO: double-click protection - await call.answer(_("Рассылка запущена")) - await bot_actions.go_mailing(bot, mailing_data) if operation == "text": await state.set_state("wait_start_text") async with state.proxy() as proxy: @@ -556,6 +546,20 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon 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)) if operation == "reset_second_text": await bot_actions.reset_bot_second_text(bot, call) return await send_bot_second_text_menu(bot, call)