#!/usr/bin/env python3 """ QQ 群管理动作 — 通过 NapCat (OneBot) HTTP API 支持操作: - 退群(leave group) - 按群号加群(join group) - 接受进群邀请(approve invite) - 拒绝进群邀请(reject invite) - 处理加群请求(approve/reject group request) 注意: - 进群/退群操作 **必须** 先问老板,老板同意才能执行 - 紧急情况(如检测到攻击性信息)除外 - 退群操作不可逆,执行前二次确认 - 加群需要有人邀请或群主通过群链接/名片形式邀请,无法通过纯 API 直接加入 用法: python3 qq_group_action.py --leave YOUR_BOT_QQ # 退出群 python3 qq_group_action.py --join YOUR_BOT_QQ # 按群号尝试加群(需有邀请) python3 qq_group_action.py --approve-invite # 接受进群邀请 python3 qq_group_action.py --reject-invite # 拒绝进群邀请 python3 qq_group_action.py --list-groups # 列出当前群列表 """ import json import sys import argparse import requests CQHTTP_URL = "http://YOUR_NAPCAT_HOST:25570" def get_group_list(): """获取当前已加入的群列表""" resp = requests.post(f"{CQHTTP_URL}/get_group_list", json={}, timeout=10) data = resp.json() if data.get("status") == "ok": return data.get("data", []) raise Exception(f"API 异常: {json.dumps(data, ensure_ascii=False)}") def get_group_system_msgs(): """获取系统消息,包括待处理的进群邀请""" resp = requests.post(f"{CQHTTP_URL}/get_group_system_msg", json={}, timeout=10) data = resp.json() if data.get("status") == "ok": return data.get("data", {}) return {} def leave_group(group_id: int) -> dict: """退出指定群聊。不可逆!""" resp = requests.post(f"{CQHTTP_URL}/set_group_leave", json={"group_id": group_id}, timeout=10) return resp.json() def join_group_via_invite(group_id: int) -> str: """ 尝试通过群号加群。 策略: 1. 检查是否已在群里 → 直接返回 2. 检查是否有该群的待处理邀请 → 接受邀请 3. 没有邀请 → 告知需要通过邀请方式加群 """ # 1. 检查是否已在群 groups = get_group_list() for g in groups: if g.get("group_id") == group_id: return f"✅ 已经在群里: {g.get('group_name', '未知')} ({group_id})" # 2. 检查待处理邀请 sys_msgs = get_group_system_msgs() invites = sys_msgs.get("invited_requests", []) for inv in invites: if inv.get("group_id") == group_id: # 接受邀请 flag = str(inv.get("request_id", "")) resp = requests.post(f"{CQHTTP_URL}/set_group_add_request", json={ "flag": flag, "sub_type": "invite", "approve": True, }, timeout=10) data = resp.json() if data.get("status") == "ok": return f"✅ 已接受群邀请,成功加入: {inv.get('group_name', '?')} ({group_id})" else: return f"❌ 接受邀请失败: {data.get('message', '未知错误')}" # 3. 没有待处理的邀请 group_name = "(未知)" try: resp = requests.post(f"{CQHTTP_URL}/get_group_info", json={"group_id": group_id}, timeout=5) d = resp.json() if d.get("status") == "ok" and d.get("data"): group_name = d["data"].get("group_name", group_name) except: pass return ( f"⚠️ 无法自动加入群 {group_name} ({group_id})\n" f" QQ 协议不提供通过 API 直接加群的能力。\n" f" 需要有人邀请 bot 进群,bot 收到邀请后自动处理。\n" f" 请让群管理用 QQ 客户端邀请 bot(QQ号: YOUR_ADMIN_QQ),\n" f" 或发送群邀请链接/二维码给 bot,bot 会自动接受。" ) def handle_group_request(flag: str, approve: bool, reason: str = "") -> dict: """处理加群请求 / 群邀请""" params = { "flag": flag, "sub_type": "invite" if "invite" in flag else "add", "approve": approve, } if not approve and reason: params["reason"] = reason resp = requests.post(f"{CQHTTP_URL}/set_group_add_request", json=params, timeout=10) return resp.json() def format_group_info(groups: list) -> str: """格式化群列表为可读文本""" if not groups: return "当前没有加入任何群" lines = [f"共 {len(groups)} 个群\n"] for g in groups: gid = g.get("group_id", "?") name = g.get("group_name", "?") mc = g.get("member_count", "?") mm = g.get("max_member_count", "?") lines.append(f"• {name} ({gid}) — {mc}/{mm} 人") return "\n".join(lines) def main(): parser = argparse.ArgumentParser(description="QQ 群管理操作") parser.add_argument("--leave", type=int, help="退出指定群(输入群号)") parser.add_argument("--join", type=int, help="尝试加入指定群(输入群号)") parser.add_argument("--approve-invite", type=str, help="接受进群邀请(输入 flag)") parser.add_argument("--reject-invite", type=str, help="拒绝进群邀请(输入 flag)") parser.add_argument("--reason", type=str, default="", help="拒绝理由(可选)") parser.add_argument("--list-groups", action="store_true", help="列出当前加入的群") parser.add_argument("--json", action="store_true", help="JSON 格式输出") args = parser.parse_args() try: if args.list_groups: groups = get_group_list() if args.json: print(json.dumps(groups, ensure_ascii=False, indent=2)) else: print(format_group_info(groups)) return if args.leave: result = leave_group(args.leave) if result.get("status") == "ok": print(f"✅ 已退出群 {args.leave}") else: print(f"❌ 退群失败: {result.get('message', '未知错误')}") sys.exit(1) return if args.join: result = join_group_via_invite(args.join) success = result.startswith("✅") print(result) sys.exit(0 if success else 1) return if args.approve_invite: result = handle_group_request(args.approve_invite, approve=True) if result.get("status") == "ok": print(f"✅ 已接受群邀请 {args.approve_invite}") else: print(f"❌ 接受邀请失败: {result.get('message', '未知错误')}") sys.exit(1) return if args.reject_invite: result = handle_group_request(args.reject_invite, approve=False, reason=args.reason) if result.get("status") == "ok": print(f"✅ 已拒绝群邀请 {args.reject_invite}") else: print(f"❌ 拒绝邀请失败: {result.get('message', '未知错误')}") sys.exit(1) return parser.print_help() except requests.exceptions.ConnectionError: print(f"❌ 连接失败: 无法连接到 NapCat ({CQHTTP_URL})") sys.exit(1) except Exception as e: print(f"❌ 操作失败: {e}") sys.exit(1) if __name__ == "__main__": main()