502 lines
26 KiB
Markdown
502 lines
26 KiB
Markdown
# PrimoExampleBot
|
||
Конечно. Давай разберём весь твой класс `BotDatabase` и методы, и покажу **как использовать каждый метод на практике** с аннотациями и примерами. Я постараюсь дать максимально ясное объяснение, чтобы можно было сразу применять в aiogram-боте.
|
||
|
||
---
|
||
|
||
## 1️⃣ Инициализация базы данных
|
||
|
||
```python
|
||
db: BotDatabase = BotDatabase() # создаём экземпляр класса
|
||
await db.setup() # создаёт все таблицы в SQLite (users, user_messages, roles)
|
||
```
|
||
|
||
* `init_db()` автоматически создаёт таблицы, если их нет.
|
||
* Вызывается один раз при старте бота.
|
||
|
||
---
|
||
|
||
## 2️⃣ Работа с пользователями
|
||
|
||
### Добавление пользователя вручную
|
||
|
||
```python
|
||
await db.add_user(
|
||
user_id=12345,
|
||
username="alex",
|
||
full_name="Alex Test",
|
||
is_admin=True
|
||
)
|
||
```
|
||
|
||
* `user_id` — Telegram ID пользователя.
|
||
* `username` — никнейм Telegram.
|
||
* `full_name` — полное имя.
|
||
* `is_admin` — если True, пользователь получает статус `ADMIN`, иначе `ACTIVE`.
|
||
|
||
---
|
||
|
||
### Бан и разбан пользователя
|
||
|
||
```python
|
||
await db.ban_user(12345) # забанить
|
||
await db.unban_user(12345) # разбанить
|
||
```
|
||
|
||
* Статусы хранятся в Enum `UserStatus` (`ACTIVE`, `ADMIN`, `BANNED`).
|
||
* Можно применять фильтры при выборке пользователей, чтобы исключать забаненных.
|
||
|
||
---
|
||
|
||
### Получение списка ID пользователей
|
||
|
||
```python
|
||
ids: List[int] = await db.get_user_ids(
|
||
only_active=True, # исключаем забаненных
|
||
include_admins=False, # исключаем админов
|
||
order_asc=True # сортировка по возрастанию
|
||
)
|
||
```
|
||
|
||
* Возвращает чистый `List[int]` Telegram ID, чтобы можно было делать рассылку сообщений.
|
||
* Можно исключать забаненных и/или админов.
|
||
|
||
---
|
||
|
||
## 3️⃣ Работа с сообщениями
|
||
|
||
### Добавление сообщения пользователя
|
||
|
||
```python
|
||
await db.add_message(user_id=12345, message_text="Привет!")
|
||
```
|
||
|
||
* Если пользователя нет в базе — создаётся новая запись.
|
||
* Сообщение автоматически связывается с пользователем через `UserMessage`.
|
||
|
||
---
|
||
|
||
### Статистика сообщений
|
||
|
||
```python
|
||
day, week, month, total = await db.get_message_stats(user_id=12345)
|
||
print(f"За день: {day}, за неделю: {week}, за месяц: {month}, всего: {total}")
|
||
```
|
||
|
||
* Возвращает кортеж `(день, неделя, месяц, всё время)`.
|
||
* Неделя считается **с понедельника по текущий день**, месяц — последние 30 дней.
|
||
|
||
---
|
||
|
||
## 4️⃣ Работа с ролями
|
||
|
||
### Инициализация ролей
|
||
|
||
```python
|
||
await db.init_roles(["Альбедо", "Чжун Ли", "Кэйа"])
|
||
```
|
||
|
||
* Создаёт роли, если их нет.
|
||
* Используется один раз при настройке базы или при добавлении новых персонажей.
|
||
|
||
---
|
||
|
||
### Назначение роли пользователю
|
||
|
||
```python
|
||
success: bool = await db.assign_role("Альбедо", user_id=12345)
|
||
if success:
|
||
print("Роль назначена!")
|
||
else:
|
||
print("Роль уже занята")
|
||
```
|
||
|
||
* Возвращает `True`, если роль успешно назначена.
|
||
* Если роль уже занята — возвращает `False`.
|
||
|
||
---
|
||
|
||
### Освобождение роли
|
||
|
||
```python
|
||
await db.release_role("Альбедо")
|
||
```
|
||
|
||
* Делает `occupied_by = None`.
|
||
* Позволяет передать роль другому пользователю.
|
||
|
||
---
|
||
|
||
### Получение статуса всех ролей
|
||
|
||
```python
|
||
roles: List[Tuple[str, Optional[int]]] = await db.get_role_status()
|
||
for role_name, user_id in roles:
|
||
print(f"{role_name} занята пользователем {user_id}")
|
||
```
|
||
|
||
* Возвращает список кортежей `(имя роли, user_id или None)`.
|
||
* Полезно для отображения в админ-панели бота.
|
||
|
||
---
|
||
|
||
### Получение ролей конкретного пользователя
|
||
|
||
```python
|
||
roles_for_user: List[str] = await db.get_roles_by_user(user_id=12345)
|
||
print("Пользователь занимает роли:", roles_for_user)
|
||
```
|
||
|
||
* Возвращает все имена ролей, которые занимает конкретный пользователь.
|
||
|
||
---
|
||
|
||
## 5️⃣ Примеры типичного использования в aiogram
|
||
|
||
```python
|
||
from aiogram import Bot, Dispatcher, types
|
||
|
||
@dp.message_handler(commands=["start"])
|
||
async def cmd_start(message: types.Message):
|
||
# автоматически добавляем пользователя в базу
|
||
await db.add_user(
|
||
user_id=message.from_user.id,
|
||
username=message.from_user.username,
|
||
full_name=message.from_user.full_name
|
||
)
|
||
await message.answer("Привет! Ты добавлен в базу.")
|
||
|
||
@dp.message_handler(commands=["stats"])
|
||
async def cmd_stats(message: types.Message):
|
||
day, week, month, total = await db.get_message_stats(message.from_user.id)
|
||
await message.answer(
|
||
f"Сообщения:\nЗа день: {day}\nЗа неделю: {week}\nЗа месяц: {month}\nВсего: {total}"
|
||
)
|
||
|
||
@dp.message_handler()
|
||
async def all_messages(message: types.Message):
|
||
# сохраняем любое сообщение пользователя
|
||
await db.add_message(message.from_user.id, message.text)
|
||
```
|
||
|
||
* В этом примере база автоматически создаёт пользователя при первом сообщении.
|
||
* Любые сообщения сохраняются для статистики.
|
||
* Можно легко подключить роли и рассылку по ID пользователей.
|
||
|
||
---
|
||
# Сохранение сообщений с ролями
|
||
|
||
```python
|
||
await db.save_role_message(
|
||
game_type="genshin",
|
||
channel_id=CHANNEL_ID,
|
||
message_id=MESSAGE_ID,
|
||
message_text="Текст сообщения с персонажами Genshin..."
|
||
)
|
||
```
|
||
|
||
```python
|
||
# Для Honkai: Star Rail
|
||
await db.save_role_message(
|
||
game_type="hsr",
|
||
channel_id=CHANNEL_ID,
|
||
message_id=MESSAGE_ID,
|
||
message_text="Текст сообщения с персонажами HSR..."
|
||
)
|
||
```
|
||
|
||
# 🎯 4. Назначение и освобождение ролей
|
||
```python
|
||
|
||
# Назначение роли (автоматически определит игру по региону)
|
||
success = await db.assign_role("Альбедо", user_id, bot) # Genshin
|
||
success = await db.assign_role("Вельт", user_id, bot) # HSR
|
||
|
||
# Освобождение роли
|
||
success = await db.release_role("Альбедо", bot) # Genshin
|
||
success = await db.release_role("Вельт", bot) # HSR
|
||
```
|
||
|
||
# 📊 5. Получение статистики по регионам
|
||
```python
|
||
# Для Genshin Impact
|
||
mondstadt_roles = await db.get_roles_by_region(RoleRegion.MONDSTADT)
|
||
liyue_roles = await db.get_roles_by_region(RoleRegion.LIYUE)
|
||
|
||
# Для Honkai: Star Rail
|
||
star_express_roles = await db.get_roles_by_region(RoleRegion.HSR_STAR)
|
||
lofu_roles = await db.get_roles_by_region(RoleRegion.HSR_LOFU)
|
||
```
|
||
|
||
# 🔍 6. Поиск ролей
|
||
```python
|
||
# Найти роль по имени
|
||
role = await db.get_role_by_name("Альбедо")
|
||
if role:
|
||
print(f"Персонаж: {role.name}, Регион: {role.region}, Занята: {role.occupied_by is not None}")
|
||
|
||
# Получить все свободные роли в регионе
|
||
free_roles = await db.get_available_roles(RoleRegion.MONDSTADT)
|
||
```
|
||
|
||
# 📝 7. Обновление сообщений вручную
|
||
```python
|
||
# Обновить сообщение с персонажами Genshin
|
||
await db.update_role_message("genshin", bot)
|
||
|
||
# Обновить сообщение с персонажами HSR
|
||
await db.update_role_message("hsr", bot)
|
||
```
|
||
|
||
# 🎮 8. Как система определяет игру для роли
|
||
|
||
Система автоматически определяет к какой игре принадлежит роль на основе региона:
|
||
|
||
Все регионы Genshin Impact (от MONDSTADT до OTHER) → игра "genshin"
|
||
|
||
Все регионы HSR (начинающиеся с HSR_) → игра "hsr"
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
---
|
||
|
||
|
||
Отлично! Ваша база данных очень мощная и имеет множество функций. Давайте разберем все возможности по категориям:
|
||
|
||
## 📊 **1. Статистика сообщений пользователя**
|
||
|
||
### Получение статистики за разные периоды:
|
||
```python
|
||
# Получить статистику сообщений пользователя
|
||
day, week, month, total = await db.get_message_stats(user_id)
|
||
|
||
print(f"За день: {day} сообщений")
|
||
print(f"За неделю: {week} сообщений")
|
||
print(f"За месяц: {month} сообщений")
|
||
print(f"Всего: {total} сообщений")
|
||
```
|
||
|
||
### Пример использования в хендлере:
|
||
```python
|
||
@dp.message_handler(commands=["stats"])
|
||
async def stats_handler(message: Message):
|
||
user_id = message.from_user.id
|
||
day, week, month, total = await db.get_message_stats(user_id)
|
||
|
||
text = (
|
||
f"📊 Ваша статистика сообщений:\n"
|
||
f"• За день: {day}\n"
|
||
f"• За неделю: {week}\n"
|
||
f"• За месяц: {month}\n"
|
||
f"• Всего: {total}"
|
||
)
|
||
await message.answer(text)
|
||
```
|
||
|
||
## 👥 **2. Управление пользователями**
|
||
|
||
### Добавление/получение пользователей:
|
||
```python
|
||
# Добавить пользователя
|
||
await db.add_user(
|
||
user_id=123456789,
|
||
username="username",
|
||
full_name="Имя Фамилия",
|
||
is_admin=False # или True для админа
|
||
)
|
||
|
||
# Получить пользователя
|
||
user = await db.get_user(123456789)
|
||
if user:
|
||
print(f"Имя: {user.full_name}, Статус: {user.status}")
|
||
|
||
# Получить всех пользователей
|
||
users = await db.get_all_users(include_banned=False)
|
||
for user in users:
|
||
print(f"{user.id}: {user.username}")
|
||
|
||
# Получить ID всех пользователей (для рассылки)
|
||
user_ids = await db.get_user_ids(only_active=True, include_admins=True)
|
||
```
|
||
|
||
### Бан/разбан:
|
||
```python
|
||
# Забанить пользователя
|
||
await db.ban_user(user_id)
|
||
|
||
# Разбанить пользователя
|
||
await db.unban_user(user_id)
|
||
|
||
# Сделать админом
|
||
await db.set_admin(user_id, make_admin=True)
|
||
|
||
# Убрать админку
|
||
await db.set_admin(user_id, make_admin=False)
|
||
```
|
||
|
||
## 💬 **3. Работа с сообщениями**
|
||
|
||
### Сохранение сообщений:
|
||
```python
|
||
# Вручную добавить сообщение
|
||
await db.add_message(
|
||
user_id=123456789,
|
||
message_text="Текст сообщения",
|
||
created_at=datetime.now(timezone.utc) # опционально
|
||
)
|
||
|
||
# Автоматически из aiogram сообщения
|
||
await db.add_message_from_message(message)
|
||
```
|
||
|
||
## 🎭 **4. Система ролей (персонажей)**
|
||
|
||
### Инициализация ролей:
|
||
```python
|
||
# Инициализировать стандартные роли
|
||
await db.init_default_roles()
|
||
|
||
# Или создать свои роли
|
||
roles = [
|
||
("Альбедо", RoleRegion.MONDSTADT),
|
||
("Нахида", RoleRegion.SUMERU),
|
||
("Кафка", RoleRegion.HSR_STAR)
|
||
]
|
||
await db.init_roles(roles)
|
||
```
|
||
|
||
### Работа с ролями:
|
||
```python
|
||
# Назначить роль пользователю
|
||
success = await db.assign_role("Альбедо", user_id, bot)
|
||
if success:
|
||
print("Роль назначена!")
|
||
|
||
# Освободить роль
|
||
success = await db.release_role("Альбедо", bot)
|
||
|
||
# Получить все роли пользователя
|
||
user_roles = await db.get_roles_by_user(user_id)
|
||
print(f"Ваши роли: {', '.join(user_roles)}")
|
||
|
||
# Посмотреть статус всех ролей
|
||
all_roles = await db.get_role_status()
|
||
for role_name, occupied_by in all_roles:
|
||
status = "✅ занята" if occupied_by else "❌ свободна"
|
||
print(f"{role_name}: {status}")
|
||
|
||
# Найти свободные роли в регионе
|
||
free_roles = await db.get_available_roles(RoleRegion.MONDSTADT)
|
||
for role in free_roles:
|
||
print(role.name)
|
||
|
||
# Получить информацию о конкретной роли
|
||
role = await db.get_role_by_name("Альбедо")
|
||
if role:
|
||
print(f"Регион: {role.region}, Занята: {role.occupied_by is not None}")
|
||
```
|
||
|
||
### Статистика по регионам:
|
||
```python
|
||
# Получить статистику по всем регионам
|
||
stats = await db.get_region_stats()
|
||
for region, data in stats.items():
|
||
print(f"{region}: {data['free']} свободно, {data['occupied']} занято")
|
||
```
|
||
|
||
## 📝 **5. Управление сообщениями с ролями в каналах**
|
||
|
||
```python
|
||
# Сохранить информацию о сообщении с ролями
|
||
await db.save_role_message(
|
||
game_type="genshin", # или "hsr"
|
||
channel_id=-100123456789,
|
||
message_id=123,
|
||
message_text="Список персонажей Genshin Impact"
|
||
)
|
||
|
||
# Обновить сообщение с ролями (после назначения/освобождения)
|
||
await db.update_role_message("genshin", bot)
|
||
```
|
||
|
||
## 🛠 **6. Сервисные функции**
|
||
|
||
```python
|
||
# Инициализация базы данных
|
||
await db.setup()
|
||
|
||
# Проверка соединения
|
||
if await db.check_connection():
|
||
print("База данных подключена")
|
||
|
||
# Корректное закрытие соединений
|
||
await db.dispose()
|
||
```
|
||
|
||
## 🎯 **Практические примеры использования:**
|
||
|
||
### 1. **Команда для просмотра статистики**
|
||
```python
|
||
@dp.message_handler(commands=["my_stats"])
|
||
async def my_stats_handler(message: Message):
|
||
user_id = message.from_user.id
|
||
day, week, month, total = await db.get_message_stats(user_id)
|
||
|
||
text = (
|
||
"📊 Ваша активность в чатах:\n"
|
||
f"• Сегодня: {day} сообщений\n"
|
||
f"• За неделю: {week} сообщений\n"
|
||
f"• За месяц: {month} сообщений\n"
|
||
f"• Всего: {total} сообщений"
|
||
)
|
||
await message.answer(text)
|
||
```
|
||
|
||
### 2. **Команда для просмотра ролей**
|
||
```python
|
||
@dp.message_handler(commands=["my_roles"])
|
||
async def my_roles_handler(message: Message):
|
||
user_id = message.from_user.id
|
||
roles = await db.get_roles_by_user(user_id)
|
||
|
||
if roles:
|
||
text = "🎭 Ваши роли:\n• " + "\n• ".join(roles)
|
||
else:
|
||
text = "У вас пока нет ролей. Используйте /roles чтобы посмотреть доступные."
|
||
|
||
await message.answer(text)
|
||
```
|
||
|
||
### 3. **Команда для админов - статистика чата**
|
||
```python
|
||
@dp.message_handler(commands=["chat_stats"], is_admin=True)
|
||
async def chat_stats_handler(message: Message):
|
||
users = await db.get_all_users(include_banned=False)
|
||
|
||
total_messages = 0
|
||
user_stats = []
|
||
|
||
for user in users:
|
||
day, week, month, total = await db.get_message_stats(user.id)
|
||
total_messages += total
|
||
user_stats.append((user, total))
|
||
|
||
# Сортировка по активности
|
||
user_stats.sort(key=lambda x: x[1], reverse=True)
|
||
|
||
text = "📈 Статистика чата:\n"
|
||
text += f"• Всего пользователей: {len(users)}\n"
|
||
text += f"• Всего сообщений: {total_messages}\n\n"
|
||
text += "🏆 Топ-5 активных пользователей:\n"
|
||
|
||
for i, (user, count) in enumerate(user_stats[:5], 1):
|
||
username = user.username or user.full_name or f"ID {user.id}"
|
||
text += f"{i}. {username}: {count} сообщений\n"
|
||
|
||
await message.answer(text)
|
||
```
|
||
|
||
Ваша база данных отлично спроектирована и покрывает все основные потребности Telegram-бота! 🚀 |