@xmanrui/dsh-im 4.2.1 → 4.4.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 +4 -2
- package/README.md +4 -2
- package/lib/client.js +1267 -787
- package/lib/index.js +237 -235
- package/package.json +1 -1
- package/plugin-src/client/access-policy-settings.js +351 -0
- package/plugin-src/client/channel-card-meta.js +2 -1
- package/plugin-src/client/channels/dingtalk/api.js +5 -0
- package/plugin-src/client/channels/dingtalk/index.js +1 -0
- package/plugin-src/client/channels/feishu/api.js +5 -0
- package/plugin-src/client/channels/feishu/index.js +1 -0
- package/plugin-src/client/channels/qq/api.js +5 -0
- package/plugin-src/client/channels/qq/index.js +1 -0
- package/plugin-src/client/channels/shared/token-api.js +5 -0
- package/plugin-src/client/channels/shared/token-channel.js +1 -0
- package/plugin-src/client/channels/telegram/api.js +2 -17
- package/plugin-src/client/channels/telegram/index.js +0 -112
- package/plugin-src/client/channels/telegram/styles.js +0 -28
- package/plugin-src/client/channels/wecom/api.js +5 -0
- package/plugin-src/client/channels/wecom/index.js +1 -0
- package/plugin-src/client/channels/weixin/api.js +5 -0
- package/plugin-src/client/channels/weixin/index.js +1 -0
- package/plugin-src/client/channels/whatsapp/api.js +4 -10
- package/plugin-src/client/channels/whatsapp/index.js +1 -125
- package/plugin-src/client/channels/whatsapp/styles.js +0 -25
- package/plugin-src/client/context-enhancement.js +207 -102
- package/plugin-src/client/delivery-settings.js +61 -9
- package/plugin-src/client/i18n.js +71 -3
- package/plugin-src/client/index.js +1 -0
- package/plugin-src/client/styles.js +59 -8
- package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +39 -1
- package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
- package/plugin-src/host/channels/qq/production.mjs +12 -1
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
- package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
- package/plugin-src/host/channels/shared/production.mjs +15 -1
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/slack/production.mjs +12 -1
- package/plugin-src/host/channels/slack/rpc.mjs +10 -0
- package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
- package/plugin-src/host/channels/wecom/production.mjs +12 -1
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +12 -1
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
- package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
- package/scripts/verify-package.mjs +4 -3
- package/src/channels/dingtalk/dingtalk-bridge.mjs +61 -9
- package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
- package/src/channels/discord/discord-runtime.mjs +29 -1
- package/src/channels/feishu/bridge.mjs +286 -120
- package/src/channels/feishu/feishu-runtime.mjs +4 -0
- package/src/channels/feishu/message-utils.mjs +5 -0
- package/src/channels/qq/qq-bridge.mjs +59 -17
- package/src/channels/qq/qq-runtime.mjs +25 -3
- package/src/channels/shared/access-policy.mjs +210 -0
- package/src/channels/shared/bot-workspace-store.mjs +176 -33
- package/src/channels/shared/command-permission.mjs +32 -0
- package/src/channels/shared/compact-command.mjs +5 -2
- package/src/channels/shared/context-enhancement.mjs +73 -20
- package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
- package/src/channels/shared/inbound-access.mjs +43 -0
- package/src/channels/shared/text-harness-bridge.mjs +47 -4
- package/src/channels/shared/workspace-command.mjs +10 -1
- package/src/channels/slack/slack-runtime.mjs +4 -0
- package/src/channels/telegram/telegram-runtime.mjs +5 -13
- package/src/channels/wecom/wecom-bridge.mjs +51 -4
- package/src/channels/wecom/wecom-runtime.mjs +4 -0
- package/src/channels/weixin/weixin-bridge.mjs +59 -16
- package/src/channels/weixin/weixin-runtime.mjs +4 -0
- package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -20
|
@@ -2,9 +2,10 @@ import * as React from 'react';
|
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
CONTEXT_DIRECT_GUIDANCE_EXAMPLE,
|
|
5
6
|
CONTEXT_ENHANCEMENT_FIELDS,
|
|
6
7
|
CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
|
7
|
-
|
|
8
|
+
CONTEXT_GROUP_GUIDANCE_EXAMPLE,
|
|
8
9
|
normalizeContextEnhancementConfig,
|
|
9
10
|
validateContextEnhancementConfig,
|
|
10
11
|
} from '../../src/channels/shared/context-enhancement.mjs';
|
|
@@ -18,11 +19,34 @@ const FIELD_LABELS = Object.freeze({
|
|
|
18
19
|
botId: '机器人标识',
|
|
19
20
|
});
|
|
20
21
|
|
|
22
|
+
const SCOPE_COPY = Object.freeze({
|
|
23
|
+
group: Object.freeze({
|
|
24
|
+
title: '群聊',
|
|
25
|
+
enable: '启用',
|
|
26
|
+
fieldsHelpLabel: '查看群聊来源字段说明',
|
|
27
|
+
senderNameHelpLabel: '查看群聊发送者昵称字段说明',
|
|
28
|
+
guidanceLabel: '增强提示词',
|
|
29
|
+
guidanceHelpLabel: '查看群聊增强提示词使用说明',
|
|
30
|
+
guidanceUsage: '用于告诉模型如何使用当前群聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。',
|
|
31
|
+
guidanceBehavior: '仅在群聊开关开启时使用。清空并保存后不再附加群聊增强提示词;所选来源字段仍按当前场景设置发送。',
|
|
32
|
+
}),
|
|
33
|
+
direct: Object.freeze({
|
|
34
|
+
title: '私聊',
|
|
35
|
+
enable: '启用',
|
|
36
|
+
fieldsHelpLabel: '查看私聊来源字段说明',
|
|
37
|
+
senderNameHelpLabel: '查看私聊发送者昵称字段说明',
|
|
38
|
+
guidanceLabel: '增强提示词',
|
|
39
|
+
guidanceHelpLabel: '查看私聊增强提示词使用说明',
|
|
40
|
+
guidanceUsage: '用于告诉模型如何使用当前私聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。',
|
|
41
|
+
guidanceBehavior: '仅在私聊开关开启时使用。清空并保存后不再附加私聊增强提示词;所选来源字段仍按当前场景设置发送。',
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
|
|
21
45
|
export function contextEnhancementLabel(config) {
|
|
22
|
-
const {
|
|
23
|
-
if (
|
|
24
|
-
if (
|
|
25
|
-
if (
|
|
46
|
+
const { group, direct } = normalizeContextEnhancementConfig(config);
|
|
47
|
+
if (group.enabled && direct.enabled) return '群聊和私聊';
|
|
48
|
+
if (group.enabled) return '仅群聊';
|
|
49
|
+
if (direct.enabled) return '仅私聊';
|
|
26
50
|
return '未开启';
|
|
27
51
|
}
|
|
28
52
|
|
|
@@ -37,27 +61,132 @@ function ContextIcon({ kind = 'sliders' }) {
|
|
|
37
61
|
}, h('path', { d: path }));
|
|
38
62
|
}
|
|
39
63
|
|
|
64
|
+
function ContextEnhancementScopeEditor({
|
|
65
|
+
kind,
|
|
66
|
+
scope,
|
|
67
|
+
example,
|
|
68
|
+
supported,
|
|
69
|
+
busy,
|
|
70
|
+
idPrefix,
|
|
71
|
+
onChange,
|
|
72
|
+
}) {
|
|
73
|
+
const copy = SCOPE_COPY[kind];
|
|
74
|
+
const unavailableId = `${idPrefix}-${kind}-unavailable`;
|
|
75
|
+
const fieldsHelpId = `${idPrefix}-${kind}-fields-help`;
|
|
76
|
+
const senderNameHelpId = `${idPrefix}-${kind}-sender-name-help`;
|
|
77
|
+
const guidanceId = `${idPrefix}-${kind}-guidance`;
|
|
78
|
+
const guidanceHelpId = `${idPrefix}-${kind}-guidance-help`;
|
|
79
|
+
const disabled = busy || !supported;
|
|
80
|
+
|
|
81
|
+
return h('fieldset', {
|
|
82
|
+
className: 'dim-contextSection dim-contextScope',
|
|
83
|
+
'data-context-kind': kind,
|
|
84
|
+
'aria-label': copy.title,
|
|
85
|
+
disabled,
|
|
86
|
+
},
|
|
87
|
+
h('label', { className: 'dim-contextSwitchRow' },
|
|
88
|
+
h('span', { className: 'dim-contextSwitchLabel' },
|
|
89
|
+
h('span', null, copy.enable),
|
|
90
|
+
!supported ? h('span', {
|
|
91
|
+
id: unavailableId, className: 'dim-contextUnavailable',
|
|
92
|
+
}, '(当前渠道不支持群聊)') : null),
|
|
93
|
+
h('input', {
|
|
94
|
+
type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
|
|
95
|
+
checked: supported && scope.enabled,
|
|
96
|
+
disabled,
|
|
97
|
+
'aria-describedby': !supported ? unavailableId : undefined,
|
|
98
|
+
onChange: (event) => { if (supported) onChange('enabled', event.target.checked); },
|
|
99
|
+
})),
|
|
100
|
+
h('div', { className: 'dim-contextScopeBlock' },
|
|
101
|
+
h('div', { className: 'dim-contextLegend' },
|
|
102
|
+
h('span', null, '来源字段'),
|
|
103
|
+
h('span', { className: 'dim-contextHelp dim-contextLegendHelp' },
|
|
104
|
+
h('button', {
|
|
105
|
+
type: 'button', className: 'dim-contextHelpButton', disabled,
|
|
106
|
+
'aria-label': copy.fieldsHelpLabel, 'aria-describedby': fieldsHelpId,
|
|
107
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
108
|
+
h('span', { id: fieldsHelpId, className: 'dim-contextTooltip dim-contextLegendTooltip', role: 'tooltip' },
|
|
109
|
+
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送当前会话中勾选且可用的字段,不会额外查询或补全。'))),
|
|
110
|
+
h('div', { className: 'dim-contextFields' }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
|
|
111
|
+
const fieldId = `${idPrefix}-${kind}-field-${field}`;
|
|
112
|
+
return h('div', { key: field, className: 'dim-contextField' },
|
|
113
|
+
h('input', {
|
|
114
|
+
id: fieldId, type: 'checkbox', name: `${kind}-${field}`,
|
|
115
|
+
checked: scope.fields.includes(field), disabled,
|
|
116
|
+
onChange: (event) => onChange('fields', event.target.checked
|
|
117
|
+
? [...scope.fields, field] : scope.fields.filter((value) => value !== field)),
|
|
118
|
+
}),
|
|
119
|
+
h('span', { className: 'dim-contextFieldText' },
|
|
120
|
+
h('label', { className: 'dim-contextFieldName', htmlFor: fieldId }, FIELD_LABELS[field]),
|
|
121
|
+
field === 'senderName' ? h('span', { className: 'dim-contextHelp dim-contextFieldHelp' },
|
|
122
|
+
h('button', {
|
|
123
|
+
type: 'button', className: 'dim-contextHelpButton dim-contextFieldHelpButton', disabled,
|
|
124
|
+
'aria-label': copy.senderNameHelpLabel, 'aria-describedby': senderNameHelpId,
|
|
125
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
126
|
+
h('span', {
|
|
127
|
+
id: senderNameHelpId,
|
|
128
|
+
className: 'dim-contextTooltip dim-contextFieldTooltip',
|
|
129
|
+
role: 'tooltip',
|
|
130
|
+
}, '该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。')) : null,
|
|
131
|
+
h('label', { className: 'dim-contextFieldKey', htmlFor: fieldId }, field)));
|
|
132
|
+
}))),
|
|
133
|
+
h('div', { className: 'dim-contextGuidance dim-contextScopeBlock' },
|
|
134
|
+
h('div', { className: 'dim-contextEditorHeader' },
|
|
135
|
+
h('span', { className: 'dim-contextEditorTitle' },
|
|
136
|
+
h('label', { htmlFor: guidanceId }, copy.guidanceLabel),
|
|
137
|
+
h('span', { className: 'dim-contextHelp' },
|
|
138
|
+
h('button', {
|
|
139
|
+
type: 'button', className: 'dim-contextHelpButton', disabled,
|
|
140
|
+
'aria-label': copy.guidanceHelpLabel, 'aria-describedby': guidanceHelpId,
|
|
141
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
142
|
+
h('span', { id: guidanceHelpId, className: 'dim-contextTooltip dim-contextGuidanceTooltip', role: 'tooltip' },
|
|
143
|
+
h('strong', null, '使用说明'),
|
|
144
|
+
h('span', null, copy.guidanceUsage),
|
|
145
|
+
h('strong', null, '生效规则'),
|
|
146
|
+
h('span', null, copy.guidanceBehavior),
|
|
147
|
+
h('strong', null, '隐私提示'),
|
|
148
|
+
h('span', null, '发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。'),
|
|
149
|
+
h('strong', null, '使用示例'),
|
|
150
|
+
h('span', { className: 'dim-contextTooltipExample' }, example)))),
|
|
151
|
+
h('div', { className: 'dim-contextTextActions' },
|
|
152
|
+
h('button', { type: 'button', disabled, onClick: () => onChange('guidance', example) }, '填入示例'),
|
|
153
|
+
h('button', { type: 'button', disabled, onClick: () => onChange('guidance', '') }, '清空'))),
|
|
154
|
+
h('textarea', {
|
|
155
|
+
id: guidanceId, value: scope.guidance, placeholder: example, rows: 4, disabled,
|
|
156
|
+
'data-context-kind': kind,
|
|
157
|
+
maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
|
158
|
+
'aria-describedby': guidanceHelpId,
|
|
159
|
+
onChange: (event) => onChange('guidance', event.target.value),
|
|
160
|
+
})));
|
|
161
|
+
}
|
|
162
|
+
|
|
40
163
|
function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id }) {
|
|
41
164
|
// A mounted dialog owns its draft; status refreshes must not replace unsaved edits.
|
|
42
|
-
const [draft, setDraft] = React.useState(() =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
165
|
+
const [draft, setDraft] = React.useState(() => {
|
|
166
|
+
const normalized = normalizeContextEnhancementConfig(config);
|
|
167
|
+
return {
|
|
168
|
+
...normalized,
|
|
169
|
+
...(!groupSupported ? {
|
|
170
|
+
group: { ...normalized.group, enabled: false },
|
|
171
|
+
} : {}),
|
|
172
|
+
};
|
|
173
|
+
});
|
|
46
174
|
const [saving, setSaving] = React.useState(false);
|
|
47
175
|
const [error, setError] = React.useState(null);
|
|
176
|
+
const [activeScope, setActiveScope] = React.useState('direct');
|
|
48
177
|
const savingRef = React.useRef(false);
|
|
49
178
|
const dialogRef = React.useRef(null);
|
|
50
179
|
const mountedRef = React.useRef(true);
|
|
180
|
+
const groupTabRef = React.useRef(null);
|
|
181
|
+
const directTabRef = React.useRef(null);
|
|
51
182
|
const titleId = React.useId();
|
|
52
183
|
const descriptionId = React.useId();
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const fieldIdPrefix = React.useId();
|
|
57
|
-
const fieldsHelpId = React.useId();
|
|
58
|
-
const senderNameHelpId = React.useId();
|
|
59
|
-
const guidanceExample = localizeText(CONTEXT_GUIDANCE_EXAMPLE);
|
|
184
|
+
const scopeIdPrefix = React.useId();
|
|
185
|
+
const groupGuidanceExample = localizeText(CONTEXT_GROUP_GUIDANCE_EXAMPLE);
|
|
186
|
+
const directGuidanceExample = localizeText(CONTEXT_DIRECT_GUIDANCE_EXAMPLE);
|
|
60
187
|
const busy = disabled || saving;
|
|
188
|
+
const scopeKinds = ['direct', 'group'];
|
|
189
|
+
const tabRefs = { group: groupTabRef, direct: directTabRef };
|
|
61
190
|
|
|
62
191
|
React.useEffect(() => {
|
|
63
192
|
mountedRef.current = true;
|
|
@@ -73,9 +202,12 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|
|
73
202
|
};
|
|
74
203
|
}, [returnFocusRef]);
|
|
75
204
|
|
|
76
|
-
const
|
|
205
|
+
const changeScope = (kind, key, value) => {
|
|
77
206
|
if (busy || savingRef.current) return;
|
|
78
|
-
setDraft((current) => ({
|
|
207
|
+
setDraft((current) => ({
|
|
208
|
+
...current,
|
|
209
|
+
[kind]: { ...current[kind], [key]: value },
|
|
210
|
+
}));
|
|
79
211
|
setError(null);
|
|
80
212
|
};
|
|
81
213
|
|
|
@@ -83,6 +215,25 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|
|
83
215
|
if (!savingRef.current) onClose();
|
|
84
216
|
};
|
|
85
217
|
|
|
218
|
+
const activateScope = (kind, focus = false) => {
|
|
219
|
+
if (busy || !scopeKinds.includes(kind)) return;
|
|
220
|
+
setActiveScope(kind);
|
|
221
|
+
if (focus) tabRefs[kind].current?.focus?.();
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const handleTabKeyDown = (event, kind) => {
|
|
225
|
+
let next;
|
|
226
|
+
if (event.key === 'Home') next = scopeKinds[0];
|
|
227
|
+
else if (event.key === 'End') next = scopeKinds.at(-1);
|
|
228
|
+
else if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
|
229
|
+
const offset = event.key === 'ArrowRight' ? 1 : -1;
|
|
230
|
+
next = scopeKinds[(scopeKinds.indexOf(kind) + offset + scopeKinds.length) % scopeKinds.length];
|
|
231
|
+
}
|
|
232
|
+
if (!next) return;
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
activateScope(next, true);
|
|
235
|
+
};
|
|
236
|
+
|
|
86
237
|
const save = async () => {
|
|
87
238
|
if (busy || savingRef.current) return;
|
|
88
239
|
savingRef.current = true;
|
|
@@ -155,90 +306,42 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|
|
155
306
|
type: 'button', className: 'dim-contextClose', 'aria-label': '关闭弹窗',
|
|
156
307
|
disabled: saving, onClick: cancel,
|
|
157
308
|
}, h(ContextIcon, { kind: 'close' }))),
|
|
158
|
-
h('
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
h('
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
h('input', {
|
|
177
|
-
type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
|
|
178
|
-
checked: draft.directEnabled, disabled: busy,
|
|
179
|
-
onChange: (event) => change('directEnabled', event.target.checked),
|
|
180
|
-
}))),
|
|
181
|
-
),
|
|
182
|
-
h('fieldset', { className: 'dim-contextSection', disabled: busy },
|
|
183
|
-
h('legend', { className: 'dim-contextLegend' },
|
|
184
|
-
h('span', null, '来源字段'),
|
|
185
|
-
h('span', { className: 'dim-contextHelp dim-contextLegendHelp' },
|
|
186
|
-
h('button', {
|
|
187
|
-
type: 'button', className: 'dim-contextHelpButton', disabled: busy,
|
|
188
|
-
'aria-label': '查看来源字段说明', 'aria-describedby': fieldsHelpId,
|
|
189
|
-
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
190
|
-
h('span', { id: fieldsHelpId, className: 'dim-contextTooltip dim-contextLegendTooltip', role: 'tooltip' },
|
|
191
|
-
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送勾选且当前消息中可用的字段,不会额外查询或补全。'))),
|
|
192
|
-
h('div', { className: 'dim-contextFields' }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
|
|
193
|
-
const fieldId = `${fieldIdPrefix}-${field}`;
|
|
194
|
-
return h('div', { key: field, className: 'dim-contextField' },
|
|
195
|
-
h('input', {
|
|
196
|
-
id: fieldId, type: 'checkbox', name: field,
|
|
197
|
-
checked: draft.fields.includes(field), disabled: busy,
|
|
198
|
-
onChange: (event) => change('fields', event.target.checked
|
|
199
|
-
? [...draft.fields, field] : draft.fields.filter((value) => value !== field)),
|
|
200
|
-
}),
|
|
201
|
-
h('span', { className: 'dim-contextFieldText' },
|
|
202
|
-
h('label', { className: 'dim-contextFieldName', htmlFor: fieldId }, FIELD_LABELS[field]),
|
|
203
|
-
field === 'senderName' ? h('span', { className: 'dim-contextHelp dim-contextFieldHelp' },
|
|
204
|
-
h('button', {
|
|
205
|
-
type: 'button', className: 'dim-contextHelpButton dim-contextFieldHelpButton', disabled: busy,
|
|
206
|
-
'aria-label': '查看发送者昵称字段说明', 'aria-describedby': senderNameHelpId,
|
|
207
|
-
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
208
|
-
h('span', {
|
|
209
|
-
id: senderNameHelpId,
|
|
210
|
-
className: 'dim-contextTooltip dim-contextFieldTooltip',
|
|
211
|
-
role: 'tooltip',
|
|
212
|
-
}, '该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。')) : null,
|
|
213
|
-
h('label', { className: 'dim-contextFieldKey', htmlFor: fieldId }, field)));
|
|
214
|
-
}))),
|
|
215
|
-
h('div', { className: 'dim-contextGuidance' },
|
|
216
|
-
h('div', { className: 'dim-contextEditorHeader' },
|
|
217
|
-
h('span', { className: 'dim-contextEditorTitle' },
|
|
218
|
-
h('label', { htmlFor: guidanceId }, '增强提示词'),
|
|
219
|
-
h('span', { className: 'dim-contextHelp' },
|
|
220
|
-
h('button', {
|
|
221
|
-
type: 'button', className: 'dim-contextHelpButton', disabled: busy,
|
|
222
|
-
'aria-label': '查看增强提示词使用说明', 'aria-describedby': guidanceHelpId,
|
|
223
|
-
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
224
|
-
h('span', { id: guidanceHelpId, className: 'dim-contextTooltip dim-contextGuidanceTooltip', role: 'tooltip' },
|
|
225
|
-
h('strong', null, '使用说明'),
|
|
226
|
-
h('span', null, '用于告诉模型如何使用 <dsh_im_source> 中已选择的来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。'),
|
|
227
|
-
h('strong', null, '生效规则'),
|
|
228
|
-
h('span', null, '只需填写正文,插件自动添加 <dsh_im_source_guidance> 成对标签。清空并保存后,不再附加增强提示词,已选来源字段仍按开关设置发送。'),
|
|
229
|
-
h('strong', null, '隐私提示'),
|
|
230
|
-
h('span', null, '发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。'),
|
|
231
|
-
h('strong', null, '使用示例'),
|
|
232
|
-
h('span', { className: 'dim-contextTooltipExample' }, guidanceExample)))),
|
|
233
|
-
h('div', { className: 'dim-contextTextActions' },
|
|
234
|
-
h('button', { type: 'button', disabled: busy, onClick: () => change('guidance', guidanceExample) }, '填入示例'),
|
|
235
|
-
h('button', { type: 'button', disabled: busy, onClick: () => change('guidance', '') }, '清空'))),
|
|
236
|
-
h('textarea', {
|
|
237
|
-
id: guidanceId, value: draft.guidance, placeholder: guidanceExample, rows: 4, disabled: busy,
|
|
238
|
-
maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
|
239
|
-
'aria-describedby': guidanceHelpId,
|
|
240
|
-
onChange: (event) => change('guidance', event.target.value),
|
|
309
|
+
h('div', { className: 'dim-contextTabs', role: 'tablist', 'aria-label': '上下文增强范围' },
|
|
310
|
+
scopeKinds.map((kind) => {
|
|
311
|
+
const selected = activeScope === kind;
|
|
312
|
+
return h('button', {
|
|
313
|
+
key: kind,
|
|
314
|
+
id: `${scopeIdPrefix}-${kind}-tab`,
|
|
315
|
+
ref: tabRefs[kind],
|
|
316
|
+
type: 'button',
|
|
317
|
+
role: 'tab',
|
|
318
|
+
className: 'dim-contextTab',
|
|
319
|
+
'data-context-kind': kind,
|
|
320
|
+
'aria-selected': selected,
|
|
321
|
+
'aria-controls': `${scopeIdPrefix}-${kind}-panel`,
|
|
322
|
+
tabIndex: selected ? 0 : -1,
|
|
323
|
+
disabled: busy,
|
|
324
|
+
onClick: () => activateScope(kind),
|
|
325
|
+
onKeyDown: (event) => handleTabKeyDown(event, kind),
|
|
326
|
+
}, SCOPE_COPY[kind].title);
|
|
241
327
|
})),
|
|
328
|
+
scopeKinds.map((kind) => h('div', {
|
|
329
|
+
key: kind,
|
|
330
|
+
id: `${scopeIdPrefix}-${kind}-panel`,
|
|
331
|
+
className: 'dim-contextTabPanel',
|
|
332
|
+
role: 'tabpanel',
|
|
333
|
+
'data-context-kind': kind,
|
|
334
|
+
'aria-labelledby': `${scopeIdPrefix}-${kind}-tab`,
|
|
335
|
+
hidden: activeScope !== kind,
|
|
336
|
+
}, h(ContextEnhancementScopeEditor, {
|
|
337
|
+
kind,
|
|
338
|
+
scope: draft[kind],
|
|
339
|
+
example: kind === 'group' ? groupGuidanceExample : directGuidanceExample,
|
|
340
|
+
supported: kind === 'group' ? groupSupported : true,
|
|
341
|
+
busy,
|
|
342
|
+
idPrefix: scopeIdPrefix,
|
|
343
|
+
onChange: (key, value) => changeScope(kind, key, value),
|
|
344
|
+
}))),
|
|
242
345
|
error ? h('p', { className: 'dim-contextError', role: 'alert' }, error) : null,
|
|
243
346
|
h('footer', { className: 'dim-contextFooter' },
|
|
244
347
|
h('button', { type: 'button', disabled: saving, onClick: cancel }, '取消'),
|
|
@@ -256,7 +359,9 @@ export function ContextEnhancementEditor({ config, groupSupported = true, disabl
|
|
|
256
359
|
const dialogId = React.useId();
|
|
257
360
|
const statusId = React.useId();
|
|
258
361
|
const saved = normalizeContextEnhancementConfig(config);
|
|
259
|
-
const label = contextEnhancementLabel(groupSupported ? saved : {
|
|
362
|
+
const label = contextEnhancementLabel(groupSupported ? saved : {
|
|
363
|
+
...saved, group: { ...saved.group, enabled: false },
|
|
364
|
+
});
|
|
260
365
|
|
|
261
366
|
return h(React.Fragment, null,
|
|
262
367
|
h('button', {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
import { AccessPolicySettingsPage } from './access-policy-settings.js';
|
|
3
4
|
import { h, isEnglish, localizeText } from './i18n.js';
|
|
4
5
|
|
|
5
6
|
export const DELIVERY_RPC_CHANNEL = '/dsh-im-delivery';
|
|
@@ -18,6 +19,11 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
test: 'target.test',
|
|
19
20
|
});
|
|
20
21
|
|
|
22
|
+
export const BOT_SETTINGS_TABS = Object.freeze([
|
|
23
|
+
Object.freeze({ id: 'delivery', label: '投递设置' }),
|
|
24
|
+
Object.freeze({ id: 'access', label: '访问设置' }),
|
|
25
|
+
]);
|
|
26
|
+
|
|
21
27
|
const CHANNEL_DEFINITIONS = Object.freeze({
|
|
22
28
|
weixin: {
|
|
23
29
|
label: '微信',
|
|
@@ -532,8 +538,15 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
532
538
|
: null);
|
|
533
539
|
}
|
|
534
540
|
|
|
535
|
-
export function DeliveryTargetSettingsPage({
|
|
541
|
+
export function DeliveryTargetSettingsPage({
|
|
542
|
+
channel,
|
|
543
|
+
account,
|
|
544
|
+
rpcCall,
|
|
545
|
+
accessRpcCall,
|
|
546
|
+
onBack,
|
|
547
|
+
}) {
|
|
536
548
|
const definition = CHANNEL_DEFINITIONS[channel];
|
|
549
|
+
const [activeTabId, setActiveTabId] = React.useState(BOT_SETTINGS_TABS[0].id);
|
|
537
550
|
const [phase, setPhase] = React.useState('loading');
|
|
538
551
|
const [targets, setTargets] = React.useState([]);
|
|
539
552
|
const [suggestionPhase, setSuggestionPhase] = React.useState('idle');
|
|
@@ -543,8 +556,13 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack }
|
|
|
543
556
|
const [editor, setEditor] = React.useState(null);
|
|
544
557
|
const [saving, setSaving] = React.useState(false);
|
|
545
558
|
const [botCopyState, setBotCopyState] = React.useState(null);
|
|
559
|
+
const [accessPolicy, setAccessPolicy] = React.useState(account.accessPolicy);
|
|
546
560
|
const mounted = React.useRef(true);
|
|
547
561
|
|
|
562
|
+
React.useEffect(() => {
|
|
563
|
+
setAccessPolicy(account.accessPolicy);
|
|
564
|
+
}, [account.botId, account.accessPolicy]);
|
|
565
|
+
|
|
548
566
|
const invoke = React.useCallback(async (endpoint, payload = {}, signal) => {
|
|
549
567
|
if (typeof rpcCall !== 'function') throw new Error('投递目标设置暂不可用。');
|
|
550
568
|
return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
|
|
@@ -654,13 +672,51 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack }
|
|
|
654
672
|
}
|
|
655
673
|
};
|
|
656
674
|
|
|
675
|
+
const activeTab = BOT_SETTINGS_TABS.find((tab) => tab.id === activeTabId)
|
|
676
|
+
?? BOT_SETTINGS_TABS[0];
|
|
677
|
+
const activeTabDomId = `dim-bot-settings-${activeTab.id}-tab`;
|
|
678
|
+
const activePanelId = `dim-bot-settings-${activeTab.id}-panel`;
|
|
679
|
+
|
|
657
680
|
return h('section', {
|
|
658
681
|
className: 'dim-deliveryPage',
|
|
659
682
|
'aria-label': `${account.botName || definition.label}机器人设置`,
|
|
660
683
|
},
|
|
661
684
|
h('header', { className: 'dim-deliveryHeader' },
|
|
662
|
-
h(DeliveryButton, { className: 'dim-deliveryBack', onClick: onBack }, '← 返回机器人列表'),
|
|
663
|
-
|
|
685
|
+
h(DeliveryButton, { className: 'dim-deliveryBack', onClick: onBack }, '← 返回机器人列表')),
|
|
686
|
+
h('div', { className: 'dim-botSettingsTabsBar' },
|
|
687
|
+
h('nav', {
|
|
688
|
+
className: 'dim-botSettingsTabs',
|
|
689
|
+
role: 'tablist',
|
|
690
|
+
'aria-label': '机器人设置页签',
|
|
691
|
+
}, BOT_SETTINGS_TABS.map((tab) => h('button', {
|
|
692
|
+
key: tab.id,
|
|
693
|
+
id: `dim-bot-settings-${tab.id}-tab`,
|
|
694
|
+
type: 'button',
|
|
695
|
+
role: 'tab',
|
|
696
|
+
className: 'dim-botSettingsTab',
|
|
697
|
+
'aria-selected': tab.id === activeTab.id,
|
|
698
|
+
'aria-controls': `dim-bot-settings-${tab.id}-panel`,
|
|
699
|
+
tabIndex: tab.id === activeTab.id ? 0 : -1,
|
|
700
|
+
onClick: () => setActiveTabId(tab.id),
|
|
701
|
+
}, tab.label)))),
|
|
702
|
+
h('div', {
|
|
703
|
+
id: activePanelId,
|
|
704
|
+
className: 'dim-botSettingsTabPanel',
|
|
705
|
+
role: 'tabpanel',
|
|
706
|
+
'aria-labelledby': activeTabDomId,
|
|
707
|
+
},
|
|
708
|
+
activeTab.id === 'access'
|
|
709
|
+
? h(AccessPolicySettingsPage, {
|
|
710
|
+
channel,
|
|
711
|
+
account: { ...account, accessPolicy },
|
|
712
|
+
rpcCall: accessRpcCall,
|
|
713
|
+
onSaved: setAccessPolicy,
|
|
714
|
+
})
|
|
715
|
+
: h(React.Fragment, null,
|
|
716
|
+
h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' },
|
|
717
|
+
h('div', { className: 'dim-deliveryIdentityHeading' },
|
|
718
|
+
h('h2', { id: 'dim-delivery-bot-title', className: 'dim-deliveryBotName' },
|
|
719
|
+
account.botName || '机器人设置'),
|
|
664
720
|
h('a', {
|
|
665
721
|
className: 'dim-deliveryDocsLink',
|
|
666
722
|
href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh,
|
|
@@ -669,11 +725,7 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack }
|
|
|
669
725
|
'aria-label': '打开主动投递使用文档',
|
|
670
726
|
},
|
|
671
727
|
h('span', null, '使用文档'),
|
|
672
|
-
h('span', { 'aria-hidden': 'true' }, '↗')))
|
|
673
|
-
h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' },
|
|
674
|
-
h('div', null,
|
|
675
|
-
h('h2', { id: 'dim-delivery-bot-title', className: 'dim-deliveryBotName' },
|
|
676
|
-
account.botName || '机器人设置')),
|
|
728
|
+
h('span', { 'aria-hidden': 'true' }, '↗'))),
|
|
677
729
|
h('div', { className: 'dim-deliveryBotId' },
|
|
678
730
|
h('span', null, 'Bot ID'),
|
|
679
731
|
h('code', { title: account.botId }, account.botId),
|
|
@@ -745,5 +797,5 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack }
|
|
|
745
797
|
rpcCall: invoke,
|
|
746
798
|
onChanged: () => loadTargets({ silent: true }),
|
|
747
799
|
onEdit: () => setEditor({ mode: 'edit', target, source: 'edit' }),
|
|
748
|
-
})))));
|
|
800
|
+
})))))));
|
|
749
801
|
}
|
|
@@ -7,6 +7,62 @@ const EN = Object.freeze({
|
|
|
7
7
|
'IM机器人': 'IM bots',
|
|
8
8
|
'IM机器人设置': 'IM bot settings',
|
|
9
9
|
'更多机器人设置': 'More bot settings',
|
|
10
|
+
'机器人设置': 'Bot settings',
|
|
11
|
+
'机器人设置页签': 'Bot settings tabs',
|
|
12
|
+
'投递设置': 'Delivery settings',
|
|
13
|
+
'访问设置': 'Access settings',
|
|
14
|
+
'查看访问权限说明': 'View access permission details',
|
|
15
|
+
'允许所有用户': 'Allow all users',
|
|
16
|
+
'仅白名单用户': 'Allowlisted users only',
|
|
17
|
+
'默认命令权限': 'Default command permission',
|
|
18
|
+
'命令权限': 'Command permission',
|
|
19
|
+
'可以执行命令': 'Can run commands',
|
|
20
|
+
'不可以执行命令': 'Cannot run commands',
|
|
21
|
+
'白名单用户': 'Allowlisted users',
|
|
22
|
+
'查看白名单说明': 'View allowlist details',
|
|
23
|
+
'命令权限例外': 'Command permission exceptions',
|
|
24
|
+
'当前没有白名单用户,保存后普通用户将无法使用机器人。': 'There are currently no allowlisted users. After saving, regular users will not be able to use the bot.',
|
|
25
|
+
'新增用户': 'Add user',
|
|
26
|
+
'尚未添加用户': 'No users added',
|
|
27
|
+
'当前渠道不支持群聊': 'Group chat is not supported by this channel',
|
|
28
|
+
'原所有者或扫码接入者始终可以访问并执行命令;以下设置仅约束其他用户。': 'The original owner or QR-code operator can always access the bot and run commands; the settings below apply only to other users.',
|
|
29
|
+
'此区域无需配置,保存私聊设置时会保留现有群聊策略。': 'No setup is needed here. Saving direct-message settings keeps the existing group policy.',
|
|
30
|
+
'访问设置已保存。': 'Access settings saved.',
|
|
31
|
+
'访问设置暂不可用。': 'Access settings are currently unavailable.',
|
|
32
|
+
'访问设置保存失败,请稍后重试。': 'Could not save access settings. Try again later.',
|
|
33
|
+
'服务没有返回已保存的访问策略,请刷新后重试。': 'The service did not return the saved access policy. Refresh and try again.',
|
|
34
|
+
'访问策略尚未就绪,请返回机器人列表刷新后重试。': 'The access policy is not ready. Return to the bot list, refresh, and try again.',
|
|
35
|
+
'当前渠道暂不支持访问设置。': 'Access settings are not supported by this channel yet.',
|
|
36
|
+
'用户标识无效。': 'The user ID is invalid.',
|
|
37
|
+
'用户标识必须是字符串。': 'The user ID must be a string.',
|
|
38
|
+
'用户标识不能为空、包含控制字符或超过 256 个字符。': 'The user ID cannot be empty, contain control characters, or exceed 256 characters.',
|
|
39
|
+
'用户条目必须包含用户标识和命令权限。': 'Each user entry must include a user ID and command permission.',
|
|
40
|
+
'命令权限必须是布尔值。': 'Command permission must be a boolean.',
|
|
41
|
+
'访问模式只能是 open 或 allowlist。': 'Access mode must be open or allowlist.',
|
|
42
|
+
'开放模式设置必须完整。': 'Open-mode settings must be complete.',
|
|
43
|
+
'开放模式默认命令权限必须是布尔值。': 'The open-mode default command permission must be a boolean.',
|
|
44
|
+
'开放模式命令权限覆盖用户必须是数组。': 'Open-mode command permission overrides must be an array.',
|
|
45
|
+
'开放模式命令权限覆盖用户不能包含重复的用户标识。': 'Open-mode command permission overrides cannot contain duplicate user IDs.',
|
|
46
|
+
'白名单模式设置必须完整。': 'Allowlist-mode settings must be complete.',
|
|
47
|
+
'白名单模式用户必须是数组。': 'Allowlist-mode users must be an array.',
|
|
48
|
+
'白名单模式用户不能包含重复的用户标识。': 'Allowlist-mode users cannot contain duplicate user IDs.',
|
|
49
|
+
'访问场景设置必须同时包含模式、开放模式设置和白名单模式设置。': 'Each access context must include its mode, open-mode settings, and allowlist-mode settings.',
|
|
50
|
+
'请同时提交完整的私聊和群聊访问设置。': 'Submit complete direct-message and group access settings together.',
|
|
51
|
+
'填写微信用户 ID': 'Enter a WeChat user ID',
|
|
52
|
+
'飞书 Open ID': 'Feishu Open ID',
|
|
53
|
+
'群成员 Open ID': 'Group member Open ID',
|
|
54
|
+
'钉钉用户 ID': 'DingTalk user ID',
|
|
55
|
+
'填写 senderStaffId 或 senderId': 'Enter senderStaffId or senderId',
|
|
56
|
+
'群成员用户 ID': 'Group member user ID',
|
|
57
|
+
'企业微信用户 ID': 'WeCom user ID',
|
|
58
|
+
'填写 userid': 'Enter userid',
|
|
59
|
+
'填写 member_openid': 'Enter member_openid',
|
|
60
|
+
'QQ User Open ID': 'QQ User Open ID',
|
|
61
|
+
'群成员 User ID': 'Group member User ID',
|
|
62
|
+
'填写数字 User ID': 'Enter a numeric user ID',
|
|
63
|
+
'WhatsApp 电话号码或 JID': 'WhatsApp phone number or JID',
|
|
64
|
+
'群成员电话号码或 JID': 'Group member phone number or JID',
|
|
65
|
+
'8613800000000 或完整 JID': '8613800000000 or a full JID',
|
|
10
66
|
'IM 渠道': 'IM channels',
|
|
11
67
|
'让 DeepSeek Harness 触手可及': 'Connecting DeepSeek Harness',
|
|
12
68
|
'当前版本': 'Current version',
|
|
@@ -216,31 +272,44 @@ const EN = Object.freeze({
|
|
|
216
272
|
'群聊和私聊': 'Groups and direct',
|
|
217
273
|
'关闭弹窗': 'Close dialog',
|
|
218
274
|
'查看上下文增强说明': 'View context enhancement details',
|
|
275
|
+
'上下文增强范围': 'Context enhancement scope',
|
|
219
276
|
'启用范围': 'Enable in',
|
|
220
|
-
'
|
|
221
|
-
'私聊中启用': 'Enable in direct chats',
|
|
277
|
+
'启用': 'Enabled',
|
|
222
278
|
'(当前渠道不支持群聊)': '(This channel does not support group chats)',
|
|
223
279
|
'来源字段': 'Source fields',
|
|
224
280
|
'查看来源字段说明': 'View source field details',
|
|
281
|
+
'查看群聊来源字段说明': 'View group source field details',
|
|
282
|
+
'查看私聊来源字段说明': 'View direct source field details',
|
|
225
283
|
'渠道': 'Channel',
|
|
226
284
|
'会话类型': 'Conversation type',
|
|
227
285
|
'发送者标识': 'Sender ID',
|
|
228
286
|
'发送者昵称': 'Sender name',
|
|
229
287
|
'查看发送者昵称字段说明': 'View sender name field details',
|
|
288
|
+
'查看群聊发送者昵称字段说明': 'View group sender name field details',
|
|
289
|
+
'查看私聊发送者昵称字段说明': 'View direct sender name field details',
|
|
230
290
|
'该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。': 'This field is not available on every channel. If the current message has no sender name, <dsh_im_source> omits senderName even when the field is selected.',
|
|
231
291
|
'机器人标识': 'Bot ID',
|
|
232
292
|
'增强提示词': 'Guidance',
|
|
233
293
|
'查看增强提示词使用说明': 'View guidance instructions',
|
|
294
|
+
'查看群聊增强提示词使用说明': 'View group guidance instructions',
|
|
295
|
+
'查看私聊增强提示词使用说明': 'View direct guidance instructions',
|
|
234
296
|
'使用说明': 'How to use',
|
|
235
297
|
'用于告诉模型如何使用 <dsh_im_source> 中已选择的来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use the selected fields in <dsh_im_source>. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
|
236
298
|
'生效规则': 'Behavior',
|
|
237
299
|
'隐私提示': 'Privacy',
|
|
238
300
|
'使用示例': 'Example',
|
|
239
301
|
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\nconversationType是群聊时回复严肃一点,conversationType是私聊时回复一定要幽默搞笑,像周星驰的电影一样搞笑': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nWhen conversationType is group, reply more seriously. When conversationType is direct, make the reply humorous and funny.',
|
|
302
|
+
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\n当前消息来自群聊,请使用严肃、克制、简洁的表达方式。': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a group chat. Use a serious, restrained, and concise style.',
|
|
303
|
+
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\n当前消息来自私聊,可以使用更轻松、幽默、详细的表达方式。': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a direct chat. You may use a lighter, more humorous, and detailed style.',
|
|
240
304
|
'填入示例': 'Use example',
|
|
241
305
|
'清空': 'Clear',
|
|
242
306
|
'选择在哪些会话中启用、提供哪些来源字段,以及如何使用这些信息。仅使用已有消息元数据,不查询平台 API。': 'Choose which conversations to enhance, which source fields to include, and how to use them. Only existing message metadata is used; no platform APIs are queried.',
|
|
243
307
|
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送勾选且当前消息中可用的字段,不会额外查询或补全。': 'Reference these values by field name (such as senderId or conversationType) in the guidance. Only selected fields available in the current message are sent; no extra lookups are made.',
|
|
308
|
+
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送当前会话中勾选且可用的字段,不会额外查询或补全。': 'Reference these values by field name (such as senderId or conversationType) in the guidance. Only fields selected and available in the current conversation are sent; no extra lookups are made.',
|
|
309
|
+
'用于告诉模型如何使用当前群聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use <dsh_im_source> fields for the current group message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
|
310
|
+
'用于告诉模型如何使用当前私聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use <dsh_im_source> fields for the current direct message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
|
311
|
+
'仅在群聊开关开启时使用。清空并保存后不再附加群聊增强提示词;所选来源字段仍按当前场景设置发送。': 'Used only when group enhancement is enabled. Clear and save to omit group guidance; selected source fields still follow the group settings.',
|
|
312
|
+
'仅在私聊开关开启时使用。清空并保存后不再附加私聊增强提示词;所选来源字段仍按当前场景设置发送。': 'Used only when direct enhancement is enabled. Clear and save to omit direct guidance; selected source fields still follow the direct settings.',
|
|
244
313
|
'只需填写正文,插件自动添加 <dsh_im_source_guidance> 成对标签。清空并保存后,不再附加增强提示词,已选来源字段仍按开关设置发送。': 'Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags. Clear and save to omit guidance. Selected source fields still follow the conversation switches.',
|
|
245
314
|
'发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。': 'Sender IDs may contain platform user IDs or phone-number-like identifiers. Turning this off does not remove information already saved in conversation history.',
|
|
246
315
|
'上下文增强保存失败,请重试。': 'Could not save context enhancement. Try again.',
|
|
@@ -508,7 +577,6 @@ const EN = Object.freeze({
|
|
|
508
577
|
'先通过 @BotFather 获取 Bot Token,再在这里完成接入。': 'Get a Bot Token from @BotFather, then connect it here.',
|
|
509
578
|
'填写 @BotFather 生成的 Bot Token': 'Enter the Bot Token from @BotFather',
|
|
510
579
|
'访问模式': 'Access mode',
|
|
511
|
-
'访问设置': 'Access settings',
|
|
512
580
|
'Telegram 访问模式': 'Telegram access mode',
|
|
513
581
|
'查看 Telegram 访问模式说明': 'View Telegram access mode details',
|
|
514
582
|
'群聊全部忽略,私聊仅允许白名单用户。': 'All group messages are ignored; only allowlisted users may send DMs.',
|
|
@@ -294,6 +294,7 @@ export function IMSettingsTab({
|
|
|
294
294
|
channel: active.id,
|
|
295
295
|
account: deliverySettings,
|
|
296
296
|
rpcCall: rpcCalls.deliveryRpcCall,
|
|
297
|
+
accessRpcCall: rpcCalls[`${active.id}RpcCall`],
|
|
297
298
|
onBack: () => setDeliverySettings(null),
|
|
298
299
|
})
|
|
299
300
|
: active.id === 'weixin'
|