#!/usr/bin/env python3 """Notion ↔ 代码仓库双向同步器""" import os, sys, json, time, subprocess from datetime import datetime try: import requests except: print('pip install requests'); sys.exit(1) NOTION_TOKEN = os.environ.get('NOTION_TOKEN') or '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' # 数据库URL中的ID(不是集合ID) NOTION_DATABASE_ID = '663e4f29c7404d088f3a02fcc70b1418' REPO_PATH = '/opt/guanghulab-repo' DB_PATH = os.path.join(REPO_PATH, 'persona-brain-db', 'brain.db') CACHE_FILE = os.path.join(REPO_PATH, 'notion', 'tickets-cache.json') INTERVAL = int(os.environ.get('SYNC_INTERVAL', '1800')) HEADERS = {'Authorization': f'Bearer {NOTION_TOKEN}', 'Notion-Version': '2022-06-28', 'Content-Type': 'application/json'} def log(m): print(f'[{datetime.now().strftime("%H:%M:%S")}] {m}', flush=True) def query(): url = f'https://api.notion.com/v1/databases/{NOTION_DATABASE_ID}/query' body = {'page_size': 50, 'sorts': [{'timestamp': 'last_edited_time', 'direction': 'descending'}]} try: r = requests.post(url, headers=HEADERS, json=body, timeout=15) if r.status_code == 401: log('Token过期'); return [] if not r.ok: log(f'API错误 {r.status_code}'); return [] return r.json().get('results', []) except Exception as e: log(f'异常: {e}'); return [] def get_mine(tickets): mine = [] for t in tickets: p = t.get('properties', {}) agent = p.get('负责Agent', {}).get('select', {}).get('name', '') status = p.get('状态', {}).get('select', {}).get('name', '') title = p.get('任务标题', {}).get('title', [{}])[0].get('plain_text', '?') code = p.get('编号', {}).get('rich_text', [{}])[0].get('plain_text', '') if not p.get('编号', {}).get('type') else '' if agent in ('铸渊', '') and status not in ('已完成', '暂缓'): mine.append({'page_id': t['id'], 'title': title, 'status': status, 'agent': agent, 'url': t.get('url', ''), 'code': code}) return mine def sync_cycle(): log('=' * 40) tickets = query() if not tickets: return mine = get_mine(tickets) if mine: log(f'铸渊相关工单: {len(mine)} 条') for item in mine: log(f' [{item["status"]}] {item["agent"]} | {item["title"][:40]}') else: log('无铸渊相关工单') try: with open(CACHE_FILE, 'w') as f: json.dump({'last_sync': datetime.now().isoformat(), 'tickets': [{'id': t['id'], 'title': t.get('properties',{}).get('任务标题',{}).get('title',[{}])[0].get('plain_text',''), 'status': t.get('properties',{}).get('状态',{}).get('select',{}).get('name','')} for t in tickets]}, f, ensure_ascii=False, indent=2) except: pass log('同步完成') if __name__ == '__main__': log(f'Notion同步器启动 | 间隔{INTERVAL//60}分钟 | 数据库: {NOTION_DATABASE_ID[:12]}...') sync_cycle() try: while True: time.sleep(INTERVAL) sync_cycle() except KeyboardInterrupt: log('停止')