164 lines
5.4 KiB
Python
164 lines
5.4 KiB
Python
# bot/models.py
|
||
from django.db import models
|
||
from django.utils import timezone
|
||
|
||
|
||
class TelegramBot(models.Model):
|
||
"""
|
||
Карточка бота. Можно держать несколько активных одновременно.
|
||
"""
|
||
name = models.CharField(
|
||
max_length=100,
|
||
help_text="Произвольное имя бота в проекте",
|
||
)
|
||
username = models.CharField(
|
||
max_length=100,
|
||
blank=True,
|
||
help_text="@username в Telegram",
|
||
)
|
||
token = models.CharField(
|
||
max_length=200,
|
||
help_text="Токен от @BotFather",
|
||
)
|
||
is_active = models.BooleanField(
|
||
default=True,
|
||
help_text="Если включен — может быть запущен runbots",
|
||
)
|
||
|
||
class Meta:
|
||
verbose_name = "Telegram бот"
|
||
verbose_name_plural = "Telegram боты"
|
||
ordering = ("-is_active", "name")
|
||
|
||
def __str__(self) -> str:
|
||
return f"{self.name} ({self.username or 'no-username'})"
|
||
|
||
|
||
class BotConfig(models.Model):
|
||
"""
|
||
Конфигурация для конкретного бота (парсинг, allowed_updates, админы, вебхук).
|
||
Делается OneToOne, но без опоры на обратный атрибут .config у TelegramBot.
|
||
"""
|
||
bot = models.OneToOneField(
|
||
TelegramBot,
|
||
on_delete=models.CASCADE,
|
||
help_text="Какому боту принадлежит конфигурация",
|
||
)
|
||
parse_mode = models.CharField(
|
||
max_length=20,
|
||
default="HTML",
|
||
choices=[("HTML", "HTML"), ("MarkdownV2", "MarkdownV2"), ("None", "None")],
|
||
help_text="Режим парсинга сообщений (или None для обычного текста)",
|
||
)
|
||
allowed_updates = models.JSONField(
|
||
default=list,
|
||
blank=True,
|
||
help_text="Список типов апдейтов, например ['message','my_chat_member','chat_member']",
|
||
)
|
||
admin_user_ids = models.JSONField(
|
||
default=list,
|
||
blank=True,
|
||
help_text="Список Telegram user_id, имеющих права администратора",
|
||
)
|
||
webhook_url = models.URLField(
|
||
blank=True,
|
||
default="",
|
||
help_text="URL вебхука (если используете вебхуки)",
|
||
)
|
||
use_webhook = models.BooleanField(
|
||
default=False,
|
||
help_text="Включить режим webhook вместо polling",
|
||
)
|
||
|
||
class Meta:
|
||
verbose_name = "Конфигурация бота"
|
||
verbose_name_plural = "Конфигурации ботов"
|
||
|
||
def __str__(self) -> str:
|
||
return f"Config for {self.bot}"
|
||
|
||
|
||
class TelegramChat(models.Model):
|
||
"""
|
||
Чаты храним ПО БОТУ. Один и тот же chat_id TG может встречаться у разных ботов,
|
||
поэтому уникальность задаётся на (bot, chat_id).
|
||
|
||
Важно: не используем поле с именем 'id' под chat_id Telegram, чтобы не ломать Django.
|
||
Стандартный PK (BigAutoField) оставляем как есть.
|
||
"""
|
||
CHAT_TYPES = [
|
||
("private", "private"),
|
||
("group", "group"),
|
||
("supergroup", "supergroup"),
|
||
("channel", "channel"),
|
||
]
|
||
|
||
bot = models.ForeignKey(
|
||
TelegramBot,
|
||
on_delete=models.CASCADE,
|
||
related_name="chats",
|
||
help_text="К какому боту относится этот чат",
|
||
)
|
||
chat_id = models.BigIntegerField(
|
||
help_text="Идентификатор чата в Telegram (может быть до ~52 бит)",
|
||
)
|
||
type = models.CharField(
|
||
max_length=20,
|
||
choices=CHAT_TYPES,
|
||
help_text="Тип чата",
|
||
)
|
||
title = models.CharField(
|
||
max_length=255,
|
||
blank=True,
|
||
default="",
|
||
help_text="Название (для групп/каналов)",
|
||
)
|
||
username = models.CharField(
|
||
max_length=255,
|
||
blank=True,
|
||
default="",
|
||
help_text="Username чата/канала (если есть)",
|
||
)
|
||
|
||
# Статус участия бота
|
||
is_member = models.BooleanField(
|
||
default=True,
|
||
help_text="Состоит ли бот в чате сейчас",
|
||
)
|
||
joined_at = models.DateTimeField(
|
||
default=timezone.now,
|
||
help_text="Когда бот был добавлен",
|
||
)
|
||
left_at = models.DateTimeField(
|
||
null=True,
|
||
blank=True,
|
||
help_text="Когда бот покинул чат",
|
||
)
|
||
|
||
# Метаданные активности
|
||
last_message_at = models.DateTimeField(
|
||
null=True,
|
||
blank=True,
|
||
help_text="Когда последний раз видели сообщение в этом чате",
|
||
)
|
||
|
||
class Meta:
|
||
verbose_name = "Telegram чат"
|
||
verbose_name_plural = "Telegram чаты"
|
||
constraints = [
|
||
models.UniqueConstraint(
|
||
fields=["bot", "chat_id"],
|
||
name="uniq_bot_chat",
|
||
),
|
||
]
|
||
indexes = [
|
||
models.Index(fields=["bot", "chat_id"]),
|
||
models.Index(fields=["bot", "type"]),
|
||
models.Index(fields=["bot", "is_member"]),
|
||
]
|
||
ordering = ("-is_member", "-joined_at")
|
||
|
||
def __str__(self) -> str:
|
||
base = self.title or self.username or str(self.chat_id)
|
||
return f"{base} [{self.type}]"
|