Merge pull request #70 from machka-pasla/dev

Broadcast fix
This commit is contained in:
Machka Pasla
2025-08-09 10:00:30 +03:00
committed by GitHub
3 changed files with 39 additions and 4 deletions
+36 -4
View File
@@ -1,7 +1,7 @@
import logging import logging
import asyncio import asyncio
from aiogram import Router, F, types, Bot from aiogram import Router, F, types, Bot
from aiogram.exceptions import TelegramRetryAfter from aiogram.exceptions import TelegramRetryAfter, TelegramBadRequest
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from typing import Optional from typing import Optional
@@ -58,13 +58,14 @@ async def broadcast_message_prompt_handler(
await state.set_state(AdminStates.waiting_for_broadcast_message) await state.set_state(AdminStates.waiting_for_broadcast_message)
@router.message(AdminStates.waiting_for_broadcast_message, F.text) @router.message(AdminStates.waiting_for_broadcast_message)
async def process_broadcast_message_handler( async def process_broadcast_message_handler(
message: types.Message, message: types.Message,
state: FSMContext, state: FSMContext,
i18n_data: dict, i18n_data: dict,
settings: Settings, settings: Settings,
session: AsyncSession, session: AsyncSession,
bot: Bot,
): ):
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE) current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance") i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
@@ -76,9 +77,39 @@ async def process_broadcast_message_handler(
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
# Сохраняем в state исходный текст и entities # Сохраняем в state исходный текст и entities
text = message.text or message.caption or "" text = (message.text or message.caption or "").strip()
entities = message.entities or message.caption_entities or [] entities = message.entities or message.caption_entities or []
# Если текст пустой (например, прислали стикер/фото без подписи) — просим ввести текст
if not text:
await message.answer(_("admin_broadcast_error_no_message"))
return
# Предварительная проверка HTML: попробуем отправить и сразу удалить
# Если HTML некорректный, Telegram вернёт ошибку парсинга
try:
test_msg = await bot.send_message(
chat_id=message.chat.id,
text=text,
parse_mode="HTML",
disable_web_page_preview=True,
disable_notification=True,
)
# Удалим тестовое сообщение
try:
await bot.delete_message(chat_id=message.chat.id, message_id=test_msg.message_id)
except Exception:
pass
except TelegramBadRequest as e:
await message.answer(
_(
"admin_broadcast_invalid_html",
default="❌ Некорректный HTML в сообщении. Пожалуйста, отправьте корректный HTML (поддерживаются теги Telegram) или уберите теги.\nОшибка: {error}",
error=str(e),
)
)
return
await state.update_data( await state.update_data(
broadcast_text=text, broadcast_text=text,
broadcast_entities=entities, broadcast_entities=entities,
@@ -184,7 +215,8 @@ async def confirm_broadcast_callback_handler(
await queue_manager.send_message( await queue_manager.send_message(
chat_id=uid, chat_id=uid,
text=text, text=text,
entities=entities, parse_mode="HTML",
disable_web_page_preview=True,
) )
sent_count += 1 sent_count += 1
+2
View File
@@ -165,6 +165,8 @@
"admin_broadcast_cancelled_alert": "Broadcast cancelled!", "admin_broadcast_cancelled_alert": "Broadcast cancelled!",
"admin_broadcast_cancelled_nav_back": "Broadcast cancelled. You are returned to the admin panel.", "admin_broadcast_cancelled_nav_back": "Broadcast cancelled. You are returned to the admin panel.",
"admin_broadcast_invalid_html": "❌ Invalid HTML in message. Please send valid HTML (Telegram-supported tags) or remove tags.",
"admin_promo_create_prompt": "Enter promo details in the format: CODE BONUS_DAYS MAX_USES [VALIDITY_DAYS]\nExample: <code>{example_format}</code>\n(Validity is optional; default is indefinite)", "admin_promo_create_prompt": "Enter promo details in the format: CODE BONUS_DAYS MAX_USES [VALIDITY_DAYS]\nExample: <code>{example_format}</code>\n(Validity is optional; default is indefinite)",
"admin_promo_invalid_format": "Invalid format. Please use: CODE BONUS_DAYS MAX_USES [VALIDITY_DAYS]", "admin_promo_invalid_format": "Invalid format. Please use: CODE BONUS_DAYS MAX_USES [VALIDITY_DAYS]",
"admin_promo_invalid_code_format": "Code must be 330 alphanumeric characters.", "admin_promo_invalid_code_format": "Code must be 330 alphanumeric characters.",
+1
View File
@@ -268,6 +268,7 @@
"sync_critical_error": "❌ Критическая ошибка синхронизации", "sync_critical_error": "❌ Критическая ошибка синхронизации",
"no_errors_placeholder": "нет", "no_errors_placeholder": "нет",
"admin_sync_initiated_from_panel": "Синхронизация запущена...", "admin_sync_initiated_from_panel": "Синхронизация запущена...",
"admin_broadcast_invalid_html": "❌ Некорректный HTML в сообщении. Пожалуйста, отправьте корректный HTML (поддерживаются теги Telegram) или уберите теги.",
"admin_panel_user_creation_failed": "❌ Не удалось создать пользователя на панели для TG ID {user_id}. Панель недоступна?", "admin_panel_user_creation_failed": "❌ Не удалось создать пользователя на панели для TG ID {user_id}. Панель недоступна?",
"error_displaying_logs_too_long": "Ошибка: логи слишком длинные для отображения одним сообщением. Попробуйте найти логи по конкретному пользователю.", "error_displaying_logs_too_long": "Ошибка: логи слишком длинные для отображения одним сообщением. Попробуйте найти логи по конкретному пользователю.",
"error_displaying_statistics": "Ошибка отображения статистики.", "error_displaying_statistics": "Ошибка отображения статистики.",