Stabilize staff concurrency and premium emoji; enable verified Drone deployment
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
Хендлеры для управления кастомными эмодзи админом
|
||||
Админ отправляет эмодзи боту, бот сохраняет emoji_id и использует его в сообщениях в чатах
|
||||
"""
|
||||
from src.utils.errors import public_error
|
||||
import logging
|
||||
from html import escape
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.filters import Command, StateFilter
|
||||
@@ -11,6 +13,7 @@ from aiogram.fsm.state import State, StatesGroup
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ..core.database import async_session_maker
|
||||
from ..core.access import is_admin
|
||||
from ..core.config import ADMIN_IDS
|
||||
from ..core.emoji_mapping_service import EmojiMappingService
|
||||
|
||||
@@ -26,73 +29,36 @@ class EmojiStates(StatesGroup):
|
||||
@router.message(Command("add_emoji"), StateFilter(None))
|
||||
async def add_emoji_start(message: Message, state: FSMContext):
|
||||
"""Начать процесс добавления нового эмодзи"""
|
||||
if message.from_user.id not in ADMIN_IDS:
|
||||
if not await is_admin(message.from_user.id):
|
||||
await message.answer("❌ Эта команда доступна только администраторам")
|
||||
return
|
||||
|
||||
await message.answer(
|
||||
"🎨 Отправьте эмодзи, который хотите зарегистрировать.\n\n"
|
||||
"Бот получит его <code>emoji_id</code> и будет использовать этот ID "
|
||||
"при отправке сообщений в чаты, чтобы эмодзи выглядел точно так же.",
|
||||
"🎨 Отправьте один премиум-эмодзи из панели Telegram.\n\n"
|
||||
"Бот сохранит его в каталоге для использования в шаблонах. "
|
||||
"В чатах и рассылках оформление сохраняется прямо из отправленного сообщения.",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
await state.set_state(EmojiStates.waiting_for_emoji)
|
||||
|
||||
|
||||
@router.message(EmojiStates.waiting_for_emoji)
|
||||
@router.message(EmojiStates.waiting_for_emoji, F.text)
|
||||
async def receive_emoji(message: Message, state: FSMContext):
|
||||
"""Получить эмодзи от админа и сохранить его emoji_id"""
|
||||
# Проверяем что это именно тект сообщение с эмодзи
|
||||
if not message.text or len(message.text) > 10:
|
||||
await message.answer(
|
||||
"❌ Пожалуйста, отправьте просто эмодзи или маленький текст с эмодзи"
|
||||
)
|
||||
entities = [entity for entity in message.entities or () if entity.type == "custom_emoji"]
|
||||
if len(entities) != 1:
|
||||
await message.answer("Отправьте один премиум-эмодзи из панели эмодзи Telegram. Обычный символ не содержит его ID.")
|
||||
return
|
||||
|
||||
emoji_text = message.text.strip()
|
||||
|
||||
# Проверяем что хотя бы один символ это эмодзи
|
||||
has_emoji = any(ord(c) > 127 for c in emoji_text)
|
||||
if not has_emoji:
|
||||
await message.answer(
|
||||
"❌ Текст не содержит эмодзи. Пожалуйста, отправьте эмодзи"
|
||||
)
|
||||
entity = entities[0]
|
||||
emoji_text = entity.extract_from(message.text)
|
||||
emoji_id = entity.custom_emoji_id
|
||||
if message.text.strip() != emoji_text or len(emoji_text) > 10 or not emoji_id or not emoji_id.isascii() or not emoji_id.isdigit():
|
||||
await message.answer("Отправьте только один премиум-эмодзи, без дополнительного текста.")
|
||||
return
|
||||
|
||||
# Извлекаем emoji_id из entities если это есть
|
||||
emoji_id = None
|
||||
|
||||
# Проверяем есть ли entities в сообщении (custom emoji имеют свой entitytype)
|
||||
if message.entities:
|
||||
for entity in message.entities:
|
||||
if entity.type == "custom_emoji":
|
||||
# Получаем text с этим entity
|
||||
emoji_id = entity.custom_emoji_id
|
||||
break
|
||||
|
||||
# Если нет custom_emoji entity, пробуем другой способ
|
||||
if not emoji_id:
|
||||
# Используем встроенный способ Telegram - отправляем тестовое сообщение с этим эмодзи
|
||||
# и смотрим entities
|
||||
try:
|
||||
# Отправляем сообщение с эмодзи обратно
|
||||
test_msg = await message.answer(
|
||||
f"Тестирую эмодзи: {emoji_text}",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
# Пытаемся получить emoji_id из реакции
|
||||
# В Telegram для premium emoji нужно обращаться к API
|
||||
# Но мы можем просто использовать сам emoji как ID - он уникален
|
||||
emoji_id = emoji_text
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing emoji: {e}")
|
||||
emoji_id = emoji_text
|
||||
|
||||
# Сохраняем в состояние
|
||||
await state.update_data(emoji_text=emoji_text, emoji_id=emoji_id if emoji_id else emoji_text)
|
||||
|
||||
await state.update_data(emoji_text=emoji_text, emoji_id=emoji_id)
|
||||
|
||||
await message.answer(
|
||||
f"✅ Получил эмодзи: <code>{emoji_text}</code>\n\n"
|
||||
f"✅ Получил эмодзи: <code>{escape(emoji_text or '')}</code>\n\n"
|
||||
f"Теперь отправьте описание этого эмодзи (для чего его использовать?)\n"
|
||||
f"Например: <code>Для лотереи</code>, <code>Для победителей</code> и т.д.",
|
||||
parse_mode="HTML"
|
||||
@@ -100,7 +66,7 @@ async def receive_emoji(message: Message, state: FSMContext):
|
||||
await state.set_state(EmojiStates.waiting_for_description)
|
||||
|
||||
|
||||
@router.message(EmojiStates.waiting_for_description)
|
||||
@router.message(EmojiStates.waiting_for_description, F.text)
|
||||
async def receive_emoji_description(message: Message, state: FSMContext):
|
||||
"""Получить описание эмодзи и сохранить в БД"""
|
||||
if not message.text:
|
||||
@@ -108,10 +74,18 @@ async def receive_emoji_description(message: Message, state: FSMContext):
|
||||
return
|
||||
|
||||
description = message.text.strip()
|
||||
if not description or len(description) > 255:
|
||||
await message.answer("Введите описание длиной от 1 до 255 символов.")
|
||||
return
|
||||
data = await state.get_data()
|
||||
emoji_text = data.get("emoji_text")
|
||||
emoji_id = data.get("emoji_id")
|
||||
|
||||
if not emoji_text or not emoji_id:
|
||||
await state.clear()
|
||||
await message.answer("Начните добавление заново: /add_emoji")
|
||||
return
|
||||
|
||||
# Сохраняем в БД
|
||||
async with async_session_maker() as session:
|
||||
emoji_service = EmojiMappingService(session)
|
||||
@@ -120,8 +94,8 @@ async def receive_emoji_description(message: Message, state: FSMContext):
|
||||
existing = await emoji_service.get_emoji_by_text(emoji_text, message.from_user.id)
|
||||
if existing:
|
||||
await message.answer(
|
||||
f"⚠️ Вы уже зарегистрировали этот эмодзи: {emoji_text}\n"
|
||||
f"Описание: <code>{existing.description}</code>",
|
||||
f"⚠️ Вы уже зарегистрировали этот эмодзи: {escape(emoji_text or '')}\n"
|
||||
f"Описание: <code>{escape(existing.description or '')}</code>",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
await state.clear()
|
||||
@@ -137,16 +111,18 @@ async def receive_emoji_description(message: Message, state: FSMContext):
|
||||
|
||||
await message.answer(
|
||||
f"✅ <b>Эмодзи успешно зарегистрировано!</b>\n\n"
|
||||
f"Эмодзи: <code>{emoji_text}</code>\n"
|
||||
f"Описание: <code>{description}</code>\n"
|
||||
f"Эмодзи: <code>{escape(emoji_text or '')}</code>\n"
|
||||
f"Описание: <code>{escape(description or '')}</code>\n"
|
||||
f"ID: <code>{emoji_id[:50]}</code>...\n\n"
|
||||
f"Теперь это эмодзи будет автоматически использоваться в сообщениях бота.",
|
||||
"Эмодзи сохранён в каталоге. В чатах и рассылках отправляйте его из панели Telegram.",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
except ValueError as error:
|
||||
await message.answer(str(error), parse_mode=None)
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering emoji: {e}")
|
||||
await message.answer(
|
||||
f"❌ Ошибка при сохранении эмодзи: {str(e)}",
|
||||
f"❌ Ошибка при сохранении эмодзи: {public_error(e)}",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
@@ -156,7 +132,7 @@ async def receive_emoji_description(message: Message, state: FSMContext):
|
||||
@router.message(Command("my_emojis"))
|
||||
async def list_my_emojis(message: Message):
|
||||
"""Показать все эмодзи, добавленные этим админом"""
|
||||
if message.from_user.id not in ADMIN_IDS:
|
||||
if not await is_admin(message.from_user.id):
|
||||
await message.answer("❌ Эта команда доступна только администраторам")
|
||||
return
|
||||
|
||||
@@ -174,7 +150,7 @@ async def list_my_emojis(message: Message):
|
||||
text = "🎨 <b>Ваши зарегистрированные эмодзи:</b>\n\n"
|
||||
for emoji in emojis:
|
||||
text += (
|
||||
f"<code>{emoji.emoji_text}</code> — {emoji.description}\n"
|
||||
f"<code>{escape(emoji.emoji_text or '')}</code> — {escape(emoji.description or '')}\n"
|
||||
f" ID: <code>{emoji.emoji_id[:30]}</code>...\n"
|
||||
f" Добавлено: <code>{emoji.created_at.strftime('%d.%m.%Y %H:%M')}</code>\n\n"
|
||||
)
|
||||
@@ -185,7 +161,7 @@ async def list_my_emojis(message: Message):
|
||||
@router.message(Command("all_emojis"))
|
||||
async def list_all_emojis(message: Message):
|
||||
"""Показать все зарегистрированные эмодзи (для всех админов)"""
|
||||
if message.from_user.id not in ADMIN_IDS:
|
||||
if not await is_admin(message.from_user.id):
|
||||
await message.answer("❌ Эта команда доступна только администраторам")
|
||||
return
|
||||
|
||||
@@ -202,8 +178,8 @@ async def list_all_emojis(message: Message):
|
||||
text = "🎨 <b>Все зарегистрированные эмодзи в системе:</b>\n\n"
|
||||
for emoji in emojis:
|
||||
text += (
|
||||
f"<code>{emoji.emoji_text}</code> — {emoji.description}\n"
|
||||
f" Админ: <code>{emoji.admin.first_name or 'Unknown'}</code> "
|
||||
f"<code>{escape(emoji.emoji_text or '')}</code> — {escape(emoji.description or '')}\n"
|
||||
f" Админ: <code>{escape(emoji.admin.first_name or 'Администратор') if emoji.admin else 'Администратор'}</code> "
|
||||
f"(ID: {emoji.admin_id})\n"
|
||||
f" Добавлено: <code>{emoji.created_at.strftime('%d.%m.%Y %H:%M')}</code>\n\n"
|
||||
)
|
||||
@@ -214,7 +190,7 @@ async def list_all_emojis(message: Message):
|
||||
@router.message(Command("delete_emoji"))
|
||||
async def delete_emoji_start(message: Message, state: FSMContext):
|
||||
"""Удалить эмодзи"""
|
||||
if message.from_user.id not in ADMIN_IDS:
|
||||
if not await is_admin(message.from_user.id):
|
||||
await message.answer("❌ Эта команда доступна только администраторам")
|
||||
return
|
||||
|
||||
@@ -233,7 +209,7 @@ async def delete_emoji_start(message: Message, state: FSMContext):
|
||||
for emoji in emojis:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(
|
||||
text=f"{emoji.emoji_text} ({emoji.description})",
|
||||
text=f"{emoji.emoji_text} ({emoji.description or ''})",
|
||||
callback_data=f"delete_emoji_{emoji.emoji_id}"
|
||||
)
|
||||
])
|
||||
@@ -258,14 +234,14 @@ async def delete_emoji_confirm(callback: CallbackQuery):
|
||||
await callback.answer("❌ Эмодзи не найден", show_alert=True)
|
||||
return
|
||||
|
||||
if emoji.admin_id != callback.from_user.id and callback.from_user.id not in ADMIN_IDS:
|
||||
if (not emoji.admin or emoji.admin.telegram_id != callback.from_user.id) and callback.from_user.id not in ADMIN_IDS:
|
||||
await callback.answer("❌ Вы не можете удалить эмодзи другого админа", show_alert=True)
|
||||
return
|
||||
|
||||
success = await emoji_service.delete_emoji(emoji_id)
|
||||
if success:
|
||||
await callback.answer(
|
||||
f"✅ Эмодзи <code>{emoji.emoji_text}</code> удалено",
|
||||
f"✅ Эмодзи {emoji.emoji_text} удалено",
|
||||
show_alert=True
|
||||
)
|
||||
await callback.message.delete()
|
||||
|
||||
Reference in New Issue
Block a user