@xmanrui/dsh-im 1.0.2 → 1.2.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.
- package/README.en.md +23 -3
- package/README.md +23 -3
- package/assets/logo-dsh-im-chinese-readme-3x2.png +0 -0
- package/assets/logo_cn.png +0 -0
- package/lib/client.js +815 -560
- package/lib/index.js +163 -163
- package/package.json +1 -1
- package/plugin-src/client/agent-preset.js +15 -6
- package/plugin-src/client/channel-card-meta.js +48 -0
- package/plugin-src/client/channels/dingtalk/index.js +25 -19
- package/plugin-src/client/channels/dingtalk/styles.js +0 -6
- package/plugin-src/client/channels/feishu/index.js +41 -35
- package/plugin-src/client/channels/feishu/styles.js +0 -5
- package/plugin-src/client/channels/qq/index.js +24 -16
- package/plugin-src/client/channels/shared/token-channel.js +32 -24
- package/plugin-src/client/channels/wecom/index.js +24 -16
- package/plugin-src/client/channels/weixin/index.js +29 -23
- package/plugin-src/client/channels/weixin/styles.js +0 -5
- package/plugin-src/client/channels/whatsapp/api.js +11 -0
- package/plugin-src/client/channels/whatsapp/index.js +152 -23
- package/plugin-src/client/channels/whatsapp/styles.js +25 -0
- package/plugin-src/client/i18n.js +20 -0
- package/plugin-src/client/styles.js +23 -8
- package/plugin-src/host/channels/whatsapp/rpc.mjs +19 -1
- package/plugin-src/host/index.mjs +14 -1
- package/src/channels/dingtalk/dingtalk-api.mjs +215 -2
- package/src/channels/dingtalk/dingtalk-bridge.mjs +155 -4
- package/src/channels/discord/discord-api.mjs +134 -6
- package/src/channels/discord/discord-runtime.mjs +15 -4
- package/src/channels/feishu/bridge.mjs +223 -15
- package/src/channels/feishu/feishu-channel.mjs +227 -1
- package/src/channels/feishu/plugin-controller.mjs +1 -0
- package/src/channels/qq/qq-bridge.mjs +217 -10
- package/src/channels/shared/editable-message-stream.mjs +18 -1
- package/src/channels/shared/harness-client.mjs +99 -7
- package/src/channels/shared/semantic/artifact.mjs +748 -0
- package/src/channels/shared/semantic/delivery.mjs +153 -0
- package/src/channels/shared/text-harness-bridge.mjs +149 -3
- package/src/channels/shared/workspace-session.mjs +15 -1
- package/src/channels/slack/manifest.mjs +1 -0
- package/src/channels/slack/slack-api.mjs +167 -4
- package/src/channels/slack/slack-runtime.mjs +21 -5
- package/src/channels/telegram/telegram-api.mjs +111 -5
- package/src/channels/telegram/telegram-runtime.mjs +18 -4
- package/src/channels/wecom/wecom-bridge.mjs +260 -12
- package/src/channels/weixin/weixin-api.mjs +268 -2
- package/src/channels/weixin/weixin-bridge.mjs +134 -3
- package/src/channels/weixin/weixin-controller.mjs +5 -1
- package/src/channels/weixin/weixin-runtime.mjs +5 -1
- package/src/channels/whatsapp/config-store.mjs +43 -0
- package/src/channels/whatsapp/whatsapp-controller.mjs +22 -1
- package/src/channels/whatsapp/whatsapp-runtime.mjs +149 -5
|
@@ -77,10 +77,6 @@ const CSS = String.raw`
|
|
|
77
77
|
.dxw-accountIdentity h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; }
|
|
78
78
|
.dxw-accountIdentity p { color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; margin-top: 4px; }
|
|
79
79
|
.dxw-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
|
|
80
|
-
.dxw-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 20px 0; }
|
|
81
|
-
.dxw-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
82
|
-
.dxw-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
|
|
83
|
-
.dxw-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
84
80
|
.dxw-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
85
81
|
.dxw-accountFooter .dxw-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
|
|
86
82
|
.dxw-accountFooter .dxw-button { flex: none; white-space: nowrap; }
|
|
@@ -99,7 +95,6 @@ const CSS = String.raw`
|
|
|
99
95
|
.dxw-logo { display: none; }
|
|
100
96
|
.dxw-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
|
|
101
97
|
.dxw-qrCopy { width: 100%; }
|
|
102
|
-
.dxw-metrics { grid-template-columns: minmax(0, 1fr); }
|
|
103
98
|
.dxw-cardBody { padding: 20px; }
|
|
104
99
|
}
|
|
105
100
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -9,6 +9,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
9
9
|
cancelProvisioning: 'provision.cancel',
|
|
10
10
|
reconnectBot: 'bot.reconnect',
|
|
11
11
|
deleteBot: 'bot.delete',
|
|
12
|
+
setAccessPolicy: 'bot.access-policy.set',
|
|
12
13
|
setWorkspace: 'bot.workspace.set',
|
|
13
14
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
14
15
|
});
|
|
@@ -86,6 +87,16 @@ function normalizeBot(value) {
|
|
|
86
87
|
state: connected ? 'connected' : state,
|
|
87
88
|
workspace: text(value.workspace, '', 4_096),
|
|
88
89
|
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
|
90
|
+
accessPolicy: {
|
|
91
|
+
accessMode: ['self-only', 'private-allowlist', 'open'].includes(
|
|
92
|
+
value.accessPolicy?.accessMode,
|
|
93
|
+
) ? value.accessPolicy.accessMode : 'self-only',
|
|
94
|
+
allowedNumbers: Array.isArray(value.accessPolicy?.allowedNumbers)
|
|
95
|
+
? [...new Set(value.accessPolicy.allowedNumbers.filter((entry) => (
|
|
96
|
+
typeof entry === 'string' && /^[1-9]\d{4,14}$/.test(entry)
|
|
97
|
+
)))]
|
|
98
|
+
: [],
|
|
99
|
+
},
|
|
89
100
|
bot: {
|
|
90
101
|
name: text(value.bot?.name, 'WhatsApp机器人', 100),
|
|
91
102
|
idMasked: text(value.bot?.idMasked, 'WhatsApp账号', 140),
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
11
11
|
} from '../../agent-preset.js';
|
|
12
12
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
13
|
+
import { BotStatusMeta, ChannelListHeading } from '../../channel-card-meta.js';
|
|
13
14
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
14
15
|
import {
|
|
15
16
|
WHATSAPP_ENDPOINTS,
|
|
@@ -24,6 +25,119 @@ import { installWhatsappStyles } from './styles.js';
|
|
|
24
25
|
|
|
25
26
|
const ACTIVE_STATES = new Set(['pending', 'connecting']);
|
|
26
27
|
|
|
28
|
+
function accessPolicyFor(account) {
|
|
29
|
+
const accessMode = ['self-only', 'private-allowlist', 'open'].includes(
|
|
30
|
+
account?.accessPolicy?.accessMode,
|
|
31
|
+
) ? account.accessPolicy.accessMode : 'self-only';
|
|
32
|
+
return {
|
|
33
|
+
accessMode,
|
|
34
|
+
allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers)
|
|
35
|
+
? account.accessPolicy.allowedNumbers : [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function allowedNumbersFromText(value) {
|
|
40
|
+
const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
|
|
41
|
+
const normalized = entries.map((entry) => entry.replace(/^\+/, ''));
|
|
42
|
+
if (normalized.some((entry) => !/^[1-9]\d{4,14}$/.test(entry))) {
|
|
43
|
+
throw new TypeError('电话号码必须包含国家或地区代码,每行一个。');
|
|
44
|
+
}
|
|
45
|
+
return [...new Set(normalized)];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function WhatsappAccessSettings({ account, busy = false, onSave }) {
|
|
49
|
+
const policy = accessPolicyFor(account);
|
|
50
|
+
const sourceNumbers = policy.allowedNumbers.join('\n');
|
|
51
|
+
const helpId = React.useId();
|
|
52
|
+
const [accessMode, setAccessMode] = React.useState(policy.accessMode);
|
|
53
|
+
const [allowedNumbers, setAllowedNumbers] = React.useState(sourceNumbers);
|
|
54
|
+
const [error, setError] = React.useState(null);
|
|
55
|
+
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
setAccessMode(policy.accessMode);
|
|
58
|
+
setAllowedNumbers(sourceNumbers);
|
|
59
|
+
setError(null);
|
|
60
|
+
}, [policy.accessMode, sourceNumbers]);
|
|
61
|
+
|
|
62
|
+
const save = async (event) => {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
setError(null);
|
|
65
|
+
try {
|
|
66
|
+
const normalized = allowedNumbersFromText(allowedNumbers);
|
|
67
|
+
if (typeof onSave !== 'function') throw new Error('WhatsApp 访问设置暂不可用。');
|
|
68
|
+
await onSave({ accessMode, allowedNumbers: normalized });
|
|
69
|
+
} catch (caught) {
|
|
70
|
+
setError(caught?.message ?? 'WhatsApp 访问设置保存失败。');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const allowlistEnabled = accessMode === 'private-allowlist';
|
|
75
|
+
const labels = {
|
|
76
|
+
'self-only': '仅自己模式',
|
|
77
|
+
'private-allowlist': '指定联系人模式',
|
|
78
|
+
open: '开放响应模式',
|
|
79
|
+
};
|
|
80
|
+
return h('form', { className: 'dwa-access', onSubmit: save },
|
|
81
|
+
h('div', { className: 'dwa-accessHeading' },
|
|
82
|
+
h('strong', null, '访问设置'),
|
|
83
|
+
h('span', { className: 'dwa-accessStatus' },
|
|
84
|
+
h('span', { className: 'dwa-accessBadge', 'data-mode': policy.accessMode },
|
|
85
|
+
['已生效:', labels[policy.accessMode]]),
|
|
86
|
+
h('span', { className: 'dwa-accessHelp' },
|
|
87
|
+
h('button', {
|
|
88
|
+
type: 'button',
|
|
89
|
+
className: 'dwa-accessHelpButton',
|
|
90
|
+
'aria-label': '查看 WhatsApp 访问模式说明',
|
|
91
|
+
'aria-describedby': helpId,
|
|
92
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
93
|
+
h('span', { id: helpId, className: 'dwa-accessTooltip', role: 'tooltip' },
|
|
94
|
+
h('span', { className: 'dwa-accessTooltipItem' },
|
|
95
|
+
h('strong', null, '仅自己模式'),
|
|
96
|
+
h('span', null, '只响应已绑定 WhatsApp 账号的自聊消息。')),
|
|
97
|
+
h('span', { className: 'dwa-accessTooltipItem' },
|
|
98
|
+
h('strong', null, '指定联系人模式'),
|
|
99
|
+
h('span', null, '响应自聊和白名单联系人的私聊,忽略群聊。')),
|
|
100
|
+
h('span', { className: 'dwa-accessTooltipItem' },
|
|
101
|
+
h('strong', null, '开放响应模式'),
|
|
102
|
+
h('span', null, '响应所有私聊,以及群聊中的提及或回复。')))))),
|
|
103
|
+
h('label', { className: 'dwa-accessField' },
|
|
104
|
+
h('span', null, '模式'),
|
|
105
|
+
h('select', {
|
|
106
|
+
value: accessMode,
|
|
107
|
+
disabled: busy,
|
|
108
|
+
'aria-label': 'WhatsApp 访问模式',
|
|
109
|
+
onChange: (event) => { setAccessMode(event.target.value); setError(null); },
|
|
110
|
+
},
|
|
111
|
+
h('option', { value: 'self-only' }, '仅自己模式(默认)'),
|
|
112
|
+
h('option', { value: 'private-allowlist' }, '指定联系人模式'),
|
|
113
|
+
h('option', { value: 'open' }, '开放响应模式'))),
|
|
114
|
+
allowlistEnabled
|
|
115
|
+
? h('label', { className: 'dwa-accessField' },
|
|
116
|
+
h('span', null, '允许私聊的 WhatsApp 电话号码'),
|
|
117
|
+
h('textarea', {
|
|
118
|
+
value: allowedNumbers,
|
|
119
|
+
disabled: busy,
|
|
120
|
+
rows: 3,
|
|
121
|
+
placeholder: '每行一个含国家或地区代码的号码',
|
|
122
|
+
'aria-label': '允许私聊的 WhatsApp 电话号码',
|
|
123
|
+
onChange: (event) => { setAllowedNumbers(event.target.value); setError(null); },
|
|
124
|
+
}),
|
|
125
|
+
h('small', null, '可以包含开头的 +,保存时会自动移除。'))
|
|
126
|
+
: null,
|
|
127
|
+
allowlistEnabled && allowedNumbers.trim() === ''
|
|
128
|
+
? h('p', { className: 'dwa-accessWarning', role: 'status' },
|
|
129
|
+
'白名单为空;保存后将只接受自聊消息。')
|
|
130
|
+
: null,
|
|
131
|
+
error ? h('p', { className: 'dwa-accessError', role: 'alert' }, error) : null,
|
|
132
|
+
h('div', { className: 'dwa-accessActions' },
|
|
133
|
+
h('button', {
|
|
134
|
+
type: 'submit',
|
|
135
|
+
className: 'ddt-button',
|
|
136
|
+
'data-kind': 'secondary',
|
|
137
|
+
disabled: busy,
|
|
138
|
+
}, busy ? '正在保存…' : '保存访问设置')));
|
|
139
|
+
}
|
|
140
|
+
|
|
27
141
|
const Button = React.forwardRef(function Button(
|
|
28
142
|
{ children, kind = 'secondary', className = '', ...props },
|
|
29
143
|
ref,
|
|
@@ -179,6 +293,7 @@ export function WhatsappAccountCard({
|
|
|
179
293
|
onReconnect,
|
|
180
294
|
onWorkspaceSave,
|
|
181
295
|
onAgentPresetSave,
|
|
296
|
+
onAccessPolicySave,
|
|
182
297
|
onRequestRemove,
|
|
183
298
|
onConfirmRemove,
|
|
184
299
|
onCancelRemove,
|
|
@@ -197,15 +312,14 @@ export function WhatsappAccountCard({
|
|
|
197
312
|
}, h(WhatsappLogoGlyph, { size: 29 })),
|
|
198
313
|
h('div', { className: 'dim-botName' },
|
|
199
314
|
h('h3', null, account.bot.name), h('p', null, account.bot.idMasked))),
|
|
200
|
-
h(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
h('dt', null, '最近检查'), h('dd', null, checkedTime(account.health.lastCheckedAt)))),
|
|
315
|
+
h(BotStatusMeta, {
|
|
316
|
+
className: 'ddt-health',
|
|
317
|
+
dotClassName: 'ddt-dot',
|
|
318
|
+
tone,
|
|
319
|
+
stateLabel,
|
|
320
|
+
lastCheckedAt: account.health.lastCheckedAt,
|
|
321
|
+
formatCheckedTime: checkedTime,
|
|
322
|
+
})),
|
|
209
323
|
h(WorkspaceEditor, {
|
|
210
324
|
workspace: account.workspace,
|
|
211
325
|
disabled: Boolean(busy),
|
|
@@ -216,19 +330,25 @@ export function WhatsappAccountCard({
|
|
|
216
330
|
disabled: Boolean(busy),
|
|
217
331
|
onSave: onAgentPresetSave,
|
|
218
332
|
}),
|
|
333
|
+
h(WhatsappAccessSettings, {
|
|
334
|
+
account,
|
|
335
|
+
busy: Boolean(busy),
|
|
336
|
+
onSave: onAccessPolicySave,
|
|
337
|
+
}),
|
|
219
338
|
h('div', { className: 'ddt-accountFooter dim-cardFooter' },
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
h(
|
|
230
|
-
className: 'dim-
|
|
231
|
-
|
|
339
|
+
h('div', { className: 'dim-cardFooterLayout' },
|
|
340
|
+
h('div', { className: 'ddt-actions dim-cardActions' },
|
|
341
|
+
h(Button, {
|
|
342
|
+
className: 'dim-cardAction', onClick: onReconnect, disabled: Boolean(busy),
|
|
343
|
+
}, busy === 'reconnect' ? '检查中…' : account.connected ? '检查连接' : '重试连接'),
|
|
344
|
+
h(Button, {
|
|
345
|
+
className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy),
|
|
346
|
+
}, '移除接入')),
|
|
347
|
+
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
348
|
+
testNotice ? h('div', {
|
|
349
|
+
className: 'ddt-summary dim-cardFeedback',
|
|
350
|
+
role: 'status',
|
|
351
|
+
}, testNotice) : null))),
|
|
232
352
|
removing ? h(RemoveConfirmation, {
|
|
233
353
|
account,
|
|
234
354
|
busy: busy === 'delete',
|
|
@@ -439,8 +559,11 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
439
559
|
|
|
440
560
|
const botList = model.bots.length > 0
|
|
441
561
|
? h('section', { className: 'dim-listSection' },
|
|
442
|
-
h(
|
|
443
|
-
|
|
562
|
+
h(ChannelListHeading, {
|
|
563
|
+
className: 'ddt-listHeading',
|
|
564
|
+
title: '已接入的 WhatsApp 机器人',
|
|
565
|
+
connectionLabel: 'WhatsApp Web',
|
|
566
|
+
}),
|
|
444
567
|
h('ul', { className: 'ddt-list dim-botList' }, model.bots.map((account) =>
|
|
445
568
|
h('li', { key: account.botId }, h(WhatsappAccountCard, {
|
|
446
569
|
account,
|
|
@@ -465,6 +588,12 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
465
588
|
WHATSAPP_ENDPOINTS.setAgentPreset,
|
|
466
589
|
{ botId: account.botId, agentPreset },
|
|
467
590
|
),
|
|
591
|
+
onAccessPolicySave: (accessPolicy) => botAction(
|
|
592
|
+
account,
|
|
593
|
+
'access',
|
|
594
|
+
WHATSAPP_ENDPOINTS.setAccessPolicy,
|
|
595
|
+
{ botId: account.botId, ...accessPolicy },
|
|
596
|
+
),
|
|
468
597
|
onRequestRemove: () => setRemoveTarget(account.botId),
|
|
469
598
|
onCancelRemove: () => setRemoveTarget(null),
|
|
470
599
|
onConfirmRemove: async () => {
|
|
@@ -4,6 +4,31 @@ const CSS = String.raw`
|
|
|
4
4
|
.dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
|
|
5
5
|
.dwa-avatar { color: #fff; background: #25d366; }
|
|
6
6
|
.dwa-avatar svg { display: block; }
|
|
7
|
+
.dwa-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
|
|
8
|
+
.dwa-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
9
|
+
.dwa-accessHeading > strong { font-size: 13px; }
|
|
10
|
+
.dwa-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
|
|
11
|
+
.dwa-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #08785f; background: #eafbf0; font-size: 11px; font-weight: 700; }
|
|
12
|
+
.dwa-accessBadge[data-mode="private-allowlist"] { color: #0f6f8f; background: #eaf7fd; }
|
|
13
|
+
.dwa-accessBadge[data-mode="open"] { color: #a15c00; background: #fff3d6; }
|
|
14
|
+
.dwa-accessHelp { position: relative; display: inline-flex; flex: none; }
|
|
15
|
+
.dwa-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #25d366 34%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #128c7e; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; }
|
|
16
|
+
.dwa-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 270px; max-width: min(290px, calc(100vw - 48px)); display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
17
|
+
.dwa-accessTooltipItem { display: grid; gap: 2px; }
|
|
18
|
+
.dwa-accessTooltipItem + .dwa-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
|
|
19
|
+
.dwa-accessTooltipItem strong { font-size: 12px; line-height: 17px; }
|
|
20
|
+
.dwa-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
|
|
21
|
+
.dwa-accessHelp:hover .dwa-accessTooltip, .dwa-accessHelp:focus-within .dwa-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
22
|
+
.dwa-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
|
|
23
|
+
.dwa-accessField select, .dwa-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
|
|
24
|
+
.dwa-accessField select { height: 34px; padding: 0 9px; }
|
|
25
|
+
.dwa-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
26
|
+
.dwa-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
|
|
27
|
+
.dwa-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
|
|
28
|
+
.dwa-accessWarning, .dwa-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
|
|
29
|
+
.dwa-accessWarning { color: #a15c00; }
|
|
30
|
+
.dwa-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
|
|
31
|
+
.dwa-accessActions { display: flex; justify-content: flex-end; }
|
|
7
32
|
`;
|
|
8
33
|
|
|
9
34
|
export function installWhatsappStyles() {
|
|
@@ -107,6 +107,7 @@ const EN = Object.freeze({
|
|
|
107
107
|
'离线': 'Offline',
|
|
108
108
|
'已断开': 'Disconnected',
|
|
109
109
|
'消息通道': 'Message channel',
|
|
110
|
+
'查看消息通道说明': 'View message channel details',
|
|
110
111
|
'最近检查': 'Last checked',
|
|
111
112
|
'当前工作区': 'Current workspace',
|
|
112
113
|
'选择目录': 'Choose folder',
|
|
@@ -136,6 +137,7 @@ const EN = Object.freeze({
|
|
|
136
137
|
'工作区路径必须指向一个目录。': 'The workspace path must point to a directory.',
|
|
137
138
|
'找不到要修改的机器人。': 'The bot could not be found.',
|
|
138
139
|
'Agent Preset': 'Agent Preset',
|
|
140
|
+
'查看 Agent Preset 说明': 'View Agent Preset help',
|
|
139
141
|
'跟随 Host 默认': 'Follow the Host default',
|
|
140
142
|
'(已不可用)': ' (unavailable)',
|
|
141
143
|
'只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。': 'This affects only new sessions. If the current chat already has a session, send /new, then send a regular message to apply it.',
|
|
@@ -358,6 +360,24 @@ const EN = Object.freeze({
|
|
|
358
360
|
'正在建立安全的关联设备会话。': 'Creating a secure linked-device session.',
|
|
359
361
|
'关联设备正在接入 DeepSeek Harness。': 'Linking the device to DeepSeek Harness.',
|
|
360
362
|
'WhatsApp Web 关联设备运行正常': 'WhatsApp linked device is healthy',
|
|
363
|
+
'查看 WhatsApp 访问模式说明': 'View WhatsApp access mode details',
|
|
364
|
+
'WhatsApp 访问模式': 'WhatsApp access mode',
|
|
365
|
+
'仅自己模式': 'Only me',
|
|
366
|
+
'指定联系人模式': 'Selected contacts',
|
|
367
|
+
'开放响应模式': 'Open responses',
|
|
368
|
+
'仅自己模式(默认)': 'Only me (default)',
|
|
369
|
+
'已生效:': 'Active: ',
|
|
370
|
+
'只响应已绑定 WhatsApp 账号的自聊消息。': 'Only respond to self-chat messages from the linked WhatsApp account.',
|
|
371
|
+
'响应自聊和白名单联系人的私聊,忽略群聊。': 'Respond to self-chat and allowlisted direct messages; ignore group messages.',
|
|
372
|
+
'响应所有私聊,以及群聊中的提及或回复。': 'Respond to all direct messages and to group mentions or replies.',
|
|
373
|
+
'允许私聊的 WhatsApp 电话号码': 'WhatsApp phone numbers allowed to send direct messages',
|
|
374
|
+
'每行一个含国家或地区代码的号码': 'One number with country or region code per line',
|
|
375
|
+
'可以包含开头的 +,保存时会自动移除。': 'A leading + is allowed and removed when saved.',
|
|
376
|
+
'仅指定联系人模式使用白名单,切换模式时会保留。': 'Only Selected contacts uses the allowlist; it is retained when modes change.',
|
|
377
|
+
'白名单为空;保存后将只接受自聊消息。': 'The allowlist is empty; only self-chat messages will be accepted after saving.',
|
|
378
|
+
'电话号码必须包含国家或地区代码,每行一个。': 'Each phone number must include a country or region code on its own line.',
|
|
379
|
+
'WhatsApp 访问设置暂不可用。': 'WhatsApp access settings are currently unavailable.',
|
|
380
|
+
'WhatsApp 访问设置保存失败。': 'Could not save WhatsApp access settings.',
|
|
361
381
|
'Bot API 长轮询': 'Bot API long polling',
|
|
362
382
|
' Gateway 长连接': ' Gateway persistent connection',
|
|
363
383
|
'Gateway 长连接': 'Gateway persistent connection',
|
|
@@ -87,6 +87,14 @@ const CSS = String.raw`
|
|
|
87
87
|
.dim-panel .dim-listSection { display: flex; flex-direction: column; gap: 0; }
|
|
88
88
|
.dim-panel .dim-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 6px; padding: 0; }
|
|
89
89
|
.dim-panel .dim-listHeading h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: normal; font-weight: 650; }
|
|
90
|
+
.dim-panel .dim-listTitle { min-width: 0; display: inline-flex; align-items: center; gap: 6px; }
|
|
91
|
+
.dim-panel .dim-channelHelp { position: relative; display: inline-flex; flex: none; }
|
|
92
|
+
.dim-panel .dim-channelHelpButton { width: 17px; height: 17px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #1677ff 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1677ff; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 11px; line-height: 1; font-weight: 700; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
|
|
93
|
+
.dim-panel .dim-channelHelpButton:hover { border-color: #1677ff; color: #0f5fce; background: color-mix(in srgb, #1677ff 8%, var(--dsw-alias-bg-layer-1, #fff)); }
|
|
94
|
+
.dim-panel .dim-channelHelpButton:focus-visible { outline: none; border-color: #1677ff; box-shadow: 0 0 0 3px color-mix(in srgb, #1677ff 16%, transparent); }
|
|
95
|
+
.dim-panel .dim-channelTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: max-content; max-width: min(280px, calc(100vw - 48px)); display: flex; align-items: baseline; gap: 5px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); font-size: 11px; line-height: 16px; font-weight: 400; white-space: normal; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
96
|
+
.dim-panel .dim-channelTooltip strong { color: var(--dsw-alias-label-primary, #1f2329); font-weight: 600; white-space: nowrap; }
|
|
97
|
+
.dim-panel .dim-channelHelp:hover .dim-channelTooltip, .dim-panel .dim-channelHelp:focus-within .dim-channelTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
90
98
|
.dim-panel .dim-botList { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
|
|
91
99
|
.dim-panel .dim-loadingView { padding: 38px; color: var(--dsw-alias-label-secondary, #646a73); text-align: center; }
|
|
92
100
|
.dim-panel .dim-loadingView h3 { margin: 0 0 7px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: normal; font-weight: 650; }
|
|
@@ -143,13 +151,18 @@ const CSS = String.raw`
|
|
|
143
151
|
.dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
|
|
144
152
|
.dim-panel .dim-workspacePath { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; display: block; overflow-x: auto; overflow-y: hidden; color: var(--dsw-alias-label-primary, #1f2329); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
|
|
145
153
|
.dim-panel .dim-preset { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 4px; margin-top: 6px; padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
|
146
|
-
.dim-panel .dim-presetHeader { display:
|
|
147
|
-
.dim-panel .dim-
|
|
154
|
+
.dim-panel .dim-presetHeader { position: relative; min-width: 0; grid-column: 1 / -1; grid-row: 1; display: flex; align-items: center; justify-content: space-between; gap: 10px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
|
|
155
|
+
.dim-panel .dim-presetTitle { min-width: 0; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
|
|
156
|
+
.dim-panel .dim-presetHelp { display: inline-flex; align-items: center; flex: none; }
|
|
157
|
+
.dim-panel .dim-presetHelpButton { width: 17px; height: 17px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #1677ff 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1677ff; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 11px; line-height: 1; font-weight: 700; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
|
|
158
|
+
.dim-panel .dim-presetHelpButton:hover { border-color: #1677ff; color: #0f5fce; background: color-mix(in srgb, #1677ff 8%, var(--dsw-alias-bg-layer-1, #fff)); }
|
|
159
|
+
.dim-panel .dim-presetHelpButton:focus-visible { outline: none; border-color: #1677ff; box-shadow: 0 0 0 3px color-mix(in srgb, #1677ff 16%, transparent); }
|
|
160
|
+
.dim-panel .dim-presetTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: min(320px, 100%); padding: 9px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); font-size: 11px; line-height: 16px; font-weight: 400; overflow-wrap: anywhere; white-space: normal; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
161
|
+
.dim-panel .dim-presetHelp:hover .dim-presetTooltip, .dim-panel .dim-presetHelp:focus-within .dim-presetTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
148
162
|
.dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
|
|
149
163
|
.dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
|
|
150
164
|
.dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
|
|
151
|
-
.dim-panel .dim-
|
|
152
|
-
.dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 4; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
|
|
165
|
+
.dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 3; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
|
|
153
166
|
.dim-directoryPickerBackdrop { --dim-blue: var(--dsw-alias-state-business-primary, #3370ff); --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent); position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: rgb(15 17 21 / 42%); backdrop-filter: blur(3px); }
|
|
154
167
|
.dim-directoryPickerBackdrop, .dim-directoryPickerBackdrop *, .dim-directoryPickerBackdrop *::before, .dim-directoryPickerBackdrop *::after { box-sizing: border-box; }
|
|
155
168
|
.dim-directoryPicker { width: min(720px, 100%); height: min(620px, calc(100vh - 48px)); min-height: 420px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 18px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 24px 72px rgb(15 17 21 / 24%); }
|
|
@@ -192,6 +205,9 @@ const CSS = String.raw`
|
|
|
192
205
|
.dim-directoryPickerActions button:hover:not(:disabled) { filter: brightness(.97); }
|
|
193
206
|
.dim-directoryPickerActions button:disabled { cursor: not-allowed; opacity: .52; }
|
|
194
207
|
.dim-panel .dim-cardSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; }
|
|
208
|
+
.dim-panel .dim-cardFooterLayout { min-width: 0; width: 100%; display: flex; flex-direction: column; align-items: stretch; gap: 9px; }
|
|
209
|
+
.dim-panel .dim-cardFooterLayout > .dim-cardActions { align-self: flex-end; }
|
|
210
|
+
.dim-panel .dim-cardFeedback { width: 100%; padding: 8px 10px; border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font: inherit; font-size: 12px; font-weight: 400; line-height: 18px; overflow-wrap: anywhere; white-space: normal; }
|
|
195
211
|
.dim-panel .dim-cardActions { flex: none; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; margin: 0 0 0 auto; }
|
|
196
212
|
.dim-panel .dim-cardActions .dim-cardAction { flex: none; min-height: 32px; padding: 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
|
|
197
213
|
.dim-panel .dim-cardActions .dim-cardAction:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
@@ -206,15 +222,13 @@ const CSS = String.raw`
|
|
|
206
222
|
.dim-panel .dim-botName { min-width: 0; }
|
|
207
223
|
.dim-panel .dim-botName h3 { overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; font-weight: 650; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
208
224
|
.dim-panel .dim-botName p { overflow: hidden; margin: 4px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
225
|
+
.dim-panel .dim-botHealthGroup { flex: none; display: grid; justify-items: end; gap: 5px; }
|
|
209
226
|
.dim-panel .dim-botCard .dim-botHealth { flex: none; min-height: 0; display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; border-radius: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
|
227
|
+
.dim-panel .dim-lastChecked { display: inline-flex; align-items: baseline; gap: 4px; color: var(--dsw-alias-label-tertiary, #8f959e); font: inherit; font-size: 11px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
|
210
228
|
.dim-panel .dim-botCard .dim-healthDot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: #aeb3bb; box-shadow: none; }
|
|
211
229
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary, #20a162) 14%, transparent); }
|
|
212
230
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="warning"] { background: var(--dsw-alias-state-warn-primary, #d97706); }
|
|
213
231
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
|
|
214
|
-
.dim-panel .dim-botMetrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 6px 0; }
|
|
215
|
-
.dim-panel .dim-botMetric { min-width: 0; padding: 6px 8px; border: 0; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
216
|
-
.dim-panel .dim-botMetric dt { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; font-weight: 400; line-height: normal; }
|
|
217
|
-
.dim-panel .dim-botMetric dd { overflow: hidden; margin: 3px 0 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 400; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
218
232
|
.dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
|
|
219
233
|
.dim-panel .ddt-headingCopy { display: none; }
|
|
220
234
|
.dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
|
|
@@ -245,6 +259,7 @@ const CSS = String.raw`
|
|
|
245
259
|
}
|
|
246
260
|
@media (max-width: 720px) {
|
|
247
261
|
.dim-panel .dim-botCardTop { flex-direction: column; align-items: stretch; }
|
|
262
|
+
.dim-panel .dim-botHealthGroup { justify-items: start; }
|
|
248
263
|
}
|
|
249
264
|
@media (max-width: 560px) {
|
|
250
265
|
.dim-title { flex-direction: column; gap: 10px; }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
2
|
|
|
3
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
4
|
+
import { normalizeWhatsappAccessPolicy } from '../../../../src/channels/whatsapp/config-store.mjs';
|
|
4
5
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
5
6
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
6
7
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -13,6 +14,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
13
14
|
cancelProvisioning: 'provision.cancel',
|
|
14
15
|
reconnectBot: 'bot.reconnect',
|
|
15
16
|
deleteBot: 'bot.delete',
|
|
17
|
+
setAccessPolicy: 'bot.access-policy.set',
|
|
16
18
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
17
19
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
18
20
|
});
|
|
@@ -50,6 +52,17 @@ function payloadFailure(endpoint, payload) {
|
|
|
50
52
|
return exactKeys(payload, ['botId', 'confirm']) && validId(payload.botId)
|
|
51
53
|
&& payload.confirm === true ? null : 'bot.delete requires a botId and confirm=true.';
|
|
52
54
|
}
|
|
55
|
+
if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
56
|
+
if (!exactKeys(payload, ['botId', 'accessMode', 'allowedNumbers'])
|
|
57
|
+
|| Object.keys(payload).length !== 3
|
|
58
|
+
|| !validId(payload.botId)) return '请输入有效的 WhatsApp 访问模式和电话号码。';
|
|
59
|
+
try {
|
|
60
|
+
normalizeWhatsappAccessPolicy(payload);
|
|
61
|
+
return null;
|
|
62
|
+
} catch {
|
|
63
|
+
return '请输入有效的 WhatsApp 访问模式和电话号码。';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
53
66
|
if (endpoint === WHATSAPP_ENDPOINTS.setWorkspace) {
|
|
54
67
|
return validWorkspacePayload(payload)
|
|
55
68
|
? null : '请输入工作区绝对路径。';
|
|
@@ -92,7 +105,7 @@ async function publicStatus(value, encodeQr) {
|
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
95
|
-
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'reconnectBot', 'deleteBot']) {
|
|
108
|
+
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'reconnectBot', 'deleteBot', 'setAccessPolicy']) {
|
|
96
109
|
if (typeof controller?.[method] !== 'function') {
|
|
97
110
|
throw new TypeError(`A complete WhatsApp controller is required (${method})`);
|
|
98
111
|
}
|
|
@@ -166,6 +179,11 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
166
179
|
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
167
180
|
cachedEncode,
|
|
168
181
|
);
|
|
182
|
+
} else if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
183
|
+
value = await publicStatus(
|
|
184
|
+
await controller.setAccessPolicy(payload.botId, normalizeWhatsappAccessPolicy(payload)),
|
|
185
|
+
cachedEncode,
|
|
186
|
+
);
|
|
169
187
|
} else {
|
|
170
188
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
171
189
|
}
|
|
@@ -8,9 +8,15 @@ import { apply as applyTelegram } from './channels/telegram/index.mjs';
|
|
|
8
8
|
import { apply as applyWecom } from './channels/wecom/index.mjs';
|
|
9
9
|
import { apply as applyWeixin } from './channels/weixin/index.mjs';
|
|
10
10
|
import { apply as applyWhatsapp } from './channels/whatsapp/index.mjs';
|
|
11
|
+
import { installOutboundArtifactTool } from '../../src/channels/shared/semantic/artifact.mjs';
|
|
11
12
|
|
|
12
13
|
export const name = 'dsh-im-host';
|
|
13
|
-
export const inject = [
|
|
14
|
+
export const inject = [
|
|
15
|
+
'connection',
|
|
16
|
+
'credentials',
|
|
17
|
+
'webServer',
|
|
18
|
+
'typertGateway',
|
|
19
|
+
];
|
|
14
20
|
|
|
15
21
|
function channelConfig(config, name) {
|
|
16
22
|
const channel = config[name] ?? {};
|
|
@@ -34,6 +40,13 @@ export function createImHostPlugin(internals = {}) {
|
|
|
34
40
|
name,
|
|
35
41
|
inject,
|
|
36
42
|
async apply(ctx, config = {}) {
|
|
43
|
+
if (typeof ctx?.inject === 'function') {
|
|
44
|
+
ctx.inject(['tools', 'systemPrompt'], (artifactCtx) => {
|
|
45
|
+
installOutboundArtifactTool(artifactCtx);
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
installOutboundArtifactTool(ctx);
|
|
49
|
+
}
|
|
37
50
|
await startFeishu(ctx, channelConfig(config, 'feishu'));
|
|
38
51
|
await startWeixin(ctx, channelConfig(config, 'weixin'));
|
|
39
52
|
await startDingtalk(ctx, channelConfig(config, 'dingtalk'));
|