101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""垃圾消息检测 handler"""
|
|
import asyncio
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
|
|
from telegram import Update
|
|
from telegram.ext import ContextTypes, MessageHandler, filters
|
|
|
|
import config
|
|
from db.models import Message, Ban, Action
|
|
from services.spam_detector import detect
|
|
|
|
logger = logging.getLogger("spam_guard")
|
|
|
|
# 简单的防刷屏缓存:user_id -> [(timestamp, msg_id)]
|
|
_flood_cache: dict[int, list] = {}
|
|
|
|
|
|
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
"""处理群消息:防刷屏 + 垃圾检测"""
|
|
msg = update.effective_message
|
|
if not msg or not msg.text:
|
|
return
|
|
|
|
user = update.effective_user
|
|
if not user or user.is_bot:
|
|
return
|
|
|
|
username = user.username or ""
|
|
first_name = user.first_name or ""
|
|
text = msg.text
|
|
|
|
# === 防刷屏 ===
|
|
now = datetime.now().timestamp()
|
|
if user.id not in _flood_cache:
|
|
_flood_cache[user.id] = []
|
|
_flood_cache[user.id].append((now, msg.message_id))
|
|
# 清理过期
|
|
_flood_cache[user.id] = [
|
|
(t, mid) for t, mid in _flood_cache[user.id]
|
|
if now - t < config.RATE_LIMIT_WINDOW
|
|
]
|
|
if len(_flood_cache[user.id]) > config.RATE_LIMIT_MESSAGES:
|
|
try:
|
|
await msg.delete()
|
|
except Exception:
|
|
pass
|
|
logger.warning(f"⚡ 防刷屏: @{username} ({user.id}) {len(_flood_cache[user.id])}条/{config.RATE_LIMIT_WINDOW}s")
|
|
return
|
|
|
|
# 记录消息
|
|
await Message.create(
|
|
msg_id=msg.message_id,
|
|
chat_id=update.effective_chat.id,
|
|
user_id=user.id,
|
|
username=username,
|
|
first_name=first_name,
|
|
text=text[:500],
|
|
)
|
|
|
|
# === 垃圾检测 ===
|
|
is_spam, reason = await detect(text, username, first_name)
|
|
|
|
if is_spam:
|
|
logger.warning(f"🚨 垃圾消息 [{username}]: {reason}")
|
|
|
|
# 删消息
|
|
try:
|
|
await msg.delete()
|
|
await Action.create(action="DELETE", user_id=user.id, username=username, details={"reason": reason, "text": text[:120]})
|
|
except Exception as e:
|
|
logger.error(f"❌ 删除消息失败: {e}")
|
|
|
|
# 封禁
|
|
try:
|
|
unban_at = datetime.now() + timedelta(minutes=config.BAN_DURATION_MINUTES)
|
|
until_date = int(unban_at.timestamp())
|
|
await update.effective_chat.ban_member(user_id=user.id, until_date=until_date)
|
|
|
|
ban_record = {
|
|
"user_id": user.id,
|
|
"username": username,
|
|
"first_name": first_name,
|
|
"reason": reason,
|
|
"method": "auto",
|
|
"duration_minutes": config.BAN_DURATION_MINUTES,
|
|
"unban_at": unban_at,
|
|
}
|
|
await Ban.create(**ban_record)
|
|
await Action.create(action="BAN", user_id=user.id, username=username, details={"reason": reason})
|
|
logger.info(f"🔨 已封禁 [{username}] {config.BAN_DURATION_MINUTES} 分钟")
|
|
except Exception as e:
|
|
logger.error(f"❌ 封禁失败: {e}")
|
|
|
|
# 标记为垃圾
|
|
await Message.filter(msg_id=msg.message_id).update(is_spam=True, spam_reason=reason)
|
|
|
|
|
|
def register_spam(app):
|
|
app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.GROUPS & ~filters.COMMAND, handle_message))
|