@lazyneoaz/nkxchat 1.0.3 → 1.0.4
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/package.json +2 -1
- package/src/apis/listenMqtt.js +88 -1
- package/src/apis/scheduler.js +129 -0
- package/src/apis/sendEffect.js +182 -8
- package/src/apis/sendMessage.js +147 -159
- package/src/apis/sendMessageMqtt.js +190 -174
- package/src/apis/sendTypingIndicator.js +68 -34
package/src/apis/sendMessage.js
CHANGED
|
@@ -13,38 +13,26 @@ const allowedProperties = {
|
|
|
13
13
|
mentions: true,
|
|
14
14
|
location: true,
|
|
15
15
|
effect: true,
|
|
16
|
+
replyToMessage: true,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
module.exports = (defaultFuncs, api, ctx) => {
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
const cache = getThreadCache();
|
|
28
|
-
if (Object.prototype.hasOwnProperty.call(cache, tid)) return !!cache[tid];
|
|
29
|
-
try {
|
|
30
|
-
const info = await api.getThreadInfo(tid);
|
|
31
|
-
cache[tid] = !!info.isGroup;
|
|
32
|
-
return !!info.isGroup;
|
|
33
|
-
} catch (_) {
|
|
34
|
-
const fallback = tid.length >= 16;
|
|
35
|
-
cache[tid] = fallback;
|
|
36
|
-
return fallback;
|
|
37
|
-
}
|
|
20
|
+
async function getUrl(url) {
|
|
21
|
+
const resData = await defaultFuncs.post(
|
|
22
|
+
"https://www.facebook.com/message_share_attachment/fromURI/",
|
|
23
|
+
ctx.jar,
|
|
24
|
+
{ image_height: 960, image_width: 960, uri: url }
|
|
25
|
+
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
26
|
+
if (!resData || resData.error || !resData.payload) throw new Error("Invalid url");
|
|
27
|
+
return resData.payload.share_data.share_params;
|
|
38
28
|
}
|
|
39
29
|
|
|
40
30
|
function detectAttachmentType(attachment) {
|
|
41
|
-
const
|
|
42
|
-
const ext =
|
|
43
|
-
|
|
31
|
+
const p = attachment.path || '';
|
|
32
|
+
const ext = p.toLowerCase().split('.').pop();
|
|
44
33
|
const audioTypes = ['mp3', 'wav', 'aac', 'm4a', 'ogg', 'opus', 'flac'];
|
|
45
34
|
const videoTypes = ['mp4', 'mov', 'avi', 'mkv', 'webm', 'wmv', 'flv'];
|
|
46
35
|
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
|
47
|
-
|
|
48
36
|
if (audioTypes.includes(ext)) return { voice_clip: "true" };
|
|
49
37
|
if (videoTypes.includes(ext)) return { video: "true" };
|
|
50
38
|
if (imageTypes.includes(ext)) return { image: "true" };
|
|
@@ -63,7 +51,6 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
63
51
|
{},
|
|
64
52
|
{ ...ctx, requestThreadID: threadIDHint }
|
|
65
53
|
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
66
|
-
|
|
67
54
|
if (oksir.error) throw new Error(JSON.stringify(oksir));
|
|
68
55
|
return oksir.payload.metadata[0];
|
|
69
56
|
}
|
|
@@ -82,24 +69,122 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
82
69
|
return uploads;
|
|
83
70
|
}
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
ctx.jar,
|
|
89
|
-
{ image_height: 960, image_width: 960, uri: url }
|
|
90
|
-
).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
|
|
91
|
-
if (!resData || resData.error || !resData.payload) throw new Error("Invalid url");
|
|
92
|
-
return resData.payload.share_data.share_params;
|
|
72
|
+
function getThreadCache() {
|
|
73
|
+
if (!ctx.threadTypeCache) ctx.threadTypeCache = Object.create(null);
|
|
74
|
+
return ctx.threadTypeCache;
|
|
93
75
|
}
|
|
94
76
|
|
|
95
|
-
async function
|
|
96
|
-
if (utils.getType(
|
|
97
|
-
|
|
98
|
-
|
|
77
|
+
async function isGroupThread(threadID, explicitIsGroup) {
|
|
78
|
+
if (utils.getType(explicitIsGroup) === "Boolean") return !!explicitIsGroup;
|
|
79
|
+
const tid = threadID.toString();
|
|
80
|
+
const cache = getThreadCache();
|
|
81
|
+
if (Object.prototype.hasOwnProperty.call(cache, tid)) return !!cache[tid];
|
|
82
|
+
try {
|
|
83
|
+
const info = await api.getThreadInfo(tid);
|
|
84
|
+
cache[tid] = !!info.isGroup;
|
|
85
|
+
return !!info.isGroup;
|
|
86
|
+
} catch (_) {
|
|
87
|
+
const fallback = tid.length >= 16;
|
|
88
|
+
cache[tid] = fallback;
|
|
89
|
+
return fallback;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function sendViaHttp(msg, threadID, replyToMessage, isGroup) {
|
|
94
|
+
const isSingleUser = !(await isGroupThread(threadID, isGroup));
|
|
95
|
+
let messageAndOTID = utils.generateOfflineThreadingID();
|
|
96
|
+
let form = {
|
|
97
|
+
client: "mercury",
|
|
98
|
+
action_type: "ma-type:user-generated-message",
|
|
99
|
+
author: "fbid:" + ctx.userID,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
timestamp_absolute: "Today",
|
|
102
|
+
timestamp_relative: utils.generateTimestampRelative(),
|
|
103
|
+
timestamp_time_passed: "0",
|
|
104
|
+
is_unread: false,
|
|
105
|
+
is_cleared: false,
|
|
106
|
+
is_forward: false,
|
|
107
|
+
is_filtered_content: false,
|
|
108
|
+
is_filtered_content_bh: false,
|
|
109
|
+
is_filtered_content_account: false,
|
|
110
|
+
is_filtered_content_quasar: false,
|
|
111
|
+
is_filtered_content_invalid_app: false,
|
|
112
|
+
is_spoof_warning: false,
|
|
113
|
+
source: "source:chat:web",
|
|
114
|
+
"source_tags[0]": "source:chat",
|
|
115
|
+
...(msg.body && { body: msg.body }),
|
|
116
|
+
html_body: false,
|
|
117
|
+
ui_push_phase: "V3",
|
|
118
|
+
status: "0",
|
|
119
|
+
offline_threading_id: messageAndOTID,
|
|
120
|
+
message_id: messageAndOTID,
|
|
121
|
+
threading_id: utils.generateThreadingID(ctx.clientID),
|
|
122
|
+
"ephemeral_ttl_mode:": "0",
|
|
123
|
+
manual_retry_cnt: "0",
|
|
124
|
+
has_attachment: !!(msg.attachment || msg.url || msg.sticker),
|
|
125
|
+
signatureID: utils.getSignatureID(),
|
|
126
|
+
...(replyToMessage && { replied_to_message_id: replyToMessage })
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (msg.location) {
|
|
130
|
+
if (!msg.location.latitude || !msg.location.longitude) throw new Error("location property needs both latitude and longitude");
|
|
131
|
+
form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
|
|
132
|
+
form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
|
|
133
|
+
form["location_attachment[is_current_location]"] = !!msg.location.current;
|
|
134
|
+
}
|
|
135
|
+
if (msg.sticker) form["sticker_id"] = msg.sticker;
|
|
136
|
+
if (msg.effect) {
|
|
137
|
+
const effectTag = String(msg.effect).toUpperCase().replace(/[\s\-]+/g, '_');
|
|
138
|
+
form.has_lightweight_action = true;
|
|
139
|
+
form['lightweight_action_attached[message_id]'] = messageAndOTID;
|
|
140
|
+
form['lightweight_action_attached[messaging_tag]'] = 'fb.messaging.effects.' + effectTag;
|
|
141
|
+
}
|
|
142
|
+
if (msg.attachment) {
|
|
143
|
+
form.image_ids = [];
|
|
144
|
+
form.gif_ids = [];
|
|
145
|
+
form.file_ids = [];
|
|
146
|
+
form.video_ids = [];
|
|
147
|
+
form.audio_ids = [];
|
|
148
|
+
if (utils.getType(msg.attachment) !== "Array") msg.attachment = [msg.attachment];
|
|
149
|
+
const files = await uploadAttachment(msg.attachment, threadID);
|
|
150
|
+
files.forEach(file => {
|
|
151
|
+
const type = Object.keys(file)[0];
|
|
152
|
+
form["" + type + "s"].push(file[type]);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (msg.url) {
|
|
156
|
+
form["shareable_attachment[share_type]"] = "100";
|
|
157
|
+
const params = await getUrl(msg.url);
|
|
158
|
+
form["shareable_attachment[share_params]"] = params;
|
|
159
|
+
}
|
|
160
|
+
if (msg.emoji) {
|
|
161
|
+
if (!msg.emojiSize) msg.emojiSize = "medium";
|
|
162
|
+
if (msg.emojiSize !== "small" && msg.emojiSize !== "medium" && msg.emojiSize !== "large") throw new Error("emojiSize property is invalid");
|
|
163
|
+
if (form.body && form.body !== "") throw new Error("body is not empty");
|
|
164
|
+
form.body = msg.emoji;
|
|
165
|
+
form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
|
|
166
|
+
}
|
|
167
|
+
if (msg.mentions) {
|
|
168
|
+
for (let i = 0; i < msg.mentions.length; i++) {
|
|
169
|
+
const mention = msg.mentions[i];
|
|
170
|
+
const tag = mention.tag;
|
|
171
|
+
if (typeof tag !== "string") throw new Error("Mention tags must be strings.");
|
|
172
|
+
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
|
|
173
|
+
if (offset < 0) utils.warn("handleMention", 'Mention for "' + tag + '" not found in message string.');
|
|
174
|
+
const id = mention.id || 0;
|
|
175
|
+
const emptyChar = '\u200E';
|
|
176
|
+
form["body"] = emptyChar + msg.body;
|
|
177
|
+
form["profile_xmd[" + i + "][offset]"] = offset + 1;
|
|
178
|
+
form["profile_xmd[" + i + "][length]"] = tag.length;
|
|
179
|
+
form["profile_xmd[" + i + "][id]"] = id;
|
|
180
|
+
form["profile_xmd[" + i + "][type]"] = "p";
|
|
99
181
|
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (utils.getType(threadID) === "Array") {
|
|
185
|
+
for (let i = 0; i < threadID.length; i++) form["specific_to_list[" + i + "]"] = "fbid:" + threadID[i];
|
|
100
186
|
form["specific_to_list[" + threadID.length + "]"] = "fbid:" + ctx.userID;
|
|
101
187
|
form["client_thread_id"] = "root:" + messageAndOTID;
|
|
102
|
-
utils.log("sendMessage", "Sending message to multiple users: " + threadID);
|
|
103
188
|
} else {
|
|
104
189
|
if (isSingleUser) {
|
|
105
190
|
form["specific_to_list[0]"] = "fbid:" + threadID;
|
|
@@ -110,7 +195,6 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
110
195
|
form["thread_fbid"] = threadID;
|
|
111
196
|
}
|
|
112
197
|
}
|
|
113
|
-
|
|
114
198
|
if (ctx.globalOptions.pageID) {
|
|
115
199
|
form["author"] = "fbid:" + ctx.globalOptions.pageID;
|
|
116
200
|
form["specific_to_list[1]"] = "fbid:" + ctx.globalOptions.pageID;
|
|
@@ -131,14 +215,10 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
131
215
|
|
|
132
216
|
if (!resData) throw new Error("Send message failed.");
|
|
133
217
|
if (resData.error) {
|
|
134
|
-
if (resData.error === 1545012)
|
|
135
|
-
utils.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
|
|
136
|
-
}
|
|
137
|
-
// Check for suspension signals in error
|
|
218
|
+
if (resData.error === 1545012) utils.warn("sendMessage", "Got error 1545012. This might mean that you're not part of the conversation " + threadID);
|
|
138
219
|
globalAntiSuspension.detectSuspensionSignal(String(resData.error) + ' ' + JSON.stringify(resData));
|
|
139
220
|
throw new Error(JSON.stringify(resData));
|
|
140
221
|
}
|
|
141
|
-
|
|
142
222
|
const messageInfo = resData.payload.actions.reduce((p, v) => {
|
|
143
223
|
return { threadID: v.thread_fbid, messageID: v.message_id, timestamp: v.timestamp } || p;
|
|
144
224
|
}, null);
|
|
@@ -182,11 +262,11 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
182
262
|
return callback(new Error("MessageID should be of type string and not " + threadIDType + "."));
|
|
183
263
|
}
|
|
184
264
|
|
|
185
|
-
if (!ctx.validator.isValidMessage(msg)) {
|
|
265
|
+
if (ctx.validator && !ctx.validator.isValidMessage(msg)) {
|
|
186
266
|
return callback(new Error("Invalid message content"));
|
|
187
267
|
}
|
|
188
268
|
const threadIDs = Array.isArray(threadID) ? threadID : [threadID];
|
|
189
|
-
if (!ctx.validator.validateIDArray(threadIDs, ctx.validator.isValidThreadID)) {
|
|
269
|
+
if (ctx.validator && !ctx.validator.validateIDArray(threadIDs, ctx.validator.isValidThreadID)) {
|
|
190
270
|
return callback(new Error("Invalid thread ID(s)"));
|
|
191
271
|
}
|
|
192
272
|
|
|
@@ -198,122 +278,18 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
198
278
|
}
|
|
199
279
|
|
|
200
280
|
try {
|
|
201
|
-
|
|
202
|
-
let form = {
|
|
203
|
-
client: "mercury",
|
|
204
|
-
action_type: "ma-type:user-generated-message",
|
|
205
|
-
author: "fbid:" + ctx.userID,
|
|
206
|
-
timestamp: Date.now(),
|
|
207
|
-
timestamp_absolute: "Today",
|
|
208
|
-
timestamp_relative: utils.generateTimestampRelative(),
|
|
209
|
-
timestamp_time_passed: "0",
|
|
210
|
-
is_unread: false,
|
|
211
|
-
is_cleared: false,
|
|
212
|
-
is_forward: false,
|
|
213
|
-
is_filtered_content: false,
|
|
214
|
-
is_filtered_content_bh: false,
|
|
215
|
-
is_filtered_content_account: false,
|
|
216
|
-
is_filtered_content_quasar: false,
|
|
217
|
-
is_filtered_content_invalid_app: false,
|
|
218
|
-
is_spoof_warning: false,
|
|
219
|
-
source: "source:chat:web",
|
|
220
|
-
"source_tags[0]": "source:chat",
|
|
221
|
-
...(msg.body && { body: msg.body }),
|
|
222
|
-
html_body: false,
|
|
223
|
-
ui_push_phase: "V3",
|
|
224
|
-
status: "0",
|
|
225
|
-
offline_threading_id: messageAndOTID,
|
|
226
|
-
message_id: messageAndOTID,
|
|
227
|
-
threading_id: utils.generateThreadingID(ctx.clientID),
|
|
228
|
-
"ephemeral_ttl_mode:": "0",
|
|
229
|
-
manual_retry_cnt: "0",
|
|
230
|
-
has_attachment: !!(msg.attachment || msg.url || msg.sticker),
|
|
231
|
-
signatureID: utils.getSignatureID(),
|
|
232
|
-
...(replyToMessage && { replied_to_message_id: replyToMessage })
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
if (msg.location) {
|
|
236
|
-
if (!msg.location.latitude || !msg.location.longitude) {
|
|
237
|
-
return callback(new Error("location property needs both latitude and longitude"));
|
|
238
|
-
}
|
|
239
|
-
form["location_attachment[coordinates][latitude]"] = msg.location.latitude;
|
|
240
|
-
form["location_attachment[coordinates][longitude]"] = msg.location.longitude;
|
|
241
|
-
form["location_attachment[is_current_location]"] = !!msg.location.current;
|
|
242
|
-
}
|
|
243
|
-
if (msg.sticker) form["sticker_id"] = msg.sticker;
|
|
244
|
-
if (msg.effect) {
|
|
245
|
-
const effectTag = String(msg.effect).toUpperCase().replace(/[\s\-]+/g, '_');
|
|
246
|
-
const messagingTag = 'fb.messaging.effects.' + effectTag;
|
|
247
|
-
form.has_lightweight_action = true;
|
|
248
|
-
form['lightweight_action_attached[message_id]'] = messageAndOTID;
|
|
249
|
-
form['lightweight_action_attached[messaging_tag]'] = messagingTag;
|
|
250
|
-
}
|
|
251
|
-
if (msg.attachment) {
|
|
252
|
-
form.image_ids = [];
|
|
253
|
-
form.gif_ids = [];
|
|
254
|
-
form.file_ids = [];
|
|
255
|
-
form.video_ids = [];
|
|
256
|
-
form.audio_ids = [];
|
|
257
|
-
if (utils.getType(msg.attachment) !== "Array") msg.attachment = [msg.attachment];
|
|
258
|
-
const files = await uploadAttachment(msg.attachment, threadID);
|
|
259
|
-
files.forEach(file => {
|
|
260
|
-
const type = Object.keys(file)[0];
|
|
261
|
-
form["" + type + "s"].push(file[type]);
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
if (msg.url) {
|
|
265
|
-
form["shareable_attachment[share_type]"] = "100";
|
|
266
|
-
const params = await getUrl(msg.url);
|
|
267
|
-
form["shareable_attachment[share_params]"] = params;
|
|
268
|
-
}
|
|
269
|
-
if (msg.emoji) {
|
|
270
|
-
if (!msg.emojiSize) msg.emojiSize = "medium";
|
|
271
|
-
if (msg.emojiSize !== "small" && msg.emojiSize !== "medium" && msg.emojiSize !== "large") {
|
|
272
|
-
return callback(new Error("emojiSize property is invalid"));
|
|
273
|
-
}
|
|
274
|
-
if (form.body && form.body !== "") return callback(new Error("body is not empty"));
|
|
275
|
-
form.body = msg.emoji;
|
|
276
|
-
form["tags[0]"] = "hot_emoji_size:" + msg.emojiSize;
|
|
277
|
-
}
|
|
278
|
-
if (msg.mentions) {
|
|
279
|
-
for (let i = 0; i < msg.mentions.length; i++) {
|
|
280
|
-
const mention = msg.mentions[i];
|
|
281
|
-
const tag = mention.tag;
|
|
282
|
-
if (typeof tag !== "string") return callback(new Error("Mention tags must be strings."));
|
|
283
|
-
const offset = msg.body.indexOf(tag, mention.fromIndex || 0);
|
|
284
|
-
if (offset < 0) utils.warn("handleMention", 'Mention for "' + tag + '" not found in message string.');
|
|
285
|
-
if (!mention.id) utils.warn("handleMention", "Mention id should be non-null.");
|
|
286
|
-
const id = mention.id || 0;
|
|
287
|
-
const emptyChar = '\u200E';
|
|
288
|
-
form["body"] = emptyChar + msg.body;
|
|
289
|
-
form["profile_xmd[" + i + "][offset]"] = offset + 1;
|
|
290
|
-
form["profile_xmd[" + i + "][length]"] = tag.length;
|
|
291
|
-
form["profile_xmd[" + i + "][id]"] = id;
|
|
292
|
-
form["profile_xmd[" + i + "][type]"] = "p";
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const isSingleUser = !(await isGroupThread(threadID, isGroup));
|
|
281
|
+
await globalAntiSuspension.prepareBeforeMessage(String(Array.isArray(threadID) ? threadID[0] : threadID), msg.body || '');
|
|
297
282
|
|
|
298
|
-
// ── Optimised anti-suspension send flow ───────────────────────────────
|
|
299
|
-
// Step 1: enforce thread throttle (single delay — no stacking).
|
|
300
|
-
await globalAntiSuspension.prepareBeforeMessage(threadID, msg.body || '');
|
|
301
|
-
|
|
302
|
-
// Step 2: start typing indicator BEFORE the typing delay so the delay
|
|
303
|
-
// is "hidden" inside the visible typing indicator — zero extra latency.
|
|
304
283
|
let typingStarted = false;
|
|
305
284
|
let typingTimeout;
|
|
306
|
-
const shouldSimulateTyping = ctx.globalOptions && ctx.globalOptions.simulateTyping && api.sendTypingIndicator;
|
|
285
|
+
const shouldSimulateTyping = ctx.globalOptions && ctx.globalOptions.simulateTyping && api.sendTypingIndicator && ctx.mqttClient && ctx.mqttClient.connected;
|
|
307
286
|
if (shouldSimulateTyping) {
|
|
308
287
|
try {
|
|
309
288
|
await api.sendTypingIndicator(true, threadID);
|
|
310
289
|
typingStarted = true;
|
|
311
|
-
|
|
312
|
-
// Typing delay runs while the indicator is already showing.
|
|
313
290
|
const msgLen = (msg.body || '').length;
|
|
314
291
|
const typingMs = await globalAntiSuspension.simulateTyping(threadID, msgLen);
|
|
315
292
|
await new Promise(resolve => setTimeout(resolve, typingMs));
|
|
316
|
-
|
|
317
293
|
typingTimeout = setTimeout(() => {
|
|
318
294
|
if (typingStarted) {
|
|
319
295
|
try { api.sendTypingIndicator(false, threadID); } catch (_) {}
|
|
@@ -323,21 +299,33 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
323
299
|
} catch (_) {}
|
|
324
300
|
}
|
|
325
301
|
|
|
326
|
-
// Step 3: send.
|
|
327
302
|
try {
|
|
328
|
-
|
|
303
|
+
let result;
|
|
304
|
+
const mqttReady = ctx.mqttClient && ctx.mqttClient.connected;
|
|
305
|
+
const isMultiRecipient = Array.isArray(threadID);
|
|
306
|
+
|
|
307
|
+
if (mqttReady && !isMultiRecipient && api.sendMessageMqtt) {
|
|
308
|
+
result = await api.sendMessageMqtt(msg, threadID, replyToMessage);
|
|
309
|
+
} else {
|
|
310
|
+
result = await sendViaHttp(msg, threadID, replyToMessage, isGroup);
|
|
311
|
+
}
|
|
329
312
|
callback(null, result);
|
|
330
|
-
} catch (
|
|
331
|
-
|
|
332
|
-
if (api.sendMessageMqtt) {
|
|
313
|
+
} catch (sendErr) {
|
|
314
|
+
const mqttReady = ctx.mqttClient && ctx.mqttClient.connected;
|
|
315
|
+
if (mqttReady && !Array.isArray(threadID) && api.sendMessageMqtt) {
|
|
333
316
|
try {
|
|
334
317
|
const mqttRes = await api.sendMessageMqtt(msg, threadID, replyToMessage);
|
|
335
318
|
callback(null, mqttRes);
|
|
336
|
-
} catch (
|
|
337
|
-
callback(
|
|
319
|
+
} catch (_mqttErr) {
|
|
320
|
+
callback(sendErr);
|
|
338
321
|
}
|
|
339
322
|
} else {
|
|
340
|
-
|
|
323
|
+
try {
|
|
324
|
+
const httpRes = await sendViaHttp(msg, threadID, replyToMessage, isGroup);
|
|
325
|
+
callback(null, httpRes);
|
|
326
|
+
} catch (_httpErr) {
|
|
327
|
+
callback(sendErr);
|
|
328
|
+
}
|
|
341
329
|
}
|
|
342
330
|
} finally {
|
|
343
331
|
if (typingTimeout) clearTimeout(typingTimeout);
|