guanghulab/notion/sync-ticket.py

66 lines
3.0 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
"""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('停止')