Files
tg-spam-guard/db/models.py
T
2026-07-07 16:53:39 +08:00

218 lines
8.3 KiB
Python

"""数据库模型定义"""
from tortoise import fields
from tortoise.models import Model
class TimestampMixin:
"""通用时间戳字段。所有业务表统一包含 created_at / updated_at。"""
created_at = fields.DatetimeField(auto_now_add=True, description="创建时间")
updated_at = fields.DatetimeField(auto_now=True, description="更新时间")
class Message(TimestampMixin, Model):
"""消息记录"""
id = fields.BigIntField(pk=True, generated=True)
msg_id = fields.BigIntField(null=True)
chat_id = fields.BigIntField()
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
first_name = fields.CharField(max_length=255, null=True)
text = fields.TextField(null=True)
is_spam = fields.BooleanField(default=False)
spam_reason = fields.CharField(max_length=255, null=True)
manually_deleted = fields.BooleanField(default=False)
deleted_at = fields.DatetimeField(null=True)
delete_reason = fields.CharField(max_length=255, null=True)
class Meta:
table = "messages"
indexes = (("chat_id", "msg_id"), ("user_id", "created_at"), ("is_spam", "created_at"))
class Ban(TimestampMixin, Model):
"""封禁记录"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
first_name = fields.CharField(max_length=255, null=True)
reason = fields.CharField(max_length=500, null=True)
method = fields.CharField(max_length=50, default="auto") # auto/command/verify_fail
duration_minutes = fields.IntField(default=2)
auto_unban = fields.BooleanField(default=True)
unban_at = fields.DatetimeField(null=True)
class Meta:
table = "bans"
indexes = (("user_id", "created_at"), ("auto_unban", "unban_at"))
class Action(TimestampMixin, Model):
"""操作日志(入群/退群/删除/解封/警告等)"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
action = fields.CharField(max_length=50)
user_id = fields.BigIntField(null=True)
username = fields.CharField(max_length=255, null=True)
operator_id = fields.BigIntField(null=True)
details = fields.JSONField(null=True)
class Meta:
table = "actions"
indexes = (("action", "created_at"), ("user_id", "created_at"), ("operator_id", "created_at"))
class VerifyQueue(TimestampMixin, Model):
"""入群验证队列"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
question = fields.CharField(max_length=500, null=True)
answer = fields.CharField(max_length=500, null=True)
attempts = fields.IntField(default=0)
status = fields.CharField(max_length=20, default="pending")
verified_at = fields.DatetimeField(null=True)
class Meta:
table = "verify_queue"
indexes = (("user_id", "status"), ("status", "created_at"))
class UserBinding(TimestampMixin, Model):
"""TG ↔ Harvest 账号绑定"""
id = fields.BigIntField(pk=True, generated=True)
tg_user_id = fields.BigIntField(unique=True)
tg_username = fields.CharField(max_length=255, null=True)
harvest_uid = fields.CharField(max_length=255, null=True)
harvest_token = fields.CharField(max_length=500, null=True)
auth_email = fields.CharField(max_length=255, null=True)
time_expire = fields.CharField(max_length=100, null=True)
# 兼容旧代码/展示语义:绑定时间;created_at 仍作为统一审计字段
bound_at = fields.DatetimeField(auto_now_add=True)
class Meta:
table = "user_bindings"
indexes = (("tg_user_id",), ("harvest_uid",))
class Warning(TimestampMixin, Model):
"""警告记录"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
reason = fields.CharField(max_length=500, null=True)
operator_id = fields.BigIntField(null=True)
active = fields.BooleanField(default=True)
class Meta:
table = "warnings"
indexes = (("user_id", "active"), ("operator_id", "created_at"))
class GameScore(TimestampMixin, Model):
"""游戏积分"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
game_type = fields.CharField(max_length=50)
score = fields.IntField(default=0)
wins = fields.IntField(default=0)
total = fields.IntField(default=0)
streak = fields.IntField(default=0)
last_played = fields.DatetimeField(null=True)
class Meta:
table = "game_scores"
unique_together = (("chat_id", "user_id", "game_type"),)
indexes = (("chat_id", "game_type", "score"), ("chat_id", "user_id", "game_type"))
class GroupConfig(TimestampMixin, Model):
"""群组配置"""
key = fields.CharField(max_length=100, pk=True)
value = fields.JSONField(null=True)
class Meta:
table = "group_config"
class ShopItem(TimestampMixin, Model):
"""群组积分商城商品"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
item_key = fields.CharField(max_length=50)
name = fields.CharField(max_length=255)
cost = fields.IntField(default=0)
stock = fields.IntField(default=-1) # -1 表示不限库存
desc = fields.CharField(max_length=500, null=True)
enabled = fields.BooleanField(default=True)
class Meta:
table = "shop_items"
unique_together = (("chat_id", "item_key"),)
indexes = (("chat_id", "enabled"),)
class ShopTransaction(TimestampMixin, Model):
"""积分商城交易记录"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField(default=0)
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
item_key = fields.CharField(max_length=50)
item_name = fields.CharField(max_length=255)
cost = fields.IntField(default=0)
status = fields.CharField(max_length=30, default="success")
note = fields.CharField(max_length=500, null=True)
class Meta:
table = "shop_transactions"
indexes = (("chat_id", "created_at"), ("chat_id", "user_id"), ("item_key", "created_at"),)
class LotteryEvent(TimestampMixin, Model):
"""群内抽奖活动"""
id = fields.BigIntField(pk=True, generated=True)
chat_id = fields.BigIntField()
message_id = fields.BigIntField(null=True)
creator_id = fields.BigIntField()
creator_name = fields.CharField(max_length=255, null=True)
title = fields.CharField(max_length=255, null=True)
description = fields.TextField(null=True)
draw_type = fields.CharField(max_length=50, default="people") # instant/people/time/manual
draw_at = fields.DatetimeField(null=True)
max_participants = fields.IntField(default=0)
prize = fields.CharField(max_length=500)
prize_count = fields.IntField(default=1)
remaining_count = fields.IntField(default=1)
condition_type = fields.CharField(max_length=50, default="all") # all/checkin/min_points
condition_value = fields.IntField(default=0)
end_type = fields.CharField(max_length=50, default="people") # people/minutes/manual
end_value = fields.IntField(default=0)
status = fields.CharField(max_length=20, default="active")
winner_id = fields.BigIntField(null=True)
winner_name = fields.CharField(max_length=255, null=True)
ended_at = fields.DatetimeField(null=True)
class Meta:
table = "lottery_events"
indexes = (("chat_id", "status"), ("creator_id", "created_at"))
class LotteryParticipant(TimestampMixin, Model):
"""抽奖参与者"""
id = fields.BigIntField(pk=True, generated=True)
event_id = fields.BigIntField()
user_id = fields.BigIntField()
username = fields.CharField(max_length=255, null=True)
first_name = fields.CharField(max_length=255, null=True)
is_winner = fields.BooleanField(default=False)
class Meta:
table = "lottery_participants"
indexes = (("event_id", "user_id"),)
unique_together = (("event_id", "user_id"),)