Some checks failed
continuous-integration/drone/pr Build is failing
- Create emoji_mappings table to store emoji->emoji_id mappings - Add EmojiMappingService for managing emoji registration and replacement - Add admin emoji handlers (/add_emoji, /my_emojis, /delete_emoji, /all_emojis) - Create emoji message helper for automatic emoji processing - Add Alembic migration for emoji_mappings table - Integrate emoji router into main dispatcher - Add comprehensive documentation (EMOJI_SYSTEM.md) - Fix migration chain issue with merge_migration Features: - Admins can register premium emojis via /add_emoji command - Automatic emoji->emoji_id replacement before sending messages - Per-admin unique constraint on emoji registration - Track last used timestamp for analytics - Bulk operations support
245 lines
8.5 KiB
Markdown
245 lines
8.5 KiB
Markdown
# Система управления кастомными эмодзи
|
||
|
||
## Обзор
|
||
|
||
Система позволяет администраторам регистрировать премиум эмодзи и использовать их в сообщениях бота. Когда админ отправляет эмодзи боту:
|
||
|
||
1. Бот получает `emoji_id` от Telegram API
|
||
2. Сохраняет эмодзи в таблице `emoji_mappings`
|
||
3. При отправке сообщений в чаты бот автоматически использует `emoji_id` вместо текста эмодзи
|
||
|
||
Это обеспечивает, что эмодзи будут выглядеть точно так же, как их отправил админ, даже если это премиум эмодзи.
|
||
|
||
## Команды администратора
|
||
|
||
### 1. Добавить новый эмодзи
|
||
|
||
```
|
||
/add_emoji
|
||
```
|
||
|
||
Процесс:
|
||
1. Админ запускает команду `/add_emoji`
|
||
2. Бот просит отправить эмодзи
|
||
3. Админ отправляет эмодзи (например, 🎲)
|
||
4. Бот просит описание (для чего используется)
|
||
5. Админ отправляет描述 (например, "Для лотереи")
|
||
6. Бот сохраняет в БД и подтверждает
|
||
|
||
### 2. Просмотр своих эмодзи
|
||
|
||
```
|
||
/my_emojis
|
||
```
|
||
|
||
Показывает все эмодзи, добавленные этим админом:
|
||
- Сам эмодзи
|
||
- Описание
|
||
- ID (первые 30 символов)
|
||
- Дату добавления
|
||
|
||
### 3. Просмотр всех эмодзи в системе
|
||
|
||
```
|
||
/all_emojis
|
||
```
|
||
|
||
Показывает все эмодзи всех админов с информацией об администраторе
|
||
|
||
### 4. Удалить эмодзи
|
||
|
||
```
|
||
/delete_emoji
|
||
```
|
||
|
||
Админ может удалить только свои эмодзи. Процесс:
|
||
1. Вызвать команду
|
||
2. Выбрать эмодзи из список (кнопки)
|
||
3. Бот удалит из БД
|
||
|
||
## Использование в коде
|
||
|
||
### Простой способ - прямое использование эмодзи
|
||
|
||
```python
|
||
from aiogram.types import Message
|
||
|
||
async def handler(message: Message):
|
||
await message.answer(
|
||
text="🎲 Добро пожаловать на лотерею! 🏆",
|
||
parse_mode="HTML"
|
||
)
|
||
```
|
||
|
||
### С обработкой эмодзи
|
||
|
||
```python
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from src.core.emoji_message_helper import get_emoji_aware_text
|
||
from aiogram.types import Message
|
||
|
||
async def handler(message: Message, session: AsyncSession):
|
||
# Текст с эмодзи
|
||
original_text = "🎲 Выиграли! 🏆"
|
||
|
||
# Обработаны текст (эмодзи заменены на ID для корректного отображения)
|
||
processed_text = await get_emoji_aware_text(session, original_text)
|
||
|
||
await message.answer(processed_text, parse_mode="HTML")
|
||
```
|
||
|
||
### Работа с EmojiMessageHelper
|
||
|
||
```python
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from src.core.emoji_message_helper import EmojiMessageHelper
|
||
|
||
async def handler(message: Message, session: AsyncSession):
|
||
helper = EmojiMessageHelper(session)
|
||
|
||
# Обработка перед отправкой
|
||
text = "🎲 Лотерея начинается! 💎"
|
||
processed = await helper.process_text_before_send(text)
|
||
|
||
await message.answer(processed, parse_mode="HTML")
|
||
```
|
||
|
||
## Структура БД
|
||
|
||
### Таблица `emoji_mappings`
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---------|-----|---------|
|
||
| `id` | Integer | Primary Key |
|
||
| `emoji_text` | String(10) | Сам эмодзи (например, 🎲) |
|
||
| `emoji_id` | String(255) | telegram_emoji_id от API (уникален) |
|
||
| `admin_id` | Integer | FK на user (администратор) |
|
||
| `description` | String(255) | Описание назначения эмодзи |
|
||
| `created_at` | DateTime | Дата добавления |
|
||
| `last_used_at` | DateTime | Последнее использование |
|
||
|
||
### Уникальные ограничения
|
||
|
||
- `emoji_id` — уникален во всей системе
|
||
- `(emoji_text, admin_id)` — один админ не может добавить один эмодзи дважды
|
||
|
||
## API сервиса EmojiMappingService
|
||
|
||
### Регистрация эмодзи
|
||
|
||
```python
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from src.core.emoji_mapping_service import EmojiMappingService
|
||
|
||
async with async_session_maker() as session:
|
||
service = EmojiMappingService(session)
|
||
|
||
emoji = await service.register_emoji(
|
||
emoji_text="🎲",
|
||
emoji_id="telegram_emoji_id_here",
|
||
admin_id=12345,
|
||
description="Для лотереи"
|
||
)
|
||
```
|
||
|
||
### Получение эмодзи
|
||
|
||
```python
|
||
# По тексту
|
||
emoji = await service.get_emoji_by_text("🎲")
|
||
|
||
# По emoji_id
|
||
emoji = await service.get_emoji_by_id("telegram_emoji_id")
|
||
|
||
# Все эмодзи админа
|
||
emojis = await service.get_all_emoji_by_admin(admin_id=12345)
|
||
|
||
# Все эмодзи
|
||
all_emojis = await service.get_all_emojis()
|
||
```
|
||
|
||
### Замена эмодзи в тексте
|
||
|
||
```python
|
||
# Текст → с заменой эмодзи на ID
|
||
processed = await service.replace_emojis_in_text(
|
||
"🎲 Выиграли! 🏆"
|
||
)
|
||
|
||
# Обратно - ID → эмодзи
|
||
original = await service.restore_emojis_in_text(processed)
|
||
```
|
||
|
||
### Получить словарь маппинга
|
||
|
||
```python
|
||
# {emoji_text: emoji_id}
|
||
mapping = await service.get_emoji_mapping_dict()
|
||
# {'🎲': 'telegram_emoji_id_1', '🏆': 'telegram_emoji_id_2', ...}
|
||
```
|
||
|
||
## Примеры использования в разных рутерах
|
||
|
||
### В регистрации
|
||
|
||
```python
|
||
async def registration_complete(message: Message, session: AsyncSession):
|
||
text = "✅ Регистрация завершена! 🎉"
|
||
text = await get_emoji_aware_text(session, text)
|
||
await message.answer(text, parse_mode="HTML")
|
||
```
|
||
|
||
### В админ-панели
|
||
|
||
```python
|
||
async def lottery_created(callback: CallbackQuery, session: AsyncSession):
|
||
text = "🎰 Новый розыгрыш создан! 🏆"
|
||
text = await get_emoji_aware_text(session, text)
|
||
await callback.message.edit_text(text, parse_mode="HTML")
|
||
```
|
||
|
||
### В чатовой рассылке
|
||
|
||
```python
|
||
async def broadcast_message(message: Message, session: AsyncSession):
|
||
text = f"📢 Сообщение от админа: {message.text}\n\n💎 Удачи!"
|
||
text = await get_emoji_aware_text(session, text)
|
||
|
||
for user_id in target_users:
|
||
await bot.send_message(user_id, text, parse_mode="HTML")
|
||
```
|
||
|
||
## Важные моменты
|
||
|
||
1. **Parse Mode**: Всегда используйте `parse_mode="HTML"` при работе с эмодзи
|
||
2. **Кеширование ID**: Система не кеширует, каждый раз обращается к БД. Для оптимизации можно добавить кеширование
|
||
3. **Лог использования**: `last_used_at` обновляется автоматически при замене в тексте
|
||
4. **Удаление**: Удаленный эмодзи больше не будет заменяться в новых сообщениях
|
||
5. **Конфликты**: Если два админа добавляют один эмодзи - они сохранятся отдельно (разные admin_id)
|
||
|
||
## Миграция
|
||
|
||
Таблица создана миграцией:
|
||
```
|
||
migrations/versions/20260307_0100_add_emoji_mappings.py
|
||
```
|
||
|
||
Применить миграцию:
|
||
```bash
|
||
alembic upgrade head
|
||
```
|
||
|
||
## Trouble Shooting
|
||
|
||
### Эмодзи не отображается корректно
|
||
- Проверьте что используете `parse_mode="HTML"`
|
||
- Убедитесь что эмодзи зарегистрирован с помощью `/my_emojis`
|
||
|
||
### Ошибка "Can't parse entities"
|
||
- Это означает что есть конфликт форматирования
|
||
- Убедитесь что используете HTML теги (`<b>`, `<i>`, и т.д.), а не Markdown (`**`, `__`)
|
||
|
||
### Эмодзи не заменяется
|
||
- Проверьте что был зарегистрирован с помощью `/add_emoji`
|
||
- Убедитесь что используете функцию `get_emoji_aware_text()` перед отправкой
|