🔴 致命(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 回复
207 lines
7.3 KiB
Python
207 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
QQ 好友管理动作 — 通过 NapCat (OneBot) HTTP API
|
||
|
||
支持操作:
|
||
- 删除好友(delete friend)
|
||
- 拉黑用户(delete friend + 从所有群踢出+禁止加群)
|
||
- 同意好友请求(approve friend request)
|
||
- 拒绝好友请求(reject friend request)
|
||
- 列出好友列表
|
||
|
||
权限规则:
|
||
- 删除好友、拉黑:只有 boss(YOUR_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()
|