openclaw-glance-plugin 0.1.28 → 0.1.30
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/README.md +1 -4
- package/package.json +1 -1
- package/skills/glance-watch/SKILL.md +6 -16
- package/skills/glance-watch/references/channels.md +21 -7
- package/skills/glance-watch/references/examples.md +1 -14
- package/skills/glance-watch/references/quote-realtime.md +2 -34
- package/skills/glance-watch/references/symbol-search-and-calendar.md +0 -3
- package/skills/glance-watch/references/troubleshooting.md +1 -1
- package/skills/glance-watch/references/watch-contract.md +8 -5
- package/src/OpenClawPluginAdapter.js +30 -4
- package/src/plugin/index.js +45 -22
package/README.md
CHANGED
|
@@ -107,8 +107,7 @@ await adapter.submitWatchDemand({
|
|
|
107
107
|
variables: { threshold: 8.97 },
|
|
108
108
|
channels: ['openclaw', 'email', 'call', 'sms', 'dingtalk'], // openclaw 必传,其它可选
|
|
109
109
|
emailConfig: {
|
|
110
|
-
to_address: 'demo@example.com'
|
|
111
|
-
template_id: 4
|
|
110
|
+
to_address: 'demo@example.com'
|
|
112
111
|
},
|
|
113
112
|
callConfig: {
|
|
114
113
|
phone: '13800138000',
|
|
@@ -116,12 +115,10 @@ await adapter.submitWatchDemand({
|
|
|
116
115
|
},
|
|
117
116
|
smsConfig: {
|
|
118
117
|
receiver: '13800138000',
|
|
119
|
-
template_id: 90010,
|
|
120
118
|
content: '测试短信'
|
|
121
119
|
},
|
|
122
120
|
dingtalkConfig: {
|
|
123
121
|
cas_id: 'user.dingtalk',
|
|
124
|
-
template_id: 3,
|
|
125
122
|
msg_type: 'text',
|
|
126
123
|
content: '测试钉钉消息'
|
|
127
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: glance-watch
|
|
3
|
-
description: 用于监控A股、港股、比特币等金融市场行情并在条件触发时发送提醒。当用户要求盯盘、监控价格、设置提醒、需要通过邮件/电话/短信/钉钉发起通知、查询A
|
|
3
|
+
description: 用于监控A股、港股、比特币等金融市场行情并在条件触发时发送提醒。当用户要求盯盘、监控价格、设置提醒、需要通过邮件/电话/短信/钉钉发起通知、查询A股港股指数加密标的和行情、交易日历、新闻快讯时使用,例如"帮我盯着比特币"、监控某只股票、涨跌幅提醒、短信通知我、这个月交易日有哪些、有哪些新闻等。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Glance Watch 智能盯盘(主入口)
|
|
@@ -10,16 +10,14 @@ description: 用于监控A股、港股、比特币等金融市场行情并在条
|
|
|
10
10
|
P0(最高)策略能力:
|
|
11
11
|
- 面向 **A股个股、港股个股、A股/港股指数、比特币** 创建/查询/暂停/恢复/删除盯盘策略。
|
|
12
12
|
- 仅用:`watch_create` / `watch_list` / `watch_pause` / `watch_activate` / `watch_remove`。
|
|
13
|
-
- **禁止**:对基金创建盯盘策略(包括场外基金 `xxxxxx.OF`)。
|
|
14
13
|
|
|
15
14
|
P1 行情能力(为策略创建提供上下文):
|
|
16
15
|
- 个股/指数/比特币实时行情:`watch_query_ticker`
|
|
17
|
-
- 基金当日估值:`watch_query_fund_estimates`
|
|
18
16
|
|
|
19
17
|
P2 标的解析能力(创建策略前补齐参数):
|
|
20
18
|
- 先查本地 CSV(`skills/glance-watch/data/*.csv`)做名称/代码映射。
|
|
21
19
|
- 本地不确定再用网关基础信息检索:
|
|
22
|
-
`watch_search_a_stock_basic` / `watch_search_hk_stock_basic` / `watch_search_index_basic`
|
|
20
|
+
`watch_search_a_stock_basic` / `watch_search_hk_stock_basic` / `watch_search_index_basic`
|
|
23
21
|
|
|
24
22
|
P3 辅助能力:
|
|
25
23
|
- 交易日历:`watch_trade_calendar`
|
|
@@ -31,11 +29,9 @@ P3 辅助能力:
|
|
|
31
29
|
- 工具名统一使用下划线形式(如 `watch_query_ticker`),不要写成点号形式(如 `watch.query_ticker`)。
|
|
32
30
|
|
|
33
31
|
- `watch_query_ticker`
|
|
34
|
-
- `watch_query_fund_estimates`
|
|
35
32
|
- `watch_search_a_stock_basic`
|
|
36
33
|
- `watch_search_hk_stock_basic`
|
|
37
34
|
- `watch_search_index_basic`
|
|
38
|
-
- `watch_search_fund_basic`
|
|
39
35
|
- `watch_fin_news`
|
|
40
36
|
- `watch_trade_calendar`
|
|
41
37
|
- `watch_create`
|
|
@@ -56,9 +52,6 @@ P3 辅助能力:
|
|
|
56
52
|
|----------|------------|--------|
|
|
57
53
|
| 现价/涨跌/几块钱(**已有代码**) | `watch_query_ticker` | `market`+`symbol`;`market` 可用 **A股/港股/加密** 等中文别名 |
|
|
58
54
|
| 现价但**只有公司或指数名** | 查询`skills/glance-watch/data/*.csv`或 `watch_search_*_basic` | 从 `data[]` 取 `ts_code` 再 `watch_query_ticker` |
|
|
59
|
-
| 基金**今天估值** | `watch_query_fund_estimates` | `fund_codes`;勿用 `watch_query_ticker` |
|
|
60
|
-
| 基金**档案/是不是这只基** | `watch_search_fund_basic` | `ts_code` 或 `keyword` |
|
|
61
|
-
| 要创建基金盯盘策略 | 不调用 `watch_create` | 明确告知“当前仅支持A股港股的股票/指数或比特币盯盘;基金仅支持估值查询” |
|
|
62
55
|
| **新闻/快讯** | `watch_fin_news` | 必须有关键词 `keyword` 或 `q` |
|
|
63
56
|
| **开不开盘/休市/交易日** | `watch_trade_calendar` | `exchange`(如 SSE/SZSE)+ `start_date`+`end_date`(单日则相同) |
|
|
64
57
|
|
|
@@ -67,8 +60,7 @@ P3 辅助能力:
|
|
|
67
60
|
1. **盯盘策略任务**优先:`watch_create/watch_list/watch_pause/watch_activate/watch_remove`
|
|
68
61
|
2. 创建策略若缺代码或市场:先本地 CSV,再 `watch_search_*_basic`
|
|
69
62
|
3. 代码已明确再查实时价:`watch_query_ticker`
|
|
70
|
-
4.
|
|
71
|
-
5. 再处理辅助任务:`watch_trade_calendar` / `watch_fin_news` / `notify_*`
|
|
63
|
+
4. 再处理辅助任务:`watch_trade_calendar` / `watch_fin_news` / `notify_*`
|
|
72
64
|
|
|
73
65
|
### 创建策略最小必填
|
|
74
66
|
|
|
@@ -79,7 +71,6 @@ P3 辅助能力:
|
|
|
79
71
|
- `operator_parameters.variables`
|
|
80
72
|
|
|
81
73
|
缺任一项先追问,不猜测阈值。
|
|
82
|
-
若 `product_type` 为基金(或代码为 `xxxxxx.OF`),拒绝创建并改为提供基金估值查询方案。
|
|
83
74
|
|
|
84
75
|
### 渠道选择规则(`watch_create`)
|
|
85
76
|
|
|
@@ -107,7 +98,6 @@ P3 辅助能力:
|
|
|
107
98
|
- 把 `channel_configs.*` 传成 JSON 字符串(必须是对象)
|
|
108
99
|
- 用户明确“仅/只用某几个渠道”时,仍强行附加其他渠道
|
|
109
100
|
- 通过 `watch_list` 传 `user_id/use_id` 越权查询
|
|
110
|
-
- 用 `watch_query_ticker` 查**场外基金估值**(应使用 `watch_query_fund_estimates`)
|
|
111
101
|
- 对基金调用 `watch_create`
|
|
112
102
|
|
|
113
103
|
### 联系人 CSV(强约束,适用于 `watch_create` 与 `notify_*`)
|
|
@@ -127,7 +117,7 @@ P3 辅助能力:
|
|
|
127
117
|
|
|
128
118
|
- 盯盘策略(创建/管理):`references/watch-contract.md`
|
|
129
119
|
- 直连通知(短信/电话/邮件/钉钉):`references/channels.md`
|
|
130
|
-
-
|
|
120
|
+
- 实时行情(股/指/加密):`references/quote-realtime.md`
|
|
131
121
|
- 标的检索 + 是否交易日:`references/symbol-search-and-calendar.md`
|
|
132
122
|
- 快讯:`references/news-briefing.md`
|
|
133
123
|
- 示例 payload:`references/examples.md`
|
|
@@ -138,7 +128,7 @@ P3 辅助能力:
|
|
|
138
128
|
| 用户主意图 | 必读文档(最小集合) |
|
|
139
129
|
|---|---|
|
|
140
130
|
| 创建/管理盯盘策略 | `references/watch-contract.md` |
|
|
141
|
-
|
|
|
131
|
+
| 查行情(股/指/加密) | `references/quote-realtime.md` |
|
|
142
132
|
| 名称→代码 / 交易日判断 | `references/symbol-search-and-calendar.md` |
|
|
143
133
|
| 快讯 | `references/news-briefing.md` |
|
|
144
134
|
| 立即发通知 | `references/channels.md` |
|
|
@@ -160,7 +150,7 @@ P3 辅助能力:
|
|
|
160
150
|
|
|
161
151
|
- 用户说「帮我盯 BTC 跌 2% 提醒」 -> `references/watch-contract.md` + `references/channels.md`
|
|
162
152
|
- 用户说「腾讯现在多少钱」 -> `references/quote-realtime.md`(如缺代码再读 `references/symbol-search-and-calendar.md`)
|
|
163
|
-
- 用户说「这只基金今天估值多少」 ->
|
|
153
|
+
- 用户说「这只基金今天估值多少」 -> 明确告知“当前 OpenClaw 暂不提供基金能力”
|
|
164
154
|
- 用户说「今天 A 股开不开盘」 -> `references/symbol-search-and-calendar.md`(`watch_trade_calendar`,SSE/SZSE)
|
|
165
155
|
- 用户说「发短信给我」 -> `references/channels.md`
|
|
166
156
|
- 用户说「有什么央行快讯」 -> `references/news-briefing.md`
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
|
|
22
22
|
### `notify_sms`
|
|
23
23
|
- `receiver`(或 `phone`)
|
|
24
|
-
- `template_id`
|
|
25
24
|
- `content`
|
|
26
25
|
|
|
27
26
|
### `notify_call`
|
|
@@ -31,16 +30,32 @@
|
|
|
31
30
|
|
|
32
31
|
### `notify_email`
|
|
33
32
|
- `to_address`
|
|
34
|
-
- `template_id`
|
|
35
33
|
- `title`
|
|
36
34
|
- `content`
|
|
37
35
|
|
|
38
36
|
### `notify_dingtalk`
|
|
39
37
|
- `cas_id`
|
|
40
|
-
- `template_id`
|
|
41
38
|
- `msg_type`(`text` 或 `markdown`)
|
|
42
39
|
- `content`
|
|
43
40
|
|
|
41
|
+
## 参数怎么填(直连通知)
|
|
42
|
+
|
|
43
|
+
- `notify_sms`
|
|
44
|
+
- `receiver/phone`:11 位手机号(优先用户本轮给出,否则从联系人 CSV 补)。
|
|
45
|
+
- `content`:一句可直接发送的短信正文,可带变量占位(如 `${price}`)。
|
|
46
|
+
- `notify_call`
|
|
47
|
+
- `phone`:11 位手机号。
|
|
48
|
+
- `customer_name`:称呼(优先用户输入,否则 CSV)。
|
|
49
|
+
- `condition`:电话播报内容,直接写“因为什么触发了通知”。
|
|
50
|
+
- `notify_email`
|
|
51
|
+
- `to_address`:收件邮箱(优先用户输入,否则 CSV)。
|
|
52
|
+
- `title`:邮件标题(简短,包含标的+触发事件)。
|
|
53
|
+
- `content`:邮件正文(包含当前值、阈值、建议动作)。
|
|
54
|
+
- `notify_dingtalk`
|
|
55
|
+
- `cas_id`:接收人钉钉账号(优先用户输入,否则 CSV 的 `dingtalk_cas_id`)。
|
|
56
|
+
- `msg_type`:`text`(普通文本)或 `markdown`(需要格式时)。
|
|
57
|
+
- `content`:消息正文。
|
|
58
|
+
|
|
44
59
|
## 联系人记忆(强约束)
|
|
45
60
|
|
|
46
61
|
联系人 CSV:`~/.openclaw/workspace/memory/watch-notify-contacts.csv`
|
|
@@ -74,12 +89,11 @@ rg -n '^sms,jinguo\.xie,' ~/.openclaw/workspace/memory/watch-notify-contacts.csv
|
|
|
74
89
|
## 常见禁止项
|
|
75
90
|
|
|
76
91
|
- 手机号含空格、中划线、`+86-` 等非纯数字
|
|
77
|
-
- `template_id` 传字符串
|
|
78
92
|
- `msg_type` 不是 `text/markdown`
|
|
79
93
|
|
|
80
94
|
## `watch_create` 渠道必填映射
|
|
81
95
|
|
|
82
|
-
- 选 `email`:`channel_configs.email.to_address/
|
|
96
|
+
- 选 `email`:`channel_configs.email.to_address/title/content`
|
|
83
97
|
- 选 `call`:`channel_configs.call.phone/customer_name/condition`
|
|
84
|
-
- 选 `sms`:`channel_configs.sms.receiver(或phone)/
|
|
85
|
-
- 选 `dingtalk`:`channel_configs.dingtalk.cas_id/
|
|
98
|
+
- 选 `sms`:`channel_configs.sms.receiver(或phone)/content`
|
|
99
|
+
- 选 `dingtalk`:`channel_configs.dingtalk.cas_id/msg_type/content`
|
|
@@ -49,7 +49,6 @@ await watch_create({
|
|
|
49
49
|
},
|
|
50
50
|
sms: {
|
|
51
51
|
receiver: '13800138000',
|
|
52
|
-
template_id: 90010,
|
|
53
52
|
content: '浦发银行触发盯盘条件,当前价 ${price}'
|
|
54
53
|
}
|
|
55
54
|
}
|
|
@@ -72,10 +71,9 @@ await watch_remove({ strategy_id: 's_123' })
|
|
|
72
71
|
### 2.1 发送短信(含联系人 CSV 补值)
|
|
73
72
|
|
|
74
73
|
```javascript
|
|
75
|
-
// Step 0: 先查联系人 CSV,补 receiver
|
|
74
|
+
// Step 0: 先查联系人 CSV,补 receiver
|
|
76
75
|
await notify_sms({
|
|
77
76
|
receiver: '13800138000',
|
|
78
|
-
template_id: 90010,
|
|
79
77
|
content: '测试短信:比特币跌幅超过2%'
|
|
80
78
|
})
|
|
81
79
|
|
|
@@ -93,14 +91,12 @@ await notify_call({
|
|
|
93
91
|
|
|
94
92
|
await notify_email({
|
|
95
93
|
to_address: 'demo@example.com',
|
|
96
|
-
template_id: 4,
|
|
97
94
|
title: '盯盘提醒',
|
|
98
95
|
content: 'BTCUSDT 触发阈值'
|
|
99
96
|
})
|
|
100
97
|
|
|
101
98
|
await notify_dingtalk({
|
|
102
99
|
cas_id: 'user.dingtalk',
|
|
103
|
-
template_id: 3,
|
|
104
100
|
msg_type: 'text',
|
|
105
101
|
content: '盯盘触发:BTCUSDT'
|
|
106
102
|
})
|
|
@@ -116,13 +112,6 @@ await watch_query_ticker({ market: 'hk', symbol: '00700', segment: 'stock' })
|
|
|
116
112
|
await watch_query_ticker({ market: 'crypto', symbol: 'BTCUSDT' })
|
|
117
113
|
```
|
|
118
114
|
|
|
119
|
-
### 3.2 基金当日估值(基金不支持创建盯盘)
|
|
120
|
-
|
|
121
|
-
```javascript
|
|
122
|
-
await watch_query_fund_estimates({ fund_codes: '000006.OF' })
|
|
123
|
-
await watch_query_fund_estimates({ fund_codes: ['000006.OF', '110011.OF'] })
|
|
124
|
-
```
|
|
125
|
-
|
|
126
115
|
## 4) 标的检索与是否交易日查询
|
|
127
116
|
|
|
128
117
|
### 4.1 名称 -> 代码
|
|
@@ -131,7 +120,6 @@ await watch_query_fund_estimates({ fund_codes: ['000006.OF', '110011.OF'] })
|
|
|
131
120
|
await watch_search_a_stock_basic({ keyword: '平安银行', limit: 5 })
|
|
132
121
|
await watch_search_hk_stock_basic({ q: '腾讯', limit: 5 })
|
|
133
122
|
await watch_search_index_basic({ keyword: '沪深300', limit: 5 })
|
|
134
|
-
await watch_search_fund_basic({ keyword: '西部利得量化成长', limit: 5 })
|
|
135
123
|
```
|
|
136
124
|
|
|
137
125
|
### 4.2 是否交易日
|
|
@@ -158,6 +146,5 @@ await watch_fin_news({
|
|
|
158
146
|
|
|
159
147
|
## 常见边界提示
|
|
160
148
|
|
|
161
|
-
- 基金(`xxxxxx.OF`)不能用 `watch_create`,只能用 `watch_query_fund_estimates` / `watch_search_fund_basic`。
|
|
162
149
|
- 使用 `call/sms/email/dingtalk` 时,`watch_create` 与 `notify_*` 都要先查联系人 CSV。
|
|
163
150
|
- `operator_type` 固定 `rule`,不要用其他值。
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 实时行情
|
|
2
2
|
|
|
3
3
|
## 适用范围
|
|
4
4
|
|
|
5
5
|
- 股票/指数/加密实时行情:`watch_query_ticker`
|
|
6
|
-
- 基金当日估值:`watch_query_fund_estimates`
|
|
7
|
-
- 注意:基金不支持 `watch_create` 创建盯盘策略
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## 股票/指数/加密实时行情:`watch_query_ticker`
|
|
10
8
|
|
|
11
9
|
必填参数:
|
|
12
10
|
- `market`:`a` / `hk` / `crypto`(支持中文别名)
|
|
@@ -30,33 +28,3 @@ await watch_query_ticker({ market: 'hk', symbol: '00700', segment: 'stock' })
|
|
|
30
28
|
await watch_query_ticker({ market: 'A股', symbol: '600000.SH' })
|
|
31
29
|
await watch_query_ticker({ market: 'crypto', symbol: 'BTCUSDT' })
|
|
32
30
|
```
|
|
33
|
-
|
|
34
|
-
## 2) 基金估值:`watch_query_fund_estimates`
|
|
35
|
-
|
|
36
|
-
参数(二选一):
|
|
37
|
-
- `fund_codes`
|
|
38
|
-
- `fundCodes`
|
|
39
|
-
|
|
40
|
-
值格式:
|
|
41
|
-
- 单只:`"000006.OF"`
|
|
42
|
-
- 多只:`["000006.OF", "110011.OF"]`
|
|
43
|
-
|
|
44
|
-
成功判定:
|
|
45
|
-
- `success === true`
|
|
46
|
-
- `http_status === 200`
|
|
47
|
-
- `data` 为基金代码映射
|
|
48
|
-
|
|
49
|
-
失败处理:
|
|
50
|
-
- 读取 `error` 与 `http_status` 反馈用户
|
|
51
|
-
|
|
52
|
-
示例:
|
|
53
|
-
|
|
54
|
-
```javascript
|
|
55
|
-
await watch_query_fund_estimates({ fund_codes: '000006.OF' })
|
|
56
|
-
await watch_query_fund_estimates({ fund_codes: ['000006.OF', '110011.OF'] })
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## 3) 硬边界
|
|
60
|
-
|
|
61
|
-
- 不要用 `watch_query_ticker` 查基金当日估值
|
|
62
|
-
- 不要对基金调用 `watch_create`
|
|
@@ -16,7 +16,6 @@ CSV 映射:
|
|
|
16
16
|
- `stock_hk.csv` -> `product_type=hk_stock`
|
|
17
17
|
- `index_a.csv` -> `product_type=index`
|
|
18
18
|
- `index_hk.csv` -> `product_type=index`
|
|
19
|
-
- 基金 `xxxxxx.OF` -> 仅估值/基础信息,不创建策略
|
|
20
19
|
|
|
21
20
|
### 本地 CSV 匹配算法(执行约束)
|
|
22
21
|
|
|
@@ -34,7 +33,6 @@ CSV 映射:
|
|
|
34
33
|
- `watch_search_a_stock_basic`(A股)
|
|
35
34
|
- `watch_search_hk_stock_basic`(港股)
|
|
36
35
|
- `watch_search_index_basic`(指数)
|
|
37
|
-
- `watch_search_fund_basic`(基金基础信息)
|
|
38
36
|
|
|
39
37
|
统一规则:
|
|
40
38
|
- 名称检索至少给 `keyword` 或 `q`
|
|
@@ -46,7 +44,6 @@ CSV 映射:
|
|
|
46
44
|
await watch_search_a_stock_basic({ keyword: '平安银行', limit: 5 })
|
|
47
45
|
await watch_search_hk_stock_basic({ q: '腾讯' })
|
|
48
46
|
await watch_search_index_basic({ keyword: '沪深300' })
|
|
49
|
-
await watch_search_fund_basic({ ts_code: '000006.OF' })
|
|
50
47
|
```
|
|
51
48
|
|
|
52
49
|
## 3) 交易日查询:`watch_trade_calendar`
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- 若报“未注册的算子类型”,将 `operator_type` 修正为 `rule` 后重试。
|
|
14
14
|
- 若报 `UNSUPPORTED_PRODUCT_TYPE`,说明命中了基金边界(如 `fund` 或 `000006.OF`):
|
|
15
15
|
- 不再重试 `watch_create`
|
|
16
|
-
-
|
|
16
|
+
- 直接告知“当前 OpenClaw 暂不提供基金能力”
|
|
17
17
|
|
|
18
18
|
## 通知失败(`notify_*`)
|
|
19
19
|
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
## 0. 产品边界
|
|
12
12
|
|
|
13
13
|
- 支持盯盘:`stock` / `hk_stock` / `index` / `crypto`
|
|
14
|
-
- 不支持盯盘:基金(`product_type=fund` 或代码形如 `xxxxxx.OF`)
|
|
15
|
-
- 基金相关请改用:`watch_query_fund_estimates` / `watch_search_fund_basic`
|
|
16
14
|
|
|
17
15
|
## 1. `watch_create`
|
|
18
16
|
|
|
@@ -66,19 +64,25 @@ OpenClaw 路由约束(当 `channels` 包含 `openclaw`):
|
|
|
66
64
|
},
|
|
67
65
|
dingtalk: {
|
|
68
66
|
cas_id: 'jinguo.xie',
|
|
69
|
-
template_id: 3,
|
|
70
67
|
msg_type: 'text',
|
|
71
68
|
content: '比特币跌幅超2%!当前价格 ${price},跌幅 ${change_percent}%。建议卖出!'
|
|
72
69
|
},
|
|
73
70
|
sms: {
|
|
74
71
|
receiver: '13800138000',
|
|
75
|
-
template_id: 90010,
|
|
76
72
|
content: '比特币跌幅超2%!当前价格 ${price},建议卖出!'
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
```
|
|
81
77
|
|
|
78
|
+
参数填写指引(创建策略时):
|
|
79
|
+
- `channel_configs.sms.receiver`:手机号,优先本轮用户输入,否则联系人 CSV。
|
|
80
|
+
- `channel_configs.sms.content`:短信正文,写清触发条件+关键行情值。
|
|
81
|
+
- `channel_configs.email.to_address`:收件邮箱,优先本轮输入,否则 CSV。
|
|
82
|
+
- `channel_configs.email.title/content`:标题写“标的 + 触发事件”,正文写“当前值 + 阈值 + 建议动作”。
|
|
83
|
+
- `channel_configs.call.phone/customer_name/condition`:分别为号码、称呼、电话播报文案。
|
|
84
|
+
- `channel_configs.dingtalk.cas_id`:钉钉接收账号;`msg_type` 用 `text` 或 `markdown`;`content` 为消息正文。
|
|
85
|
+
|
|
82
86
|
成功判定:
|
|
83
87
|
- `success === true`
|
|
84
88
|
|
|
@@ -92,7 +96,6 @@ OpenClaw 路由约束(当 `channels` 包含 `openclaw`):
|
|
|
92
96
|
- 顶层放 `condition`
|
|
93
97
|
- `channel_configs.*` 传 JSON 字符串
|
|
94
98
|
- 用户明确“仅/只用某几个渠道”时仍强行附加渠道
|
|
95
|
-
- 基金标的调用 `watch_create`
|
|
96
99
|
|
|
97
100
|
## 联系人 CSV(创建策略场景必遵守)
|
|
98
101
|
|
|
@@ -7,12 +7,38 @@ import {
|
|
|
7
7
|
import { OpenClawBridgeClient } from './OpenClawBridgeClient.js';
|
|
8
8
|
import { extractOpenclawRoutingFromRecord } from './openclawRouting.js';
|
|
9
9
|
|
|
10
|
+
const CHANNEL_TEMPLATE_DEFAULTS = Object.freeze({
|
|
11
|
+
sms: 90010,
|
|
12
|
+
email: 4,
|
|
13
|
+
dingtalk: 3
|
|
14
|
+
});
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* 全局单例 Adapter 实例
|
|
12
18
|
* @type {OpenClawPluginAdapter|null}
|
|
13
19
|
*/
|
|
14
20
|
let globalAdapterInstance = null;
|
|
15
21
|
|
|
22
|
+
function withChannelTemplateDefaults(channel, config) {
|
|
23
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
26
|
+
const defaultTemplateId = CHANNEL_TEMPLATE_DEFAULTS[channel];
|
|
27
|
+
if (defaultTemplateId == null || config.template_id != null) {
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
30
|
+
return { ...config, template_id: defaultTemplateId };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function applyChannelTemplateDefaults(channelConfigs = {}) {
|
|
34
|
+
return {
|
|
35
|
+
...channelConfigs,
|
|
36
|
+
sms: withChannelTemplateDefaults('sms', channelConfigs.sms),
|
|
37
|
+
email: withChannelTemplateDefaults('email', channelConfigs.email),
|
|
38
|
+
dingtalk: withChannelTemplateDefaults('dingtalk', channelConfigs.dingtalk)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
/**
|
|
17
43
|
* 获取全局单例 Adapter 实例
|
|
18
44
|
* @param {Object} clientOrOptions - 客户端实例或配置选项
|
|
@@ -87,13 +113,13 @@ export class OpenClawPluginAdapter {
|
|
|
87
113
|
.filter((x) => typeof x === 'string' && x.trim())
|
|
88
114
|
.map((x) => x.trim().toLowerCase())
|
|
89
115
|
: [];
|
|
90
|
-
const channelConfigs = { ...(demand.channelConfigs || {}) };
|
|
116
|
+
const channelConfigs = applyChannelTemplateDefaults({ ...(demand.channelConfigs || {}) });
|
|
91
117
|
|
|
92
118
|
if (demand.openclawConfig) {
|
|
93
119
|
if (!channels.includes('openclaw')) channels.push('openclaw');
|
|
94
120
|
}
|
|
95
121
|
if (demand.emailConfig) {
|
|
96
|
-
channelConfigs.email = demand.emailConfig;
|
|
122
|
+
channelConfigs.email = withChannelTemplateDefaults('email', demand.emailConfig);
|
|
97
123
|
if (!channels.includes('email')) channels.push('email');
|
|
98
124
|
}
|
|
99
125
|
if (demand.callConfig) {
|
|
@@ -101,11 +127,11 @@ export class OpenClawPluginAdapter {
|
|
|
101
127
|
if (!channels.includes('call')) channels.push('call');
|
|
102
128
|
}
|
|
103
129
|
if (demand.smsConfig) {
|
|
104
|
-
channelConfigs.sms = demand.smsConfig;
|
|
130
|
+
channelConfigs.sms = withChannelTemplateDefaults('sms', demand.smsConfig);
|
|
105
131
|
if (!channels.includes('sms')) channels.push('sms');
|
|
106
132
|
}
|
|
107
133
|
if (demand.dingtalkConfig) {
|
|
108
|
-
channelConfigs.dingtalk = demand.dingtalkConfig;
|
|
134
|
+
channelConfigs.dingtalk = withChannelTemplateDefaults('dingtalk', demand.dingtalkConfig);
|
|
109
135
|
if (!channels.includes('dingtalk')) channels.push('dingtalk');
|
|
110
136
|
}
|
|
111
137
|
if (!channels.includes('openclaw')) channels.unshift('openclaw');
|
package/src/plugin/index.js
CHANGED
|
@@ -13,6 +13,11 @@ import { ProcessLock } from '../runtime/lock/ProcessLock.js';
|
|
|
13
13
|
/** 与 BridgeRuntime FINANCE_TABLE_REQUEST_TIMEOUT_MS 一致 */
|
|
14
14
|
const GATEWAY_TABLE_REQUEST_TIMEOUT_MS = 90_000;
|
|
15
15
|
const FUND_CODE_PATTERN = /^\d{6}\.OF$/i;
|
|
16
|
+
const CHANNEL_TEMPLATE_DEFAULTS = Object.freeze({
|
|
17
|
+
sms: 90010,
|
|
18
|
+
email: 4,
|
|
19
|
+
dingtalk: 3
|
|
20
|
+
});
|
|
16
21
|
|
|
17
22
|
let activeRuntime = null;
|
|
18
23
|
|
|
@@ -73,13 +78,13 @@ function mapDemandToCreatePayload(demand = {}) {
|
|
|
73
78
|
.filter((x) => typeof x === 'string' && x.trim())
|
|
74
79
|
.map((x) => x.trim().toLowerCase())
|
|
75
80
|
: [];
|
|
76
|
-
const channelConfigs = { ...(demand.channelConfigs || {}) };
|
|
81
|
+
const channelConfigs = applyChannelTemplateDefaults({ ...(demand.channelConfigs || {}) });
|
|
77
82
|
|
|
78
83
|
if (demand.openclawConfig) {
|
|
79
84
|
if (!channels.includes('openclaw')) channels.push('openclaw');
|
|
80
85
|
}
|
|
81
86
|
if (demand.emailConfig) {
|
|
82
|
-
channelConfigs.email = demand.emailConfig;
|
|
87
|
+
channelConfigs.email = withChannelTemplateDefaults('email', demand.emailConfig);
|
|
83
88
|
if (!channels.includes('email')) channels.push('email');
|
|
84
89
|
}
|
|
85
90
|
if (demand.callConfig) {
|
|
@@ -87,11 +92,11 @@ function mapDemandToCreatePayload(demand = {}) {
|
|
|
87
92
|
if (!channels.includes('call')) channels.push('call');
|
|
88
93
|
}
|
|
89
94
|
if (demand.smsConfig) {
|
|
90
|
-
channelConfigs.sms = demand.smsConfig;
|
|
95
|
+
channelConfigs.sms = withChannelTemplateDefaults('sms', demand.smsConfig);
|
|
91
96
|
if (!channels.includes('sms')) channels.push('sms');
|
|
92
97
|
}
|
|
93
98
|
if (demand.dingtalkConfig) {
|
|
94
|
-
channelConfigs.dingtalk = demand.dingtalkConfig;
|
|
99
|
+
channelConfigs.dingtalk = withChannelTemplateDefaults('dingtalk', demand.dingtalkConfig);
|
|
95
100
|
if (!channels.includes('dingtalk')) channels.push('dingtalk');
|
|
96
101
|
}
|
|
97
102
|
if (!channels.includes('openclaw')) channels.unshift('openclaw');
|
|
@@ -126,29 +131,47 @@ function mapDemandToCreatePayload(demand = {}) {
|
|
|
126
131
|
|
|
127
132
|
function mergeOpenclawChannelConfig(payload = {}, context = {}) {
|
|
128
133
|
const merged = { ...(payload || {}) };
|
|
129
|
-
const channelConfigs = { ...(merged.channel_configs || {}) };
|
|
130
|
-
const openclawConfig = { ...(channelConfigs.openclaw || {}) };
|
|
134
|
+
const channelConfigs = applyChannelTemplateDefaults({ ...(merged.channel_configs || {}) });
|
|
131
135
|
const routing = deriveOpenclawRouting({ params: merged, context });
|
|
136
|
+
merged.channel_configs = channelConfigs;
|
|
137
|
+
|
|
138
|
+
if (Object.keys(routing).length > 0) {
|
|
139
|
+
const openclawConfig = { ...(channelConfigs.openclaw || {}) };
|
|
140
|
+
channelConfigs.openclaw = {
|
|
141
|
+
...openclawConfig,
|
|
142
|
+
...routing
|
|
143
|
+
};
|
|
132
144
|
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
const channels = Array.isArray(merged.channels)
|
|
146
|
+
? merged.channels
|
|
147
|
+
.filter((x) => typeof x === 'string' && x.trim())
|
|
148
|
+
.map((x) => x.trim().toLowerCase())
|
|
149
|
+
: [];
|
|
150
|
+
if (!channels.includes('openclaw')) channels.unshift('openclaw');
|
|
151
|
+
merged.channels = channels;
|
|
135
152
|
}
|
|
136
153
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
...routing
|
|
140
|
-
};
|
|
154
|
+
return merged;
|
|
155
|
+
}
|
|
141
156
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (
|
|
157
|
+
function withChannelTemplateDefaults(channel, config) {
|
|
158
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
159
|
+
return config;
|
|
160
|
+
}
|
|
161
|
+
const defaultTemplateId = CHANNEL_TEMPLATE_DEFAULTS[channel];
|
|
162
|
+
if (defaultTemplateId == null || config.template_id != null) {
|
|
163
|
+
return config;
|
|
164
|
+
}
|
|
165
|
+
return { ...config, template_id: defaultTemplateId };
|
|
166
|
+
}
|
|
148
167
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
function applyChannelTemplateDefaults(channelConfigs = {}) {
|
|
169
|
+
return {
|
|
170
|
+
...channelConfigs,
|
|
171
|
+
sms: withChannelTemplateDefaults('sms', channelConfigs.sms),
|
|
172
|
+
email: withChannelTemplateDefaults('email', channelConfigs.email),
|
|
173
|
+
dingtalk: withChannelTemplateDefaults('dingtalk', channelConfigs.dingtalk)
|
|
174
|
+
};
|
|
152
175
|
}
|
|
153
176
|
|
|
154
177
|
function assertWatchCreateSupported(payload = {}) {
|
|
@@ -263,7 +286,7 @@ function buildControlApi(startupPromise) {
|
|
|
263
286
|
'notify.send requires input.channel to be one of: sms, email, call, dingtalk'
|
|
264
287
|
);
|
|
265
288
|
}
|
|
266
|
-
const payload = { ...(input.payload || {}) };
|
|
289
|
+
const payload = withChannelTemplateDefaults(ch, { ...(input.payload || {}) });
|
|
267
290
|
return runtime.request('notify.send', {
|
|
268
291
|
...payload,
|
|
269
292
|
channel: ch
|