fix: preserve user commands while expiring bot replies
This commit is contained in:
@@ -30,7 +30,6 @@ async def cmd_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
f"📦 历史封禁: {stats['total_bans']} 条\n"
|
||||
)
|
||||
reply = await update.message.reply_text(text)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -42,7 +41,6 @@ async def cmd_ban(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
user_id, target = await resolve_target_user(update, context.args)
|
||||
if not user_id:
|
||||
reply = await update.message.reply_text("用法: 回复用户消息 /ban,或 /ban 用户ID/@历史用户名")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -63,7 +61,6 @@ async def cmd_ban(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
logger.exception("手动封禁失败")
|
||||
reply = await update.message.reply_text(f"❌ 封禁失败: {e}")
|
||||
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -75,7 +72,6 @@ async def cmd_unban(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
user_id, target = await resolve_target_user(update, context.args)
|
||||
if not user_id:
|
||||
reply = await update.message.reply_text("用法: /unban 用户ID/@历史用户名")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -88,7 +84,6 @@ async def cmd_unban(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
logger.exception("手动解封失败")
|
||||
reply = await update.message.reply_text(f"❌ 解封失败: {e}")
|
||||
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -100,7 +95,6 @@ async def cmd_warn(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
user_id, target = await resolve_target_user(update, context.args)
|
||||
if not user_id:
|
||||
reply = await update.message.reply_text("用法: 回复用户消息 /warn [原因],或 /warn 用户ID/@历史用户名 [原因]")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -115,7 +109,6 @@ async def cmd_warn(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await Action.create(action="WARN", user_id=user_id, username=target, operator_id=update.effective_user.id, details={"reason": reason})
|
||||
|
||||
reply = await update.message.reply_text(f"⚠️ 已警告 {target} ({user_id})\n原因: {reason}\n累计警告: {warn_count}")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -127,7 +120,6 @@ async def cmd_mute(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
user_id, target = await resolve_target_user(update, context.args)
|
||||
if not user_id:
|
||||
reply = await update.message.reply_text("用法: 回复用户消息 /mute [分钟],或 /mute 用户ID/@历史用户名 [分钟]")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -151,7 +143,6 @@ async def cmd_mute(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
logger.exception("禁言失败")
|
||||
reply = await update.message.reply_text(f"❌ 禁言失败: {e}")
|
||||
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -162,13 +153,11 @@ async def cmd_purge(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
|
||||
if not context.args or not context.args[0].isdigit():
|
||||
reply = await update.message.reply_text("用法: 回复一条消息后 /purge 数量(最多50)")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
if not update.message.reply_to_message:
|
||||
reply = await update.message.reply_text("❌ /purge 需要回复一条消息作为删除起点")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -213,7 +202,6 @@ async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"🧹 回复消息 /purge 数量\n"
|
||||
)
|
||||
reply = await update.message.reply_text(text)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply, seconds=60))
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ async def cmd_bind(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
f"📅 绑定时间: {existing['bound_at']}\n\n"
|
||||
f"如需重新绑定,先 /unbind"
|
||||
)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -35,7 +34,6 @@ async def cmd_bind(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
f"请点击以下链接完成绑定:\n{url}\n\n"
|
||||
"⏳ 链接 5 分钟内有效"
|
||||
)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -48,7 +46,6 @@ async def cmd_unbind(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await Action.create(action="UNBIND", user_id=user.id, username=user.username)
|
||||
else:
|
||||
reply = await update.message.reply_text("❌ 你没有绑定的账号")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ async def cmd_checkin(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
)
|
||||
else:
|
||||
reply = await update.message.reply_text("❌ 你今天已经签到过了")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -69,7 +68,6 @@ async def cmd_trivia(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
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))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -85,7 +83,6 @@ async def cmd_score(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
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))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -101,7 +98,6 @@ async def cmd_rank(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
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))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
@@ -142,7 +138,6 @@ async def handle_trivia_answer(update: Update, context: ContextTypes.DEFAULT_TYP
|
||||
f"📊 当前积分:{result['score']}"
|
||||
)
|
||||
reply = await msg.reply_text(text)
|
||||
asyncio.create_task(auto_delete(msg))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
raise ApplicationHandlerStop
|
||||
|
||||
|
||||
@@ -117,13 +117,11 @@ async def cmd_ask(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"例如: /ask 怎么安装收割机\n"
|
||||
f"文档地址: {config.SITE_URL}"
|
||||
)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
logger.info(f"📩 用户提问: [{update.message.from_user.username}] {query}")
|
||||
searching_msg = await safe_send(update, context, "🔍 正在搜索知识库并整理详细答案...")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
|
||||
try:
|
||||
from services.knowledge_base import ask_knowledge_base
|
||||
|
||||
@@ -25,7 +25,6 @@ async def cmd_night(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"/night on 开启\n"
|
||||
"/night off 关闭"
|
||||
)
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
return
|
||||
|
||||
@@ -39,7 +38,6 @@ async def cmd_night(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
else:
|
||||
reply = await update.message.reply_text("❓ 用法: /night on|off")
|
||||
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
|
||||
+22
-21
@@ -6,7 +6,7 @@ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext import ContextTypes, CallbackQueryHandler, MessageHandler, filters
|
||||
|
||||
import config
|
||||
from db.models import VerifyQueue, Action
|
||||
from db.models import Action
|
||||
from services.verify_service import create_verify, check_answer, get_pending_verify
|
||||
|
||||
logger = logging.getLogger("spam_guard")
|
||||
@@ -28,9 +28,10 @@ async def handle_verify_callback(update: Update, context: ContextTypes.DEFAULT_T
|
||||
f"✅ 验证问题:\n\n{result['question']}\n\n"
|
||||
f"请直接回复你的答案(文字消息)"
|
||||
)
|
||||
# 设置状态等待回答
|
||||
context.user_data["verifying"] = True
|
||||
context.user_data["verify_user_id"] = user.id
|
||||
if query.message:
|
||||
asyncio.create_task(auto_delete(query.message))
|
||||
|
||||
elif data == "verify_help":
|
||||
await query.edit_message_text(
|
||||
@@ -38,21 +39,21 @@ async def handle_verify_callback(update: Update, context: ContextTypes.DEFAULT_T
|
||||
"请直接在群内发送你的答案文字消息。\n"
|
||||
f"你有 {config.VERIFY_MAX_ATTEMPTS} 次机会"
|
||||
)
|
||||
if query.message:
|
||||
asyncio.create_task(auto_delete(query.message))
|
||||
|
||||
|
||||
async def handle_verify_answer(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""处理验证回答"""
|
||||
"""处理验证回答。用户答案不删,只焚毁 Bot 回复。"""
|
||||
if not context.user_data.get("verifying"):
|
||||
return
|
||||
|
||||
user_id = context.user_data.get("verify_user_id", update.effective_user.id)
|
||||
answer = update.message.text.strip()
|
||||
|
||||
result = await check_answer(user_id, answer)
|
||||
|
||||
if result["ok"]:
|
||||
context.user_data["verifying"] = False
|
||||
# 恢复发言权限
|
||||
try:
|
||||
from telegram import ChatPermissions
|
||||
await update.effective_chat.restrict_member(
|
||||
@@ -74,26 +75,26 @@ async def handle_verify_answer(update: Update, context: ContextTypes.DEFAULT_TYP
|
||||
except Exception as e:
|
||||
logger.error(f"⚠️ 恢复权限失败: {e}")
|
||||
|
||||
await update.message.reply_text("🎉 验证通过!欢迎加入群组!")
|
||||
reply = await update.message.reply_text("🎉 验证通过!欢迎加入群组!")
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
await Action.create(action="VERIFY_PASS", user_id=user_id, username=update.effective_user.username)
|
||||
logger.info(f"✅ 验证通过: {user_id}")
|
||||
return
|
||||
|
||||
# 60s 后删除
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
if result["reason"] == "max_attempts":
|
||||
context.user_data["verifying"] = False
|
||||
reply = await update.message.reply_text("❌ 验证失败次数过多,你将被移出群组")
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
try:
|
||||
await update.effective_chat.ban_member(user_id=user_id)
|
||||
await asyncio.sleep(5)
|
||||
await update.effective_chat.unban_member(user_id=user_id)
|
||||
except Exception:
|
||||
pass
|
||||
await Action.create(action="VERIFY_FAIL", user_id=user_id, username=update.effective_user.username)
|
||||
else:
|
||||
if result["reason"] == "max_attempts":
|
||||
context.user_data["verifying"] = False
|
||||
await update.message.reply_text("❌ 验证失败次数过多,你将被移出群组")
|
||||
try:
|
||||
await update.effective_chat.ban_member(user_id=user_id)
|
||||
await asyncio.sleep(5)
|
||||
await update.effective_chat.unban_member(user_id=user_id)
|
||||
except Exception:
|
||||
pass
|
||||
await Action.create(action="VERIFY_FAIL", user_id=user_id, username=update.effective_user.username)
|
||||
else:
|
||||
await update.message.reply_text(f"❌ 答案错误,剩余 {config.VERIFY_MAX_ATTEMPTS - result.get('attempts', 0)} 次机会")
|
||||
asyncio.create_task(auto_delete(update.message))
|
||||
reply = await update.message.reply_text(f"❌ 答案错误,剩余 {config.VERIFY_MAX_ATTEMPTS - result.get('attempts', 0)} 次机会")
|
||||
asyncio.create_task(auto_delete(reply))
|
||||
|
||||
|
||||
async def auto_delete(message, seconds=60):
|
||||
|
||||
Reference in New Issue
Block a user