@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
|
@@ -16,240 +16,256 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
16
16
|
return { file: "true" };
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!utils.isReadableStream(attachments[i])) {
|
|
25
|
-
throw { error: "Attachment should be a readable stream and not " + utils.getType(attachments[i]) + "." };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (i > 0) {
|
|
29
|
-
await globalAntiSuspension.addSmartDelay();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
var form = {
|
|
33
|
-
upload_1024: attachments[i],
|
|
34
|
-
...detectAttachmentType(attachments[i]),
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const upload = await defaultFuncs
|
|
38
|
-
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, { ...ctx, requestThreadID: String(ctx._lastThreadHint || "") })
|
|
39
|
-
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
40
|
-
.then(resData => {
|
|
41
|
-
if (resData.error) throw resData;
|
|
42
|
-
return resData.payload.metadata[0];
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
uploads.push(upload);
|
|
19
|
+
async function uploadAttachments(attachments) {
|
|
20
|
+
const uploads = [];
|
|
21
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
22
|
+
if (!utils.isReadableStream(attachments[i])) {
|
|
23
|
+
throw new Error("Attachment should be a readable stream and not " + utils.getType(attachments[i]) + ".");
|
|
46
24
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
25
|
+
if (i > 0) {
|
|
26
|
+
await globalAntiSuspension.addSmartDelay();
|
|
27
|
+
}
|
|
28
|
+
const form = {
|
|
29
|
+
upload_1024: attachments[i],
|
|
30
|
+
...detectAttachmentType(attachments[i]),
|
|
31
|
+
};
|
|
32
|
+
const upload = await defaultFuncs
|
|
33
|
+
.postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {}, { ...ctx, requestThreadID: String(ctx._lastThreadHint || "") })
|
|
34
|
+
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
|
|
35
|
+
.then(resData => {
|
|
36
|
+
if (resData.error) throw resData;
|
|
37
|
+
return resData.payload.metadata[0];
|
|
38
|
+
});
|
|
39
|
+
uploads.push(upload);
|
|
51
40
|
}
|
|
41
|
+
return uploads;
|
|
52
42
|
}
|
|
53
43
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (typeof msg === 'object') {
|
|
69
|
-
if (msg.sticker) {
|
|
70
|
-
payload.send_type = 2;
|
|
71
|
-
payload.sticker_id = msg.sticker;
|
|
72
|
-
payload.text = null;
|
|
73
|
-
}
|
|
74
|
-
if (msg.attachment) {
|
|
75
|
-
payload.send_type = 3;
|
|
76
|
-
payload.attachment_fbids = Array.isArray(msg.attachment) ? msg.attachment : [msg.attachment];
|
|
77
|
-
}
|
|
78
|
-
if (msg.effect) {
|
|
79
|
-
const effectTag = String(msg.effect).toUpperCase().replace(/[\s\-]+/g, '_');
|
|
80
|
-
payload.lightweight_action_attached = {
|
|
81
|
-
messaging_tag: 'fb.messaging.effects.' + effectTag,
|
|
82
|
-
};
|
|
44
|
+
function buildMentionData(msg, baseBody) {
|
|
45
|
+
if (!Array.isArray(msg.mentions) || msg.mentions.length === 0) return null;
|
|
46
|
+
const ids = [], offsets = [], lengths = [], types = [];
|
|
47
|
+
let cursor = 0;
|
|
48
|
+
for (const mention of msg.mentions) {
|
|
49
|
+
const rawTag = String(mention.tag || "");
|
|
50
|
+
const displayName = rawTag.replace(/^@+/, "");
|
|
51
|
+
const start = Number.isInteger(mention.fromIndex) ? mention.fromIndex : cursor;
|
|
52
|
+
let index = baseBody.indexOf(rawTag, start);
|
|
53
|
+
let adjustment = 0;
|
|
54
|
+
if (index === -1) {
|
|
55
|
+
index = baseBody.indexOf(displayName, start);
|
|
56
|
+
} else {
|
|
57
|
+
adjustment = rawTag.length - displayName.length;
|
|
83
58
|
}
|
|
59
|
+
if (index < 0) { index = 0; adjustment = 0; }
|
|
60
|
+
const offset = index + adjustment;
|
|
61
|
+
ids.push(String(mention.id || 0));
|
|
62
|
+
offsets.push(offset);
|
|
63
|
+
lengths.push(displayName.length);
|
|
64
|
+
types.push("p");
|
|
65
|
+
cursor = offset + displayName.length;
|
|
84
66
|
}
|
|
85
|
-
return
|
|
67
|
+
return {
|
|
68
|
+
mention_ids: ids.join(","),
|
|
69
|
+
mention_offsets: offsets.join(","),
|
|
70
|
+
mention_lengths: lengths.join(","),
|
|
71
|
+
mention_types: types.join(","),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hasLinks(text) {
|
|
76
|
+
return /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(text);
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
function extractIdsFromPayload(payload) {
|
|
89
80
|
let messageID = null;
|
|
90
81
|
let threadID = null;
|
|
91
82
|
function walk(n) {
|
|
92
|
-
if (Array.isArray(n))
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
for (const x of n) walk(x);
|
|
83
|
+
if (!Array.isArray(n)) return;
|
|
84
|
+
if (n[0] === 5 && (n[1] === "replaceOptimsiticMessage" || n[1] === "replaceOptimisticMessage")) {
|
|
85
|
+
messageID = String(n[3]);
|
|
86
|
+
}
|
|
87
|
+
if (n[0] === 5 && n[1] === "writeCTAIdToThreadsTable") {
|
|
88
|
+
const a = n[2];
|
|
89
|
+
if (Array.isArray(a) && a[0] === 19) threadID = String(a[1]);
|
|
101
90
|
}
|
|
91
|
+
for (const x of n) walk(x);
|
|
102
92
|
}
|
|
103
93
|
walk(payload?.step);
|
|
104
94
|
return { threadID, messageID };
|
|
105
95
|
}
|
|
106
96
|
|
|
107
|
-
function publishWithAck(content, reqID
|
|
97
|
+
function publishWithAck(content, reqID) {
|
|
108
98
|
return new Promise((resolve, reject) => {
|
|
109
99
|
if (!ctx.mqttClient || typeof ctx.mqttClient.on !== "function" || typeof ctx.mqttClient.publish !== "function") {
|
|
110
|
-
|
|
111
|
-
utils.error("sendMessageMqtt", err);
|
|
112
|
-
callback && callback(err);
|
|
113
|
-
return reject(err);
|
|
100
|
+
return reject(new Error("MQTT client is not initialized"));
|
|
114
101
|
}
|
|
115
|
-
|
|
116
102
|
if (typeof ctx.mqttClient.setMaxListeners === "function") {
|
|
117
103
|
ctx.mqttClient.setMaxListeners(0);
|
|
118
104
|
}
|
|
119
|
-
|
|
120
|
-
let
|
|
105
|
+
let settled = false;
|
|
106
|
+
let timer;
|
|
121
107
|
const cleanup = () => {
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
108
|
+
if (settled) return;
|
|
109
|
+
settled = true;
|
|
110
|
+
if (timer) clearTimeout(timer);
|
|
111
|
+
ctx.mqttClient.removeListener("message", onMessage);
|
|
125
112
|
};
|
|
126
|
-
const
|
|
113
|
+
const onMessage = (topic, message) => {
|
|
127
114
|
if (topic !== "/ls_resp") return;
|
|
128
|
-
let
|
|
115
|
+
let parsed;
|
|
129
116
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} catch {
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
if (jsonMsg.request_id !== reqID) return;
|
|
136
|
-
const { threadID, messageID } = extractIdsFromPayload(jsonMsg.payload);
|
|
137
|
-
const result = { messageID, threadID };
|
|
117
|
+
parsed = JSON.parse(message.toString());
|
|
118
|
+
if (typeof parsed.payload === "string") parsed.payload = JSON.parse(parsed.payload);
|
|
119
|
+
} catch { return; }
|
|
120
|
+
if (parsed.request_id !== reqID) return;
|
|
121
|
+
const { threadID, messageID } = extractIdsFromPayload(parsed.payload);
|
|
138
122
|
cleanup();
|
|
139
|
-
|
|
140
|
-
resolve(result);
|
|
123
|
+
resolve({ messageID, threadID });
|
|
141
124
|
};
|
|
142
|
-
ctx.mqttClient.on("message",
|
|
143
|
-
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false }, err => {
|
|
144
|
-
if (err) {
|
|
145
|
-
cleanup();
|
|
146
|
-
callback && callback(err);
|
|
147
|
-
reject(err);
|
|
148
|
-
}
|
|
125
|
+
ctx.mqttClient.on("message", onMessage);
|
|
126
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false }, (err) => {
|
|
127
|
+
if (err) { cleanup(); reject(err); }
|
|
149
128
|
});
|
|
150
|
-
setTimeout(() => {
|
|
151
|
-
if (
|
|
129
|
+
timer = setTimeout(() => {
|
|
130
|
+
if (settled) return;
|
|
152
131
|
cleanup();
|
|
153
|
-
|
|
154
|
-
callback && callback(err);
|
|
155
|
-
reject(err);
|
|
132
|
+
reject({ error: "Timeout waiting for ACK" });
|
|
156
133
|
}, 15000);
|
|
157
134
|
});
|
|
158
135
|
}
|
|
159
136
|
|
|
160
137
|
return async (msg, threadID, replyToMessage, callback) => {
|
|
161
|
-
if (typeof msg !==
|
|
138
|
+
if (typeof msg !== "string" && typeof msg !== "object") {
|
|
162
139
|
throw new Error("Message should be of type string or object, not " + utils.getType(msg) + ".");
|
|
163
140
|
}
|
|
164
|
-
|
|
165
|
-
if (typeof threadID !== 'string' && typeof threadID !== 'number') {
|
|
141
|
+
if (typeof threadID !== "string" && typeof threadID !== "number") {
|
|
166
142
|
throw new Error("threadID must be a string or number.");
|
|
167
143
|
}
|
|
168
|
-
|
|
169
|
-
if (!callback && typeof threadID === "function") {
|
|
170
|
-
throw new Error("Pass a threadID as a second argument.");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
144
|
if (!callback && typeof replyToMessage === "function") {
|
|
174
145
|
callback = replyToMessage;
|
|
175
146
|
replyToMessage = null;
|
|
176
147
|
}
|
|
177
148
|
|
|
178
|
-
// Apply anti-suspension throttling and volume checks before every MQTT send
|
|
179
149
|
try {
|
|
180
|
-
await globalAntiSuspension.prepareBeforeMessage(String(threadID), typeof msg ===
|
|
181
|
-
} catch (
|
|
182
|
-
utils.warn("sendMessageMqtt", "Anti-suspension check raised:", suspErr && suspErr.message ? suspErr.message : suspErr);
|
|
183
|
-
}
|
|
150
|
+
await globalAntiSuspension.prepareBeforeMessage(String(threadID), typeof msg === "string" ? msg : (msg.body || ""));
|
|
151
|
+
} catch (_) {}
|
|
184
152
|
|
|
185
|
-
const
|
|
153
|
+
const normalized = typeof msg === "string" ? { body: msg } : msg;
|
|
154
|
+
const baseBody = normalized.body != null ? String(normalized.body) : "";
|
|
155
|
+
const epoch = (BigInt(Date.now()) << 22n).toString();
|
|
156
|
+
const requestId = Math.floor(100 + Math.random() * 900);
|
|
186
157
|
const otid = utils.generateOfflineThreadingID();
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
158
|
+
|
|
159
|
+
const payload0 = {
|
|
160
|
+
thread_id: String(threadID),
|
|
161
|
+
otid: otid.toString(),
|
|
162
|
+
source: 2097153,
|
|
163
|
+
send_type: 1,
|
|
164
|
+
sync_group: 1,
|
|
165
|
+
mark_thread_read: 1,
|
|
166
|
+
text: baseBody === "" ? null : baseBody,
|
|
167
|
+
initiating_source: 0,
|
|
168
|
+
skip_url_preview_gen: 0,
|
|
169
|
+
text_has_links: hasLinks(baseBody) ? 1 : 0,
|
|
170
|
+
multitab_env: 0,
|
|
171
|
+
metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } }),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (normalized.mentions && Array.isArray(normalized.mentions) && normalized.mentions.length > 0) {
|
|
175
|
+
const mentionData = buildMentionData(normalized, baseBody);
|
|
176
|
+
if (mentionData) payload0.mention_data = mentionData;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (normalized.sticker) {
|
|
180
|
+
payload0.send_type = 2;
|
|
181
|
+
payload0.sticker_id = normalized.sticker;
|
|
182
|
+
payload0.text = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (normalized.emoji) {
|
|
186
|
+
payload0.send_type = 1;
|
|
187
|
+
payload0.text = normalized.emoji;
|
|
188
|
+
const sizeMap = { small: 1, medium: 2, large: 3 };
|
|
189
|
+
const emojiSize = normalized.emojiSize;
|
|
190
|
+
payload0.hot_emoji_size = (typeof emojiSize === "number" ? Math.min(3, Math.max(1, emojiSize)) : sizeMap[emojiSize]) || 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (normalized.location && normalized.location.latitude != null && normalized.location.longitude != null) {
|
|
194
|
+
payload0.send_type = 1;
|
|
195
|
+
payload0.location_data = {
|
|
196
|
+
coordinates: { latitude: normalized.location.latitude, longitude: normalized.location.longitude },
|
|
197
|
+
is_current_location: Boolean(normalized.location.current),
|
|
198
|
+
is_live_location: Boolean(normalized.location.live),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (normalized.effect) {
|
|
203
|
+
const effectTag = String(normalized.effect).toUpperCase().replace(/[\s\-]+/g, "_");
|
|
204
|
+
payload0.lightweight_action_attached = {
|
|
205
|
+
message_id: otid.toString(),
|
|
206
|
+
messaging_tag: "fb.messaging.effects." + effectTag,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (replyToMessage || normalized.replyToMessage) {
|
|
211
|
+
const replyId = replyToMessage || normalized.replyToMessage;
|
|
212
|
+
payload0.reply_metadata = {
|
|
213
|
+
reply_source_id: replyId,
|
|
211
214
|
reply_source_type: 1,
|
|
212
215
|
reply_type: 0,
|
|
213
216
|
};
|
|
214
217
|
}
|
|
215
218
|
|
|
216
|
-
|
|
217
|
-
|
|
219
|
+
if (normalized.attachment) {
|
|
220
|
+
payload0.send_type = 3;
|
|
221
|
+
if (payload0.text === "") payload0.text = null;
|
|
222
|
+
const list = Array.isArray(normalized.attachment) ? normalized.attachment : [normalized.attachment];
|
|
223
|
+
ctx._lastThreadHint = threadID;
|
|
224
|
+
const files = await uploadAttachments(list.filter(a => utils.isReadableStream(a)));
|
|
225
|
+
payload0.attachment_fbids = files.map(f => String(Object.values(f)[0]));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const content = {
|
|
218
229
|
app_id: "2220391788200892",
|
|
219
230
|
payload: {
|
|
220
|
-
tasks
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
231
|
+
tasks: [
|
|
232
|
+
{
|
|
233
|
+
label: "46",
|
|
234
|
+
payload: payload0,
|
|
235
|
+
queue_name: String(threadID),
|
|
236
|
+
task_id: 400,
|
|
237
|
+
failure_count: null,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
label: "21",
|
|
241
|
+
payload: {
|
|
242
|
+
thread_id: String(threadID),
|
|
243
|
+
last_read_watermark_ts: Date.now(),
|
|
244
|
+
sync_group: 1,
|
|
245
|
+
},
|
|
246
|
+
queue_name: String(threadID),
|
|
247
|
+
task_id: 401,
|
|
248
|
+
failure_count: null,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
epoch_id: epoch,
|
|
252
|
+
version_id: "24804310205905615",
|
|
253
|
+
data_trace_id: `#${Buffer.from(String(Math.random())).toString("base64").replace(/=+$/g, "")}`,
|
|
224
254
|
},
|
|
225
|
-
request_id,
|
|
255
|
+
request_id: requestId,
|
|
226
256
|
type: 3,
|
|
227
257
|
};
|
|
228
258
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
ctx._lastThreadHint = threadID;
|
|
232
|
-
const files = await new Promise((resolve, reject) => {
|
|
233
|
-
uploadAttachment(
|
|
234
|
-
Array.isArray(msg.attachment) ? msg.attachment : [msg.attachment],
|
|
235
|
-
(err, files) => {
|
|
236
|
-
if (err) return reject(err);
|
|
237
|
-
return resolve(files);
|
|
238
|
-
}
|
|
239
|
-
);
|
|
240
|
-
});
|
|
241
|
-
form.payload.tasks[0].payload.attachment_fbids = files.map(file => Object.values(file)[0]);
|
|
242
|
-
} catch (err) {
|
|
243
|
-
utils.error("Attachment upload failed:", err);
|
|
244
|
-
throw err;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
259
|
+
content.payload.tasks = content.payload.tasks.map(t => ({ ...t, payload: JSON.stringify(t.payload) }));
|
|
260
|
+
content.payload = JSON.stringify(content.payload);
|
|
247
261
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
262
|
+
try {
|
|
263
|
+
const result = await publishWithAck(content, requestId);
|
|
264
|
+
if (callback) callback(undefined, result);
|
|
265
|
+
return result;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (callback) callback(err);
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
254
270
|
};
|
|
255
271
|
};
|
|
@@ -2,39 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
const utils = require('../utils');
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* @param {Object} defaultFuncs
|
|
7
|
-
* @param {Object} api
|
|
8
|
-
* @param {Object} ctx
|
|
9
|
-
*/
|
|
10
5
|
module.exports = function (defaultFuncs, api, ctx) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
return function sendTypingIndicator(sendTyping, threadID, callback) {
|
|
7
|
+
let resolveFunc, rejectFunc;
|
|
8
|
+
const returnPromise = new Promise((resolve, reject) => {
|
|
9
|
+
resolveFunc = resolve;
|
|
10
|
+
rejectFunc = reject;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!callback) {
|
|
14
|
+
callback = (err) => {
|
|
15
|
+
if (err) return rejectFunc(err);
|
|
16
|
+
resolveFunc(true);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!ctx.mqttClient || typeof ctx.mqttClient.publish !== "function") {
|
|
21
|
+
const err = new Error("You can only use sendTypingIndicator after you start listening.");
|
|
22
|
+
callback(err);
|
|
23
|
+
return returnPromise;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const threadIDs = Array.isArray(threadID) ? threadID : [threadID];
|
|
27
|
+
if (!threadIDs.length) {
|
|
28
|
+
const err = new Error("threadID is required");
|
|
29
|
+
callback(err);
|
|
30
|
+
return returnPromise;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
|
|
34
|
+
|
|
35
|
+
function buildPayload(tid) {
|
|
36
|
+
const isGroup = String(tid).length >= 16 ? 1 : 0;
|
|
37
|
+
return {
|
|
38
|
+
app_id: "772021112871879",
|
|
39
|
+
payload: JSON.stringify({
|
|
40
|
+
label: "3",
|
|
41
|
+
payload: JSON.stringify({
|
|
42
|
+
thread_key: Number.parseInt(String(tid), 10),
|
|
43
|
+
is_group_thread: isGroup,
|
|
44
|
+
is_typing: sendTyping ? 1 : 0,
|
|
45
|
+
attribution: 0,
|
|
46
|
+
sync_group: 1,
|
|
47
|
+
thread_type: isGroup ? 2 : 1,
|
|
48
|
+
}),
|
|
49
|
+
version: "8965252033599983",
|
|
50
|
+
}),
|
|
51
|
+
request_id: ++ctx.wsReqNumber,
|
|
52
|
+
type: 4,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const publishes = threadIDs.map(tid =>
|
|
57
|
+
new Promise((resolve, reject) => {
|
|
58
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(buildPayload(tid)), { qos: 1 }, (err) => {
|
|
59
|
+
if (err) return reject(err);
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
Promise.all(publishes)
|
|
66
|
+
.then(() => callback(null, true))
|
|
67
|
+
.catch(err => {
|
|
68
|
+
utils.error("sendTypingIndicator", err);
|
|
69
|
+
callback(err);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return returnPromise;
|
|
73
|
+
};
|
|
40
74
|
};
|