@xmanrui/dsh-im 0.1.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/LICENSE +21 -0
- package/README.md +143 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/bin/dsh-im.mjs +99 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +6211 -0
- package/lib/index.js +13339 -0
- package/package.json +73 -0
- package/plugin-src/client/build.mjs +38 -0
- package/plugin-src/client/channel-logos.js +107 -0
- package/plugin-src/client/channels/dingtalk/api.js +203 -0
- package/plugin-src/client/channels/dingtalk/index.js +709 -0
- package/plugin-src/client/channels/dingtalk/styles.js +140 -0
- package/plugin-src/client/channels/discord/api.js +11 -0
- package/plugin-src/client/channels/discord/index.js +25 -0
- package/plugin-src/client/channels/discord/styles.js +19 -0
- package/plugin-src/client/channels/feishu/api.js +325 -0
- package/plugin-src/client/channels/feishu/index.js +986 -0
- package/plugin-src/client/channels/feishu/styles.js +505 -0
- package/plugin-src/client/channels/qq/api.js +119 -0
- package/plugin-src/client/channels/qq/index.js +402 -0
- package/plugin-src/client/channels/qq/styles.js +19 -0
- package/plugin-src/client/channels/shared/token-api.js +88 -0
- package/plugin-src/client/channels/shared/token-channel.js +277 -0
- package/plugin-src/client/channels/telegram/api.js +11 -0
- package/plugin-src/client/channels/telegram/index.js +25 -0
- package/plugin-src/client/channels/telegram/styles.js +19 -0
- package/plugin-src/client/channels/wecom/api.js +119 -0
- package/plugin-src/client/channels/wecom/index.js +402 -0
- package/plugin-src/client/channels/wecom/styles.js +19 -0
- package/plugin-src/client/channels/weixin/api.js +156 -0
- package/plugin-src/client/channels/weixin/index.js +577 -0
- package/plugin-src/client/channels/weixin/styles.js +120 -0
- package/plugin-src/client/credential-binding.js +117 -0
- package/plugin-src/client/index.js +192 -0
- package/plugin-src/client/lifecycle.js +86 -0
- package/plugin-src/client/styles.js +192 -0
- package/plugin-src/host/build.mjs +24 -0
- package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
- package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
- package/plugin-src/host/channels/discord/index.mjs +30 -0
- package/plugin-src/host/channels/discord/production.mjs +17 -0
- package/plugin-src/host/channels/discord/rpc.mjs +21 -0
- package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
- package/plugin-src/host/channels/feishu/controller.mjs +172 -0
- package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
- package/plugin-src/host/channels/feishu/index.mjs +73 -0
- package/plugin-src/host/channels/feishu/production.mjs +138 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
- package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/qq/index.mjs +30 -0
- package/plugin-src/host/channels/qq/production.mjs +109 -0
- package/plugin-src/host/channels/qq/rpc.mjs +146 -0
- package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
- package/plugin-src/host/channels/shared/production.mjs +104 -0
- package/plugin-src/host/channels/shared/rpc.mjs +119 -0
- package/plugin-src/host/channels/telegram/index.mjs +30 -0
- package/plugin-src/host/channels/telegram/production.mjs +17 -0
- package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
- package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/wecom/index.mjs +36 -0
- package/plugin-src/host/channels/wecom/production.mjs +113 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
- package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
- package/plugin-src/host/channels/weixin/index.mjs +39 -0
- package/plugin-src/host/channels/weixin/production.mjs +119 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
- package/plugin-src/host/index.mjs +44 -0
- package/plugin-src/host/rpc-authority.mjs +11 -0
- package/scripts/verify-package.mjs +97 -0
- package/src/channels/dingtalk/config-store.mjs +282 -0
- package/src/channels/dingtalk/device-auth.mjs +237 -0
- package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
- package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
- package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
- package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
- package/src/channels/dingtalk/harness-client.mjs +299 -0
- package/src/channels/dingtalk/state-store.mjs +212 -0
- package/src/channels/discord/config-store.mjs +24 -0
- package/src/channels/discord/discord-api.mjs +153 -0
- package/src/channels/discord/discord-bridge.mjs +15 -0
- package/src/channels/discord/discord-controller.mjs +16 -0
- package/src/channels/discord/discord-runtime.mjs +460 -0
- package/src/channels/discord/harness-client.mjs +3 -0
- package/src/channels/discord/state-store.mjs +3 -0
- package/src/channels/feishu/bridge.mjs +216 -0
- package/src/channels/feishu/config.mjs +71 -0
- package/src/channels/feishu/feishu-app.mjs +51 -0
- package/src/channels/feishu/feishu-channel.mjs +153 -0
- package/src/channels/feishu/feishu-runtime.mjs +222 -0
- package/src/channels/feishu/harness-client.mjs +268 -0
- package/src/channels/feishu/message-utils.mjs +51 -0
- package/src/channels/feishu/multi-bot-controller.mjs +686 -0
- package/src/channels/feishu/plugin-config-store.mjs +204 -0
- package/src/channels/feishu/plugin-controller.mjs +248 -0
- package/src/channels/feishu/registration-manager.mjs +345 -0
- package/src/channels/feishu/state-store.mjs +71 -0
- package/src/channels/qq/config-store.mjs +171 -0
- package/src/channels/qq/harness-client.mjs +3 -0
- package/src/channels/qq/qq-bridge.mjs +177 -0
- package/src/channels/qq/qq-controller.mjs +452 -0
- package/src/channels/qq/qq-runtime.mjs +208 -0
- package/src/channels/qq/qr-auth.mjs +24 -0
- package/src/channels/qq/state-store.mjs +96 -0
- package/src/channels/shared/conversation-state-store.mjs +107 -0
- package/src/channels/shared/editable-message-stream.mjs +81 -0
- package/src/channels/shared/text-harness-bridge.mjs +180 -0
- package/src/channels/shared/token-bot-controller.mjs +298 -0
- package/src/channels/shared/token-config-store.mjs +180 -0
- package/src/channels/telegram/config-store.mjs +24 -0
- package/src/channels/telegram/harness-client.mjs +3 -0
- package/src/channels/telegram/state-store.mjs +3 -0
- package/src/channels/telegram/telegram-api.mjs +120 -0
- package/src/channels/telegram/telegram-bridge.mjs +15 -0
- package/src/channels/telegram/telegram-controller.mjs +16 -0
- package/src/channels/telegram/telegram-runtime.mjs +278 -0
- package/src/channels/wecom/config-store.mjs +170 -0
- package/src/channels/wecom/harness-client.mjs +3 -0
- package/src/channels/wecom/qr-auth.mjs +95 -0
- package/src/channels/wecom/state-store.mjs +90 -0
- package/src/channels/wecom/wecom-bridge.mjs +243 -0
- package/src/channels/wecom/wecom-controller.mjs +474 -0
- package/src/channels/wecom/wecom-runtime.mjs +209 -0
- package/src/channels/weixin/config-store.mjs +182 -0
- package/src/channels/weixin/harness-client.mjs +259 -0
- package/src/channels/weixin/state-store.mjs +112 -0
- package/src/channels/weixin/weixin-api.mjs +319 -0
- package/src/channels/weixin/weixin-bridge.mjs +173 -0
- package/src/channels/weixin/weixin-controller.mjs +545 -0
- package/src/channels/weixin/weixin-runtime.mjs +204 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export const WEIXIN_STYLE_ID = 'xmanrui-dsh-weixin-settings';
|
|
2
|
+
|
|
3
|
+
const CSS = String.raw`
|
|
4
|
+
.dxw-page {
|
|
5
|
+
--dxw-accent: #07c160;
|
|
6
|
+
--dxw-accent-dark: #05994c;
|
|
7
|
+
--dxw-success: var(--dsw-alias-state-success-primary, #20a162);
|
|
8
|
+
--dxw-warning: var(--dsw-alias-state-warning-primary, #d97706);
|
|
9
|
+
--dxw-error: var(--dsw-alias-state-error-primary, #d54941);
|
|
10
|
+
width: 100%;
|
|
11
|
+
max-width: 880px;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 18px;
|
|
15
|
+
padding: 2px 0 28px;
|
|
16
|
+
color: var(--dsw-alias-label-primary, #1f2329);
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
.dxw-page *, .dxw-page *::before, .dxw-page *::after { box-sizing: border-box; }
|
|
20
|
+
.dxw-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
|
|
21
|
+
.dxw-heading h2, .dxw-heading p, .dxw-card h3, .dxw-card p { margin: 0; }
|
|
22
|
+
.dxw-eyebrow { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; font-weight: 650; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 3px; }
|
|
23
|
+
.dxw-heading h2 { font-size: 20px; line-height: 28px; font-weight: 680; }
|
|
24
|
+
.dxw-heading p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 20px; margin-top: 5px; white-space: nowrap; }
|
|
25
|
+
.dxw-tools, .dxw-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
26
|
+
.dxw-tools { width: 100%; justify-content: space-between; flex-wrap: nowrap; }
|
|
27
|
+
.dxw-badge { display: inline-flex; align-items: center; gap: 7px; min-height: 30px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-fill-secondary, #f2f3f5); font-size: 12px; white-space: nowrap; }
|
|
28
|
+
.dxw-dot { width: 8px; height: 8px; border-radius: 50%; background: #aeb3bb; flex: none; }
|
|
29
|
+
.dxw-dot[data-tone="success"] { background: var(--dxw-success); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dxw-success) 14%, transparent); }
|
|
30
|
+
.dxw-dot[data-tone="warning"] { background: var(--dxw-warning); }
|
|
31
|
+
.dxw-dot[data-tone="error"] { background: var(--dxw-error); }
|
|
32
|
+
.dxw-button { min-height: 34px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 8px; padding: 0 13px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-body, #fff); font: inherit; font-size: 13px; font-weight: 560; cursor: pointer; text-decoration: none; transition: border-color .15s ease, background .15s ease, transform .15s ease; }
|
|
33
|
+
.dxw-button:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
|
|
34
|
+
.dxw-button:active:not(:disabled) { transform: translateY(1px); }
|
|
35
|
+
.dxw-button:focus-visible, .dxw-input:focus-visible { outline: 2px solid color-mix(in srgb, var(--dxw-accent) 70%, white); outline-offset: 2px; }
|
|
36
|
+
.dxw-button:disabled { cursor: not-allowed; opacity: .55; }
|
|
37
|
+
.dxw-button[data-kind="primary"] { color: white; border-color: var(--dxw-accent); background: var(--dxw-accent); }
|
|
38
|
+
.dxw-button[data-kind="primary"]:hover:not(:disabled) { border-color: var(--dxw-accent-dark); background: var(--dxw-accent-dark); }
|
|
39
|
+
.dxw-button[data-kind="danger"] { color: var(--dxw-error); }
|
|
40
|
+
.dxw-card { overflow: hidden; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-body, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
41
|
+
.dxw-cardBody { padding: 24px; }
|
|
42
|
+
.dxw-empty { min-height: 230px; display: grid; grid-template-columns: minmax(0, 1fr) 180px; align-items: center; gap: 30px; }
|
|
43
|
+
.dxw-empty h3 { font-size: 18px; margin-bottom: 8px; }
|
|
44
|
+
.dxw-empty p { max-width: 560px; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.65; }
|
|
45
|
+
.dxw-empty .dxw-actions { margin-top: 20px; }
|
|
46
|
+
.dxw-logo { width: 110px; height: 110px; display: grid; place-items: center; justify-self: center; border-radius: 28px; color: white; background: var(--dxw-accent); box-shadow: 0 18px 45px rgb(7 193 96 / 22%); }
|
|
47
|
+
.dxw-logo svg { width: 62px; height: 62px; }
|
|
48
|
+
.dxw-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: center; }
|
|
49
|
+
.dxw-qrColumn { display: flex; flex-direction: column; align-items: center; gap: 12px; }
|
|
50
|
+
.dxw-qrFrame { position: relative; width: 270px; aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 16px; background: white; }
|
|
51
|
+
.dxw-qrFrame img { display: block; width: 100%; height: 100%; object-fit: contain; }
|
|
52
|
+
.dxw-qrFallback { padding: 24px; text-align: center; color: #646a73; }
|
|
53
|
+
.dxw-expired { position: absolute; inset: 0; display: grid; place-items: center; padding: 30px; color: white; text-align: center; font-weight: 650; background: rgb(31 35 41 / 76%); backdrop-filter: blur(3px); }
|
|
54
|
+
.dxw-countdown { width: 270px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
|
|
55
|
+
.dxw-countdown div { display: flex; justify-content: space-between; margin-bottom: 6px; }
|
|
56
|
+
.dxw-progress { height: 4px; overflow: hidden; border-radius: 99px; background: #eef0f3; }
|
|
57
|
+
.dxw-progress span { display: block; width: var(--dxw-progress); height: 100%; background: var(--dxw-accent); transition: width .2s linear; }
|
|
58
|
+
.dxw-qrCopy h3 { margin: 9px 0 8px; font-size: 18px; }
|
|
59
|
+
.dxw-qrCopy > p { color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.65; }
|
|
60
|
+
.dxw-steps { margin: 18px 0 22px; padding-left: 22px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 1.9; }
|
|
61
|
+
.dxw-stateLabel { display: inline-flex; align-items: center; gap: 8px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; font-weight: 600; }
|
|
62
|
+
.dxw-verify { max-width: 560px; margin: 0 auto; padding: 32px; text-align: center; }
|
|
63
|
+
.dxw-verify h3 { margin: 8px 0; font-size: 19px; }
|
|
64
|
+
.dxw-verify p { color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.6; }
|
|
65
|
+
.dxw-codeRow { display: flex; justify-content: center; gap: 10px; margin: 24px 0 10px; }
|
|
66
|
+
.dxw-input { width: 190px; height: 42px; padding: 0 14px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-body, white); color: inherit; font: inherit; font-size: 18px; letter-spacing: .16em; text-align: center; }
|
|
67
|
+
.dxw-statusNotice, .dxw-error { display: flex; align-items: center; gap: 10px; padding: 13px 15px; border: 1px solid color-mix(in srgb, var(--dxw-error) 28%, transparent); border-radius: 10px; color: var(--dxw-error); background: color-mix(in srgb, var(--dxw-error) 7%, transparent); font-size: 13px; }
|
|
68
|
+
.dxw-error { align-items: flex-start; flex-direction: column; padding: 22px; }
|
|
69
|
+
.dxw-error h3 { font-size: 17px; }
|
|
70
|
+
.dxw-errorCode { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 11px; opacity: .8; }
|
|
71
|
+
.dxw-listHeading { display: flex; justify-content: space-between; align-items: center; margin: 2px 0 9px; }
|
|
72
|
+
.dxw-listHeading h3 { margin: 0; font-size: 14px; }
|
|
73
|
+
.dxw-list { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
|
|
74
|
+
.dxw-accountTop { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
|
75
|
+
.dxw-accountIdentity { display: flex; align-items: center; gap: 12px; min-width: 0; }
|
|
76
|
+
.dxw-avatar { width: 42px; height: 42px; display: grid; place-items: center; flex: none; border-radius: 12px; color: white; background: var(--dxw-accent); }
|
|
77
|
+
.dxw-accountIdentity h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; }
|
|
78
|
+
.dxw-accountIdentity p { color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; margin-top: 4px; }
|
|
79
|
+
.dxw-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
|
|
80
|
+
.dxw-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 20px 0; }
|
|
81
|
+
.dxw-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
|
|
82
|
+
.dxw-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
|
|
83
|
+
.dxw-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
84
|
+
.dxw-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-line-divider, #eef0f3); }
|
|
85
|
+
.dxw-accountFooter .dxw-actions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
|
|
86
|
+
.dxw-accountFooter .dxw-button { flex: none; white-space: nowrap; }
|
|
87
|
+
.dxw-summary { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
|
|
88
|
+
.dxw-confirm { padding: 18px 24px; border-top: 1px solid color-mix(in srgb, var(--dxw-error) 25%, transparent); background: color-mix(in srgb, var(--dxw-error) 5%, transparent); }
|
|
89
|
+
.dxw-confirm strong { display: block; font-size: 14px; margin-bottom: 6px; }
|
|
90
|
+
.dxw-confirm p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.55; }
|
|
91
|
+
.dxw-confirm .dxw-actions { margin-top: 13px; }
|
|
92
|
+
.dxw-loading { padding: 36px; color: var(--dsw-alias-label-secondary, #646a73); text-align: center; }
|
|
93
|
+
.dxw-spinner { width: 24px; height: 24px; margin: 0 auto 12px; border: 3px solid #e6e8eb; border-top-color: var(--dxw-accent); border-radius: 50%; animation: dxw-spin .8s linear infinite; }
|
|
94
|
+
.dxw-visuallyHidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
95
|
+
@keyframes dxw-spin { to { transform: rotate(360deg); } }
|
|
96
|
+
@media (max-width: 720px) {
|
|
97
|
+
.dxw-heading, .dxw-accountTop { flex-direction: column; align-items: stretch; }
|
|
98
|
+
.dxw-empty { grid-template-columns: minmax(0, 1fr); }
|
|
99
|
+
.dxw-logo { display: none; }
|
|
100
|
+
.dxw-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
|
|
101
|
+
.dxw-qrCopy { width: 100%; }
|
|
102
|
+
.dxw-metrics { grid-template-columns: minmax(0, 1fr); }
|
|
103
|
+
.dxw-cardBody { padding: 20px; }
|
|
104
|
+
}
|
|
105
|
+
@media (prefers-reduced-motion: reduce) {
|
|
106
|
+
.dxw-page *, .dxw-page *::before, .dxw-page *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
export function installWeixinStyles() {
|
|
111
|
+
if (typeof document === 'undefined') return () => {};
|
|
112
|
+
const existing = document.querySelector(`style[data-plugin-css="${WEIXIN_STYLE_ID}"]`);
|
|
113
|
+
if (existing) return () => {};
|
|
114
|
+
const style = document.createElement('style');
|
|
115
|
+
style.dataset.plugin = '@xmanrui/dsh-weixin';
|
|
116
|
+
style.dataset.pluginCss = WEIXIN_STYLE_ID;
|
|
117
|
+
style.textContent = CSS;
|
|
118
|
+
document.head.appendChild(style);
|
|
119
|
+
return () => style.remove();
|
|
120
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
const h = React.createElement;
|
|
4
|
+
|
|
5
|
+
function ActionIcon({ children }) {
|
|
6
|
+
return h('svg', {
|
|
7
|
+
className: 'dim-actionIcon',
|
|
8
|
+
width: 15,
|
|
9
|
+
height: 15,
|
|
10
|
+
viewBox: '0 0 20 20',
|
|
11
|
+
fill: 'none',
|
|
12
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
13
|
+
'aria-hidden': 'true',
|
|
14
|
+
focusable: 'false',
|
|
15
|
+
}, children);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function QrActionIcon() {
|
|
19
|
+
return h(ActionIcon, null,
|
|
20
|
+
h('path', {
|
|
21
|
+
d: 'M2.5 2.5h5v5h-5v-5Zm10 0h5v5h-5v-5Zm-10 10h5v5h-5v-5Z',
|
|
22
|
+
stroke: 'currentColor', strokeWidth: '1.6', strokeLinejoin: 'round',
|
|
23
|
+
}),
|
|
24
|
+
h('path', {
|
|
25
|
+
d: 'M11.5 11.5h2v2h-2v-2Zm4 0h2v3h-2v-3Zm-4 4h3v2h-3v-2Zm5 1h1v1h-1v-1Z',
|
|
26
|
+
fill: 'currentColor',
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function CredentialActionIcon() {
|
|
31
|
+
return h(ActionIcon, null,
|
|
32
|
+
h('circle', {
|
|
33
|
+
cx: '6.25', cy: '10', r: '3.5', stroke: 'currentColor', strokeWidth: '1.6',
|
|
34
|
+
}),
|
|
35
|
+
h('path', {
|
|
36
|
+
d: 'M9.75 10h7.75m-2.5 0v2m-2.5-2v2',
|
|
37
|
+
stroke: 'currentColor', strokeWidth: '1.6', strokeLinecap: 'round', strokeLinejoin: 'round',
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function CredentialBindingPanel({
|
|
42
|
+
channel,
|
|
43
|
+
identityLabel,
|
|
44
|
+
identityPlaceholder,
|
|
45
|
+
secretLabel,
|
|
46
|
+
secretPlaceholder,
|
|
47
|
+
busy = false,
|
|
48
|
+
error = null,
|
|
49
|
+
onSubmit,
|
|
50
|
+
onCancel,
|
|
51
|
+
}) {
|
|
52
|
+
const [identity, setIdentity] = React.useState('');
|
|
53
|
+
const [secret, setSecret] = React.useState('');
|
|
54
|
+
const headingId = React.useId();
|
|
55
|
+
const hasIdentity = Boolean(identityLabel);
|
|
56
|
+
|
|
57
|
+
const submit = (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
const normalizedIdentity = identity.trim();
|
|
60
|
+
const normalizedSecret = secret.trim();
|
|
61
|
+
if ((hasIdentity && !normalizedIdentity) || !normalizedSecret || busy) return;
|
|
62
|
+
void onSubmit?.({ identity: normalizedIdentity, secret: normalizedSecret });
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return h('section', {
|
|
66
|
+
className: 'ddt-card dim-surfaceCard dim-credentialPanel',
|
|
67
|
+
'aria-labelledby': headingId,
|
|
68
|
+
},
|
|
69
|
+
h('h3', { id: headingId, className: 'dim-credentialTitle' }, `手动接入${channel}机器人`),
|
|
70
|
+
h('form', {
|
|
71
|
+
className: `dim-credentialForm${hasIdentity ? '' : ' dim-credentialFormSingle'}`,
|
|
72
|
+
onSubmit: submit,
|
|
73
|
+
},
|
|
74
|
+
hasIdentity ? h('label', { className: 'dim-credentialField' },
|
|
75
|
+
h('span', null, identityLabel),
|
|
76
|
+
h('input', {
|
|
77
|
+
value: identity,
|
|
78
|
+
onChange: (event) => setIdentity(event.target.value),
|
|
79
|
+
placeholder: identityPlaceholder,
|
|
80
|
+
maxLength: 512,
|
|
81
|
+
autoCapitalize: 'none',
|
|
82
|
+
autoCorrect: 'off',
|
|
83
|
+
spellCheck: false,
|
|
84
|
+
autoComplete: 'off',
|
|
85
|
+
disabled: busy,
|
|
86
|
+
required: true,
|
|
87
|
+
})) : null,
|
|
88
|
+
h('label', { className: 'dim-credentialField' },
|
|
89
|
+
h('span', null, secretLabel),
|
|
90
|
+
h('input', {
|
|
91
|
+
type: 'password',
|
|
92
|
+
value: secret,
|
|
93
|
+
onChange: (event) => setSecret(event.target.value),
|
|
94
|
+
placeholder: secretPlaceholder,
|
|
95
|
+
maxLength: 1024,
|
|
96
|
+
autoCapitalize: 'none',
|
|
97
|
+
autoCorrect: 'off',
|
|
98
|
+
spellCheck: false,
|
|
99
|
+
autoComplete: 'new-password',
|
|
100
|
+
disabled: busy,
|
|
101
|
+
required: true,
|
|
102
|
+
})),
|
|
103
|
+
error ? h('p', { className: 'dim-credentialError', role: 'alert' }, error.message ?? String(error)) : null,
|
|
104
|
+
h('div', { className: 'ddt-actions dim-viewActions dim-credentialActions' },
|
|
105
|
+
h('button', {
|
|
106
|
+
type: 'submit',
|
|
107
|
+
className: 'ddt-button',
|
|
108
|
+
'data-kind': 'primary',
|
|
109
|
+
disabled: busy || (hasIdentity && !identity.trim()) || !secret.trim(),
|
|
110
|
+
}, busy ? '正在绑定…' : '绑定并连接'),
|
|
111
|
+
h('button', {
|
|
112
|
+
type: 'button',
|
|
113
|
+
className: 'ddt-button',
|
|
114
|
+
onClick: onCancel,
|
|
115
|
+
disabled: busy,
|
|
116
|
+
}, '取消'))));
|
|
117
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DingtalkLogoGlyph,
|
|
5
|
+
DiscordLogoGlyph,
|
|
6
|
+
FeishuLogoGlyph,
|
|
7
|
+
QqLogoGlyph,
|
|
8
|
+
TelegramLogoGlyph,
|
|
9
|
+
WecomLogoGlyph,
|
|
10
|
+
WeixinLogoGlyph,
|
|
11
|
+
} from './channel-logos.js';
|
|
12
|
+
import { DINGTALK_RPC_CHANNEL } from './channels/dingtalk/api.js';
|
|
13
|
+
import { DingtalkSettingsTab } from './channels/dingtalk/index.js';
|
|
14
|
+
import { DISCORD_RPC_CHANNEL } from './channels/discord/api.js';
|
|
15
|
+
import { DiscordSettingsTab } from './channels/discord/index.js';
|
|
16
|
+
import { installDiscordStyles } from './channels/discord/styles.js';
|
|
17
|
+
import { FeishuSettingsTab } from './channels/feishu/index.js';
|
|
18
|
+
import { FEISHU_RPC_CHANNEL } from './channels/feishu/api.js';
|
|
19
|
+
import { installFeishuStyles } from './channels/feishu/styles.js';
|
|
20
|
+
import { QQ_RPC_CHANNEL } from './channels/qq/api.js';
|
|
21
|
+
import { QqSettingsTab } from './channels/qq/index.js';
|
|
22
|
+
import { installQqStyles } from './channels/qq/styles.js';
|
|
23
|
+
import { TELEGRAM_RPC_CHANNEL } from './channels/telegram/api.js';
|
|
24
|
+
import { TelegramSettingsTab } from './channels/telegram/index.js';
|
|
25
|
+
import { installTelegramStyles } from './channels/telegram/styles.js';
|
|
26
|
+
import { WECOM_RPC_CHANNEL } from './channels/wecom/api.js';
|
|
27
|
+
import { WecomSettingsTab } from './channels/wecom/index.js';
|
|
28
|
+
import { installWecomStyles } from './channels/wecom/styles.js';
|
|
29
|
+
import { WeixinSettingsTab } from './channels/weixin/index.js';
|
|
30
|
+
import { WEIXIN_RPC_CHANNEL } from './channels/weixin/api.js';
|
|
31
|
+
import { installWeixinStyles } from './channels/weixin/styles.js';
|
|
32
|
+
import { installImStyles } from './styles.js';
|
|
33
|
+
|
|
34
|
+
const h = React.createElement;
|
|
35
|
+
|
|
36
|
+
export const name = 'im-settings';
|
|
37
|
+
export const inject = ['slots', 'connection'];
|
|
38
|
+
|
|
39
|
+
const CHANNELS = Object.freeze([
|
|
40
|
+
{ id: 'weixin', label: '微信' },
|
|
41
|
+
{ id: 'feishu', label: '飞书' },
|
|
42
|
+
{ id: 'dingtalk', label: '钉钉' },
|
|
43
|
+
{ id: 'wecom', label: '企业微信' },
|
|
44
|
+
{ id: 'qq', label: 'QQ' },
|
|
45
|
+
{ id: 'telegram', label: 'Telegram' },
|
|
46
|
+
{ id: 'discord', label: 'Discord' },
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
function WeixinLogo() {
|
|
50
|
+
return h('span', { className: 'dim-logo dim-logoWeixin', 'aria-hidden': 'true' },
|
|
51
|
+
h(WeixinLogoGlyph));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function FeishuLogo() {
|
|
55
|
+
return h('span', { className: 'dim-logo dim-logoFeishu', 'aria-hidden': 'true' },
|
|
56
|
+
h(FeishuLogoGlyph));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function DingtalkLogo() {
|
|
60
|
+
return h('span', { className: 'dim-logo dim-logoDingtalk', 'aria-hidden': 'true' },
|
|
61
|
+
h(DingtalkLogoGlyph));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function QqLogo() {
|
|
65
|
+
return h('span', { className: 'dim-logo dim-logoQq', 'aria-hidden': 'true' }, h(QqLogoGlyph));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function WecomLogo() {
|
|
69
|
+
return h('span', { className: 'dim-logo dim-logoWecom', 'aria-hidden': 'true' }, h(WecomLogoGlyph));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function TelegramLogo() {
|
|
73
|
+
return h('span', { className: 'dim-logo dim-logoTelegram', 'aria-hidden': 'true' },
|
|
74
|
+
h(TelegramLogoGlyph));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function DiscordLogo() {
|
|
78
|
+
return h('span', { className: 'dim-logo dim-logoDiscord', 'aria-hidden': 'true' },
|
|
79
|
+
h(DiscordLogoGlyph));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ChannelLogo({ channel }) {
|
|
83
|
+
if (channel === 'weixin') return h(WeixinLogo);
|
|
84
|
+
if (channel === 'feishu') return h(FeishuLogo);
|
|
85
|
+
if (channel === 'dingtalk') return h(DingtalkLogo);
|
|
86
|
+
if (channel === 'wecom') return h(WecomLogo);
|
|
87
|
+
if (channel === 'qq') return h(QqLogo);
|
|
88
|
+
if (channel === 'telegram') return h(TelegramLogo);
|
|
89
|
+
return h(DiscordLogo);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function IMSettingsTab({
|
|
93
|
+
dingtalkRpcCall,
|
|
94
|
+
discordRpcCall,
|
|
95
|
+
feishuRpcCall,
|
|
96
|
+
qqRpcCall,
|
|
97
|
+
telegramRpcCall,
|
|
98
|
+
wecomRpcCall,
|
|
99
|
+
weixinRpcCall,
|
|
100
|
+
}) {
|
|
101
|
+
const [selected, setSelected] = React.useState('weixin');
|
|
102
|
+
const active = CHANNELS.find((channel) => channel.id === selected) ?? CHANNELS[0];
|
|
103
|
+
return h('section', { className: 'dim-page', 'aria-label': 'IM机器人设置' },
|
|
104
|
+
h('header', { className: 'dim-title' },
|
|
105
|
+
h('p', null, '把机器人接入 DeepSeek Harness'),
|
|
106
|
+
),
|
|
107
|
+
h('div', { className: 'dim-layout' },
|
|
108
|
+
h('nav', { className: 'dim-rail', role: 'tablist', 'aria-label': 'IM 渠道' },
|
|
109
|
+
CHANNELS.map((channel) => h('button', {
|
|
110
|
+
key: channel.id,
|
|
111
|
+
type: 'button',
|
|
112
|
+
role: 'tab',
|
|
113
|
+
id: `dim-tab-${channel.id}`,
|
|
114
|
+
className: 'dim-channel',
|
|
115
|
+
'aria-selected': channel.id === active.id,
|
|
116
|
+
'aria-controls': `dim-panel-${channel.id}`,
|
|
117
|
+
onClick: () => setSelected(channel.id),
|
|
118
|
+
},
|
|
119
|
+
h(ChannelLogo, { channel: channel.id }),
|
|
120
|
+
h('span', { className: 'dim-channelCopy' },
|
|
121
|
+
h('strong', null, channel.label),
|
|
122
|
+
)))),
|
|
123
|
+
h('div', { className: 'dim-divider', 'aria-hidden': 'true' }),
|
|
124
|
+
h('main', {
|
|
125
|
+
className: 'dim-panel',
|
|
126
|
+
role: 'tabpanel',
|
|
127
|
+
id: `dim-panel-${active.id}`,
|
|
128
|
+
'aria-labelledby': `dim-tab-${active.id}`,
|
|
129
|
+
}, active.id === 'weixin'
|
|
130
|
+
? h(WeixinSettingsTab, { rpcCall: weixinRpcCall })
|
|
131
|
+
: active.id === 'feishu'
|
|
132
|
+
? h(FeishuSettingsTab, { rpcCall: feishuRpcCall })
|
|
133
|
+
: active.id === 'dingtalk'
|
|
134
|
+
? h(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall })
|
|
135
|
+
: active.id === 'wecom'
|
|
136
|
+
? h(WecomSettingsTab, { rpcCall: wecomRpcCall })
|
|
137
|
+
: active.id === 'qq'
|
|
138
|
+
? h(QqSettingsTab, { rpcCall: qqRpcCall })
|
|
139
|
+
: active.id === 'telegram'
|
|
140
|
+
? h(TelegramSettingsTab, { rpcCall: telegramRpcCall })
|
|
141
|
+
: h(DiscordSettingsTab, { rpcCall: discordRpcCall })),
|
|
142
|
+
),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function apply(ctx) {
|
|
147
|
+
ctx.effect(() => {
|
|
148
|
+
const disposers = [
|
|
149
|
+
installFeishuStyles(),
|
|
150
|
+
installWeixinStyles(),
|
|
151
|
+
installWecomStyles(),
|
|
152
|
+
installQqStyles(),
|
|
153
|
+
installTelegramStyles(),
|
|
154
|
+
installDiscordStyles(),
|
|
155
|
+
installImStyles(),
|
|
156
|
+
];
|
|
157
|
+
return () => {
|
|
158
|
+
for (const dispose of disposers.reverse()) dispose();
|
|
159
|
+
};
|
|
160
|
+
}, 'im-settings: install combined channel styles');
|
|
161
|
+
|
|
162
|
+
const feishuRpcCall = (endpoint, payload, signal) =>
|
|
163
|
+
ctx.connection.rpc.call(FEISHU_RPC_CHANNEL, endpoint, payload, signal);
|
|
164
|
+
const weixinRpcCall = (endpoint, payload, signal) =>
|
|
165
|
+
ctx.connection.rpc.call(WEIXIN_RPC_CHANNEL, endpoint, payload, signal);
|
|
166
|
+
const dingtalkRpcCall = (endpoint, payload, signal) =>
|
|
167
|
+
ctx.connection.rpc.call(DINGTALK_RPC_CHANNEL, endpoint, payload, signal);
|
|
168
|
+
const qqRpcCall = (endpoint, payload, signal) =>
|
|
169
|
+
ctx.connection.rpc.call(QQ_RPC_CHANNEL, endpoint, payload, signal);
|
|
170
|
+
const wecomRpcCall = (endpoint, payload, signal) =>
|
|
171
|
+
ctx.connection.rpc.call(WECOM_RPC_CHANNEL, endpoint, payload, signal);
|
|
172
|
+
const telegramRpcCall = (endpoint, payload, signal) =>
|
|
173
|
+
ctx.connection.rpc.call(TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
|
|
174
|
+
const discordRpcCall = (endpoint, payload, signal) =>
|
|
175
|
+
ctx.connection.rpc.call(DISCORD_RPC_CHANNEL, endpoint, payload, signal);
|
|
176
|
+
|
|
177
|
+
ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register({
|
|
178
|
+
name: 'settings.plugins.tab',
|
|
179
|
+
id: 'im',
|
|
180
|
+
order: 20,
|
|
181
|
+
label: 'IM机器人',
|
|
182
|
+
inject: () => ({
|
|
183
|
+
dingtalkRpcCall,
|
|
184
|
+
discordRpcCall,
|
|
185
|
+
feishuRpcCall,
|
|
186
|
+
qqRpcCall,
|
|
187
|
+
telegramRpcCall,
|
|
188
|
+
wecomRpcCall,
|
|
189
|
+
weixinRpcCall,
|
|
190
|
+
}),
|
|
191
|
+
}, IMSettingsTab));
|
|
192
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
|
|
4
|
+
let disposed = false;
|
|
5
|
+
let timer;
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
get disposed() {
|
|
9
|
+
return disposed;
|
|
10
|
+
},
|
|
11
|
+
schedule(callback, delayMs) {
|
|
12
|
+
if (disposed) return false;
|
|
13
|
+
if (timer !== undefined) clearTimeoutFn(timer);
|
|
14
|
+
timer = setTimeoutFn(() => {
|
|
15
|
+
timer = undefined;
|
|
16
|
+
if (!disposed) void callback();
|
|
17
|
+
}, delayMs);
|
|
18
|
+
return true;
|
|
19
|
+
},
|
|
20
|
+
dispose() {
|
|
21
|
+
if (disposed) return;
|
|
22
|
+
disposed = true;
|
|
23
|
+
if (timer !== undefined) clearTimeoutFn(timer);
|
|
24
|
+
timer = undefined;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
|
|
30
|
+
let disposed = false;
|
|
31
|
+
const frames = new Set();
|
|
32
|
+
const keyedFrames = new Map();
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
schedule(callback, key) {
|
|
36
|
+
if (disposed) return false;
|
|
37
|
+
const previous = key === undefined ? undefined : keyedFrames.get(key);
|
|
38
|
+
if (previous !== undefined) {
|
|
39
|
+
keyedFrames.delete(key);
|
|
40
|
+
frames.delete(previous);
|
|
41
|
+
cancelFrame(previous);
|
|
42
|
+
}
|
|
43
|
+
let frame;
|
|
44
|
+
let completed = false;
|
|
45
|
+
frame = requestFrame(() => {
|
|
46
|
+
completed = true;
|
|
47
|
+
if (frame !== undefined) frames.delete(frame);
|
|
48
|
+
if (key !== undefined && keyedFrames.get(key) === frame) keyedFrames.delete(key);
|
|
49
|
+
if (!disposed) callback();
|
|
50
|
+
});
|
|
51
|
+
if (!completed) {
|
|
52
|
+
frames.add(frame);
|
|
53
|
+
if (key !== undefined) keyedFrames.set(key, frame);
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
},
|
|
57
|
+
dispose() {
|
|
58
|
+
if (disposed) return;
|
|
59
|
+
disposed = true;
|
|
60
|
+
for (const frame of frames) cancelFrame(frame);
|
|
61
|
+
frames.clear();
|
|
62
|
+
keyedFrames.clear();
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function useAnimationFrameScheduler() {
|
|
68
|
+
const schedulerRef = React.useRef(null);
|
|
69
|
+
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
const scheduler = createAnimationFrameScheduler({
|
|
72
|
+
requestFrame: (callback) => window.requestAnimationFrame(callback),
|
|
73
|
+
cancelFrame: (frame) => window.cancelAnimationFrame(frame),
|
|
74
|
+
});
|
|
75
|
+
schedulerRef.current = scheduler;
|
|
76
|
+
return () => {
|
|
77
|
+
scheduler.dispose();
|
|
78
|
+
if (schedulerRef.current === scheduler) schedulerRef.current = null;
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return React.useCallback(
|
|
83
|
+
(callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
|
|
84
|
+
[],
|
|
85
|
+
);
|
|
86
|
+
}
|