guanghulab/notion/sync-ticket.py
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

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