🔄 卡若AI 同步 2026-02-25 10:22 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个

This commit is contained in:
2026-02-25 10:22:56 +08:00
parent 6d8a0697c2
commit 52e176c1fa
5 changed files with 90 additions and 17 deletions

View File

@@ -14,12 +14,18 @@ import subprocess
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
import time import time
import re
# ============ 配置 ============ # ============ 配置 ============
CONFIG = { CONFIG = {
'APP_ID': 'cli_a48818290ef8100d', 'APP_ID': 'cli_a48818290ef8100d',
'APP_SECRET': 'dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4', 'APP_SECRET': 'dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4',
'WIKI_TOKEN': 'JZiiwxEjHiRxouk8hSPcqBn6nrd', 'WIKI_TOKEN': 'JZiiwxEjHiRxouk8hSPcqBn6nrd',
# 按月份路由到对应日志文档,避免跨月误写
'MONTH_WIKI_TOKENS': {
1: 'JZiiwxEjHiRxouk8hSPcqBn6nrd', # 2026年1月 运营团队启动
2: 'Jn2EwXP2OiTujNkAbNCcDcM7nRA', # 2026年2月 (突破执行)
},
'SERVICE_PORT': 5050, 'SERVICE_PORT': 5050,
'TOKEN_FILE': os.path.join(os.path.dirname(__file__), '.feishu_tokens.json') 'TOKEN_FILE': os.path.join(os.path.dirname(__file__), '.feishu_tokens.json')
} }
@@ -217,20 +223,50 @@ def build_blocks(date_str, tasks):
return blocks 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'} headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
if not date_str or not tasks: if not date_str or not tasks:
date_str, tasks = get_today_tasks() date_str, tasks = get_today_tasks()
target_wiki_token = resolve_wiki_token_for_date(date_str, wiki_token)
# 获取文档ID # 获取文档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) headers=headers, timeout=30)
if r.json().get('code') != 0: if r.json().get('code') != 0:
print(f"❌ 获取文档失败") print(f"❌ 获取文档失败")
return False 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检查日期是否存在 # 获取blocks检查日期是否存在
r = requests.get(f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/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) headers=headers, json={'children': content_blocks, 'index': insert_index}, timeout=30)
if r.json().get('code') == 0: if r.json().get('code') == 0:
print(f"{date_str} 日志写入成功") print(f"{date_str} 日志写入成功 -> {doc_title}")
return True return True
else: else:
print(f"❌ 写入失败: {r.json().get('msg')}") print(f"❌ 写入失败: {r.json().get('msg')}")
return False return False
def open_result(): def open_result(wiki_token=None):
"""打开飞书查看结果""" """打开飞书查看结果"""
subprocess.run(['open', WIKI_URL], capture_output=True) token = wiki_token or CONFIG['WIKI_TOKEN']
print(f"📎 已打开飞书: {WIKI_URL}") url = f"https://cunkebao.feishu.cn/wiki/{token}"
subprocess.run(['open', url], capture_output=True)
print(f"📎 已打开飞书: {url}")
# ============ 主流程 ============ # ============ 主流程 ============
def main(): def main():
@@ -289,14 +327,16 @@ def main():
print("❌ 无法获取Token") print("❌ 无法获取Token")
sys.exit(1) sys.exit(1)
# 3. 写入日志 # 3. 写入日志(按月份自动路由)
print("\n📝 Step 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) sys.exit(1)
# 4. 打开结果 # 4. 打开结果
print("\n🎉 Step 4: 完成!") print("\n🎉 Step 4: 完成!")
open_result() open_result(target_wiki_token)
print("\n" + "=" * 50) print("\n" + "=" * 50)
print("✅ 全部完成!") print("✅ 全部完成!")

View File

@@ -35,11 +35,25 @@ def _image_placeholder(idx: int, path: str) -> dict:
return {"__image__": path, "__index__": idx} 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: def md_to_blocks(md: str, image_paths: list[str] | None = None) -> list:
"""将 Markdown 转为飞书 blocks""" """将 Markdown 转为飞书 blocks"""
blocks = [] blocks = []
image_paths = image_paths or [] image_paths = image_paths or []
img_idx = 0 img_idx = 0
first_h1_consumed = False
in_code = False in_code = False
code_lines = [] code_lines = []
@@ -73,16 +87,33 @@ def md_to_blocks(md: str, image_paths: list[str] | None = None) -> list:
# 标题 # 标题
if line.startswith("# "): 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("## "): elif line.startswith("## "):
blocks.append(_h2(line[3:].strip())) blocks.append(_h2(_clean_inline_markdown(line[3:].strip())))
elif line.startswith("### "): elif line.startswith("### "):
blocks.append(_h3(line[4:].strip())) blocks.append(_h3(_clean_inline_markdown(line[4:].strip())))
elif line.lstrip().startswith(">"): 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(): elif line.strip():
blocks.append(_text(line.strip())) raw = line.strip()
# 无序列表统一成 •,减少 markdown 观感噪音
if re.match(r"^[-*]\s+", raw):
raw = "" + re.sub(r"^[-*]\s+", "", raw)
# 有序列表统一成 12样式
raw = re.sub(r"^(\d+)\.\s+", r"\1", raw)
cleaned = _clean_inline_markdown(raw)
if cleaned:
blocks.append(_text(cleaned))
return blocks return blocks

View File

@@ -10,7 +10,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if SCRIPT_DIR not in sys.path: if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR) 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日" DATE_STR = "2月25日"
TASKS = [ TASKS = [
@@ -46,5 +46,5 @@ if __name__ == "__main__":
print("📝 写入飞书日志2月25日...") print("📝 写入飞书日志2月25日...")
ok = write_log(token, DATE_STR, TASKS) ok = write_log(token, DATE_STR, TASKS)
if ok: if ok:
open_result() open_result(resolve_wiki_token_for_date(DATE_STR))
sys.exit(0 if ok else 1) sys.exit(0 if ok else 1)

View File

@@ -135,3 +135,4 @@
| 2026-02-25 09:24:48 | 🔄 卡若AI 同步 2026-02-25 09:24 | 更新:水桥平台对接、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | | 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: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 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 个 |

View File

@@ -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: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: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 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) |