From f3abba0881784d0966174fdfa2c44be05c7b869f Mon Sep 17 00:00:00 2001 From: yangxuan Date: Thu, 9 Jul 2026 18:00:04 +0800 Subject: [PATCH] auto backup 2026-07-09 18:00 --- credentials/openclaw-weixin-pairing.json | 4 ++ identity/device-auth.json | 2 +- openclaw-weixin/accounts.json | 3 + .../accounts/d843ed5f6ccc-im-bot.json | 6 ++ .../accounts/d843ed5f6ccc-im-bot.sync.json | 1 + openclaw.json | 67 +++++++++++++------ plugin-skills/dingtalk-channel-rules | 1 + plugin-skills/dingtalk-troubleshoot | 1 + plugin-skills/dws-cli | 1 + skill-workshop/proposals.json | 2 +- .../proposal.json | 2 +- .../rollback.json | 1 + .../proposal.json | 2 +- .../rollback.json | 1 + .../proposal.json | 2 +- .../rollback.json | 1 + .../proposal.json | 2 +- .../rollback.json | 1 + 18 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 credentials/openclaw-weixin-pairing.json create mode 100644 openclaw-weixin/accounts.json create mode 100644 openclaw-weixin/accounts/d843ed5f6ccc-im-bot.json create mode 100644 openclaw-weixin/accounts/d843ed5f6ccc-im-bot.sync.json create mode 120000 plugin-skills/dingtalk-channel-rules create mode 120000 plugin-skills/dingtalk-troubleshoot create mode 120000 plugin-skills/dws-cli create mode 100644 skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/rollback.json create mode 100644 skill-workshop/proposals/dianping-review-20260611-d402abec10/rollback.json create mode 100644 skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/rollback.json create mode 100644 skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/rollback.json diff --git a/credentials/openclaw-weixin-pairing.json b/credentials/openclaw-weixin-pairing.json new file mode 100644 index 0000000..f122bcb --- /dev/null +++ b/credentials/openclaw-weixin-pairing.json @@ -0,0 +1,4 @@ +{ + "version": 1, + "requests": [] +} diff --git a/identity/device-auth.json b/identity/device-auth.json index 516d3a7..e9b0c1c 100644 --- a/identity/device-auth.json +++ b/identity/device-auth.json @@ -12,7 +12,7 @@ "operator.read", "operator.write" ], - "updatedAtMs": 1783587147922 + "updatedAtMs": 1783589780228 } } } diff --git a/openclaw-weixin/accounts.json b/openclaw-weixin/accounts.json new file mode 100644 index 0000000..b7de5a0 --- /dev/null +++ b/openclaw-weixin/accounts.json @@ -0,0 +1,3 @@ +[ + "d843ed5f6ccc-im-bot" +] \ No newline at end of file diff --git a/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.json b/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.json new file mode 100644 index 0000000..fa8b85f --- /dev/null +++ b/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.json @@ -0,0 +1,6 @@ +{ + "token": "d843ed5f6ccc@im.bot:0600009749b0216cbafb0dced2fc3f3fd977f3", + "savedAt": "2026-07-09T09:06:38.981Z", + "baseUrl": "https://ilinkai.weixin.qq.com", + "userId": "o9cq80w6ZnnPGItW-GGACkwd69bA@im.wechat" +} \ No newline at end of file diff --git a/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.sync.json b/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.sync.json new file mode 100644 index 0000000..7b16658 --- /dev/null +++ b/openclaw-weixin/accounts/d843ed5f6ccc-im-bot.sync.json @@ -0,0 +1 @@ +{"get_updates_buf":"CgkIABiDuYSx9DMSOmQ4NDNlZDVmNmNjY0BpbS5ib3Q6MDYwMDAwOTc0OWIwMjE2Y2JhZmIwZGNlZDJmYzNmM2ZkOTc3ZjM="} \ No newline at end of file diff --git a/openclaw.json b/openclaw.json index c9c3d8e..2bef9eb 100644 --- a/openclaw.json +++ b/openclaw.json @@ -1,7 +1,7 @@ { "meta": { "lastTouchedVersion": "2026.6.11", - "lastTouchedAt": "2026-07-09T08:52:21.805Z" + "lastTouchedAt": "2026-07-09T09:33:04.986Z" }, "wizard": { "lastRunAt": "2026-06-29T03:08:31.226Z", @@ -290,18 +290,18 @@ "list": [ { "id": "main", - "name": "主智能体", + "name": "助手", "model": { "primary": "deepseek/deepseek-v4-flash" }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "主智能体" + "name": "助手" } }, { "id": "storage", - "name": "仓库管理", + "name": "仓库", "workspace": "/home/yangxuan/.openclaw/workspace/storage", "agentDir": "/home/yangxuan/.openclaw/agents/storage/agent", "model": { @@ -309,12 +309,12 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "仓库管理" + "name": "仓库" } }, { "id": "backend", - "name": "后端开发", + "name": "后端", "workspace": "/home/yangxuan/.openclaw/workspace/backend", "agentDir": "/home/yangxuan/.openclaw/agents/backend/agent", "model": { @@ -322,7 +322,7 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "后端开发" + "name": "后端" }, "tools": { "alsoAllow": [ @@ -332,7 +332,7 @@ }, { "id": "frontend", - "name": "前端开发", + "name": "前端", "workspace": "/home/yangxuan/.openclaw/workspace/frontend", "agentDir": "/home/yangxuan/.openclaw/agents/frontend/agent", "model": { @@ -340,7 +340,7 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "前端开发" + "name": "前端" }, "tools": { "alsoAllow": [ @@ -350,7 +350,7 @@ }, { "id": "resume", - "name": "简历管理", + "name": "简历", "workspace": "/home/yangxuan/.openclaw/workspace/resume", "agentDir": "/home/yangxuan/.openclaw/agents/resume/agent", "model": { @@ -358,12 +358,12 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "简历管理" + "name": "简历" } }, { "id": "travel", - "name": "旅行规划", + "name": "旅行", "workspace": "/home/yangxuan/.openclaw/workspace/travel", "agentDir": "/home/yangxuan/.openclaw/agents/travel/agent", "model": { @@ -371,12 +371,12 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "旅行规划" + "name": "旅行" } }, { "id": "planner", - "name": "方案规划", + "name": "方案", "workspace": "/home/yangxuan/.openclaw/workspace/planner", "agentDir": "/home/yangxuan/.openclaw/agents/planner/agent", "model": { @@ -384,17 +384,19 @@ }, "identity": { "avatar": "./avatars/assistant.jpg", - "name": "方案规划" + "name": "方案" } } ] }, "tools": { "profile": "full", - "sessions":{"visibilty":"all"}, - "agentToAgent":{ - "enabled":true - }, + "sessions": { + "visibility": "all" + }, + "agentToAgent": { + "enabled": true + }, "deny": [ "group:web", "browser" @@ -612,7 +614,9 @@ "ezviz", "memory-core", "ollama", - "searxng" + "searxng", + "openclaw-weixin", + "dingtalk-connector" ], "entries": { "ollama": { @@ -635,6 +639,12 @@ }, "ezviz": { "enabled": false + }, + "openclaw-weixin": { + "enabled": true + }, + "dingtalk-connector": { + "enabled": true } }, "bundledDiscovery": "compat" @@ -644,7 +654,22 @@ "enabled": true, "appId": "92aa2d25a04247afacb76b1f75f9cf80", "appSecret": "38ac0b0468b0dca991ce1c8ed888200e" + }, + "openclaw-weixin": { + "channelConfigUpdatedAt": "2026-07-09T09:06:39.005Z" + }, + "dingtalk-connector": { + "clientId": "dingnumf057etbbowhfu", + "clientSecret": "m-g-EknD5uxH9DFnSpHPT87bLWOPrNYa-SMvT6kDOMqg_8nmPL7jiFxbdpWWqzIl" } }, - "bindings": [] + "bindings": [ + { + "agentId": "main", + "match": { + "channel": "openclaw-weixin", + "accountId": "d843ed5f6ccc-im-bot" + } + } + ] } diff --git a/plugin-skills/dingtalk-channel-rules b/plugin-skills/dingtalk-channel-rules new file mode 120000 index 0000000..3a3cc13 --- /dev/null +++ b/plugin-skills/dingtalk-channel-rules @@ -0,0 +1 @@ +/home/yangxuan/.openclaw/npm/projects/dingtalk-real-ai-dingtalk-connector-aa54111b45/node_modules/@dingtalk-real-ai/dingtalk-connector/skills/dingtalk-channel-rules \ No newline at end of file diff --git a/plugin-skills/dingtalk-troubleshoot b/plugin-skills/dingtalk-troubleshoot new file mode 120000 index 0000000..ac49b87 --- /dev/null +++ b/plugin-skills/dingtalk-troubleshoot @@ -0,0 +1 @@ +/home/yangxuan/.openclaw/npm/projects/dingtalk-real-ai-dingtalk-connector-aa54111b45/node_modules/@dingtalk-real-ai/dingtalk-connector/skills/dingtalk-troubleshoot \ No newline at end of file diff --git a/plugin-skills/dws-cli b/plugin-skills/dws-cli new file mode 120000 index 0000000..3e7c2e4 --- /dev/null +++ b/plugin-skills/dws-cli @@ -0,0 +1 @@ +/home/yangxuan/.openclaw/npm/projects/dingtalk-real-ai-dingtalk-connector-aa54111b45/node_modules/@dingtalk-real-ai/dingtalk-connector/skills/dws-cli \ No newline at end of file diff --git a/skill-workshop/proposals.json b/skill-workshop/proposals.json index 7a92b53..d98f9aa 100644 --- a/skill-workshop/proposals.json +++ b/skill-workshop/proposals.json @@ -1 +1 @@ -{"schema":"openclaw.skill-workshop.proposals-manifest.v1","updatedAt":"2026-07-07T12:14:43.092Z","proposals":[{"id":"jav-media-sorter-20260707-b1f90c8a4d","kind":"create","status":"pending","title":"Create jav-media-sorter","description":"JAV 视频整理:组装文件、按演员分组、刮削 nfo 元数据与封面图","skillName":"jav-media-sorter","skillKey":"jav-media-sorter","createdAt":"2026-07-07T12:14:43.074Z","updatedAt":"2026-07-07T12:14:43.074Z","scanState":"clean"},{"id":"f-download-sorter-20260706-902cc0d5aa","kind":"create","status":"applied","title":"Create f-download-sorter","description":"整理 /mnt/f 下下载的视频文件:扫描→清垃圾→按规则归档到7个大类目录","skillName":"f-download-sorter","skillKey":"f-download-sorter","createdAt":"2026-07-06T09:03:54.941Z","updatedAt":"2026-07-06T09:04:18.513Z","scanState":"clean"},{"id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"pending","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-01T23:52:24.406Z","scanState":"clean"},{"id":"weekly-report-g5-20260626-7595273f1d","kind":"create","status":"applied","title":"Create weekly-report-g5","description":"维云智造G5周报生成:整理每日工作→多段邮件(纯文本+HTML含签名图)→存入钉邮草稿箱","skillName":"weekly-report-g5","skillKey":"weekly-report-g5","createdAt":"2026-06-26T00:46:22.478Z","updatedAt":"2026-06-26T00:46:35.812Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"pending","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-06-24T03:23:27.062Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-cdbeb219a3","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:03:28.166Z","updatedAt":"2026-06-24T03:03:30.915Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-a441798af9","kind":"create","status":"applied","title":"Create calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T02:29:02.278Z","updatedAt":"2026-06-24T02:29:27.227Z","scanState":"clean"},{"id":"calibre-title-cleaner-20260624-3760b64e8b","kind":"create","status":"applied","title":"Create calibre-title-cleaner","description":"清洗 Calibre 书库中损坏的 txt 文档书名(HTML标签/长文本/乱码/路径超限修复)","skillName":"calibre-title-cleaner","skillKey":"calibre-title-cleaner","createdAt":"2026-06-24T01:10:56.296Z","updatedAt":"2026-06-24T01:13:39.670Z","scanState":"clean"},{"id":"dianping-review-20260611-d402abec10","kind":"create","status":"pending","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","skillName":"dianping-review","skillKey":"dianping-review","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-06-11T23:50:15.731Z","scanState":"clean"}]} +{"schema":"openclaw.skill-workshop.proposals-manifest.v1","updatedAt":"2026-07-09T09:56:20.877Z","proposals":[{"id":"dianping-review-20260611-d402abec10","kind":"create","status":"applied","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","skillName":"dianping-review","skillKey":"dianping-review","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-07-09T09:56:20.865Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-07-09T09:56:20.404Z","scanState":"clean"},{"id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"applied","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-09T09:56:19.928Z","scanState":"clean"},{"id":"jav-media-sorter-20260707-b1f90c8a4d","kind":"create","status":"applied","title":"Create jav-media-sorter","description":"JAV 视频整理:组装文件、按演员分组、刮削 nfo 元数据与封面图","skillName":"jav-media-sorter","skillKey":"jav-media-sorter","createdAt":"2026-07-07T12:14:43.074Z","updatedAt":"2026-07-09T09:56:18.413Z","scanState":"clean"},{"id":"f-download-sorter-20260706-902cc0d5aa","kind":"create","status":"applied","title":"Create f-download-sorter","description":"整理 /mnt/f 下下载的视频文件:扫描→清垃圾→按规则归档到7个大类目录","skillName":"f-download-sorter","skillKey":"f-download-sorter","createdAt":"2026-07-06T09:03:54.941Z","updatedAt":"2026-07-06T09:04:18.513Z","scanState":"clean"},{"id":"weekly-report-g5-20260626-7595273f1d","kind":"create","status":"applied","title":"Create weekly-report-g5","description":"维云智造G5周报生成:整理每日工作→多段邮件(纯文本+HTML含签名图)→存入钉邮草稿箱","skillName":"weekly-report-g5","skillKey":"weekly-report-g5","createdAt":"2026-06-26T00:46:22.478Z","updatedAt":"2026-06-26T00:46:35.812Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-cdbeb219a3","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:03:28.166Z","updatedAt":"2026-06-24T03:03:30.915Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-a441798af9","kind":"create","status":"applied","title":"Create calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T02:29:02.278Z","updatedAt":"2026-06-24T02:29:27.227Z","scanState":"clean"},{"id":"calibre-title-cleaner-20260624-3760b64e8b","kind":"create","status":"applied","title":"Create calibre-title-cleaner","description":"清洗 Calibre 书库中损坏的 txt 文档书名(HTML标签/长文本/乱码/路径超限修复)","skillName":"calibre-title-cleaner","skillKey":"calibre-title-cleaner","createdAt":"2026-06-24T01:10:56.296Z","updatedAt":"2026-06-24T01:13:39.670Z","scanState":"clean"}]} diff --git a/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/proposal.json b/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/proposal.json index 70f5427..d55ac9f 100644 --- a/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/proposal.json +++ b/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/proposal.json @@ -1 +1 @@ -{"schema":"openclaw.skill-workshop.proposal.v1","id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"pending","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-06-24T03:23:27.062Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"62debb6e-62f3-41ff-ab3a-0d2fdc63ff4d","messageId":"62debb6e-62f3-41ff-ab3a-0d2fdc63ff4d"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"9f8e90cfa549288742db7af62bc54db12a9650208104a2efcba6c7a4fb2b4a49","target":{"skillName":"calibre-cleaner","skillKey":"calibre-cleaner","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/calibre-cleaner","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/calibre-cleaner/SKILL.md","source":"openclaw-workspace","currentContentHash":"929fdbb7a4de265565ca75bdf3692d33623a634f4991c21a51e5f829b0e9b2c0"},"scan":{"state":"clean","scannedAt":"2026-06-24T03:23:27.063Z","critical":0,"warn":0,"info":0,"findings":[]}} +{"schema":"openclaw.skill-workshop.proposal.v1","id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-07-09T09:56:20.404Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"62debb6e-62f3-41ff-ab3a-0d2fdc63ff4d","messageId":"62debb6e-62f3-41ff-ab3a-0d2fdc63ff4d"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"9f8e90cfa549288742db7af62bc54db12a9650208104a2efcba6c7a4fb2b4a49","target":{"skillName":"calibre-cleaner","skillKey":"calibre-cleaner","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/calibre-cleaner","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/calibre-cleaner/SKILL.md","source":"openclaw-workspace","currentContentHash":"929fdbb7a4de265565ca75bdf3692d33623a634f4991c21a51e5f829b0e9b2c0"},"scan":{"state":"clean","scannedAt":"2026-07-09T09:56:20.395Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-07-09T09:56:20.404Z"} diff --git a/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/rollback.json b/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/rollback.json new file mode 100644 index 0000000..acec122 --- /dev/null +++ b/skill-workshop/proposals/calibre-cleaner-20260624-0fc374efd7/rollback.json @@ -0,0 +1 @@ +{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"calibre-cleaner-20260624-0fc374efd7","writtenAt":"2026-07-09T09:56:20.398Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/calibre-cleaner/SKILL.md","action":"update","previousContent":"---\nname: \"calibre-cleaner\"\ndescription: \"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除\"\n---\n\n# Calibre 书库清洗 Skill\n\n## 概述\n\nCalibre 书库整理三件套:\n\n1. **书名清洗** —— 修复从 txt 导入产生的 HTML 标签/长文本/乱码书名\n2. **作者提取** —— 从书名中自动提取作者名(匹配 `作者:xxx`、`-xxx`、`BY xxx` 等格式)\n3. **脏数据清理** —— 检测并删除内容损坏的 txt(中文 < 10%)以及小于 10KB 的垃圾文件\n\n## 前置条件\n\n- 书库路径:`/home/yangxuan/calibre/books`\n- 元数据文件:`/home/yangxuan/calibre/books/metadata.db`\n- 运行前须停止其他 Calibre 进程(桌面版、Docker Calibre-Web)\n- 运行前 **必须备份** `metadata.db`\n\n---\n\n## 一、书名清洗 & 作者提取\n\n### `shorten(s, max_bytes=120)`\n\n将字符串截断到指定字节数,避免因 UTF-8 中文字符跨字节而损坏。\n\n```python\ndef shorten(s, max_bytes=120):\n encoded = s.encode('utf-8')\n if len(encoded) <= max_bytes:\n return s\n try:\n return encoded[:max_bytes].decode('utf-8')\n except:\n return encoded[:max_bytes-3].decode('utf-8', errors='ignore')\n```\n\n### `clean_title(title)`\n\n```python\ndef clean_title(title):\n t = title\n # 移除 HTML 标签\n t = re.sub(r'<[^>]+>', '', t)\n # 替换 _BR_ / _BR 标记\n t = re.sub(r'_BR_', ' ', t, flags=re.IGNORECASE)\n t = re.sub(r'_BR(?![a-zA-Z])', ' ', t)\n # 移除 _fontxxx_ / _imgxxx_ 标记\n t = re.sub(r'_font[^_]*_', '', t)\n t = re.sub(r'_img[^_]*_', '', t)\n # 合并多余空格\n t = re.sub(r'\\s+', ' ', t).strip()\n # 超长截短\n if len(t) > 100:\n m = re.search(r'[\\u4e00-\\u9fff]{2,}', t)\n if m:\n t = t[m.start():]\n t = t[:60].rstrip(',。;:、!?…\\ufffd\\'\"').rstrip('.,;:!?\\'').strip()\n # 移除控制字符\n t = re.sub(r'[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]', '', t)\n return shorten(t, 100).strip() or 'Unknown'\n```\n\n### 作者提取规则(从书名中提取)\n\n格式 | 示例 | 正则\n---|---|---\n`作者:xxx`(中文冒号) | `...作者:镜欲` | `作者[::]\\s*([^\\s,,、\\))]+)`\n`作者:xxx`(英文冒号) | `...作者:镜欲` | 同上\n`-xxx`(英文 dash 结尾) | `善良的美艳妈妈-糠麸` | `-([^-()【】\\[\\]]+)$`\n`翻译:xxx` | `...翻译:kkmanlg` | `翻译[::]\\s*([^\\s,,、\\))]+)`\n`BY xxx`(英文 BY) | `...BY狱厨` | `\\b[Bb][Yy]\\s+([^\\s,,、\\))]+)`\n`&xxx`(and 符结尾) | `...&axenic` | `&([^&()【】\\[\\] ]+)$`\n\n提取策略:先匹配 `作者:` → `-xxx` → `翻译:` → `BY` → `&xxx`。排除项:`正文`、`内容简介`、`第\\\\d+[章节]`。\n\n```python\nEXCLUDE_AUTHORS = {'正文', '内容简介', ''}\n\ndef extract_author_from_title(title):\n m = re.search(r'作者[::]\\s*([^\\s,,、\\))《\\[]+)', title)\n if m:\n name = m.group(1).strip()\n if name not in EXCLUDE_AUTHORS and not re.match(r'^第\\d+[章节]', name):\n return name\n m = re.search(r'翻译[::]\\s*([^\\s,,、\\))《\\[]+)', title)\n if m:\n return m.group(1).strip()\n m = re.search(r'\\b[Bb][Yy]\\s+([^\\s,,、\\))《\\[]+)', title)\n if m:\n name = m.group(1).strip()\n if name not in EXCLUDE_AUTHORS:\n return name\n m = re.search(r'-([^-()【】\\[\\]{}《》\\s]{2,30})$', title)\n if m:\n name = m.group(1).strip()\n if name not in EXCLUDE_AUTHORS and not re.match(r'^\\d+', name):\n return name\n m = re.search(r'&([^&()【】\\[\\]\\s]{2,30})$', title)\n if m:\n name = m.group(1).strip()\n if name not in EXCLUDE_AUTHORS:\n return name\n return None\n```\n\n### 坏书检测条件\n\n```python\nis_bad = (\n len(title) > 100 or\n ' 150\n)\n```\n\n---\n\n## 二、脏数据清理\n\n### 2.1 检测条件\n\n扫描书库中所有 TXT 文件,读取前 20KB 检测中文比例:\n\n- **内容损坏/二进制垃圾**:中文比例 < 10%(标记为待删除)\n- **小文件垃圾**:`uncompressed_size < 10240`(10KB 以下)\n\n```python\nwith open(full_path, 'rb') as f:\n raw = f.read(20480)\ntext = raw.decode('utf-8', errors='replace')\nchinese = sum(1 for c in text if '\\u4e00' <= c <= '\\u9fff')\nratio = chinese / len(text) if len(text) > 0 else 0\n\nif ratio < 0.10: # 内容损坏,删除\nif size < 10240: # 小文件垃圾,删除\n```\n\n### 2.2 批量删除(通过 SQLite 直写)\n\n停掉 calibre 进程后,通过 SQLite 删除记录并清理文件系统:\n\n```sql\nDELETE FROM data WHERE book = ?\nDELETE FROM books_authors_link WHERE book = ?\nDELETE FROM books_languages_link WHERE book = ?\nDELETE FROM books_publishers_link WHERE book = ?\nDELETE FROM books_series_link WHERE book = ?\nDELETE FROM books_tags_link WHERE book = ?\nDELETE FROM comments WHERE book = ?\nDELETE FROM identifiers WHERE book = ?\nDELETE FROM books WHERE id = ?\n```\n\n同时删除对应目录:`shutil.rmtree(os.path.join('/home/yangxuan/calibre/books', bpath))`\n\n### 2.3 完整删除脚本\n\n```python\nimport sqlite3, os, shutil\n\ndb_path = '/home/yangxuan/calibre/books/metadata.db'\nconn = sqlite3.connect(db_path)\nc = conn.cursor()\n\nc.execute('''\n SELECT b.id, b.title, b.path, d.name, d.uncompressed_size\n FROM books b \n JOIN data d ON d.book = b.id \n WHERE d.format = 'TXT'\n''')\nrows = c.fetchall()\n\nto_delete = []\n\nfor bid, title, bpath, fname, size in rows:\n full_path = os.path.join('/home/yangxuan/calibre/books', bpath, fname + '.txt')\n if not os.path.exists(full_path):\n continue\n \n # 条件1:小文件垃圾\n if size < 10240:\n to_delete.append((bid, bpath))\n continue\n \n # 条件2:内容损坏(中文 < 10%)\n with open(full_path, 'rb') as f:\n raw = f.read(20480)\n text = raw.decode('utf-8', errors='replace')\n chinese = sum(1 for c in text if '\\u4e00' <= c <= '\\u9fff')\n ratio = chinese / len(text) if len(text) > 0 else 0\n if ratio < 0.10:\n to_delete.append((bid, bpath))\n\nprint(f\"待删除: {len(to_delete)}\", flush=True)\nfor bid, bpath in to_delete:\n c.execute('DELETE FROM data WHERE book=?', (bid,))\n c.execute('DELETE FROM books_authors_link WHERE book=?', (bid,))\n c.execute('DELETE FROM books_languages_link WHERE book=?', (bid,))\n c.execute('DELETE FROM books_publishers_link WHERE book=?', (bid,))\n c.execute('DELETE FROM books_series_link WHERE book=?', (bid,))\n c.execute('DELETE FROM books_tags_link WHERE book=?', (bid,))\n c.execute('DELETE FROM comments WHERE book=?', (bid,))\n c.execute('DELETE FROM identifiers WHERE book=?', (bid,))\n c.execute('DELETE FROM books WHERE id=?', (bid,))\n full_dir = os.path.join('/home/yangxuan/calibre/books', bpath)\n if os.path.exists(full_dir):\n shutil.rmtree(full_dir)\n\nconn.commit()\nconn.close()\nprint(f\"完成,删除: {len(to_delete)}\", flush=True)\n```\n\n---\n\n## 三、常用操作\n\n### 3.1 查看 XX 作者的书\n\n```bash\nsqlite3 /home/yangxuan/calibre/books/metadata.db \"\nSELECT b.id, b.title\nFROM books b JOIN books_authors_link l ON b.id = l.book\nJOIN authors a ON l.author = a.id\nWHERE a.name = 'Unknown'\nLIMIT 20;\n\"\n```\n\n### 3.2 统计作者分布\n\n```bash\nsqlite3 /home/yangxuan/calibre/books/metadata.db \"\nSELECT a.name, COUNT(*) as cnt\nFROM books b JOIN books_authors_link l ON b.id = l.book\nJOIN authors a ON l.author = a.id\nGROUP BY a.name ORDER BY cnt DESC LIMIT 20;\n\"\n```\n\n### 3.3 书库统计\n\n```bash\npython3 -c \"\nimport sqlite3\nconn = sqlite3.connect('/home/yangxuan/calibre/books/metadata.db')\nc = conn.cursor()\nc.execute('SELECT COUNT(*) FROM books')\nprint(f'总书籍: {c.fetchone()[0]}')\nc.execute('SELECT COUNT(*) FROM data WHERE format=\\\"TXT\\\"')\nprint(f'TXT 文件: {c.fetchone()[0]}')\nconn.close()\n\"\n```\n\n---\n\n## 四、操作流程(推荐顺序)\n\n```bash\ncd /home/yangxuan/calibre\n\n# 1. 停服务 + 备份\ndocker compose down\ncp books/metadata.db books/metadata.db.backup.$(date +%Y%m%d)\nkillall calibre 2>/dev/null\n\n# 2. 脏数据清理(内容损坏 + 小文件)\npython3 /path/to/clean_dirty_data.py\n\n# 3. 书名清洗 & 作者提取\ncalibre-debug /path/to/fix_titles.py\n\n# 4. 重启\ndocker compose up -d\n```\n\n---\n\n## 五、已知问题\n\n### `set_metadata` 报 `Errno 36 文件名过长`\n\n当 `author_sort` 字段超 80 字节时,Calibre 路径会触及 ext4 255 字节限制。先清理极长作者名:\n\n```bash\nsqlite3 /home/yangxuan/calibre/books/metadata.db \"\nUPDATE authors SET name='Unknown' WHERE LENGTH(name) > 30;\n\"\n```\n\n### WSL `/mnt/f` 删除 NTFS 文件权限不够\n\nWSL 的 drvfs 默认不带 metadata 选项,`sudo rm`、`os.remove`、`cmd.exe /c del` 均无法删除 NTFS 文件。不要在 WSL 下尝试跨分区删除操作,直接让 Windows 侧(文件资源管理器/PowerShell)操作。\n","previousContentHash":"929fdbb7a4de265565ca75bdf3692d33623a634f4991c21a51e5f829b0e9b2c0"} diff --git a/skill-workshop/proposals/dianping-review-20260611-d402abec10/proposal.json b/skill-workshop/proposals/dianping-review-20260611-d402abec10/proposal.json index abaeff7..778fa4e 100644 --- a/skill-workshop/proposals/dianping-review-20260611-d402abec10/proposal.json +++ b/skill-workshop/proposals/dianping-review-20260611-d402abec10/proposal.json @@ -1 +1 @@ -{"schema":"openclaw.skill-workshop.proposal.v1","id":"dianping-review-20260611-d402abec10","kind":"create","status":"pending","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-06-11T23:50:15.731Z","createdBy":"skill-workshop","origin":{"agentId":"travel","sessionKey":"agent:travel:openclaw-weixin:direct:o9cq80w6znnpgitw-ggackwd69ba@im.wechat","runId":"877b3a72-a43d-4105-bc96-68d122db4362","messageId":"openclaw-weixin:1781221807078-aeb5f501"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"e1b2e0249d29f948d5f7c08421dc563c8ffa8da5eef28d7ee1d7df5cc0315cbf","target":{"skillName":"dianping-review","skillKey":"dianping-review","skillDir":"/home/yangxuan/.openclaw/workspace/travel/skills/dianping-review","skillFile":"/home/yangxuan/.openclaw/workspace/travel/skills/dianping-review/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-06-11T23:50:15.731Z","critical":0,"warn":0,"info":0,"findings":[]}} +{"schema":"openclaw.skill-workshop.proposal.v1","id":"dianping-review-20260611-d402abec10","kind":"create","status":"applied","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-07-09T09:56:20.865Z","createdBy":"skill-workshop","origin":{"agentId":"travel","sessionKey":"agent:travel:openclaw-weixin:direct:o9cq80w6znnpgitw-ggackwd69ba@im.wechat","runId":"877b3a72-a43d-4105-bc96-68d122db4362","messageId":"openclaw-weixin:1781221807078-aeb5f501"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"e1b2e0249d29f948d5f7c08421dc563c8ffa8da5eef28d7ee1d7df5cc0315cbf","target":{"skillName":"dianping-review","skillKey":"dianping-review","skillDir":"/home/yangxuan/.openclaw/workspace/travel/skills/dianping-review","skillFile":"/home/yangxuan/.openclaw/workspace/travel/skills/dianping-review/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-09T09:56:20.858Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-07-09T09:56:20.865Z"} diff --git a/skill-workshop/proposals/dianping-review-20260611-d402abec10/rollback.json b/skill-workshop/proposals/dianping-review-20260611-d402abec10/rollback.json new file mode 100644 index 0000000..dabb76f --- /dev/null +++ b/skill-workshop/proposals/dianping-review-20260611-d402abec10/rollback.json @@ -0,0 +1 @@ +{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"dianping-review-20260611-d402abec10","writtenAt":"2026-07-09T09:56:20.859Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace/travel/skills/dianping-review/SKILL.md","action":"create"} diff --git a/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/proposal.json b/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/proposal.json index a152736..5c7723b 100644 --- a/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/proposal.json +++ b/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/proposal.json @@ -1 +1 @@ -{"schema":"openclaw.skill-workshop.proposal.v1","id":"jav-media-sorter-20260707-b1f90c8a4d","kind":"create","status":"pending","title":"Create jav-media-sorter","description":"JAV 视频整理:组装文件、按演员分组、刮削 nfo 元数据与封面图","createdAt":"2026-07-07T12:14:43.074Z","updatedAt":"2026-07-07T12:14:43.074Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"6bfe1579-7e3c-4bdc-8435-9ee0822885bc","messageId":"6bfe1579-7e3c-4bdc-8435-9ee0822885bc"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"aa0d2afcf8a91294ba24b1df0b7326254b82701baa5ad1c1770f7e19de52834a","target":{"skillName":"jav-media-sorter","skillKey":"jav-media-sorter","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/jav-media-sorter","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jav-media-sorter/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-07T12:14:43.075Z","critical":0,"warn":0,"info":0,"findings":[]}} +{"schema":"openclaw.skill-workshop.proposal.v1","id":"jav-media-sorter-20260707-b1f90c8a4d","kind":"create","status":"applied","title":"Create jav-media-sorter","description":"JAV 视频整理:组装文件、按演员分组、刮削 nfo 元数据与封面图","createdAt":"2026-07-07T12:14:43.074Z","updatedAt":"2026-07-09T09:56:18.413Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"6bfe1579-7e3c-4bdc-8435-9ee0822885bc","messageId":"6bfe1579-7e3c-4bdc-8435-9ee0822885bc"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"aa0d2afcf8a91294ba24b1df0b7326254b82701baa5ad1c1770f7e19de52834a","target":{"skillName":"jav-media-sorter","skillKey":"jav-media-sorter","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/jav-media-sorter","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jav-media-sorter/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-09T09:56:18.403Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-07-09T09:56:18.413Z"} diff --git a/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/rollback.json b/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/rollback.json new file mode 100644 index 0000000..a1a0bb2 --- /dev/null +++ b/skill-workshop/proposals/jav-media-sorter-20260707-b1f90c8a4d/rollback.json @@ -0,0 +1 @@ +{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"jav-media-sorter-20260707-b1f90c8a4d","writtenAt":"2026-07-09T09:56:18.406Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jav-media-sorter/SKILL.md","action":"create"} diff --git a/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/proposal.json b/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/proposal.json index 8bb7e30..f6adc26 100644 --- a/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/proposal.json +++ b/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/proposal.json @@ -1 +1 @@ -{"schema":"openclaw.skill-workshop.proposal.v1","id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"pending","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-01T23:52:24.406Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"1b45e119-4276-4a7c-8a20-e52d1679a304","messageId":"1b45e119-4276-4a7c-8a20-e52d1679a304"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"fa9a54a33aecdbb0a96e175f7acf1e923606cbf90114460da48548e127710a48","target":{"skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/jellyfin-nfo-builder","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jellyfin-nfo-builder/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-01T23:52:24.407Z","critical":0,"warn":0,"info":0,"findings":[]}} +{"schema":"openclaw.skill-workshop.proposal.v1","id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"applied","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-09T09:56:19.928Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"1b45e119-4276-4a7c-8a20-e52d1679a304","messageId":"1b45e119-4276-4a7c-8a20-e52d1679a304"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"fa9a54a33aecdbb0a96e175f7acf1e923606cbf90114460da48548e127710a48","target":{"skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/jellyfin-nfo-builder","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jellyfin-nfo-builder/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-09T09:56:19.920Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-07-09T09:56:19.928Z"} diff --git a/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/rollback.json b/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/rollback.json new file mode 100644 index 0000000..23d98cb --- /dev/null +++ b/skill-workshop/proposals/jellyfin-nfo-builder-20260701-6434352e0d/rollback.json @@ -0,0 +1 @@ +{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"jellyfin-nfo-builder-20260701-6434352e0d","writtenAt":"2026-07-09T09:56:19.921Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/jellyfin-nfo-builder/SKILL.md","action":"create"}