200 lines
6.4 KiB
Python
200 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
QQ 号/群号 ↔ 用户名/群名 转换脚本
|
||
|
||
将数字 ID 转为可读的名称。通过 NapCat (OneBot) HTTP API 查询。
|
||
|
||
用法:
|
||
# 解析单个 QQ 号
|
||
python3 qq_resolve_name.py --uid YOUR_ADMIN_QQ
|
||
python3 qq_resolve_name.py --uid YOUR_ADMIN_QQ --json
|
||
|
||
# 解析单个群号
|
||
python3 qq_resolve_name.py --gid YOUR_GROUP_ID
|
||
python3 qq_resolve_name.py --gid YOUR_GROUP_ID --json
|
||
|
||
# 批量解析
|
||
python3 qq_resolve_name.py --uid YOUR_ADMIN_QQ --uid 12345678 --gid YOUR_GROUP_ID
|
||
python3 qq_resolve_name.py --uid YOUR_ADMIN_QQ --gid YOUR_GROUP_ID --json
|
||
|
||
# 从文件读取待解析的 ID(每行一个:uid:123456 或 gid:YOUR_GROUP_ID)
|
||
python3 qq_resolve_name.py --from-file /tmp/ids.txt
|
||
python3 qq_resolve_name.py --from-file /tmp/ids.txt --json
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
import argparse
|
||
import requests
|
||
|
||
CQHTTP_URL = "http://YOUR_NAPCAT_HOST:25570"
|
||
|
||
|
||
def get_stranger_info(user_id):
|
||
"""通过 OneBot API 获取陌生人/好友信息"""
|
||
url = f"{CQHTTP_URL}/get_stranger_info"
|
||
payload = {"user_id": int(user_id), "no_cache": True}
|
||
resp = requests.post(url, json=payload, timeout=10)
|
||
data = resp.json()
|
||
if data.get("status") == "ok":
|
||
return data.get("data", {})
|
||
return None
|
||
|
||
|
||
def get_group_info(group_id):
|
||
"""通过 OneBot API 获取群信息"""
|
||
url = f"{CQHTTP_URL}/get_group_info"
|
||
payload = {"group_id": int(group_id), "no_cache": True}
|
||
resp = requests.post(url, json=payload, timeout=10)
|
||
data = resp.json()
|
||
if data.get("status") == "ok":
|
||
return data.get("data", {})
|
||
return None
|
||
|
||
|
||
def format_user_info(info, uid):
|
||
"""格式化用户信息"""
|
||
if not info:
|
||
return f"• {uid} → 未找到(可能不是好友或不存在)"
|
||
uid = info.get("user_id", uid)
|
||
nickname = info.get("nickname", "?")
|
||
remark = info.get("remark", "")
|
||
level = info.get("level", 0)
|
||
sex = {"male": "男", "female": "女", "unknown": "未知"}.get(info.get("sex", ""), "?")
|
||
age = info.get("age", "?")
|
||
remark_str = f"(备注:{remark})" if remark else ""
|
||
return f"• {nickname} ({uid}){remark_str} [{sex}/{age}岁/等级{level}]"
|
||
|
||
|
||
def format_group_info(info, gid):
|
||
"""格式化群信息"""
|
||
if not info:
|
||
return f"• {gid} → 未找到(群不存在或 bot 未加入)"
|
||
gid = info.get("group_id", gid)
|
||
name = info.get("group_name", "?")
|
||
member_count = info.get("member_count", "?")
|
||
max_member = info.get("max_member_count", "?")
|
||
level = info.get("group_level", 0)
|
||
owner = info.get("owner_id", "?")
|
||
return f"• {name} ({gid}) — {member_count}/{max_member} 人 | 群主:{owner} | 等级:{level}"
|
||
|
||
|
||
def resolve_uids(uids):
|
||
"""批量解析多个 QQ 号"""
|
||
results = {"users": []}
|
||
for uid in uids:
|
||
info = get_stranger_info(uid)
|
||
results["users"].append({
|
||
"user_id": uid,
|
||
"nickname": info.get("nickname", "?") if info else None,
|
||
"remark": info.get("remark", "") if info else None,
|
||
"found": info is not None,
|
||
"raw": info
|
||
})
|
||
return results
|
||
|
||
|
||
def resolve_gids(gids):
|
||
"""批量解析多个群号"""
|
||
results = {"groups": []}
|
||
for gid in gids:
|
||
info = get_group_info(gid)
|
||
results["groups"].append({
|
||
"group_id": gid,
|
||
"group_name": info.get("group_name", "?") if info else None,
|
||
"found": info is not None,
|
||
"raw": info
|
||
})
|
||
return results
|
||
|
||
|
||
def parse_id_line(line):
|
||
"""解析行格式 uid:123456 或 gid:123456"""
|
||
line = line.strip()
|
||
if not line or line.startswith("#"):
|
||
return None
|
||
if ":" in line:
|
||
kind, val = line.split(":", 1)
|
||
return kind.strip().lower(), val.strip()
|
||
# 纯数字行:默认当作 uid
|
||
if line.isdigit():
|
||
return "uid", line
|
||
return None
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="QQ 号/群号 ↔ 用户名/群名 转换")
|
||
parser.add_argument("--uid", action="append", type=str, dest="uids",
|
||
help="待解析的 QQ 号(可多次使用)")
|
||
parser.add_argument("--gid", action="append", type=str, dest="gids",
|
||
help="待解析的群号(可多次使用)")
|
||
parser.add_argument("--from-file", type=str,
|
||
help="从文件读取 ID(每行 uid:123456 或 gid:123456)")
|
||
parser.add_argument("--json", action="store_true", help="输出 JSON 格式")
|
||
args = parser.parse_args()
|
||
|
||
uids = args.uids or []
|
||
gids = args.gids or []
|
||
|
||
if args.from_file:
|
||
with open(args.from_file, "r") as f:
|
||
for line in f:
|
||
parsed = parse_id_line(line)
|
||
if parsed:
|
||
kind, val = parsed
|
||
if kind in ("uid", "user", "qq"):
|
||
uids.append(val)
|
||
elif kind in ("gid", "group"):
|
||
gids.append(val)
|
||
|
||
if not uids and not gids:
|
||
print("❌ 请指定 --uid 或 --gid 或 --from-file")
|
||
sys.exit(1)
|
||
|
||
try:
|
||
results = {}
|
||
if uids:
|
||
results["users"] = []
|
||
for uid in uids:
|
||
info = get_stranger_info(uid)
|
||
results["users"].append({
|
||
"user_id": uid,
|
||
"info": info
|
||
})
|
||
|
||
if gids:
|
||
results["groups"] = []
|
||
for gid in gids:
|
||
info = get_group_info(gid)
|
||
results["groups"].append({
|
||
"group_id": gid,
|
||
"info": info
|
||
})
|
||
|
||
if args.json:
|
||
print(json.dumps(results, ensure_ascii=False, indent=2))
|
||
else:
|
||
lines = []
|
||
if "users" in results:
|
||
lines.append("📋 用户信息:")
|
||
for item in results["users"]:
|
||
lines.append(format_user_info(item["info"], item["user_id"]))
|
||
if "groups" in results:
|
||
if lines:
|
||
lines.append("")
|
||
lines.append("📋 群信息:")
|
||
for item in results["groups"]:
|
||
lines.append(format_group_info(item["info"], item["group_id"]))
|
||
print("\n".join(lines))
|
||
|
||
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()
|