124 lines
4.7 KiB
Python
124 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""写入 Wiki 页面内容。支持 Markdown 文件和 Block JSON。"""
|
|
import json, sys, os, argparse
|
|
import subprocess
|
|
|
|
SPACE_ID = os.environ.get('FEISHU_SPACE_ID', '7664817589230570761')
|
|
|
|
def get_token():
|
|
r = subprocess.run(['python3', os.path.join(os.path.dirname(__file__), 'init_token.py')],
|
|
capture_output=True, text=True)
|
|
return json.loads(r.stdout)['token']
|
|
|
|
# Block helpers
|
|
def te(t, **kw):
|
|
d = {}
|
|
d.update(kw)
|
|
return {'text_run': {'content': t, 'text_element_style': d}}
|
|
|
|
def link_el(t, u):
|
|
return {'text_run': {'content': t, 'text_element_style': {'link': {'url': u}}}}
|
|
|
|
H1 = lambda t: {'block_type': 3, 'heading1': {'elements': [te(t, bold=True)], 'style': {}}}
|
|
H2 = lambda t: {'block_type': 4, 'heading2': {'elements': [te(t, bold=True)], 'style': {}}}
|
|
H3 = lambda t: {'block_type': 5, 'heading3': {'elements': [te(t, bold=True)], 'style': {}}}
|
|
DIV = lambda: {'block_type': 22, 'divider': {}}
|
|
TXT = lambda t: {'block_type': 2, 'text': {'elements': [te(t)], 'style': {}}}
|
|
BLD = lambda t: {'block_type': 2, 'text': {'elements': [te(t, bold=True)], 'style': {}}}
|
|
BUL = lambda t, lt='', lu='': {'block_type': 12, 'bullet': {'elements': [te(t)] + ([link_el(' ' + lt, lu)] if lt and lu else []), 'style': {}}}
|
|
|
|
def json_to_blocks(data):
|
|
"""Convert JSON block data to Feishu block objects"""
|
|
if isinstance(data, list):
|
|
return [json_to_blocks(d) for d in data]
|
|
bt = data.get('type', 'text')
|
|
text = data.get('text', '')
|
|
|
|
type_map = {
|
|
'h1': lambda: H1(text),
|
|
'h2': lambda: H2(text),
|
|
'h3': lambda: H3(text),
|
|
'text': lambda: TXT(text),
|
|
'bold': lambda: BLD(text),
|
|
'divider': lambda: DIV(),
|
|
'bullet': lambda: BUL(text, data.get('link_text', ''), data.get('link_url', '')),
|
|
}
|
|
return type_map.get(bt, lambda: TXT(text))()
|
|
|
|
def write_blocks(node_token, blocks):
|
|
import requests
|
|
token = get_token()
|
|
h = {'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json'}
|
|
|
|
# Get node detail to find obj_token
|
|
r = requests.get(
|
|
'https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node',
|
|
params={'token': node_token}, headers=h
|
|
)
|
|
nd = r.json()
|
|
if nd.get('code') != 0:
|
|
raise Exception(f"Get node failed: {nd.get('msg')}")
|
|
doc_id = nd['data']['node']['obj_token']
|
|
|
|
# Clear defaults
|
|
r = requests.get(
|
|
f'https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{doc_id}',
|
|
headers=h
|
|
)
|
|
kids = r.json().get('data', {}).get('block', {}).get('children', [])
|
|
if kids:
|
|
cids = [c['block_id'] for c in kids]
|
|
requests.delete(
|
|
f'https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{doc_id}/children/batch_delete',
|
|
headers=h, json={'start_index': 0, 'end_index': len(cids)}
|
|
)
|
|
|
|
# Write in batches
|
|
batch_size = 25
|
|
for i in range(0, len(blocks), batch_size):
|
|
batch = blocks[i:i + batch_size]
|
|
r = requests.post(
|
|
f'https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{doc_id}/children',
|
|
headers=h, json={'children': batch, 'index': -1}
|
|
)
|
|
if r.json().get('code') != 0:
|
|
raise Exception(f"Write batch {i // batch_size} failed: {r.json().get('msg')}")
|
|
return True
|
|
|
|
if __name__ == '__main__':
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument('--node', required=True, help='Node token')
|
|
p.add_argument('--file', default=None, help='Content file (JSON block array or Markdown)')
|
|
p.add_argument('--json', default=None, help='Inline JSON block array')
|
|
p.add_argument('--markdown', action='store_true', help='Treat --file as Markdown')
|
|
args = p.parse_args()
|
|
|
|
try:
|
|
if args.json:
|
|
data = json.loads(args.json)
|
|
elif args.file:
|
|
if args.markdown or args.file.endswith('.md'):
|
|
import subprocess
|
|
r = subprocess.run(
|
|
['python3', os.path.join(os.path.dirname(__file__), 'md_to_blocks.py'), args.file],
|
|
capture_output=True, text=True
|
|
)
|
|
if r.returncode != 0:
|
|
raise Exception(f"md_to_blocks failed: {r.stderr}")
|
|
data = json.loads(r.stdout)
|
|
else:
|
|
with open(args.file) as f:
|
|
data = json.load(f)
|
|
else:
|
|
data = json.load(sys.stdin)
|
|
|
|
if isinstance(data, list) and not isinstance(data, str):
|
|
blocks = data
|
|
else:
|
|
blocks = json_to_blocks(data)
|
|
write_blocks(args.node, blocks)
|
|
print('OK')
|
|
except Exception as e:
|
|
print(f'Error: {e}', file=sys.stderr)
|
|
sys.exit(1)
|