Files
chatrebot_aireply_plug/scripts/qq_send_file.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

177 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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 主动发文件脚本 - 后台发送
原理:
1. 立即返回fire-and-forget不阻塞 agent
2. 后台独立进程:复制 → 删除旧文件 → 通过 NapCat API 发文件
3. 文件发送即完成,不再额外通知 agent
用法:
python3 qq_send_file.py --private YOUR_ADMIN_QQ /path/to/file
python3 qq_send_file.py --group YOUR_GROUP_ID --name "报告.txt" /tmp/report.txt
python3 qq_send_file.py --group YOUR_GROUP_ID --image /tmp/screenshot.png
"""
import shutil
import os
import sys
import time
import json
import argparse
import subprocess
import requests
TARGET_DIR = "YOUR_SHARED_DIR"
CQHTTP_URL = "http://YOUR_NAPCAT_HOST:25570"
HISTORY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".send_history.json")
SCRIPT_PATH = os.path.abspath(__file__)
GATEWAY_URL = "http://127.0.0.1:18789"
GATEWAY_TOKEN = "YOUR_GATEWAY_TOKEN"
def log(msg):
sys.stderr.write(f"[qq_send_file] {msg}\n")
def load_history():
try:
if os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
log(f"读取历史失败: {e}")
return {"files": []}
def save_history(history):
try:
with open(HISTORY_FILE, "w", encoding="utf-8") as f:
json.dump(history, f, ensure_ascii=False, indent=2)
except Exception as e:
log(f"保存历史失败: {e}")
def cleanup_old_files(history):
for old_file in history.get("files", []):
if os.path.exists(old_file):
try:
os.remove(old_file)
log(f"已删除旧文件: {old_file}")
except Exception as e:
log(f"删除旧文件失败: {old_file} - {e}")
def _fmt_size(size):
for unit in ["B", "KB", "MB", "GB"]:
if size < 1024:
return f"{size:.1f}{unit}"
size /= 1024
return f"{size:.1f}TB"
def notify_agent_via_gateway(api, target_id, display_name, src_size, ok):
"""已禁用。文件发送完成后不再通知 agent减少 Gateway 负载。"""
pass
def background_work(api, target_id, src_path, display_name, is_image):
"""后台进程执行的完整工作流程"""
try:
if not os.path.exists(src_path):
notify_agent_via_gateway(api, target_id, display_name, 0, False)
return 1
src_size = os.path.getsize(src_path)
# 1. 清理旧文件
history = load_history()
cleanup_old_files(history)
# 2. 复制到共享目录
ts = int(time.time())
basename = display_name or os.path.basename(src_path)
target_filename = f"{ts}_{basename}"
target_path = os.path.join(TARGET_DIR, target_filename)
shutil.copy2(src_path, target_path)
log(f"已复制: {target_path} ({src_size} bytes)")
# 3. 发送文件
file_uri = f"file:///app/napcat/share/{target_filename}"
if is_image:
msg = f"[CQ:image,file={file_uri}]"
else:
msg = f"[CQ:file,file={file_uri},title={display_name or basename}]"
if api == "group":
url = f"{CQHTTP_URL}/send_group_msg"
payload = {"group_id": int(target_id), "message": msg}
else:
url = f"{CQHTTP_URL}/send_private_msg"
payload = {"user_id": int(target_id), "message": msg}
resp = requests.post(url, json=payload, timeout=120)
data = resp.json()
if data.get("status") == "ok":
msg_id = data.get("data", {}).get("message_id", "unknown")
log(f"发送成功, message_id={msg_id}")
save_history({"files": [target_path]})
notify_agent_via_gateway(api, target_id, display_name, src_size, True)
else:
log(f"发送失败: {json.dumps(data, ensure_ascii=False)}")
notify_agent_via_gateway(api, target_id, display_name, src_size, False)
except Exception as e:
log(f"后台工作异常: {e}")
try:
notify_agent_via_gateway(api, target_id, display_name, 0, False)
except:
pass
return 0
def main():
parser = argparse.ArgumentParser(description="发送 QQ 文件(后台发送)")
target = parser.add_mutually_exclusive_group(required=True)
target.add_argument("--group", type=str, help="目标群号")
target.add_argument("--private", type=str, help="目标用户 QQ 号")
parser.add_argument("--name", type=str, help="显示的文件名")
parser.add_argument("--image", action="store_true", help="作为图片发送")
parser.add_argument("file_path", help="本地文件路径")
args = parser.parse_args()
if not os.path.exists(args.file_path):
print(f"❌ 文件不存在: {args.file_path}")
sys.exit(1)
api = "group" if args.group else "private"
target_id = args.group or args.private
display_name = args.name or os.path.basename(args.file_path)
subprocess.Popen(
[sys.executable, SCRIPT_PATH, "--bgworker",
api, target_id, args.file_path, display_name,
str(int(args.image))],
start_new_session=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL,
)
print("✅ 已触发后台发送")
if __name__ == "__main__":
if "--bgworker" in sys.argv:
_, api, target_id, file_path, display_name, is_image_str = sys.argv[1:]
is_image = is_image_str == "1"
rc = background_work(api, target_id, file_path, display_name, is_image)
sys.exit(rc)
else:
main()