""" Модуль смены аватарки бота. Совместим с aiogram 3.22.0 и Bot API 9.4+ Использует: - SetMyProfilePhoto - InputProfilePhotoStatic """ from __future__ import annotations import os from typing import Union from aiogram import Router, Bot, F from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State from aiogram.types import ( Message, CallbackQuery, FSInputFile, InputProfilePhotoStatic, ) from aiogram.methods.set_my_profile_photo import SetMyProfilePhoto from aiogram.exceptions import TelegramAPIError, TelegramRetryAfter from aiogram.utils.i18n import gettext as _ from bot.filters import IsSuperAdmin from bot.templates import msg from bot.utils import format_retry_time, status_clear from bot.core.bots import BotInfo from bot.handlers.commands.settings.settings_cmd import settings_keyboard from configs import COMMANDS from middleware.loggers import logger __all__ = ("router",) CMD: str = "set_avatar".lower() router: Router = Router(name=f"{CMD}_router") # ================= FSM ================= class SetBotAvatarForm(StatesGroup): """ FSM состояния для смены аватарки. """ waiting_for_photo: State = State() # ================= CORE ================= async def handle_set_avatar( update: Union[Message, CallbackQuery], state: FSMContext, bot: Bot ) -> None: """ Устанавливает новую аватарку бота. Args: update: Message или CallbackQuery state: FSM контекст bot: Экземпляр бота """ message: Message = update.message if isinstance(update, CallbackQuery) else update if not message.photo: return largest_photo = message.photo[-1] # Получаем файл от Telegram tg_file = await bot.get_file(largest_photo.file_id) temp_path: str = f"/tmp/{largest_photo.file_unique_id}.jpg" await bot.download_file(tg_file.file_path, destination=temp_path) try: method = SetMyProfilePhoto( photo=InputProfilePhotoStatic( photo=FSInputFile(temp_path) ) ) result: bool = await bot(method) if result: logger.info("Аватарка бота успешно обновлена", log_type="BOT_SETUP") await state.clear() await msg( update=update, text=_("✅ Аватарка бота успешно обновлена."), markup=settings_keyboard(), state=state ) except TelegramRetryAfter as e: retry_text: str = format_retry_time(e.retry_after) logger.warning( f"Rate limit при смене аватарки. Повтор через {retry_text}", log_type="BOT_SETUP" ) await msg( update=update, text=_( "⚠️ Слишком частая смена аватарки.\n" "Попробуйте снова через: {retry}" ).format(retry=retry_text), markup=settings_keyboard(), state=state ) except TelegramAPIError as e: logger.error( f"Ошибка Telegram API при смене аватарки: {e}", log_type="BOT_SETUP" ) await msg( update=update, text=_( "❌ Ошибка Telegram API:\n" "
{error}
" ).format(error=str(e)), markup=settings_keyboard(), state=state ) except Exception as e: logger.error( f"Непредвиденная ошибка при смене аватарки: {e}", log_type="BOT_SETUP" ) await msg( update=update, text=_( "❌ Непредвиденная ошибка:\n" "
{error}
" ).format(error=str(e)), markup=settings_keyboard(), state=state ) finally: if os.path.exists(temp_path): os.remove(temp_path) # ================= COMMAND ================= @router.callback_query(F.data.lower() == CMD, IsSuperAdmin()) @router.message( Command(*COMMANDS.get(CMD, [CMD]), prefix=BotInfo.prefix, ignore_case=True), IsSuperAdmin() ) async def set_avatar_cmd( message: Message | CallbackQuery, state: FSMContext, bot: Bot ) -> None: """ Команда /set_avatar Поддерживает: - Фото вместе с командой - Ответ на фото - FSM режим """ await status_clear(update=message, state=state) msg_obj: Message = message.message if isinstance(message, CallbackQuery) else message if msg_obj.photo: await handle_set_avatar(message, state, bot) return if msg_obj.reply_to_message and msg_obj.reply_to_message.photo: await handle_set_avatar(msg_obj.reply_to_message, state, bot) return await msg( update=message, text=_( "🖼 Смена аватарки бота\n\n" "Отправьте фотографию." ), markup=settings_keyboard(), state=state ) await state.set_state(SetBotAvatarForm.waiting_for_photo) # ================= FSM ================= @router.message(SetBotAvatarForm.waiting_for_photo, IsSuperAdmin(), F.photo) async def process_avatar_photo( message: Message, state: FSMContext, bot: Bot ) -> None: """ Обработка фото через FSM. """ await handle_set_avatar(message, state, bot) @router.message(SetBotAvatarForm.waiting_for_photo, IsSuperAdmin()) async def invalid_input(message: Message) -> None: """ Обработка некорректного ввода. """ await message.answer(_("❌ Пожалуйста, отправьте фотографию."))