Files
chatrebot_aireply_plug/scripts/qq_friend_action.py
root 9dff73e7bc fix: 通过本地完整测试发现的 11 个 bug
🔴 致命(5个):
- Bug1: onebot11.json 格式错误(旧 {http:{},ws:{}} → network.httpServers/httppClients)
- Bug2: 端口映射错位(WebUI 6099, OneBot API 6097 独立端口)
- Bug3: 变量名含空格 NAP CAT_HOST → NAPCAT_HOST(14脚本+2SKILL+README)
- Bug4: heredoc 单引号阻止变量展开
- Bug5: process.py 缺失 _get_high_risk_words() 方法

🟡 重要(3个):
- Bug6: onebot11.json 默认不存在(cat > 而非 edit)
- Bug7: API 须登录后才响应(新增扫码引导步骤)
- Bug8: qqrebot systemd 依赖 napcat 服务

🟢 中等(3个):
- Bug9: Skills 占位符缺 YOUR_NAPCAT_HOST sed 覆盖
- Bug10: venv 不可用需降级方案
- Bug11: 端口 25580 冲突处理
已验证全链路:NapCat + qqrebot + 插件 + OpenClaw Gateway + AI 回复
2026-05-04 19:47:26 +08:00

207 lines
7.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

#!/usr/bin/env python3
"""
QQ 好友管理动作 — 通过 NapCat (OneBot) HTTP API
支持操作:
- 删除好友delete friend
- 拉黑用户delete friend + 从所有群踢出+禁止加群)
- 同意好友请求approve friend request
- 拒绝好友请求reject friend request
- 列出好友列表
权限规则:
- 删除好友、拉黑:只有 bossYOUR_ADMIN_QQ批准后才能执行
- 例外:检测到攻击性信息时,**自动删除+拉黑一条龙**,无需等待批准
- 同意/拒绝好友请求:必须问 boss
用法:
python3 qq_friend_action.py --delete 12345678 # 删除好友
python3 qq_friend_action.py --block 12345678 # 拉黑用户(删好友+从所有群踢出)
python3 qq_friend_action.py --block 12345678 --gid YOUR_GROUP_ID # 从指定群踢出+拒绝加群
python3 qq_friend_action.py --approve-friend <flag> # 同意好友请求
python3 qq_friend_action.py --reject-friend <flag> # 拒绝好友请求
python3 qq_friend_action.py --list-friends # 列出好友
"""
import json
import sys
import argparse
import requests
CQHTTP_URL = "http://YOUR_NAPCAT_HOST:25570"
def delete_friend(user_id: int) -> dict:
"""删除好友"""
resp = requests.post(f"{CQHTTP_URL}/delete_friend",
json={"user_id": user_id}, timeout=10)
return resp.json()
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", [])
return []
def set_group_kick(group_id: int, user_id: int) -> dict:
"""从群踢出用户并拒绝加群"""
# 注意NapCat 可能需要 uid 格式try-except 兜底
resp = requests.post(f"{CQHTTP_URL}/set_group_kick", json={
"group_id": group_id,
"user_id": user_id,
"reject_add_request": True
}, timeout=10)
return resp.json()
def set_group_ban(group_id: int, user_id: int, duration: int = 2592000) -> dict:
"""禁言用户30天"""
resp = requests.post(f"{CQHTTP_URL}/set_group_ban", json={
"group_id": group_id,
"user_id": user_id,
"duration": duration
}, timeout=10)
return resp.json()
def handle_friend_request(flag: str, approve: bool, remark: str = "") -> dict:
"""处理好友请求"""
params = {
"flag": flag,
"approve": approve,
}
if approve and remark:
params["remark"] = remark
resp = requests.post(f"{CQHTTP_URL}/set_friend_add_request",
json=params, timeout=10)
return resp.json()
def get_friend_list():
"""获取好友列表"""
resp = requests.post(f"{CQHTTP_URL}/get_friend_list", json={}, timeout=10)
data = resp.json()
if data.get("status") == "ok":
return data.get("data", [])
return []
def format_friends(friends: list) -> str:
if not friends:
return "暂无好友数据"
lines = [f"{len(friends)} 个好友\n"]
for f in friends:
uid = f.get("user_id", "?")
nickname = f.get("nickname", "?")
remark = f.get("remark", "")
remark_str = f"(备注:{remark}" if remark else ""
lines.append(f"{nickname} ({uid}){remark_str}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="QQ 好友管理操作")
parser.add_argument("--delete", type=int, metavar="QQ号", help="删除好友")
parser.add_argument("--block", type=int, metavar="QQ号", help="拉黑用户(删好友+踢出所有群)")
parser.add_argument("--gid", type=int, help="仅从指定群踢出(配合 --block")
parser.add_argument("--approve-friend", type=str, help="同意好友请求(输入 flag")
parser.add_argument("--reject-friend", type=str, help="拒绝好友请求(输入 flag")
parser.add_argument("--remark", type=str, default="", help="好友备注(可选,仅同意时生效)")
parser.add_argument("--list-friends", action="store_true", help="列出好友列表")
parser.add_argument("--json", action="store_true", help="JSON 格式输出")
args = parser.parse_args()
try:
if args.list_friends:
friends = get_friend_list()
if args.json:
print(json.dumps(friends, ensure_ascii=False, indent=2))
else:
print(format_friends(friends))
return
if args.delete:
user_id = args.delete
result = delete_friend(user_id)
if result.get("status") == "ok":
print(f"✅ 已删除好友 {user_id}")
else:
print(f"❌ 删除好友失败: {result.get('message', '未知错误')}")
sys.exit(1)
return
if args.block:
user_id = args.block
results = []
# 1. 删除好友
r1 = delete_friend(user_id)
deleted = r1.get("status") == "ok"
if deleted:
results.append(f"✅ 已删除好友 {user_id}")
else:
results.append(f"❌ 删除好友失败: {r1.get('message', '未知错误')}")
# 2. 从群踢出
if args.gid:
groups = [{"group_id": args.gid, "group_name": ""}]
else:
groups = get_group_list()
kicked_groups = []
for g in groups:
gid = g.get("group_id")
if not gid:
continue
try:
r_kick = set_group_kick(gid, user_id)
if r_kick.get("status") == "ok":
gname = g.get("group_name", str(gid))
kicked_groups.append(gname)
except Exception:
pass
if kicked_groups:
results.append(f"✅ 已从 {len(kicked_groups)} 个群踢出:{', '.join(kicked_groups[:3])}")
else:
results.append(" 未执行踢出操作(可能已不是好友/不在群中)")
print("\n".join(results))
return
if args.approve_friend:
result = handle_friend_request(args.approve_friend, approve=True,
remark=args.remark)
if result.get("status") == "ok":
print(f"✅ 已同意好友请求 {args.approve_friend}")
else:
print(f"❌ 同意失败: {result.get('message', '未知错误')}")
sys.exit(1)
return
if args.reject_friend:
result = handle_friend_request(args.reject_friend, approve=False)
if result.get("status") == "ok":
print(f"✅ 已拒绝好友请求 {args.reject_friend}")
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()