guanghulab/notion/sync-ticket.py

66 lines
3.0 KiB
Python
Raw Normal View History

#!/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('停止')