@xmanrui/dsh-im 4.37.0 → 4.38.1
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/PROACTIVE_DELIVERY.en.md +18 -2
- package/PROACTIVE_DELIVERY.md +18 -2
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/assets/logo-plugin-message-link-gradient.webp +0 -0
- package/lib/client.js +158 -62
- package/lib/index.js +322 -317
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +1 -1
- package/plugin-src/client/bot-general-settings.js +79 -42
- package/plugin-src/client/channels/feishu/api.js +3 -1
- package/plugin-src/client/delivery-settings.js +2 -2
- package/plugin-src/client/i18n.js +8 -7
- package/plugin-src/client/styles.js +19 -0
- package/plugin-src/host/channels/feishu/production.mjs +1 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +18 -0
- package/plugin-src/host/delivery-adapter.mjs +9 -1
- package/plugin-src/host/delivery-service.mjs +4 -3
- package/plugin-src/host/session-sync-coordinator.mjs +9 -5
- package/scripts/verify-package.mjs +11 -6
- package/src/channels/dingtalk/dingtalk-api.mjs +41 -24
- package/src/channels/dingtalk/dingtalk-runtime.mjs +3 -2
- package/src/channels/discord/discord-runtime.mjs +13 -2
- package/src/channels/feishu/bridge.mjs +30 -11
- package/src/channels/feishu/feishu-cards.mjs +8 -7
- package/src/channels/feishu/feishu-runtime.mjs +10 -1
- package/src/channels/feishu/multi-bot-controller.mjs +18 -0
- package/src/channels/feishu/plugin-config-store.mjs +2 -1
- package/src/channels/feishu/step-push-mode.mjs +21 -0
- package/src/channels/matrix/matrix-crypto.mjs +19 -5
- package/src/channels/matrix/matrix-runtime.mjs +16 -6
- package/src/channels/qq/markdown-reply.mjs +7 -340
- package/src/channels/qq/qq-runtime.mjs +11 -3
- package/src/channels/shared/harness-approval.mjs +102 -2
- package/src/channels/shared/harness-client.mjs +15 -3
- package/src/channels/shared/i18n-en/shared-c.mjs +4 -0
- package/src/channels/shared/i18n-en/wecom.mjs +3 -0
- package/src/channels/shared/markdown-chunks.mjs +338 -0
- package/src/channels/shared/message-failure.mjs +1 -1
- package/src/channels/shared/text-harness-bridge.mjs +4 -1
- package/src/channels/slack/slack-api.mjs +2 -1
- package/src/channels/slack/slack-runtime.mjs +30 -0
- package/src/channels/telegram/telegram-runtime.mjs +1 -0
- package/src/channels/wecom/wecom-bridge.mjs +45 -10
- package/src/channels/wecom/wecom-runtime.mjs +11 -5
- package/src/channels/weixin/weixin-runtime.mjs +17 -10
- package/src/channels/whatsapp/whatsapp-markdown.mjs +59 -0
- package/src/channels/whatsapp/whatsapp-runtime.mjs +26 -1
package/PROACTIVE_DELIVERY.en.md
CHANGED
|
@@ -175,9 +175,24 @@ await ctx.dshIm.send(botId, targetId, '# Daily report\n\n**Checks complete**', {
|
|
|
175
175
|
});
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
`format` accepts only `plain` and `markdown`, defaulting to `plain`.
|
|
178
|
+
`format` accepts only `plain` and `markdown`, defaulting to `plain`. HTTP and `message.send` RPC accept the same optional field. Markdown uses the existing channel delivery paths, without starting a Session or a new stream:
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
| Channel | Markdown presentation |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| Feishu/Lark | Native Markdown card |
|
|
183
|
+
| Telegram | Rich Markdown, with the existing text fallback |
|
|
184
|
+
| QQ | Markdown message; known format rejection falls back for the current chunk |
|
|
185
|
+
| DingTalk | Proactive Markdown message; definite format rejection falls back to text |
|
|
186
|
+
| Slack | Standard Markdown block with top-level notification text; explicit block rejection falls back to the existing text sender |
|
|
187
|
+
| WeCom bot | Existing Markdown message type |
|
|
188
|
+
| Matrix | Sanitized HTML with a text body |
|
|
189
|
+
| Discord | Native Markdown in message content |
|
|
190
|
+
| WhatsApp | Limited conversion of common formatting; code is protected and complex syntax stays literal |
|
|
191
|
+
| WeCom app / Weixin | Full text, including code and link destinations |
|
|
192
|
+
|
|
193
|
+
Channels apply their own message limits and splitting. Feishu proactive cards retain their existing unsplit behavior and do not automatically resend as text after rejection or an uncertain result. Other newly added fallback paths only handle local formatting errors or definite format rejection; they do not resend accepted chunks or retry ambiguous network outcomes. Older Hosts may ignore `format`; load the updated plugin on the Host.
|
|
194
|
+
|
|
195
|
+
Session sync requests Markdown for final assistant answers and retains the existing default text behavior for user echoes. Feishu keeps its dedicated mirror cards and existing plain-text fallback when the mirror cannot confirm the final answer. This does not expand the supported sync targets.
|
|
181
196
|
|
|
182
197
|
A same-Host plugin may also list the saved targets for one bot:
|
|
183
198
|
|
|
@@ -308,6 +323,7 @@ The HTTP protocol layer may also return `method-not-allowed` (405), `unsupported
|
|
|
308
323
|
- Targets remain editable while a bot is offline, but testing and delivery require a connected bot.
|
|
309
324
|
- WeChat proactive sends, connection tests, and deferred task results use the latest `context_token` received from the corresponding user by that bot. Context is stored only in the Host account state and restored after restart; it is never included in delivery targets or returned to callers. Rebinding with a different login credential clears it. After upgrading, an inbound user message is needed to populate the cache.
|
|
310
325
|
- iLink server rules still govern whether WeChat accepts a send. Healthy long polling does not guarantee proactive delivery. If `ret=-2 prepare failed` persists, avoid repeated heartbeat messages as a renewal strategy; ask the recipient to send a message before retrying. This error alone does not establish login expiry, context expiry, or exhausted quota.
|
|
326
|
+
- Long proactive WeChat text uses the existing 1,800-character chunk budget. Synced assistant Markdown keeps complete code fences in each chunk. Each chunk consumes a platform send; splitting does not remove server quotas. A rejection or uncertain result stops delivery and reports failure without resending accepted chunks. Diagnostics identify the failed chunk and total chunk count.
|
|
311
327
|
|
|
312
328
|
- Failed WeChat proactive sends remain visible in the account’s latest message error, with sanitized diagnostics including the provider code and whether context was included. Healthy polling does not clear this error; a successful outbound send does. HTTP/RPC still return the existing `delivery-failed` error, with no automatic retry or disconnection of healthy long polling.
|
|
313
329
|
|
package/PROACTIVE_DELIVERY.md
CHANGED
|
@@ -175,9 +175,24 @@ await ctx.dshIm.send(botId, targetId, '# 每日报告\n\n**检查完成**', {
|
|
|
175
175
|
});
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
`format` 只接受 `plain` 和 `markdown`,省略时为 `plain
|
|
178
|
+
`format` 只接受 `plain` 和 `markdown`,省略时为 `plain`。HTTP 和 `message.send` RPC 可添加同名字段。Markdown 复用各渠道的发送路径,不启动 Session 或新的流式输出:
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
| 渠道 | Markdown 呈现方式 |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| 飞书/Lark | 原生 Markdown 卡片 |
|
|
183
|
+
| Telegram | Rich Markdown,复用既有文本回退 |
|
|
184
|
+
| QQ | Markdown 消息;已知格式拒绝时仅当前分段回退文本 |
|
|
185
|
+
| 钉钉 | 主动 Markdown 消息;明确格式拒绝时回退文本 |
|
|
186
|
+
| Slack | 标准 Markdown block,保留通知文本;明确 block 拒绝时使用原有文本发送 |
|
|
187
|
+
| 企业微信机器人 | 现有 Markdown 消息类型 |
|
|
188
|
+
| Matrix | 清理后的 HTML,并附带文本正文 |
|
|
189
|
+
| Discord | 消息正文的原生 Markdown |
|
|
190
|
+
| WhatsApp | 有限转换常用格式,保护代码,复杂语法保留原文 |
|
|
191
|
+
| 企业微信应用/微信 | 完整文本,保留代码和链接目标 |
|
|
192
|
+
|
|
193
|
+
长消息按渠道限制分段。飞书主动卡片保留原有不自动分段的行为,平台拒绝或结果未知时不自动改发文本。其他新增回退仅针对本地格式处理错误或明确的格式拒绝,不重发已成功的分段,不因网络结果未知而改格式重试。旧 Host 可能忽略 `format`,需要加载更新后的插件。
|
|
194
|
+
|
|
195
|
+
会话同步的最终助手回答请求 Markdown,用户回显继续使用原有默认文字行为。飞书继续使用专用镜像卡片,无法确认最终答案可见时仍走现有纯文本兜底。同步目标范围不变。
|
|
181
196
|
|
|
182
197
|
同 Host 插件也可以列出某个机器人的已保存目标:
|
|
183
198
|
|
|
@@ -308,6 +323,7 @@ HTTP 协议层还可能返回 `method-not-allowed`(405)、`unsupported-media
|
|
|
308
323
|
- 机器人离线时仍可编辑目标,但不能测试或主动发送。
|
|
309
324
|
- 微信主动投递、连接测试和延迟任务结果会使用该机器人最近收到的对应用户 `context_token`。上下文仅保存在 Host 的账号状态中,重启后恢复,不放进投递目标或返回给调用方;重新绑定并更换登录凭据后清理。升级后需要收到一次用户消息才能建立缓存。
|
|
310
325
|
- 微信是否接受发送仍受 iLink 服务端规则约束。长轮询在线不保证主动发送可用;若持续出现 `ret=-2 prepare failed`,不要通过反复发送心跳尝试续期。可让目标用户发一条消息后重试;这不是登录凭据失效的确定证据,也不能仅凭此错误确定上下文过期或额度耗尽。
|
|
326
|
+
- 微信主动长文本沿用 1,800 字符分段预算,同步助手的 Markdown 代码块在各段补齐围栏。每一段都占用平台发送次数;分段不会解除平台额度限制。中途拒发或结果不确定时停止并报告失败,不重发已经接受的段;诊断会标明失败段序号和总段数。
|
|
311
327
|
|
|
312
328
|
- 微信主动发送失败会保留在账号的最近消息错误中,包含平台错误码和是否携带上下文等脱敏诊断;健康轮询不会清除此错误,后续成功投递会清除。HTTP/RPC 仍返回既有的 `delivery-failed`,不会自动重试或断开正常的长轮询连接。
|
|
313
329
|
|
package/README.en.md
CHANGED
|
@@ -181,6 +181,8 @@ IM channels with proactive delivery support can send text through a stable `botI
|
|
|
181
181
|
|
|
182
182
|
Saved direct-message targets also offer an opt-in **Two-way Session sync** switch. Once enabled, user text submitted from DSH Web/CLI and the final assistant text in that DM's current Session are mirrored back to the DM, including final assistant text from successfully completed scheduled tasks; ordinary IM prompts and `/steer` are not duplicated. The switch follows the current Session across `/session`, `/new`, and workspace changes. The first version supports text DMs on the current Host only; groups, Topics, Threads, and explicit remote `harnessBaseUrl` connections are unavailable.
|
|
183
183
|
|
|
184
|
+
Synced answers use channel-specific Markdown rendering: Feishu cards, Telegram rich text, QQ/DingTalk/Slack formatted messages, and existing WeCom bot, Matrix and Discord support. WhatsApp adapts common formatting; WeCom app and Weixin preserve the full text. Formatting fallback and long-message handling reuse channel mechanisms without adding retries for uncertain delivery.
|
|
185
|
+
|
|
184
186
|
See the [Proactive Delivery Guide](PROACTIVE_DELIVERY.en.md) ([简体中文](PROACTIVE_DELIVERY.md)) for setup steps, native fields for each supported channel, complete call examples, management endpoints, error codes, and troubleshooting.
|
|
185
187
|
|
|
186
188
|
### Client panel integration
|
package/README.md
CHANGED
|
@@ -183,6 +183,8 @@ Logo 由 dsh-im 的浏览器适配显示,无需修改 DSH。适配保留原始
|
|
|
183
183
|
|
|
184
184
|
已保存的私聊目标还可以开启默认关闭的「会话双向同步」。开启后,DSH Web/CLI 在该私聊当前 Session 中发送的用户文字和最终助手文字会同步回私聊,定时任务成功完成后的最终助手文字也会同步;IM 侧原有提问与 `/steer` 不会重复。开关自动跟随 `/session`、`/new` 和工作区切换后的当前 Session。首版仅支持当前 Host 的私聊文字;群聊、Topic、Thread 与显式远程 `harnessBaseUrl` 不支持。
|
|
185
185
|
|
|
186
|
+
同步回答按渠道能力呈现 Markdown:复用飞书卡片、Telegram 富文本、QQ/钉钉/Slack 的格式消息,以及企业微信机器人、Matrix、Discord 的已有能力;WhatsApp 适配常用格式,企业微信应用和微信保留完整文本。格式降级与长消息处理沿用或复用渠道机制,发送结果未知时不新增自动重发。
|
|
187
|
+
|
|
186
188
|
设置步骤、各渠道字段、完整调用示例、管理端点、错误码与排错说明请查看[《主动投递使用指南》](PROACTIVE_DELIVERY.md)([English](PROACTIVE_DELIVERY.en.md))。
|
|
187
189
|
|
|
188
190
|
### 客户端面板接入
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -595,7 +595,7 @@ var React40 = __toESM(require("react"), 1);
|
|
|
595
595
|
// package.json
|
|
596
596
|
var package_default = {
|
|
597
597
|
name: "@xmanrui/dsh-im",
|
|
598
|
-
version: "4.
|
|
598
|
+
version: "4.38.1",
|
|
599
599
|
icon: "./assets/logo-plugin-message-link-gradient.webp",
|
|
600
600
|
description: "\u628A\u5FAE\u4FE1\u3001\u98DE\u4E66\u3001\u4F01\u5FAE\u3001\u9489\u9489\u3001QQ\u3001Telegram\u3001WhatsApp\u3001Slack\u3001Discord\u3001Matrix\u63A5\u5165DSH\u3002 Connect Telegram, WhatsApp, Slack, Discord, Matrix, WeChat, Feishu, WeCom, DingTalk, and QQ to DSH.",
|
|
601
601
|
keywords: [
|
|
@@ -2177,14 +2177,15 @@ var EN = Object.freeze({
|
|
|
2177
2177
|
"\u901A\u7528": "General",
|
|
2178
2178
|
"\u5904\u7406\u4E2D\u2026": "Processing\u2026",
|
|
2179
2179
|
"\u673A\u5668\u4EBA\u901A\u7528\u8BBE\u7F6E": "General bot settings",
|
|
2180
|
-
"\
|
|
2181
|
-
"\
|
|
2182
|
-
"\
|
|
2180
|
+
"\u5C55\u5F00\u601D\u8003\u8FC7\u7A0B": "Expand thinking",
|
|
2181
|
+
"\u5C55\u5F00\u5DE5\u5177\u6458\u8981": "Expand tool summaries",
|
|
2182
|
+
"\u4EC5\u5BF9\u5B9E\u65F6\u8FC7\u7A0B\u5361\u751F\u6548\uFF0C\u4EFB\u52A1\u7ED3\u675F\u540E\u81EA\u52A8\u6536\u8D77\u3002": "Applies to streaming process cards only. Panels collapse when the task ends.",
|
|
2183
|
+
"\u8BF7\u9009\u62E9\u5B9E\u65F6\u8FC7\u7A0B\u5361\u9762\u677F\u7684\u5C55\u5F00\u72B6\u6001\u3002": "Choose whether to expand the streaming process card panels.",
|
|
2184
|
+
"\u6D88\u606F\u5904\u7406\u65B9\u5F0F": "Message handling",
|
|
2185
|
+
"\u6392\u961F": "Queue",
|
|
2183
2186
|
"\u63D2\u8BDD": "Steer",
|
|
2184
|
-
"\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u540E\
|
|
2185
|
-
"\u5C06\u65B0\
|
|
2186
|
-
"\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\u65F6\uFF0C\u6B63\u5E38\u5F00\u59CB\u65B0\u4EFB\u52A1\u3002\u56FE\u7247\u3001\u6587\u4EF6\u3001\u8BED\u97F3\u548C\u5E26\u5F15\u7528\u7684\u6D88\u606F\u6309\u539F\u6709\u65B9\u5F0F\u5904\u7406\u3002": "When idle, start a new task normally. Images, files, voice and quoted messages keep their existing handling.",
|
|
2187
|
-
"\u5DF2\u4FDD\u5B58\uFF0C\u5BF9\u65B0\u6536\u5230\u7684\u6D88\u606F\u751F\u6548\u3002": "Saved. Applies to newly received messages.",
|
|
2187
|
+
"\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u540E\u5904\u7406\u65B0\u6D88\u606F": "Process new messages after the current task",
|
|
2188
|
+
"\u5C06\u65B0\u6D88\u606F\u8865\u5145\u5230\u5F53\u524D\u4EFB\u52A1": "Add new messages to the current task",
|
|
2188
2189
|
"\u65E0\u6CD5\u8BFB\u53D6\u6D88\u606F\u5904\u7406\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not read message handling settings. Try again later.",
|
|
2189
2190
|
"\u8BF7\u63D0\u4EA4\u6709\u6548\u7684\u6D88\u606F\u5904\u7406\u65B9\u5F0F\u3002": "Choose a valid message handling mode.",
|
|
2190
2191
|
"\u901A\u7528\u8BBE\u7F6E": "General settings",
|
|
@@ -7239,6 +7240,16 @@ var FEISHU_STEP_PUSH_MODES = Object.freeze({
|
|
|
7239
7240
|
LIVE_COT: "live_cot"
|
|
7240
7241
|
});
|
|
7241
7242
|
var DEFAULT_FEISHU_STEP_PUSH_MODE = FEISHU_STEP_PUSH_MODES.STREAMING_CARD;
|
|
7243
|
+
var DEFAULT_FEISHU_STEP_CARD_PANELS = Object.freeze({
|
|
7244
|
+
thinkingExpanded: false,
|
|
7245
|
+
toolsExpanded: true
|
|
7246
|
+
});
|
|
7247
|
+
function normalizeFeishuStepCardPanels(value) {
|
|
7248
|
+
return Object.freeze({
|
|
7249
|
+
thinkingExpanded: typeof value?.thinkingExpanded === "boolean" ? value.thinkingExpanded : DEFAULT_FEISHU_STEP_CARD_PANELS.thinkingExpanded,
|
|
7250
|
+
toolsExpanded: typeof value?.toolsExpanded === "boolean" ? value.toolsExpanded : DEFAULT_FEISHU_STEP_CARD_PANELS.toolsExpanded
|
|
7251
|
+
});
|
|
7252
|
+
}
|
|
7242
7253
|
function normalizeFeishuStepPushMode(value) {
|
|
7243
7254
|
if (value === FEISHU_STEP_PUSH_MODES.STREAMING_CARD) {
|
|
7244
7255
|
return FEISHU_STEP_PUSH_MODES.STREAMING_CARD;
|
|
@@ -7367,6 +7378,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
|
|
|
7367
7378
|
setMentionTopicReply: "bot.mention-topic-reply.set",
|
|
7368
7379
|
setStepPush: "bot.step-push.set",
|
|
7369
7380
|
setStepPushMode: "bot.step-push-mode.set",
|
|
7381
|
+
setStepCardPanels: "bot.step-card-panels.set",
|
|
7370
7382
|
setVoice: "bot.voice.set",
|
|
7371
7383
|
setSlashPanel: "bot.slash-panel.set",
|
|
7372
7384
|
// Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
|
|
@@ -7526,6 +7538,7 @@ function normalizeBotConnection(value, fallbackBotId) {
|
|
|
7526
7538
|
mentionTopicReply: value.mentionTopicReply !== false,
|
|
7527
7539
|
stepPush: value.stepPush === true,
|
|
7528
7540
|
stepPushMode: normalizeFeishuStepPushMode(value.stepPushMode),
|
|
7541
|
+
stepCardPanels: normalizeFeishuStepCardPanels(value.stepCardPanels),
|
|
7529
7542
|
voice: normalizeFeishuVoiceConfig(value.voice),
|
|
7530
7543
|
slashPanel: normalizeSlashPanelConfig(value.slashPanel),
|
|
7531
7544
|
groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
|
|
@@ -16658,29 +16671,41 @@ function AccessPolicySettingsPage({ channel: channel7, account, rpcCall, onSaved
|
|
|
16658
16671
|
|
|
16659
16672
|
// plugin-src/client/bot-general-settings.js
|
|
16660
16673
|
var React29 = __toESM(require("react"), 1);
|
|
16661
|
-
function
|
|
16662
|
-
if (result?.ok !== true) throw new Error(result?.error?.message || "\u65E0\u6CD5\u8BFB\u53D6\
|
|
16674
|
+
function settingsFromResult(result, botId) {
|
|
16675
|
+
if (result?.ok !== true) throw new Error(result?.error?.message || "\u65E0\u6CD5\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
16663
16676
|
const bot = result.value?.bots?.find((entry) => entry.botId === botId);
|
|
16664
16677
|
if (!bot) throw new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");
|
|
16665
|
-
return
|
|
16678
|
+
return {
|
|
16679
|
+
busyMessageMode: normalizeBusyMessageMode(bot.busyMessageMode),
|
|
16680
|
+
stepCardPanels: normalizeFeishuStepCardPanels(bot.stepCardPanels)
|
|
16681
|
+
};
|
|
16666
16682
|
}
|
|
16667
|
-
function BotGeneralSettingsPage({ account, rpcCall }) {
|
|
16668
|
-
const
|
|
16669
|
-
const [
|
|
16683
|
+
function BotGeneralSettingsPage({ channel: channel7, account, rpcCall }) {
|
|
16684
|
+
const modeId = React29.useId();
|
|
16685
|
+
const [settings, setSettings] = React29.useState(() => ({
|
|
16686
|
+
busyMessageMode: "queue",
|
|
16687
|
+
stepCardPanels: normalizeFeishuStepCardPanels()
|
|
16688
|
+
}));
|
|
16689
|
+
const [savedSettings, setSavedSettings] = React29.useState(null);
|
|
16670
16690
|
const [busy, setBusy] = React29.useState(false);
|
|
16671
16691
|
const [feedback, setFeedback] = React29.useState(null);
|
|
16672
16692
|
const mounted = React29.useRef(false);
|
|
16693
|
+
const inFlight = React29.useRef(false);
|
|
16694
|
+
const mode = settings.busyMessageMode;
|
|
16673
16695
|
const load = React29.useCallback(async () => {
|
|
16696
|
+
if (inFlight.current) return;
|
|
16697
|
+
inFlight.current = true;
|
|
16674
16698
|
setBusy(true);
|
|
16675
16699
|
setFeedback(null);
|
|
16676
16700
|
try {
|
|
16677
|
-
const value =
|
|
16701
|
+
const value = settingsFromResult(await rpcCall("connection.status", {}), account.botId);
|
|
16678
16702
|
if (!mounted.current) return;
|
|
16679
|
-
|
|
16680
|
-
|
|
16703
|
+
setSettings(value);
|
|
16704
|
+
setSavedSettings(value);
|
|
16681
16705
|
} catch (error) {
|
|
16682
|
-
if (mounted.current) setFeedback(
|
|
16706
|
+
if (mounted.current) setFeedback(error.message);
|
|
16683
16707
|
} finally {
|
|
16708
|
+
inFlight.current = false;
|
|
16684
16709
|
if (mounted.current) setBusy(false);
|
|
16685
16710
|
}
|
|
16686
16711
|
}, [account.botId, rpcCall]);
|
|
@@ -16691,63 +16716,115 @@ function BotGeneralSettingsPage({ account, rpcCall }) {
|
|
|
16691
16716
|
mounted.current = false;
|
|
16692
16717
|
};
|
|
16693
16718
|
}, [load]);
|
|
16694
|
-
const save = async () => {
|
|
16719
|
+
const save = async (field2, value) => {
|
|
16720
|
+
if (inFlight.current || savedSettings === null || value === savedSettings[field2]) return;
|
|
16721
|
+
inFlight.current = true;
|
|
16722
|
+
setSettings({ ...settings, [field2]: value });
|
|
16695
16723
|
setBusy(true);
|
|
16696
16724
|
setFeedback(null);
|
|
16697
16725
|
try {
|
|
16698
|
-
const
|
|
16726
|
+
const endpoint = field2 === "stepCardPanels" ? FEISHU_ENDPOINTS.setStepCardPanels : SET_MESSAGE_MODE_ENDPOINT;
|
|
16727
|
+
const saved = settingsFromResult(await rpcCall(endpoint, {
|
|
16699
16728
|
botId: account.botId,
|
|
16700
|
-
|
|
16729
|
+
[field2]: value
|
|
16701
16730
|
}), account.botId);
|
|
16702
16731
|
if (!mounted.current) return;
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
setFeedback({ text: "\u5DF2\u4FDD\u5B58\uFF0C\u5BF9\u65B0\u6536\u5230\u7684\u6D88\u606F\u751F\u6548\u3002" });
|
|
16732
|
+
setSettings(saved);
|
|
16733
|
+
setSavedSettings(saved);
|
|
16706
16734
|
} catch (error) {
|
|
16707
|
-
if (mounted.current)
|
|
16735
|
+
if (mounted.current) {
|
|
16736
|
+
setSettings(savedSettings);
|
|
16737
|
+
setFeedback(error.message);
|
|
16738
|
+
}
|
|
16708
16739
|
} finally {
|
|
16740
|
+
inFlight.current = false;
|
|
16709
16741
|
if (mounted.current) setBusy(false);
|
|
16710
16742
|
}
|
|
16711
16743
|
};
|
|
16712
16744
|
return h2(
|
|
16713
16745
|
"section",
|
|
16714
|
-
{ className: "dim-
|
|
16715
|
-
h2("h2", null, "\u901A\u7528"),
|
|
16716
|
-
h2("p", null, "\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u5F71\u54CD\u5F53\u524D\u673A\u5668\u4EBA\u3002"),
|
|
16746
|
+
{ className: "dim-botGeneralSettings", "aria-label": "\u673A\u5668\u4EBA\u901A\u7528\u8BBE\u7F6E", "aria-busy": busy },
|
|
16717
16747
|
h2(
|
|
16718
|
-
"
|
|
16719
|
-
{ className: "dim-
|
|
16720
|
-
h2("span", null, "\u4EFB\u52A1\u8FD0\u884C\u65F6\u7684\u65B0\u6D88\u606F\u5904\u7406\u65B9\u5F0F"),
|
|
16748
|
+
"div",
|
|
16749
|
+
{ className: "dim-botGeneralList" },
|
|
16721
16750
|
h2(
|
|
16722
|
-
"
|
|
16723
|
-
{
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
"
|
|
16727
|
-
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
|
|
16734
|
-
|
|
16751
|
+
"div",
|
|
16752
|
+
{ className: "dim-botGeneralRow" },
|
|
16753
|
+
h2(
|
|
16754
|
+
"div",
|
|
16755
|
+
{ className: "dim-botGeneralText" },
|
|
16756
|
+
h2("label", { className: "dim-botGeneralTitle", htmlFor: modeId }, "\u6D88\u606F\u5904\u7406\u65B9\u5F0F"),
|
|
16757
|
+
h2("p", { className: "dim-botGeneralHelp", id: `${modeId}-description` }, mode === "queue" ? "\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u540E\u5904\u7406\u65B0\u6D88\u606F" : "\u5C06\u65B0\u6D88\u606F\u8865\u5145\u5230\u5F53\u524D\u4EFB\u52A1")
|
|
16758
|
+
),
|
|
16759
|
+
h2(
|
|
16760
|
+
"div",
|
|
16761
|
+
{ className: "dim-botGeneralControl" },
|
|
16762
|
+
h2(
|
|
16763
|
+
"select",
|
|
16764
|
+
{
|
|
16765
|
+
id: modeId,
|
|
16766
|
+
value: mode,
|
|
16767
|
+
disabled: busy || savedSettings === null,
|
|
16768
|
+
"aria-label": "\u6D88\u606F\u5904\u7406\u65B9\u5F0F",
|
|
16769
|
+
"aria-describedby": `${modeId}-description`,
|
|
16770
|
+
onChange: (event) => void save("busyMessageMode", event.target.value)
|
|
16771
|
+
},
|
|
16772
|
+
h2("option", { value: "queue" }, "\u6392\u961F"),
|
|
16773
|
+
h2("option", { value: "steer" }, "\u63D2\u8BDD")
|
|
16774
|
+
),
|
|
16775
|
+
h2(
|
|
16776
|
+
"svg",
|
|
16777
|
+
{ width: 14, height: 14, viewBox: "0 0 16 16", fill: "none", "aria-hidden": true },
|
|
16778
|
+
h2("path", { d: "m4 6 4 4 4-4", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" })
|
|
16779
|
+
)
|
|
16780
|
+
)
|
|
16781
|
+
),
|
|
16782
|
+
channel7 === "feishu" ? [
|
|
16783
|
+
["thinkingExpanded", "\u5C55\u5F00\u601D\u8003\u8FC7\u7A0B"],
|
|
16784
|
+
["toolsExpanded", "\u5C55\u5F00\u5DE5\u5177\u6458\u8981"]
|
|
16785
|
+
].map(([field2, label]) => h2(
|
|
16786
|
+
"div",
|
|
16787
|
+
{ key: field2, className: "dim-botGeneralRow dim-botGeneralPanelRow" },
|
|
16788
|
+
h2("label", { className: "dim-botGeneralTitle", htmlFor: `${modeId}-${field2}` }, label),
|
|
16789
|
+
h2(
|
|
16790
|
+
"div",
|
|
16791
|
+
{ className: "dim-botGeneralControl" },
|
|
16792
|
+
h2("input", {
|
|
16793
|
+
id: `${modeId}-${field2}`,
|
|
16794
|
+
type: "checkbox",
|
|
16795
|
+
role: "switch",
|
|
16796
|
+
className: "dim-contextSwitch",
|
|
16797
|
+
checked: settings.stepCardPanels[field2],
|
|
16798
|
+
disabled: busy || savedSettings === null,
|
|
16799
|
+
"aria-label": label,
|
|
16800
|
+
"aria-describedby": `${modeId}-panels-description`,
|
|
16801
|
+
onChange: (event) => void save("stepCardPanels", {
|
|
16802
|
+
...settings.stepCardPanels,
|
|
16803
|
+
[field2]: event.target.checked
|
|
16804
|
+
})
|
|
16805
|
+
})
|
|
16806
|
+
)
|
|
16807
|
+
)) : null
|
|
16735
16808
|
),
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
|
|
16748
|
-
|
|
16749
|
-
|
|
16750
|
-
|
|
16809
|
+
channel7 === "feishu" ? h2(
|
|
16810
|
+
"p",
|
|
16811
|
+
{ className: "dim-botGeneralHelp dim-botGeneralPanelHelp", id: `${modeId}-panels-description` },
|
|
16812
|
+
"\u4EC5\u5BF9\u5B9E\u65F6\u8FC7\u7A0B\u5361\u751F\u6548\uFF0C\u4EFB\u52A1\u7ED3\u675F\u540E\u81EA\u52A8\u6536\u8D77\u3002"
|
|
16813
|
+
) : null,
|
|
16814
|
+
feedback ? h2("p", {
|
|
16815
|
+
className: "dim-accessFeedback",
|
|
16816
|
+
"data-tone": "error",
|
|
16817
|
+
role: "alert"
|
|
16818
|
+
}, feedback) : null,
|
|
16819
|
+
savedSettings === null && !busy ? h2(
|
|
16820
|
+
"div",
|
|
16821
|
+
{ className: "dim-botGeneralActions" },
|
|
16822
|
+
h2("button", {
|
|
16823
|
+
type: "button",
|
|
16824
|
+
className: "dim-deliveryButton",
|
|
16825
|
+
onClick: () => void load()
|
|
16826
|
+
}, "\u91CD\u65B0\u8BFB\u53D6")
|
|
16827
|
+
) : null
|
|
16751
16828
|
);
|
|
16752
16829
|
}
|
|
16753
16830
|
|
|
@@ -17974,9 +18051,9 @@ var DELIVERY_ENDPOINTS = Object.freeze({
|
|
|
17974
18051
|
test: "target.test"
|
|
17975
18052
|
});
|
|
17976
18053
|
var BOT_SETTINGS_TABS = Object.freeze([
|
|
18054
|
+
Object.freeze({ id: "general", label: "\u901A\u7528" }),
|
|
17977
18055
|
Object.freeze({ id: "delivery", label: "\u6295\u9012\u8BBE\u7F6E" }),
|
|
17978
|
-
Object.freeze({ id: "access", label: "\u8BBF\u95EE\u8BBE\u7F6E" })
|
|
17979
|
-
Object.freeze({ id: "general", label: "\u901A\u7528" })
|
|
18056
|
+
Object.freeze({ id: "access", label: "\u8BBF\u95EE\u8BBE\u7F6E" })
|
|
17980
18057
|
]);
|
|
17981
18058
|
var FEISHU_BOT_SETTINGS_TABS = Object.freeze([
|
|
17982
18059
|
...BOT_SETTINGS_TABS,
|
|
@@ -18733,7 +18810,7 @@ function DeliveryTargetSettingsPage({
|
|
|
18733
18810
|
role: "tabpanel",
|
|
18734
18811
|
"aria-labelledby": activeTabDomId
|
|
18735
18812
|
},
|
|
18736
|
-
activeTab.id === "general" ? h2(BotGeneralSettingsPage, { key: account.botId, account, rpcCall: accessRpcCall }) : activeTab.id === "access" ? h2(AccessPolicySettingsPage, {
|
|
18813
|
+
activeTab.id === "general" ? h2(BotGeneralSettingsPage, { key: `${channel7}:${account.botId}`, channel: channel7, account, rpcCall: accessRpcCall }) : activeTab.id === "access" ? h2(AccessPolicySettingsPage, {
|
|
18737
18814
|
channel: channel7,
|
|
18738
18815
|
account: { ...account, accessPolicy },
|
|
18739
18816
|
rpcCall: accessRpcCall,
|
|
@@ -20072,6 +20149,25 @@ var CSS15 = String.raw`
|
|
|
20072
20149
|
.dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
|
|
20073
20150
|
.dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
20074
20151
|
.dim-accessActions { display: flex; justify-content: flex-end; }
|
|
20152
|
+
.dim-botGeneralSettings { min-width: 0; }
|
|
20153
|
+
.dim-botGeneralList { min-width: 0; display: grid; }
|
|
20154
|
+
.dim-botGeneralRow { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; gap: 24px; padding: 16px 0; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
|
|
20155
|
+
.dim-botGeneralRow:last-child { border-bottom: 0; }
|
|
20156
|
+
.dim-botGeneralPanelRow { border-bottom: 0; }
|
|
20157
|
+
.dim-botGeneralText { min-width: 0; display: grid; gap: 4px; }
|
|
20158
|
+
.dim-botGeneralTitle { color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 22px; font-weight: 400; }
|
|
20159
|
+
.dim-botGeneralHelp { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
|
|
20160
|
+
.dim-botGeneralPanelHelp { padding-bottom: 16px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
|
|
20161
|
+
.dim-botGeneralControl { position: relative; min-width: 0; max-width: 100%; justify-self: end; color: var(--dsw-alias-label-primary, #1f2329); }
|
|
20162
|
+
.dim-botGeneralControl select { display: block; min-width: 104px; max-width: 100%; height: 36px; padding: 0 36px 0 14px; border: 0; border-radius: 10px; color: inherit; background: var(--dsw-alias-bg-module-platform, #f5f6f7); font: inherit; font-size: 14px; line-height: 22px; appearance: none; cursor: pointer; }
|
|
20163
|
+
.dim-botGeneralControl select:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
|
|
20164
|
+
.dim-botGeneralControl select:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
|
|
20165
|
+
.dim-botGeneralControl select:disabled { opacity: .5; cursor: not-allowed; }
|
|
20166
|
+
.dim-botGeneralControl svg { position: absolute; top: 50%; right: 12px; transform: translateY(-50%); pointer-events: none; }
|
|
20167
|
+
.dim-botGeneralControl .dim-contextSwitch { display: block; }
|
|
20168
|
+
.dim-botGeneralControl .dim-contextSwitch:disabled { opacity: .5; cursor: not-allowed; }
|
|
20169
|
+
.dim-botGeneralControl .dim-contextSwitch:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 3px; }
|
|
20170
|
+
.dim-botGeneralActions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; padding-top: 16px; }
|
|
20075
20171
|
.dim-generalSettingsPage { min-width: 0; display: grid; }
|
|
20076
20172
|
.dim-generalSettingsHeader { min-width: 0; margin: 0 0 8px; }
|
|
20077
20173
|
.dim-generalSettingsHeader h2 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 25px; font-weight: 680; }
|