shadowx-fca 2.5.0 → 2.7.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/index.js +197 -347
- package/package.json +2 -2
- package/src/GetBotInfo.js +29 -20
- package/src/OldMessage.js +106 -114
- package/src/comment.js +28 -15
- package/src/getUserInfo.js +43 -222
- package/src/listenMqtt.js +116 -9
- package/src/nickname.js +89 -81
- package/src/sendMessage.js +292 -117
- package/src/sendMessage2.js +243 -0
- package/src/sendTypingIndicator.js +101 -45
- package/src/shareContact.js +78 -30
- package/utils.js +1305 -7
package/src/sendMessage.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
// shadowx-fca — sendMessage (MQTT Version)
|
|
3
|
+
// Supports: BOTH DM/E2EE Inbox AND Group Threads
|
|
4
|
+
// Supports: Both callback style and Promise/await style
|
|
3
5
|
|
|
4
6
|
const utils = require('../utils');
|
|
7
|
+
const log = require('npmlog');
|
|
5
8
|
|
|
6
9
|
const allowedProperties = {
|
|
7
10
|
attachment : true,
|
|
@@ -14,10 +17,18 @@ const allowedProperties = {
|
|
|
14
17
|
location : true,
|
|
15
18
|
};
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
20
|
module.exports = (defaultFuncs, api, ctx) => {
|
|
20
21
|
|
|
22
|
+
let variance = 0;
|
|
23
|
+
const epoch_id = () => Math.floor(Date.now() * (4194304 + (variance = (variance + 0.1) % 5)));
|
|
24
|
+
|
|
25
|
+
const emojiSizes = {
|
|
26
|
+
small: 1,
|
|
27
|
+
medium: 2,
|
|
28
|
+
large: 3,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Upload attachments
|
|
21
32
|
async function uploadAttachment(attachments) {
|
|
22
33
|
const uploads = [];
|
|
23
34
|
for (const att of attachments) {
|
|
@@ -30,45 +41,54 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
30
41
|
{ upload_1024: att, voice_clip: "true" },
|
|
31
42
|
{}
|
|
32
43
|
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
44
|
+
|
|
33
45
|
if (res.error) throw new Error("Upload failed: " + JSON.stringify(res));
|
|
34
46
|
uploads.push(res.payload.metadata[0]);
|
|
35
47
|
}
|
|
36
48
|
return uploads;
|
|
37
49
|
}
|
|
38
50
|
|
|
51
|
+
// Get URL share params
|
|
39
52
|
async function getUrl(url) {
|
|
40
53
|
const res = await defaultFuncs.post(
|
|
41
54
|
"https://www.facebook.com/message_share_attachment/fromURI/",
|
|
42
55
|
ctx.jar,
|
|
43
56
|
{ image_height: 960, image_width: 960, uri: url }
|
|
44
57
|
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
|
|
59
|
+
if (!res || res.error || !res.payload) {
|
|
60
|
+
throw new Error("URL attachment failed: " + JSON.stringify(res));
|
|
61
|
+
}
|
|
62
|
+
return res.payload.share_data.share_params;
|
|
47
63
|
}
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
// Send via HTTP Mercury (Fallback for Groups only)
|
|
66
|
+
async function sendHttp(form, threadID, messageAndOTID, isSingleUser) {
|
|
50
67
|
const tid = String(threadID);
|
|
51
68
|
|
|
52
69
|
if (Array.isArray(threadID)) {
|
|
53
70
|
threadID.forEach((id, idx) => { form[`specific_to_list[${idx}]`] = "fbid:" + id; });
|
|
54
71
|
form[`specific_to_list[${threadID.length}]`] = "fbid:" + ctx.userID;
|
|
55
72
|
form["client_thread_id"] = "root:" + messageAndOTID;
|
|
56
|
-
utils.log("sendMessage", "Creating new group with users: " + threadID.join(', '));
|
|
57
73
|
} else {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
if (isSingleUser) {
|
|
75
|
+
form["specific_to_list[0]"] = "fbid:" + threadID;
|
|
76
|
+
form["specific_to_list[1]"] = "fbid:" + ctx.userID;
|
|
77
|
+
form["other_user_fbid"] = threadID;
|
|
78
|
+
} else {
|
|
79
|
+
form["thread_fbid"] = tid;
|
|
80
|
+
}
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
if (ctx.globalOptions.pageID) {
|
|
64
|
-
form["author"]
|
|
65
|
-
form["specific_to_list[1]"]
|
|
66
|
-
form["creator_info[creatorID]"]
|
|
67
|
-
form["creator_info[creatorType]"]
|
|
68
|
-
form["creator_info[labelType]"]
|
|
69
|
-
form["creator_info[pageID]"]
|
|
70
|
-
form["request_user_id"]
|
|
71
|
-
form["creator_info[profileURI]"]
|
|
84
|
+
form["author"] = "fbid:" + ctx.globalOptions.pageID;
|
|
85
|
+
form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
|
|
86
|
+
form["creator_info[creatorID]"] = ctx.userID;
|
|
87
|
+
form["creator_info[creatorType]"] = "direct_admin";
|
|
88
|
+
form["creator_info[labelType]"] = "sent_message";
|
|
89
|
+
form["creator_info[pageID]"] = ctx.globalOptions.pageID;
|
|
90
|
+
form["request_user_id"] = ctx.globalOptions.pageID;
|
|
91
|
+
form["creator_info[profileURI]"] = "https://www.facebook.com/profile.php?id=" + ctx.userID;
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
const resData = await defaultFuncs
|
|
@@ -78,49 +98,145 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
78
98
|
if (!resData) throw new Error("Send message failed — no response.");
|
|
79
99
|
if (resData.error) {
|
|
80
100
|
if (resData.error === 1545012) {
|
|
81
|
-
|
|
101
|
+
throw new Error(`Cannot send message to thread ${tid}: Bot is not part of this conversation`);
|
|
82
102
|
}
|
|
83
103
|
throw new Error("Send message error: " + JSON.stringify(resData));
|
|
84
104
|
}
|
|
85
105
|
|
|
86
106
|
return resData.payload.actions.reduce((p, v) => ({
|
|
87
|
-
threadID
|
|
88
|
-
messageID
|
|
89
|
-
timestamp
|
|
107
|
+
threadID: v.thread_fbid,
|
|
108
|
+
messageID: v.message_id,
|
|
109
|
+
timestamp: v.timestamp,
|
|
90
110
|
}), null);
|
|
91
111
|
}
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
// Send via MQTT (Works for BOTH DM and Group!)
|
|
114
|
+
function sendMqtt(payload, threadID, replyToMessage, callback) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
if (!ctx.mqttClient) {
|
|
117
|
+
return reject(new Error('Not connected to MQTT. Call listenMqtt first.'));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const timestamp = Date.now();
|
|
121
|
+
const epoch = timestamp << 22;
|
|
122
|
+
const otid = epoch + Math.floor(Math.random() * 4194304);
|
|
123
|
+
|
|
124
|
+
const tasks = [
|
|
125
|
+
{
|
|
126
|
+
label: "46",
|
|
127
|
+
payload: {
|
|
128
|
+
thread_id: String(threadID),
|
|
129
|
+
otid: String(otid),
|
|
130
|
+
source: 0,
|
|
131
|
+
send_type: 1,
|
|
132
|
+
sync_group: 1,
|
|
133
|
+
text: payload.text || "",
|
|
134
|
+
initiating_source: 1,
|
|
135
|
+
skip_url_preview_gen: 0
|
|
136
|
+
},
|
|
137
|
+
queue_name: String(threadID),
|
|
138
|
+
task_id: 0,
|
|
139
|
+
failure_count: null
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
label: "21",
|
|
143
|
+
payload: {
|
|
144
|
+
thread_id: String(threadID),
|
|
145
|
+
last_read_watermark_ts: Date.now(),
|
|
146
|
+
sync_group: 1
|
|
147
|
+
},
|
|
148
|
+
queue_name: String(threadID),
|
|
149
|
+
task_id: 1,
|
|
150
|
+
failure_count: null
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
// Add reply metadata if replying
|
|
155
|
+
if (replyToMessage) {
|
|
156
|
+
tasks[0].payload.reply_metadata = {
|
|
157
|
+
reply_source_id: String(replyToMessage),
|
|
158
|
+
reply_source_type: 1,
|
|
159
|
+
reply_type: 0
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Add emoji size if present
|
|
164
|
+
if (payload.hot_emoji_size) {
|
|
165
|
+
tasks[0].payload.hot_emoji_size = payload.hot_emoji_size;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Add sticker if present
|
|
169
|
+
if (payload.sticker_id) {
|
|
170
|
+
tasks[0].payload.send_type = 2;
|
|
171
|
+
tasks[0].payload.sticker_id = payload.sticker_id;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Add attachments if present
|
|
175
|
+
if (payload.attachment_fbids && payload.attachment_fbids.length > 0) {
|
|
176
|
+
tasks[0].payload.send_type = 3;
|
|
177
|
+
tasks[0].payload.attachment_fbids = payload.attachment_fbids;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Add mentions if present
|
|
181
|
+
if (payload.mention_data) {
|
|
182
|
+
tasks[0].payload.mention_data = payload.mention_data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Add location if present
|
|
186
|
+
if (payload.location_data) {
|
|
187
|
+
tasks[0].payload.location_data = payload.location_data;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const mqttForm = {
|
|
191
|
+
app_id: ctx.appID || "2220391788200892",
|
|
192
|
+
payload: {
|
|
193
|
+
tasks: tasks,
|
|
194
|
+
epoch_id: epoch_id(),
|
|
195
|
+
version_id: "6120284488008082",
|
|
196
|
+
data_trace_id: null
|
|
197
|
+
},
|
|
198
|
+
request_id: 1,
|
|
199
|
+
type: 3
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Stringify all task payloads
|
|
203
|
+
mqttForm.payload.tasks.forEach(task => {
|
|
204
|
+
task.payload = JSON.stringify(task.payload);
|
|
205
|
+
});
|
|
206
|
+
mqttForm.payload = JSON.stringify(mqttForm.payload);
|
|
207
|
+
|
|
208
|
+
log.info("sendMessage", `Sending via MQTT to ${threadID}`);
|
|
209
|
+
|
|
210
|
+
ctx.mqttClient.publish('/ls_req', JSON.stringify(mqttForm), { qos: 1, retain: false }, (err) => {
|
|
211
|
+
if (err) {
|
|
212
|
+
log.error("sendMessage", `MQTT publish failed: ${err.message || err}`);
|
|
213
|
+
reject(new Error(`MQTT publish failed: ${err.message || err}`));
|
|
214
|
+
} else {
|
|
215
|
+
const result = {
|
|
216
|
+
threadID: String(threadID),
|
|
217
|
+
messageID: String(otid),
|
|
218
|
+
timestamp: timestamp
|
|
219
|
+
};
|
|
220
|
+
if (callback) callback(null, result);
|
|
221
|
+
resolve(result);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Main send function
|
|
228
|
+
return async function sendMessage(msg, threadID, replyOrCallback, callbackArg) {
|
|
111
229
|
let replyToMessage = null;
|
|
112
|
-
let callback
|
|
230
|
+
let callback = null;
|
|
113
231
|
|
|
232
|
+
// Parse arguments
|
|
114
233
|
if (typeof replyOrCallback === 'function') {
|
|
115
|
-
// sendMessage(msg, threadID, callback)
|
|
116
234
|
callback = replyOrCallback;
|
|
117
|
-
} else if (typeof replyOrCallback === 'string') {
|
|
118
|
-
|
|
119
|
-
replyToMessage = replyOrCallback;
|
|
235
|
+
} else if (typeof replyOrCallback === 'string' || typeof replyOrCallback === 'number') {
|
|
236
|
+
replyToMessage = String(replyOrCallback);
|
|
120
237
|
if (typeof callbackArg === 'function') callback = callbackArg;
|
|
121
238
|
}
|
|
122
239
|
|
|
123
|
-
// ── Core send logic (async) ──────────────────────────────────
|
|
124
240
|
const doSend = async () => {
|
|
125
241
|
const msgType = utils.getType(msg);
|
|
126
242
|
if (msgType !== "String" && msgType !== "Object") {
|
|
@@ -133,100 +249,159 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
133
249
|
throw new Error("Disallowed message properties: " + disallowed.join(", "));
|
|
134
250
|
}
|
|
135
251
|
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
timestamp_time_passed : "0",
|
|
145
|
-
is_unread : false,
|
|
146
|
-
is_cleared : false,
|
|
147
|
-
is_forward : false,
|
|
148
|
-
is_filtered_content : false,
|
|
149
|
-
is_filtered_content_bh : false,
|
|
150
|
-
is_filtered_content_account : false,
|
|
151
|
-
is_filtered_content_quasar : false,
|
|
152
|
-
is_filtered_content_invalid_app : false,
|
|
153
|
-
is_spoof_warning : false,
|
|
154
|
-
source : "source:chat:web",
|
|
155
|
-
"source_tags[0]" : "source:chat",
|
|
156
|
-
...(msg.body && { body: msg.body }),
|
|
157
|
-
html_body : false,
|
|
158
|
-
ui_push_phase : "V3",
|
|
159
|
-
status : "0",
|
|
160
|
-
offline_threading_id : messageAndOTID,
|
|
161
|
-
message_id : messageAndOTID,
|
|
162
|
-
threading_id : utils.generateThreadingID(ctx.clientID),
|
|
163
|
-
"ephemeral_ttl_mode:" : "0",
|
|
164
|
-
manual_retry_cnt : "0",
|
|
165
|
-
has_attachment : !!(msg.attachment || msg.url || msg.sticker),
|
|
166
|
-
signatureID : utils.getSignatureID(),
|
|
167
|
-
...(replyToMessage && { replied_to_message_id: replyToMessage }),
|
|
252
|
+
// Detect if DM or Group
|
|
253
|
+
const threadIDStr = String(threadID);
|
|
254
|
+
const isSingleUser = !Array.isArray(threadID) &&
|
|
255
|
+
(threadIDStr.length === 15 || !threadIDStr.match(/^\d{16,}$/));
|
|
256
|
+
|
|
257
|
+
// Build MQTT payload
|
|
258
|
+
const mqttPayload = {
|
|
259
|
+
text: msg.body || ""
|
|
168
260
|
};
|
|
169
261
|
|
|
262
|
+
// Handle mentions
|
|
263
|
+
if (msg.mentions && msg.mentions.length > 0) {
|
|
264
|
+
const arrayIds = [];
|
|
265
|
+
const arrayOffsets = [];
|
|
266
|
+
const arrayLengths = [];
|
|
267
|
+
const mention_types = [];
|
|
268
|
+
|
|
269
|
+
for (const mention of msg.mentions) {
|
|
270
|
+
const { tag, id, fromIndex } = mention;
|
|
271
|
+
if (typeof tag !== "string") throw new Error("Mention tags must be strings.");
|
|
272
|
+
|
|
273
|
+
const offset = msg.body.indexOf(tag, fromIndex || 0);
|
|
274
|
+
if (offset < 0) log.warn("sendMessage", `Mention "${tag}" not found in body.`);
|
|
275
|
+
|
|
276
|
+
arrayIds.push(id || 0);
|
|
277
|
+
arrayOffsets.push(offset);
|
|
278
|
+
arrayLengths.push(tag.length);
|
|
279
|
+
mention_types.push("p");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
mqttPayload.mention_data = {
|
|
283
|
+
mention_ids: arrayIds.join(","),
|
|
284
|
+
mention_offsets: arrayOffsets.join(","),
|
|
285
|
+
mention_lengths: arrayLengths.join(","),
|
|
286
|
+
mention_types: mention_types.join(",")
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Handle emoji
|
|
291
|
+
if (msg.emoji) {
|
|
292
|
+
let emojiSize = msg.emojiSize || "medium";
|
|
293
|
+
if (!["small", "medium", "large"].includes(emojiSize)) {
|
|
294
|
+
throw new Error("emojiSize must be small, medium, or large.");
|
|
295
|
+
}
|
|
296
|
+
mqttPayload.text = msg.emoji;
|
|
297
|
+
mqttPayload.hot_emoji_size = emojiSizes[emojiSize];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Handle sticker
|
|
301
|
+
if (msg.sticker) {
|
|
302
|
+
mqttPayload.sticker_id = String(msg.sticker);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Handle location
|
|
170
306
|
if (msg.location) {
|
|
171
307
|
if (!msg.location.latitude || !msg.location.longitude) {
|
|
172
308
|
throw new Error("location needs both latitude and longitude.");
|
|
173
309
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
310
|
+
mqttPayload.location_data = {
|
|
311
|
+
coordinates: {
|
|
312
|
+
latitude: msg.location.latitude,
|
|
313
|
+
longitude: msg.location.longitude
|
|
314
|
+
},
|
|
315
|
+
is_current_location: !!msg.location.current,
|
|
316
|
+
is_live_location: !!msg.location.live
|
|
317
|
+
};
|
|
177
318
|
}
|
|
178
319
|
|
|
179
|
-
|
|
180
|
-
|
|
320
|
+
// Handle attachments
|
|
181
321
|
if (msg.attachment) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
322
|
+
if (utils.getType(msg.attachment) !== "Array") {
|
|
323
|
+
msg.attachment = [msg.attachment];
|
|
324
|
+
}
|
|
185
325
|
const files = await uploadAttachment(msg.attachment);
|
|
186
|
-
files.
|
|
187
|
-
const
|
|
188
|
-
|
|
326
|
+
mqttPayload.attachment_fbids = files.map(file => {
|
|
327
|
+
const key = Object.keys(file)[0];
|
|
328
|
+
return file[key];
|
|
189
329
|
});
|
|
190
330
|
}
|
|
191
331
|
|
|
332
|
+
// Handle URL
|
|
192
333
|
if (msg.url) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
throw new Error("emojiSize must be small, medium, or large.");
|
|
334
|
+
try {
|
|
335
|
+
const params = await getUrl(msg.url);
|
|
336
|
+
// For MQTT, we just append URL to text
|
|
337
|
+
mqttPayload.text = mqttPayload.text ? mqttPayload.text + " " + msg.url : msg.url;
|
|
338
|
+
} catch (err) {
|
|
339
|
+
log.warn("sendMessage", "URL attachment failed, sending as text link");
|
|
340
|
+
mqttPayload.text = mqttPayload.text ? mqttPayload.text + " " + msg.url : msg.url;
|
|
201
341
|
}
|
|
202
|
-
if (!form.body) throw new Error("body must not be empty when using emoji.");
|
|
203
|
-
form.body = msg.emoji;
|
|
204
|
-
form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
|
|
205
342
|
}
|
|
206
343
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
344
|
+
// Try MQTT first (works for both DM and Group)
|
|
345
|
+
try {
|
|
346
|
+
return await sendMqtt(mqttPayload, threadID, replyToMessage, callback);
|
|
347
|
+
} catch (mqttErr) {
|
|
348
|
+
log.warn("sendMessage", "MQTT send failed: " + mqttErr.message);
|
|
349
|
+
|
|
350
|
+
// Fallback to HTTP for groups only
|
|
351
|
+
if (!isSingleUser && !Array.isArray(threadID)) {
|
|
352
|
+
log.info("sendMessage", "Falling back to HTTP for group message");
|
|
353
|
+
|
|
354
|
+
const messageAndOTID = utils.generateOfflineThreadingID();
|
|
355
|
+
const httpForm = {
|
|
356
|
+
client: "mercury",
|
|
357
|
+
action_type: "ma-type:user-generated-message",
|
|
358
|
+
author: "fbid:" + ctx.userID,
|
|
359
|
+
timestamp: Date.now(),
|
|
360
|
+
timestamp_absolute: "Today",
|
|
361
|
+
timestamp_relative: utils.generateTimestampRelative(),
|
|
362
|
+
timestamp_time_passed: "0",
|
|
363
|
+
is_unread: false,
|
|
364
|
+
is_cleared: false,
|
|
365
|
+
is_forward: false,
|
|
366
|
+
is_filtered_content: false,
|
|
367
|
+
is_filtered_content_bh: false,
|
|
368
|
+
is_filtered_content_account: false,
|
|
369
|
+
is_filtered_content_quasar: false,
|
|
370
|
+
is_filtered_content_invalid_app: false,
|
|
371
|
+
is_spoof_warning: false,
|
|
372
|
+
source: "source:chat:web",
|
|
373
|
+
"source_tags[0]": "source:chat",
|
|
374
|
+
body: msg.body || "",
|
|
375
|
+
html_body: false,
|
|
376
|
+
ui_push_phase: "V3",
|
|
377
|
+
status: "0",
|
|
378
|
+
offline_threading_id: messageAndOTID,
|
|
379
|
+
message_id: messageAndOTID,
|
|
380
|
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
|
381
|
+
"ephemeral_ttl_mode:": "0",
|
|
382
|
+
manual_retry_cnt: "0",
|
|
383
|
+
has_attachment: !!(msg.attachment || msg.url || msg.sticker),
|
|
384
|
+
signatureID: utils.getSignatureID(),
|
|
385
|
+
...(replyToMessage && { replied_to_message_id: replyToMessage }),
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
if (msg.sticker) httpForm["sticker_id"] = msg.sticker;
|
|
389
|
+
|
|
390
|
+
return await sendHttp(httpForm, threadID, messageAndOTID, isSingleUser);
|
|
391
|
+
} else {
|
|
392
|
+
throw new Error("DM/E2EE messages require MQTT. Make sure listenMqtt is called first.");
|
|
219
393
|
}
|
|
220
394
|
}
|
|
221
|
-
|
|
222
|
-
return sendContent(form, threadID, messageAndOTID);
|
|
223
395
|
};
|
|
224
396
|
|
|
225
|
-
//
|
|
397
|
+
// Execute and handle callback/promise
|
|
226
398
|
if (callback) {
|
|
227
|
-
doSend()
|
|
399
|
+
doSend()
|
|
400
|
+
.then(info => callback(null, info))
|
|
401
|
+
.catch(err => callback(err));
|
|
402
|
+
return undefined;
|
|
228
403
|
} else {
|
|
229
404
|
return doSend();
|
|
230
405
|
}
|
|
231
406
|
};
|
|
232
|
-
};
|
|
407
|
+
};
|