@xmanrui/dsh-im 4.4.0 → 4.6.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 -2
- package/README.md +2 -2
- package/lib/client.js +29 -9
- package/lib/index.js +252 -241
- package/package.json +1 -1
- package/plugin-src/client/context-enhancement.js +19 -5
- package/plugin-src/client/i18n.js +5 -1
- package/plugin-src/host/modern-harness-api.mjs +3 -0
- package/src/channels/dingtalk/dingtalk-bridge.mjs +175 -23
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +9 -2
- package/src/channels/dingtalk/state-store.mjs +98 -0
- package/src/channels/discord/discord-api.mjs +7 -0
- package/src/channels/discord/discord-runtime.mjs +97 -2
- package/src/channels/feishu/bridge.mjs +30 -7
- package/src/channels/feishu/feishu-cards.mjs +2 -2
- package/src/channels/feishu/feishu-channel.mjs +36 -6
- package/src/channels/feishu/message-utils.mjs +229 -0
- package/src/channels/qq/qq-bridge.mjs +56 -9
- package/src/channels/shared/batch-input.mjs +3 -3
- package/src/channels/shared/bot-workspace-store.mjs +5 -0
- package/src/channels/shared/context-enhancement.mjs +9 -4
- package/src/channels/shared/harness-client.mjs +88 -30
- package/src/channels/shared/i18n-en/feishu.mjs +3 -3
- package/src/channels/shared/i18n-en/shared-a.mjs +2 -1
- package/src/channels/shared/i18n-en/shared-b.mjs +6 -3
- package/src/channels/shared/i18n-en/shared-c.mjs +6 -4
- package/src/channels/shared/image-prompt.mjs +51 -0
- package/src/channels/shared/semantic/reply-reference.mjs +153 -0
- package/src/channels/shared/session-reply-recovery.mjs +104 -0
- package/src/channels/shared/session-title.mjs +74 -0
- package/src/channels/shared/text-harness-bridge.mjs +19 -8
- package/src/channels/shared/workspace-command.mjs +16 -4
- package/src/channels/shared/workspace-session.mjs +28 -2
- package/src/channels/slack/manifest.mjs +3 -0
- package/src/channels/slack/slack-api.mjs +18 -0
- package/src/channels/slack/slack-runtime.mjs +56 -0
- package/src/channels/telegram/telegram-runtime.mjs +118 -2
- package/src/channels/wecom/wecom-bridge.mjs +55 -9
- package/src/channels/weixin/state-store.mjs +110 -0
- package/src/channels/weixin/weixin-api.mjs +86 -2
- package/src/channels/weixin/weixin-bridge.mjs +104 -12
- package/src/channels/weixin/weixin-runtime.mjs +26 -6
- package/src/channels/whatsapp/whatsapp-runtime.mjs +56 -0
|
@@ -2,9 +2,11 @@ import {
|
|
|
2
2
|
DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
|
|
3
3
|
extractWeixinFiles,
|
|
4
4
|
extractWeixinImages,
|
|
5
|
+
extractWeixinReplyReference,
|
|
5
6
|
extractWeixinText,
|
|
6
7
|
splitWeixinText,
|
|
7
8
|
weixinMessageId,
|
|
9
|
+
weixinMessageTimestampMs,
|
|
8
10
|
} from './weixin-api.mjs';
|
|
9
11
|
import {
|
|
10
12
|
harnessAnswerForQuestion,
|
|
@@ -38,7 +40,6 @@ import {
|
|
|
38
40
|
hasInboundImages,
|
|
39
41
|
imagePromptDiagnostic,
|
|
40
42
|
imagePromptUserMessage,
|
|
41
|
-
promptContentForMessage,
|
|
42
43
|
} from '../shared/image-prompt.mjs';
|
|
43
44
|
import {
|
|
44
45
|
hasInboundFiles,
|
|
@@ -47,10 +48,15 @@ import {
|
|
|
47
48
|
} from '../shared/inbound-file.mjs';
|
|
48
49
|
import { rememberConnectionTestTarget } from '../shared/connection-test.mjs';
|
|
49
50
|
import { deliverOutboundArtifacts } from '../shared/semantic/artifact-delivery.mjs';
|
|
51
|
+
import {
|
|
52
|
+
hasReplyReference,
|
|
53
|
+
promptContentForInboundMessage,
|
|
54
|
+
} from '../shared/semantic/reply-reference.mjs';
|
|
50
55
|
import {
|
|
51
56
|
createDeliveryReceipt,
|
|
52
57
|
providerMessageIdsFor,
|
|
53
58
|
} from '../shared/semantic/delivery.mjs';
|
|
59
|
+
import { recoverAssistantTextByTimestamp } from '../shared/session-reply-recovery.mjs';
|
|
54
60
|
import {
|
|
55
61
|
channelDeliveryFailure,
|
|
56
62
|
clearLastMessageFailure,
|
|
@@ -67,6 +73,10 @@ const INTERACTION_RESOLVED_TEXT = () => t('这个问题已在其他客户端处
|
|
|
67
73
|
const DEFAULT_TYPING_KEEPALIVE_MS = 5_000;
|
|
68
74
|
const TYPING_RETRY_DELAY_MS = 60_000;
|
|
69
75
|
const WEIXIN_SEND_DIAGNOSTIC = Symbol('weixin-send-diagnostic');
|
|
76
|
+
const WEIXIN_REPLY_HISTORY_PAGE_SIZE = 100;
|
|
77
|
+
const WEIXIN_REPLY_HISTORY_MAX_PAGES = 3;
|
|
78
|
+
const WEIXIN_REPLY_HISTORY_TIMEOUT_MS = 5_000;
|
|
79
|
+
const WEIXIN_REPLY_HISTORY_MATCH_TOLERANCE_MS = 15_000;
|
|
70
80
|
|
|
71
81
|
const HELP_TEXT = () => [
|
|
72
82
|
t('微信已连接 DeepSeek Harness。'),
|
|
@@ -75,7 +85,7 @@ const HELP_TEXT = () => [
|
|
|
75
85
|
t('/new 开启一个全新会话'),
|
|
76
86
|
t('/compact 压缩当前会话的较早上下文'),
|
|
77
87
|
t('/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)'),
|
|
78
|
-
t('/workspace
|
|
88
|
+
t('/workspace 工作区序号或绝对路径 切换工作区'),
|
|
79
89
|
t('/workspacelist 列出工作区绝对路径'),
|
|
80
90
|
t('/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
|
81
91
|
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
|
@@ -182,7 +192,20 @@ function weixinSendFailureOptions(error) {
|
|
|
182
192
|
return undefined;
|
|
183
193
|
}
|
|
184
194
|
|
|
185
|
-
export function weixinInboundMessage(message, api) {
|
|
195
|
+
export function weixinInboundMessage(message, api, state, { loadReplyContent } = {}) {
|
|
196
|
+
const toUserId = nonEmptyString(message?.from_user_id);
|
|
197
|
+
const replyTo = extractWeixinReplyReference(message, {
|
|
198
|
+
resolveContent: (reference) => state?.recentOutboundTextFor?.({
|
|
199
|
+
toUserId,
|
|
200
|
+
...reference,
|
|
201
|
+
}),
|
|
202
|
+
...(typeof loadReplyContent === 'function' ? {
|
|
203
|
+
loadContent: (reference, options) => loadReplyContent({
|
|
204
|
+
toUserId,
|
|
205
|
+
...reference,
|
|
206
|
+
}, options),
|
|
207
|
+
} : {}),
|
|
208
|
+
});
|
|
186
209
|
return {
|
|
187
210
|
content: extractWeixinText(message) ?? '',
|
|
188
211
|
images: typeof api?.inboundImages === 'function'
|
|
@@ -191,6 +214,7 @@ export function weixinInboundMessage(message, api) {
|
|
|
191
214
|
files: typeof api?.inboundFiles === 'function'
|
|
192
215
|
? api.inboundFiles(message)
|
|
193
216
|
: extractWeixinFiles(message),
|
|
217
|
+
...(replyTo ? { replyTo } : {}),
|
|
194
218
|
};
|
|
195
219
|
}
|
|
196
220
|
|
|
@@ -375,7 +399,8 @@ export class WeixinHarnessBridge {
|
|
|
375
399
|
&& (this.#queues.has(key) || pending || this.#approvals.hasPending(key))
|
|
376
400
|
? { handled: true, kind: 'busy', message: batchInputBusyMessage() }
|
|
377
401
|
: this.#batchInputs.handle(key, commandText, {
|
|
378
|
-
plainText: isNativeWeixinText(message)
|
|
402
|
+
plainText: isNativeWeixinText(message)
|
|
403
|
+
&& !extractWeixinReplyReference(message),
|
|
379
404
|
});
|
|
380
405
|
if (result.handled) {
|
|
381
406
|
if (result.kind === 'submit') {
|
|
@@ -489,7 +514,7 @@ export class WeixinHarnessBridge {
|
|
|
489
514
|
batchSubmission = null,
|
|
490
515
|
} = {}) {
|
|
491
516
|
const preparedMessage = prefetchInboundFiles(
|
|
492
|
-
|
|
517
|
+
this.#inboundMessage(message),
|
|
493
518
|
{ signal: this.#signal },
|
|
494
519
|
);
|
|
495
520
|
const previous = this.#queues.get(key) ?? Promise.resolve();
|
|
@@ -508,6 +533,54 @@ export class WeixinHarnessBridge {
|
|
|
508
533
|
return current;
|
|
509
534
|
}
|
|
510
535
|
|
|
536
|
+
#inboundMessage(message) {
|
|
537
|
+
return weixinInboundMessage(message, this.#api, this.#state, {
|
|
538
|
+
loadReplyContent: (reference, options) => this.#loadReplyContent(reference, options),
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async #loadReplyContent(reference, { signal: callerSignal } = {}) {
|
|
543
|
+
const indexed = this.#state.recentOutboundTextFor?.(reference);
|
|
544
|
+
if (indexed) return { content: indexed };
|
|
545
|
+
const quotedAt = [
|
|
546
|
+
weixinMessageTimestampMs(reference?.messageId),
|
|
547
|
+
Number(reference?.createTimeMs),
|
|
548
|
+
Number(reference?.updateTimeMs),
|
|
549
|
+
].find(Number.isSafeInteger);
|
|
550
|
+
if (quotedAt === undefined) return { unavailableReason: 'not-delivered' };
|
|
551
|
+
const sender = nonEmptyString(reference?.toUserId);
|
|
552
|
+
const sessionId = sender ? this.#state.sessionFor(conversationKey(sender)) : null;
|
|
553
|
+
const session = typeof sessionId === 'string' && sessionId
|
|
554
|
+
? this.#harness.workspaceSession?.(sessionId)
|
|
555
|
+
: null;
|
|
556
|
+
if (typeof session?.readHistory !== 'function') {
|
|
557
|
+
return { unavailableReason: 'not-delivered' };
|
|
558
|
+
}
|
|
559
|
+
const text = await recoverAssistantTextByTimestamp({
|
|
560
|
+
session,
|
|
561
|
+
quotedAt,
|
|
562
|
+
signal: callerSignal,
|
|
563
|
+
pageSize: WEIXIN_REPLY_HISTORY_PAGE_SIZE,
|
|
564
|
+
maxPages: WEIXIN_REPLY_HISTORY_MAX_PAGES,
|
|
565
|
+
timeoutMs: WEIXIN_REPLY_HISTORY_TIMEOUT_MS,
|
|
566
|
+
toleranceMs: WEIXIN_REPLY_HISTORY_MATCH_TOLERANCE_MS,
|
|
567
|
+
});
|
|
568
|
+
if (!text) return { unavailableReason: 'not-delivered' };
|
|
569
|
+
const messageId = nonEmptyString(reference?.messageId);
|
|
570
|
+
try {
|
|
571
|
+
await this.#state.rememberOutboundMessage?.({
|
|
572
|
+
toUserId: sender,
|
|
573
|
+
text,
|
|
574
|
+
sentAt: quotedAt,
|
|
575
|
+
completedAt: quotedAt,
|
|
576
|
+
providerMessageIds: messageId ? [messageId] : [],
|
|
577
|
+
});
|
|
578
|
+
} catch (error) {
|
|
579
|
+
this.#logger.warn?.('[dsh-weixin] failed to remember a recovered quote:', error);
|
|
580
|
+
}
|
|
581
|
+
return { content: text };
|
|
582
|
+
}
|
|
583
|
+
|
|
511
584
|
#finishBatchResult(message, messageId, key, sender, contextToken, runId, result) {
|
|
512
585
|
let task;
|
|
513
586
|
task = Promise.resolve().then(async () => {
|
|
@@ -644,11 +717,12 @@ export class WeixinHarnessBridge {
|
|
|
644
717
|
let batchSettled = batchSubmission === null;
|
|
645
718
|
let promptRecorded = false;
|
|
646
719
|
try {
|
|
647
|
-
const promptMessage = preparedMessage ??
|
|
720
|
+
const promptMessage = preparedMessage ?? this.#inboundMessage(message);
|
|
648
721
|
const text = promptMessage.content;
|
|
649
722
|
const hasImages = hasInboundImages(promptMessage);
|
|
650
723
|
const hasFiles = hasInboundFiles(promptMessage);
|
|
651
|
-
|
|
724
|
+
const hasReply = hasReplyReference(promptMessage);
|
|
725
|
+
if (!text && !hasImages && !hasFiles && !hasReply) {
|
|
652
726
|
await this.#send(sender, t('目前支持文字、图片、文件,以及微信已转成文字的语音消息。'), contextToken, runId);
|
|
653
727
|
await this.#state.markSeen(messageId);
|
|
654
728
|
return;
|
|
@@ -701,15 +775,18 @@ export class WeixinHarnessBridge {
|
|
|
701
775
|
let artifacts = [];
|
|
702
776
|
await this.#startTyping(sender, contextToken);
|
|
703
777
|
try {
|
|
704
|
-
let content = hasImages
|
|
705
|
-
? await
|
|
778
|
+
let content = hasImages || hasReply
|
|
779
|
+
? await promptContentForInboundMessage(promptMessage, { signal: this.#signal })
|
|
706
780
|
: undefined;
|
|
707
781
|
const snapshot = this.#acceptedMessageIds.get(messageId);
|
|
782
|
+
let contextEnhanced = false;
|
|
708
783
|
if (snapshot) {
|
|
709
|
-
|
|
784
|
+
const originalContent = content ?? text;
|
|
785
|
+
content = enhanceContextContent(originalContent, snapshot, () => ({
|
|
710
786
|
channel: 'weixin',
|
|
711
787
|
senderId: sender,
|
|
712
788
|
}));
|
|
789
|
+
contextEnhanced = content !== originalContent;
|
|
713
790
|
}
|
|
714
791
|
await this.#state.markSeen(messageId);
|
|
715
792
|
promptRecorded = true;
|
|
@@ -717,7 +794,9 @@ export class WeixinHarnessBridge {
|
|
|
717
794
|
harness: this.#harness,
|
|
718
795
|
state: this.#state,
|
|
719
796
|
key,
|
|
720
|
-
|
|
797
|
+
text,
|
|
798
|
+
content,
|
|
799
|
+
contextEnhanced,
|
|
721
800
|
createOptions: { signal: this.#signal },
|
|
722
801
|
existsOptions: { signal: this.#signal },
|
|
723
802
|
askOptions: {
|
|
@@ -1149,6 +1228,7 @@ export class WeixinHarnessBridge {
|
|
|
1149
1228
|
const chunks = splitWeixinText(text, this.#maxMessageChars);
|
|
1150
1229
|
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex += 1) {
|
|
1151
1230
|
const chunk = chunks[chunkIndex];
|
|
1231
|
+
const sentAt = Date.now();
|
|
1152
1232
|
try {
|
|
1153
1233
|
const result = await this.#api.sendText({
|
|
1154
1234
|
baseUrl: this.#baseUrl,
|
|
@@ -1159,7 +1239,19 @@ export class WeixinHarnessBridge {
|
|
|
1159
1239
|
runId,
|
|
1160
1240
|
signal: this.#signal,
|
|
1161
1241
|
});
|
|
1162
|
-
|
|
1242
|
+
const chunkMessageIds = providerMessageIdsFor(result);
|
|
1243
|
+
providerMessageIds.push(...chunkMessageIds);
|
|
1244
|
+
try {
|
|
1245
|
+
await this.#state.rememberOutboundMessage?.({
|
|
1246
|
+
toUserId,
|
|
1247
|
+
text: chunk,
|
|
1248
|
+
sentAt,
|
|
1249
|
+
completedAt: Date.now(),
|
|
1250
|
+
providerMessageIds: chunkMessageIds,
|
|
1251
|
+
});
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
this.#logger.warn?.('[dsh-weixin] failed to remember an outbound message:', error);
|
|
1254
|
+
}
|
|
1163
1255
|
} catch (error) {
|
|
1164
1256
|
throw weixinSendError(error, {
|
|
1165
1257
|
baseUrl: this.#baseUrl,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_WEIXIN_MAX_MESSAGE_CHARS, WeixinApiError } from './weixin-api.mjs';
|
|
2
2
|
import { createWeixinBridgeStatus, WeixinHarnessBridge } from './weixin-bridge.mjs';
|
|
3
|
+
import { providerMessageIdsFor } from '../shared/semantic/delivery.mjs';
|
|
3
4
|
import {
|
|
4
5
|
connectionTestTarget,
|
|
5
6
|
connectionTestTargetUnavailable,
|
|
@@ -324,9 +325,7 @@ export class WeixinRuntime {
|
|
|
324
325
|
if (!this.#status.ready || !this.#abortController) {
|
|
325
326
|
throw new Error('Weixin runtime is not connected');
|
|
326
327
|
}
|
|
327
|
-
await this.#
|
|
328
|
-
baseUrl: this.#config.baseUrl,
|
|
329
|
-
token: this.#token,
|
|
328
|
+
await this.#sendTrackedText({
|
|
330
329
|
toUserId,
|
|
331
330
|
text,
|
|
332
331
|
signal: this.#abortController.signal,
|
|
@@ -334,6 +333,29 @@ export class WeixinRuntime {
|
|
|
334
333
|
return { sent: true };
|
|
335
334
|
}
|
|
336
335
|
|
|
336
|
+
async #sendTrackedText({ toUserId, text, signal }) {
|
|
337
|
+
const sentAt = Date.now();
|
|
338
|
+
const result = await this.#api.sendText({
|
|
339
|
+
baseUrl: this.#config.baseUrl,
|
|
340
|
+
token: this.#token,
|
|
341
|
+
toUserId,
|
|
342
|
+
text,
|
|
343
|
+
signal,
|
|
344
|
+
});
|
|
345
|
+
try {
|
|
346
|
+
await this.#state.rememberOutboundMessage?.({
|
|
347
|
+
toUserId,
|
|
348
|
+
text,
|
|
349
|
+
sentAt,
|
|
350
|
+
completedAt: Date.now(),
|
|
351
|
+
providerMessageIds: providerMessageIdsFor(result),
|
|
352
|
+
});
|
|
353
|
+
} catch (error) {
|
|
354
|
+
this.#logger.warn?.('[dsh-weixin] failed to remember an outbound message:', error);
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
337
359
|
async sendProactiveText(target, text, { signal } = {}) {
|
|
338
360
|
const toUserId = typeof target?.route?.toUserId === 'string'
|
|
339
361
|
? target.route.toUserId.trim() : '';
|
|
@@ -348,9 +370,7 @@ export class WeixinRuntime {
|
|
|
348
370
|
throw error;
|
|
349
371
|
}
|
|
350
372
|
signal?.throwIfAborted();
|
|
351
|
-
await this.#
|
|
352
|
-
baseUrl: this.#config.baseUrl,
|
|
353
|
-
token: this.#token,
|
|
373
|
+
await this.#sendTrackedText({
|
|
354
374
|
toUserId,
|
|
355
375
|
text,
|
|
356
376
|
signal: signal ?? this.#abortController.signal,
|
|
@@ -96,6 +96,60 @@ function messageText(content) {
|
|
|
96
96
|
?? '';
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function whatsappReplyAttachment(kind, media, fallbackName) {
|
|
100
|
+
if (!media || typeof media !== 'object') return null;
|
|
101
|
+
const name = typeof media.fileName === 'string' && media.fileName
|
|
102
|
+
? media.fileName : typeof fallbackName === 'string' && fallbackName
|
|
103
|
+
? fallbackName : undefined;
|
|
104
|
+
return { kind, ...(name ? { name } : {}) };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function whatsappReplyAttachments(content) {
|
|
108
|
+
const attachments = [];
|
|
109
|
+
if (content?.imageMessage) {
|
|
110
|
+
attachments.push(whatsappReplyAttachment('image', content.imageMessage));
|
|
111
|
+
}
|
|
112
|
+
if (content?.documentMessage) {
|
|
113
|
+
const mediaType = typeof content.documentMessage.mimetype === 'string'
|
|
114
|
+
? content.documentMessage.mimetype.toLowerCase() : '';
|
|
115
|
+
attachments.push(whatsappReplyAttachment(
|
|
116
|
+
mediaType.startsWith('image/') ? 'image' : 'file',
|
|
117
|
+
content.documentMessage,
|
|
118
|
+
));
|
|
119
|
+
}
|
|
120
|
+
if (content?.audioMessage) {
|
|
121
|
+
attachments.push(whatsappReplyAttachment('audio', content.audioMessage));
|
|
122
|
+
}
|
|
123
|
+
if (content?.videoMessage) {
|
|
124
|
+
attachments.push(whatsappReplyAttachment('video', content.videoMessage));
|
|
125
|
+
}
|
|
126
|
+
if (content?.stickerMessage) {
|
|
127
|
+
const mediaType = typeof content.stickerMessage.mimetype === 'string'
|
|
128
|
+
? content.stickerMessage.mimetype.toLowerCase() : '';
|
|
129
|
+
attachments.push(whatsappReplyAttachment(
|
|
130
|
+
mediaType.startsWith('video/') || content.stickerMessage.isAnimated === true
|
|
131
|
+
? 'video' : 'image',
|
|
132
|
+
content.stickerMessage,
|
|
133
|
+
));
|
|
134
|
+
}
|
|
135
|
+
return attachments.filter(Boolean);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function whatsappReplyReference(context) {
|
|
139
|
+
if (!context?.quotedMessage || typeof context.quotedMessage !== 'object') return undefined;
|
|
140
|
+
const content = normalizeMessageContent(context.quotedMessage);
|
|
141
|
+
const messageId = typeof context.stanzaId === 'string' && context.stanzaId
|
|
142
|
+
? context.stanzaId : undefined;
|
|
143
|
+
const authorId = typeof context.participant === 'string' && context.participant
|
|
144
|
+
? context.participant : undefined;
|
|
145
|
+
return {
|
|
146
|
+
...(messageId ? { messageId } : {}),
|
|
147
|
+
...(authorId ? { authorId } : {}),
|
|
148
|
+
content: messageText(content),
|
|
149
|
+
attachments: whatsappReplyAttachments(content),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
99
153
|
function mediaSize(value) {
|
|
100
154
|
if (Number.isSafeInteger(value) && value >= 0) return value;
|
|
101
155
|
let converted;
|
|
@@ -260,6 +314,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
260
314
|
&& areJidsSameUser(context.participant, accountJid);
|
|
261
315
|
const image = whatsappImageSource(message, content, download, { viewOnce });
|
|
262
316
|
const file = whatsappFileSource(message, content, download);
|
|
317
|
+
const replyTo = whatsappReplyReference(context);
|
|
263
318
|
return {
|
|
264
319
|
messageId: `${remoteJid}:${messageId}`,
|
|
265
320
|
providerMessageId: messageId,
|
|
@@ -274,6 +329,7 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
274
329
|
|| typeof content?.extendedTextMessage?.text === 'string',
|
|
275
330
|
images: image ? [image] : [],
|
|
276
331
|
files: file ? [file] : [],
|
|
332
|
+
...(replyTo ? { replyTo } : {}),
|
|
277
333
|
addressed: !group || fromMe || mentioned || replyToSelf,
|
|
278
334
|
selfChat,
|
|
279
335
|
replyTarget: { jid: remoteJid, quoted: message, selfChat },
|