From d0b44f5a8c5568879f0ddabf147677a9b2e33626 Mon Sep 17 00:00:00 2001 From: --git_token Date: Sun, 10 May 2026 08:10:01 +0800 Subject: [PATCH] auto backup 2026-05-10 08:10:01 --- .vala_skill_hashes | 2 +- memory/2026-05-09.md | 96 +++++++++++++ .../sync_feishu_feedback.cpython-312.pyc | Bin 57876 -> 61730 bytes .../scripts/sync_feishu_feedback.py | 135 ++++++++++++++---- 4 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 memory/2026-05-09.md diff --git a/.vala_skill_hashes b/.vala_skill_hashes index 63cbca4..ab8e852 100644 --- a/.vala_skill_hashes +++ b/.vala_skill_hashes @@ -14,4 +14,4 @@ user-feedback-collector c0320451bf7ea0ce3d8ceaa603ae0a7b55c373c048363a5142258a4c user-feedback-data-source a95eb9142f3019fd193c46f89147dc7e0bf01dfe250202565a86f8bc52f37b13 feishu-group-msg-sync 085f95a5b89fec3b6a627da25d66ffeeb0be430098387739a64f7903f0ee88d4 user-feedback-processor 61783a8e9f03a973c187b359a87749ad1993dc71f8364b0a853d8b3ff64c75e8 -feishu-feedback-sync 878cad61ebd9d480eac6d1146a556031f02580c75841d4173f58afb076af7c34 +feishu-feedback-sync 7bd0ccd7112156e8fcfca7512b669f1c956cc085c6de9530e02838fa27d583cc diff --git a/memory/2026-05-09.md b/memory/2026-05-09.md new file mode 100644 index 0000000..c351c66 --- /dev/null +++ b/memory/2026-05-09.md @@ -0,0 +1,96 @@ +## 2026-05-09 工作日志 + +### 飞书反馈同步 - 三个文档问题修复 + +**问题一:图片无法点击查看** +- 根因:Markdown表格中 `|` 被 `dialogue_info` 内部的 ` | ` 分隔符破坏 + `![图片]()` 格式在飞书导入时不可靠 +- 修复:`info_parts` 分隔符从 `" | "` 改为 `
`;图片格式从 `![图片](url)` 改为 `📎 [图片](url)` 可点击链接 + +**问题二:子文档作者显示"小研"** +- 根因:现有子文档由 xiaoyan bot (`ou_3e97d43a66639a457f0020a0d7f2bd74`) 创建,xiaokui 无法直接覆盖 +- 修复:在 `update_summary_doc_as_children` 中添加 creator 校验,非 xiaokui 创建则先通过 xiaoyan 凭证删除再重建 +- 关键常量:`XIAOKUI_BOT_OPEN_ID = "ou_fdbf5fdafd91670db34b6ac887f30fb7"` +- 注意:xiaokui 无法删除 xiaoyan 创建的文档(跨应用权限隔离),需回退到 xiaoyan 凭证删除 + +**问题三:子文档排序不规则** +- 根因:旧 `sort_tag = dt.timestamp()` 升序导致旧日期在前 +- 修复:改为 `sort_tag = 9999999999 - int(dt.timestamp())` 实现日期降序 +- ⚠️ 不足:飞书 Wiki V2 API 创建节点时 `sort_tag` 参数可能被忽略(API 返回均为 null) +- 兜底方案:按日期由近到远的顺序依次创建子文档,利用 `node_create_time` 自然排序 +- 所有旧文档已删除并按正确顺序重建(5月8日→5月7日→4月28日),5月6日需手动创建 + +### 飞书分发消息 `` 标签修复 +- 根因:`dispatch_summary_to_chat` 中两步打架——第一步 `re.sub` 注入 HTML `` 文本,第二步 `content_parts` 用正确 `{"tag":"at"}` 格式插入 +- 修复:删除 `re.sub` 注入原始 HTML 标签的代码,仅保留富文本 at tag + +### 废弃定时任务的 crontab 清理 +- 已删除 xiaokui crontab 中 `*/5 * * * *` 的「飞书问题反馈同步每分钟」任务(含 wrapper 脚本调用) +- 该任务每分钟执行一次打开 MySQL 连接/查询/返回存在潜在连接泄漏风险 + +### 5月9日补跑问题 +- 5月9日10:00定时任务因 `IndentationError` 失败(凌晨08:10自动备份 `c3c8dbb` 损坏了脚本) +- 修复:从上游版本恢复被清空的步骤4-7逻辑 + 模块常量 +- 手动补跑5月8日数据(8条反馈,1个P0)成功 +## 2026-05-09 工作日志 + +### 飞书反馈同步 - 三个文档问题修复 + +**问题一:图片无法点击查看** +- 根因:Markdown表格中 `|` 被 `dialogue_info` 内部的 ` | ` 分隔符破坏 + `![图片]()` 格式在飞书导入时不可靠 +- 修复:`info_parts` 分隔符从 `" | "` 改为 `
`;图片格式从 `![图片](url)` 改为 `📎 [图片](url)` 可点击链接 + +**问题二:子文档作者显示"小研"** +- 根因:现有子文档由 xiaoyan bot (`ou_3e97d43a66639a457f0020a0d7f2bd74`) 创建,xiaokui 无法直接覆盖 +- 修复:在 `update_summary_doc_as_children` 中添加 creator 校验,非 xiaokui 创建则先通过 xiaoyan 凭证删除再重建 +- 关键常量:`XIAOKUI_BOT_OPEN_ID = "ou_fdbf5fdafd91670db34b6ac887f30fb7"` +- 注意:xiaokui 无法删除 xiaoyan 创建的文档(跨应用权限隔离),需回退到 xiaoyan 凭证删除 + +**问题三:子文档排序不规则** +- 根因:旧 `sort_tag = dt.timestamp()` 升序导致旧日期在前 +- 修复:改为 `sort_tag = 9999999999 - int(dt.timestamp())` 实现日期降序 +- ⚠️ 不足:飞书 Wiki V2 API 创建节点时 `sort_tag` 参数可能被忽略(API 返回均为 null) +- 兜底方案:按日期由近到远的顺序依次创建子文档,利用 `node_create_time` 自然排序 +- 所有旧文档已删除并按正确顺序重建(5月8日→5月7日→4月28日),5月6日需手动创建 + +### 飞书分发消息 `` 标签修复 +- 根因:`dispatch_summary_to_chat` 中两步打架——第一步 `re.sub` 注入 HTML `` 文本,第二步 `content_parts` 用正确 `{"tag":"at"}` 格式插入 +- 修复:删除 `re.sub` 注入原始 HTML 标签的代码,仅保留富文本 at tag + +### 5月9日补跑问题 +- 5月9日10:00定时任务因 `IndentationError` 失败(凌晨08:10自动备份 `c3c8dbb` 损坏了脚本) +- 修复:从上游版本恢复被清空的步骤4-7逻辑 + 模块常量 +- 手动补跑5月8日数据(8条反馈,1个P0)成功 + +### 待验证 +- [ ] 确认「用户反馈问题汇总」下子文档排序是否为:5月8日→5月7日→4月28日(由近及远) +- [ ] 5月6日文档需手动创建(重建过程中因无有效问题簇被跳过) +- [ ] 下次定时任务执行时验证 sort_tag 创建-删除-重建流程是否完全正常 +## 2026-05-09 工作日志 + +### 飞书反馈同步 - 三个文档问题修复 + +**问题一:图片无法点击查看** +- 根因:Markdown表格中 `|` 被 `dialogue_info` 内部的 ` | ` 分隔符破坏 + `![图片]()` 格式在飞书导入时不可靠 +- 修复:`info_parts` 分隔符从 `" | "` 改为 `
`;图片格式从 `![图片](url)` 改为 `📎 [图片](url)` 可点击链接 + +**问题二:子文档作者显示"小研"** +- 根因:现有子文档由 xiaoyan bot (`ou_3e97d43a66639a457f0020a0d7f2bd74`) 创建,xiaokui 无法直接覆盖 +- 修复:在 `update_summary_doc_as_children` 中添加 creator 校验,非 xiaokui 创建则先通过 xiaoyan 凭证删除再重建 +- 关键常量:`XIAOKUI_BOT_OPEN_ID = "ou_fdbf5fdafd91670db34b6ac887f30fb7"` +- 注意:xiaokui 无法删除 xiaoyan 创建的文档(跨应用权限隔离),需回退到 xiaoyan 凭证删除 + +**问题三:子文档排序不规则** +- 根因:旧 `sort_tag = dt.timestamp()` 升序导致旧日期在前 +- 修复:改为 `sort_tag = 9999999999 - int(dt.timestamp())` 实现日期降序 +- ⚠️ 不足:飞书 Wiki V2 API 创建节点时 `sort_tag` 参数可能被忽略(API 返回均为 null) +- 兜底方案:按日期由近到远的顺序依次创建子文档,利用 `node_create_time` 自然排序 +- 所有旧文档已删除并按正确顺序重建(5月8日→5月7日→4月28日),5月6日需手动创建 + +### 飞书分发消息 `` 标签修复 +- 根因:`dispatch_summary_to_chat` 中两步打架——第一步 `re.sub` 注入 HTML `` 文本,第二步 `content_parts` 用正确 `{"tag":"at"}` 格式插入 +- 修复:删除 `re.sub` 注入原始 HTML 标签的代码,仅保留富文本 at tag + +### 5月9日补跑问题 +- 5月9日10:00定时任务因 `IndentationError` 失败(凌晨08:10自动备份 `c3c8dbb` 损坏了脚本) +- 修复:从上游版本恢复被清空的步骤4-7逻辑 + 模块常量 +- 手动补跑5月8日数据(8条反馈,1个P0)成功 diff --git a/skills/feishu-feedback-sync/scripts/__pycache__/sync_feishu_feedback.cpython-312.pyc b/skills/feishu-feedback-sync/scripts/__pycache__/sync_feishu_feedback.cpython-312.pyc index c0cb53da84beeda670a80aabcd4f6271957d6ed5..676abc84dc858c2f177a1e14dfb71fbe45177bbd 100644 GIT binary patch delta 12854 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 fTRE7$ delta 9594 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 UdSe8~#2z93EB$nl#B&w?4+8nr2mk;8 diff --git a/skills/feishu-feedback-sync/scripts/sync_feishu_feedback.py b/skills/feishu-feedback-sync/scripts/sync_feishu_feedback.py index ca423cc..3d48084 100755 --- a/skills/feishu-feedback-sync/scripts/sync_feishu_feedback.py +++ b/skills/feishu-feedback-sync/scripts/sync_feishu_feedback.py @@ -36,6 +36,16 @@ CLI = "/root/.nvm/versions/node/v24.14.0/bin/lark-cli" HEADER = ["消息ID", "发送者", "消息类型", "内容", "媒体URL", "引用消息ID", "消息时间", "消息时间戳"] +# === 分发配置 === +DISPATCH_CHAT_ID = "oc_4171a2188f2554522a4309f2d7c27753" # 「小葵小葵」问题反馈群 +DISPATCH_CRED_DIR = "/root/.openclaw/credentials/xiaokui" +XIAOKUI_BOT_OPEN_ID = "ou_fdbf5fdafd91670db34b6ac887f30fb7" # 小葵Bot的open_id,用于校验文档归属 + +# P0问题默认@的人(user_id) +P0_NOTIFY_USERS = [ + "eggbg21g", # 毋益飞 +] + # 推断引用策略参数 TIME_WINDOW_MIN = 2 # 同发送者聚类时间窗口(分钟) GAP_THRESHOLD_MIN = 30 # 大时间跨度视为新话题(分钟) @@ -725,14 +735,12 @@ def summarize_cluster(cluster_msgs, idx, priority_info=None): text = text[:77] + "..." info_parts.append(text) if media_url: - # 图片格式直接用图片标签,飞书可点击预览;其他文件保留链接格式 - if media_url.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')): - info_parts.append(f"![图片]({media_url})") - else: - info_parts.append(f"📎 [文件]({media_url})") + # 图片和文件统一使用可点击链接,避免 Markdown 表格 | 分隔符冲突 + label = "图片" if media_url.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')) else "文件" + info_parts.append(f"📎 [{label}]({media_url})") if not info_parts: info_parts.append("[图片]") - dialogue_info = " | ".join(info_parts) + dialogue_info = "
".join(info_parts) if len(info_parts) > 1 else info_parts[0] role_tag = "" if name == first_speaker and name not in seen_speakers: @@ -906,9 +914,9 @@ def get_tenant_token(cred_dir=None): def list_child_nodes(): - """列出「用户反馈问题汇总」下的所有子节点,返回 {title: {node_token, obj_token}}。同名节点取 obj_edit_time 最新的。""" + """列出「用户反馈问题汇总」下的所有子节点,返回 {title: {node_token, obj_token, creator}}。同名节点取 obj_edit_time 最新的。""" import urllib.request - token = get_tenant_token() + token = get_tenant_token(cred_dir="/root/.openclaw/credentials/xiaokui") url = f"https://open.feishu.cn/open-apis/wiki/v2/spaces/{SUMMARY_SPACE_ID}/nodes?parent_node_token={SUMMARY_PARENT_NODE}" req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) resp = urllib.request.urlopen(req, timeout=10) @@ -923,6 +931,7 @@ def list_child_nodes(): nodes[title] = { "node_token": item["node_token"], "obj_token": item["obj_token"], + "creator": item.get("creator", ""), "_edit_time": edit_time } return nodes @@ -931,16 +940,18 @@ def list_child_nodes(): def create_child_doc(title): """在「用户反馈问题汇总」下创建子文档,返回 obj_token""" import urllib.request, time - token = get_tenant_token() + token = get_tenant_token(cred_dir="/root/.openclaw/credentials/xiaokui") url = f"https://open.feishu.cn/open-apis/wiki/v2/spaces/{SUMMARY_SPACE_ID}/nodes" - # 提取标题中的日期,生成sort_tag(时间戳越大排序越靠前) + # 提取标题中的日期,生成sort_tag + # 飞书Wiki按sort_tag升序排列子节点,因此日期越新sort_tag越小(排在前面) sort_tag = int(time.time()) # 默认当前时间 try: # 标题格式如:2026-05-08 问题反馈 date_str = title.split(" ")[0] dt = datetime.strptime(date_str, "%Y-%m-%d") - sort_tag = int(dt.timestamp()) + # 用大基数减去时间戳:日期越新时间戳越大,减后值越小 → 排在前面 + sort_tag = 9999999999 - int(dt.timestamp()) except: pass @@ -963,6 +974,37 @@ def create_child_doc(title): return None +def _delete_child_node(obj_token): + """删除指定子文档(使用 obj_token)。先尝试 xiaokui 凭证,失败则回退到 xiaoyan(处理旧文档归属问题)""" + import urllib.request + last_error = None + for cred_dir in ["/root/.openclaw/credentials/xiaokui", "/root/.openclaw/credentials/xiaoyan"]: + try: + token = get_tenant_token(cred_dir=cred_dir) + body = json.dumps({"obj_type": "docx"}).encode() + url = f"https://open.feishu.cn/open-apis/wiki/v2/spaces/{SUMMARY_SPACE_ID}/nodes/{obj_token}" + req = urllib.request.Request(url, data=body, headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + }, method="DELETE") + resp = urllib.request.urlopen(req, timeout=10) + data = json.loads(resp.read()) + if data.get("code") == 0: + print(f" 🗑️ 已删除旧文档") + return True + # 权限拒绝 / 服务暂不可用时尝试下一个凭证 + if data.get("code") in (131006, 131001): + last_error = data + continue + last_error = data + break + except Exception as e: + last_error = {"error": str(e)} + continue + print(f" ⚠️ 删除旧文档失败: {last_error}") + return False + + def update_summary_doc_as_children(day_summaries): """ 将各日期的归纳结果写入「用户反馈问题汇总」的子文档中。 @@ -988,8 +1030,17 @@ def update_summary_doc_as_children(day_summaries): fcntl.flock(lock_f, fcntl.LOCK_EX) existing_nodes = list_child_nodes() if title in existing_nodes: - obj_token = existing_nodes[title]["obj_token"] - print(f" 📝 更新子文档: {title}") + node = existing_nodes[title] + # 检查文档是否由小葵创建,若不是则删除重建(修复 author 归属) + if node.get("creator") != XIAOKUI_BOT_OPEN_ID: + print(f" 🔄 文档归属非小葵,重建: {title}") + _delete_child_node(node["obj_token"]) + obj_token = create_child_doc(title) + if not obj_token: + continue + else: + obj_token = node["obj_token"] + print(f" 📝 更新子文档: {title}") else: obj_token = create_child_doc(title) if obj_token: @@ -1112,19 +1163,8 @@ def dispatch_summary_to_chat(day_label, summary_text, p0_only=False): # P0 @跟在标题行后面,不放单独段落 title = f"📋 {day_label} 用户反馈问题归纳" - # 将归纳内容中的 P0 标题行注入 @人 - # 格式:⚠️ P0级核心问题(需优先处理) → ⚠️ P0级核心问题(需优先处理) @某某 - if has_p0 and P0_NOTIFY_USERS: - at_str = "".join(f"" for uid in P0_NOTIFY_USERS) - # 在 P0 标题行后面插入 @ - 归纳_content = re.sub( - r'(⚠️ P0级核心问题(需优先处理))', - rf'\1 {at_str}', - 归纳_content - ) - # 飞书 post 消息不支持 HTML 标签的 at,需要用富文本 tag - # 所以改为:把归纳内容按 P0/P1/P2/P3 段落拆分,P0 段落后追加 at tag + # 把归纳内容按 P0/P1/P2/P3 段落拆分,P0 标题行后追加 at tag content_parts = [] if has_p0 and P0_NOTIFY_USERS: @@ -1262,8 +1302,47 @@ def main(): run_steps = args.steps do_summary = "4" in run_steps or "5" in run_steps or "6" in run_steps or "7" in run_steps - if do_summary: - # 步骤 4:问题归纳 + if do_summary: + # 步骤 4:问题归纳 + # 步骤 5:优先级判定(默认启用,--skip-priority 可跳过) + summary, has_content = generate_summary(clusters, cluster_order, skip_priority=args.skip_priority) + if has_content: + priority_count = summary.count('优先级:') if not args.skip_priority else 0 + print(f" 归纳完成:{summary.count('### 问题')} 个问题") + if not args.skip_priority: + print(f" 步骤5 优先级判定完成:{priority_count} 个问题已评定优先级并排序") + print(f" (归纳内容见下方)") + print(summary) + day_summaries[day] = summary + else: + print(" ⚠️ 无有效问题簇可归纳") + + # 写入表格(步骤2) + success = write_sheet(sheet_id, sorted_msgs) + if success: + total_written += len(sorted_msgs) + print(f" ✅ 写入 {len(sorted_msgs)} 条") + else: + print(f" ❌ 写入失败") + + # 统一写入「用户反馈问题汇总」文档(增量更新,保留已有日期内容)— 步骤4-6 + if day_summaries and do_summary: + update_summary_doc_as_children(day_summaries) + + # 步骤7:问题分发(发送到群聊 + @相关人) + if do_summary and not args.skip_dispatch: + dispatch_mode = args.dispatch_mode + print(f"\n📨 步骤7:问题分发(模式: {dispatch_mode})...") + for day, summary_text in day_summaries.items(): + # 检查当天归纳中是否有 P0 问题 + has_p0 = "⚠️ P0级" in summary_text + if dispatch_mode == "p0" and not has_p0: + print(f" [{day}] 无P0问题,跳过分发") + continue + dispatch_summary_to_chat(day, summary_text, p0_only=(dispatch_mode == "p0")) + + print(f"\n🎉 同步完成,总计写入 {total_written} 条") + if __name__ == "__main__": -main() + main()