@xmanrui/dsh-im 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/lib/index.js +176 -163
  4. package/package.json +1 -1
  5. package/plugin-src/host/channels/dingtalk/production.mjs +3 -1
  6. package/plugin-src/host/channels/feishu/production.mjs +3 -1
  7. package/plugin-src/host/channels/qq/production.mjs +3 -1
  8. package/plugin-src/host/channels/shared/production.mjs +3 -1
  9. package/plugin-src/host/channels/slack/production.mjs +3 -1
  10. package/plugin-src/host/channels/wecom/production.mjs +3 -1
  11. package/plugin-src/host/channels/weixin/production.mjs +3 -1
  12. package/plugin-src/host/channels/whatsapp/production.mjs +3 -1
  13. package/plugin-src/host/harness-session-coordinator.mjs +32 -5
  14. package/src/channels/dingtalk/dingtalk-api.mjs +93 -27
  15. package/src/channels/dingtalk/dingtalk-bridge.mjs +60 -13
  16. package/src/channels/discord/discord-runtime.mjs +23 -0
  17. package/src/channels/feishu/bridge.mjs +18 -10
  18. package/src/channels/feishu/message-utils.mjs +47 -0
  19. package/src/channels/qq/markdown-reply.mjs +176 -0
  20. package/src/channels/qq/qq-bridge.mjs +124 -55
  21. package/src/channels/shared/file-download.mjs +64 -0
  22. package/src/channels/shared/harness-client.mjs +111 -13
  23. package/src/channels/shared/inbound-file.mjs +206 -0
  24. package/src/channels/shared/text-harness-bridge.mjs +31 -11
  25. package/src/channels/slack/slack-api.mjs +27 -4
  26. package/src/channels/slack/slack-runtime.mjs +55 -5
  27. package/src/channels/telegram/telegram-api.mjs +21 -6
  28. package/src/channels/telegram/telegram-runtime.mjs +24 -3
  29. package/src/channels/wecom/wecom-bridge.mjs +73 -10
  30. package/src/channels/weixin/weixin-api.mjs +45 -0
  31. package/src/channels/weixin/weixin-bridge.mjs +52 -18
  32. package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -2
  33. package/src/channels/whatsapp/whatsapp-web-session.mjs +1 -1
@@ -170,6 +170,39 @@ function whatsappImageSource(message, content, download, { viewOnce = false } =
170
170
  };
171
171
  }
172
172
 
173
+ function whatsappFileSource(message, content, download) {
174
+ const media = content?.documentMessage;
175
+ if (!media) return null;
176
+ const mediaType = typeof media.mimetype === 'string' && media.mimetype
177
+ ? media.mimetype.toLowerCase() : undefined;
178
+ if (IMAGE_MEDIA_TYPES.has(mediaType)) return null;
179
+ return {
180
+ name: typeof media.fileName === 'string' && media.fileName
181
+ ? media.fileName : 'whatsapp-file',
182
+ ...(mediaType ? { mediaType } : {}),
183
+ size: mediaSize(media.fileLength),
184
+ async load({ signal } = {}) {
185
+ signal?.throwIfAborted();
186
+ const stream = await download(message, 'stream', { options: { signal } });
187
+ signal?.throwIfAborted();
188
+ return { stream };
189
+ },
190
+ };
191
+ }
192
+
193
+ export function createWhatsappMediaDownloader({
194
+ socket,
195
+ logger = console,
196
+ download = downloadMediaMessage,
197
+ } = {}) {
198
+ if (typeof download !== 'function') throw new TypeError('WhatsApp media downloader is required');
199
+ if (typeof socket?.updateMediaMessage !== 'function') return download;
200
+ return (message, type, options) => download(message, type, options, {
201
+ logger,
202
+ reuploadRequest: (candidate) => socket.updateMediaMessage(candidate),
203
+ });
204
+ }
205
+
173
206
  export function normalizeWhatsappMessage(message, accountJid, {
174
207
  download = downloadMediaMessage,
175
208
  } = {}) {
@@ -195,6 +228,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
195
228
  const replyToSelf = typeof context?.participant === 'string'
196
229
  && areJidsSameUser(context.participant, accountJid);
197
230
  const image = whatsappImageSource(message, content, download, { viewOnce });
231
+ const file = whatsappFileSource(message, content, download);
198
232
  return {
199
233
  messageId: `${remoteJid}:${messageId}`,
200
234
  providerMessageId: messageId,
@@ -205,6 +239,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
205
239
  conversationId: remoteJid,
206
240
  content: messageText(content),
207
241
  images: image ? [image] : [],
242
+ files: file ? [file] : [],
208
243
  addressed: !group || mentioned || replyToSelf,
209
244
  selfChat,
210
245
  replyTarget: { jid: remoteJid, quoted: message, selfChat },
@@ -495,8 +530,13 @@ export class WhatsappRuntime {
495
530
  new Error('WhatsApp linked-device session must be scanned again'),
496
531
  { code: 'relink-required' },
497
532
  )),
498
- onMessage: async (raw) => {
499
- const message = normalizeWhatsappMessage(raw, this.#config.accountJid);
533
+ onMessage: async (raw, context) => {
534
+ const message = normalizeWhatsappMessage(raw, this.#config.accountJid, {
535
+ download: createWhatsappMediaDownloader({
536
+ socket: context?.socket,
537
+ logger: this.#logger,
538
+ }),
539
+ });
500
540
  if (!message || outboundIds.has(message.providerMessageId) || !this.#bridge) return;
501
541
  this.#status.lastCheckedAt = Date.now();
502
542
  if (!whatsappInboundAllowed(message, {
@@ -190,7 +190,7 @@ export async function createWhatsappWebSession({
190
190
  const timestamp = messageTimestampMs(message?.messageTimestamp);
191
191
  if (timestamp === null || timestamp < sessionStartedAt - APPEND_RECENT_GRACE_MS) continue;
192
192
  }
193
- Promise.resolve(onMessage(message)).catch(() => {
193
+ Promise.resolve(onMessage(message, { socket: nextSocket })).catch(() => {
194
194
  logger.error?.('[dsh-im:whatsapp] failed to process an inbound WhatsApp message');
195
195
  });
196
196
  }