From 84fee7e28751a65e042a34ed1fca3bbed987473f Mon Sep 17 00:00:00 2001 From: ngfchl Date: Tue, 7 Jul 2026 09:18:11 +0800 Subject: [PATCH] feat: support reply-based admin actions --- handlers/admin.py | 69 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/handlers/admin.py b/handlers/admin.py index da6d345..8c96c2f 100644 --- a/handlers/admin.py +++ b/handlers/admin.py @@ -38,6 +38,25 @@ async def mark_reply_message_deleted(reply_msg, reason: str = "command_ban_reply await rec.save() +async def delete_replied_message(update: Update, reason: str) -> None: + """删除管理命令引用的目标消息,并同步标记本地消息记录。""" + reply_msg = update.message.reply_to_message if update.message else None + if not reply_msg: + return + try: + await reply_msg.delete() + await mark_reply_message_deleted(reply_msg, reason=reason) + except Exception as e: + logger.debug(f"删除引用消息失败: {type(e).__name__}: {e}") + + +async def delete_command_message(update: Update) -> None: + """尽量删除管理员命令消息,失败不影响主流程。""" + try: + await update.message.delete() + except Exception as e: + logger.debug(f"删除命令消息失败: {type(e).__name__}: {e}") + async def cmd_stats(update: Update, context: ContextTypes.DEFAULT_TYPE): """今日统计""" @@ -80,19 +99,12 @@ async def cmd_ban(update: Update, context: ContextTypes.DEFAULT_TYPE): method="reply_command" if update.message.reply_to_message else "command", ) if update.message.reply_to_message: - try: - await update.message.reply_to_message.delete() - await mark_reply_message_deleted(update.message.reply_to_message) - except Exception: - pass - try: - await update.message.delete() - except Exception: - pass + await delete_replied_message(update, reason="command_ban_reply") + await delete_command_message(update) reply = await update.effective_chat.send_message(f"🔨 已封禁 {target} ({user_id}) {config.BAN_DURATION_MINUTES} 分钟") except Exception as e: logger.error(f"手动封禁失败: {type(e).__name__}: {e}") - reply = await update.message.reply_text(f"❌ 封禁失败: {e}") + reply = await update.message.reply_text("❌ 封禁失败,请检查 Bot 权限或目标用户状态") asyncio.create_task(auto_delete(reply)) @@ -115,7 +127,7 @@ async def cmd_unban(update: Update, context: ContextTypes.DEFAULT_TYPE): reply = await update.message.reply_text(f"🔓 已解封用户 {target} ({user_id})") except Exception as e: logger.error(f"手动解封失败: {type(e).__name__}: {e}") - reply = await update.message.reply_text(f"❌ 解封失败: {e}") + reply = await update.message.reply_text("❌ 解封失败,请检查 Bot 权限或目标用户状态") asyncio.create_task(auto_delete(reply)) @@ -137,14 +149,22 @@ async def cmd_warn(update: Update, context: ContextTypes.DEFAULT_TYPE): else: reason = " ".join(context.args[1:]) if len(context.args) > 1 else "未说明原因" - warn_count = await Warning.filter(user_id=user_id).count() + 1 - await Warning.create(user_id=user_id, username=target, reason=reason, operator_id=update.effective_user.id) - await Action.create(action="WARN", user_id=user_id, username=target, operator_id=update.effective_user.id, details={"reason": reason}) + try: + warn_count = await Warning.filter(user_id=user_id).count() + 1 + await Warning.create(user_id=user_id, username=target, reason=reason, operator_id=update.effective_user.id) + await Action.create(action="WARN", user_id=user_id, username=target, operator_id=update.effective_user.id, details={"reason": reason}) + if update.message.reply_to_message: + await delete_replied_message(update, reason="command_warn_reply") + await delete_command_message(update) + reply = await update.effective_chat.send_message(f"⚠️ 已警告 {target} ({user_id})\n原因: {reason}\n累计警告: {warn_count}") + else: + reply = await update.message.reply_text(f"⚠️ 已警告 {target} ({user_id})\n原因: {reason}\n累计警告: {warn_count}") + except Exception as e: + logger.error(f"警告失败: {type(e).__name__}: {e}") + reply = await update.message.reply_text("❌ 警告失败,请检查 Bot 权限或数据库状态") - reply = await update.message.reply_text(f"⚠️ 已警告 {target} ({user_id})\n原因: {reason}\n累计警告: {warn_count}") asyncio.create_task(auto_delete(reply)) - async def cmd_mute(update: Update, context: ContextTypes.DEFAULT_TYPE): """禁言用户""" if not await require_admin(update, context): @@ -171,10 +191,15 @@ async def cmd_mute(update: Update, context: ContextTypes.DEFAULT_TYPE): until_date=until_date, ) await Action.create(action="MUTE", user_id=user_id, username=target, operator_id=update.effective_user.id, details={"minutes": minutes}) - reply = await update.message.reply_text(f"🔇 已禁言 {target} ({user_id}) {minutes} 分钟") + if update.message.reply_to_message: + await delete_replied_message(update, reason="command_mute_reply") + await delete_command_message(update) + reply = await update.effective_chat.send_message(f"🔇 已禁言 {target} ({user_id}) {minutes} 分钟") + else: + reply = await update.message.reply_text(f"🔇 已禁言 {target} ({user_id}) {minutes} 分钟") except Exception as e: logger.error(f"禁言失败: {type(e).__name__}: {e}") - reply = await update.message.reply_text(f"❌ 禁言失败: {e}") + reply = await update.message.reply_text("❌ 禁言失败,请检查 Bot 权限或目标用户状态") asyncio.create_task(auto_delete(reply)) @@ -228,10 +253,10 @@ async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE): "🔗 /bind - 绑定账号\n" "🌙 /night on/off - 夜间模式(管理员)\n\n" "管理命令(管理员):\n" - "🔨 回复消息 /ban 或 /ban 用户ID\n" - "🔓 /unban 用户ID\n" - "⚠️ 回复消息 /warn [原因]\n" - "🔇 回复消息 /mute [分钟]\n" + "🔨 /ban 用户ID/@历史用户名,或回复消息 /ban(删除原消息并封禁)\n" + "🔇 /mute 用户ID/@历史用户名 [分钟],或回复消息 /mute [分钟](禁言并删除原消息)\n" + "⚠️ /warn 用户ID/@历史用户名 [原因],或回复消息 /warn [原因](警告并删除原消息)\n" + "🔓 /unban 用户ID/@历史用户名\n" "🧹 回复消息 /purge 数量\n" ) reply = await update.message.reply_text(text)