Files
tg-spam-guard/handlers/fun.py
T
2026-07-07 10:32:57 +08:00

251 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""气氛组 + 小游戏 handler"""
import asyncio
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ApplicationHandlerStop, CallbackQueryHandler, ContextTypes, CommandHandler, MessageHandler, filters
from db.models import Action
from services.game_service import (
get_trivia_question, check_trivia_answer,
get_leaderboard, daily_checkin, get_scores_summary,
lottery_draw, SHOP_ITEMS, buy_shop_item,
)
logger = logging.getLogger("spam_guard")
# 关键词触发
TRIGGER_REACTIONS = {
"谢谢": "👍❤️",
"感谢": "👍❤️🎉",
"哈哈": "😂",
"笑死": "😂💀",
"牛逼": "🔥💪",
"nb": "🔥💪",
"666": "🔥",
"晚安": "🌙✨",
}
async def handle_trigger(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""关键词触发气氛组"""
msg = update.effective_message
if not msg or not msg.text:
return
text = msg.text.strip()
for keyword, emoji in TRIGGER_REACTIONS.items():
if text == keyword or text.startswith(keyword):
try:
await msg.react(emoji)
except Exception:
pass
break
async def cmd_checkin(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""每日签到"""
user = update.effective_user
result = await daily_checkin(user.id, user.username or user.first_name)
if result["ok"]:
reply = await update.message.reply_text(
f"✅ 签到成功!\n"
f"📊 积分: {result['score']}\n"
f"🔥 连续签到: {result['streak']}\n"
f"🎁 签到奖励: +{result['bonus']} 分(基础 {result.get('base', 0)} + 连签 {result.get('streak_bonus', 0)}"
)
else:
reply = await update.message.reply_text("❌ 你今天已经签到过了")
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_trivia(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""收割机知识答题"""
q = await get_trivia_question()
text = f"❓ {q['q']}\n\n"
for i, opt in enumerate(q["opts"], 1):
text += f"{i}. {opt}\n"
text += f"\n💡 请回复序号或答案文字"
context.user_data["trivia_answer"] = q["a"]
context.user_data["trivia_options"] = q["opts"]
reply = await update.message.reply_text(text)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_score(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""查看积分"""
from db.models import GameScore
user = update.effective_user
scores = await GameScore.filter(user_id=user.id).all()
if not scores:
reply = await update.message.reply_text("📊 你还没有积分记录\n💡 签到 /checkin 或答题 /trivia 开始赚积分")
else:
text = "📊 你的积分\n━━━━━━━━━━━━\n"
for s in scores:
text += f"🎮 {s.game_type}: {s.score} 分 (胜率 {s.wins}/{s.total})\n"
reply = await update.message.reply_text(text)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_rank(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""排行榜"""
top = await get_leaderboard("trivia", 10)
if not top:
reply = await update.message.reply_text("🏆 暂无排行数据")
else:
text = "🏆 知识答题排行榜\n━━━━━━━━━━━━━━━━━━\n"
medals = ["🥇", "🥈", "🥉"]
for i, s in enumerate(top):
medal = medals[i] if i < 3 else f" {i+1}."
text += f"{medal} @{s.username or s.user_id}{s.score}\n"
reply = await update.message.reply_text(text)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_lottery(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
result = await lottery_draw(user.id, user.username or user.first_name or str(user.id))
if not result["ok"]:
reply = await update.message.reply_text(f"🎁 抽奖需要 {result.get('cost', 10)} 积分,当前积分不足。\n发送 /checkin 或 /trivia 先赚积分吧。")
else:
reply = await update.message.reply_text(
"🎁 抽奖结果\n"
f"🎯 {result['prize']}\n"
f"💸 消耗:{result['cost']}\n"
f"🎉 奖励:+{result['reward']}\n"
f"📊 当前总积分:{result['total']} 分"
)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_shop(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
summary = await get_scores_summary(user.id)
text = "🛒 积分商城\n━━━━━━━━━━━━\n"
text += f"📊 当前总积分:{summary['total']}\n\n"
for item_id, item in SHOP_ITEMS.items():
text += f"{item_id}. {item['name']}{item['cost']}\n {item['desc']}\n"
text += "\n购买:/buy 商品编号,例如 /buy 1"
reply = await update.message.reply_text(text)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_buy(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not context.args:
reply = await update.message.reply_text("用法:/buy 商品编号,例如 /buy 1")
else:
result = await buy_shop_item(update.effective_user.id, context.args[0])
if result["ok"]:
item = result["item"]
await Action.create(action="SHOP_BUY", user_id=update.effective_user.id, username=update.effective_user.username, details={"item": item})
reply = await update.message.reply_text(f"✅ 兑换成功:{item['name']}\n📊 剩余总积分:{result['total']} 分")
else:
item = result.get("item")
extra = f",需要 {item['cost']} 分" if item else ""
reply = await update.message.reply_text(f"❌ 兑换失败:{result['reason']}{extra}")
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
async def cmd_panel(update: Update, context: ContextTypes.DEFAULT_TYPE):
keyboard = InlineKeyboardMarkup([
[InlineKeyboardButton("👤 我的信息", callback_data="panel:me"), InlineKeyboardButton("✅ 签到", callback_data="panel:checkin")],
[InlineKeyboardButton("🎁 抽奖", callback_data="panel:lottery"), InlineKeyboardButton("🛒 商城", callback_data="panel:shop")],
[InlineKeyboardButton("📖 帮助", callback_data="panel:help")],
])
reply = await update.message.reply_text("🎛️ 操作面板\n请选择要执行的操作:", reply_markup=keyboard)
asyncio.create_task(auto_delete(update.message, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=180))
async def handle_panel_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
user = query.from_user
action = query.data.split(":", 1)[1]
if action == "checkin":
result = await daily_checkin(user.id, user.username or user.first_name or str(user.id))
text = "❌ 你今天已经签到过了" if not result["ok"] else f"✅ 签到成功!奖励 +{result['bonus']} 分,连续 {result['streak']} 天"
elif action == "lottery":
result = await lottery_draw(user.id, user.username or user.first_name or str(user.id))
text = f"❌ {result.get('reason')},抽奖需要 {result.get('cost', 10)} 分" if not result["ok"] else f"🎁 {result['prize']}\n当前总积分:{result['total']} 分"
elif action == "shop":
summary = await get_scores_summary(user.id)
text = f"🛒 积分商城\n当前总积分:{summary['total']}\n" + "\n".join([f"{k}. {v['name']}{v['cost']} 分" for k, v in SHOP_ITEMS.items()]) + "\n\n购买请发送 /buy 商品编号"
elif action == "me":
summary = await get_scores_summary(user.id)
text = f"👤 我的积分\n总积分:{summary['total']}\n" + "\n".join([f"{s.game_type}: {s.score} 分" for s in summary['scores']])
else:
text = "📖 常用:/me /checkin /trivia /lottery /shop /panel"
await query.edit_message_text(text)
async def handle_trivia_answer(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""处理 /trivia 后的用户答题。"""
msg = update.effective_message
user = update.effective_user
if not msg or not msg.text or not user or user.is_bot:
return
correct_answer = context.user_data.get("trivia_answer")
options = context.user_data.get("trivia_options") or []
if not correct_answer:
return
raw_answer = msg.text.strip()
answer = raw_answer
if raw_answer.isdigit():
idx = int(raw_answer) - 1
if 0 <= idx < len(options):
answer = options[idx]
result = await check_trivia_answer(user.id, user.username or user.first_name or str(user.id), answer, correct_answer)
context.user_data.pop("trivia_answer", None)
context.user_data.pop("trivia_options", None)
if result["correct"]:
text = (
"✅ 回答正确!\n"
f"🎯 正确答案:{correct_answer}\n"
f"📊 当前积分:{result['score']}\n"
f"🔥 连击:{result['streak']}"
)
else:
text = (
"❌ 回答错误\n"
f"🎯 正确答案:{correct_answer}\n"
f"📊 当前积分:{result['score']}"
)
reply = await msg.reply_text(text)
asyncio.create_task(auto_delete(msg, seconds=60))
asyncio.create_task(auto_delete(reply, seconds=60))
raise ApplicationHandlerStop
async def auto_delete(message, seconds=60):
try:
await asyncio.sleep(seconds)
await message.delete()
except Exception:
pass
def register_fun(app):
app.add_handler(CommandHandler("checkin", cmd_checkin, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("trivia", cmd_trivia, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("score", cmd_score, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("rank", cmd_rank, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("lottery", cmd_lottery, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("shop", cmd_shop, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("buy", cmd_buy, filters.ChatType.GROUPS))
app.add_handler(CommandHandler("panel", cmd_panel, filters.ChatType.GROUPS))
app.add_handler(CallbackQueryHandler(handle_panel_callback, pattern=r"^panel:"))
app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.GROUPS & ~filters.COMMAND, handle_trivia_answer), group=-1)
app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.GROUPS, handle_trigger), group=2)