diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/auto_log.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/auto_log.py index 60e06878..6dd7c0af 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/auto_log.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/auto_log.py @@ -14,12 +14,18 @@ import subprocess import requests from datetime import datetime, timedelta import time +import re # ============ 配置 ============ CONFIG = { 'APP_ID': 'cli_a48818290ef8100d', 'APP_SECRET': 'dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4', 'WIKI_TOKEN': 'JZiiwxEjHiRxouk8hSPcqBn6nrd', + # 按月份路由到对应日志文档,避免跨月误写 + 'MONTH_WIKI_TOKENS': { + 1: 'JZiiwxEjHiRxouk8hSPcqBn6nrd', # 2026年1月 运营团队启动 + 2: 'Jn2EwXP2OiTujNkAbNCcDcM7nRA', # 2026年2月 (突破执行) + }, 'SERVICE_PORT': 5050, 'TOKEN_FILE': os.path.join(os.path.dirname(__file__), '.feishu_tokens.json') } @@ -217,20 +223,50 @@ def build_blocks(date_str, tasks): return blocks -def write_log(token, date_str=None, tasks=None): + +def parse_month_from_date_str(date_str): + """从如 '2月25日' 提取月份整数""" + m = re.search(r'(\d+)\s*月', date_str or '') + if not m: + return None + try: + return int(m.group(1)) + except ValueError: + return None + + +def resolve_wiki_token_for_date(date_str, explicit_wiki_token=None): + """根据日期路由文档token;允许显式覆盖""" + if explicit_wiki_token: + return explicit_wiki_token + month = parse_month_from_date_str(date_str) + if month and month in CONFIG.get('MONTH_WIKI_TOKENS', {}): + return CONFIG['MONTH_WIKI_TOKENS'][month] + return CONFIG['WIKI_TOKEN'] + +def write_log(token, date_str=None, tasks=None, wiki_token=None): """写入日志(倒序插入:新日期在最上面)""" headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} if not date_str or not tasks: date_str, tasks = get_today_tasks() + target_wiki_token = resolve_wiki_token_for_date(date_str, wiki_token) # 获取文档ID - r = requests.get(f"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token={CONFIG['WIKI_TOKEN']}", + r = requests.get(f"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token={target_wiki_token}", headers=headers, timeout=30) if r.json().get('code') != 0: print(f"❌ 获取文档失败") return False - doc_id = r.json()['data']['node']['obj_token'] + node = r.json()['data']['node'] + doc_id = node['obj_token'] + doc_title = node.get('title', '') + + # 防串月:日期月份与文档标题不一致时拒绝写入 + month = parse_month_from_date_str(date_str) + if month and f"{month}月" not in doc_title: + print(f"❌ 月份校验失败:{date_str} 不应写入《{doc_title}》") + return False # 获取blocks检查日期是否存在 r = requests.get(f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks", @@ -261,16 +297,18 @@ def write_log(token, date_str=None, tasks=None): headers=headers, json={'children': content_blocks, 'index': insert_index}, timeout=30) if r.json().get('code') == 0: - print(f"✅ {date_str} 日志写入成功") + print(f"✅ {date_str} 日志写入成功 -> {doc_title}") return True else: print(f"❌ 写入失败: {r.json().get('msg')}") return False -def open_result(): +def open_result(wiki_token=None): """打开飞书查看结果""" - subprocess.run(['open', WIKI_URL], capture_output=True) - print(f"📎 已打开飞书: {WIKI_URL}") + token = wiki_token or CONFIG['WIKI_TOKEN'] + url = f"https://cunkebao.feishu.cn/wiki/{token}" + subprocess.run(['open', url], capture_output=True) + print(f"📎 已打开飞书: {url}") # ============ 主流程 ============ def main(): @@ -289,14 +327,16 @@ def main(): print("❌ 无法获取Token") sys.exit(1) - # 3. 写入日志 + # 3. 写入日志(按月份自动路由) print("\n📝 Step 3: 写入日志...") - if not write_log(token): + date_str, tasks = get_today_tasks() + target_wiki_token = resolve_wiki_token_for_date(date_str) + if not write_log(token, date_str, tasks, target_wiki_token): sys.exit(1) # 4. 打开结果 print("\n🎉 Step 4: 完成!") - open_result() + open_result(target_wiki_token) print("\n" + "=" * 50) print("✅ 全部完成!") diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/md_to_feishu_json.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/md_to_feishu_json.py index d6ec0f6a..b30bec95 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/md_to_feishu_json.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/md_to_feishu_json.py @@ -35,11 +35,25 @@ def _image_placeholder(idx: int, path: str) -> dict: return {"__image__": path, "__index__": idx} +def _clean_inline_markdown(text: str) -> str: + """清理常见行内 markdown 标记,输出更适合飞书阅读的纯文本。""" + t = text + # 粗体/斜体标记 + t = re.sub(r"\*\*(.*?)\*\*", r"\1", t) + t = re.sub(r"__(.*?)__", r"\1", t) + t = re.sub(r"\*(.*?)\*", r"\1", t) + t = re.sub(r"_(.*?)_", r"\1", t) + # 行内代码保留内容,去掉反引号 + t = re.sub(r"`([^`]+)`", r"\1", t) + return t.strip() + + def md_to_blocks(md: str, image_paths: list[str] | None = None) -> list: """将 Markdown 转为飞书 blocks""" blocks = [] image_paths = image_paths or [] img_idx = 0 + first_h1_consumed = False in_code = False code_lines = [] @@ -73,16 +87,33 @@ def md_to_blocks(md: str, image_paths: list[str] | None = None) -> list: # 标题 if line.startswith("# "): - blocks.append(_h1(line[2:].strip())) + # 避免正文和文档标题重复:默认跳过第一行 H1 + if first_h1_consumed: + blocks.append(_h1(_clean_inline_markdown(line[2:].strip()))) + else: + first_h1_consumed = True elif line.startswith("## "): - blocks.append(_h2(line[3:].strip())) + blocks.append(_h2(_clean_inline_markdown(line[3:].strip()))) elif line.startswith("### "): - blocks.append(_h3(line[4:].strip())) + blocks.append(_h3(_clean_inline_markdown(line[4:].strip()))) elif line.lstrip().startswith(">"): # 引用块转普通说明行,降低写入失败概率 - blocks.append(_text(line.lstrip()[1:].strip())) + quote = line.lstrip() + while quote.startswith(">"): + quote = quote[1:].lstrip() + quote = _clean_inline_markdown(quote) + if quote: + blocks.append(_text(quote)) elif line.strip(): - blocks.append(_text(line.strip())) + raw = line.strip() + # 无序列表统一成 •,减少 markdown 观感噪音 + if re.match(r"^[-*]\s+", raw): + raw = "• " + re.sub(r"^[-*]\s+", "", raw) + # 有序列表统一成 1)2)样式 + raw = re.sub(r"^(\d+)\.\s+", r"\1)", raw) + cleaned = _clean_inline_markdown(raw) + if cleaned: + blocks.append(_text(cleaned)) return blocks diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_today_20260225.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_today_20260225.py index 81244074..0104c36a 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_today_20260225.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_today_20260225.py @@ -10,7 +10,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) if SCRIPT_DIR not in sys.path: sys.path.insert(0, SCRIPT_DIR) -from auto_log import get_token_silent, write_log, open_result +from auto_log import get_token_silent, write_log, open_result, resolve_wiki_token_for_date DATE_STR = "2月25日" TASKS = [ @@ -46,5 +46,5 @@ if __name__ == "__main__": print("📝 写入飞书日志(2月25日)...") ok = write_log(token, DATE_STR, TASKS) if ok: - open_result() + open_result(resolve_wiki_token_for_date(DATE_STR)) sys.exit(0 if ok else 1) diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 4d639a4a..30c6c1e7 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -135,3 +135,4 @@ | 2026-02-25 09:24:48 | 🔄 卡若AI 同步 2026-02-25 09:24 | 更新:水桥平台对接、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | | 2026-02-25 09:28:50 | 🔄 卡若AI 同步 2026-02-25 09:28 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | | 2026-02-25 09:31:15 | 🔄 卡若AI 同步 2026-02-25 09:31 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | +| 2026-02-25 10:15:21 | 🔄 卡若AI 同步 2026-02-25 10:15 | 更新:水桥平台对接、运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 9f1cab32..283408f3 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -138,3 +138,4 @@ | 2026-02-25 09:24:48 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 09:24 | 更新:水桥平台对接、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-25 09:28:50 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 09:28 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-25 09:31:15 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 09:31 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-25 10:15:21 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 10:15 | 更新:水桥平台对接、运营中枢、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |