c676c84bc1
Deploy Dashboard / deploy (push) Failing after 38s
- Telegram collector (Telethon MTProto) with checkpoint-based message fetching - Claude API summarization via proxy (haiku model) - Dashboard page with date picker, markdown summary, raw message drill-down - Separate container lifecycle from dashboard for stable Telethon sessions - Shared SQLite DB mounted read-only into dashboard
78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
import os
|
|
import logging
|
|
from datetime import datetime, timedelta, timezone
|
|
import aiosqlite
|
|
import httpx
|
|
from db import DB_PATH
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
API_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
|
|
BASE_URL = os.environ.get("ANTHROPIC_BASE_URL", "https://www.bytecatcode.org")
|
|
MODEL = "claude-haiku-4-5-20251001"
|
|
CST = timezone(timedelta(hours=8))
|
|
|
|
PROMPT = """You are a chat group digest assistant. Summarize the following chat messages into a concise daily digest.
|
|
|
|
Rules:
|
|
- Group by topic/theme, not by chat group
|
|
- Highlight key decisions, shared links, and action items
|
|
- Skip greetings, small talk, and low-value chatter
|
|
- Use markdown formatting
|
|
- Write in the same language as the messages (likely Chinese)
|
|
- Be concise
|
|
|
|
Messages:
|
|
{messages}"""
|
|
|
|
async def summarize(date_str: str = None):
|
|
now = datetime.now(CST)
|
|
if not date_str:
|
|
date_str = now.strftime("%Y-%m-%d")
|
|
|
|
start = datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=CST)
|
|
end = start + timedelta(days=1)
|
|
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
cursor = await db.execute(
|
|
"SELECT group_name, sender_name, text, timestamp FROM messages WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp",
|
|
(start.isoformat(), end.isoformat())
|
|
)
|
|
rows = await cursor.fetchall()
|
|
|
|
if not rows:
|
|
log.info("No messages for %s, skipping summary", date_str)
|
|
return None
|
|
|
|
# Format messages for the prompt
|
|
formatted = []
|
|
for group_name, sender, text, ts in rows:
|
|
formatted.append(f"[{group_name}] {sender}: {text}")
|
|
messages_text = "\n".join(formatted)
|
|
|
|
# Call Claude API
|
|
async with httpx.AsyncClient(timeout=120) as client:
|
|
resp = await client.post(
|
|
f"{BASE_URL}/v1/messages",
|
|
headers={"x-api-key": API_KEY, "anthropic-version": "2023-06-01", "content-type": "application/json"},
|
|
json={
|
|
"model": MODEL,
|
|
"max_tokens": 4096,
|
|
"messages": [{"role": "user", "content": PROMPT.format(messages=messages_text)}]
|
|
}
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
summary = data["content"][0]["text"]
|
|
|
|
# Store summary
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
await db.execute(
|
|
"INSERT OR REPLACE INTO summaries (date, content, created_at) VALUES (?,?,?)",
|
|
(date_str, summary, now.isoformat())
|
|
)
|
|
await db.commit()
|
|
|
|
log.info("Generated summary for %s (%d messages)", date_str, len(rows))
|
|
return summary
|