1353 lines
50 KiB
Markdown
1353 lines
50 KiB
Markdown
|
|
---
|
|||
|
|
name: Telegram Bot Product Features
|
|||
|
|
overview: Добавление продуктовых фич в Telegram бот для повышения вовлеченности пользователей и создания привычки ежедневного обучения. Фокус на простых, но эффективных механиках для MVP стадии.
|
|||
|
|
todos:
|
|||
|
|
- id: daily-command
|
|||
|
|
content: Реализовать команду /daily для карточки дня с изображением
|
|||
|
|
status: pending
|
|||
|
|
- id: stats-command
|
|||
|
|
content: Реализовать команду /stats и backend endpoint для статистики
|
|||
|
|
status: pending
|
|||
|
|
- id: streaks-tracking
|
|||
|
|
content: Добавить tracking стриков в PostgreSQL (таблица user_streaks)
|
|||
|
|
status: pending
|
|||
|
|
- id: quiz-command
|
|||
|
|
content: Реализовать мини-квиз /quiz с inline keyboard
|
|||
|
|
status: pending
|
|||
|
|
- id: reminders-command
|
|||
|
|
content: Реализовать настройку напоминаний /reminders и cron job для отправки
|
|||
|
|
status: pending
|
|||
|
|
- id: achievements
|
|||
|
|
content: Добавить автоматические поздравления с достижениями
|
|||
|
|
status: pending
|
|||
|
|
- id: collectible-cards-system
|
|||
|
|
content: Реализовать систему коллекционных карточек с редкостью (Common/Rare/Legendary)
|
|||
|
|
status: pending
|
|||
|
|
- id: collection-command
|
|||
|
|
content: Реализовать команду /collection для просмотра коллекции
|
|||
|
|
status: pending
|
|||
|
|
- id: card-variants-generation
|
|||
|
|
content: Добавить генерацию изображений с разными рамками для редкостей
|
|||
|
|
status: pending
|
|||
|
|
- id: tests
|
|||
|
|
content: Написать unit и integration тесты для новых фич
|
|||
|
|
status: pending
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# План продуктовых фич для Telegram бота Mnemo Cards
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
|
|||
|
|
Текущий бот выполняет технические функции (бэкап, авторизация, админ-панель) и имеет базовую команду `/share`. Предлагается добавить **продуктовые фичи для вовлечения и контента**, которые помогут:
|
|||
|
|
|
|||
|
|
- Создать привычку ежедневного использования
|
|||
|
|
- Повысить удержание пользователей
|
|||
|
|
- Предоставить дополнительную ценность помимо основного приложения
|
|||
|
|
- Собрать аналитику вовлеченности
|
|||
|
|
|
|||
|
|
## Приоритетные фичи (MVP)
|
|||
|
|
|
|||
|
|
### 1. 🎴 Карточка дня (`/daily` команда)
|
|||
|
|
|
|||
|
|
**Описание:** Пользователь получает случайную карточку для изучения
|
|||
|
|
|
|||
|
|
**UX Flow:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /daily
|
|||
|
|
Бот:
|
|||
|
|
[Красивое изображение карточки]
|
|||
|
|
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
|
|||
|
|
💡 Мнемоника: [mnemo text]
|
|||
|
|
🔊 Транскрипция: [transcription]
|
|||
|
|
|
|||
|
|
Хочешь проверить себя? Нажми /quiz
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Реализация:**
|
|||
|
|
|
|||
|
|
- Новая команда в [`bin/main.dart`](mnemo_cards_telegram_bot/bin/main.dart) → `teledart.onCommand('daily')`
|
|||
|
|
- Использовать существующий `BackendClient.getRandomCard()`
|
|||
|
|
- Повторно использовать `ImageGenerator` из `/share` команды
|
|||
|
|
- Отправлять фото + caption с информацией о карточке
|
|||
|
|
|
|||
|
|
**Backend изменения:**
|
|||
|
|
|
|||
|
|
- Endpoint уже есть: `/api/v2/telegram-bot/random-card`
|
|||
|
|
- Добавить опциональный параметр `?userId=` для персонализации (учитывать уже изученные карточки)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 📊 Статистика и стрики (`/stats` команда)
|
|||
|
|
|
|||
|
|
**Описание:** Показать пользователю его прогресс и мотивационные метрики
|
|||
|
|
|
|||
|
|
**UX Flow:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /stats
|
|||
|
|
Бот:
|
|||
|
|
📈 Твоя статистика
|
|||
|
|
|
|||
|
|
🔥 Стрик: 7 дней
|
|||
|
|
📚 Изучено карточек: 42
|
|||
|
|
✅ Завершено тестов: 5
|
|||
|
|
⏱ Последняя активность: 2 часа назад
|
|||
|
|
|
|||
|
|
Так держать! Продолжай в том же духе! 💪
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Реализация:**
|
|||
|
|
|
|||
|
|
- Новая команда `/stats` в [`bin/main.dart`](mnemo_cards_telegram_bot/bin/main.dart)
|
|||
|
|
- Новый метод в [`lib/backend_client.dart`](mnemo_cards_telegram_bot/lib/backend_client.dart):
|
|||
|
|
```dart
|
|||
|
|
Future<UserStats?> getUserStats(String telegramUserId)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|
|||
|
|
**Backend изменения:**
|
|||
|
|
|
|||
|
|
- Новый endpoint: `GET /api/v2/telegram-bot/users/:telegramUserId/stats`
|
|||
|
|
- Использовать существующую логику из [`lib/statistics/statistics_calculator.dart`](mnemo_cards_backend/lib/statistics/statistics_calculator.dart)
|
|||
|
|
- Добавить tracking стриков (consecutive days of activity)
|
|||
|
|
- Новая таблица `user_streaks` в PostgreSQL:
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE user_streaks (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
user_id INTEGER REFERENCES users(id),
|
|||
|
|
telegram_user_id TEXT,
|
|||
|
|
current_streak INTEGER DEFAULT 0,
|
|||
|
|
longest_streak INTEGER DEFAULT 0,
|
|||
|
|
last_activity_date DATE,
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. ⏰ Ежедневные напоминания
|
|||
|
|
|
|||
|
|
**Описание:** Бот отправляет напоминание о занятиях в выбранное время
|
|||
|
|
|
|||
|
|
**UX Flow:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /reminders
|
|||
|
|
Бот:
|
|||
|
|
⏰ Настрой напоминания
|
|||
|
|
|
|||
|
|
[Inline keyboard:]
|
|||
|
|
- 09:00 утра
|
|||
|
|
- 12:00 день
|
|||
|
|
- 18:00 вечер
|
|||
|
|
- 21:00 ночь
|
|||
|
|
- 🚫 Отключить
|
|||
|
|
|
|||
|
|
После выбора:
|
|||
|
|
Бот: ✅ Буду напоминать тебе каждый день в 18:00!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
В выбранное время:
|
|||
|
|
Бот:
|
|||
|
|
👋 Время изучать новые слова!
|
|||
|
|
|
|||
|
|
Попробуй карточку дня → /daily
|
|||
|
|
Или пройди быстрый квиз → /quiz
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Реализация:**
|
|||
|
|
|
|||
|
|
- Новая команда `/reminders` с inline keyboard (используя `teledart.sendMessage` с `replyMarkup`)
|
|||
|
|
- Хранение настроек в БД (новая таблица `user_reminder_settings`)
|
|||
|
|
- **Cron job** в [`lib/cron/`](mnemo_cards_backend/lib/cron/) для отправки напоминаний:
|
|||
|
|
```dart
|
|||
|
|
// lib/cron/send_reminders.dart
|
|||
|
|
class SendRemindersTask extends Task {
|
|||
|
|
@override
|
|||
|
|
String get name => 'Send Reminders';
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Duration get interval => const Duration(minutes: 30);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<void> run() async {
|
|||
|
|
// Проверять каждые 30 минут, кому нужно отправить
|
|||
|
|
// Отправлять через Telegram Bot API
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|
|||
|
|
**Backend изменения:**
|
|||
|
|
|
|||
|
|
- Новый endpoint: `POST /api/v2/telegram-bot/reminders/set`
|
|||
|
|
- Новый endpoint: `GET /api/v2/telegram-bot/reminders/pending`
|
|||
|
|
- Новая таблица:
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE user_reminder_settings (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT UNIQUE,
|
|||
|
|
reminder_time TIME, -- время в UTC
|
|||
|
|
timezone TEXT, -- часовой пояс пользователя
|
|||
|
|
enabled BOOLEAN DEFAULT true,
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 🎯 Мини-квиз (`/quiz` команда)
|
|||
|
|
|
|||
|
|
**Описание:** Быстрая проверка знаний из 3-5 вопросов прямо в боте
|
|||
|
|
|
|||
|
|
**UX Flow:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /quiz
|
|||
|
|
Бот:
|
|||
|
|
🎯 Быстрый квиз
|
|||
|
|
|
|||
|
|
Вопрос 1/3
|
|||
|
|
Как переводится "el perro"?
|
|||
|
|
|
|||
|
|
[Inline keyboard:]
|
|||
|
|
A) кошка
|
|||
|
|
B) собака ✅
|
|||
|
|
C) птица
|
|||
|
|
D) рыба
|
|||
|
|
|
|||
|
|
После ответа:
|
|||
|
|
Бот:
|
|||
|
|
✅ Правильно! (или ❌ Неправильно, правильный ответ: собака)
|
|||
|
|
|
|||
|
|
[Следующий вопрос...]
|
|||
|
|
|
|||
|
|
После завершения:
|
|||
|
|
Бот:
|
|||
|
|
🎉 Квиз завершен!
|
|||
|
|
|
|||
|
|
Правильных ответов: 2/3
|
|||
|
|
Твой результат: 67%
|
|||
|
|
|
|||
|
|
Продолжай практиковаться → /daily
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Реализация:**
|
|||
|
|
|
|||
|
|
- Новая команда `/quiz` с inline keyboard
|
|||
|
|
- Состояние квиза хранить в памяти (Map с ключом chat_id)
|
|||
|
|
- Использовать `teledart.onCallbackQuery` для обработки нажатий кнопок
|
|||
|
|
- Генерировать вопросы на бэкенде:
|
|||
|
|
- Правильный ответ
|
|||
|
|
- 3 неправильных варианта из других карточек
|
|||
|
|
|
|||
|
|
**Backend изменения:**
|
|||
|
|
|
|||
|
|
- Новый endpoint: `POST /api/v2/telegram-bot/quiz/generate`
|
|||
|
|
- Параметр: `userId` (опционально, для персонализации)
|
|||
|
|
- Параметр: `count` (количество вопросов, default: 3)
|
|||
|
|
- Endpoint возвращает:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"quizId": "uuid",
|
|||
|
|
"questions": [
|
|||
|
|
{
|
|||
|
|
"questionId": "uuid",
|
|||
|
|
"question": "Как переводится 'el perro'?",
|
|||
|
|
"options": ["кошка", "собака", "птица", "рыба"],
|
|||
|
|
"correctIndex": 1
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Новый endpoint: `POST /api/v2/telegram-bot/quiz/submit`
|
|||
|
|
- Записывать результаты квиза для статистики
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. 🎊 Мотивационные события (автоматические)
|
|||
|
|
|
|||
|
|
**Описание:** Бот автоматически поздравляет с достижениями
|
|||
|
|
|
|||
|
|
**События:**
|
|||
|
|
|
|||
|
|
- 🔥 Стрик 3 дня: "Отличное начало! Продолжай в том же духе!"
|
|||
|
|
- 🔥 Стрик 7 дней: "Целая неделя! Ты молодец! 🎉"
|
|||
|
|
- 🔥 Стрик 14 дней: "Две недели ежедневных занятий! Потрясающе!"
|
|||
|
|
- 🔥 Стрик 30 дней: "Месяц без перерывов! Ты легенда! 🏆"
|
|||
|
|
- 📚 Изучено 10/50/100 карточек
|
|||
|
|
- ✅ Завершено 5/10/20 тестов
|
|||
|
|
|
|||
|
|
**Реализация:**
|
|||
|
|
|
|||
|
|
- **Webhook или cron job** для проверки достижений
|
|||
|
|
- Новый cron task: `CheckAchievementsTask` каждый час
|
|||
|
|
- При достижении milestone → отправить сообщение пользователю
|
|||
|
|
|
|||
|
|
**Backend изменения:**
|
|||
|
|
|
|||
|
|
- Новая таблица `user_achievements`:
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE user_achievements (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT,
|
|||
|
|
achievement_type TEXT, -- 'streak_3', 'streak_7', 'cards_10', etc.
|
|||
|
|
achieved_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
notified BOOLEAN DEFAULT false
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Endpoint для получения новых достижений: `GET /api/v2/telegram-bot/achievements/pending`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 6. 🃏 Коллекционные карточки (Collectible Cards System)
|
|||
|
|
|
|||
|
|
**Описание:** Система коллекционирования карточек с разными уровнями редкости. Одно и то же слово может иметь несколько вариантов карточек с разными изображениями и редкостью.
|
|||
|
|
|
|||
|
|
#### Концепция редкости
|
|||
|
|
|
|||
|
|
**Три уровня редкости:**
|
|||
|
|
|
|||
|
|
1. **🟢 Common (Обычная)**
|
|||
|
|
- Базовое изображение карточки
|
|||
|
|
- Серая рамка
|
|||
|
|
- Получение: автоматически при изучении слова или через `/daily`
|
|||
|
|
|
|||
|
|
2. **🔵 Rare (Редкая)**
|
|||
|
|
- Улучшенное/детальное изображение
|
|||
|
|
- Синяя рамка с эффектом свечения
|
|||
|
|
- Получение: за достижения (стрик 7 дней, квиз 100%, и т.д.)
|
|||
|
|
|
|||
|
|
3. **🟣 Legendary (Легендарная)**
|
|||
|
|
- Уникальное артовое изображение
|
|||
|
|
- Золотая рамка с блеском и эффектами
|
|||
|
|
- Получение: за высокие достижения (стрик 30+ дней, особые награды)
|
|||
|
|
|
|||
|
|
#### Пример: слово "el perro"
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
el perro (собака)
|
|||
|
|
├─ 🟢 Common ✅ Получена 15.12.2024
|
|||
|
|
├─ 🔵 Rare ✅ Получена 20.12.2024 (стрик 7 дней)
|
|||
|
|
└─ 🟣 Legendary 🔒 Требуется: стрик 30 дней (текущий: 12)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### UX Flow
|
|||
|
|
|
|||
|
|
**Команда `/collection` - просмотр коллекции:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /collection
|
|||
|
|
Бот:
|
|||
|
|
📚 Твоя коллекция
|
|||
|
|
|
|||
|
|
📊 Общий прогресс: 45/450 карточек (10%)
|
|||
|
|
🟢 Common: 15/150 слов (10%)
|
|||
|
|
🔵 Rare: 8/150 слов (5%)
|
|||
|
|
🟣 Legendary: 2/150 слов (1%)
|
|||
|
|
|
|||
|
|
[Inline keyboard:]
|
|||
|
|
- 📖 По словам
|
|||
|
|
- 🎨 По редкости
|
|||
|
|
- 📦 По пакам
|
|||
|
|
- 🔍 Поиск слова
|
|||
|
|
|
|||
|
|
При выборе "По словам":
|
|||
|
|
|
|||
|
|
🇪🇸 Испанский (15 слов)
|
|||
|
|
|
|||
|
|
📝 el perro (собака)
|
|||
|
|
✅🟢 ✅🔵 🔒🟣
|
|||
|
|
|
|||
|
|
📝 el gato (кот)
|
|||
|
|
✅🟢 🔒🔵 🔒🟣
|
|||
|
|
|
|||
|
|
📝 la casa (дом)
|
|||
|
|
✅🟢 🔒🔵 🔒🟣
|
|||
|
|
|
|||
|
|
[Кнопка: Показать детали]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Команда `/showcase [слово]` - детали карточки:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /showcase el perro
|
|||
|
|
Бот:
|
|||
|
|
[Отправляет изображение Common версии с серой рамкой]
|
|||
|
|
|
|||
|
|
🟢 COMMON
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
✅ В коллекции с 15.12.2024
|
|||
|
|
|
|||
|
|
[Отправляет изображение Rare версии с синей рамкой]
|
|||
|
|
|
|||
|
|
🔵 RARE
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
✅ Получена за стрик 7 дней (20.12.2024)
|
|||
|
|
|
|||
|
|
[Placeholder для Legendary]
|
|||
|
|
|
|||
|
|
🟣 LEGENDARY
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
🔒 Как получить: стрик 30 дней
|
|||
|
|
📊 Твой прогресс: 12/30 дней (осталось 18)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновленная команда `/daily` с коллекционированием:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /daily
|
|||
|
|
Бот:
|
|||
|
|
[Красивое изображение карточки с рамкой редкости]
|
|||
|
|
|
|||
|
|
🎴 Карточка дня
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
|
|||
|
|
💡 Мнемоника: [текст]
|
|||
|
|
🔊 Транскрипция: [текст]
|
|||
|
|
|
|||
|
|
📦 Коллекция:
|
|||
|
|
✅ 🟢 Common - уже в коллекции
|
|||
|
|
🔒 🔵 Rare - пройди квиз на 100%
|
|||
|
|
🔒 🟣 Legendary - стрик 30 дней
|
|||
|
|
|
|||
|
|
Хочешь проверить себя? → /quiz
|
|||
|
|
Посмотреть коллекцию → /collection
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Получение редкой карточки (событие):**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[После достижения стрика 7 дней]
|
|||
|
|
|
|||
|
|
Бот:
|
|||
|
|
🎉 ПОЗДРАВЛЯЕМ!
|
|||
|
|
|
|||
|
|
🔥 Ты держишь стрик 7 дней подряд!
|
|||
|
|
|
|||
|
|
🎁 Награда: случайная RARE карточка!
|
|||
|
|
|
|||
|
|
[Анимация/эффект открытия "пака"]
|
|||
|
|
[Задержка 1-2 секунды]
|
|||
|
|
|
|||
|
|
[Отправляет изображение с синей рамкой и блеском]
|
|||
|
|
|
|||
|
|
✨ 🔵 RARE CARD! ✨
|
|||
|
|
|
|||
|
|
🇪🇸 el perro
|
|||
|
|
🇷🇺 собака
|
|||
|
|
|
|||
|
|
Это улучшенная версия карточки!
|
|||
|
|
Теперь у тебя 2/3 версии этого слова!
|
|||
|
|
|
|||
|
|
Посмотри свою коллекцию → /collection
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновленная команда `/stats` с коллекцией:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /stats
|
|||
|
|
Бот:
|
|||
|
|
📈 Твоя статистика
|
|||
|
|
|
|||
|
|
🔥 Стрик: 12 дней
|
|||
|
|
📚 Изучено слов: 42
|
|||
|
|
✅ Завершено тестов: 5
|
|||
|
|
|
|||
|
|
📦 Коллекция карточек:
|
|||
|
|
🟢 Common: 42/150 (28%)
|
|||
|
|
🔵 Rare: 8/150 (5%)
|
|||
|
|
🟣 Legendary: 2/150 (1%)
|
|||
|
|
|
|||
|
|
🏆 Ближайшие награды:
|
|||
|
|
- 🔥 Стрик 14 дней → 1 Rare карточка (через 2 дня)
|
|||
|
|
- 🔥 Стрик 30 дней → 1 Legendary! (через 18 дней)
|
|||
|
|
- 🎯 Квиз 100% → Rare версия карточки из квиза
|
|||
|
|
|
|||
|
|
Продолжай в том же духе! 💪
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Условия получения карточек
|
|||
|
|
|
|||
|
|
**🟢 Common (автоматически):**
|
|||
|
|
- Изучил слово в основном приложении
|
|||
|
|
- Получил через `/daily` (первый раз)
|
|||
|
|
- Прошел тест, где встретилось слово
|
|||
|
|
|
|||
|
|
**🔵 Rare (за активность):**
|
|||
|
|
- 🔥 Стрик 7 дней → 1 случайная Rare
|
|||
|
|
- 🔥 Стрик 14 дней → 2 случайные Rare
|
|||
|
|
- 🎯 Квиз 100% правильных → Rare версия одной карточки из квиза
|
|||
|
|
- 📅 Использовал `/daily` 10 раз → выбери любую Rare
|
|||
|
|
- ⭐ Завершил пак полностью → все карточки пака в Rare версии
|
|||
|
|
- 📚 Изучил 50 карточек → 3 Rare на выбор
|
|||
|
|
|
|||
|
|
**🟣 Legendary (за высокие достижения):**
|
|||
|
|
- 🔥 Стрик 30 дней → 1 случайная Legendary
|
|||
|
|
- 🔥 Стрик 60 дней → 2 случайные Legendary
|
|||
|
|
- 🔥 Стрик 100 дней → 5 Legendary на выбор
|
|||
|
|
- 🎯 Прошел 50 квизов → 1 Legendary на выбор
|
|||
|
|
- 🎯 10 квизов подряд на 100% → 2 Legendary
|
|||
|
|
- 📚 Завершил 5 паков → 3 Legendary на выбор
|
|||
|
|
- 🏆 Топ-10 в месячном рейтинге активности → 2 Legendary
|
|||
|
|
|
|||
|
|
#### Визуальные отличия
|
|||
|
|
|
|||
|
|
**Генерация изображений с разными рамками:**
|
|||
|
|
|
|||
|
|
```dart
|
|||
|
|
// Обновленный ImageGenerator
|
|||
|
|
class ImageGenerator {
|
|||
|
|
Future<Uint8List?> generateCardImage({
|
|||
|
|
required GameCardModel card,
|
|||
|
|
required CardRarity rarity,
|
|||
|
|
}) async {
|
|||
|
|
final baseImage = await _loadCardImage(card.image);
|
|||
|
|
final frame = _getFrameForRarity(rarity);
|
|||
|
|
final effects = _getEffectsForRarity(rarity);
|
|||
|
|
|
|||
|
|
return _composeImage(baseImage, frame, effects);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FrameStyle _getFrameForRarity(CardRarity rarity) {
|
|||
|
|
switch (rarity) {
|
|||
|
|
case CardRarity.common:
|
|||
|
|
return FrameStyle(
|
|||
|
|
color: 0xFF808080, // Серый
|
|||
|
|
width: 40,
|
|||
|
|
glowIntensity: 0,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
case CardRarity.rare:
|
|||
|
|
return FrameStyle(
|
|||
|
|
color: 0xFF4169E1, // Синий
|
|||
|
|
width: 50,
|
|||
|
|
glowIntensity: 0.3,
|
|||
|
|
glowColor: 0xFF6495ED,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
case CardRarity.legendary:
|
|||
|
|
return FrameStyle(
|
|||
|
|
color: 0xFFFFD700, // Золотой
|
|||
|
|
width: 60,
|
|||
|
|
glowIntensity: 0.6,
|
|||
|
|
glowColor: 0xFFFFA500,
|
|||
|
|
sparkles: true,
|
|||
|
|
gradient: true,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Цветовая схема:**
|
|||
|
|
- 🟢 Common: #808080 (серый)
|
|||
|
|
- 🔵 Rare: #4169E1 (royal blue) + свечение
|
|||
|
|
- 🟣 Legendary: #FFD700 (золотой) + градиент + блестки
|
|||
|
|
|
|||
|
|
#### Социальный аспект
|
|||
|
|
|
|||
|
|
**Команда `/share_card [слово]` - поделиться карточкой:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Пользователь: /share_card el perro
|
|||
|
|
Бот:
|
|||
|
|
Какую версию карточки хочешь показать?
|
|||
|
|
|
|||
|
|
[Inline keyboard:]
|
|||
|
|
- 🟢 Common
|
|||
|
|
- 🔵 Rare ✅
|
|||
|
|
- 🟣 Legendary 🔒
|
|||
|
|
|
|||
|
|
После выбора Rare:
|
|||
|
|
|
|||
|
|
[Генерирует красивое изображение с синей рамкой]
|
|||
|
|
|
|||
|
|
Вот твоя RARE карточка! 🔵
|
|||
|
|
|
|||
|
|
Ты можешь поделиться ей:
|
|||
|
|
- Отправь друзьям в Telegram
|
|||
|
|
- Опубликуй в соц. сетях
|
|||
|
|
|
|||
|
|
💬 Сообщение для друзей:
|
|||
|
|
"Смотри какую редкую карточку я собрал в Mnemo Cards! 🔵
|
|||
|
|
|
|||
|
|
Присоединяйся и собери свою коллекцию!
|
|||
|
|
[ссылка на бота]"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Техническая реализация
|
|||
|
|
|
|||
|
|
**Новые таблицы PostgreSQL:**
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- Варианты карточек (все возможные collectibles)
|
|||
|
|
CREATE TABLE card_variants (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
card_id TEXT NOT NULL, -- ID основной карточки
|
|||
|
|
rarity TEXT NOT NULL, -- 'common', 'rare', 'legendary'
|
|||
|
|
image_variant TEXT, -- Путь к альтернативному изображению
|
|||
|
|
-- Если NULL, используется базовое с рамкой
|
|||
|
|
frame_color TEXT NOT NULL, -- HEX цвет рамки
|
|||
|
|
frame_width INTEGER DEFAULT 40, -- Ширина рамки в px
|
|||
|
|
has_glow BOOLEAN DEFAULT false, -- Эффект свечения
|
|||
|
|
has_sparkles BOOLEAN DEFAULT false, -- Эффект блесток
|
|||
|
|
unlock_condition TEXT, -- Условие получения
|
|||
|
|
description TEXT, -- Описание варианта
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
|
|||
|
|
UNIQUE(card_id, rarity)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_variants_card_id ON card_variants(card_id);
|
|||
|
|
CREATE INDEX idx_variants_rarity ON card_variants(rarity);
|
|||
|
|
|
|||
|
|
-- Коллекция пользователя
|
|||
|
|
CREATE TABLE user_card_collection (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT NOT NULL,
|
|||
|
|
card_variant_id INTEGER REFERENCES card_variants(id),
|
|||
|
|
obtained_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
obtained_via TEXT, -- 'daily', 'quiz', 'streak_7', 'achievement', etc.
|
|||
|
|
|
|||
|
|
UNIQUE(telegram_user_id, card_variant_id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_collection_user ON user_card_collection(telegram_user_id);
|
|||
|
|
CREATE INDEX idx_collection_obtained ON user_card_collection(obtained_at);
|
|||
|
|
|
|||
|
|
-- Прогресс коллекции (кэш для быстрого доступа)
|
|||
|
|
CREATE TABLE user_collection_progress (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT UNIQUE NOT NULL,
|
|||
|
|
total_cards INTEGER DEFAULT 0,
|
|||
|
|
common_count INTEGER DEFAULT 0,
|
|||
|
|
rare_count INTEGER DEFAULT 0,
|
|||
|
|
legendary_count INTEGER DEFAULT 0,
|
|||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Backend endpoints:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/v2/telegram-bot/collection/:telegramUserId
|
|||
|
|
- Получить коллекцию пользователя
|
|||
|
|
- Query params: ?rarity=common|rare|legendary, ?pack_id=X
|
|||
|
|
|
|||
|
|
POST /api/v2/telegram-bot/collection/add
|
|||
|
|
- Добавить карточку в коллекцию
|
|||
|
|
- Body: { telegramUserId, cardId, rarity, obtainedVia }
|
|||
|
|
|
|||
|
|
GET /api/v2/telegram-bot/card-variants/:cardId
|
|||
|
|
- Получить все варианты карточки (common, rare, legendary)
|
|||
|
|
|
|||
|
|
GET /api/v2/telegram-bot/collection/:telegramUserId/progress
|
|||
|
|
- Получить статистику коллекции
|
|||
|
|
|
|||
|
|
POST /api/v2/telegram-bot/collection/award-rare
|
|||
|
|
- Наградить случайной rare карточкой
|
|||
|
|
- Body: { telegramUserId, reason }
|
|||
|
|
|
|||
|
|
POST /api/v2/telegram-bot/collection/award-legendary
|
|||
|
|
- Наградить legendary карточкой
|
|||
|
|
- Body: { telegramUserId, cardId (optional), reason }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменения в боте:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
mnemo_cards_telegram_bot/
|
|||
|
|
├── lib/
|
|||
|
|
│ ├── backend_client.dart [ОБНОВИТЬ]
|
|||
|
|
│ │ + getCollection()
|
|||
|
|
│ │ + getCardVariants()
|
|||
|
|
│ │ + addToCollection()
|
|||
|
|
│ │ + getCollectionProgress()
|
|||
|
|
│ │ + awardRareCard()
|
|||
|
|
│ │ + awardLegendaryCard()
|
|||
|
|
│ │
|
|||
|
|
│ ├── image_generator.dart [ОБНОВИТЬ]
|
|||
|
|
│ │ + generateCardImage(card, rarity)
|
|||
|
|
│ │ + _getFrameForRarity()
|
|||
|
|
│ │ + _addGlowEffect()
|
|||
|
|
│ │ + _addSparkles()
|
|||
|
|
│ │
|
|||
|
|
│ └── models/
|
|||
|
|
│ ├── card_rarity.dart [НОВЫЙ]
|
|||
|
|
│ ├── card_variant.dart [НОВЫЙ]
|
|||
|
|
│ └── user_collection.dart [НОВЫЙ]
|
|||
|
|
│
|
|||
|
|
└── bin/
|
|||
|
|
└── main.dart [ОБНОВИТЬ]
|
|||
|
|
+ onCommand('collection')
|
|||
|
|
+ onCommand('showcase')
|
|||
|
|
+ onCommand('share_card')
|
|||
|
|
+ обновить onCommand('daily') - показывать прогресс коллекции
|
|||
|
|
+ обновить onCommand('stats') - добавить статистику коллекции
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменения в бэкенде:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
mnemo_cards_backend/
|
|||
|
|
├── lib/
|
|||
|
|
│ ├── api/v2/
|
|||
|
|
│ │ └── telegram_bot_api_v2.dart [ОБНОВИТЬ]
|
|||
|
|
│ │ + GET /telegram-bot/collection/:id
|
|||
|
|
│ │ + POST /telegram-bot/collection/add
|
|||
|
|
│ │ + GET /telegram-bot/card-variants/:cardId
|
|||
|
|
│ │ + POST /telegram-bot/collection/award-rare
|
|||
|
|
│ │ + POST /telegram-bot/collection/award-legendary
|
|||
|
|
│ │
|
|||
|
|
│ ├── database/tables/
|
|||
|
|
│ │ ├── card_variants.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── user_card_collection.dart [НОВЫЙ]
|
|||
|
|
│ │ └── user_collection_progress.dart [НОВЫЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── collection/
|
|||
|
|
│ │ ├── collection_manager.dart [НОВЫЙ]
|
|||
|
|
│ │ │ - Логика добавления карточек
|
|||
|
|
│ │ │ - Проверка условий unlock
|
|||
|
|
│ │ │ - Награждение карточками
|
|||
|
|
│ │ │
|
|||
|
|
│ │ └── rarity_calculator.dart [НОВЫЙ]
|
|||
|
|
│ │ - Определение редкости при награждении
|
|||
|
|
│ │ - Случайный выбор карточек для наград
|
|||
|
|
│ │
|
|||
|
|
│ └── cron/
|
|||
|
|
│ └── collection_rewards.dart [НОВЫЙ]
|
|||
|
|
- Автоматическое награждение за стрики/достижения
|
|||
|
|
- Интеграция с системой достижений
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Геймификация и мотивация
|
|||
|
|
|
|||
|
|
**Прогресс-системы:**
|
|||
|
|
|
|||
|
|
1. **Коллекционирование по пакам:**
|
|||
|
|
- "Собери все Common карточки пака 'Животные'" → награда: 1 Rare любой карточки пака
|
|||
|
|
- "Собери все Rare карточки пака" → награда: 1 Legendary из пака
|
|||
|
|
|
|||
|
|
2. **Коллекционирование по редкости:**
|
|||
|
|
- "Собери 10 Rare карточек" → награда: 1 Legendary на выбор
|
|||
|
|
- "Собери первую Legendary" → достижение + особая награда
|
|||
|
|
|
|||
|
|
3. **Тематические коллекции:**
|
|||
|
|
- "Собери все карточки с животными" → награда
|
|||
|
|
- "Собери все карточки с едой" → награда
|
|||
|
|
|
|||
|
|
**Лидерборд коллекционеров (опционально):**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Команда: /leaderboard collection
|
|||
|
|
|
|||
|
|
Бот:
|
|||
|
|
🏆 Топ коллекционеров
|
|||
|
|
|
|||
|
|
🥇 @username1 - 145/450 (32%)
|
|||
|
|
🟣 15 Legendary
|
|||
|
|
|
|||
|
|
🥈 @username2 - 132/450 (29%)
|
|||
|
|
🟣 12 Legendary
|
|||
|
|
|
|||
|
|
🥉 @username3 - 128/450 (28%)
|
|||
|
|
🟣 10 Legendary
|
|||
|
|
|
|||
|
|
...
|
|||
|
|
|
|||
|
|
🔹 Ты - #15 - 45/450 (10%)
|
|||
|
|
🟣 2 Legendary
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Канал (если будет)
|
|||
|
|
|
|||
|
|
**Еженедельный пост "Карточка недели":**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
🌟 КАРТОЧКА НЕДЕЛИ
|
|||
|
|
|
|||
|
|
[Изображение Legendary карточки с золотой рамкой]
|
|||
|
|
|
|||
|
|
🟣 LEGENDARY
|
|||
|
|
🇪🇸 el perro (собака)
|
|||
|
|
|
|||
|
|
Это одна из самых популярных легендарных карточек!
|
|||
|
|
|
|||
|
|
💎 Как получить:
|
|||
|
|
- 🔥 Стрик 30 дней
|
|||
|
|
- 🎯 Завершить 3 пака подряд
|
|||
|
|
|
|||
|
|
📊 Статистика:
|
|||
|
|
- У 5% пользователей есть эта карточка
|
|||
|
|
- Средний срок получения: 45 дней
|
|||
|
|
|
|||
|
|
Сколько Legendary карточек у тебя? 👇
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Событие "Редкие выходные":**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
🎉 СОБЫТИЕ: РЕДКИЕ ВЫХОДНЫЕ!
|
|||
|
|
|
|||
|
|
В эти выходные (23-24 декабря):
|
|||
|
|
- 🔵 Шанс получить Rare увеличен вдвое!
|
|||
|
|
- 🎁 Каждый квиз на 80%+ даёт Rare карточку
|
|||
|
|
- 🎯 /daily даёт Rare вместо Common
|
|||
|
|
|
|||
|
|
Не упусти шанс! Время ограничено! ⏰
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Дальнейшее развитие (Phase 3)
|
|||
|
|
|
|||
|
|
**Обмен карточками между пользователями:**
|
|||
|
|
```
|
|||
|
|
/trade @username
|
|||
|
|
- Предложить обмен: 2 твои Rare на 1 его Rare
|
|||
|
|
- Система подтверждения обмена
|
|||
|
|
- История обменов
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Крафтинг (объединение карточек):**
|
|||
|
|
```
|
|||
|
|
/craft el perro
|
|||
|
|
- 3 Common → 1 Rare (того же слова)
|
|||
|
|
- 3 Rare → 1 Legendary
|
|||
|
|
- Требует "пыль" (dust) - получаешь за дубликаты
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Сезонные/тематические карточки:**
|
|||
|
|
```
|
|||
|
|
- Новогодние версии (декабрь)
|
|||
|
|
- Летние версии (июль-август)
|
|||
|
|
- Halloween версии (октябрь)
|
|||
|
|
- Ограниченное время получения
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Дополнительные фичи (Phase 2)
|
|||
|
|
|
|||
|
|
### 7. 📢 Telegram канал (опционально)
|
|||
|
|
|
|||
|
|
Если канал будет создан, можно автоматизировать:
|
|||
|
|
|
|||
|
|
**Карточка дня в канале:**
|
|||
|
|
|
|||
|
|
- Автопостинг красивого изображения с карточкой в канал каждый день в 9:00
|
|||
|
|
- Использовать `ImageGenerator` + `Telegram.sendPhoto`
|
|||
|
|
- Реализация: новый cron task `PostDailyCardTask`
|
|||
|
|
|
|||
|
|
**Еженедельные итоги:**
|
|||
|
|
|
|||
|
|
- Каждое воскресенье пост с статистикой:
|
|||
|
|
- Количество активных пользователей за неделю
|
|||
|
|
- Самые популярные карточки
|
|||
|
|
- Мотивационное сообщение
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Технические детали
|
|||
|
|
|
|||
|
|
### Структура проекта
|
|||
|
|
|
|||
|
|
**Изменения в боте:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
mnemo_cards_telegram_bot/
|
|||
|
|
├── lib/
|
|||
|
|
│ ├── backend_client.dart [ОБНОВИТЬ]
|
|||
|
|
│ │ + getUserStats()
|
|||
|
|
│ │ + generateQuiz()
|
|||
|
|
│ │ + submitQuiz()
|
|||
|
|
│ │ + setReminder()
|
|||
|
|
│ │ + getPendingReminders()
|
|||
|
|
│ │ + getPendingAchievements()
|
|||
|
|
│ │ + getCollection() [НОВЫЙ - для коллекционных карточек]
|
|||
|
|
│ │ + getCardVariants() [НОВЫЙ]
|
|||
|
|
│ │ + addToCollection() [НОВЫЙ]
|
|||
|
|
│ │ + getCollectionProgress() [НОВЫЙ]
|
|||
|
|
│ │ + getPendingCardRewards() [НОВЫЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── bot_config.dart [БЕЗ ИЗМЕНЕНИЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── image_generator.dart [ОБНОВИТЬ]
|
|||
|
|
│ │ + generateCardImage(card, rarity) [НОВЫЙ]
|
|||
|
|
│ │ + _getFrameForRarity() [НОВЫЙ]
|
|||
|
|
│ │ + _addGlowEffect() [НОВЫЙ]
|
|||
|
|
│ │ + _addSparklesEffect() [НОВЫЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── quiz_manager.dart [НОВЫЙ]
|
|||
|
|
│ │ - In-memory state для активных квизов
|
|||
|
|
│ │ - Логика генерации inline keyboards
|
|||
|
|
│ │
|
|||
|
|
│ └── models/ [НОВАЯ ПАПКА]
|
|||
|
|
│ ├── card_rarity.dart [НОВЫЙ]
|
|||
|
|
│ ├── card_variant.dart [НОВЫЙ]
|
|||
|
|
│ └── user_collection.dart [НОВЫЙ]
|
|||
|
|
│
|
|||
|
|
└── bin/
|
|||
|
|
└── main.dart [ОБНОВИТЬ]
|
|||
|
|
+ onCommand('daily')
|
|||
|
|
+ onCommand('stats')
|
|||
|
|
+ onCommand('reminders')
|
|||
|
|
+ onCommand('quiz')
|
|||
|
|
+ onCommand('collection') [НОВЫЙ]
|
|||
|
|
+ onCommand('showcase') [НОВЫЙ]
|
|||
|
|
+ onCommand('share_card') [НОВЫЙ]
|
|||
|
|
+ onCallbackQuery (для квизов, напоминаний, коллекции)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Изменения в бэкенде:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
mnemo_cards_backend/
|
|||
|
|
├── lib/
|
|||
|
|
│ ├── api/v2/
|
|||
|
|
│ │ └── telegram_bot_api_v2.dart [НОВЫЙ]
|
|||
|
|
│ │ + GET /telegram-bot/users/:id/stats
|
|||
|
|
│ │ + POST /telegram-bot/quiz/generate
|
|||
|
|
│ │ + POST /telegram-bot/quiz/submit
|
|||
|
|
│ │ + POST /telegram-bot/reminders/set
|
|||
|
|
│ │ + GET /telegram-bot/reminders/pending
|
|||
|
|
│ │ + GET /telegram-bot/achievements/pending
|
|||
|
|
│ │ + GET /telegram-bot/collection/:id [НОВЫЙ]
|
|||
|
|
│ │ + POST /telegram-bot/collection/add [НОВЫЙ]
|
|||
|
|
│ │ + GET /telegram-bot/card-variants/:cardId [НОВЫЙ]
|
|||
|
|
│ │ + GET /telegram-bot/collection/:id/progress [НОВЫЙ]
|
|||
|
|
│ │ + POST /telegram-bot/collection/award-rare [НОВЫЙ]
|
|||
|
|
│ │ + POST /telegram-bot/collection/award-legendary [НОВЫЙ]
|
|||
|
|
│ │ + GET /telegram-bot/rewards/pending [НОВЫЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── database/tables/
|
|||
|
|
│ │ ├── user_streaks.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── user_reminder_settings.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── user_achievements.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── quiz_results.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── card_variants.dart [НОВЫЙ - коллекционные карточки]
|
|||
|
|
│ │ ├── user_card_collection.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── user_collection_progress.dart [НОВЫЙ]
|
|||
|
|
│ │ └── pending_card_rewards.dart [НОВЫЙ]
|
|||
|
|
│ │
|
|||
|
|
│ ├── collection/ [НОВАЯ ПАПКА]
|
|||
|
|
│ │ ├── collection_manager.dart [НОВЫЙ]
|
|||
|
|
│ │ │ - Добавление карточек в коллекцию
|
|||
|
|
│ │ │ - Проверка unlock conditions
|
|||
|
|
│ │ │ - Награждение карточками
|
|||
|
|
│ │ │ - Обновление прогресса
|
|||
|
|
│ │ │
|
|||
|
|
│ │ ├── rarity_calculator.dart [НОВЫЙ]
|
|||
|
|
│ │ │ - Определение редкости для наград
|
|||
|
|
│ │ │ - Случайный выбор карточек
|
|||
|
|
│ │ │ - Весовые коэффициенты
|
|||
|
|
│ │ │
|
|||
|
|
│ │ └── variant_generator.dart [НОВЫЙ]
|
|||
|
|
│ │ - Генерация вариантов карточек при инициализации
|
|||
|
|
│ │ - Создание Common/Rare/Legendary версий
|
|||
|
|
│ │
|
|||
|
|
│ ├── cron/
|
|||
|
|
│ │ ├── send_reminders.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── check_achievements.dart [НОВЫЙ]
|
|||
|
|
│ │ ├── collection_rewards.dart [НОВЫЙ - награды за коллекцию]
|
|||
|
|
│ │ └── post_daily_card.dart [НОВЫЙ, опционально для канала]
|
|||
|
|
│ │
|
|||
|
|
│ └── statistics/
|
|||
|
|
│ └── streak_calculator.dart [НОВЫЙ]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Миграции базы данных
|
|||
|
|
|
|||
|
|
### Новые таблицы PostgreSQL
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- User streaks tracking
|
|||
|
|
CREATE TABLE user_streaks (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
user_id INTEGER REFERENCES users(id),
|
|||
|
|
telegram_user_id TEXT UNIQUE,
|
|||
|
|
current_streak INTEGER DEFAULT 0,
|
|||
|
|
longest_streak INTEGER DEFAULT 0,
|
|||
|
|
last_activity_date DATE,
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_user_streaks_telegram_user_id ON user_streaks(telegram_user_id);
|
|||
|
|
CREATE INDEX idx_user_streaks_last_activity ON user_streaks(last_activity_date);
|
|||
|
|
|
|||
|
|
-- Reminder settings
|
|||
|
|
CREATE TABLE user_reminder_settings (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT UNIQUE,
|
|||
|
|
reminder_time TIME NOT NULL,
|
|||
|
|
timezone TEXT DEFAULT 'UTC',
|
|||
|
|
enabled BOOLEAN DEFAULT true,
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_reminder_settings_time ON user_reminder_settings(reminder_time, enabled);
|
|||
|
|
|
|||
|
|
-- Achievements
|
|||
|
|
CREATE TABLE user_achievements (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT NOT NULL,
|
|||
|
|
achievement_type TEXT NOT NULL,
|
|||
|
|
achieved_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
notified BOOLEAN DEFAULT false
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_achievements_notified ON user_achievements(notified);
|
|||
|
|
CREATE INDEX idx_achievements_telegram_user ON user_achievements(telegram_user_id);
|
|||
|
|
|
|||
|
|
-- Quiz results
|
|||
|
|
CREATE TABLE quiz_results (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT NOT NULL,
|
|||
|
|
quiz_id TEXT NOT NULL,
|
|||
|
|
total_questions INTEGER NOT NULL,
|
|||
|
|
correct_answers INTEGER NOT NULL,
|
|||
|
|
completed_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_quiz_results_telegram_user ON quiz_results(telegram_user_id);
|
|||
|
|
|
|||
|
|
-- ==================================================
|
|||
|
|
-- COLLECTIBLE CARDS SYSTEM
|
|||
|
|
-- ==================================================
|
|||
|
|
|
|||
|
|
-- Card variants (all possible collectible versions)
|
|||
|
|
CREATE TABLE card_variants (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
card_id TEXT NOT NULL, -- ID основной карточки (game_card.id)
|
|||
|
|
rarity TEXT NOT NULL, -- 'common', 'rare', 'legendary'
|
|||
|
|
image_variant TEXT, -- Путь к альтернативному изображению
|
|||
|
|
-- NULL = использовать базовое изображение с рамкой
|
|||
|
|
frame_color TEXT NOT NULL, -- HEX цвет рамки (например: '#808080')
|
|||
|
|
frame_width INTEGER DEFAULT 40, -- Ширина рамки в пикселях
|
|||
|
|
has_glow BOOLEAN DEFAULT false, -- Эффект свечения вокруг рамки
|
|||
|
|
has_sparkles BOOLEAN DEFAULT false, -- Эффект блесток/искр
|
|||
|
|
unlock_condition TEXT, -- Текстовое описание условия получения
|
|||
|
|
description TEXT, -- Описание варианта карточки
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
|
|||
|
|
UNIQUE(card_id, rarity),
|
|||
|
|
CHECK (rarity IN ('common', 'rare', 'legendary'))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_variants_card_id ON card_variants(card_id);
|
|||
|
|
CREATE INDEX idx_variants_rarity ON card_variants(rarity);
|
|||
|
|
|
|||
|
|
-- User card collection (что собрал пользователь)
|
|||
|
|
CREATE TABLE user_card_collection (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT NOT NULL,
|
|||
|
|
card_variant_id INTEGER NOT NULL REFERENCES card_variants(id) ON DELETE CASCADE,
|
|||
|
|
obtained_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
obtained_via TEXT, -- 'daily', 'quiz_perfect', 'streak_7', 'streak_30', 'achievement', etc.
|
|||
|
|
|
|||
|
|
UNIQUE(telegram_user_id, card_variant_id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_collection_user ON user_card_collection(telegram_user_id);
|
|||
|
|
CREATE INDEX idx_collection_obtained ON user_card_collection(obtained_at);
|
|||
|
|
CREATE INDEX idx_collection_obtained_via ON user_card_collection(obtained_via);
|
|||
|
|
|
|||
|
|
-- User collection progress (кэш для быстрого доступа к статистике)
|
|||
|
|
CREATE TABLE user_collection_progress (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT UNIQUE NOT NULL,
|
|||
|
|
total_unique_words INTEGER DEFAULT 0, -- Количество уникальных слов в коллекции
|
|||
|
|
total_cards INTEGER DEFAULT 0, -- Общее количество карточек (с учетом редкостей)
|
|||
|
|
common_count INTEGER DEFAULT 0,
|
|||
|
|
rare_count INTEGER DEFAULT 0,
|
|||
|
|
legendary_count INTEGER DEFAULT 0,
|
|||
|
|
completion_percentage DECIMAL(5,2) DEFAULT 0.0,
|
|||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_collection_progress_user ON user_collection_progress(telegram_user_id);
|
|||
|
|
|
|||
|
|
-- Pending card rewards (очередь наград, которые нужно выдать)
|
|||
|
|
CREATE TABLE pending_card_rewards (
|
|||
|
|
id SERIAL PRIMARY KEY,
|
|||
|
|
telegram_user_id TEXT NOT NULL,
|
|||
|
|
reward_type TEXT NOT NULL, -- 'rare_random', 'legendary_random', 'rare_choice', 'legendary_choice'
|
|||
|
|
quantity INTEGER DEFAULT 1,
|
|||
|
|
reason TEXT, -- 'streak_7', 'streak_30', 'quiz_perfect', etc.
|
|||
|
|
card_choices TEXT[], -- Массив card_id для выбора (если reward_type содержит 'choice')
|
|||
|
|
claimed BOOLEAN DEFAULT false,
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|||
|
|
claimed_at TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX idx_pending_rewards_user ON pending_card_rewards(telegram_user_id, claimed);
|
|||
|
|
CREATE INDEX idx_pending_rewards_claimed ON pending_card_rewards(claimed);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Аналитика и метрики
|
|||
|
|
|
|||
|
|
После реализации фич, отслеживать:
|
|||
|
|
|
|||
|
|
**Вовлеченность:**
|
|||
|
|
|
|||
|
|
- DAU (Daily Active Users) через команды
|
|||
|
|
- Частота использования команд `/daily`, `/quiz`, `/stats`
|
|||
|
|
- Retention rate (возвращаются ли пользователи на следующий день)
|
|||
|
|
|
|||
|
|
**Эффективность:**
|
|||
|
|
|
|||
|
|
- Средняя длина стрика
|
|||
|
|
- Процент пользователей с настроенными напоминаниями
|
|||
|
|
- Процент прохождения квизов (completion rate)
|
|||
|
|
- Средний результат в квизах
|
|||
|
|
|
|||
|
|
**Endpoints для админа:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/v2/admin/telegram-bot/analytics
|
|||
|
|
- Количество активных пользователей бота
|
|||
|
|
- Популярные команды
|
|||
|
|
- Средняя длина стриков
|
|||
|
|
- Retention metrics
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Приоритеты реализации
|
|||
|
|
|
|||
|
|
### MVP (Iteration 1) - 2-3 недели
|
|||
|
|
|
|||
|
|
1. ✅ `/daily` - Карточка дня (базовая версия без коллекционирования)
|
|||
|
|
2. ✅ `/stats` - Базовая статистика
|
|||
|
|
3. ✅ Backend: Endpoints для карточек и статистики
|
|||
|
|
4. ✅ Backend: Таблица user_streaks
|
|||
|
|
|
|||
|
|
### Iteration 2 - 1-2 недели
|
|||
|
|
|
|||
|
|
5. ✅ `/quiz` - Мини-квизы
|
|||
|
|
6. ✅ Backend: Quiz generation и results tracking
|
|||
|
|
|
|||
|
|
### Iteration 3 (Коллекционные карточки) - 2-3 недели
|
|||
|
|
|
|||
|
|
7. ✅ Система коллекционных карточек:
|
|||
|
|
- Backend: таблицы card_variants, user_card_collection, user_collection_progress
|
|||
|
|
- Backend: endpoints для коллекции
|
|||
|
|
- Генерация вариантов карточек с разными рамками (Common/Rare/Legendary)
|
|||
|
|
|
|||
|
|
8. ✅ Команда `/collection` - просмотр коллекции
|
|||
|
|
|
|||
|
|
9. ✅ Команда `/showcase [слово]` - детали карточки
|
|||
|
|
|
|||
|
|
10. ✅ Обновление `/daily` - интеграция с коллекцией
|
|||
|
|
|
|||
|
|
11. ✅ Обновление `/stats` - статистика коллекции
|
|||
|
|
|
|||
|
|
12. ✅ Система награждения карточками за достижения
|
|||
|
|
|
|||
|
|
### Iteration 4 - 1-2 недели
|
|||
|
|
|
|||
|
|
13. ✅ `/reminders` - Напоминания
|
|||
|
|
14. ✅ Cron job для отправки напоминаний
|
|||
|
|
|
|||
|
|
### Iteration 5 - 1 неделя
|
|||
|
|
|
|||
|
|
15. ✅ Автоматические достижения
|
|||
|
|
16. ✅ Cron job для проверки achievements
|
|||
|
|
17. ✅ Интеграция достижений с наградами коллекционных карточек
|
|||
|
|
|
|||
|
|
### Iteration 6 (Социальные фичи) - 1-2 недели
|
|||
|
|
|
|||
|
|
18. ✅ `/share_card` - поделиться карточкой
|
|||
|
|
19. ✅ `/leaderboard collection` - топ коллекционеров (опционально)
|
|||
|
|
20. ✅ Тематические события для коллекции (опционально)
|
|||
|
|
|
|||
|
|
### Optional (если канал появится)
|
|||
|
|
|
|||
|
|
21. 📢 Автопостинг в канал
|
|||
|
|
22. 📊 Еженедельные итоги в канале
|
|||
|
|
23. 🌟 "Карточка недели" в канале
|
|||
|
|
24. 🎉 События "Редкие выходные"
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Риски и митигация
|
|||
|
|
|
|||
|
|
### Технические риски
|
|||
|
|
|
|||
|
|
**Проблема:** Telegram API rate limits
|
|||
|
|
|
|||
|
|
- **Митигация:** Использовать батчинг сообщений, queue для напоминаний
|
|||
|
|
|
|||
|
|
**Проблема:** Сложность state management для квизов
|
|||
|
|
|
|||
|
|
- **Митигация:** Простой in-memory Map, ограничить время жизни квиза (15 минут)
|
|||
|
|
|
|||
|
|
**Проблема:** Часовые пояса для напоминаний
|
|||
|
|
|
|||
|
|
- **Митигация:** Начать с UTC, потом добавить определение timezone через location
|
|||
|
|
|
|||
|
|
### Продуктовые риски
|
|||
|
|
|
|||
|
|
**Проблема:** Низкая активация (пользователи не используют команды)
|
|||
|
|
|
|||
|
|
- **Митигация:**
|
|||
|
|
- Добавить onboarding после авторизации
|
|||
|
|
- Welcome message с перечислением команд
|
|||
|
|
- Push notification после регистрации
|
|||
|
|
|
|||
|
|
**Проблема:** Спам напоминаниями
|
|||
|
|
|
|||
|
|
- **Митигация:**
|
|||
|
|
- Максимум 1 напоминание в день
|
|||
|
|
- Легкое отключение через `/reminders`
|
|||
|
|
- Автоматическое отключение после 7 дней неактивности
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Дизайн сообщений
|
|||
|
|
|
|||
|
|
### Единый стиль
|
|||
|
|
|
|||
|
|
Использовать эмодзи для визуальной привлекательности:
|
|||
|
|
|
|||
|
|
- 🎴 Карточки
|
|||
|
|
- 📊 Статистика
|
|||
|
|
- 🔥 Стрики
|
|||
|
|
- 🎯 Квизы
|
|||
|
|
- ⏰ Напоминания
|
|||
|
|
- 🎉 Достижения
|
|||
|
|
- ✅ Успех
|
|||
|
|
- ❌ Ошибка
|
|||
|
|
|
|||
|
|
**Тон общения:**
|
|||
|
|
|
|||
|
|
- Дружелюбный, мотивирующий
|
|||
|
|
- Короткие сообщения (2-4 строки)
|
|||
|
|
- Призывы к действию (CTA)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Пример onboarding flow
|
|||
|
|
|
|||
|
|
После первой авторизации через `/start` или `/login`:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Бот:
|
|||
|
|
Привет! 👋
|
|||
|
|
|
|||
|
|
Я бот Mnemo Cards. Помогу тебе учить языки каждый день!
|
|||
|
|
|
|||
|
|
Что я умею:
|
|||
|
|
🎴 /daily - карточка дня для изучения
|
|||
|
|
🎯 /quiz - быстрый квиз для проверки знаний
|
|||
|
|
📊 /stats - твоя статистика и стрики
|
|||
|
|
⏰ /reminders - настрой напоминания
|
|||
|
|
|
|||
|
|
Начнем с карточки дня? → /daily
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Тестирование
|
|||
|
|
|
|||
|
|
### Unit тесты
|
|||
|
|
|
|||
|
|
- `BackendClient.getUserStats()` → проверить парсинг response
|
|||
|
|
- `BackendClient.generateQuiz()` → проверить структуру квиза
|
|||
|
|
- `QuizManager` → state management для активных квизов
|
|||
|
|
- `StreakCalculator` → логика подсчета стриков
|
|||
|
|
|
|||
|
|
### Integration тесты
|
|||
|
|
|
|||
|
|
- End-to-end flow: `/daily` → получить карточку → отправить изображение
|
|||
|
|
- End-to-end flow: `/quiz` → ответить на вопросы → получить результат
|
|||
|
|
- Cron job: `SendRemindersTask` → отправка напоминаний в нужное время
|
|||
|
|
|
|||
|
|
### Manual QA
|
|||
|
|
|
|||
|
|
- Тестирование команд в реальном Telegram
|
|||
|
|
- Проверка inline keyboards
|
|||
|
|
- Проверка таймингов напоминаний
|
|||
|
|
- Проверка корректности стриков при активности в разные дни
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Документация
|
|||
|
|
|
|||
|
|
После реализации обновить:
|
|||
|
|
|
|||
|
|
- [`mnemo_cards_telegram_bot/README.md`](mnemo_cards_telegram_bot/README.md) - описание новых команд
|
|||
|
|
- [`mnemo_cards_backend/README.md`](mnemo_cards_backend/README.md) - новые API endpoints
|
|||
|
|
- Создать `mnemo_cards_telegram_bot/COMMANDS.md` - справочник команд для пользователей
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Заключение
|
|||
|
|
|
|||
|
|
Предложенные фичи создают **полноценную продуктовую экосистему** вокруг Telegram бота:
|
|||
|
|
|
|||
|
|
✅ **Привычка** - ежедневные напоминания и карточка дня
|
|||
|
|
|
|||
|
|
✅ **Мотивация** - стрики, достижения и коллекционирование
|
|||
|
|
|
|||
|
|
✅ **Практика** - квизы для закрепления знаний
|
|||
|
|
|
|||
|
|
✅ **Прогресс** - видимая статистика успехов и коллекции
|
|||
|
|
|
|||
|
|
✅ **Удобство** - обучение прямо в Telegram без открытия приложения
|
|||
|
|
|
|||
|
|
✅ **Геймификация** - система коллекционных карточек с редкостью создает долгосрочную цель
|
|||
|
|
|
|||
|
|
✅ **Социальный аспект** - возможность делиться своими достижениями и редкими карточками
|
|||
|
|
|
|||
|
|
### Почему коллекционные карточки важны для retention:
|
|||
|
|
|
|||
|
|
1. **Долгосрочная мотивация** - собрать все Legendary карточки может занять месяцы
|
|||
|
|
2. **Визуальная награда** - красивые изображения с разными рамками создают эмоциональную связь
|
|||
|
|
3. **Прогресс виден** - пользователь всегда видит, сколько ещё осталось собрать
|
|||
|
|
4. **FOMO эффект** - ограниченные/сезонные карточки заставляют возвращаться
|
|||
|
|
5. **Гордость достижениями** - возможность похвастаться редкими карточками
|
|||
|
|
|
|||
|
|
Это создает дополнительную ценность для пользователей и значительно повышает retention, что критично для MVP стадии проекта.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Roadmap Summary
|
|||
|
|
|
|||
|
|
**Минимальный MVP (4-5 недель):**
|
|||
|
|
- Базовые команды: /daily, /stats, /quiz
|
|||
|
|
- Система стриков
|
|||
|
|
- Простая коллекция (только Common карточки)
|
|||
|
|
|
|||
|
|
**Полноценный MVP (8-10 недель):**
|
|||
|
|
- Все базовые команды
|
|||
|
|
- Полная система коллекционных карточек (Common/Rare/Legendary)
|
|||
|
|
- Напоминания
|
|||
|
|
- Достижения с наградами в виде карточек
|
|||
|
|
- Команды /collection, /showcase
|
|||
|
|
|
|||
|
|
**Phase 2 (12+ недель):**
|
|||
|
|
- Социальные фичи (/share_card, /leaderboard)
|
|||
|
|
- Telegram канал с автопостингом
|
|||
|
|
- Тематические события
|
|||
|
|
- Обмен карточками (опционально)
|
|||
|
|
- Крафтинг (опционально)
|