@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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/THIRD_PARTY_NOTICES.md +15 -0
  4. package/bin/dsh-im.mjs +99 -0
  5. package/cordis.patch.yml +3 -0
  6. package/lib/client.js +6211 -0
  7. package/lib/index.js +13339 -0
  8. package/package.json +73 -0
  9. package/plugin-src/client/build.mjs +38 -0
  10. package/plugin-src/client/channel-logos.js +107 -0
  11. package/plugin-src/client/channels/dingtalk/api.js +203 -0
  12. package/plugin-src/client/channels/dingtalk/index.js +709 -0
  13. package/plugin-src/client/channels/dingtalk/styles.js +140 -0
  14. package/plugin-src/client/channels/discord/api.js +11 -0
  15. package/plugin-src/client/channels/discord/index.js +25 -0
  16. package/plugin-src/client/channels/discord/styles.js +19 -0
  17. package/plugin-src/client/channels/feishu/api.js +325 -0
  18. package/plugin-src/client/channels/feishu/index.js +986 -0
  19. package/plugin-src/client/channels/feishu/styles.js +505 -0
  20. package/plugin-src/client/channels/qq/api.js +119 -0
  21. package/plugin-src/client/channels/qq/index.js +402 -0
  22. package/plugin-src/client/channels/qq/styles.js +19 -0
  23. package/plugin-src/client/channels/shared/token-api.js +88 -0
  24. package/plugin-src/client/channels/shared/token-channel.js +277 -0
  25. package/plugin-src/client/channels/telegram/api.js +11 -0
  26. package/plugin-src/client/channels/telegram/index.js +25 -0
  27. package/plugin-src/client/channels/telegram/styles.js +19 -0
  28. package/plugin-src/client/channels/wecom/api.js +119 -0
  29. package/plugin-src/client/channels/wecom/index.js +402 -0
  30. package/plugin-src/client/channels/wecom/styles.js +19 -0
  31. package/plugin-src/client/channels/weixin/api.js +156 -0
  32. package/plugin-src/client/channels/weixin/index.js +577 -0
  33. package/plugin-src/client/channels/weixin/styles.js +120 -0
  34. package/plugin-src/client/credential-binding.js +117 -0
  35. package/plugin-src/client/index.js +192 -0
  36. package/plugin-src/client/lifecycle.js +86 -0
  37. package/plugin-src/client/styles.js +192 -0
  38. package/plugin-src/host/build.mjs +24 -0
  39. package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
  40. package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
  41. package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
  42. package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
  43. package/plugin-src/host/channels/discord/index.mjs +30 -0
  44. package/plugin-src/host/channels/discord/production.mjs +17 -0
  45. package/plugin-src/host/channels/discord/rpc.mjs +21 -0
  46. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
  47. package/plugin-src/host/channels/feishu/controller.mjs +172 -0
  48. package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
  49. package/plugin-src/host/channels/feishu/index.mjs +73 -0
  50. package/plugin-src/host/channels/feishu/production.mjs +138 -0
  51. package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
  52. package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
  53. package/plugin-src/host/channels/qq/index.mjs +30 -0
  54. package/plugin-src/host/channels/qq/production.mjs +109 -0
  55. package/plugin-src/host/channels/qq/rpc.mjs +146 -0
  56. package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
  57. package/plugin-src/host/channels/shared/production.mjs +104 -0
  58. package/plugin-src/host/channels/shared/rpc.mjs +119 -0
  59. package/plugin-src/host/channels/telegram/index.mjs +30 -0
  60. package/plugin-src/host/channels/telegram/production.mjs +17 -0
  61. package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
  62. package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
  63. package/plugin-src/host/channels/wecom/index.mjs +36 -0
  64. package/plugin-src/host/channels/wecom/production.mjs +113 -0
  65. package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
  66. package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
  67. package/plugin-src/host/channels/weixin/index.mjs +39 -0
  68. package/plugin-src/host/channels/weixin/production.mjs +119 -0
  69. package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
  70. package/plugin-src/host/index.mjs +44 -0
  71. package/plugin-src/host/rpc-authority.mjs +11 -0
  72. package/scripts/verify-package.mjs +97 -0
  73. package/src/channels/dingtalk/config-store.mjs +282 -0
  74. package/src/channels/dingtalk/device-auth.mjs +237 -0
  75. package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
  76. package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
  77. package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
  78. package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
  79. package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
  80. package/src/channels/dingtalk/harness-client.mjs +299 -0
  81. package/src/channels/dingtalk/state-store.mjs +212 -0
  82. package/src/channels/discord/config-store.mjs +24 -0
  83. package/src/channels/discord/discord-api.mjs +153 -0
  84. package/src/channels/discord/discord-bridge.mjs +15 -0
  85. package/src/channels/discord/discord-controller.mjs +16 -0
  86. package/src/channels/discord/discord-runtime.mjs +460 -0
  87. package/src/channels/discord/harness-client.mjs +3 -0
  88. package/src/channels/discord/state-store.mjs +3 -0
  89. package/src/channels/feishu/bridge.mjs +216 -0
  90. package/src/channels/feishu/config.mjs +71 -0
  91. package/src/channels/feishu/feishu-app.mjs +51 -0
  92. package/src/channels/feishu/feishu-channel.mjs +153 -0
  93. package/src/channels/feishu/feishu-runtime.mjs +222 -0
  94. package/src/channels/feishu/harness-client.mjs +268 -0
  95. package/src/channels/feishu/message-utils.mjs +51 -0
  96. package/src/channels/feishu/multi-bot-controller.mjs +686 -0
  97. package/src/channels/feishu/plugin-config-store.mjs +204 -0
  98. package/src/channels/feishu/plugin-controller.mjs +248 -0
  99. package/src/channels/feishu/registration-manager.mjs +345 -0
  100. package/src/channels/feishu/state-store.mjs +71 -0
  101. package/src/channels/qq/config-store.mjs +171 -0
  102. package/src/channels/qq/harness-client.mjs +3 -0
  103. package/src/channels/qq/qq-bridge.mjs +177 -0
  104. package/src/channels/qq/qq-controller.mjs +452 -0
  105. package/src/channels/qq/qq-runtime.mjs +208 -0
  106. package/src/channels/qq/qr-auth.mjs +24 -0
  107. package/src/channels/qq/state-store.mjs +96 -0
  108. package/src/channels/shared/conversation-state-store.mjs +107 -0
  109. package/src/channels/shared/editable-message-stream.mjs +81 -0
  110. package/src/channels/shared/text-harness-bridge.mjs +180 -0
  111. package/src/channels/shared/token-bot-controller.mjs +298 -0
  112. package/src/channels/shared/token-config-store.mjs +180 -0
  113. package/src/channels/telegram/config-store.mjs +24 -0
  114. package/src/channels/telegram/harness-client.mjs +3 -0
  115. package/src/channels/telegram/state-store.mjs +3 -0
  116. package/src/channels/telegram/telegram-api.mjs +120 -0
  117. package/src/channels/telegram/telegram-bridge.mjs +15 -0
  118. package/src/channels/telegram/telegram-controller.mjs +16 -0
  119. package/src/channels/telegram/telegram-runtime.mjs +278 -0
  120. package/src/channels/wecom/config-store.mjs +170 -0
  121. package/src/channels/wecom/harness-client.mjs +3 -0
  122. package/src/channels/wecom/qr-auth.mjs +95 -0
  123. package/src/channels/wecom/state-store.mjs +90 -0
  124. package/src/channels/wecom/wecom-bridge.mjs +243 -0
  125. package/src/channels/wecom/wecom-controller.mjs +474 -0
  126. package/src/channels/wecom/wecom-runtime.mjs +209 -0
  127. package/src/channels/weixin/config-store.mjs +182 -0
  128. package/src/channels/weixin/harness-client.mjs +259 -0
  129. package/src/channels/weixin/state-store.mjs +112 -0
  130. package/src/channels/weixin/weixin-api.mjs +319 -0
  131. package/src/channels/weixin/weixin-bridge.mjs +173 -0
  132. package/src/channels/weixin/weixin-controller.mjs +545 -0
  133. package/src/channels/weixin/weixin-runtime.mjs +204 -0
@@ -0,0 +1,505 @@
1
+ export const FEISHU_STYLE_ID = "beihuixinghe-dsh-feishu-settings";
2
+
3
+ const CSS = String.raw`
4
+ .bxf-page {
5
+ --bxf-accent: var(--dsw-alias-state-business-primary, #3370ff);
6
+ --bxf-success: var(--dsw-alias-state-success-primary, #20a162);
7
+ --bxf-warning: var(--dsw-alias-state-warning-primary, #d97706);
8
+ --bxf-error: var(--dsw-alias-state-error-primary, #d54941);
9
+ box-sizing: border-box;
10
+ width: 100%;
11
+ max-width: 860px;
12
+ color: var(--dsw-alias-label-primary, #1f2329);
13
+ display: flex;
14
+ flex-direction: column;
15
+ container-type: inline-size;
16
+ gap: 18px;
17
+ padding: 2px 0 24px;
18
+ }
19
+
20
+ .bxf-page *, .bxf-page *::before, .bxf-page *::after { box-sizing: border-box; }
21
+
22
+ .bxf-heading {
23
+ display: flex;
24
+ align-items: flex-start;
25
+ justify-content: space-between;
26
+ gap: 20px;
27
+ }
28
+
29
+ .bxf-headingCopy { min-width: 0; }
30
+ .bxf-heading h2, .bxf-heading p, .bxf-card h3, .bxf-card p { margin: 0; }
31
+
32
+ .bxf-eyebrow {
33
+ color: var(--dsw-alias-label-tertiary, #8f959e);
34
+ font-size: 12px;
35
+ font-weight: 600;
36
+ line-height: 18px;
37
+ letter-spacing: .08em;
38
+ text-transform: uppercase;
39
+ margin-bottom: 3px;
40
+ }
41
+
42
+ .bxf-heading h2 {
43
+ font-size: 20px;
44
+ line-height: 28px;
45
+ font-weight: 650;
46
+ letter-spacing: -.015em;
47
+ }
48
+
49
+ .bxf-heading p {
50
+ max-width: 540px;
51
+ color: var(--dsw-alias-label-secondary, #646a73);
52
+ font-size: 13px;
53
+ line-height: 20px;
54
+ margin-top: 5px;
55
+ white-space: nowrap;
56
+ }
57
+
58
+ .bxf-headingTools {
59
+ width: 100%;
60
+ flex: none;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: space-between;
64
+ flex-wrap: nowrap;
65
+ gap: 8px;
66
+ }
67
+
68
+ .bxf-totalBadge {
69
+ min-height: 28px;
70
+ display: inline-flex;
71
+ align-items: baseline;
72
+ gap: 3px;
73
+ border-radius: 999px;
74
+ padding: 4px 10px;
75
+ color: var(--dsw-alias-label-secondary, #646a73);
76
+ background: var(--dsw-alias-bg-module-platform, #f2f3f5);
77
+ font-size: 11px;
78
+ line-height: 16px;
79
+ white-space: nowrap;
80
+ }
81
+
82
+ .bxf-totalBadge strong { color: var(--bxf-success); font-size: 13px; }
83
+
84
+ .bxf-card {
85
+ position: relative;
86
+ overflow: hidden;
87
+ border: 1px solid var(--dsw-alias-border-l2, #dee0e3);
88
+ border-radius: 14px;
89
+ background: var(--dsw-alias-bg-layer-3, #fff);
90
+ box-shadow: var(--dsw-shadow-lv1, 0 3px 12px rgba(31, 35, 41, .05));
91
+ }
92
+
93
+ .bxf-card::before {
94
+ content: "";
95
+ pointer-events: none;
96
+ position: absolute;
97
+ inset: 0 0 auto;
98
+ height: 88px;
99
+ background:
100
+ radial-gradient(circle at 86% -35%, color-mix(in srgb, var(--bxf-accent) 18%, transparent), transparent 68%);
101
+ opacity: .85;
102
+ }
103
+
104
+ .bxf-cardBody { position: relative; padding: 24px; }
105
+
106
+ .bxf-intro {
107
+ min-height: 250px;
108
+ display: grid;
109
+ grid-template-columns: minmax(0, 1fr) 172px;
110
+ gap: 32px;
111
+ align-items: center;
112
+ }
113
+
114
+ .bxf-introCopy { max-width: 500px; }
115
+
116
+ .bxf-stateLabel {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ gap: 7px;
120
+ color: var(--dsw-alias-label-secondary, #646a73);
121
+ font-size: 12px;
122
+ font-weight: 600;
123
+ line-height: 18px;
124
+ margin-bottom: 13px;
125
+ }
126
+
127
+ .bxf-dot {
128
+ width: 7px;
129
+ height: 7px;
130
+ border-radius: 50%;
131
+ background: var(--dsw-alias-label-tertiary, #8f959e);
132
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--dsw-alias-label-tertiary, #8f959e) 12%, transparent);
133
+ }
134
+
135
+ .bxf-dot[data-tone="success"] {
136
+ background: var(--bxf-success);
137
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--bxf-success) 13%, transparent);
138
+ }
139
+
140
+ .bxf-dot[data-tone="warning"] {
141
+ background: var(--bxf-warning);
142
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--bxf-warning) 13%, transparent);
143
+ }
144
+
145
+ .bxf-dot[data-tone="error"] {
146
+ background: var(--bxf-error);
147
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--bxf-error) 13%, transparent);
148
+ }
149
+
150
+ .bxf-intro h3 {
151
+ font-size: 24px;
152
+ line-height: 34px;
153
+ font-weight: 650;
154
+ letter-spacing: -.02em;
155
+ }
156
+
157
+ .bxf-introCopy > p {
158
+ max-width: 490px;
159
+ color: var(--dsw-alias-label-secondary, #646a73);
160
+ font-size: 14px;
161
+ line-height: 23px;
162
+ margin-top: 8px;
163
+ }
164
+
165
+ .bxf-note {
166
+ display: flex;
167
+ gap: 8px;
168
+ align-items: flex-start;
169
+ color: var(--dsw-alias-label-tertiary, #8f959e);
170
+ font-size: 12px;
171
+ line-height: 18px;
172
+ margin-top: 16px;
173
+ }
174
+
175
+ .bxf-note svg { flex: none; margin-top: 1px; }
176
+
177
+ .bxf-actions {
178
+ display: flex;
179
+ align-items: center;
180
+ flex-wrap: wrap;
181
+ gap: 10px;
182
+ margin-top: 22px;
183
+ }
184
+
185
+ .bxf-button {
186
+ appearance: none;
187
+ min-height: 36px;
188
+ display: inline-flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ gap: 7px;
192
+ border: 1px solid var(--dsw-alias-border-l2, #dee0e3);
193
+ border-radius: 8px;
194
+ padding: 7px 13px;
195
+ color: var(--dsw-alias-label-primary, #1f2329);
196
+ background: var(--dsw-alias-bg-layer-1, #fff);
197
+ font: inherit;
198
+ font-size: 13px;
199
+ font-weight: 550;
200
+ line-height: 20px;
201
+ text-decoration: none;
202
+ cursor: pointer;
203
+ transition: background .15s var(--ds-ease-in-out, ease), border-color .15s var(--ds-ease-in-out, ease), transform .15s var(--ds-ease-in-out, ease);
204
+ }
205
+
206
+ .bxf-button:hover:not(:disabled) {
207
+ background: var(--dsw-alias-interactive-bg-hover, #f2f3f5);
208
+ border-color: var(--dsw-alias-border-l1, #c9cdd4);
209
+ }
210
+
211
+ .bxf-button:active:not(:disabled) { transform: translateY(1px); }
212
+
213
+ .bxf-button:focus-visible, .bxf-link:focus-visible {
214
+ outline: 2px solid var(--bxf-accent);
215
+ outline-offset: 2px;
216
+ }
217
+
218
+ .bxf-button:disabled { cursor: not-allowed; opacity: .55; }
219
+
220
+ .bxf-button[data-kind="primary"] {
221
+ border-color: var(--bxf-accent);
222
+ color: #fff;
223
+ background: var(--bxf-accent);
224
+ box-shadow: 0 4px 12px color-mix(in srgb, var(--bxf-accent) 24%, transparent);
225
+ }
226
+
227
+ .bxf-button[data-kind="primary"]:hover:not(:disabled) {
228
+ border-color: color-mix(in srgb, var(--bxf-accent) 86%, #000);
229
+ background: color-mix(in srgb, var(--bxf-accent) 90%, #000);
230
+ }
231
+
232
+ .bxf-button[data-kind="danger"] { color: var(--bxf-error); }
233
+ .bxf-button[data-size="small"] { min-height: 32px; padding: 5px 10px; font-size: 12px; }
234
+ .bxf-bindButton { flex: none; white-space: nowrap; }
235
+
236
+ .bxf-provisionCard {
237
+ border-color: color-mix(in srgb, var(--bxf-accent) 32%, var(--dsw-alias-border-l2, #dee0e3));
238
+ }
239
+
240
+ .bxf-markStage {
241
+ position: relative;
242
+ width: 156px;
243
+ height: 156px;
244
+ display: grid;
245
+ place-items: center;
246
+ justify-self: end;
247
+ }
248
+
249
+ .bxf-markStage::before, .bxf-markStage::after {
250
+ content: "";
251
+ position: absolute;
252
+ border-radius: 50%;
253
+ }
254
+
255
+ .bxf-markStage::before {
256
+ inset: 12px;
257
+ border: 1px solid color-mix(in srgb, var(--bxf-accent) 18%, var(--dsw-alias-border-l2, #dee0e3));
258
+ background: color-mix(in srgb, var(--bxf-accent) 4%, var(--dsw-alias-bg-layer-1, #fff));
259
+ }
260
+
261
+ .bxf-markStage::after {
262
+ inset: 0;
263
+ border: 1px dashed color-mix(in srgb, var(--bxf-accent) 16%, transparent);
264
+ animation: bxf-rotate 18s linear infinite;
265
+ }
266
+
267
+ .bxf-brandMark {
268
+ position: relative;
269
+ z-index: 1;
270
+ width: 68px;
271
+ height: 68px;
272
+ display: grid;
273
+ place-items: center;
274
+ border-radius: 20px;
275
+ color: #fff;
276
+ background: var(--bxf-accent);
277
+ box-shadow: 0 12px 28px color-mix(in srgb, var(--bxf-accent) 28%, transparent);
278
+ }
279
+
280
+ .bxf-qrLayout {
281
+ display: grid;
282
+ grid-template-columns: 236px minmax(0, 1fr);
283
+ align-items: center;
284
+ gap: 32px;
285
+ }
286
+
287
+ .bxf-qrColumn { min-width: 0; }
288
+
289
+ .bxf-qrFrame {
290
+ position: relative;
291
+ width: 222px;
292
+ height: 222px;
293
+ display: grid;
294
+ place-items: center;
295
+ border: 1px solid var(--dsw-alias-border-l2, #dee0e3);
296
+ border-radius: 14px;
297
+ padding: 13px;
298
+ background: #fff;
299
+ box-shadow: 0 8px 24px rgba(31, 35, 41, .07);
300
+ }
301
+
302
+ .bxf-qrFrame::before, .bxf-qrFrame::after {
303
+ content: "";
304
+ position: absolute;
305
+ width: 24px;
306
+ height: 24px;
307
+ border-color: var(--bxf-accent);
308
+ border-style: solid;
309
+ }
310
+
311
+ .bxf-qrFrame::before { inset: -3px auto auto -3px; border-width: 2px 0 0 2px; border-radius: 5px 0 0; }
312
+ .bxf-qrFrame::after { inset: auto -3px -3px auto; border-width: 0 2px 2px 0; border-radius: 0 0 5px; }
313
+ .bxf-qrFrame img { width: 100%; height: 100%; display: block; object-fit: contain; }
314
+
315
+ .bxf-qrFallback {
316
+ width: 100%;
317
+ height: 100%;
318
+ display: grid;
319
+ place-items: center;
320
+ border-radius: 8px;
321
+ color: var(--bxf-accent);
322
+ background: #f7f9ff;
323
+ text-align: center;
324
+ padding: 20px;
325
+ }
326
+
327
+ .bxf-qrFallback span { display: block; color: #646a73; font-size: 12px; line-height: 18px; margin-top: 8px; }
328
+
329
+ .bxf-expiredOverlay {
330
+ position: absolute;
331
+ inset: 10px;
332
+ display: grid;
333
+ place-items: center;
334
+ border-radius: 9px;
335
+ color: #1f2329;
336
+ background: rgba(255, 255, 255, .94);
337
+ backdrop-filter: blur(3px);
338
+ font-size: 13px;
339
+ font-weight: 600;
340
+ text-align: center;
341
+ }
342
+
343
+ .bxf-countdown {
344
+ width: 222px;
345
+ color: var(--dsw-alias-label-tertiary, #8f959e);
346
+ font-variant-numeric: tabular-nums;
347
+ font-size: 11px;
348
+ line-height: 17px;
349
+ margin-top: 11px;
350
+ }
351
+
352
+ .bxf-countdownTop { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
353
+ .bxf-progress { height: 3px; overflow: hidden; border-radius: 99px; background: var(--dsw-alias-bg-module-platform, #f2f3f5); margin-top: 6px; }
354
+ .bxf-progress > span { display: block; width: var(--bxf-progress, 100%); height: 100%; border-radius: inherit; background: var(--bxf-accent); transition: width 1s linear; }
355
+
356
+ .bxf-qrCopy h3 { font-size: 20px; line-height: 29px; font-weight: 650; }
357
+ .bxf-qrCopy > p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 21px; margin-top: 7px; }
358
+
359
+ .bxf-steps { counter-reset: bxf-step; display: flex; flex-direction: column; gap: 11px; margin: 20px 0 0; padding: 0; list-style: none; }
360
+ .bxf-steps li { counter-increment: bxf-step; display: grid; grid-template-columns: 23px minmax(0, 1fr); align-items: start; gap: 9px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; }
361
+ .bxf-steps li::before { content: counter(bxf-step); width: 21px; height: 21px; display: grid; place-items: center; border: 1px solid var(--dsw-alias-border-l2, #dee0e3); border-radius: 50%; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 10px; font-weight: 650; }
362
+
363
+ .bxf-connecting { min-height: 292px; display: grid; place-items: center; text-align: center; padding: 36px 24px; }
364
+ .bxf-connectingCopy { max-width: 430px; }
365
+ .bxf-orbit { position: relative; width: 86px; height: 86px; display: grid; place-items: center; margin: 0 auto 22px; }
366
+ .bxf-orbit::before, .bxf-orbit::after { content: ""; position: absolute; border-radius: 50%; }
367
+ .bxf-orbit::before { inset: 3px; border: 1px solid color-mix(in srgb, var(--bxf-accent) 24%, transparent); animation: bxf-pulse 1.8s var(--ds-ease-in-out, ease) infinite; }
368
+ .bxf-orbit::after { inset: 0; border: 2px solid transparent; border-top-color: var(--bxf-accent); animation: bxf-rotate 1.2s linear infinite; }
369
+ .bxf-orbitCore { width: 50px; height: 50px; display: grid; place-items: center; border-radius: 16px; color: var(--bxf-accent); background: color-mix(in srgb, var(--bxf-accent) 9%, var(--dsw-alias-bg-layer-1, #fff)); }
370
+ .bxf-connecting h3 { font-size: 20px; line-height: 29px; }
371
+ .bxf-connecting p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 21px; margin-top: 7px; }
372
+ .bxf-connectingCompact { min-height: 248px; }
373
+
374
+ .bxf-inlineError {
375
+ min-height: 190px;
376
+ display: grid;
377
+ grid-template-columns: 44px minmax(0, 1fr);
378
+ align-content: center;
379
+ gap: 15px;
380
+ padding: 28px;
381
+ }
382
+
383
+ .bxf-inlineError h3 { font-size: 17px; line-height: 25px; margin: 0; }
384
+ .bxf-inlineError p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 21px; margin-top: 5px; overflow-wrap: anywhere; }
385
+
386
+ .bxf-listSection { display: flex; flex-direction: column; gap: 10px; }
387
+ .bxf-listHeading { min-height: 28px; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 0 2px; }
388
+ .bxf-listHeading h3 { font-size: 14px; line-height: 22px; font-weight: 650; margin: 0; }
389
+ .bxf-botList { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; list-style: none; }
390
+ .bxf-botList > li { min-width: 0; }
391
+ .bxf-botCard:focus { outline: none; }
392
+ .bxf-botCard:focus-visible { outline: 2px solid var(--bxf-accent); outline-offset: 2px; }
393
+
394
+ .bxf-connectedTop { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
395
+ .bxf-botIdentity { min-width: 0; display: flex; align-items: center; gap: 13px; }
396
+ .bxf-avatar { flex: none; width: 48px; height: 48px; display: grid; place-items: center; overflow: hidden; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 3px rgb(31 35 41 / 7%); }
397
+ .bxf-botName { min-width: 0; }
398
+ .bxf-botName h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 17px; line-height: 24px; font-weight: 650; }
399
+ .bxf-botName p { overflow: hidden; color: var(--dsw-alias-label-tertiary, #8f959e); font-family: var(--ds-font-family-code, monospace); font-size: 12px; line-height: 18px; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
400
+
401
+ .bxf-healthPill { flex: none; display: inline-flex; align-items: center; gap: 7px; min-height: 28px; border-radius: 999px; padding: 4px 10px; color: var(--bxf-success); background: color-mix(in srgb, var(--bxf-success) 10%, transparent); font-size: 12px; font-weight: 600; line-height: 18px; }
402
+ .bxf-healthPill[data-health="degraded"], .bxf-healthPill[data-health="checking"], .bxf-healthPill[data-health="connecting"] { color: var(--bxf-warning); background: color-mix(in srgb, var(--bxf-warning) 10%, transparent); }
403
+ .bxf-healthPill[data-health="offline"], .bxf-healthPill[data-health="error"] { color: var(--bxf-error); background: color-mix(in srgb, var(--bxf-error) 10%, transparent); }
404
+
405
+ .bxf-statusGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 22px; }
406
+ .bxf-metric { min-width: 0; border: 1px solid var(--dsw-alias-border-l2, #dee0e3); border-radius: 9px; padding: 12px 13px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
407
+ .bxf-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
408
+ .bxf-metric dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 550; margin: 3px 0 0; }
409
+
410
+ .bxf-connectedFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-line-divider, #eef0f3); }
411
+ .bxf-healthSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
412
+ .bxf-healthSummary[data-error="true"] { color: var(--bxf-error); }
413
+ .bxf-botActions { flex: none; flex-wrap: nowrap; gap: 8px; margin-top: 0; justify-content: flex-end; }
414
+ .bxf-botActions .bxf-button { flex: none; white-space: nowrap; }
415
+
416
+ .bxf-confirm {
417
+ border-top: 1px solid var(--dsw-alias-border-l2, #dee0e3);
418
+ background: color-mix(in srgb, var(--bxf-error) 4%, var(--dsw-alias-bg-module-platform, #f7f8fa));
419
+ padding: 17px 24px 20px;
420
+ }
421
+ .bxf-confirm:focus { outline: none; }
422
+ .bxf-confirm h4 { font-size: 13px; line-height: 20px; margin: 0; }
423
+ .bxf-confirm p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; margin: 4px 0 0; }
424
+ .bxf-confirm .bxf-actions { margin-top: 12px; }
425
+
426
+ .bxf-error { min-height: 252px; display: grid; grid-template-columns: 44px minmax(0, 1fr); align-content: center; gap: 15px; padding: 30px; }
427
+ .bxf-errorIcon { width: 44px; height: 44px; display: grid; place-items: center; border-radius: 13px; color: var(--bxf-error); background: color-mix(in srgb, var(--bxf-error) 9%, transparent); }
428
+ .bxf-error h3 { font-size: 17px; line-height: 25px; }
429
+ .bxf-error p { color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 21px; margin-top: 5px; overflow-wrap: anywhere; }
430
+ .bxf-errorCode { display: inline-block; color: var(--dsw-alias-label-tertiary, #8f959e); font-family: var(--ds-font-family-code, monospace); font-size: 11px; margin-top: 7px; }
431
+
432
+ .bxf-statusNotice {
433
+ display: flex;
434
+ align-items: center;
435
+ gap: 9px;
436
+ border: 1px solid color-mix(in srgb, var(--bxf-warning) 28%, var(--dsw-alias-border-l2, #dee0e3));
437
+ border-radius: 10px;
438
+ padding: 9px 11px;
439
+ color: var(--dsw-alias-label-secondary, #646a73);
440
+ background: color-mix(in srgb, var(--bxf-warning) 5%, var(--dsw-alias-bg-layer-1, #fff));
441
+ font-size: 12px;
442
+ line-height: 18px;
443
+ }
444
+ .bxf-statusNotice > svg { flex: none; color: var(--bxf-warning); }
445
+ .bxf-statusNotice > span { min-width: 0; flex: 1; overflow-wrap: anywhere; }
446
+
447
+ .bxf-skeleton { min-height: 260px; padding: 28px; }
448
+ .bxf-skeletonLine { height: 12px; border-radius: 999px; background: linear-gradient(90deg, var(--dsw-alias-bg-module-platform, #f2f3f5), color-mix(in srgb, var(--dsw-alias-label-tertiary, #8f959e) 10%, transparent), var(--dsw-alias-bg-module-platform, #f2f3f5)); background-size: 220% 100%; animation: bxf-shimmer 1.5s linear infinite; }
449
+ .bxf-skeletonLine:nth-child(1) { width: 92px; }
450
+ .bxf-skeletonLine:nth-child(2) { width: 44%; height: 22px; margin-top: 23px; }
451
+ .bxf-skeletonLine:nth-child(3) { width: 72%; margin-top: 14px; }
452
+ .bxf-skeletonLine:nth-child(4) { width: 58%; margin-top: 9px; }
453
+ .bxf-skeletonBox { width: 138px; height: 38px; border-radius: 8px; background: var(--dsw-alias-bg-module-platform, #f2f3f5); margin-top: 28px; }
454
+
455
+ .bxf-visuallyHidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
456
+
457
+ @keyframes bxf-rotate { to { transform: rotate(360deg); } }
458
+ @keyframes bxf-pulse { 0%, 100% { transform: scale(.9); opacity: .45; } 50% { transform: scale(1.08); opacity: 1; } }
459
+ @keyframes bxf-shimmer { to { background-position: -220% 0; } }
460
+
461
+ @container (max-width: 620px) {
462
+ .bxf-headingTools { gap: 6px; }
463
+ .bxf-headingTools .bxf-totalBadge { padding-inline: 8px; }
464
+ .bxf-headingTools .bxf-bindButton { padding-inline: 10px; }
465
+ }
466
+
467
+ @media (max-width: 680px) {
468
+ .bxf-intro { grid-template-columns: minmax(0, 1fr); }
469
+ .bxf-markStage { display: none; }
470
+ .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
471
+ .bxf-qrCopy { width: 100%; }
472
+ .bxf-statusGrid { grid-template-columns: minmax(0, 1fr); }
473
+ .bxf-connectedTop { align-items: flex-start; flex-direction: column; }
474
+ .bxf-inlineError { grid-template-columns: minmax(0, 1fr); padding: 20px; }
475
+ .bxf-statusNotice { align-items: flex-start; flex-wrap: wrap; }
476
+ .bxf-cardBody { padding: 20px; }
477
+ }
478
+
479
+ @media (prefers-reduced-motion: reduce) {
480
+ .bxf-page *, .bxf-page *::before, .bxf-page *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: .01ms !important; }
481
+ }
482
+ `;
483
+
484
+ export function installFeishuStyles() {
485
+ if (typeof document === "undefined") {
486
+ return () => {};
487
+ }
488
+
489
+ const existing = document.querySelector(
490
+ `style[data-plugin-css="${FEISHU_STYLE_ID}"]`,
491
+ );
492
+ if (existing) {
493
+ return () => {};
494
+ }
495
+
496
+ const style = document.createElement("style");
497
+ style.dataset.plugin = "@xmanrui/dsh-feishu";
498
+ style.dataset.pluginCss = FEISHU_STYLE_ID;
499
+ style.textContent = CSS;
500
+ document.head.appendChild(style);
501
+
502
+ return () => {
503
+ style.remove();
504
+ };
505
+ }
@@ -0,0 +1,119 @@
1
+ export const QQ_RPC_CHANNEL = '/qq';
2
+
3
+ export const QQ_ENDPOINTS = Object.freeze({
4
+ status: 'connection.status',
5
+ beginProvisioning: 'provision.begin',
6
+ pollProvisioning: 'provision.poll',
7
+ cancelProvisioning: 'provision.cancel',
8
+ bindCredentials: 'bot.bind-credentials',
9
+ reconnectBot: 'bot.reconnect',
10
+ deleteBot: 'bot.delete',
11
+ });
12
+
13
+ const PROVISION_STATES = new Set(['starting', 'pending', 'refreshing', 'connecting', 'connected', 'failed', 'cancelled']);
14
+ const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
15
+ const QR_DATA_URL = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
16
+
17
+ function isRecord(value) {
18
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
19
+ }
20
+
21
+ function text(value, fallback, max = 240) {
22
+ return typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : fallback;
23
+ }
24
+
25
+ function id(value) {
26
+ const result = text(value, '', 128);
27
+ return /^[a-z\d_-]+$/i.test(result) ? result : undefined;
28
+ }
29
+
30
+ function timestamp(value) {
31
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
32
+ const parsed = typeof value === 'string' ? Date.parse(value) : Number.NaN;
33
+ return Number.isNaN(parsed) ? undefined : parsed;
34
+ }
35
+
36
+ export function unwrapRpcResult(result) {
37
+ if (!isRecord(result) || typeof result.ok !== 'boolean') throw new Error('QQ 服务返回了无法识别的响应');
38
+ if (!result.ok) {
39
+ const error = new Error(text(result.error?.message, 'QQ 操作失败'));
40
+ error.code = text(result.error?.code, 'QQ_RPC_ERROR', 80);
41
+ throw error;
42
+ }
43
+ return result.value;
44
+ }
45
+
46
+ export function safeQrSource(value) {
47
+ return typeof value === 'string' && value.length <= 2 * 1024 * 1024 && QR_DATA_URL.test(value)
48
+ ? value : undefined;
49
+ }
50
+
51
+ export function normalizeProvisioning(value, now = Date.now()) {
52
+ const source = isRecord(value?.provisioning) ? value.provisioning : value;
53
+ if (!isRecord(source)) throw new Error('QQ 服务没有返回扫码绑定进度');
54
+ const attemptId = id(source.attemptId);
55
+ if (!attemptId) throw new Error('QQ 扫码服务没有返回有效的绑定任务');
56
+ const reported = text(source.status, 'failed', 32);
57
+ const result = {
58
+ attemptId,
59
+ status: PROVISION_STATES.has(reported) ? reported : 'failed',
60
+ expiresAt: timestamp(source.expiresAt) ?? now + 5 * 60_000,
61
+ pollIntervalMs: Math.min(10_000, Math.max(500, Number(source.pollIntervalMs) || 1_000)),
62
+ qrRevision: Number.isSafeInteger(source.qrRevision) ? source.qrRevision : 0,
63
+ };
64
+ const qrCodeDataUrl = safeQrSource(source.qrCodeDataUrl);
65
+ if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
66
+ if (id(source.botId)) result.botId = id(source.botId);
67
+ if (isRecord(source.error)) result.error = {
68
+ code: text(source.error.code, 'QQ_PROVISION_FAILED', 80),
69
+ message: text(source.error.message, 'QQ 机器人没有接入完成'),
70
+ };
71
+ return result;
72
+ }
73
+
74
+ function normalizeBot(value) {
75
+ if (!isRecord(value) || !id(value.botId)) return undefined;
76
+ const connected = value.connected === true;
77
+ const state = ACCOUNT_STATES.has(value.state) ? value.state : 'offline';
78
+ return {
79
+ botId: id(value.botId),
80
+ connected,
81
+ state: connected ? 'connected' : state,
82
+ bot: {
83
+ name: text(value.bot?.name, 'QQ机器人', 100),
84
+ appIdMasked: text(value.bot?.appIdMasked, '应用标识已安全保存', 140),
85
+ },
86
+ health: {
87
+ summary: text(value.health?.summary, connected ? 'QQ WebSocket 长连接运行正常' : 'QQ 连接尚未就绪'),
88
+ lastCheckedAt: timestamp(value.health?.lastCheckedAt),
89
+ },
90
+ error: isRecord(value.error) ? {
91
+ code: text(value.error.code, 'QQ_ACCOUNT_ERROR', 80),
92
+ message: text(value.error.message, 'QQ 连接尚未就绪'),
93
+ } : null,
94
+ };
95
+ }
96
+
97
+ export function normalizeSnapshot(value) {
98
+ const source = isRecord(value?.snapshot) ? value.snapshot : value;
99
+ if (!isRecord(source) || !Array.isArray(source.bots)) throw new Error('QQ 服务没有返回有效的机器人列表');
100
+ const bots = source.bots.map(normalizeBot).filter(Boolean);
101
+ return {
102
+ revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
103
+ bots,
104
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
105
+ provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
106
+ };
107
+ }
108
+
109
+ export function presentError(error) {
110
+ return {
111
+ code: text(error?.code, 'QQ_ERROR', 80),
112
+ message: text(error?.message, 'QQ 操作失败,请稍后重试'),
113
+ };
114
+ }
115
+
116
+ export function formatRemaining(milliseconds) {
117
+ const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1_000) || 0);
118
+ return `${String(Math.floor(seconds / 60)).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`;
119
+ }