sophhub 0.1.0

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.
Files changed (125) hide show
  1. package/bin/sophhub.js +21 -0
  2. package/package.json +32 -0
  3. package/skills/VERSIONS.md +27 -0
  4. package/skills/builtin/clawhub/SKILL.md +77 -0
  5. package/skills/builtin/flight-booking/SKILL.md +288 -0
  6. package/skills/builtin/flight-booking/scripts/flight_booking.py +1232 -0
  7. package/skills/builtin/inventory-management/SKILL.md +241 -0
  8. package/skills/builtin/inventory-management/scripts/inventory.py +1844 -0
  9. package/skills/builtin/schedule-reminder/SKILL.md +619 -0
  10. package/skills/builtin/schedule-reminder/schedule_template.md +68 -0
  11. package/skills/builtin/schedule-reminder/scripts/append_event.py +204 -0
  12. package/skills/builtin/schedule-reminder/scripts/create_reminders.sh +163 -0
  13. package/skills/builtin/schedule-reminder/scripts/daily_activate.sh +175 -0
  14. package/skills/builtin/schedule-reminder/scripts/parse_schedule.py +704 -0
  15. package/skills/builtin/schedule-reminder/scripts/setup.sh +242 -0
  16. package/skills/builtin/schedule-reminder//347/224/250/346/210/267/346/214/207/345/215/227.md +311 -0
  17. package/skills/builtin/skill-creator/SKILL.md +370 -0
  18. package/skills/builtin/skill-creator/license.txt +202 -0
  19. package/skills/builtin/skill-creator/scripts/init_skill.py +378 -0
  20. package/skills/builtin/skill-creator/scripts/package_skill.py +111 -0
  21. package/skills/builtin/skill-creator/scripts/quick_validate.py +101 -0
  22. package/skills/builtin/sophnet-customer-management/SKILL.md +271 -0
  23. package/skills/builtin/sophnet-customer-management/pyproject.toml +15 -0
  24. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/__init__.py +2 -0
  25. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/__main__.py +5 -0
  26. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/cli.py +67 -0
  27. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/__init__.py +2 -0
  28. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/customer.py +60 -0
  29. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/export_file.py +18 -0
  30. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/import_file.py +15 -0
  31. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/reminder.py +26 -0
  32. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/commands/schema.py +28 -0
  33. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_cli/config.py +54 -0
  34. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/__init__.py +2 -0
  35. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/exporter.py +85 -0
  36. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/models.py +84 -0
  37. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/normalizer.py +144 -0
  38. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/parser.py +241 -0
  39. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/query.py +109 -0
  40. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/reminder.py +121 -0
  41. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/repository.py +397 -0
  42. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/schema.py +106 -0
  43. package/skills/builtin/sophnet-customer-management/src/customer_mgmt_core/service.py +565 -0
  44. package/skills/builtin/sophnet-customer-management/uv.lock +48 -0
  45. package/skills/builtin/sophnet-customized-marketing/SKILL.md +144 -0
  46. package/skills/builtin/sophnet-customized-marketing/playbooks/campaign-planning.md +187 -0
  47. package/skills/builtin/sophnet-customized-marketing/playbooks/content-generation.md +124 -0
  48. package/skills/builtin/sophnet-customized-marketing/playbooks/marketing-calendar.md +59 -0
  49. package/skills/builtin/sophnet-customized-marketing/playbooks/multi-channel-bundle.md +94 -0
  50. package/skills/builtin/sophnet-customized-marketing/playbooks/poster-generation.md +182 -0
  51. package/skills/builtin/sophnet-customized-marketing/playbooks/style-profile-workflow.md +103 -0
  52. package/skills/builtin/sophnet-customized-marketing/pyproject.toml +9 -0
  53. package/skills/builtin/sophnet-customized-marketing/references/campaign-mechanics.md +168 -0
  54. package/skills/builtin/sophnet-customized-marketing/references/content-safety.md +26 -0
  55. package/skills/builtin/sophnet-customized-marketing/references/marketing-date-checklist.md +99 -0
  56. package/skills/builtin/sophnet-customized-marketing/references/platform-writing-guidelines.md +88 -0
  57. package/skills/builtin/sophnet-customized-marketing/references/quality-checklist.md +44 -0
  58. package/skills/builtin/sophnet-customized-marketing/scripts/generate_poster.py +585 -0
  59. package/skills/builtin/sophnet-customized-marketing/scripts/style_profile.py +215 -0
  60. package/skills/builtin/sophnet-face-search/SKILL.md +115 -0
  61. package/skills/builtin/sophnet-face-search/pyproject.toml +11 -0
  62. package/skills/builtin/sophnet-face-search/scripts/face_search.py +336 -0
  63. package/skills/builtin/sophnet-face-search/uv.lock +508 -0
  64. package/skills/builtin/sophnet-image-edit/SKILL.md +140 -0
  65. package/skills/builtin/sophnet-image-edit/pyproject.toml +9 -0
  66. package/skills/builtin/sophnet-image-edit/scripts/edit_and_preview.sh +68 -0
  67. package/skills/builtin/sophnet-image-edit/scripts/edit_image.py +279 -0
  68. package/skills/builtin/sophnet-image-edit/uv.lock +234 -0
  69. package/skills/builtin/sophnet-image-generate/SKILL.md +62 -0
  70. package/skills/builtin/sophnet-image-generate/pyproject.toml +9 -0
  71. package/skills/builtin/sophnet-image-generate/scripts/generate_image.py +156 -0
  72. package/skills/builtin/sophnet-image-generate/uv.lock +234 -0
  73. package/skills/builtin/sophnet-image-ocr/SKILL.md +167 -0
  74. package/skills/builtin/sophnet-image-ocr/pyproject.toml +13 -0
  75. package/skills/builtin/sophnet-image-ocr/scripts/ocr.py +226 -0
  76. package/skills/builtin/sophnet-image-ocr/uv.lock +234 -0
  77. package/skills/builtin/sophnet-infinite-talk/SKILL.md +140 -0
  78. package/skills/builtin/sophnet-infinite-talk/pyproject.toml +9 -0
  79. package/skills/builtin/sophnet-infinite-talk/scripts/gen.py +172 -0
  80. package/skills/builtin/sophnet-oss/SKILL.md +109 -0
  81. package/skills/builtin/sophnet-oss/pyproject.toml +8 -0
  82. package/skills/builtin/sophnet-oss/scripts/upload_file.py +43 -0
  83. package/skills/builtin/sophnet-qa-install/SKILL.md +210 -0
  84. package/skills/builtin/sophnet-qa-install/pyproject.toml +6 -0
  85. package/skills/builtin/sophnet-qa-install/scripts/backup_md.py +35 -0
  86. package/skills/builtin/sophnet-qa-install/scripts/check_installed.py +143 -0
  87. package/skills/builtin/sophnet-qa-install/scripts/update_config.py +142 -0
  88. package/skills/builtin/sophnet-qa-install/scripts/update_md.py +73 -0
  89. package/skills/builtin/sophnet-training-install/SKILL.md +211 -0
  90. package/skills/builtin/sophnet-training-install/pyproject.toml +6 -0
  91. package/skills/builtin/sophnet-training-install/scripts/backup_md.py +35 -0
  92. package/skills/builtin/sophnet-training-install/scripts/check_installed.py +144 -0
  93. package/skills/builtin/sophnet-training-install/scripts/update_config.py +142 -0
  94. package/skills/builtin/sophnet-training-install/scripts/update_md.py +73 -0
  95. package/skills/builtin/sophnet-tts/SKILL.md +79 -0
  96. package/skills/builtin/sophnet-tts/pyproject.toml +9 -0
  97. package/skills/builtin/sophnet-tts/scripts/gen_tts.py +130 -0
  98. package/skills/builtin/sophnet-video-generate/SKILL.md +116 -0
  99. package/skills/builtin/sophnet-video-generate/scripts/gen_video.py +304 -0
  100. package/skills/builtin/video-understand/SKILL.md +79 -0
  101. package/skills/builtin/video-understand/scripts/video_understand.py +204 -0
  102. package/skills/builtin/weather/SKILL.md +112 -0
  103. package/skills/builtin/web-scraper/SKILL.md +101 -0
  104. package/skills/builtin/web-scraper/scripts/scrape.py +270 -0
  105. package/skills/builtin/website-builder/SKILL.md +266 -0
  106. package/skills/builtin/website-builder/scripts/deploy_site.sh +46 -0
  107. package/skills/store/didi-ride/SKILL.md +309 -0
  108. package/skills/store/didi-ride/_meta.json +6 -0
  109. package/skills/store/didi-ride/assets/PREFERENCE.md +58 -0
  110. package/skills/store/didi-ride/package.json +15 -0
  111. package/skills/store/didi-ride/references/api_references.md +171 -0
  112. package/skills/store/didi-ride/references/error_handling.md +68 -0
  113. package/skills/store/didi-ride/references/setup.md +73 -0
  114. package/skills/store/didi-ride/references/workflow.md +150 -0
  115. package/skills/store/flyai/SKILL.md +119 -0
  116. package/skills/store/flyai/references/fliggy-fast-search.md +53 -0
  117. package/skills/store/flyai/references/search-flight.md +89 -0
  118. package/skills/store/flyai/references/search-hotels.md +57 -0
  119. package/skills/store/flyai/references/search-poi.md +49 -0
  120. package/src/commands/download.js +103 -0
  121. package/src/commands/list.js +67 -0
  122. package/src/utils/config.js +24 -0
  123. package/src/utils/gitlab.js +67 -0
  124. package/src/utils/paths.js +19 -0
  125. package/src/utils/versions.js +38 -0
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ append_event.py - 将一条新事项追加到 Markdown 日程文件
4
+
5
+ 用法:
6
+ python3 append_event.py \
7
+ --file /home/node/.openclaw/workspace/日程.md \
8
+ --date 2026-03-03 \
9
+ --start 15:00 \
10
+ --end 16:00 \
11
+ --title "客户会议" \
12
+ [--location "线上"] \
13
+ [--participants "张三"] \
14
+ [--priority 中] \
15
+ [--advance 15] \
16
+ [--note "带方案"] \
17
+ [--type task] # task = 写入待提醒任务表,默认写入主日程表
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import sys
22
+ import os
23
+ import re
24
+ import argparse
25
+ from datetime import datetime, date
26
+ from typing import Optional
27
+
28
+
29
+ def parse_args():
30
+ p = argparse.ArgumentParser(description="追加日程事项到 Markdown 文件")
31
+ p.add_argument("--file", required=True, help="日程 Markdown 文件路径")
32
+ p.add_argument("--date", required=True, help="日期 YYYY-MM-DD")
33
+ p.add_argument("--start", default="", help="开始时间 HH:MM")
34
+ p.add_argument("--end", default="", help="结束时间 HH:MM")
35
+ p.add_argument("--title", required=True, help="事项名称")
36
+ p.add_argument("--location", default="", help="地点")
37
+ p.add_argument("--participants", default="", help="参与者")
38
+ p.add_argument("--priority", default="中", choices=["高", "中", "低"], help="优先级")
39
+ p.add_argument("--advance", default="15", help="提前提醒分钟数")
40
+ p.add_argument("--note", default="", help="备注")
41
+ p.add_argument("--type", default="schedule", choices=["schedule", "task"],
42
+ help="写入主日程表(schedule)还是待提醒任务表(task)")
43
+ return p.parse_args()
44
+
45
+
46
+ def expand_path(path: str) -> str:
47
+ return os.path.expanduser(path)
48
+
49
+
50
+ def read_file(path: str) -> str:
51
+ if not os.path.exists(path):
52
+ return ""
53
+ with open(path, encoding="utf-8") as f:
54
+ return f.read()
55
+
56
+
57
+ def write_file(path: str, content: str):
58
+ os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
59
+ with open(path, "w", encoding="utf-8") as f:
60
+ f.write(content)
61
+
62
+
63
+ def make_schedule_row(args) -> str:
64
+ """生成主日程表的一行"""
65
+ advance = args.advance if args.advance else "15"
66
+ return (
67
+ f"| {args.date} | {args.start} | {args.end} | {args.title} "
68
+ f"| {args.location} | {args.participants} | {args.priority} "
69
+ f"| {advance} | {args.note} |"
70
+ )
71
+
72
+
73
+ def make_task_row(args) -> str:
74
+ """生成待提醒任务表的一行"""
75
+ # 任务表:截止日期 | 事项 | 负责人 | 优先级 | 提前提醒 | 备注
76
+ advance_days = ""
77
+ try:
78
+ adv_min = int(args.advance)
79
+ if adv_min >= 1440:
80
+ days = adv_min // 1440
81
+ advance_days = f"提前{days}天"
82
+ else:
83
+ advance_days = str(adv_min)
84
+ except ValueError:
85
+ advance_days = args.advance
86
+
87
+ return (
88
+ f"| {args.date} | {args.title} | {args.participants} "
89
+ f"| {args.priority} | {advance_days} | {args.note} |"
90
+ )
91
+
92
+
93
+ SCHEDULE_HEADER = "| 日期 | 开始时间 | 结束时间 | 事项"
94
+ SCHEDULE_HEADER_ALT = "| 日期 | 开始时间"
95
+ TASK_HEADER = "| 截止日期"
96
+
97
+ SCHEDULE_TABLE_BLOCK = """\
98
+ ## 日程
99
+
100
+ | 日期 | 开始时间 | 结束时间 | 事项 | 地点 | 参与者 | 优先级 | 提前(分钟) | 备注 |
101
+ |------------|----------|----------|--------------|------|--------|--------|------------|------|
102
+ """
103
+
104
+ TASK_TABLE_BLOCK = """\
105
+ ## 待提醒任务
106
+
107
+ | 截止日期 | 事项 | 负责人 | 优先级 | 提前提醒 | 备注 |
108
+ |------------|------|--------|--------|----------|------|
109
+ """
110
+
111
+ DEFAULT_FILE_TEMPLATE = """\
112
+ <!-- skill: schedule-reminder -->
113
+ # 我的日程
114
+
115
+ > 提醒渠道:last
116
+ > 默认提前提醒:15
117
+
118
+ {schedule_block}
119
+ {task_block}"""
120
+
121
+
122
+ def find_table_end(lines: list, start_idx: int) -> int:
123
+ """从 start_idx 往下找到表格结束位置(返回最后一行表格的 index + 1)"""
124
+ i = start_idx
125
+ last_table_line = start_idx
126
+ while i < len(lines):
127
+ stripped = lines[i].strip()
128
+ if stripped.startswith("|"):
129
+ last_table_line = i
130
+ elif stripped == "" and i > start_idx:
131
+ # 空行可能是表格后的分隔,继续看下一行
132
+ if i + 1 < len(lines) and not lines[i + 1].strip().startswith("|"):
133
+ break
134
+ elif not stripped.startswith("|") and stripped != "":
135
+ break
136
+ i += 1
137
+ return last_table_line + 1
138
+
139
+
140
+ def insert_row_into_table(content: str, new_row: str, header_pattern: str) -> Optional[str]:
141
+ """在找到的表格末尾插入新行,返回修改后的内容;找不到返回 None"""
142
+ lines = content.splitlines(keepends=True)
143
+ header_idx = None
144
+ for i, line in enumerate(lines):
145
+ if line.strip().startswith(header_pattern.strip()):
146
+ header_idx = i
147
+ break
148
+
149
+ if header_idx is None:
150
+ return None
151
+
152
+ end_idx = find_table_end(lines, header_idx)
153
+ lines.insert(end_idx, new_row + "\n")
154
+ return "".join(lines)
155
+
156
+
157
+ def append_to_file(file_path: str, args) -> str:
158
+ """主逻辑:读取文件 → 找表格 → 插入新行 → 写回"""
159
+ content = read_file(file_path)
160
+
161
+ if args.type == "task":
162
+ new_row = make_task_row(args)
163
+ updated = insert_row_into_table(content, new_row, "| 截止日期")
164
+ if updated is None:
165
+ # 没有任务表,追加一个
166
+ if content and not content.endswith("\n"):
167
+ content += "\n"
168
+ content += "\n" + TASK_TABLE_BLOCK + new_row + "\n"
169
+ updated = content
170
+ else:
171
+ new_row = make_schedule_row(args)
172
+ updated = insert_row_into_table(content, new_row, "| 日期")
173
+ if updated is None:
174
+ # 没有日程表,追加一个
175
+ if not content:
176
+ # 文件为空,创建完整模板
177
+ updated = DEFAULT_FILE_TEMPLATE.format(
178
+ schedule_block=SCHEDULE_TABLE_BLOCK + new_row,
179
+ task_block=TASK_TABLE_BLOCK,
180
+ )
181
+ else:
182
+ if not content.endswith("\n"):
183
+ content += "\n"
184
+ content += "\n" + SCHEDULE_TABLE_BLOCK + new_row + "\n"
185
+ updated = content
186
+
187
+ return updated
188
+
189
+
190
+ def main():
191
+ args = parse_args()
192
+ file_path = expand_path(args.file)
193
+
194
+ updated_content = append_to_file(file_path, args)
195
+ write_file(file_path, updated_content)
196
+
197
+ # 输出确认信息
198
+ event_type = "任务" if args.type == "task" else "日程"
199
+ time_str = f"{args.start}" + (f" – {args.end}" if args.end else "")
200
+ print(f"✅ 已写入{event_type}:{args.date} {time_str} {args.title} → {file_path}")
201
+
202
+
203
+ if __name__ == "__main__":
204
+ main()
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env bash
2
+ # create_reminders.sh - 从解析结果 JSON 批量创建 cron 提醒任务
3
+ #
4
+ # 用法:
5
+ # bash create_reminders.sh \
6
+ # --events-json /tmp/schedule_upcoming.json \
7
+ # --channel dingtalk \
8
+ # --to "user_id_or_sender"
9
+ #
10
+ # 环境变量(可覆盖):
11
+ # SCHEDULE_CHANNEL - 推送渠道(dingtalk / feishu / wecom / telegram)
12
+ # SCHEDULE_TO - 接收者 ID
13
+ # SCHEDULE_TZ - 时区(默认 Asia/Shanghai)
14
+
15
+ set -euo pipefail
16
+
17
+ # ── 参数解析 ──────────────────────────────────────────────
18
+ EVENTS_JSON=""
19
+ CHANNEL="${SCHEDULE_CHANNEL:-}"
20
+ TO="${SCHEDULE_TO:-}"
21
+ TZ="${SCHEDULE_TZ:-Asia/Shanghai}"
22
+ DRY_RUN=0
23
+
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --events-json) EVENTS_JSON="$2"; shift 2 ;;
27
+ --channel) CHANNEL="$2"; shift 2 ;;
28
+ --to) TO="$2"; shift 2 ;;
29
+ --tz) TZ="$2"; shift 2 ;;
30
+ --dry-run) DRY_RUN=1; shift ;;
31
+ *) echo "未知参数: $1" >&2; exit 1 ;;
32
+ esac
33
+ done
34
+
35
+ # ── 前置检查 ──────────────────────────────────────────────
36
+ if [[ -z "$EVENTS_JSON" ]]; then
37
+ echo "错误:请指定 --events-json <file>" >&2
38
+ exit 1
39
+ fi
40
+
41
+ if [[ ! -f "$EVENTS_JSON" ]]; then
42
+ echo "错误:文件不存在: $EVENTS_JSON" >&2
43
+ exit 1
44
+ fi
45
+
46
+ if ! command -v jq &>/dev/null; then
47
+ echo "错误:需要安装 jq" >&2
48
+ exit 1
49
+ fi
50
+
51
+ if ! command -v openclaw &>/dev/null; then
52
+ echo "错误:openclaw 命令未找到" >&2
53
+ exit 1
54
+ fi
55
+
56
+ # ── 读取事项列表 ──────────────────────────────────────────
57
+ COUNT=$(jq '.upcoming | length' "$EVENTS_JSON")
58
+
59
+ if [[ "$COUNT" -eq 0 ]]; then
60
+ echo "✅ 没有需要创建提醒的事项。"
61
+ exit 0
62
+ fi
63
+
64
+ echo "📅 共 $COUNT 个事项需要创建提醒(渠道:$CHANNEL)"
65
+ echo ""
66
+
67
+ # ── 构建提醒消息 ──────────────────────────────────────────
68
+ build_message() {
69
+ local title="$1"
70
+ local start_str="$2"
71
+ local end_str="$3"
72
+ local location="$4"
73
+ local participants="$5"
74
+ local note="$6"
75
+ local advance="$7"
76
+
77
+ local msg="⏰ 日程提醒\n\n📌 【${title}】即将在 ${advance} 分钟后开始\n🕐 ${start_str}"
78
+ [[ -n "$end_str" ]] && msg="${msg} – ${end_str}"
79
+ [[ -n "$location" ]] && msg="${msg}\n📍 ${location}"
80
+ [[ -n "$participants" ]] && msg="${msg}\n👥 ${participants}"
81
+ [[ -n "$note" ]] && msg="${msg}\n📝 ${note}"
82
+ echo "$msg"
83
+ }
84
+
85
+ # ── 遍历事项并创建 cron 任务 ─────────────────────────────
86
+ CREATED=0
87
+ SKIPPED=0
88
+ FAILED=0
89
+
90
+ for i in $(seq 0 $((COUNT - 1))); do
91
+ ev=$(jq ".upcoming[$i]" "$EVENTS_JSON")
92
+
93
+ title=$(echo "$ev" | jq -r '.title')
94
+ date_str=$(echo "$ev" | jq -r '.date')
95
+ start_str=$(echo "$ev" | jq -r '.start_str')
96
+ end_str=$(echo "$ev" | jq -r '.end_str')
97
+ location=$(echo "$ev" | jq -r '.location // ""')
98
+ participants=$(echo "$ev" | jq -r '.participants // ""')
99
+ note=$(echo "$ev" | jq -r '.note // ""')
100
+ advance=$(echo "$ev" | jq -r '.advance_min')
101
+ remind_cron=$(echo "$ev" | jq -r '.remind_cron // ""')
102
+ remind_time=$(echo "$ev" | jq -r '.remind_time_str // ""')
103
+ priority=$(echo "$ev" | jq -r '.priority')
104
+
105
+ # 没有 cron 表达式则跳过
106
+ if [[ -z "$remind_cron" || "$remind_cron" == "null" ]]; then
107
+ echo " ⏭️ 跳过(无时间信息):${title}"
108
+ ((SKIPPED++)) || true
109
+ continue
110
+ fi
111
+
112
+ # 构建任务名(避免特殊字符)
113
+ safe_title=$(echo "$title" | tr ' 【】《》""''()()' '_' | tr -cd '[:alnum:]_-' | cut -c1-40)
114
+ task_name="schedule-${date_str}-${safe_title}"
115
+
116
+ # 构建提醒消息
117
+ msg=$(build_message "$title" "$start_str" "$end_str" "$location" "$participants" "$note" "$advance")
118
+
119
+ echo " ➕ [${priority}] ${title}"
120
+ echo " 提醒时刻:${remind_time}(提前 ${advance} 分钟)"
121
+ echo " Cron:${remind_cron}"
122
+
123
+ if [[ "$DRY_RUN" -eq 1 ]]; then
124
+ echo " [DRY-RUN] 跳过实际创建"
125
+ ((CREATED++)) || true
126
+ continue
127
+ fi
128
+
129
+ # 构建 openclaw cron 命令
130
+ # 使用 --system-event + --session main,触发 main session 直接显示提醒消息,
131
+ # 不走 announce 推送流程,兼容 webchat/控制台等所有渠道,无需渠道配置。
132
+ CRON_CMD=(
133
+ openclaw cron add
134
+ --name "$task_name"
135
+ --cron "$remind_cron"
136
+ --tz "$TZ"
137
+ --session main
138
+ --system-event "$msg"
139
+ --delete-after-run
140
+ )
141
+
142
+ if "${CRON_CMD[@]}" 2>/dev/null; then
143
+ echo " ✅ 已创建"
144
+ ((CREATED++)) || true
145
+ else
146
+ echo " ❌ 创建失败" >&2
147
+ ((FAILED++)) || true
148
+ fi
149
+
150
+ echo ""
151
+ done
152
+
153
+ # ── 汇总 ─────────────────────────────────────────────────
154
+ echo "────────────────────────────────"
155
+ echo "✅ 成功创建:${CREATED} 个提醒任务"
156
+ [[ "$SKIPPED" -gt 0 ]] && echo "⏭️ 跳过:${SKIPPED} 个"
157
+ [[ "$FAILED" -gt 0 ]] && echo "❌ 失败:${FAILED} 个"
158
+ echo ""
159
+ echo "查看所有提醒任务:"
160
+ echo " openclaw cron list"
161
+ echo ""
162
+ echo "删除指定任务:"
163
+ echo " openclaw cron remove <task-id>"
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env bash
2
+ # daily_activate.sh - 每天自动激活:读取日程文件 → 解析今日事项 → 创建提醒 → 发日报
3
+ #
4
+ # 由 setup.sh 注册为每日定时任务,无需用户手动触发。
5
+ #
6
+ # 依赖:
7
+ # ~/.config/schedule-reminder/config.env (由 setup.sh 写入)
8
+ # python3, jq, openclaw
9
+
10
+ set -euo pipefail
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ CONFIG_FILE="${HOME}/.config/schedule-reminder/config.env"
14
+
15
+ # ── 读取配置 ──────────────────────────────────────────────
16
+ if [[ ! -f "$CONFIG_FILE" ]]; then
17
+ echo "[schedule-reminder] 错误:配置文件不存在,请先运行 setup.sh" >&2
18
+ exit 1
19
+ fi
20
+
21
+ # shellcheck source=/dev/null
22
+ source "$CONFIG_FILE"
23
+
24
+ # 必填变量检查
25
+ : "${SCHEDULE_FILE:?'请在 config.env 中设置 SCHEDULE_FILE'}"
26
+ : "${SCHEDULE_TZ:=Asia/Shanghai}"
27
+ : "${SCHEDULE_ADVANCE_MIN:=15}"
28
+ : "${SCHEDULE_DAILY_HOUR:=7}"
29
+ : "${SCHEDULE_DAILY_MIN:=0}"
30
+ : "${SCHEDULE_SEND_SUMMARY:=true}"
31
+ # SCHEDULE_CHANNEL / SCHEDULE_RECIPIENT 为可选,留空则 cron 默认回到上次对话渠道(channel=last)
32
+ SCHEDULE_CHANNEL="${SCHEDULE_CHANNEL:-}"
33
+ SCHEDULE_RECIPIENT="${SCHEDULE_RECIPIENT:-}"
34
+
35
+ TODAY=$(TZ="$SCHEDULE_TZ" date +%Y-%m-%d)
36
+ NOW=$(TZ="$SCHEDULE_TZ" date +"%Y-%m-%d %H:%M")
37
+ LOG_DIR="${HOME}/.config/schedule-reminder/logs"
38
+ mkdir -p "$LOG_DIR"
39
+ LOG_FILE="${LOG_DIR}/activate-${TODAY}.log"
40
+
41
+ log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
42
+
43
+ log "===== 日程提醒 每日激活 ====="
44
+ log "日期:$TODAY 渠道:${SCHEDULE_CHANNEL:-last} 接收者:${SCHEDULE_RECIPIENT:-(上次对话用户)}"
45
+
46
+ # ── 展开文件路径 ──────────────────────────────────────────
47
+ SCHEDULE_FILE_EXPANDED="${SCHEDULE_FILE/#\~/$HOME}"
48
+
49
+ if [[ ! -f "$SCHEDULE_FILE_EXPANDED" ]]; then
50
+ MSG="⚠️ 日程文件未找到:${SCHEDULE_FILE}\n请检查文件路径是否正确。"
51
+ SEND_CMD=(openclaw message send --message "$MSG")
52
+ [[ -n "$SCHEDULE_CHANNEL" ]] && SEND_CMD+=(--channel "$SCHEDULE_CHANNEL")
53
+ [[ -n "$SCHEDULE_RECIPIENT" ]] && SEND_CMD+=(--to "$SCHEDULE_RECIPIENT")
54
+ "${SEND_CMD[@]}" 2>>"$LOG_FILE" || true
55
+ log "错误:日程文件不存在:$SCHEDULE_FILE_EXPANDED"
56
+ exit 1
57
+ fi
58
+
59
+ # ── 解析日程 ──────────────────────────────────────────────
60
+ PARSE_OUT="/tmp/schedule_parsed_${TODAY}.json"
61
+
62
+ python3 "${SCRIPT_DIR}/parse_schedule.py" \
63
+ --file "$SCHEDULE_FILE_EXPANDED" \
64
+ --mode today \
65
+ --now "$NOW" \
66
+ > "$PARSE_OUT" 2>>"$LOG_FILE"
67
+
68
+ UPCOMING_COUNT=$(jq '.summary.upcoming_count' "$PARSE_OUT")
69
+ CONFLICT_COUNT=$(jq '.summary.conflict_count' "$PARSE_OUT")
70
+
71
+ log "解析完成:today=$UPCOMING_COUNT 个事项,冲突=$CONFLICT_COUNT"
72
+
73
+ # ── 过滤掉「提醒时刻已过」的事项 ─────────────────────────
74
+ NOW_MIN=$(TZ="$SCHEDULE_TZ" date +%H:%M | awk -F: '{print $1*60+$2}')
75
+ FUTURE_EVENTS=$(jq --argjson now_min "$NOW_MIN" '
76
+ .upcoming | map(select(
77
+ (.remind_cron != null) and (.start_min != null) and (.start_min > $now_min)
78
+ ))
79
+ ' "$PARSE_OUT")
80
+
81
+ FUTURE_COUNT=$(echo "$FUTURE_EVENTS" | jq 'length')
82
+ log "有效(未过期)提醒事项:$FUTURE_COUNT 个"
83
+
84
+ # ── 构建今日日报消息 ──────────────────────────────────────
85
+ build_summary() {
86
+ local date_cn
87
+ date_cn=$(TZ="$SCHEDULE_TZ" date +"%m月%d日")
88
+
89
+ local lines="📅 今日日程 · ${date_cn}\n\n"
90
+
91
+ if [[ "$FUTURE_COUNT" -eq 0 && "$UPCOMING_COUNT" -eq 0 ]]; then
92
+ lines+="今日没有日程安排,好好休息!\n"
93
+ else
94
+ # 有效提醒列表
95
+ if [[ "$FUTURE_COUNT" -gt 0 ]]; then
96
+ lines+="待提醒事项(共 ${FUTURE_COUNT} 个):\n"
97
+ while IFS= read -r ev; do
98
+ local title start_str end_str location advance remind_str priority_icon
99
+ title=$(echo "$ev" | jq -r '.title')
100
+ start_str=$(echo "$ev" | jq -r '.start_str')
101
+ end_str=$(echo "$ev" | jq -r '.end_str')
102
+ location=$(echo "$ev" | jq -r '.location // ""')
103
+ advance=$(echo "$ev" | jq -r '.advance_min')
104
+ remind_str=$(echo "$ev" | jq -r '.remind_time_str // ""')
105
+ local priority
106
+ priority=$(echo "$ev" | jq -r '.priority')
107
+ case "$priority" in
108
+ 高) priority_icon="🔴" ;;
109
+ 中) priority_icon="🟡" ;;
110
+ 低) priority_icon="⚪" ;;
111
+ *) priority_icon="🔵" ;;
112
+ esac
113
+
114
+ lines+=" ${priority_icon} ${start_str}–${end_str} 【${title}】"
115
+ [[ -n "$location" ]] && lines+=" @ ${location}"
116
+ [[ -n "$remind_str" ]] && lines+="(⏰ ${remind_str} 提醒,提前${advance}分钟)"
117
+ lines+="\n"
118
+ done < <(echo "$FUTURE_EVENTS" | jq -c '.[]')
119
+ fi
120
+
121
+ # 已过期事项说明
122
+ local expired_count
123
+ expired_count=$(( UPCOMING_COUNT - FUTURE_COUNT ))
124
+ if [[ "$expired_count" -gt 0 ]]; then
125
+ lines+="\n(另有 ${expired_count} 个事项已开始或已结束,不再提醒)\n"
126
+ fi
127
+ fi
128
+
129
+ # 冲突提示
130
+ if [[ "$CONFLICT_COUNT" -gt 0 ]]; then
131
+ lines+="\n⚠️ 检测到 ${CONFLICT_COUNT} 个时间冲突:\n"
132
+ while IFS= read -r conflict; do
133
+ local msg
134
+ msg=$(echo "$conflict" | jq -r '.message')
135
+ lines+=" ${msg}\n"
136
+ done < <(jq -c '.conflicts[]' "$PARSE_OUT")
137
+ fi
138
+
139
+ lines+="\n祝今天顺利!"
140
+ echo -e "$lines"
141
+ }
142
+
143
+ # ── 发送日报 ──────────────────────────────────────────────
144
+ if [[ "$SCHEDULE_SEND_SUMMARY" == "true" && "$UPCOMING_COUNT" -gt 0 ]]; then
145
+ SUMMARY=$(build_summary)
146
+ SEND_CMD=(openclaw message send --message "$SUMMARY")
147
+ [[ -n "$SCHEDULE_CHANNEL" ]] && SEND_CMD+=(--channel "$SCHEDULE_CHANNEL")
148
+ [[ -n "$SCHEDULE_RECIPIENT" ]] && SEND_CMD+=(--to "$SCHEDULE_RECIPIENT")
149
+ "${SEND_CMD[@]}" 2>>"$LOG_FILE" && log "日报已发送" || log "日报发送失败"
150
+ elif [[ "$UPCOMING_COUNT" -eq 0 ]]; then
151
+ log "今日无日程,跳过日报"
152
+ fi
153
+
154
+ # ── 创建今日提醒 cron 任务 ───────────────────────────────
155
+ if [[ "$FUTURE_COUNT" -gt 0 ]]; then
156
+ # 将有效事项写入临时文件(按 create_reminders.sh 期望的结构)
157
+ echo "$FUTURE_EVENTS" | jq '{
158
+ meta: {},
159
+ summary: { upcoming_count: length },
160
+ upcoming: .
161
+ }' > "/tmp/schedule_upcoming_${TODAY}.json"
162
+
163
+ bash "${SCRIPT_DIR}/create_reminders.sh" \
164
+ --events-json "/tmp/schedule_upcoming_${TODAY}.json" \
165
+ --channel "$SCHEDULE_CHANNEL" \
166
+ --to "$SCHEDULE_RECIPIENT" \
167
+ --tz "$SCHEDULE_TZ" \
168
+ 2>>"$LOG_FILE" | tee -a "$LOG_FILE"
169
+
170
+ log "提醒任务创建完成"
171
+ else
172
+ log "无有效提醒事项,跳过创建"
173
+ fi
174
+
175
+ log "===== 激活完成 ====="