@oxidezap/baileyrs 0.2.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Compatibility/signal-repository.d.ts +1 -1
- package/lib/Compatibility/signal-repository.js +61 -57
- package/lib/Compatibility/stanza-responses.d.ts +3 -3
- package/lib/Compatibility/stanza-responses.js +16 -13
- package/lib/Socket/blocking.js +11 -9
- package/lib/Socket/bridge-error-boundary.d.ts +2 -2
- package/lib/Socket/bridge-error-boundary.js +2 -2
- package/lib/Socket/business.js +63 -61
- package/lib/Socket/chat-actions.js +102 -98
- package/lib/Socket/client-operations.d.ts +6 -0
- package/lib/Socket/client-operations.js +2 -0
- package/lib/Socket/communities.js +8 -8
- package/lib/Socket/contacts.js +29 -26
- package/lib/Socket/events.js +1 -1
- package/lib/Socket/groups.js +20 -52
- package/lib/Socket/index.d.ts +2 -2
- package/lib/Socket/index.js +68 -90
- package/lib/Socket/internals.js +1 -1
- package/lib/Socket/messages.js +224 -218
- package/lib/Socket/newsletter.js +137 -133
- package/lib/Socket/prekeys.d.ts +1 -1
- package/lib/Socket/prekeys.js +20 -18
- package/lib/Socket/presence.js +15 -13
- package/lib/Socket/privacy.js +10 -10
- package/lib/Socket/profile.js +20 -18
- package/lib/Socket/server-queries.js +4 -4
- package/lib/Socket/types.d.ts +2 -6
- package/package.json +4 -4
package/lib/Socket/messages.js
CHANGED
|
@@ -28,229 +28,235 @@ const getNormalizedUserJid = (ctx) => {
|
|
|
28
28
|
normalizedUserJids.set(ctx, { userId, jid });
|
|
29
29
|
return jid;
|
|
30
30
|
};
|
|
31
|
-
export const makeMessageMethods = (ctx) =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
31
|
+
export const makeMessageMethods = (ctx) => {
|
|
32
|
+
return {
|
|
33
|
+
sendMessage: async (jid, content, options) => {
|
|
34
|
+
return ctx.withClient(async (client) => {
|
|
35
|
+
const userJid = getNormalizedUserJid(ctx) ?? '';
|
|
36
|
+
// SocketConfig contains transport/auth/cache state that message
|
|
37
|
+
// generation neither reads nor owns. Copying all of it here retained a
|
|
38
|
+
// large object graph and repeated dozens of property writes per send.
|
|
39
|
+
// `options` is the one generation default shared with the socket; every
|
|
40
|
+
// other generation knob belongs to this call's typed options.
|
|
41
|
+
const generationOptions = {
|
|
42
|
+
...options,
|
|
43
|
+
options: options?.options ?? ctx.fullConfig.options,
|
|
44
|
+
logger: ctx.logger,
|
|
45
|
+
userJid,
|
|
46
|
+
waClient: client
|
|
47
|
+
};
|
|
48
|
+
const fullMsg = await generateWAMessage(jid, content, generationOptions);
|
|
49
|
+
const msg = normalizeMessageContent(fullMsg.message);
|
|
50
|
+
if (!msg)
|
|
51
|
+
throw new Boom('Failed to generate message content', { statusCode: 400 });
|
|
52
|
+
const contentType = getContentType(msg);
|
|
53
|
+
if (contentType === 'protocolMessage') {
|
|
54
|
+
const protoMsg = msg.protocolMessage;
|
|
55
|
+
if (protoMsg?.type === WAProto.Message.ProtocolMessage.Type.REVOKE && protoMsg?.key?.id) {
|
|
56
|
+
if (options?.messageId) {
|
|
57
|
+
// The core has no revoke variant that takes a stanza id, so
|
|
58
|
+
// the option cannot be honoured here. Saying so beats the
|
|
59
|
+
// silent drop this option used to get on every path.
|
|
60
|
+
ctx.logger.warn({ jid, messageId: options.messageId }, 'messageId option cannot be honoured for a revoke: the core assigns the stanza id itself');
|
|
61
|
+
}
|
|
62
|
+
// Group revokes need the original sender's participant JID —
|
|
63
|
+
// without it the server rejects the revoke. The bridge
|
|
64
|
+
// signature is `revokeMessage(jid, message_id, participant?)`.
|
|
65
|
+
// Caller passes participant via `message.delete.participant`
|
|
66
|
+
// which lands on `protoMsg.key.participant` in the generated
|
|
67
|
+
// proto.
|
|
68
|
+
await client.revokeMessage(jid, protoMsg.key.id, protoMsg.key.participant ?? null);
|
|
69
|
+
return fullMsg;
|
|
70
|
+
}
|
|
71
|
+
if (protoMsg?.type === WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT &&
|
|
72
|
+
protoMsg?.key?.id &&
|
|
73
|
+
protoMsg?.editedMessage) {
|
|
74
|
+
const editBytes = WAProto.Message.encode(protoMsg.editedMessage).finish();
|
|
75
|
+
// Edit-path counterpart of `options.messageId` on a plain send, and
|
|
76
|
+
// deliberately not `resolveMessageId`: the engine treats any supplied
|
|
77
|
+
// stanza id as borrowed and binds no id-keyed state to it, so an edit
|
|
78
|
+
// nobody asked to pin would silently lose its retry-cache entry and
|
|
79
|
+
// outbound secret. Absent stays absent. `||` and not `??` because an
|
|
80
|
+
// empty id means unspecified everywhere else in this API.
|
|
81
|
+
const editStanzaId = options?.messageId || undefined;
|
|
82
|
+
const newMsgId = await client.editMessageBytes(jid, protoMsg.key.id, editBytes, editStanzaId);
|
|
83
|
+
fullMsg.key.id = newMsgId || fullMsg.key.id;
|
|
84
|
+
return fullMsg;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const msgBytes = encodeProtoCompat('Message', msg);
|
|
88
|
+
// The with-options sends are the same core send as the plain ones, with
|
|
89
|
+
// the stanza id supplied by the caller instead of drawn by the engine.
|
|
90
|
+
// The id resolves through the same place `relayMessage` resolves it.
|
|
91
|
+
const messageId = resolveMessageId(ctx.getUser(), options?.messageId);
|
|
92
|
+
const msgId = await sendReportingUpstreamFailure(() => jid === 'status@broadcast' && options?.statusJidList?.length
|
|
93
|
+
? client.sendStatusMessageBytesWithOptions(msgBytes, options.statusJidList, messageId, EMPTY_RELAY_NODES, false)
|
|
94
|
+
: client.relayMessageBytesWithOptions(jid, msgBytes, messageId, EMPTY_RELAY_NODES, false, false));
|
|
95
|
+
fullMsg.key.id = msgId || fullMsg.key.id;
|
|
96
|
+
// Local echo of the message we just sent. Suppressed when
|
|
97
|
+
// `emitOwnEvents=false` so callers that explicitly opted out of seeing
|
|
98
|
+
// their own outbound messages don't get them looped back. This is the
|
|
99
|
+
// upstream-Baileys semantics of `emitOwnEvents` — it controls THIS
|
|
100
|
+
// echo, not inbound `fromMe` messages from other linked devices.
|
|
101
|
+
if (ctx.fullConfig.emitOwnEvents !== false) {
|
|
102
|
+
ctx.ev.emit('messages.upsert', {
|
|
103
|
+
messages: [fullMsg],
|
|
104
|
+
type: 'append'
|
|
105
|
+
});
|
|
60
106
|
}
|
|
61
|
-
// Group revokes need the original sender's participant JID —
|
|
62
|
-
// without it the server rejects the revoke. The bridge
|
|
63
|
-
// signature is `revokeMessage(jid, message_id, participant?)`.
|
|
64
|
-
// Caller passes participant via `message.delete.participant`
|
|
65
|
-
// which lands on `protoMsg.key.participant` in the generated
|
|
66
|
-
// proto.
|
|
67
|
-
await client.revokeMessage(jid, protoMsg.key.id, protoMsg.key.participant ?? null);
|
|
68
|
-
return fullMsg;
|
|
69
|
-
}
|
|
70
|
-
if (protoMsg?.type === WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT &&
|
|
71
|
-
protoMsg?.key?.id &&
|
|
72
|
-
protoMsg?.editedMessage) {
|
|
73
|
-
const editBytes = WAProto.Message.encode(protoMsg.editedMessage).finish();
|
|
74
|
-
// Edit-path counterpart of `options.messageId` on a plain send, and
|
|
75
|
-
// deliberately not `resolveMessageId`: the engine treats any supplied
|
|
76
|
-
// stanza id as borrowed and binds no id-keyed state to it, so an edit
|
|
77
|
-
// nobody asked to pin would silently lose its retry-cache entry and
|
|
78
|
-
// outbound secret. Absent stays absent. `||` and not `??` because an
|
|
79
|
-
// empty id means unspecified everywhere else in this API.
|
|
80
|
-
const editStanzaId = options?.messageId || undefined;
|
|
81
|
-
const newMsgId = await client.editMessageBytes(jid, protoMsg.key.id, editBytes, editStanzaId);
|
|
82
|
-
fullMsg.key.id = newMsgId || fullMsg.key.id;
|
|
83
107
|
return fullMsg;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const msgBytes = encodeProtoCompat('Message', msg);
|
|
87
|
-
// The with-options sends are the same core send as the plain ones, with
|
|
88
|
-
// the stanza id supplied by the caller instead of drawn by the engine.
|
|
89
|
-
// The id resolves through the same place `relayMessage` resolves it.
|
|
90
|
-
const messageId = resolveMessageId(ctx.getUser(), options?.messageId);
|
|
91
|
-
const msgId = await sendReportingUpstreamFailure(() => jid === 'status@broadcast' && options?.statusJidList?.length
|
|
92
|
-
? client.sendStatusMessageBytesWithOptions(msgBytes, options.statusJidList, messageId, EMPTY_RELAY_NODES, false)
|
|
93
|
-
: client.relayMessageBytesWithOptions(jid, msgBytes, messageId, EMPTY_RELAY_NODES, false, false));
|
|
94
|
-
fullMsg.key.id = msgId || fullMsg.key.id;
|
|
95
|
-
// Local echo of the message we just sent. Suppressed when
|
|
96
|
-
// `emitOwnEvents=false` so callers that explicitly opted out of seeing
|
|
97
|
-
// their own outbound messages don't get them looped back. This is the
|
|
98
|
-
// upstream-Baileys semantics of `emitOwnEvents` — it controls THIS
|
|
99
|
-
// echo, not inbound `fromMe` messages from other linked devices.
|
|
100
|
-
if (ctx.fullConfig.emitOwnEvents !== false) {
|
|
101
|
-
ctx.ev.emit('messages.upsert', {
|
|
102
|
-
messages: [fullMsg],
|
|
103
|
-
type: 'append'
|
|
104
108
|
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
},
|
|
110
|
+
updateMediaMessage: async (message) => {
|
|
111
|
+
return ctx.withClient(async (client) => {
|
|
112
|
+
const content = normalizeMessageContent(message.message);
|
|
113
|
+
const mediaContent = getMediaContent(content);
|
|
114
|
+
if (!mediaContent) {
|
|
115
|
+
throw new Boom('Not a media message', { statusCode: 400 });
|
|
116
|
+
}
|
|
117
|
+
const mediaKey = mediaContent.mediaKey;
|
|
118
|
+
if (!mediaKey) {
|
|
119
|
+
throw new Boom('Message has no media key', { statusCode: 400 });
|
|
120
|
+
}
|
|
121
|
+
const key = message.key;
|
|
122
|
+
const newDirectPath = await client.requestMediaReupload(key.id, key.remoteJid, mediaKey instanceof Uint8Array ? mediaKey : new Uint8Array(mediaKey), !!key.fromMe, key.participant ?? null);
|
|
123
|
+
// Update the message with the new direct path
|
|
124
|
+
// (download uses directPath via Rust bridge, url is informational)
|
|
125
|
+
mediaContent.directPath = newDirectPath;
|
|
126
|
+
ctx.logger.debug({ directPath: newDirectPath, msgId: key.id }, 'media reupload successful');
|
|
127
|
+
ctx.ev.emit('messages.update', [
|
|
128
|
+
{
|
|
129
|
+
key: message.key,
|
|
130
|
+
update: { message: message.message }
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
return message;
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* Low-level message relay — sends a raw proto.IMessage with full control
|
|
138
|
+
* over the message ID. Use `sendMessage` for the high-level API that handles
|
|
139
|
+
* media upload, message generation, link previews, etc.
|
|
140
|
+
*
|
|
141
|
+
* Returns the message ID used on the wire.
|
|
142
|
+
*
|
|
143
|
+
* @param jid Recipient JID
|
|
144
|
+
* @param message Raw protobuf Message (snake_case keys)
|
|
145
|
+
* @param options Relay options
|
|
146
|
+
*/
|
|
147
|
+
relayMessage: async (jid, message, options) => {
|
|
148
|
+
return ctx.withClient(async (client) => {
|
|
149
|
+
const plan = planMessageRelay(jid, options, ctx.getUser());
|
|
150
|
+
ctx.logger.debug({
|
|
151
|
+
jid,
|
|
152
|
+
kind: plan.kind,
|
|
153
|
+
messageId: plan.messageId,
|
|
154
|
+
extraNodes: plan.kind === 'retransmission' ? 0 : plan.nodes.length
|
|
155
|
+
}, 'relayMessage compatibility plan');
|
|
156
|
+
// The message goes to the bridge as the caller built it: the core settles
|
|
157
|
+
// messageSecret / reportingTokenVersion itself, reusing a caller-set secret
|
|
158
|
+
// rather than replacing it, so nothing here has to be dropped.
|
|
159
|
+
const bytes = encodeProtoCompat('Message', message);
|
|
160
|
+
if (plan.kind === 'retransmission') {
|
|
161
|
+
await client.retransmitMessageBytes(jid, bytes, plan.input);
|
|
162
|
+
return plan.messageId;
|
|
163
|
+
}
|
|
164
|
+
// Both sends go through the same retry: a caller node the engine derives
|
|
165
|
+
// is refused the same way whether the message is bound for a chat or for
|
|
166
|
+
// status, and the escape hatch — a node it does not derive — survives
|
|
167
|
+
// either way.
|
|
168
|
+
const drop = (tag) => ctx.logger.debug({ jid, messageId: plan.messageId, tag }, 'dropped an additionalNodes entry the engine derives from the message');
|
|
169
|
+
if (plan.kind === 'status') {
|
|
170
|
+
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.sendStatusMessageBytesWithOptions(bytes, plan.recipients, plan.messageId, nodes, plan.refreshDevices), drop));
|
|
171
|
+
}
|
|
172
|
+
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.relayMessageBytesWithOptions(jid, bytes, plan.messageId, nodes, plan.refreshGroupMetadata, plan.refreshDevices), drop));
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
readMessages: async (keys) => {
|
|
176
|
+
const receiptKeys = receiptMessageKeys(keys);
|
|
177
|
+
if (receiptKeys.length)
|
|
178
|
+
await ctx.withClient(client => client.readMessages(receiptKeys));
|
|
179
|
+
},
|
|
180
|
+
/**
|
|
181
|
+
* Send a receipt for messages. The bridge handles most receipt types automatically
|
|
182
|
+
* (delivered, sender). Use `readMessages` for the common case of sending read receipts.
|
|
183
|
+
*
|
|
184
|
+
* Supported types via bridge: 'read', 'read-self', 'played'
|
|
185
|
+
* Auto-handled by bridge: 'sender', 'inactive', undefined (delivered)
|
|
186
|
+
* Not supported: 'hist_sync', 'peer_msg' (logged as warning)
|
|
187
|
+
*/
|
|
188
|
+
sendReceipt: async (jid, participant, messageIds, type) => {
|
|
189
|
+
// Ahead of the empty-list exit: the types this method handles elsewhere
|
|
190
|
+
// resolve without sending anything, so a typo looked like a no-op.
|
|
191
|
+
assertArgumentDomain('sendReceipt', 'type', type, MESSAGE_RECEIPT_TYPES);
|
|
192
|
+
if (!messageIds.length)
|
|
193
|
+
return;
|
|
194
|
+
if (type === 'read' || type === 'read-self') {
|
|
195
|
+
const keys = messageIds.map(id => ({
|
|
196
|
+
remoteJid: jid,
|
|
197
|
+
id,
|
|
198
|
+
...(participant ? { participant } : {})
|
|
199
|
+
}));
|
|
200
|
+
await ctx.withClient(client => client.readMessages(keys));
|
|
129
201
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* @param message Raw protobuf Message (snake_case keys)
|
|
142
|
-
* @param options Relay options
|
|
143
|
-
*/
|
|
144
|
-
relayMessage: async (jid, message, options) => {
|
|
145
|
-
const client = await ctx.getClient();
|
|
146
|
-
const plan = planMessageRelay(jid, options, ctx.getUser());
|
|
147
|
-
ctx.logger.debug({
|
|
148
|
-
jid,
|
|
149
|
-
kind: plan.kind,
|
|
150
|
-
messageId: plan.messageId,
|
|
151
|
-
extraNodes: plan.kind === 'retransmission' ? 0 : plan.nodes.length
|
|
152
|
-
}, 'relayMessage compatibility plan');
|
|
153
|
-
// The message goes to the bridge as the caller built it: the core settles
|
|
154
|
-
// messageSecret / reportingTokenVersion itself, reusing a caller-set secret
|
|
155
|
-
// rather than replacing it, so nothing here has to be dropped.
|
|
156
|
-
const bytes = encodeProtoCompat('Message', message);
|
|
157
|
-
if (plan.kind === 'retransmission') {
|
|
158
|
-
await client.retransmitMessageBytes(jid, bytes, plan.input);
|
|
159
|
-
return plan.messageId;
|
|
160
|
-
}
|
|
161
|
-
// Both sends go through the same retry: a caller node the engine derives
|
|
162
|
-
// is refused the same way whether the message is bound for a chat or for
|
|
163
|
-
// status, and the escape hatch — a node it does not derive — survives
|
|
164
|
-
// either way.
|
|
165
|
-
const drop = (tag) => ctx.logger.debug({ jid, messageId: plan.messageId, tag }, 'dropped an additionalNodes entry the engine derives from the message');
|
|
166
|
-
if (plan.kind === 'status') {
|
|
167
|
-
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.sendStatusMessageBytesWithOptions(bytes, plan.recipients, plan.messageId, nodes, plan.refreshDevices), drop));
|
|
168
|
-
}
|
|
169
|
-
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.relayMessageBytesWithOptions(jid, bytes, plan.messageId, nodes, plan.refreshGroupMetadata, plan.refreshDevices), drop));
|
|
170
|
-
},
|
|
171
|
-
readMessages: async (keys) => {
|
|
172
|
-
const receiptKeys = receiptMessageKeys(keys);
|
|
173
|
-
if (receiptKeys.length)
|
|
174
|
-
await (await ctx.getClient()).readMessages(receiptKeys);
|
|
175
|
-
},
|
|
176
|
-
/**
|
|
177
|
-
* Send a receipt for messages. The bridge handles most receipt types automatically
|
|
178
|
-
* (delivered, sender). Use `readMessages` for the common case of sending read receipts.
|
|
179
|
-
*
|
|
180
|
-
* Supported types via bridge: 'read', 'read-self', 'played'
|
|
181
|
-
* Auto-handled by bridge: 'sender', 'inactive', undefined (delivered)
|
|
182
|
-
* Not supported: 'hist_sync', 'peer_msg' (logged as warning)
|
|
183
|
-
*/
|
|
184
|
-
sendReceipt: async (jid, participant, messageIds, type) => {
|
|
185
|
-
// Ahead of the empty-list exit: the types this method handles elsewhere
|
|
186
|
-
// resolve without sending anything, so a typo looked like a no-op.
|
|
187
|
-
assertArgumentDomain('sendReceipt', 'type', type, MESSAGE_RECEIPT_TYPES);
|
|
188
|
-
if (!messageIds.length)
|
|
189
|
-
return;
|
|
190
|
-
if (type === 'read' || type === 'read-self') {
|
|
191
|
-
const keys = messageIds.map(id => ({
|
|
192
|
-
remoteJid: jid,
|
|
193
|
-
id,
|
|
194
|
-
...(participant ? { participant } : {})
|
|
195
|
-
}));
|
|
196
|
-
await (await ctx.getClient()).readMessages(keys);
|
|
197
|
-
}
|
|
198
|
-
else if (type === 'played') {
|
|
199
|
-
// Voice/video-note played receipts. The bridge (and core) pick the wire
|
|
200
|
-
// type (`played` vs `played-self` for newsletters) and the `participant`
|
|
201
|
-
// attr from the chat jid, so we just hand over the keys — same shape as
|
|
202
|
-
// readMessages.
|
|
203
|
-
const keys = messageIds.map(id => ({
|
|
204
|
-
remoteJid: jid,
|
|
205
|
-
id,
|
|
206
|
-
...(participant ? { participant } : {})
|
|
207
|
-
}));
|
|
208
|
-
await (await ctx.getClient()).markPlayed(keys);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
// delivered/sender/inactive receipts are sent automatically by the Rust bridge
|
|
212
|
-
// hist_sync/peer_msg require bridge-side support
|
|
213
|
-
ctx.logger.debug({ type, jid, count: messageIds.length }, 'sendReceipt: type handled automatically by bridge or not yet supported');
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
/**
|
|
217
|
-
* Send receipts for multiple message keys, grouped by JID and participant.
|
|
218
|
-
*/
|
|
219
|
-
sendReceipts: async (keys, type) => {
|
|
220
|
-
assertArgumentDomain('sendReceipts', 'type', type, MESSAGE_RECEIPT_TYPES);
|
|
221
|
-
const client = await ctx.getClient();
|
|
222
|
-
const receiptKeys = receiptMessageKeys(keys);
|
|
223
|
-
if (type === 'read' || type === 'read-self') {
|
|
224
|
-
if (receiptKeys.length) {
|
|
225
|
-
await client.readMessages(receiptKeys);
|
|
202
|
+
else if (type === 'played') {
|
|
203
|
+
// Voice/video-note played receipts. The bridge (and core) pick the wire
|
|
204
|
+
// type (`played` vs `played-self` for newsletters) and the `participant`
|
|
205
|
+
// attr from the chat jid, so we just hand over the keys — same shape as
|
|
206
|
+
// readMessages.
|
|
207
|
+
const keys = messageIds.map(id => ({
|
|
208
|
+
remoteJid: jid,
|
|
209
|
+
id,
|
|
210
|
+
...(participant ? { participant } : {})
|
|
211
|
+
}));
|
|
212
|
+
await ctx.withClient(client => client.markPlayed(keys));
|
|
226
213
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
214
|
+
else {
|
|
215
|
+
// delivered/sender/inactive receipts are sent automatically by the Rust bridge
|
|
216
|
+
// hist_sync/peer_msg require bridge-side support
|
|
217
|
+
ctx.logger.debug({ type, jid, count: messageIds.length }, 'sendReceipt: type handled automatically by bridge or not yet supported');
|
|
231
218
|
}
|
|
219
|
+
},
|
|
220
|
+
/**
|
|
221
|
+
* Send receipts for multiple message keys, grouped by JID and participant.
|
|
222
|
+
*/
|
|
223
|
+
sendReceipts: async (keys, type) => {
|
|
224
|
+
assertArgumentDomain('sendReceipts', 'type', type, MESSAGE_RECEIPT_TYPES);
|
|
225
|
+
return ctx.withClient(async (client) => {
|
|
226
|
+
const receiptKeys = receiptMessageKeys(keys);
|
|
227
|
+
if (type === 'read' || type === 'read-self') {
|
|
228
|
+
if (receiptKeys.length) {
|
|
229
|
+
await client.readMessages(receiptKeys);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else if (type === 'played') {
|
|
233
|
+
if (receiptKeys.length) {
|
|
234
|
+
await client.markPlayed(receiptKeys);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
ctx.logger.debug({ type, count: keys.length }, 'sendReceipts: type handled automatically by bridge or not yet supported');
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
/**
|
|
243
|
+
* Request resend of a placeholder message via PeerDataOperationRequest.
|
|
244
|
+
* Wraps the request in a protocolMessage and relays it to the chat.
|
|
245
|
+
*/
|
|
246
|
+
requestPlaceholderResend: async (messageKey, msgData) => {
|
|
247
|
+
void msgData;
|
|
248
|
+
const message = {
|
|
249
|
+
protocolMessage: {
|
|
250
|
+
peerDataOperationRequestMessage: {
|
|
251
|
+
peerDataOperationRequestType: WAProto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND,
|
|
252
|
+
placeholderMessageResendRequest: [{ messageKey }]
|
|
253
|
+
},
|
|
254
|
+
type: WAProto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const bytes = encodeProtoCompat('Message', message);
|
|
258
|
+
return ctx.withClient(client => client.relayMessageBytes(messageKey.remoteJid, bytes, null));
|
|
232
259
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
/**
|
|
238
|
-
* Request resend of a placeholder message via PeerDataOperationRequest.
|
|
239
|
-
* Wraps the request in a protocolMessage and relays it to the chat.
|
|
240
|
-
*/
|
|
241
|
-
requestPlaceholderResend: async (messageKey, msgData) => {
|
|
242
|
-
void msgData;
|
|
243
|
-
const message = {
|
|
244
|
-
protocolMessage: {
|
|
245
|
-
peerDataOperationRequestMessage: {
|
|
246
|
-
peerDataOperationRequestType: WAProto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND,
|
|
247
|
-
placeholderMessageResendRequest: [{ messageKey }]
|
|
248
|
-
},
|
|
249
|
-
type: WAProto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_MESSAGE
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
const bytes = encodeProtoCompat('Message', message);
|
|
253
|
-
return (await ctx.getClient()).relayMessageBytes(messageKey.remoteJid, bytes, null);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
260
|
+
};
|
|
261
|
+
};
|
|
256
262
|
//# sourceMappingURL=messages.js.map
|