Первый коммит
This commit is contained in:
215
bot/handlers/commands/users/emoji.py
Normal file
215
bot/handlers/commands/users/emoji.py
Normal file
@@ -0,0 +1,215 @@
|
||||
"""
|
||||
Обработчик команды /emoji для извлечения ID премиум эмодзи
|
||||
"""
|
||||
from aiogram import Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from bot.filters.admin import IsAdmin
|
||||
from configs import settings, COMMANDS
|
||||
from middleware.loggers import logger
|
||||
|
||||
__all__ = ("router",)
|
||||
|
||||
router: Router = Router(name="emoji_extractor_router")
|
||||
|
||||
|
||||
# ================= ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =================
|
||||
|
||||
def extract_custom_emojis(message: Message) -> list[dict]:
|
||||
"""
|
||||
Извлекает все кастомные эмодзи из сообщения.
|
||||
|
||||
Args:
|
||||
message: Сообщение для анализа
|
||||
|
||||
Returns:
|
||||
Список словарей с информацией об эмодзи
|
||||
"""
|
||||
if not message.entities and not message.caption_entities:
|
||||
return []
|
||||
|
||||
# Определяем текст и entities
|
||||
text = message.text or message.caption
|
||||
entities = message.entities or message.caption_entities
|
||||
|
||||
if not text or not entities:
|
||||
return []
|
||||
|
||||
custom_emojis = []
|
||||
|
||||
for entity in entities:
|
||||
if entity.type == "custom_emoji":
|
||||
# Извлекаем символ эмодзи
|
||||
emoji_char = text[entity.offset:entity.offset + entity.length]
|
||||
|
||||
custom_emojis.append({
|
||||
"char": emoji_char,
|
||||
"id": entity.custom_emoji_id,
|
||||
"offset": entity.offset
|
||||
})
|
||||
|
||||
return custom_emojis
|
||||
|
||||
|
||||
def format_emoji_html(emoji_char: str, emoji_id: str) -> str:
|
||||
"""
|
||||
Форматирует эмодзи в HTML-тег.
|
||||
|
||||
Args:
|
||||
emoji_char: Символ эмодзи (fallback)
|
||||
emoji_id: ID кастомного эмодзи
|
||||
|
||||
Returns:
|
||||
HTML-строка
|
||||
"""
|
||||
return f'<tg-emoji emoji-id="{emoji_id}">{emoji_char}</tg-emoji>'
|
||||
|
||||
|
||||
def escape_html(text: str) -> str:
|
||||
"""Экранирует HTML символы"""
|
||||
return (
|
||||
text.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
)
|
||||
|
||||
|
||||
# ================= КОМАНДА /EMOJI =================
|
||||
|
||||
@router.message(
|
||||
Command(*COMMANDS.get("emoji", ["emoji"]), prefix=settings.PREFIX, ignore_case=True),
|
||||
IsAdmin()
|
||||
)
|
||||
async def emoji_extractor_cmd(message: Message) -> None:
|
||||
"""
|
||||
Извлекает кастомные эмодзи из сообщения.
|
||||
|
||||
Доступно только администраторам.
|
||||
|
||||
Использование: /emoji (в ответ на сообщение)
|
||||
"""
|
||||
# Проверяем, что команда в ответ на сообщение
|
||||
if not message.reply_to_message:
|
||||
await message.answer(
|
||||
"❌ <b>Используйте команду в ответ на сообщение</b>\n\n"
|
||||
"📝 Как использовать:\n"
|
||||
"1. Ответьте на сообщение с премиум эмодзи\n"
|
||||
"2. Напишите <code>/emoji</code>\n\n"
|
||||
"💡 <i>Бот извлечёт все кастомные эмодзи и покажет HTML-код</i>",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
replied_message = message.reply_to_message
|
||||
|
||||
# Извлекаем кастомные эмодзи
|
||||
custom_emojis = extract_custom_emojis(replied_message)
|
||||
|
||||
if not custom_emojis:
|
||||
# Нет кастомных эмодзи
|
||||
await message.answer(
|
||||
"⚠️ <b>Кастомные эмодзи не найдены</b>\n\n"
|
||||
"В этом сообщении нет премиум эмодзи.\n\n"
|
||||
"💡 <i>Попробуйте ответить на сообщение с анимированными эмодзи</i>",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# === ФОРМИРУЕМ ОТВЕТ ===
|
||||
|
||||
output = f"✨ <b>НАЙДЕНО ЭМОДЗИ: {len(custom_emojis)}</b>\n\n"
|
||||
|
||||
for idx, emoji_data in enumerate(custom_emojis, 1):
|
||||
emoji_char = emoji_data["char"]
|
||||
emoji_id = emoji_data["id"]
|
||||
|
||||
output += f"<b>{idx}.</b> Эмодзи: {emoji_char}\n"
|
||||
output += f"📋 <b>ID:</b> <code>{emoji_id}</code>\n\n"
|
||||
|
||||
# HTML-код (экранированный для отображения)
|
||||
html_code = format_emoji_html(emoji_char, emoji_id)
|
||||
html_escaped = escape_html(html_code)
|
||||
|
||||
output += f"📝 <b>HTML-код:</b>\n"
|
||||
output += f"<code>{html_escaped}</code>\n\n"
|
||||
|
||||
# Пример использования
|
||||
output += f"🎨 <b>Превью:</b> {html_code}\n"
|
||||
|
||||
if idx < len(custom_emojis):
|
||||
output += "\n" + "─" * 30 + "\n\n"
|
||||
|
||||
output += "💡 <i>Скопируйте HTML-код и используйте в своих сообщениях</i>"
|
||||
|
||||
# Создаём клавиатуру
|
||||
ikb = InlineKeyboardBuilder()
|
||||
ikb.button(text="✖️ Закрыть", callback_data="emoji_close")
|
||||
|
||||
# Отправляем
|
||||
try:
|
||||
await message.answer(
|
||||
text=output,
|
||||
parse_mode="HTML",
|
||||
reply_markup=ikb.as_markup()
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Извлечено {len(custom_emojis)} кастомных эмодзи админом {message.from_user.id}",
|
||||
log_type="EMOJI_EXTRACT"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка отправки эмодзи: {e}", log_type="ERROR")
|
||||
await message.answer(
|
||||
"❌ <b>Ошибка извлечения эмодзи</b>\n\n"
|
||||
"Попробуйте позже или обратитесь к разработчику.",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
||||
# ================= ОБРАБОТЧИК КНОПКИ ЗАКРЫТИЯ =================
|
||||
|
||||
@router.callback_query(lambda c: c.data == "emoji_close", IsAdmin())
|
||||
async def emoji_close_callback(callback) -> None:
|
||||
"""Закрывает сообщение с эмодзи"""
|
||||
try:
|
||||
await callback.message.delete()
|
||||
await callback.answer("✅ Закрыто")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка удаления сообщения с эмодзи: {e}", log_type="ERROR")
|
||||
await callback.answer("❌ Не удалось удалить", show_alert=True)
|
||||
|
||||
|
||||
# ================= ДОПОЛНИТЕЛЬНАЯ КОМАНДА /EMOJIHELP =================
|
||||
|
||||
@router.message(
|
||||
Command(*COMMANDS.get("emojihelp", ["emojihelp"]), prefix=settings.PREFIX, ignore_case=True),
|
||||
IsAdmin()
|
||||
)
|
||||
async def emoji_help_cmd(message: Message) -> None:
|
||||
"""
|
||||
Справка по работе с кастомными эмодзи.
|
||||
"""
|
||||
text = (
|
||||
"🎨 <b>РАБОТА С КАСТОМНЫМИ ЭМОДЗИ</b>\n\n"
|
||||
"📝 <b>Команда /emoji</b>\n"
|
||||
"Извлекает ID премиум эмодзи из сообщения\n\n"
|
||||
"🔧 <b>Как использовать:</b>\n"
|
||||
"1️⃣ Ответьте на сообщение с эмодзи\n"
|
||||
"2️⃣ Напишите <code>/emoji</code>\n"
|
||||
"3️⃣ Скопируйте HTML-код\n\n"
|
||||
"💻 <b>Формат HTML-кода:</b>\n"
|
||||
"<code><tg-emoji emoji-id=\"ID\">fallback</tg-emoji></code>\n\n"
|
||||
"📌 <b>Пример использования в коде:</b>\n"
|
||||
"<code>text = 'Привет <tg-emoji emoji-id=\"5368324170671202286\">👍</tg-emoji>'\n"
|
||||
"await message.answer(text, parse_mode=\"HTML\")</code>\n\n"
|
||||
"⚠️ <b>Важно:</b>\n"
|
||||
"├─ Используйте <code>parse_mode=\"HTML\"</code>\n"
|
||||
"├─ Пользователи без Premium видят fallback\n"
|
||||
"└─ Работает только с кастомными эмодзи\n\n"
|
||||
"💡 <i>Попробуйте отправить эмодзи и ответить командой /emoji</i>"
|
||||
)
|
||||
|
||||
await message.answer(text, parse_mode="HTML")
|
||||
Reference in New Issue
Block a user