497 lines
22 KiB
Python
497 lines
22 KiB
Python
"""气氛组 + 小游戏 handler"""
|
||
import asyncio
|
||
import logging
|
||
|
||
import config
|
||
from services.message_utils import record_bot_message
|
||
from services.admin_utils import require_admin, is_admin
|
||
|
||
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,
|
||
SHOP_ITEMS, buy_shop_item, list_shop_items,
|
||
parse_lottery_spec, create_lottery_event, lottery_condition_text, lottery_end_text,
|
||
count_lottery_participants, join_lottery_event, draw_lottery_event,
|
||
)
|
||
|
||
logger = logging.getLogger("spam_guard")
|
||
PANEL_IDLE_SECONDS = 60
|
||
|
||
# 关键词触发
|
||
def panel_keyboard():
|
||
return 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")],
|
||
])
|
||
|
||
|
||
def schedule_panel_idle_delete(context: ContextTypes.DEFAULT_TYPE, message, seconds: int = PANEL_IDLE_SECONDS):
|
||
tasks = context.bot_data.setdefault("panel_idle_tasks", {})
|
||
key = (message.chat_id, message.message_id)
|
||
old_task = tasks.pop(key, None)
|
||
if old_task and not old_task.done():
|
||
old_task.cancel()
|
||
|
||
async def _delete_later():
|
||
try:
|
||
await asyncio.sleep(seconds)
|
||
await message.delete()
|
||
except asyncio.CancelledError:
|
||
return
|
||
except Exception:
|
||
pass
|
||
finally:
|
||
tasks.pop(key, None)
|
||
|
||
tasks[key] = asyncio.create_task(_delete_later())
|
||
|
||
|
||
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(update.effective_chat.id, 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))
|
||
await record_bot_message(reply)
|
||
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))
|
||
await record_bot_message(reply)
|
||
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))
|
||
await record_bot_message(reply)
|
||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||
|
||
|
||
async def cmd_rank(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
"""排行榜"""
|
||
top = await get_leaderboard(update.effective_chat.id, "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))
|
||
await record_bot_message(reply)
|
||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||
|
||
|
||
async def build_lottery_text(event, count: int | None = None) -> str:
|
||
if count is None:
|
||
count = await count_lottery_participants(event.id)
|
||
return (
|
||
"🎁 群内抽奖\n"
|
||
"━━━━━━━━━━━━\n"
|
||
f"🏷️ 标题:{event.title or event.prize}\n"
|
||
f"🎯 奖品:{event.prize}\n"
|
||
f"👤 发起人:@{event.creator_name or event.creator_id}\n"
|
||
+ (f"📝 描述:{event.description}\n" if getattr(event, "description", None) else "")
|
||
+ f"✅ 参与条件:{await lottery_condition_text(event)}\n"
|
||
f"⏱️ 结束条件:{await lottery_end_text(event)}\n"
|
||
f"👥 当前参与:{count} 人\n\n"
|
||
"点击下方按钮参与抽奖。"
|
||
)
|
||
|
||
|
||
def lottery_keyboard(event_id: int) -> InlineKeyboardMarkup:
|
||
return InlineKeyboardMarkup([[InlineKeyboardButton("🎁 参与抽奖", callback_data=f"lottery:join:{event_id}")]])
|
||
|
||
|
||
async def schedule_lottery_draw(context: ContextTypes.DEFAULT_TYPE, event_id: int, seconds: int):
|
||
async def _run():
|
||
try:
|
||
await asyncio.sleep(seconds)
|
||
result = await draw_lottery_event(event_id)
|
||
event = result.get("event")
|
||
if not event or not event.message_id:
|
||
return
|
||
if result.get("ok"):
|
||
winner = result["winner"]
|
||
text = (
|
||
"🎉 抽奖已开奖\n"
|
||
"━━━━━━━━━━━━\n"
|
||
f"🏷️ 标题:{event.title or event.prize}\n"
|
||
f"🎯 奖品:{event.prize}\n"
|
||
f"👥 参与人数:{result['count']} 人\n"
|
||
f"🏆 中奖用户:@{winner.username or winner.first_name or winner.user_id}"
|
||
)
|
||
else:
|
||
text = f"🎁 抽奖已结束\n━━━━━━━━━━━━\n🎯 奖品:{event.prize}\n❌ {result.get('reason', '未开奖')}"
|
||
await context.bot.edit_message_text(chat_id=event.chat_id, message_id=event.message_id, text=text)
|
||
except Exception as e:
|
||
logger.warning(f"⚠️ 自动开奖失败: {type(e).__name__}: {e}")
|
||
asyncio.create_task(_run())
|
||
|
||
|
||
async def create_lottery_from_text(update: Update, context: ContextTypes.DEFAULT_TYPE, spec_text: str, publish_chat_id: int | None = None):
|
||
user = update.effective_user
|
||
msg = update.effective_message
|
||
spec = parse_lottery_spec(spec_text)
|
||
target_chat_id = publish_chat_id or msg.chat_id
|
||
event = await create_lottery_event(
|
||
chat_id=target_chat_id,
|
||
creator_id=user.id,
|
||
creator_name=user.username or user.first_name or str(user.id),
|
||
spec=spec,
|
||
)
|
||
lottery_text = await build_lottery_text(event)
|
||
sent = await context.bot.send_message(
|
||
chat_id=target_chat_id,
|
||
text=lottery_text,
|
||
reply_markup=lottery_keyboard(event.id),
|
||
)
|
||
await record_bot_message(sent, lottery_text)
|
||
event.message_id = sent.message_id
|
||
await event.save()
|
||
if event.draw_type == "time" or event.end_type in {"minutes", "time"}:
|
||
await schedule_lottery_draw(context, event.id, event.end_value * 60)
|
||
return event
|
||
|
||
|
||
async def send_lottery_private入口(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
me = await context.bot.get_me()
|
||
bot_username = me.username
|
||
target_chat_id = update.effective_chat.id
|
||
url = f"https://t.me/{bot_username}?start=lottery_{target_chat_id}"
|
||
keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("去私聊设置抽奖", url=url)]])
|
||
text = (
|
||
"🎁 发起群内抽奖\n"
|
||
"为了避免奖品和条件刷屏,请到 Bot 私聊界面设置。\n\n"
|
||
f"👉 点击进入:{url}"
|
||
)
|
||
reply = await update.effective_message.reply_text(text, reply_markup=keyboard, disable_web_page_preview=True)
|
||
asyncio.create_task(auto_delete(update.effective_message, seconds=60))
|
||
await record_bot_message(reply)
|
||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||
return reply
|
||
|
||
|
||
async def cmd_lottery(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
if update.effective_chat.type != "private":
|
||
await send_lottery_private入口(update, context)
|
||
return
|
||
|
||
context.user_data["awaiting_lottery_spec"] = True
|
||
context.user_data["lottery_target_chat_id"] = config.TG_CHAT_ID
|
||
reply = await update.message.reply_text(
|
||
"🎁 发起群内抽奖\n"
|
||
"请发送:奖品 | 参与条件 | 结束条件\n\n"
|
||
"示例:月卡一张 | all | people:3\n"
|
||
"参与条件:all / checkin / points:N\n"
|
||
"结束条件:people:N / minutes:N"
|
||
)
|
||
await record_bot_message(reply)
|
||
|
||
|
||
async def cmd_shop(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
user = update.effective_user
|
||
summary = await get_scores_summary(user.id, query.message.chat_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))
|
||
await record_bot_message(reply)
|
||
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_chat.id, update.effective_user.id, context.args[0], update.effective_user.username or update.effective_user.first_name or str(update.effective_user.id))
|
||
if result["ok"]:
|
||
item = result["item"]
|
||
await Action.create(action="SHOP_BUY", chat_id=update.effective_chat.id, user_id=update.effective_user.id, username=update.effective_user.username, details={"item": item, "chat_id": update.effective_chat.id})
|
||
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))
|
||
await record_bot_message(reply)
|
||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||
|
||
|
||
async def cmd_panel(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
if not await require_admin(update, context):
|
||
return
|
||
reply = await update.message.reply_text(
|
||
"🎛️ 操作面板\n请选择要执行的操作:\n\n⏱️ 60 秒无操作后自动消失。",
|
||
reply_markup=panel_keyboard(),
|
||
)
|
||
asyncio.create_task(auto_delete(update.message, seconds=60))
|
||
schedule_panel_idle_delete(context, reply)
|
||
|
||
async def handle_panel_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
query = update.callback_query
|
||
if not await is_admin(update, context):
|
||
await query.answer("只有管理员可以使用操作面板", show_alert=True)
|
||
return
|
||
await query.answer()
|
||
user = query.from_user
|
||
action = query.data.split(":", 1)[1]
|
||
if action == "checkin":
|
||
result = await daily_checkin(query.message.chat_id, 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":
|
||
me = await context.bot.get_me()
|
||
url = f"https://t.me/{me.username}?start=lottery_{query.message.chat_id}"
|
||
keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("去私聊设置抽奖", url=url)]])
|
||
await query.edit_message_text(
|
||
"🎁 发起群内抽奖\n"
|
||
"请到 Bot 私聊界面设置奖品、参与条件和结束条件。\n\n"
|
||
f"👉 点击进入:{url}\n\n"
|
||
"⏱️ 60 秒无操作后自动消失。",
|
||
reply_markup=keyboard,
|
||
disable_web_page_preview=True,
|
||
)
|
||
schedule_panel_idle_delete(context, query.message)
|
||
return
|
||
elif action == "shop":
|
||
summary = await get_scores_summary(user.id, query.message.chat_id)
|
||
text = f"🛒 积分商城\n当前总积分:{summary['total']} 分\n" + "\n".join([f"{v['item_key']}. {v['name']} — {v['cost']} 分" for v in await list_shop_items(query.message.chat_id)]) + "\n\n购买请发送 /buy 商品编号"
|
||
elif action == "me":
|
||
summary = await get_scores_summary(user.id, query.message.chat_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 + "\n\n⏱️ 60 秒无操作后自动消失。",
|
||
reply_markup=panel_keyboard(),
|
||
)
|
||
schedule_panel_idle_delete(context, query.message)
|
||
|
||
|
||
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(update.effective_chat.id, 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))
|
||
await record_bot_message(reply)
|
||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||
raise ApplicationHandlerStop
|
||
|
||
|
||
async def handle_lottery_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
query = update.callback_query
|
||
await query.answer()
|
||
parts = query.data.split(":")
|
||
event_id = int(parts[-1])
|
||
user = query.from_user
|
||
result = await join_lottery_event(event_id, user.id, user.username or "", user.first_name or "")
|
||
if not result["ok"]:
|
||
await query.answer(result["reason"], show_alert=True)
|
||
return
|
||
event = result["event"]
|
||
if result.get("won"):
|
||
remain = max(0, int(getattr(event, "remaining_count", 0) or 0))
|
||
text = (
|
||
"🎉 即开即中\n"
|
||
"━━━━━━━━━━━━\n"
|
||
f"🏷️ 标题:{event.title or event.prize}\n"
|
||
f"🎯 奖品:{event.prize}\n"
|
||
f"🏆 中奖用户:@{user.username or user.first_name or user.id}\n"
|
||
f"🎁 剩余奖品:{remain}"
|
||
)
|
||
if result.get("ended"):
|
||
text += "\n✅ 奖品已抽完,活动结束"
|
||
await query.edit_message_text(text)
|
||
else:
|
||
await query.edit_message_text(await build_lottery_text(event, result["count"]), reply_markup=lottery_keyboard(event.id))
|
||
await query.answer("恭喜中奖!", show_alert=True)
|
||
elif result["should_draw"]:
|
||
draw = await draw_lottery_event(event.id)
|
||
if draw.get("ok"):
|
||
winner = draw.get("winner") or {}
|
||
winner_name = winner.get("username") or winner.get("first_name") or winner.get("user_id")
|
||
text = (
|
||
"🎉 抽奖已开奖\n"
|
||
"━━━━━━━━━━━━\n"
|
||
f"🏷️ 标题:{event.title or event.prize}\n"
|
||
f"🎯 奖品:{event.prize}\n"
|
||
f"👥 参与人数:{draw['count']} 人\n"
|
||
f"🏆 中奖用户:@{winner_name}"
|
||
)
|
||
await query.edit_message_text(text)
|
||
else:
|
||
await query.edit_message_text(f"🎁 抽奖已结束\n❌ {draw.get('reason', '未开奖')}")
|
||
else:
|
||
await query.edit_message_text(await build_lottery_text(event, result["count"]), reply_markup=lottery_keyboard(event.id))
|
||
await query.answer(f"参与成功,当前 {result['count']} 人")
|
||
|
||
|
||
async def cmd_lottery_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
if update.effective_chat.type != "private" or not context.args:
|
||
return
|
||
arg = context.args[0]
|
||
if not arg.startswith("lottery_"):
|
||
return
|
||
try:
|
||
target_chat_id = int(arg.split("_", 1)[1])
|
||
except Exception:
|
||
target_chat_id = config.TG_CHAT_ID
|
||
context.user_data["awaiting_lottery_spec"] = True
|
||
context.user_data["lottery_target_chat_id"] = target_chat_id
|
||
reply = await update.message.reply_text(
|
||
"🎁 发起群内抽奖\n"
|
||
"请发送:奖品 | 参与条件 | 结束条件\n\n"
|
||
"示例:月卡一张 | all | people:3\n"
|
||
"参与条件:all / checkin / points:N\n"
|
||
"结束条件:people:N / minutes:N"
|
||
)
|
||
|
||
|
||
async def handle_private_lottery_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
if update.effective_chat.type != "private" or not context.user_data.get("awaiting_lottery_spec"):
|
||
return
|
||
try:
|
||
target_chat_id = int(context.user_data.get("lottery_target_chat_id") or config.TG_CHAT_ID)
|
||
event = await create_lottery_from_text(update, context, update.message.text.strip(), publish_chat_id=target_chat_id)
|
||
context.user_data.pop("awaiting_lottery_spec", None)
|
||
context.user_data.pop("lottery_target_chat_id", None)
|
||
await update.message.reply_text(
|
||
"✅ 抽奖活动已发布到群里\n"
|
||
f"🏷️ 标题:{event.title or event.prize}\n"
|
||
f"🎯 奖品:{event.prize}\n"
|
||
f"✅ 参与条件:{await lottery_condition_text(event)}\n"
|
||
f"⏱️ 结束条件:{await lottery_end_text(event)}"
|
||
)
|
||
except Exception as e:
|
||
await update.message.reply_text(
|
||
f"❌ 抽奖格式错误:{e}\n\n"
|
||
"请重新发送:奖品 | 参与条件 | 结束条件\n"
|
||
"示例:月卡一张 | all | people:3"
|
||
)
|
||
|
||
|
||
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))
|
||
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(CommandHandler("start", cmd_lottery_start, filters.ChatType.PRIVATE), group=1)
|
||
app.add_handler(CallbackQueryHandler(handle_panel_callback, pattern=r"^panel:"))
|
||
app.add_handler(CallbackQueryHandler(handle_lottery_callback, pattern=r"^lottery:"))
|
||
app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.PRIVATE & ~filters.COMMAND, handle_private_lottery_text), group=1)
|
||
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)
|