🔴 致命(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 回复
104 lines
3.1 KiB
Python
104 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
从 QQ 接收文件/图片 - 通过 NapCat (OneBot) HTTP API
|
||
|
||
自动下载到 qqagent 工作区的 files/ 目录,供 agent 直接使用。
|
||
|
||
用法:
|
||
python3 qq_get_file.py --file <file_id>
|
||
→ 下载到 files/ 目录
|
||
→ 输出保存路径、文件名、大小、类型
|
||
|
||
python3 qq_get_file.py --file <file_id> --info --json
|
||
→ 只查文件信息(不下)
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
import os
|
||
import argparse
|
||
import requests
|
||
|
||
CQHTTP_URL = "http://YOUR_NAPCAT_HOST:25570"
|
||
FILES_DIR = "YOUR_WORKSPACE_PATH/files"
|
||
|
||
|
||
def get_file(file_id: str) -> dict:
|
||
resp = requests.post(
|
||
f"{CQHTTP_URL}/get_file",
|
||
json={"file_id": file_id},
|
||
timeout=15
|
||
)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
if data.get("status") != "ok":
|
||
raise Exception(f"API error: {data}")
|
||
return data["data"]
|
||
|
||
|
||
def save_file(file_info: dict) -> tuple[str, str]:
|
||
"""下载文件到 FILES_DIR,返回 (本地路径, 文件名)"""
|
||
os.makedirs(FILES_DIR, exist_ok=True)
|
||
filename = file_info.get("file_name", "unknown")
|
||
local_path = os.path.join(FILES_DIR, filename)
|
||
|
||
if file_info.get("base64"):
|
||
import base64
|
||
with open(local_path, "wb") as f:
|
||
f.write(base64.b64decode(file_info["base64"]))
|
||
else:
|
||
url = file_info.get("url", "")
|
||
if not url:
|
||
raise Exception("No base64 or url available")
|
||
resp = requests.get(url, timeout=30)
|
||
resp.raise_for_status()
|
||
with open(local_path, "wb") as f:
|
||
f.write(resp.content)
|
||
|
||
return local_path, filename
|
||
|
||
|
||
def detect_type(filename: str) -> str:
|
||
ext = filename.lower().rsplit(".", 1)[-1] if "." in filename else ""
|
||
image_exts = {"jpg", "jpeg", "png", "gif", "bmp", "webp"}
|
||
return "图片" if ext in image_exts else "文件"
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="从 QQ 接收文件")
|
||
parser.add_argument("--file", required=True, help="文件 ID(file_id)")
|
||
parser.add_argument("--output", help="保存目录(默认 files/)")
|
||
parser.add_argument("--info", action="store_true", help="只查文件信息,不下")
|
||
parser.add_argument("--json", action="store_true", help="JSON 格式输出")
|
||
args = parser.parse_args()
|
||
|
||
try:
|
||
file_info = get_file(args.file)
|
||
|
||
if args.info or args.json:
|
||
info = {
|
||
"file_id": args.file,
|
||
"name": file_info.get("file_name"),
|
||
"size": file_info.get("file_size"),
|
||
"has_base64": bool(file_info.get("base64")),
|
||
}
|
||
print(json.dumps(info, ensure_ascii=False, indent=2))
|
||
return
|
||
|
||
save_dir = args.output or FILES_DIR
|
||
local_path, filename = save_file(file_info)
|
||
ftype = detect_type(filename)
|
||
|
||
print(f"✅ 已保存到 {local_path}")
|
||
print(f" 文件名: {filename}")
|
||
print(f" 大小: {file_info.get('file_size')} bytes")
|
||
print(f" 类型: {ftype}")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 错误: {e}")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|