🔄 卡若AI 同步 2026-03-06 13:24 | 更新:Cursor规则、水桥平台对接、运营中枢工作台 | 排除 >20MB: 11 个
This commit is contained in:
@@ -39,6 +39,12 @@ python3 /Users/karuo/Documents/个人/卡若AI/02_卡人(水)/水桥_平台
|
||||
- `python3 feishu_token_cli.py set-march-token <token>` → 将 3 月文档 node token 写入本地(`.feishu_month_wiki_tokens.json`),写日志时自动使用
|
||||
- `python3 feishu_token_cli.py get-march-token` → 输出当前 3 月 wiki token(环境变量 > 本地文件)
|
||||
|
||||
**Token 自动获取(无 token 时)**:写今日日志(如 `write_today_with_summary.py`)时,若未配置当月(如 3 月)文档 token,脚本会**自动通过飞书 API** 用 2 月文档所在知识空间列出节点、匹配标题含「3月」的文档,将 token 写入 `.feishu_month_wiki_tokens.json` 后继续写入,无需手动配置。若自动获取失败(如空间内无 3 月文档),再提示用命令行:`python3 feishu_token_cli.py set-march-token <从飞书地址栏复制的 token>`。
|
||||
|
||||
**Token 出现问题时的处理**:若出现「获取文档失败」或「token 无效」,一律用命令行处理:先 `get-access-token` 确保登录有效,再 `set-march-token <正确 token>` 写入当月文档 token;写日志脚本会优先使用本地已保存的 token,确保能写入飞书。
|
||||
|
||||
**今日日志 + 配图**:`write_today_with_summary.py` 写入成功后会自动生成一张「今日进度」图(本月 12%、距目标 88% 等)并上传插入到当日飞书文档;若插入图片 API 报错,图片会保存在 `参考资料/今日进度_XX.png`,可手动拖入飞书文档。
|
||||
|
||||
**自动完成**:
|
||||
1. ✅ **静默Token刷新** → 优先使用 refresh_token 自动刷新(无需授权);也可命令行 `get-access-token`
|
||||
2. ✅ **检查服务** → 自动启动后端服务
|
||||
|
||||
@@ -240,6 +240,71 @@ def parse_month_from_date_str(date_str):
|
||||
return None
|
||||
|
||||
|
||||
MONTH_TOKENS_FILE = os.path.join(os.path.dirname(__file__), ".feishu_month_wiki_tokens.json")
|
||||
|
||||
|
||||
def _try_auto_fetch_march_token(access_token):
|
||||
"""无 3 月 token 时通过 API 自动获取:用 2 月文档所在 space 列出节点,匹配标题含「3月」的节点并写入本地。返回 token 或 None。"""
|
||||
feb_token = (CONFIG.get("MONTH_WIKI_TOKENS") or {}).get(2) or CONFIG.get("WIKI_TOKEN")
|
||||
if not feb_token:
|
||||
return None
|
||||
headers = {"Authorization": f"Bearer {access_token}", "Content-Type": "application/json"}
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node",
|
||||
params={"token": feb_token},
|
||||
headers=headers,
|
||||
timeout=15,
|
||||
)
|
||||
j = r.json()
|
||||
if j.get("code") != 0:
|
||||
return None
|
||||
data = j.get("data", {})
|
||||
node = data.get("node", {})
|
||||
space_id = node.get("space_id") or data.get("space_id")
|
||||
if not space_id:
|
||||
return None
|
||||
# 列出空间下节点(可能需分页)
|
||||
page_token = None
|
||||
for _ in range(5):
|
||||
params = {"page_size": 50}
|
||||
if page_token:
|
||||
params["page_token"] = page_token
|
||||
r2 = requests.get(
|
||||
f"https://open.feishu.cn/open-apis/wiki/v2/spaces/{space_id}/nodes",
|
||||
params=params,
|
||||
headers=headers,
|
||||
timeout=15,
|
||||
)
|
||||
j2 = r2.json()
|
||||
if j2.get("code") != 0:
|
||||
break
|
||||
items = j2.get("data", {}).get("items", [])
|
||||
for n in items:
|
||||
title = (n.get("title") or "")
|
||||
if "3月" in title or "3 月" in title:
|
||||
tok = n.get("node_token") or n.get("obj_token") or n.get("token")
|
||||
if tok:
|
||||
data = {}
|
||||
if os.path.exists(MONTH_TOKENS_FILE):
|
||||
try:
|
||||
with open(MONTH_TOKENS_FILE, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
data["3"] = tok
|
||||
with open(MONTH_TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
print("✅ 已通过 API 自动获取 3 月文档 token 并写入本地")
|
||||
return tok
|
||||
page_token = j2.get("data", {}).get("page_token")
|
||||
if not page_token or not j2.get("data", {}).get("has_more"):
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"⚠️ 自动获取 3 月 token 异常: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def _get_month_wiki_token(month):
|
||||
"""当月 wiki token:3 月优先 环境变量 > 本地 .feishu_month_wiki_tokens.json > CONFIG"""
|
||||
if month == 3:
|
||||
@@ -247,9 +312,8 @@ def _get_month_wiki_token(month):
|
||||
if v:
|
||||
return v
|
||||
try:
|
||||
path = os.path.join(os.path.dirname(__file__), ".feishu_month_wiki_tokens.json")
|
||||
if os.path.exists(path):
|
||||
with open(path, encoding="utf-8") as f:
|
||||
if os.path.exists(MONTH_TOKENS_FILE):
|
||||
with open(MONTH_TOKENS_FILE, encoding="utf-8") as f:
|
||||
v = (json.load(f).get("3") or "").strip()
|
||||
if v:
|
||||
return v
|
||||
@@ -311,16 +375,36 @@ def write_log(token, date_str=None, tasks=None, wiki_token=None, overwrite=False
|
||||
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)
|
||||
month = parse_month_from_date_str(date_str)
|
||||
if not target_wiki_token:
|
||||
month = parse_month_from_date_str(date_str)
|
||||
print(f"❌ 未配置当月文档 token({month or '?'} 月请设置 FEISHU_MARCH_WIKI_TOKEN 或对应环境变量)")
|
||||
return False
|
||||
if month == 3:
|
||||
print("🔄 未配置 3 月 token,尝试通过 API 自动获取...")
|
||||
target_wiki_token = _try_auto_fetch_march_token(token)
|
||||
if not target_wiki_token:
|
||||
print(f"❌ 未配置当月文档 token({month or '?'} 月请用 feishu_token_cli.py set-march-token <token> 或设置环境变量)")
|
||||
return False
|
||||
|
||||
# 获取文档ID
|
||||
r = requests.get(f"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token={target_wiki_token}",
|
||||
# 获取文档ID(若为 3 月且 get_node 失败,可再尝试自动获取后重试一次)
|
||||
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 and month == 3:
|
||||
target_wiki_token = _try_auto_fetch_march_token(token)
|
||||
if target_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"❌ 获取文档失败")
|
||||
# 若本地曾保存过无效 token,清除以便下次可重新自动获取或手动 set
|
||||
try:
|
||||
if os.path.exists(MONTH_TOKENS_FILE) and month == 3:
|
||||
with open(MONTH_TOKENS_FILE, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
if (data.get("3") or "").strip() == (target_wiki_token or "").strip():
|
||||
data["3"] = ""
|
||||
with open(MONTH_TOKENS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
except Exception:
|
||||
pass
|
||||
print(f"❌ 获取文档失败(当月 token 无效或网络异常,可用 feishu_token_cli.py set-march-token 写入正确 token)")
|
||||
return False
|
||||
node = r.json()['data']['node']
|
||||
doc_id = node['obj_token']
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
今日飞书日志:搜索全库+聊天/纪要后的最近进度总结汇总 + 每天切片20个视频 + 成交1980及全链路 + 目标百分比写清楚。
|
||||
无 3 月 token 时会自动通过 API 获取;写入成功后生成一张今日进度图并上传到飞书文档。
|
||||
"""
|
||||
import sys
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -11,6 +13,76 @@ sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from auto_log import get_token_silent, write_log, open_result, resolve_wiki_token_for_date
|
||||
|
||||
REF_DIR = SCRIPT_DIR.parent / "参考资料"
|
||||
|
||||
|
||||
def _generate_progress_image(date_str: str) -> Path | None:
|
||||
"""生成今日进度图(本月 12% 距目标 88%),返回图片路径;无 Pillow 则返回 None。"""
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
return None
|
||||
out = REF_DIR / f"今日进度_{date_str.replace('月', '').replace('日', '')}.png"
|
||||
REF_DIR.mkdir(parents=True, exist_ok=True)
|
||||
w, h = 480, 160
|
||||
img = Image.new("RGB", (w, h), color=(255, 252, 240))
|
||||
draw = ImageDraw.Draw(img)
|
||||
try:
|
||||
font = ImageFont.truetype("/System/Library/Fonts/PingFang.ttc", 28)
|
||||
font_small = ImageFont.truetype("/System/Library/Fonts/PingFang.ttc", 20)
|
||||
except Exception:
|
||||
font = ImageFont.load_default()
|
||||
font_small = font
|
||||
draw.text((20, 30), f"今日进度 · {date_str}", fill=(60, 60, 60), font=font)
|
||||
draw.text((20, 75), "本月 12% / 距目标 88%", fill=(180, 80, 60), font=font)
|
||||
draw.text((20, 115), "20 切片 · 1980 全链路", fill=(80, 80, 80), font=font_small)
|
||||
img.save(out)
|
||||
return out
|
||||
|
||||
|
||||
def _get_doc_id(token: str, wiki_token: str) -> str | None:
|
||||
"""根据 wiki node token 获取文档 obj_token。"""
|
||||
r = requests.get(
|
||||
"https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node",
|
||||
params={"token": wiki_token},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=15,
|
||||
)
|
||||
if r.json().get("code") != 0:
|
||||
return None
|
||||
return r.json().get("data", {}).get("node", {}).get("obj_token")
|
||||
|
||||
|
||||
def _upload_and_insert_image(token: str, doc_id: str, image_path: Path, date_str: str) -> bool:
|
||||
"""上传图片到文档并插入到当日标题后。"""
|
||||
from write_0301_feishu_log import upload_image_to_feishu, insert_image_block
|
||||
file_token = upload_image_to_feishu(token, doc_id, image_path)
|
||||
if not file_token:
|
||||
return False
|
||||
# 获取文档 blocks,找到 date_str 所在位置,在其后插入
|
||||
r = requests.get(
|
||||
f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks",
|
||||
params={"document_revision_id": -1, "page_size": 500},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=15,
|
||||
)
|
||||
if r.json().get("code") != 0:
|
||||
return False
|
||||
items = r.json().get("data", {}).get("items", [])
|
||||
root = [b for b in items if b.get("parent_id") == doc_id]
|
||||
idx = None
|
||||
for i, b in enumerate(root):
|
||||
for key in ("heading4", "text"):
|
||||
if key in b:
|
||||
for el in b[key].get("elements", []):
|
||||
if date_str in el.get("text_run", {}).get("content", ""):
|
||||
idx = i
|
||||
break
|
||||
if idx is not None:
|
||||
break
|
||||
insert_at = (idx + 2) if idx is not None else 1
|
||||
return insert_image_block(token, doc_id, file_token, image_path.name, insert_at)
|
||||
|
||||
|
||||
def build_tasks_today_with_summary():
|
||||
"""今日:最近进度汇总 + 每天20切片 + 1980成交及全链路 + 目标百分比"""
|
||||
@@ -69,13 +141,24 @@ def main():
|
||||
target_wiki_token = resolve_wiki_token_for_date(date_str)
|
||||
ok = write_log(token, date_str, tasks, target_wiki_token, overwrite=True)
|
||||
if ok:
|
||||
target_wiki_token = resolve_wiki_token_for_date(date_str)
|
||||
doc_id = _get_doc_id(token, target_wiki_token) if target_wiki_token else None
|
||||
if doc_id:
|
||||
img_path = _generate_progress_image(date_str)
|
||||
if img_path and img_path.exists():
|
||||
if _upload_and_insert_image(token, doc_id, img_path, date_str):
|
||||
print("✅ 今日进度图已上传并插入飞书文档")
|
||||
else:
|
||||
print(f"⚠️ 图片已生成:{img_path},可手动拖入飞书文档")
|
||||
else:
|
||||
print("💡 未安装 Pillow 或生成失败,可手动添加配图")
|
||||
open_result(target_wiki_token)
|
||||
print(f"✅ {date_str} 飞书日志已更新(含进度汇总与目标百分比)")
|
||||
sys.exit(0)
|
||||
print("❌ 写入失败")
|
||||
ref_path = SCRIPT_DIR.parent / "参考资料" / f"{date_str}_飞书日志正文_三件事与未完成.md"
|
||||
ref_path = SCRIPT_DIR.parent / "参考资料" / f"{date_str}_飞书日志_进度汇总与百分比.md"
|
||||
if ref_path.exists():
|
||||
print(f"💡 可复制 {ref_path} 内容到飞书当月文档手动粘贴")
|
||||
print(f"💡 可复制 {ref_path} 内容到飞书 3 月文档「{date_str}」下粘贴")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user