ai_member_xiaobian/tmp/format_rich_text.py
2026-05-16 08:10:01 +08:00

174 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
U21 L4 剧本 - 知识点标粗+红输出
格式: type: "text" + textFormatRuns已验证可行
- 中互动/核心互动 → 知识点标粗+红色(输出)
- TL行 → 知识点标粗(输入)
"""
import json, re, requests, os, sys
CRED_DIR = "/root/.openclaw/credentials"
BASE_URL = "https://open.feishu.cn/open-apis"
KPS_WORDS = ["flat", "hall", "room", "know"]
KPS_PHRASES = ["do you know", "what is in"]
def get_token(name="xiaobian"):
config_path = os.path.join(CRED_DIR, name, "config.json")
with open(config_path) as f:
config = json.load(f)
app_id = config["apps"][0]["appId"]
app_secret = config["apps"][0]["appSecret"]
resp = requests.post(f"{BASE_URL}/auth/v3/tenant_access_token/internal",
json={"app_id": app_id, "app_secret": app_secret}, timeout=10)
return resp.json()["tenant_access_token"]
def parse_kps(text):
"""Find all KP spans. Returns sorted non-overlapping (start, end) list."""
lower = text.lower()
spans = []
# Phrase KPs first (take precedence)
for phrase in KPS_PHRASES:
idx = 0
while True:
idx = lower.find(phrase, idx)
if idx == -1:
break
spans.append((idx, idx + len(phrase)))
idx += len(phrase)
# Word KPs (word boundary match)
for word in KPS_WORDS:
for m in re.finditer(r'\b' + re.escape(word) + r'\b', text, re.IGNORECASE):
spans.append((m.start(), m.end()))
# Sort and deduplicate overlaps
spans.sort(key=lambda x: x[0])
result = []
last_end = 0
for s, e in spans:
if s >= last_end:
result.append((s, e))
last_end = e
return result
def build_rich_text_cell(text, is_output):
"""Build a rich text cell using type:text + textFormatRuns."""
spans = parse_kps(text)
if not spans:
return text
format_runs = []
pos = 0
for i, (start, end) in enumerate(spans):
if pos < start:
format_runs.append({"format": {}, "startIndex": pos})
f = {"bold": True}
if is_output:
f["foreColor"] = {"red": 1.0, "green": 0.0, "blue": 0.0, "alpha": 0.0}
format_runs.append({"format": f, "startIndex": start})
pos = end
if pos < len(text):
format_runs.append({"format": {}, "startIndex": pos})
# Always start with a run at index 0
if not format_runs or format_runs[0]["startIndex"] != 0:
format_runs.insert(0, {"format": {}, "startIndex": 0})
return {
"type": "text",
"text": text,
"textFormatRuns": format_runs
}
def main():
token = get_token()
TKN = "NiajsPDjXhQHn8tURCeck8zlndd"
SHT = "3O2sso"
with open("tmp/u21_l4_merged.json", "r", encoding="utf-8") as f:
data = json.load(f)
rows = data["rows"]
# Determine type for each row (blank inherits from above)
current_type = ""
row_types = []
for row in rows:
rt = row[0] if len(row) > 0 and row[0] else ""
if rt in ("TL", "中互动", "核心互动"):
current_type = rt
row_types.append(current_type)
values = []
format_count = 0
for ri, row in enumerate(rows):
out_row = []
is_output = row_types[ri] in ("中互动", "核心互动")
for ci, cell in enumerate(row):
if ci == 3 and isinstance(cell, str) and cell.strip():
formatted = build_rich_text_cell(cell, is_output)
out_row.append(formatted)
if isinstance(formatted, dict):
format_count += 1
elif ci == 3 and isinstance(cell, str):
out_row.append(cell)
else:
out_row.append(cell if cell else "")
values.append(out_row)
num_rows = len(values)
num_cols = 4
col_letter = "D"
print(f"Writing {num_rows} rows, {format_count} formatted cells...")
resp = requests.put(
f"{BASE_URL}/sheets/v2/spreadsheets/{TKN}/values",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json={
"valueRange": {
"range": f"{SHT}!A1:{col_letter}{num_rows}",
"values": values,
},
},
timeout=60,
)
result = resp.json()
if result.get("code") != 0:
print(f"❌ Write failed: {result}", file=sys.stderr)
sys.exit(1)
print(json.dumps({"status": "ok", "rows": num_rows, "columns": num_cols, "formatted": format_count}))
# Set header style
sr = requests.put(
f"{BASE_URL}/sheets/v2/spreadsheets/{TKN}/style",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json={
"appendStyle": {
"range": f"{SHT}!A1:D1",
"style": {"font": {"bold": True}, "backColor": "#e8e8e8"},
},
},
timeout=10,
)
print(json.dumps({"style_status": "ok"}))
if __name__ == "__main__":
main()