@wenbin_wb/dsh-bridge 2.2.9 → 2.3.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 +2 -1
- package/README.md +2 -1
- package/client/client.js +44 -156
- package/client/index.js +42 -118
- package/docs/feishu-usage.md +93 -0
- package/lib/feishu/gateway.js +373 -0
- package/lib/feishu/index.js +203 -0
- package/lib/feishu/node.js +351 -0
- package/lib/index.js +50 -0
- package/lib/platform/conversation-bridge.js +29 -4
- package/lib/qq/gateway.js +1 -1
- package/lib/wechat/gateway.js +3 -0
- package/package.json +5 -3
package/README.en.md
CHANGED
|
@@ -19,6 +19,7 @@ Seamlessly extend your local DeepSeek Harness to mobile phones, tablets, public
|
|
|
19
19
|
- **Custom Tunnel**: Connect to your own tunnel server with a fixed domain ([Setup Guide](docs/custom-tunnel.md))
|
|
20
20
|
- **WeChat Bot (ClawBot / iLink)**: Scan a QR code to log in a WeChat personal account, then chat with, control, and approve your DeepSeek Harness agents right inside WeChat. **Multi-workspace selection, restart-persistent sessions, grouped session listing with titles, media (image/file/voice) transfer, and permission approvals** — over Tencent's official iLink Bot API, no public server or tunnel required ([Usage Guide](docs/wechat-usage.md))
|
|
21
21
|
- **QQ Bot (OpenAPI v2)**: Connect your QQ Bot to receive private/group messages, send Markdown, button keyboards, and rich media. **Full event coverage (C2C / GROUP_AT_MESSAGE_CREATE), auto token refresh, reconnection with backoff, message deduplication** — over Tencent's official QQ Bot OpenAPI v2 ([Usage Guide](docs/qq-usage.md))
|
|
22
|
+
- **Feishu / Lark Bot (Official WebSocket)**: Connect enterprise self-built apps via Feishu's official WebSocket protocol. **No public IP / no Webhook required, Markdown table formatting, native interactive card permission approvals with 1-click button actions** ([Usage Guide](docs/feishu-usage.md))
|
|
22
23
|
- **Official Brand SVG Icons**: Authentic vector brand icons for WeChat, QQ, Feishu, Telegram with real-time status indicators
|
|
23
24
|
- **Fast Version Check & 1-Click Upgrade**: Dual-channel registry check (npmmirror + npmjs fallback in ~200ms) with seamless **1-click in-app upgrade**, no terminal copying required
|
|
24
25
|
- **Dark Mode Support**: Deep integration with DeepSeek Harness Design Tokens (`--dsw-alias-*`), QR code background protection for safe dark mode scanning
|
|
@@ -38,7 +39,7 @@ Seamlessly extend your local DeepSeek Harness to mobile phones, tablets, public
|
|
|
38
39
|
| **Platform Abstraction** | Platform-agnostic core (sessions / approvals / commands / digest) shared across IM channels | ✅ **Completed** (v2.0.0) |
|
|
39
40
|
| **WeChat** | Chat with your Agent directly in WeChat | ✅ Supported (workspaces / persisted sessions / media / approvals) |
|
|
40
41
|
| **QQ Bot** | QQ bot integration for group/private chat | ✅ **Completed** (v2.1.0) — Markdown / buttons / rich media |
|
|
41
|
-
| **Feishu** | Feishu
|
|
42
|
+
| **Feishu** | Feishu / Lark official persistent WebSocket bot | ✅ **Completed** (v2.3.0) — No-public-IP WS / Card Approvals |
|
|
42
43
|
| **Telegram** | IM channel suited for self-hosting and overseas | Planned |
|
|
43
44
|
| **OpenClaw** | Connect with the OpenClaw ecosystem | Planned |
|
|
44
45
|
|
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
- **自建隧道**:连接自己的隧道服务器,获得固定域名([搭建教程](docs/custom-tunnel.md))
|
|
20
20
|
- **微信 Bot(ClawBot / iLink)**:扫码登录微信个人号后,直接在微信里对话、控制 DeepSeek Harness 的 agent。**支持多工作区选择、会话跨重启持久化、按工作区分组查看、媒体(图片/文件/语音)收发、权限审批**——走腾讯官方 iLink Bot API,无需公网([使用说明](docs/wechat-usage.md))
|
|
21
21
|
- **QQ Bot(OpenAPI v2)**:接入 QQ 机器人,私聊/群聊接收消息,发送 Markdown、按钮键盘和富媒体。**完整事件覆盖(C2C / GROUP_AT_MESSAGE_CREATE)、Token 自动刷新、断线重连、消息去重**——走腾讯官方 QQ Bot OpenAPI v2([使用说明](docs/qq-usage.md))
|
|
22
|
+
- **飞书 Bot(官方 WebSocket 长连接)**:接入飞书开放平台企业自建应用,私聊/群聊实时交互。**无需公网 IP / 无需 Webhook、支持飞书 Markdown 表格排版、原生交互卡片权限审批一键点击确认**——走飞书官方最新 WebSocket 长连接协议([使用说明](docs/feishu-usage.md))
|
|
22
23
|
- **IM 官方品牌矢量图标**:微信、QQ、飞书、Telegram 官方矢量图标与状态展示,直接在聊天软件里呼唤你的 Agent
|
|
23
24
|
- **极速版本检查与一键升级**:国内高速镜像(npmmirror)优先 + 官方源毫秒级双通道检查,检测到新版本支持**界面一键直接升级**,无需手动打开终端
|
|
24
25
|
- **深色模式深度适配**:完美适配 DeepSeek Harness 设计系统明暗主题切换,二维码自带白底安全垫,暗光下手机扫码 100% 极速识别
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
| **平台抽象层** | 平台无关的核心(会话/审批/命令/digest)跨 IM 渠道复用 | ✅ **已完成**(v2.0.0) |
|
|
39
40
|
| **微信** | 在微信里直接与你的 Agent 对话 | ✅ 已支持(多工作区 / 会话持久化 / 媒体 / 审批) |
|
|
40
41
|
| **QQ Bot** | 接入 QQ 机器人,群聊/私聊唤起 Agent | ✅ **已完成**(v2.1.0)— Markdown / 按钮 / 富媒体 |
|
|
41
|
-
| **飞书** |
|
|
42
|
+
| **飞书** | 飞书开放平台长连接机器人,办公场景直接调用 | ✅ **已完成**(v2.3.0)— 免公网 WS / 卡片审批 |
|
|
42
43
|
| **Telegram** | 适合自托管与海外的 IM 渠道 | 规划中 |
|
|
43
44
|
| **OpenClaw** | 与 OpenClaw 生态打通 | 规划中 |
|
|
44
45
|
|
package/client/client.js
CHANGED
|
@@ -425,7 +425,9 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
425
425
|
sendChunkDelayMs: String(platform.config.sendChunkDelayMs ?? 1500),
|
|
426
426
|
appId: platform.config.appId ?? "",
|
|
427
427
|
// Secret 不由后端回传;空值表示沿用已保存密钥
|
|
428
|
-
clientSecret: ""
|
|
428
|
+
clientSecret: "",
|
|
429
|
+
appSecret: "",
|
|
430
|
+
domain: platform.config.domain ?? "feishu"
|
|
429
431
|
});
|
|
430
432
|
}
|
|
431
433
|
}, [platform?.config]);
|
|
@@ -501,10 +503,14 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
501
503
|
if (platformId === "qq") {
|
|
502
504
|
payload.appId = cfgDraft.appId.trim();
|
|
503
505
|
payload.clientSecret = cfgDraft.clientSecret.trim();
|
|
506
|
+
} else if (platformId === "feishu") {
|
|
507
|
+
payload.appId = cfgDraft.appId.trim();
|
|
508
|
+
payload.appSecret = cfgDraft.appSecret.trim();
|
|
509
|
+
payload.domain = cfgDraft.domain || "feishu";
|
|
504
510
|
}
|
|
505
511
|
await act(BRIDGE_ENDPOINTS.platformSetConfig, payload);
|
|
506
512
|
}, [act, cfgDraft, platformId]);
|
|
507
|
-
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")));
|
|
513
|
+
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")) || platformId === "feishu" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.appSecret !== (platform.config.appSecret ?? "")));
|
|
508
514
|
if (!platform && !err) {
|
|
509
515
|
return React.createElement(
|
|
510
516
|
"div",
|
|
@@ -561,6 +567,18 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
561
567
|
rel: "noopener noreferrer",
|
|
562
568
|
style: s.btnGhost
|
|
563
569
|
}, "\u{1F310} QQ \u5F00\u653E\u5E73\u53F0"),
|
|
570
|
+
platformId === "feishu" && React.createElement("a", {
|
|
571
|
+
href: "https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/feishu-usage.md",
|
|
572
|
+
target: "_blank",
|
|
573
|
+
rel: "noopener noreferrer",
|
|
574
|
+
style: s.btnGhost
|
|
575
|
+
}, "\u{1F4D6} \u98DE\u4E66\u4F7F\u7528\u8BF4\u660E"),
|
|
576
|
+
platformId === "feishu" && React.createElement("a", {
|
|
577
|
+
href: "https://open.feishu.cn/app",
|
|
578
|
+
target: "_blank",
|
|
579
|
+
rel: "noopener noreferrer",
|
|
580
|
+
style: s.btnGhost
|
|
581
|
+
}, "\u{1F310} \u98DE\u4E66\u5F00\u653E\u5E73\u53F0"),
|
|
564
582
|
React.createElement("button", {
|
|
565
583
|
style: s.btnGhost,
|
|
566
584
|
onClick: () => setShowHelp((v) => !v)
|
|
@@ -695,19 +713,19 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
695
713
|
React.createElement(
|
|
696
714
|
"div",
|
|
697
715
|
{ style: { display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap", alignItems: "center" } },
|
|
698
|
-
platform.status !== "connected" && platform.status !== "starting" && React.createElement("button", { style: s.btnPri, onClick: onLogin, disabled: busy }, "\u91CD\u65B0\
|
|
716
|
+
platform.status !== "connected" && platform.status !== "starting" && React.createElement("button", { style: s.btnPri, onClick: onLogin, disabled: busy }, "\u91CD\u65B0\u8FDE\u63A5"),
|
|
699
717
|
(platform.status === "connected" || platform.status === "starting") && React.createElement("button", { style: s.btnGhost, onClick: onStop, disabled: busy }, "\u65AD\u5F00"),
|
|
700
718
|
React.createElement("button", {
|
|
701
719
|
style: { ...s.btnGhost, color: "var(--dsw-alias-state-error-primary,#dc2626)", borderColor: "var(--dsw-alias-state-error-primary,#dc2626)", opacity: busy ? 0.5 : 1 },
|
|
702
720
|
disabled: busy,
|
|
703
721
|
onClick: () => {
|
|
704
|
-
if (window.confirm("\u786E\u8BA4\u89E3\u7ED1\uFF1F\u8FD9\u5C06\u6E05\u9664\
|
|
722
|
+
if (window.confirm("\u786E\u8BA4\u89E3\u7ED1\uFF1F\u8FD9\u5C06\u6E05\u9664\u4FDD\u5B58\u7684\u51ED\u8BC1\u3002")) act(BRIDGE_ENDPOINTS.platformUnbind, {});
|
|
705
723
|
},
|
|
706
|
-
title: "\u6E05\u9664\u767B\u5F55\u51ED\u8BC1\uFF0C\u4E0B\u6B21\u9700\u91CD\u65B0\
|
|
724
|
+
title: "\u6E05\u9664\u767B\u5F55\u51ED\u8BC1\uFF0C\u4E0B\u6B21\u9700\u91CD\u65B0\u914D\u7F6E"
|
|
707
725
|
}, "\u89E3\u7ED1\u8D26\u53F7")
|
|
708
726
|
)
|
|
709
727
|
),
|
|
710
|
-
// 未配置 /
|
|
728
|
+
// 未配置 / 登录中:表单(QQ / 飞书)或二维码(微信)
|
|
711
729
|
(!platform?.configured || showQr) && React.createElement(
|
|
712
730
|
"div",
|
|
713
731
|
{ style: s.block },
|
|
@@ -721,39 +739,41 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
721
739
|
login.phase === "scaned" ? "\u5DF2\u626B\u7801\uFF0C\u8BF7\u5728\u624B\u673A\u4E0A\u786E\u8BA4\u2026" : platformId === "wechat" ? "\u8BF7\u4F7F\u7528\u5FAE\u4FE1\u626B\u7801\u767B\u5F55\uFF08ClawBot\uFF09" : "\u8BF7\u626B\u7801\u767B\u5F55"
|
|
722
740
|
),
|
|
723
741
|
login.error && React.createElement("div", { style: { ...s.muted, marginTop: 4, color: "var(--dsw-alias-state-warn-primary,#92400e)" } }, login.error)
|
|
724
|
-
) : platformId === "qq" ? React.createElement(
|
|
742
|
+
) : platformId === "qq" || platformId === "feishu" ? React.createElement(
|
|
725
743
|
"div",
|
|
726
744
|
{ style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4 } },
|
|
727
745
|
React.createElement(
|
|
728
746
|
"div",
|
|
729
747
|
null,
|
|
730
|
-
React.createElement(
|
|
748
|
+
React.createElement(
|
|
749
|
+
"div",
|
|
750
|
+
{ style: { ...s.muted, marginBottom: 4 } },
|
|
751
|
+
platformId === "qq" ? "AppID \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5E94\u7528 ID" : "App ID \u2014 \u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u81EA\u5EFA\u5E94\u7528 ID (cli_xxx)"
|
|
752
|
+
),
|
|
731
753
|
React.createElement("input", {
|
|
732
754
|
style: { ...s.input, width: "100%" },
|
|
733
|
-
placeholder: "\u8BF7\u8F93\u5165 AppID",
|
|
755
|
+
placeholder: platformId === "qq" ? "\u8BF7\u8F93\u5165 AppID" : "\u8BF7\u8F93\u5165 App ID (\u5982 cli_a1b2c3d4...)",
|
|
734
756
|
value: cfgDraft?.appId ?? "",
|
|
735
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
|
|
736
|
-
onKeyDown: (e) => {
|
|
737
|
-
if (e.key === "Enter" && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig();
|
|
738
|
-
}
|
|
757
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
|
|
739
758
|
})
|
|
740
759
|
),
|
|
741
760
|
React.createElement(
|
|
742
761
|
"div",
|
|
743
762
|
null,
|
|
744
|
-
React.createElement(
|
|
763
|
+
React.createElement(
|
|
764
|
+
"div",
|
|
765
|
+
{ style: { ...s.muted, marginBottom: 4 } },
|
|
766
|
+
platformId === "qq" ? "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5" : "App Secret \u2014 \u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u5E94\u7528\u5BC6\u94A5"
|
|
767
|
+
),
|
|
745
768
|
React.createElement(
|
|
746
769
|
"div",
|
|
747
770
|
{ style: { display: "flex", gap: 6, alignItems: "center" } },
|
|
748
771
|
React.createElement("input", {
|
|
749
772
|
style: { ...s.input, flex: 1 },
|
|
750
773
|
type: showSecret ? "text" : "password",
|
|
751
|
-
placeholder: "\u8BF7\u8F93\u5165 ClientSecret",
|
|
752
|
-
value: cfgDraft?.clientSecret ?? "",
|
|
753
|
-
onChange: (e) => setCfgDraft((d) =>
|
|
754
|
-
onKeyDown: (e) => {
|
|
755
|
-
if (e.key === "Enter" && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig();
|
|
756
|
-
}
|
|
774
|
+
placeholder: platformId === "qq" ? "\u8BF7\u8F93\u5165 ClientSecret" : "\u8BF7\u8F93\u5165 App Secret",
|
|
775
|
+
value: platformId === "qq" ? cfgDraft?.clientSecret ?? "" : cfgDraft?.appSecret ?? "",
|
|
776
|
+
onChange: (e) => setCfgDraft((d) => platformId === "qq" ? { ...d, clientSecret: e.target.value } : { ...d, appSecret: e.target.value })
|
|
757
777
|
}),
|
|
758
778
|
React.createElement("button", {
|
|
759
779
|
style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
|
|
@@ -767,11 +787,11 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
767
787
|
"div",
|
|
768
788
|
null,
|
|
769
789
|
React.createElement("a", {
|
|
770
|
-
href: "https://bot.q.qq.com/wiki/develop/api-v2/",
|
|
790
|
+
href: platformId === "qq" ? "https://bot.q.qq.com/wiki/develop/api-v2/" : "https://open.feishu.cn/app",
|
|
771
791
|
target: "_blank",
|
|
772
792
|
rel: "noopener noreferrer",
|
|
773
793
|
style: s.btnLink
|
|
774
|
-
}, "\u{1F4D6} \u524D\u5F80 QQ \u5F00\u653E\u5E73\u53F0\u7533\u8BF7\u673A\u5668\u4EBA")
|
|
794
|
+
}, platformId === "qq" ? "\u{1F4D6} \u524D\u5F80 QQ \u5F00\u653E\u5E73\u53F0\u7533\u8BF7\u673A\u5668\u4EBA" : "\u{1F4D6} \u524D\u5F80\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u521B\u5EFA\u4F01\u4E1A\u81EA\u5EFA\u5E94\u7528")
|
|
775
795
|
),
|
|
776
796
|
React.createElement(
|
|
777
797
|
"div",
|
|
@@ -779,7 +799,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
779
799
|
React.createElement("button", {
|
|
780
800
|
style: { ...s.btnPri, opacity: busy ? 0.5 : 1 },
|
|
781
801
|
onClick: saveConfig,
|
|
782
|
-
disabled: busy || !cfgDraft?.appId?.trim() || !cfgDraft?.clientSecret?.trim()
|
|
802
|
+
disabled: busy || !cfgDraft?.appId?.trim() || (platformId === "qq" ? !cfgDraft?.clientSecret?.trim() : !cfgDraft?.appSecret?.trim())
|
|
783
803
|
}, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5E76\u8FDE\u63A5"),
|
|
784
804
|
login.phase === "error" && React.createElement("div", { style: { ...s.muted, fontSize: 12 } }, login.error ?? "\u8FDE\u63A5\u5931\u8D25")
|
|
785
805
|
)
|
|
@@ -794,138 +814,6 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
794
814
|
login.phase === "error" && React.createElement("div", { style: { ...s.muted, fontSize: 12 } }, login.error ?? "\u767B\u5F55\u5931\u8D25")
|
|
795
815
|
)
|
|
796
816
|
),
|
|
797
|
-
// 高级设置(紧凑 2x2 网格 + 恢复默认值)
|
|
798
|
-
cfgDraft && React.createElement(
|
|
799
|
-
"div",
|
|
800
|
-
{ style: s.block },
|
|
801
|
-
React.createElement("button", {
|
|
802
|
-
style: { ...s.btnLink, fontSize: 12, marginBottom: showAdvanced ? 10 : 0 },
|
|
803
|
-
onClick: () => setShowAdvanced((v) => !v)
|
|
804
|
-
}, showAdvanced ? "\u25BE \u9AD8\u7EA7\u8BBE\u7F6E" : "\u25B8 \u9AD8\u7EA7\u8BBE\u7F6E"),
|
|
805
|
-
showAdvanced && React.createElement(
|
|
806
|
-
"div",
|
|
807
|
-
{ style: { display: "flex", flexDirection: "column", gap: 10 } },
|
|
808
|
-
// 2x2 参数网格
|
|
809
|
-
React.createElement(
|
|
810
|
-
"div",
|
|
811
|
-
{
|
|
812
|
-
style: {
|
|
813
|
-
display: "grid",
|
|
814
|
-
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
|
815
|
-
gap: 10
|
|
816
|
-
}
|
|
817
|
-
},
|
|
818
|
-
// 心跳间隔
|
|
819
|
-
React.createElement(
|
|
820
|
-
"div",
|
|
821
|
-
null,
|
|
822
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5FC3\u8DF3\u8FDB\u5EA6\u95F4\u9694 (\u79D2)"),
|
|
823
|
-
React.createElement("input", {
|
|
824
|
-
style: { ...s.input, width: "100%" },
|
|
825
|
-
type: "number",
|
|
826
|
-
min: 30,
|
|
827
|
-
max: 3600,
|
|
828
|
-
value: cfgDraft.digestIntervalSec,
|
|
829
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, digestIntervalSec: e.target.value }))
|
|
830
|
-
})
|
|
831
|
-
),
|
|
832
|
-
// 审批超时
|
|
833
|
-
React.createElement(
|
|
834
|
-
"div",
|
|
835
|
-
null,
|
|
836
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5BA1\u6279\u8D85\u65F6\u81EA\u52A8\u62D2\u7EDD (\u79D2)"),
|
|
837
|
-
React.createElement("input", {
|
|
838
|
-
style: { ...s.input, width: "100%" },
|
|
839
|
-
type: "number",
|
|
840
|
-
min: 30,
|
|
841
|
-
max: 86400,
|
|
842
|
-
value: cfgDraft.approvalTimeoutSec,
|
|
843
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, approvalTimeoutSec: e.target.value }))
|
|
844
|
-
})
|
|
845
|
-
),
|
|
846
|
-
// 每气泡字数
|
|
847
|
-
React.createElement(
|
|
848
|
-
"div",
|
|
849
|
-
null,
|
|
850
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5355\u6761\u6700\u5927\u5B57\u6570\u9650\u5236 (\u5B57)"),
|
|
851
|
-
React.createElement("input", {
|
|
852
|
-
style: { ...s.input, width: "100%" },
|
|
853
|
-
type: "number",
|
|
854
|
-
min: 100,
|
|
855
|
-
max: 1e4,
|
|
856
|
-
value: cfgDraft.maxMessageChars,
|
|
857
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, maxMessageChars: e.target.value }))
|
|
858
|
-
})
|
|
859
|
-
),
|
|
860
|
-
// 分块延迟
|
|
861
|
-
React.createElement(
|
|
862
|
-
"div",
|
|
863
|
-
null,
|
|
864
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5206\u5757\u53D1\u9001\u5EF6\u8FDF (\u6BEB\u79D2)"),
|
|
865
|
-
React.createElement("input", {
|
|
866
|
-
style: { ...s.input, width: "100%" },
|
|
867
|
-
type: "number",
|
|
868
|
-
min: 0,
|
|
869
|
-
max: 1e4,
|
|
870
|
-
value: cfgDraft.sendChunkDelayMs,
|
|
871
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, sendChunkDelayMs: e.target.value }))
|
|
872
|
-
})
|
|
873
|
-
)
|
|
874
|
-
),
|
|
875
|
-
// QQ 平台凭证
|
|
876
|
-
platformId === "qq" && React.createElement(
|
|
877
|
-
"div",
|
|
878
|
-
{ style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4 } },
|
|
879
|
-
React.createElement(
|
|
880
|
-
"div",
|
|
881
|
-
null,
|
|
882
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "AppID \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5E94\u7528 ID"),
|
|
883
|
-
React.createElement("input", {
|
|
884
|
-
style: { ...s.input, width: "100%" },
|
|
885
|
-
placeholder: "\u8BF7\u8F93\u5165 AppID",
|
|
886
|
-
value: cfgDraft.appId,
|
|
887
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
|
|
888
|
-
})
|
|
889
|
-
),
|
|
890
|
-
React.createElement(
|
|
891
|
-
"div",
|
|
892
|
-
null,
|
|
893
|
-
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5\uFF08\u7559\u7A7A\u5219\u4FDD\u6301\u5DF2\u4FDD\u5B58\u5BC6\u94A5\uFF09"),
|
|
894
|
-
React.createElement(
|
|
895
|
-
"div",
|
|
896
|
-
{ style: { display: "flex", gap: 6, alignItems: "center" } },
|
|
897
|
-
React.createElement("input", {
|
|
898
|
-
style: { ...s.input, flex: 1 },
|
|
899
|
-
type: showSecret ? "text" : "password",
|
|
900
|
-
placeholder: "\u7559\u7A7A\u4FDD\u6301\u5DF2\u4FDD\u5B58\u5BC6\u94A5",
|
|
901
|
-
value: cfgDraft.clientSecret,
|
|
902
|
-
onChange: (e) => setCfgDraft((d) => ({ ...d, clientSecret: e.target.value }))
|
|
903
|
-
}),
|
|
904
|
-
React.createElement("button", {
|
|
905
|
-
style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
|
|
906
|
-
onClick: () => setShowSecret((v) => !v),
|
|
907
|
-
type: "button",
|
|
908
|
-
title: showSecret ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u660E\u6587"
|
|
909
|
-
}, showSecret ? "\u{1F648} \u9690\u85CF" : "\u{1F441}\uFE0F \u663E\u793A")
|
|
910
|
-
)
|
|
911
|
-
)
|
|
912
|
-
),
|
|
913
|
-
React.createElement(
|
|
914
|
-
"div",
|
|
915
|
-
{ style: { display: "flex", gap: 8, alignItems: "center", marginTop: 4 } },
|
|
916
|
-
React.createElement("button", {
|
|
917
|
-
style: { ...s.btnPri, opacity: cfgDirty && !busy ? 1 : 0.5 },
|
|
918
|
-
disabled: !cfgDirty || busy,
|
|
919
|
-
onClick: saveConfig
|
|
920
|
-
}, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u8BBE\u7F6E"),
|
|
921
|
-
React.createElement("button", {
|
|
922
|
-
style: { ...s.btnGhost, height: 32, fontSize: 12 },
|
|
923
|
-
onClick: resetDefaults,
|
|
924
|
-
title: "\u6062\u590D\u63A8\u8350\u9ED8\u8BA4\u914D\u7F6E"
|
|
925
|
-
}, "\u21BA \u6062\u590D\u63A8\u8350\u9ED8\u8BA4")
|
|
926
|
-
)
|
|
927
|
-
)
|
|
928
|
-
),
|
|
929
817
|
React.createElement(
|
|
930
818
|
"div",
|
|
931
819
|
{ style: s.block },
|
|
@@ -1365,7 +1253,7 @@ function BridgePanel({ rpcCall }) {
|
|
|
1365
1253
|
const IM_PLATFORMS = [
|
|
1366
1254
|
{ id: "wechat", label: "\u5FAE\u4FE1", icon: Icons.wechat, brandColor: "#07C160", desc: "ClawBot \u626B\u7801\u76F4\u8FDE \xB7 \u65E0\u9700\u516C\u7F51" },
|
|
1367
1255
|
{ id: "qq", label: "QQ", icon: Icons.qq, brandColor: "#12B7F5", desc: "\u5B98\u65B9\u673A\u5668\u4EBA \xB7 \u79C1\u804A/\u7FA4\u804A/\u6309\u94AE" },
|
|
1368
|
-
{ id: "feishu", label: "\u98DE\u4E66", icon: Icons.feishu, brandColor: "#00D6B9", desc: "\u5B98\u65B9\
|
|
1256
|
+
{ id: "feishu", label: "\u98DE\u4E66", icon: Icons.feishu, brandColor: "#00D6B9", desc: "\u5B98\u65B9 WebSocket \u957F\u8FDE\u63A5 \xB7 \u514D\u516C\u7F51" },
|
|
1369
1257
|
{ id: "telegram", label: "Telegram", icon: Icons.telegram, brandColor: "#24A1DE", desc: "\u5B98\u65B9 Bot API" }
|
|
1370
1258
|
];
|
|
1371
1259
|
tabContent = React.createElement(
|
package/client/index.js
CHANGED
|
@@ -348,6 +348,8 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
348
348
|
appId: platform.config.appId ?? '',
|
|
349
349
|
// Secret 不由后端回传;空值表示沿用已保存密钥
|
|
350
350
|
clientSecret: '',
|
|
351
|
+
appSecret: '',
|
|
352
|
+
domain: platform.config.domain ?? 'feishu',
|
|
351
353
|
});
|
|
352
354
|
}
|
|
353
355
|
}, [platform?.config]);
|
|
@@ -436,10 +438,14 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
436
438
|
maxMessageChars: Number(cfgDraft.maxMessageChars),
|
|
437
439
|
sendChunkDelayMs: Number(cfgDraft.sendChunkDelayMs),
|
|
438
440
|
};
|
|
439
|
-
// QQ 平台额外携带凭证
|
|
441
|
+
// QQ / 飞书 平台额外携带凭证
|
|
440
442
|
if (platformId === 'qq') {
|
|
441
443
|
payload.appId = cfgDraft.appId.trim();
|
|
442
444
|
payload.clientSecret = cfgDraft.clientSecret.trim();
|
|
445
|
+
} else if (platformId === 'feishu') {
|
|
446
|
+
payload.appId = cfgDraft.appId.trim();
|
|
447
|
+
payload.appSecret = cfgDraft.appSecret.trim();
|
|
448
|
+
payload.domain = cfgDraft.domain || 'feishu';
|
|
443
449
|
}
|
|
444
450
|
await act(BRIDGE_ENDPOINTS.platformSetConfig, payload);
|
|
445
451
|
}, [act, cfgDraft, platformId]);
|
|
@@ -451,6 +457,10 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
451
457
|
(platformId === 'qq' && (
|
|
452
458
|
cfgDraft.appId !== (platform.config.appId ?? '') ||
|
|
453
459
|
cfgDraft.clientSecret !== (platform.config.clientSecret ?? '')
|
|
460
|
+
)) ||
|
|
461
|
+
(platformId === 'feishu' && (
|
|
462
|
+
cfgDraft.appId !== (platform.config.appId ?? '') ||
|
|
463
|
+
cfgDraft.appSecret !== (platform.config.appSecret ?? '')
|
|
454
464
|
))
|
|
455
465
|
);
|
|
456
466
|
|
|
@@ -503,6 +513,16 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
503
513
|
target: '_blank', rel: 'noopener noreferrer',
|
|
504
514
|
style: s.btnGhost,
|
|
505
515
|
}, '🌐 QQ 开放平台'),
|
|
516
|
+
platformId === 'feishu' && React.createElement('a', {
|
|
517
|
+
href: 'https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/feishu-usage.md',
|
|
518
|
+
target: '_blank', rel: 'noopener noreferrer',
|
|
519
|
+
style: s.btnGhost,
|
|
520
|
+
}, '📖 飞书使用说明'),
|
|
521
|
+
platformId === 'feishu' && React.createElement('a', {
|
|
522
|
+
href: 'https://open.feishu.cn/app',
|
|
523
|
+
target: '_blank', rel: 'noopener noreferrer',
|
|
524
|
+
style: s.btnGhost,
|
|
525
|
+
}, '🌐 飞书开放平台'),
|
|
506
526
|
React.createElement('button', {
|
|
507
527
|
style: s.btnGhost,
|
|
508
528
|
onClick: () => setShowHelp(v => !v),
|
|
@@ -613,19 +633,19 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
613
633
|
),
|
|
614
634
|
React.createElement('div', { style: { display: 'flex', gap: 8, marginTop: 10, flexWrap: 'wrap', alignItems: 'center' } },
|
|
615
635
|
platform.status !== 'connected' && platform.status !== 'starting' &&
|
|
616
|
-
React.createElement('button', { style: s.btnPri, onClick: onLogin, disabled: busy }, '
|
|
636
|
+
React.createElement('button', { style: s.btnPri, onClick: onLogin, disabled: busy }, '重新连接'),
|
|
617
637
|
(platform.status === 'connected' || platform.status === 'starting') &&
|
|
618
638
|
React.createElement('button', { style: s.btnGhost, onClick: onStop, disabled: busy }, '断开'),
|
|
619
639
|
React.createElement('button', {
|
|
620
640
|
style: { ...s.btnGhost, color: 'var(--dsw-alias-state-error-primary,#dc2626)', borderColor: 'var(--dsw-alias-state-error-primary,#dc2626)', opacity: busy ? 0.5 : 1 },
|
|
621
641
|
disabled: busy,
|
|
622
|
-
onClick: () => { if (window.confirm('
|
|
623
|
-
title: '
|
|
642
|
+
onClick: () => { if (window.confirm('确认解绑?这将清除保存的凭证。')) act(BRIDGE_ENDPOINTS.platformUnbind, {}); },
|
|
643
|
+
title: '清除登录凭证,下次需重新配置',
|
|
624
644
|
}, '解绑账号'),
|
|
625
645
|
),
|
|
626
646
|
),
|
|
627
647
|
|
|
628
|
-
// 未配置 /
|
|
648
|
+
// 未配置 / 登录中:表单(QQ / 飞书)或二维码(微信)
|
|
629
649
|
(!platform?.configured || showQr) && React.createElement('div', { style: s.block },
|
|
630
650
|
showQr && login.qr
|
|
631
651
|
? React.createElement('div', null,
|
|
@@ -637,28 +657,30 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
637
657
|
),
|
|
638
658
|
login.error && React.createElement('div', { style: { ...s.muted, marginTop: 4, color: 'var(--dsw-alias-state-warn-primary,#92400e)' } }, login.error),
|
|
639
659
|
)
|
|
640
|
-
: platformId === 'qq'
|
|
660
|
+
: (platformId === 'qq' || platformId === 'feishu')
|
|
641
661
|
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 4 } },
|
|
642
662
|
React.createElement('div', null,
|
|
643
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } },
|
|
663
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } },
|
|
664
|
+
platformId === 'qq' ? 'AppID — QQ 开放平台机器人应用 ID' : 'App ID — 飞书开放平台自建应用 ID (cli_xxx)'
|
|
665
|
+
),
|
|
644
666
|
React.createElement('input', {
|
|
645
667
|
style: { ...s.input, width: '100%' },
|
|
646
|
-
placeholder: '请输入 AppID',
|
|
668
|
+
placeholder: platformId === 'qq' ? '请输入 AppID' : '请输入 App ID (如 cli_a1b2c3d4...)',
|
|
647
669
|
value: cfgDraft?.appId ?? '',
|
|
648
670
|
onChange: (e) => setCfgDraft(d => ({ ...d, appId: e.target.value })),
|
|
649
|
-
onKeyDown: (e) => { if (e.key === 'Enter' && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig(); },
|
|
650
671
|
}),
|
|
651
672
|
),
|
|
652
673
|
React.createElement('div', null,
|
|
653
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } },
|
|
674
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } },
|
|
675
|
+
platformId === 'qq' ? 'ClientSecret — QQ 开放平台机器人密钥' : 'App Secret — 飞书开放平台应用密钥'
|
|
676
|
+
),
|
|
654
677
|
React.createElement('div', { style: { display: 'flex', gap: 6, alignItems: 'center' } },
|
|
655
678
|
React.createElement('input', {
|
|
656
679
|
style: { ...s.input, flex: 1 },
|
|
657
680
|
type: showSecret ? 'text' : 'password',
|
|
658
|
-
placeholder: '请输入 ClientSecret',
|
|
659
|
-
value: cfgDraft?.clientSecret ?? '',
|
|
660
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, clientSecret: e.target.value })),
|
|
661
|
-
onKeyDown: (e) => { if (e.key === 'Enter' && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig(); },
|
|
681
|
+
placeholder: platformId === 'qq' ? '请输入 ClientSecret' : '请输入 App Secret',
|
|
682
|
+
value: platformId === 'qq' ? (cfgDraft?.clientSecret ?? '') : (cfgDraft?.appSecret ?? ''),
|
|
683
|
+
onChange: (e) => setCfgDraft(d => platformId === 'qq' ? ({ ...d, clientSecret: e.target.value }) : ({ ...d, appSecret: e.target.value })),
|
|
662
684
|
}),
|
|
663
685
|
React.createElement('button', {
|
|
664
686
|
style: { ...s.btnGhost, height: 32, padding: '0 10px', fontSize: 13, flexShrink: 0 },
|
|
@@ -670,15 +692,16 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
670
692
|
),
|
|
671
693
|
React.createElement('div', null,
|
|
672
694
|
React.createElement('a', {
|
|
673
|
-
href: 'https://bot.q.qq.com/wiki/develop/api-v2/',
|
|
695
|
+
href: platformId === 'qq' ? 'https://bot.q.qq.com/wiki/develop/api-v2/' : 'https://open.feishu.cn/app',
|
|
674
696
|
target: '_blank', rel: 'noopener noreferrer',
|
|
675
697
|
style: s.btnLink,
|
|
676
|
-
}, '📖 前往 QQ 开放平台申请机器人'),
|
|
698
|
+
}, platformId === 'qq' ? '📖 前往 QQ 开放平台申请机器人' : '📖 前往飞书开放平台创建企业自建应用'),
|
|
677
699
|
),
|
|
678
700
|
React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' } },
|
|
679
701
|
React.createElement('button', {
|
|
680
702
|
style: { ...s.btnPri, opacity: busy ? 0.5 : 1 },
|
|
681
|
-
onClick: saveConfig,
|
|
703
|
+
onClick: saveConfig,
|
|
704
|
+
disabled: busy || !cfgDraft?.appId?.trim() || (platformId === 'qq' ? !cfgDraft?.clientSecret?.trim() : !cfgDraft?.appSecret?.trim()),
|
|
682
705
|
}, busy ? '保存中…' : '保存并连接'),
|
|
683
706
|
login.phase === 'error' && React.createElement('div', { style: { ...s.muted, fontSize: 12 } }, login.error ?? '连接失败'),
|
|
684
707
|
),
|
|
@@ -692,106 +715,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
692
715
|
),
|
|
693
716
|
),
|
|
694
717
|
|
|
695
|
-
|
|
696
|
-
cfgDraft && React.createElement('div', { style: s.block },
|
|
697
|
-
React.createElement('button', {
|
|
698
|
-
style: { ...s.btnLink, fontSize: 12, marginBottom: showAdvanced ? 10 : 0 },
|
|
699
|
-
onClick: () => setShowAdvanced(v => !v),
|
|
700
|
-
}, showAdvanced ? '▾ 高级设置' : '▸ 高级设置'),
|
|
701
|
-
showAdvanced && React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10 } },
|
|
702
|
-
// 2x2 参数网格
|
|
703
|
-
React.createElement('div', {
|
|
704
|
-
style: {
|
|
705
|
-
display: 'grid',
|
|
706
|
-
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
|
707
|
-
gap: 10,
|
|
708
|
-
},
|
|
709
|
-
},
|
|
710
|
-
// 心跳间隔
|
|
711
|
-
React.createElement('div', null,
|
|
712
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '心跳进度间隔 (秒)'),
|
|
713
|
-
React.createElement('input', {
|
|
714
|
-
style: { ...s.input, width: '100%' },
|
|
715
|
-
type: 'number', min: 30, max: 3600,
|
|
716
|
-
value: cfgDraft.digestIntervalSec,
|
|
717
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, digestIntervalSec: e.target.value })),
|
|
718
|
-
}),
|
|
719
|
-
),
|
|
720
|
-
// 审批超时
|
|
721
|
-
React.createElement('div', null,
|
|
722
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '审批超时自动拒绝 (秒)'),
|
|
723
|
-
React.createElement('input', {
|
|
724
|
-
style: { ...s.input, width: '100%' },
|
|
725
|
-
type: 'number', min: 30, max: 86400,
|
|
726
|
-
value: cfgDraft.approvalTimeoutSec,
|
|
727
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, approvalTimeoutSec: e.target.value })),
|
|
728
|
-
}),
|
|
729
|
-
),
|
|
730
|
-
// 每气泡字数
|
|
731
|
-
React.createElement('div', null,
|
|
732
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '单条最大字数限制 (字)'),
|
|
733
|
-
React.createElement('input', {
|
|
734
|
-
style: { ...s.input, width: '100%' },
|
|
735
|
-
type: 'number', min: 100, max: 10000,
|
|
736
|
-
value: cfgDraft.maxMessageChars,
|
|
737
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, maxMessageChars: e.target.value })),
|
|
738
|
-
}),
|
|
739
|
-
),
|
|
740
|
-
// 分块延迟
|
|
741
|
-
React.createElement('div', null,
|
|
742
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '分块发送延迟 (毫秒)'),
|
|
743
|
-
React.createElement('input', {
|
|
744
|
-
style: { ...s.input, width: '100%' },
|
|
745
|
-
type: 'number', min: 0, max: 10000,
|
|
746
|
-
value: cfgDraft.sendChunkDelayMs,
|
|
747
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, sendChunkDelayMs: e.target.value })),
|
|
748
|
-
}),
|
|
749
|
-
),
|
|
750
|
-
),
|
|
751
|
-
// QQ 平台凭证
|
|
752
|
-
platformId === 'qq' && React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 4 } },
|
|
753
|
-
React.createElement('div', null,
|
|
754
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, 'AppID — QQ 开放平台机器人应用 ID'),
|
|
755
|
-
React.createElement('input', {
|
|
756
|
-
style: { ...s.input, width: '100%' },
|
|
757
|
-
placeholder: '请输入 AppID',
|
|
758
|
-
value: cfgDraft.appId,
|
|
759
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, appId: e.target.value })),
|
|
760
|
-
}),
|
|
761
|
-
),
|
|
762
|
-
React.createElement('div', null,
|
|
763
|
-
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, 'ClientSecret — QQ 开放平台机器人密钥(留空则保持已保存密钥)'),
|
|
764
|
-
React.createElement('div', { style: { display: 'flex', gap: 6, alignItems: 'center' } },
|
|
765
|
-
React.createElement('input', {
|
|
766
|
-
style: { ...s.input, flex: 1 },
|
|
767
|
-
type: showSecret ? 'text' : 'password',
|
|
768
|
-
placeholder: '留空保持已保存密钥',
|
|
769
|
-
value: cfgDraft.clientSecret,
|
|
770
|
-
onChange: (e) => setCfgDraft(d => ({ ...d, clientSecret: e.target.value })),
|
|
771
|
-
}),
|
|
772
|
-
React.createElement('button', {
|
|
773
|
-
style: { ...s.btnGhost, height: 32, padding: '0 10px', fontSize: 13, flexShrink: 0 },
|
|
774
|
-
onClick: () => setShowSecret(v => !v),
|
|
775
|
-
type: 'button',
|
|
776
|
-
title: showSecret ? '隐藏密钥' : '显示明文',
|
|
777
|
-
}, showSecret ? '🙈 隐藏' : '👁️ 显示'),
|
|
778
|
-
),
|
|
779
|
-
),
|
|
780
|
-
),
|
|
781
|
-
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', marginTop: 4 } },
|
|
782
|
-
React.createElement('button', {
|
|
783
|
-
style: { ...s.btnPri, opacity: (cfgDirty && !busy) ? 1 : 0.5 },
|
|
784
|
-
disabled: !cfgDirty || busy,
|
|
785
|
-
onClick: saveConfig,
|
|
786
|
-
}, busy ? '保存中…' : '保存设置'),
|
|
787
|
-
React.createElement('button', {
|
|
788
|
-
style: { ...s.btnGhost, height: 32, fontSize: 12 },
|
|
789
|
-
onClick: resetDefaults,
|
|
790
|
-
title: '恢复推荐默认配置',
|
|
791
|
-
}, '↺ 恢复推荐默认'),
|
|
792
|
-
),
|
|
793
|
-
),
|
|
794
|
-
),
|
|
718
|
+
|
|
795
719
|
|
|
796
720
|
React.createElement('div', { style: s.block },
|
|
797
721
|
React.createElement('div', { style: { ...s.tip, fontSize: 12 } },
|
|
@@ -1221,7 +1145,7 @@ function BridgePanel({ rpcCall }) {
|
|
|
1221
1145
|
const IM_PLATFORMS = [
|
|
1222
1146
|
{ id: 'wechat', label: '微信', icon: Icons.wechat, brandColor: '#07C160', desc: 'ClawBot 扫码直连 · 无需公网' },
|
|
1223
1147
|
{ id: 'qq', label: 'QQ', icon: Icons.qq, brandColor: '#12B7F5', desc: '官方机器人 · 私聊/群聊/按钮' },
|
|
1224
|
-
{ id: 'feishu', label: '飞书', icon: Icons.feishu, brandColor: '#00D6B9', desc: '
|
|
1148
|
+
{ id: 'feishu', label: '飞书', icon: Icons.feishu, brandColor: '#00D6B9', desc: '官方 WebSocket 长连接 · 免公网' },
|
|
1225
1149
|
{ id: 'telegram', label: 'Telegram', icon: Icons.telegram, brandColor: '#24A1DE', desc: '官方 Bot API' },
|
|
1226
1150
|
];
|
|
1227
1151
|
|