完整 AgentSkill:词库外部化 + 全部脚本/Skill 脱敏上传

- 高危词库从 process.py 抽到 config.toml [security] section
- process.py 改为 _get_high_risk_words() 动态加载(配置优先)
- 新增 15 个 QQ 操作脚本(已脱敏:QQ号/IP/Token → 占位符)
- 新增 8 个 SKILL.md(qq-messenger/management/resolver/napcat-extras 等)
- 新增 SKILL.md 入口(完整部署方案文档)
- 保留 SDK 框架(plugin_modules.py, file_store_api.py, package.py 等)
This commit is contained in:
Claw
2026-05-04 16:23:59 +08:00
parent d7d33c39a4
commit e1fbd31c64
35 changed files with 5058 additions and 276 deletions

View File

@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
群文件查询与下载 — 通过 NapCat (OneBot) HTTP API
支持操作:
- 列出群根目录文件 + 文件夹结构
- 按关键字搜索文件
- 查看文件夹内文件
- 下载文件到本地
用法:
python3 qq_get_group_files.py --gid YOUR_GROUP_ID # 列出群根目录文件
python3 qq_get_group_files.py --gid YOUR_GROUP_ID --folder <folder_id> # 查看文件夹内文件
python3 qq_get_group_files.py --gid YOUR_GROUP_ID --keyword 编译 # 搜索文件名
python3 qq_get_group_files.py --gid YOUR_GROUP_ID --download <file_id> # 下载指定文件
python3 qq_get_group_files.py --gid YOUR_GROUP_ID --all --download <dir> # 批量下载所有文件(未实现)
python3 qq_get_group_files.py --gid YOUR_GROUP_ID --json # JSON 输出
"""
import json
import sys
import os
import argparse
import requests
CQHTTP_URL = "http://YOUR_NAP CAT_HOST:25570"
FILES_DIR = "YOUR_WORKSPACE_PATH/files"
DEFAULT_GID = YOUR_GROUP_ID # Liquid Studio 群
def get_root_files(group_id: int) -> dict:
"""获取群根目录文件列表"""
resp = requests.post(f"{CQHTTP_URL}/get_group_root_files",
json={"group_id": group_id}, timeout=15)
data = resp.json()
if data.get("status") == "ok":
return data.get("data", {})
raise Exception(f"API 异常: {json.dumps(data, ensure_ascii=False)}")
def get_folder_files(group_id: int, folder_id: str) -> dict:
"""获取文件夹内文件列表"""
resp = requests.post(f"{CQHTTP_URL}/get_group_files_by_folder", json={
"group_id": group_id,
"folder_id": folder_id
}, timeout=15)
data = resp.json()
if data.get("status") == "ok":
return data.get("data", {})
raise Exception(f"API 异常: {json.dumps(data, ensure_ascii=False)}")
def download_file(group_id: int, file_id: str, filename: str) -> str:
"""通过 NapCat /get_group_file_url 获取下载链接并保存文件
"""
os.makedirs(FILES_DIR, exist_ok=True)
# 1. 获取下载 URL
resp = requests.post(f"{CQHTTP_URL}/get_group_file_url", json={
"group_id": group_id,
"file_id": file_id
}, timeout=15)
data = resp.json()
if data.get("status") != "ok":
raise Exception(f"获取下载链接失败: {data.get('wording', data.get('message', '未知错误'))}")
dl_url = data["data"]["url"]
if not dl_url:
raise Exception("获取下载链接为空")
# 2. 构造完整 URL(文件名加在 ?fname= 后面)
if "?fname=" in dl_url:
full_url = dl_url + filename
else:
full_url = dl_url
# 3. 下载文件
save_path = os.path.join(FILES_DIR, filename)
try:
dl = requests.get(full_url, timeout=60, allow_redirects=True)
if dl.status_code != 200:
raise Exception(f"下载失败 (HTTP {dl.status_code})")
with open(save_path, "wb") as f:
f.write(dl.content)
size = len(dl.content)
return f"✅ 已下载: {save_path} ({size / 1024:.1f} KB)"
except requests.exceptions.ConnectionError:
raise Exception("下载连接失败(文件服务器不可达)")
except requests.exceptions.ReadTimeout:
raise Exception("下载超时")
def format_size(size_bytes: int) -> str:
"""友好显示文件大小"""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.1f} KB"
else:
return f"{size_bytes / (1024 * 1024):.1f} MB"
def format_file_info(file: dict, indent: str = "") -> str:
"""格式化为可读文本"""
name = file.get("file_name", "?")
size = format_size(file.get("size", file.get("file_size", 0)))
uploader = file.get("uploader_name", "?")
downloads = file.get("download_times", 0)
fid = file.get("file_id", "?")
return (f"{indent}📄 {name}\n"
f"{indent} 大小: {size} | 上传者: {uploader} | 下载: {downloads} 次\n"
f"{indent} 文件ID: {fid}\n")
def format_folder_info(folder: dict, indent: str = "") -> str:
"""格式化为可读文本"""
name = folder.get("folder_name", "?")
fid = folder.get("folder_id", folder.get("folder", "?"))
creator = folder.get("creator_name", "?")
count = folder.get("total_file_count", "?")
return (f"{indent}📁 {name} ({count} 个文件)\n"
f"{indent} 文件夹ID: {fid}\n")
def main():
parser = argparse.ArgumentParser(description="群文件查询与下载")
parser.add_argument("--gid", type=int, default=DEFAULT_GID, help=f"群号 (默认: {DEFAULT_GID})")
parser.add_argument("--folder", type=str, help="查看指定文件夹内容(传入 folder_id)")
parser.add_argument("--keyword", type=str, help="搜索文件名(不区分大小写)")
parser.add_argument("--download", type=str, help="下载文件(传入 file_id)")
parser.add_argument("--name", type=str, help="下载时指定文件名(可选)")
parser.add_argument("--json", action="store_true", help="JSON 格式输出")
args = parser.parse_args()
try:
# 下载模式
if args.download:
file_id = args.download
filename = args.name or f"group_file_{file_id[:16]}"
if not args.json:
print(f"⏳ 正在下载文件...")
try:
result = download_file(args.gid, file_id, filename)
if args.json:
print(json.dumps({"file_id": file_id, "status": "ok", "path": result.split(": ")[-1] if ": " in result else result}))
else:
print(result)
except Exception as e:
if args.json:
print(json.dumps({"file_id": file_id, "status": "error", "error": str(e)}))
else:
print(f"❌ 下载失败: {e}")
sys.exit(1)
return
# 获取文件列表
if args.folder:
data = get_folder_files(args.gid, args.folder)
else:
data = get_root_files(args.gid)
files = data.get("files", [])
folders = data.get("folders", [])
if args.json:
output = {
"group_id": args.gid,
"files": files,
"folders": folders
}
# 搜索过滤
if args.keyword:
kw = args.keyword.lower()
output["files"] = [f for f in files if kw in f.get("file_name", "").lower()]
output["folders"] = [f for f in folders if kw in f.get("folder_name", "").lower()]
print(json.dumps(output, ensure_ascii=False, indent=2))
return
# 文本格式输出
# 搜索过滤
if args.keyword:
kw = args.keyword.lower()
matched_files = [f for f in files if kw in f.get("file_name", "").lower()]
matched_folders = [f for f in folders if kw in f.get("folder_name", "").lower()]
files = matched_files
folders = matched_folders
lines = [f"📁 群 {args.gid} 文件列表\n"]
if args.folder:
lines.append(f"(文件夹: {args.folder})\n")
if folders:
lines.append(f"📂 文件夹 ({len(folders)} 个)\n")
for fol in folders:
lines.append(format_folder_info(fol))
lines.append("")
if files:
lines.append(f"📄 文件 ({len(files)} 个)\n")
for f in files:
lines.append(format_file_info(f))
lines.append("")
if not folders and not files:
lines.append("暂无文件")
if args.keyword:
lines.append(f"(未找到包含「{args.keyword}」的文件)")
print("".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()