sophhub 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/consensus/skill.json +20 -0
- package/skills/consensus/src/SKILL.md +93 -0
- package/skills/deepwiki/skill.json +20 -0
- package/skills/deepwiki/src/SKILL.md +45 -0
- package/skills/deepwiki/src/_meta.json +6 -0
- package/skills/deepwiki/src/scripts/deepwiki.js +135 -0
- package/skills/feishu-bitable/skill.json +20 -0
- package/skills/feishu-bitable/src/CHECKLIST.md +150 -0
- package/skills/feishu-bitable/src/README.md +178 -0
- package/skills/feishu-bitable/src/SKILL.md +113 -0
- package/skills/feishu-bitable/src/_meta.json +6 -0
- package/skills/feishu-bitable/src/api.js +381 -0
- package/skills/feishu-bitable/src/bin/cli.js +284 -0
- package/skills/feishu-bitable/src/description.md +143 -0
- package/skills/feishu-bitable/src/examples/create-records.json +52 -0
- package/skills/feishu-bitable/src/examples/create-table.json +64 -0
- package/skills/feishu-bitable/src/package-lock.json +324 -0
- package/skills/feishu-bitable/src/package.json +33 -0
- package/skills/feishu-bitable/src/publish-config.json +14 -0
- package/skills/feishu-bitable/src/test-simple.js +61 -0
- package/skills/feishu-bitable/src/utils.js +261 -0
- package/skills/google-maps/skill.json +20 -0
- package/skills/google-maps/src/SKILL.md +237 -0
- package/skills/google-maps/src/_meta.json +6 -0
- package/skills/google-maps/src/lib/map_helper.py +912 -0
- package/skills/large-task-router/skill.json +20 -0
- package/skills/large-task-router/src/SKILL.md +79 -0
- package/skills/large-task-router/src/templates/plan.md +74 -0
- package/skills/skillhub/skill.json +11 -4
- package/skills/skillhub/src/SKILL.md +11 -1
- package/skills/sophnet-dailynews/skill.json +20 -0
- package/skills/sophnet-dailynews/src/SKILL.md +179 -0
- package/skills/sophnet-dailynews/src/cache.json +151 -0
- package/skills/sophnet-dailynews/src/sources.json +230 -0
- package/skills/sophnet-schedule/skill.json +20 -0
- package/skills/sophnet-schedule/src/ARCHITECTURE.md +321 -0
- package/skills/sophnet-schedule/src/IMPROVEMENTS.md +145 -0
- package/skills/sophnet-schedule/src/SKILL.md +1050 -0
- package/skills/sophnet-schedule/src/_meta.json +6 -0
- package/skills/sophnet-schedule/src/api/__init__.py +0 -0
- package/skills/sophnet-schedule/src/api/models.py +245 -0
- package/skills/sophnet-schedule/src/apps/add_event.py +237 -0
- package/skills/sophnet-schedule/src/apps/check_reminders.py +112 -0
- package/skills/sophnet-schedule/src/apps/check_roc.py +246 -0
- package/skills/sophnet-schedule/src/apps/generate_daily_plan.py +342 -0
- package/skills/sophnet-schedule/src/apps/import_events.py +216 -0
- package/skills/sophnet-schedule/src/apps/monitor_calendar_changes.py +140 -0
- package/skills/sophnet-schedule/src/apps/register_tasks.py +169 -0
- package/skills/sophnet-schedule/src/apps/sync_roc_to_gcal.py +174 -0
- package/skills/sophnet-schedule/src/compat.py +66 -0
- package/skills/sophnet-schedule/src/config/__init__.py +0 -0
- package/skills/sophnet-schedule/src/config/reminder_rules.yaml +96 -0
- package/skills/sophnet-schedule/src/config/roc_events.yaml +44 -0
- package/skills/sophnet-schedule/src/config/settings.py +133 -0
- package/skills/sophnet-schedule/src/config/task_registry.yaml +92 -0
- package/skills/sophnet-schedule/src/docs/FRONTEND_INTEGRATION_GUIDE.md +437 -0
- package/skills/sophnet-schedule/src/gcal/__init__.py +0 -0
- package/skills/sophnet-schedule/src/gcal/client.py +374 -0
- package/skills/sophnet-schedule/src/gcal/models.py +91 -0
- package/skills/sophnet-schedule/src/requirements.txt +6 -0
- package/skills/sophnet-schedule/src/scripts/setup_gcal_token.py +85 -0
- package/skills/sophnet-schedule/src/server.py +669 -0
- package/skills/sophnet-schedule/src/services/__init__.py +0 -0
- package/skills/sophnet-schedule/src/services/calendar_backend.py +139 -0
- package/skills/sophnet-schedule/src/services/conflict_detector.py +96 -0
- package/skills/sophnet-schedule/src/services/datetime_utils.py +117 -0
- package/skills/sophnet-schedule/src/services/event_classifier.py +100 -0
- package/skills/sophnet-schedule/src/services/event_diff.py +160 -0
- package/skills/sophnet-schedule/src/services/google_integration.py +500 -0
- package/skills/sophnet-schedule/src/services/job_store.py +100 -0
- package/skills/sophnet-schedule/src/services/local_event_store.py +266 -0
- package/skills/sophnet-schedule/src/services/reminder_planner.py +116 -0
- package/skills/sophnet-schedule/src/services/runtime_utils.py +31 -0
- package/skills/sophnet-schedule/src/services/table_parser.py +286 -0
- package/skills/sophnet-schedule/src/services/task_builder.py +167 -0
- package/skills/sophnet-schedule/src/services/time_window.py +72 -0
- package/skills/sophnet-stock/skill.json +20 -0
- package/skills/sophnet-stock/src/App-Plan.md +442 -0
- package/skills/sophnet-stock/src/README.md +214 -0
- package/skills/sophnet-stock/src/SKILL.md +236 -0
- package/skills/sophnet-stock/src/TODO.md +394 -0
- package/skills/sophnet-stock/src/_meta.json +6 -0
- package/skills/sophnet-stock/src/docs/ARCHITECTURE.md +408 -0
- package/skills/sophnet-stock/src/docs/CONCEPT.md +233 -0
- package/skills/sophnet-stock/src/docs/HOT_SCANNER.md +288 -0
- package/skills/sophnet-stock/src/docs/README.md +95 -0
- package/skills/sophnet-stock/src/docs/USAGE.md +465 -0
- package/skills/sophnet-stock/src/scripts/analyze_stock.py +2565 -0
- package/skills/sophnet-stock/src/scripts/dividends.py +365 -0
- package/skills/sophnet-stock/src/scripts/hot_scanner.py +582 -0
- package/skills/sophnet-stock/src/scripts/portfolio.py +548 -0
- package/skills/sophnet-stock/src/scripts/rumor_scanner.py +342 -0
- package/skills/sophnet-stock/src/scripts/test_stock_analysis.py +409 -0
- package/skills/sophnet-stock/src/scripts/watchlist.py +336 -0
- package/skills/xiaohongshu/skill.json +20 -0
- package/skills/xiaohongshu/src/SKILL.md +91 -0
- package/skills/xiaohongshu/src/_meta.json +6 -0
- package/skills/xiaohongshu/src/assets/card.html +216 -0
- package/skills/xiaohongshu/src/assets/cover.html +82 -0
- package/skills/xiaohongshu/src/assets/example.md +84 -0
- package/skills/xiaohongshu/src/assets/styles.css +318 -0
- package/skills/xiaohongshu/src/scripts/render_xhs_v2.py +737 -0
- package/skills/xiaohongshu/src/scripts/sign_server.py +158 -0
- package/skills/xiaohongshu/src/scripts/stealth.min.js +7 -0
- package/skills/xiaohongshu/src/scripts/xhs_tool.py +186 -0
- package/skills/xiaohongshu/src/workflow.py +185 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.2",
|
|
3
|
+
"last_updated": "2026-02-09",
|
|
4
|
+
|
|
5
|
+
"sources": {
|
|
6
|
+
"tier1": {
|
|
7
|
+
"description": "中国大陆可直连的高信号 RSS 源",
|
|
8
|
+
"batch_a": [
|
|
9
|
+
{
|
|
10
|
+
"id": "ithome",
|
|
11
|
+
"name": "IT之家",
|
|
12
|
+
"url": "https://www.ithome.com/rss/",
|
|
13
|
+
"fetch_method": "webfetch",
|
|
14
|
+
"extract": "top_10_rss",
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"avg_quality": 4.2,
|
|
17
|
+
"success_rate": 0.95
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "kr36",
|
|
21
|
+
"name": "36氪",
|
|
22
|
+
"url": "https://36kr.com/feed",
|
|
23
|
+
"fetch_method": "webfetch",
|
|
24
|
+
"extract": "top_10_rss",
|
|
25
|
+
"enabled": true,
|
|
26
|
+
"avg_quality": 4.3,
|
|
27
|
+
"success_rate": 0.94
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"batch_b": [
|
|
31
|
+
{
|
|
32
|
+
"id": "jiqizhixin",
|
|
33
|
+
"name": "机器之心",
|
|
34
|
+
"url": "https://www.jiqizhixin.com/rss",
|
|
35
|
+
"fetch_method": "webfetch",
|
|
36
|
+
"extract": "top_10_rss",
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"avg_quality": 4.4,
|
|
39
|
+
"success_rate": 0.92
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "leiphone",
|
|
43
|
+
"name": "雷峰网",
|
|
44
|
+
"url": "https://www.leiphone.com/feed",
|
|
45
|
+
"fetch_method": "webfetch",
|
|
46
|
+
"extract": "top_10_rss",
|
|
47
|
+
"enabled": true,
|
|
48
|
+
"avg_quality": 4.1,
|
|
49
|
+
"success_rate": 0.90
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
"tier2": {
|
|
55
|
+
"description": "补充覆盖面(资讯/国际/生活方式)",
|
|
56
|
+
"batch_a": [
|
|
57
|
+
{
|
|
58
|
+
"id": "sspai",
|
|
59
|
+
"name": "少数派",
|
|
60
|
+
"url": "https://sspai.com/feed",
|
|
61
|
+
"fetch_method": "webfetch",
|
|
62
|
+
"extract": "top_10_rss",
|
|
63
|
+
"enabled": true,
|
|
64
|
+
"avg_quality": 4.0,
|
|
65
|
+
"success_rate": 0.90
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "chinadaily_china",
|
|
69
|
+
"name": "China Daily - China",
|
|
70
|
+
"url": "https://www.chinadaily.com.cn/rss/china_rss.xml",
|
|
71
|
+
"fetch_method": "webfetch",
|
|
72
|
+
"extract": "top_10_rss",
|
|
73
|
+
"enabled": true,
|
|
74
|
+
"avg_quality": 3.8,
|
|
75
|
+
"success_rate": 0.88
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"batch_b": [
|
|
79
|
+
{
|
|
80
|
+
"id": "chinadaily_world",
|
|
81
|
+
"name": "China Daily - World",
|
|
82
|
+
"url": "https://www.chinadaily.com.cn/rss/world_rss.xml",
|
|
83
|
+
"fetch_method": "webfetch",
|
|
84
|
+
"extract": "top_10_rss",
|
|
85
|
+
"enabled": true,
|
|
86
|
+
"avg_quality": 3.8,
|
|
87
|
+
"success_rate": 0.88
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "chinadaily_sports",
|
|
91
|
+
"name": "China Daily - Sports",
|
|
92
|
+
"url": "https://www.chinadaily.com.cn/rss/sports_rss.xml",
|
|
93
|
+
"fetch_method": "webfetch",
|
|
94
|
+
"extract": "top_10_rss",
|
|
95
|
+
"enabled": true,
|
|
96
|
+
"avg_quality": 3.6,
|
|
97
|
+
"success_rate": 0.88
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
"tier3_browser": {
|
|
103
|
+
"description": "JS 渲染源(当前禁用,避免浏览器依赖)",
|
|
104
|
+
"sources": []
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
"disabled": {
|
|
108
|
+
"description": "已禁用的源(海外/反爬/提取不稳定)",
|
|
109
|
+
"sources": [
|
|
110
|
+
{
|
|
111
|
+
"id": "hn",
|
|
112
|
+
"name": "Hacker News",
|
|
113
|
+
"url": "https://news.ycombinator.com/rss",
|
|
114
|
+
"reason": "海外源在中国大陆不稳定/被阻断",
|
|
115
|
+
"disabled_date": "2026-02-09"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "hf_papers",
|
|
119
|
+
"name": "HuggingFace Papers",
|
|
120
|
+
"url": "https://huggingface.co/papers/date/{{date}}",
|
|
121
|
+
"reason": "海外源 + 页面提取不稳定",
|
|
122
|
+
"disabled_date": "2026-02-09"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "one_useful_thing",
|
|
126
|
+
"name": "One Useful Thing",
|
|
127
|
+
"url": "https://www.oneusefulthing.org",
|
|
128
|
+
"reason": "海外源 + Substack 结构影响提取",
|
|
129
|
+
"disabled_date": "2026-02-09"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "paul_graham",
|
|
133
|
+
"name": "Paul Graham Essays",
|
|
134
|
+
"url": "https://paulgraham.com/articles.html",
|
|
135
|
+
"reason": "海外源 + 提取不稳定",
|
|
136
|
+
"disabled_date": "2026-02-09"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "james_clear",
|
|
140
|
+
"name": "James Clear 3-2-1",
|
|
141
|
+
"url": "https://jamesclear.com/3-2-1",
|
|
142
|
+
"reason": "海外源 + newsletter 需要订阅",
|
|
143
|
+
"disabled_date": "2026-02-09"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "fs_blog",
|
|
147
|
+
"name": "Farnam Street Brain Food",
|
|
148
|
+
"url": "https://fs.blog/brain-food",
|
|
149
|
+
"reason": "海外源 + 提取不稳定",
|
|
150
|
+
"disabled_date": "2026-02-09"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "hackernoon_pm",
|
|
154
|
+
"name": "HackerNoon PM",
|
|
155
|
+
"url": "https://hackernoon.com/c/product-management",
|
|
156
|
+
"reason": "海外源 + 提取不稳定",
|
|
157
|
+
"disabled_date": "2026-02-09"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": "scotthyoung",
|
|
161
|
+
"name": "Scott Young Blog",
|
|
162
|
+
"url": "https://scotthyoung.com/blog/articles",
|
|
163
|
+
"reason": "海外源 + 提取不稳定",
|
|
164
|
+
"disabled_date": "2026-02-09"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "producthunt",
|
|
168
|
+
"name": "Product Hunt",
|
|
169
|
+
"url": "https://www.producthunt.com",
|
|
170
|
+
"reason": "需要浏览器渲染",
|
|
171
|
+
"disabled_date": "2026-02-09"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "latent_space",
|
|
175
|
+
"name": "Latent Space",
|
|
176
|
+
"url": "https://www.latent.space",
|
|
177
|
+
"reason": "Substack 需要 JS 渲染",
|
|
178
|
+
"disabled_date": "2026-02-09"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"id": "tldr_ai",
|
|
182
|
+
"name": "TLDR AI",
|
|
183
|
+
"url": "https://tldr.tech/ai",
|
|
184
|
+
"reason": "订阅页面,无文章列表",
|
|
185
|
+
"disabled_date": "2026-01-21"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"id": "bensbites",
|
|
189
|
+
"name": "Ben's Bites",
|
|
190
|
+
"url": "https://bensbites.com/archive",
|
|
191
|
+
"reason": "需要登录/付费墙",
|
|
192
|
+
"disabled_date": "2026-01-21"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"id": "interconnects",
|
|
196
|
+
"name": "Interconnects AI",
|
|
197
|
+
"url": "https://interconnects.ai",
|
|
198
|
+
"reason": "内容提取失败,Substack 结构问题",
|
|
199
|
+
"disabled_date": "2026-01-21"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"id": "beehiiv_rss",
|
|
203
|
+
"name": "Beehiiv RSS feeds",
|
|
204
|
+
"url": "https://rss.beehiiv.com",
|
|
205
|
+
"reason": "RSS 抓取困难",
|
|
206
|
+
"disabled_date": "2026-01-21"
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
"fetch_config": {
|
|
213
|
+
"webfetch": {
|
|
214
|
+
"timeout_ms": 30000,
|
|
215
|
+
"retry_count": 1,
|
|
216
|
+
"cache_ttl_minutes": 60
|
|
217
|
+
},
|
|
218
|
+
"browser": {
|
|
219
|
+
"timeout_ms": 45000,
|
|
220
|
+
"wait_for_selector": "article, .post, .item",
|
|
221
|
+
"screenshot_on_error": true
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
"quality_thresholds": {
|
|
226
|
+
"min_score_to_include": 3,
|
|
227
|
+
"target_items": 20,
|
|
228
|
+
"early_stop_threshold": 25
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sophnet-schedule",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"types": [
|
|
5
|
+
"store"
|
|
6
|
+
],
|
|
7
|
+
"displayName": "日程管理助手",
|
|
8
|
+
"description": "支持本地托底与 Google Calendar 接入的日程管理 skill",
|
|
9
|
+
"changelog": [
|
|
10
|
+
{
|
|
11
|
+
"version": "1.0.0",
|
|
12
|
+
"date": "2026-04-14",
|
|
13
|
+
"changes": [
|
|
14
|
+
"初次提交"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"createdAt": "2026-04-14",
|
|
19
|
+
"updatedAt": "2026-04-14"
|
|
20
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Sophnet 日程提醒 — 架构设计
|
|
2
|
+
|
|
3
|
+
## 一、整体分层架构
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
7
|
+
│ 用户 / 前端 App │
|
|
8
|
+
│ 自然语言输入 │ 文件/截图上传 │ 日历视图(待建) │
|
|
9
|
+
└──────────────────────────┬──────────────────────────────────────┘
|
|
10
|
+
│ 对话消息 / 文件路径 │ HTTP REST
|
|
11
|
+
┌──────────────────────────▼──────────────────┐ ┌────▼──────────────────┐
|
|
12
|
+
│ Agent 层(SKILL.md) │ │ server.py(FastAPI) │
|
|
13
|
+
│ 解析用户意图 → 调用 app → 格式化结果回复 │ │ Sophnet 日程提醒 API │
|
|
14
|
+
└──────┬───────────────────┬──────────────────┘ └────┬──────────────────┘
|
|
15
|
+
│ 定时触发 │ 用户触发 │ 直接调用 services/
|
|
16
|
+
┌──────▼──────┐ ┌────────▼───────────────────────────▼──────────────────┐
|
|
17
|
+
│ OpenClaw │ │ apps/ 入口层 │
|
|
18
|
+
│ Cron 调度器 │ │ add_event │ import_events │ check_roc │ check_reminders│
|
|
19
|
+
│(jobs.json) │ │ generate_daily_plan │ monitor_calendar_changes │
|
|
20
|
+
└──────┬──────┘ └──────────────────────────┬──────────────────────────┘
|
|
21
|
+
│ │ 调用
|
|
22
|
+
┌──────▼───────────────────────────────────── ▼──────────────────────────┐
|
|
23
|
+
│ services/ 业务逻辑层 │
|
|
24
|
+
│ time_window │ event_classifier │ reminder_planner │ conflict_detector │
|
|
25
|
+
│ event_diff │ task_builder │ table_parser │
|
|
26
|
+
└──────────────────────────────────────────┬─────────────────────────────┘
|
|
27
|
+
│ 调用
|
|
28
|
+
┌──────────────────────────────────────────▼─────────────────────────────┐
|
|
29
|
+
│ gcal/ 接入层 │
|
|
30
|
+
│ client.py(OAuth2 + REST) │ models.py │
|
|
31
|
+
└──────────────────────────────────────────┬─────────────────────────────┘
|
|
32
|
+
│ HTTPS
|
|
33
|
+
┌──────────────────────────────────────────▼─────────────────────────────┐
|
|
34
|
+
│ Google Calendar API v3 │
|
|
35
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
36
|
+
|
|
37
|
+
配置层(只读) 状态层(读写)
|
|
38
|
+
config/settings.py data/last_events.json
|
|
39
|
+
config/reminder_rules.yaml data/roc_state.json
|
|
40
|
+
config/task_registry.yaml daily/cron-YYYYMMDD.md
|
|
41
|
+
config/roc_events.yaml ~/.openclaw/cron/jobs.json
|
|
42
|
+
api/models.py(API schema)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 二、各层职责
|
|
48
|
+
|
|
49
|
+
### 1. Agent 层(`SKILL.md`)
|
|
50
|
+
|
|
51
|
+
OpenClaw Agent 的行为规范文件。定义:
|
|
52
|
+
- 欢迎语和自动功能说明
|
|
53
|
+
- 17 个用户交互场景的触发词、处理步骤、响应格式
|
|
54
|
+
- 错误处理话术
|
|
55
|
+
|
|
56
|
+
**Agent 本身不包含业务逻辑**,只负责意图识别 → 调用 app → 格式化输出。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### 1.5 HTTP API 层(`server.py` + `api/models.py`)
|
|
61
|
+
|
|
62
|
+
为前端 App 提供 REST 接口,与 Agent 层**共用同一套** `services/` 和 `gcal/` 逻辑。
|
|
63
|
+
|
|
64
|
+
| 文件 | 职责 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| `server.py` | FastAPI 应用,18 条路由,API Key 认证,CORS |
|
|
67
|
+
| `api/models.py` | 所有接口的 Pydantic 请求/响应 schema |
|
|
68
|
+
|
|
69
|
+
**启动方式**:
|
|
70
|
+
```bash
|
|
71
|
+
# 在 skills/sophnet-schedule/src 目录下
|
|
72
|
+
API_KEY=your-secret uv run uvicorn server:app --host 0.0.0.0 --port 8765
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**接口速览**:
|
|
76
|
+
|
|
77
|
+
| 分组 | 方法 | 路径 | 说明 |
|
|
78
|
+
|------|------|------|------|
|
|
79
|
+
| 系统 | GET | `/health` | 健康检查 |
|
|
80
|
+
| 日历 | GET | `/events` | 查询事件(支持 `date` 或 `start/end`)|
|
|
81
|
+
| 日历 | POST | `/events` | 创建事件 + 可选注册提醒 |
|
|
82
|
+
| 日历 | DELETE | `/events/{id}` | 删除事件 |
|
|
83
|
+
| 日历 | GET | `/events/conflicts` | 冲突检测 |
|
|
84
|
+
| ROC | GET | `/roc/events` | ROC 事件清单(含今年日期)|
|
|
85
|
+
| ROC | POST | `/roc/events` | 新增 ROC 事件 |
|
|
86
|
+
| ROC | PUT | `/roc/events/{id}` | 修改 ROC 事件 |
|
|
87
|
+
| ROC | DELETE | `/roc/events/{id}` | 删除 ROC 事件 |
|
|
88
|
+
| ROC | GET | `/roc/upcoming` | 近期 ROC 提醒预览 |
|
|
89
|
+
| 提醒 | GET | `/reminders` | 窗口内即将触发的提醒 |
|
|
90
|
+
| 任务 | GET | `/tasks` | Cron 任务列表 |
|
|
91
|
+
| 任务 | POST | `/tasks/trigger/daily-plan` | 手动触发今日计划生成 |
|
|
92
|
+
| 日报 | GET | `/daily-plan` | 日报数据(含 Markdown 原文)|
|
|
93
|
+
|
|
94
|
+
Swagger UI:`http://host:8765/docs`
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### 2. Apps 层(`apps/`)
|
|
99
|
+
|
|
100
|
+
各功能的 CLI 入口,均输出 JSON,供 Agent 读取后格式化。
|
|
101
|
+
|
|
102
|
+
| 脚本 | 触发方式 | 职责 |
|
|
103
|
+
|------|---------|------|
|
|
104
|
+
| `generate_daily_plan.py` | Cron 07:50 / 用户手动 | 拉取今日 GCal 事件 → 冲突检测 → 注册提醒任务 → 生成日报 |
|
|
105
|
+
| `check_reminders.py` | Cron 每分钟 / 用户查询 | 检查指定时间窗口内即将触发的提醒 |
|
|
106
|
+
| `monitor_calendar_changes.py` | Cron 每小时 | 对比日程快照,有变化才输出,无变化静默 |
|
|
107
|
+
| `register_tasks.py` | 启动时 / 手动 | 读取 `task_registry.yaml`,写入 `jobs.json` |
|
|
108
|
+
| `add_event.py` | 用户自然语言输入 | 接收结构化参数,写入 Google Calendar,可选注册提醒 |
|
|
109
|
+
| `import_events.py` | 文件/OCR 导入 | 批量接收 JSON 事件列表,逐一写入 Google Calendar |
|
|
110
|
+
| `check_roc.py` | Cron 08:05 / 用户查询 | 读取 `roc_events.yaml`,计算今年日期,检查今日是否需要提醒 |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### 3. Services 层(`services/`)
|
|
115
|
+
|
|
116
|
+
纯业务逻辑,无 I/O 副作用,100% 单元可测试。
|
|
117
|
+
|
|
118
|
+
| 模块 | 职责 |
|
|
119
|
+
|------|------|
|
|
120
|
+
| `time_window.py` | 计算今日/明日的时间区间(UTC/本地时间转换)|
|
|
121
|
+
| `event_classifier.py` | 根据 `reminder_rules.yaml` 关键词将事件分类为 meeting/sport/meal |
|
|
122
|
+
| `reminder_planner.py` | 根据分类和规则生成 `ReminderPlan`(提醒时间 + 消息文本)|
|
|
123
|
+
| `conflict_detector.py` | 检测时间重叠(`⚠️ 冲突`)和紧接场景(`⚡ 紧接`,间隔 < 5 分钟)|
|
|
124
|
+
| `event_diff.py` | 对比两份事件列表,输出新增/取消/修改的差异 |
|
|
125
|
+
| `task_builder.py` | 构造 OpenClaw Cron 的 `job` payload(区分三种投递模式)|
|
|
126
|
+
| `table_parser.py` | 解析 Markdown 表格,自动识别 30+ 中英文列名别名,提取事件 |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 4. GCal 层(`gcal/`)
|
|
131
|
+
|
|
132
|
+
Google Calendar API 的统一访问封装。
|
|
133
|
+
|
|
134
|
+
| 模块 | 职责 |
|
|
135
|
+
|------|------|
|
|
136
|
+
| `client.py` | OAuth2 token 管理(自动刷新)、list_events / create_event / delete_event |
|
|
137
|
+
| `models.py` | `CalendarEvent` dataclass(屏蔽 API 原始 JSON 结构)|
|
|
138
|
+
|
|
139
|
+
Token 存于 `~/.config/gcalcli/token.json`,过期自动刷新写回,无需手动管理。
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### 5. 配置层(`config/`)
|
|
144
|
+
|
|
145
|
+
所有行为参数集中于此,**修改 YAML 无需改代码**。
|
|
146
|
+
|
|
147
|
+
| 文件 | 内容 |
|
|
148
|
+
|------|------|
|
|
149
|
+
| `settings.py` | 读取 `.env`,暴露 `SKILL_BASE / GCAL_TOKEN_PATH / PROXIES / TIMEZONE` 等路径和常量 |
|
|
150
|
+
| `reminder_rules.yaml` | 事件类型提前提醒分钟数 + 分类关键词 |
|
|
151
|
+
| `task_registry.yaml` | 全部固定重复任务注册表(7 条,含 ROC 检查)|
|
|
152
|
+
| `roc_events.yaml` | ROC 周期事件清单 + 春节日期(**前端 CRUD 的唯一数据源**)|
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### 6. 状态层(`data/`)
|
|
157
|
+
|
|
158
|
+
运行时读写的状态文件,不纳入 Git。
|
|
159
|
+
|
|
160
|
+
| 文件 | 内容 |
|
|
161
|
+
|------|------|
|
|
162
|
+
| `data/last_events.json` | 日程快照,`monitor_calendar_changes.py` 用于差异对比 |
|
|
163
|
+
| `data/roc_state.json` | 记录每个 ROC 事件最近一次已提醒的日期,防重复推送 |
|
|
164
|
+
| `daily/cron-YYYYMMDD.md` | 每日生成的任务日报(含今日日程 + 注册的 cron 任务列表)|
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 三、核心数据流
|
|
169
|
+
|
|
170
|
+
### 3.1 每日计划生成(07:50 自动)
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
OpenClaw Cron
|
|
174
|
+
→ generate_daily_plan.py
|
|
175
|
+
→ time_window.get_today_window()
|
|
176
|
+
→ gcal.client.list_events(start, end) # 拉取今日 GCal 事件
|
|
177
|
+
→ event_classifier.classify(events) # 分类
|
|
178
|
+
→ reminder_planner.plan(events, rules) # 计算提醒时间
|
|
179
|
+
→ conflict_detector.detect(events) # 冲突检测
|
|
180
|
+
→ task_builder.build_reminder_jobs(plans) # 构造 cron payload
|
|
181
|
+
→ register_tasks.write(jobs, jobs.json) # 写入 OpenClaw 调度器
|
|
182
|
+
→ 生成 daily/cron-YYYYMMDD.md # 日报落盘
|
|
183
|
+
→ OpenClaw announce → Telegram 推送
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3.2 自然语言写入日历(用户触发)
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
用户:"后天下午3点牙医预约,提前15分钟提醒"
|
|
190
|
+
→ Agent 解析日期时间 → 展示确认卡片
|
|
191
|
+
→ 用户确认
|
|
192
|
+
→ add_event.py --summary "牙医预约" --date ... --remind 15
|
|
193
|
+
→ gcal.client.create_event() # 写入 Google Calendar
|
|
194
|
+
→ task_builder.build_one_off_reminder() # 构造一次性提醒 payload
|
|
195
|
+
→ register_tasks.write(job, jobs.json) # 注册到 OpenClaw
|
|
196
|
+
→ Agent 回复成功消息
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 3.3 文件 / OCR 导入(用户触发)
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
用户上传 Excel / 截图
|
|
203
|
+
→ Agent 定位文件路径
|
|
204
|
+
→ markitdown <file> → /tmp/schedule_converted.md # 文档转 Markdown
|
|
205
|
+
→ table_parser.parse_events_from_markdown() # 提取结构化事件列表
|
|
206
|
+
→ Agent 展示确认表格
|
|
207
|
+
→ 用户确认
|
|
208
|
+
→ import_events.py --json '[...]'
|
|
209
|
+
→ gcal.client.create_event() × N # 批量写入 Google Calendar
|
|
210
|
+
→ Agent 回复写入汇总
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 3.4 ROC 周期事件检查(08:05 自动)
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
OpenClaw Cron
|
|
217
|
+
→ check_roc.py
|
|
218
|
+
→ 读取 config/roc_events.yaml # 事件清单
|
|
219
|
+
→ 按 rule 计算今年各事件日期 # 固定日期/春节/第N个周X
|
|
220
|
+
→ 对比今日 vs reminder_date # 是否到提醒日
|
|
221
|
+
→ 检查 data/roc_state.json # 防重复提醒
|
|
222
|
+
→ 输出 JSON(reminders 数组)
|
|
223
|
+
→ 写回 roc_state.json
|
|
224
|
+
→ Agent 读取 JSON → 格式化推送
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 四、OpenClaw Cron 投递模式
|
|
230
|
+
|
|
231
|
+
`task_builder.py` 强制三条路径,不可混用:
|
|
232
|
+
|
|
233
|
+
| 场景 | sessionTarget | payload.kind | delivery |
|
|
234
|
+
|------|--------------|--------------|----------|
|
|
235
|
+
| 会前/事前一次性提醒 | `main` | `systemEvent` | 不设置 |
|
|
236
|
+
| 周期静默监控(日程变化)| `isolated` | `agentTurn` | `none` |
|
|
237
|
+
| 日报/ROC 结果推送 | `isolated` | `agentTurn` | `announce` |
|
|
238
|
+
|
|
239
|
+
> **为什么提醒用 `systemEvent + main`**:`isolated + announce` 不支持 webchat 渠道,会报 `Unsupported channel`;`systemEvent + main` 直接注入主会话,所有渠道均可用。
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 五、前端扩展接入点
|
|
244
|
+
|
|
245
|
+
当前 skill 是纯后端(Bot + GCal),前端 App 接入时各数据接口已预留:
|
|
246
|
+
|
|
247
|
+
| 前端功能 | 接入方式 |
|
|
248
|
+
|---------|---------|
|
|
249
|
+
| 日/周/月日历视图 | 封装 `gcal.client.list_events()` 为 REST API |
|
|
250
|
+
| ROC 事件管理页 | CRUD `config/roc_events.yaml`(已是结构化 YAML,天然适合 API 包装)|
|
|
251
|
+
| 提醒状态查询 | 读取 `data/roc_state.json` |
|
|
252
|
+
| 系统推送通知 | App 层注册 APNs/FCM;skill 层已生成提醒时间,无需修改 |
|
|
253
|
+
| 日程操作同步 | `add_event.py` / `delete_event()` 均已支持,包一层 HTTP endpoint |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 六、目录速查
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
sophnet-schedule/src/
|
|
261
|
+
├── SKILL.md # Agent 行为规范(17 个交互场景)
|
|
262
|
+
├── server.py # HTTP API 服务(FastAPI,18 条路由)
|
|
263
|
+
│
|
|
264
|
+
├── api/ # API Schema 层
|
|
265
|
+
│ └── models.py # Pydantic 请求/响应模型
|
|
266
|
+
│
|
|
267
|
+
├── compat.py # Python 版本兼容补丁
|
|
268
|
+
│
|
|
269
|
+
├── gcal/ # Google Calendar 接入层
|
|
270
|
+
│ ├── client.py # OAuth2 + REST(list/create/delete)
|
|
271
|
+
│ └── models.py # CalendarEvent dataclass
|
|
272
|
+
│
|
|
273
|
+
├── services/ # 业务逻辑层(纯函数,无 I/O)
|
|
274
|
+
│ ├── time_window.py # 今日/明日时间区间计算
|
|
275
|
+
│ ├── event_classifier.py # 事件类型分类(meeting/sport/meal)
|
|
276
|
+
│ ├── reminder_planner.py # 提醒计划生成 + 消息格式化
|
|
277
|
+
│ ├── conflict_detector.py # 时间冲突 + 紧接检测
|
|
278
|
+
│ ├── event_diff.py # 日程变化对比
|
|
279
|
+
│ ├── task_builder.py # OpenClaw Cron payload 构造
|
|
280
|
+
│ └── table_parser.py # Markdown 表格 → 事件列表(列名别名识别)
|
|
281
|
+
│
|
|
282
|
+
├── apps/ # CLI 入口层(输出 JSON)
|
|
283
|
+
│ ├── generate_daily_plan.py # 每日计划生成(核心入口)
|
|
284
|
+
│ ├── check_reminders.py # 即时提醒窗口检查
|
|
285
|
+
│ ├── monitor_calendar_changes.py # 日程变化监控
|
|
286
|
+
│ ├── register_tasks.py # 任务注册到 jobs.json
|
|
287
|
+
│ ├── add_event.py # 单条事件写入 GCal
|
|
288
|
+
│ ├── import_events.py # 批量事件写入 GCal
|
|
289
|
+
│ └── check_roc.py # ROC 周期事件提醒检查
|
|
290
|
+
│
|
|
291
|
+
├── config/ # 配置层(改 YAML 不改代码)
|
|
292
|
+
│ ├── settings.py # 路径 + 环境变量读取
|
|
293
|
+
│ ├── reminder_rules.yaml # 提醒规则 + 分类关键词
|
|
294
|
+
│ ├── task_registry.yaml # 固定重复任务注册表
|
|
295
|
+
│ └── roc_events.yaml # ROC 周期事件清单
|
|
296
|
+
│
|
|
297
|
+
├── data/ # 运行时状态(不纳入 Git)
|
|
298
|
+
│ ├── last_events.json # 日程快照(监控用)
|
|
299
|
+
│ └── roc_state.json # ROC 已提醒记录(防重复)
|
|
300
|
+
│
|
|
301
|
+
├── daily/ # 每日自动生成的日报
|
|
302
|
+
│ └── cron-YYYYMMDD.md
|
|
303
|
+
│
|
|
304
|
+
├── scripts/
|
|
305
|
+
│ └── setup_gcal_token.py # Google OAuth 初始化工具
|
|
306
|
+
│
|
|
307
|
+
├── tests/ # 单元测试(8 个文件,121 个用例)
|
|
308
|
+
│ ├── test_time_window.py
|
|
309
|
+
│ ├── test_event_classifier.py
|
|
310
|
+
│ ├── test_reminder_planner.py
|
|
311
|
+
│ ├── test_conflict_detector.py
|
|
312
|
+
│ ├── test_event_diff.py
|
|
313
|
+
│ ├── test_task_builder.py
|
|
314
|
+
│ ├── test_add_event.py
|
|
315
|
+
│ ├── test_table_parser.py
|
|
316
|
+
│ └── test_check_roc.py
|
|
317
|
+
│
|
|
318
|
+
└── docs/
|
|
319
|
+
├── GOOGLE_CALENDAR_SETUP.md # OAuth 配置指南
|
|
320
|
+
└── TESTING_GUIDE.md # 测试运行指南
|
|
321
|
+
```
|