"""Extract todos from the latest inspiration digest + sync to todo API. Supports multiple formats: - [ ] checkbox style - 1. **bold**: description (numbered list with bold prefix) - - **bold**: description (dash list with bold prefix) """ import json import os import re import requests DIGEST_DIR = os.path.expanduser("~/inspiration-collector/ai-insights/daily") TODO_API = "http://127.0.0.1:9001/api/todos" def get_latest_digest(): """Find the most recent digest file (checks date subdirectories).""" if not os.path.isdir(DIGEST_DIR): return None files = [] for root, dirs, filenames in os.walk(DIGEST_DIR): for f in filenames: if "_digest" in f and f.endswith(".md"): files.append(os.path.join(root, f)) if not files: return None files.sort(key=lambda x: os.path.getmtime(x), reverse=True) return files[0] def extract_todos(filepath): """Extract todo items from the digest markdown. Matches: 1. [ ] checkbox items 2. Numbered items like "1. **bold title**:description" 3. Dash items like "- **bold title**:description" Only extracts from the "待办事项" section onwards. """ with open(filepath) as f: content = f.read() todos = [] in_todo_section = False for line in content.split("\n"): stripped = line.strip() # Detect start of todo section if re.search(r"待办事项|待办|TODO|Todo", stripped) and not in_todo_section: in_todo_section = True continue # End of todo section: next major heading or empty line after content if in_todo_section and stripped.startswith("## ") and "待办" not in stripped: break if in_todo_section and stripped == "" and todos: # Allow one blank line, but stop at second if todos and todos[-1] == "__blank__": todos.pop() # remove previous blank marker break todos.append("__blank__") continue if not in_todo_section: continue # Pattern 1: [ ] checkbox m = re.match(r"\s*[-*]\s*\[\s*[ ]\s*\]\s*(.+)", line) if m: text = re.sub(r"\*\*", "", m.group(1).strip()) # strip bold markers if text: todos.append(text) continue # Pattern 2: numbered list "1. **bold**:description" or "1. description" m = re.match(r"\s*\d+\.\s+(.+)", line) if m: text = re.sub(r"\*\*", "", m.group(1).strip()) # strip bold markers if text: todos.append(text) continue # Pattern 3: dash list "- **bold**:description" m = re.match(r"\s*[-*]\s+(.+)", line) if m: text = re.sub(r"\*\*", "", m.group(1).strip()) # Skip template headers like 【今日输入】 if text and len(text) > 8 and not re.match(r"^【.+】$", text): todos.append(text) continue # Clean up blank markers todos = [t for t in todos if t != "__blank__"] return todos def push_todos(todos): """Send extracted todos to the API (skip if already exists).""" try: resp = requests.get(TODO_API, timeout=5) existing = [t["text"] for t in resp.json().get("todos", [])] except: existing = [] count = 0 for text in todos: if text not in existing: requests.post(TODO_API, json={"text": text, "source": "inspiration"}, timeout=5) count += 1 return count if __name__ == "__main__": filepath = get_latest_digest() if not filepath: print("No digest found") exit(0) todos = extract_todos(filepath) print(f"Found {len(todos)} todos in {os.path.basename(filepath)}") if todos: for t in todos: print(f" - {t[:60]}") pushed = push_todos(todos) print(f"Pushed {pushed} new todos to API")