@dongdev/fca-unofficial 3.0.31 → 4.0.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/LICENSE +191 -0
- package/README.md +224 -406
- package/dist/index.d.mts +1241 -0
- package/dist/index.d.ts +1241 -0
- package/dist/index.js +27749 -0
- package/dist/index.mjs +27713 -0
- package/docs/ARCHITECTURE.md +467 -0
- package/docs/DOCS.md +686 -0
- package/fca-config.example.json +33 -0
- package/package.json +32 -22
- package/test/fca.test.cjs +533 -0
- package/CHANGELOG.md +0 -296
- package/DOCS.md +0 -2712
- package/func/checkUpdate.js +0 -222
- package/func/logAdapter.js +0 -33
- package/func/logger.js +0 -48
- package/index.d.ts +0 -751
- package/index.js +0 -8
- package/module/config.js +0 -40
- package/module/login.js +0 -133
- package/module/loginHelper.js +0 -1296
- package/module/options.js +0 -44
- package/src/api/action/addExternalModule.js +0 -25
- package/src/api/action/changeAvatar.js +0 -137
- package/src/api/action/changeBio.js +0 -75
- package/src/api/action/enableAutoSaveAppState.js +0 -73
- package/src/api/action/getCurrentUserID.js +0 -7
- package/src/api/action/handleFriendRequest.js +0 -57
- package/src/api/action/logout.js +0 -76
- package/src/api/action/refreshFb_dtsg.js +0 -48
- package/src/api/action/setPostReaction.js +0 -106
- package/src/api/action/unfriend.js +0 -54
- package/src/api/http/httpGet.js +0 -46
- package/src/api/http/httpPost.js +0 -52
- package/src/api/http/postFormData.js +0 -47
- package/src/api/messaging/addUserToGroup.js +0 -68
- package/src/api/messaging/changeAdminStatus.js +0 -126
- package/src/api/messaging/changeArchivedStatus.js +0 -55
- package/src/api/messaging/changeBlockedStatus.js +0 -48
- package/src/api/messaging/changeGroupImage.js +0 -91
- package/src/api/messaging/changeNickname.js +0 -70
- package/src/api/messaging/changeThreadColor.js +0 -79
- package/src/api/messaging/changeThreadEmoji.js +0 -111
- package/src/api/messaging/createNewGroup.js +0 -88
- package/src/api/messaging/createPoll.js +0 -46
- package/src/api/messaging/createThemeAI.js +0 -98
- package/src/api/messaging/deleteMessage.js +0 -136
- package/src/api/messaging/deleteThread.js +0 -56
- package/src/api/messaging/editMessage.js +0 -68
- package/src/api/messaging/forwardAttachment.js +0 -57
- package/src/api/messaging/getEmojiUrl.js +0 -29
- package/src/api/messaging/getFriendsList.js +0 -82
- package/src/api/messaging/getMessage.js +0 -829
- package/src/api/messaging/getThemePictures.js +0 -62
- package/src/api/messaging/handleMessageRequest.js +0 -65
- package/src/api/messaging/markAsDelivered.js +0 -57
- package/src/api/messaging/markAsRead.js +0 -88
- package/src/api/messaging/markAsReadAll.js +0 -49
- package/src/api/messaging/markAsSeen.js +0 -61
- package/src/api/messaging/muteThread.js +0 -50
- package/src/api/messaging/removeUserFromGroup.js +0 -62
- package/src/api/messaging/resolvePhotoUrl.js +0 -43
- package/src/api/messaging/scheduler.js +0 -264
- package/src/api/messaging/searchForThread.js +0 -53
- package/src/api/messaging/sendMessage.js +0 -270
- package/src/api/messaging/sendTypingIndicator.js +0 -74
- package/src/api/messaging/setMessageReaction.js +0 -90
- package/src/api/messaging/setTitle.js +0 -124
- package/src/api/messaging/shareContact.js +0 -49
- package/src/api/messaging/threadColors.js +0 -128
- package/src/api/messaging/unsendMessage.js +0 -81
- package/src/api/messaging/uploadAttachment.js +0 -492
- package/src/api/socket/core/connectMqtt.js +0 -258
- package/src/api/socket/core/emitAuth.js +0 -103
- package/src/api/socket/core/getSeqID.js +0 -320
- package/src/api/socket/core/getTaskResponseData.js +0 -25
- package/src/api/socket/core/parseDelta.js +0 -377
- package/src/api/socket/detail/buildStream.js +0 -215
- package/src/api/socket/detail/constants.js +0 -28
- package/src/api/socket/listenMqtt.js +0 -377
- package/src/api/socket/middleware/index.js +0 -216
- package/src/api/threads/getThreadHistory.js +0 -664
- package/src/api/threads/getThreadInfo.js +0 -296
- package/src/api/threads/getThreadList.js +0 -293
- package/src/api/threads/getThreadPictures.js +0 -78
- package/src/api/users/getUserID.js +0 -65
- package/src/api/users/getUserInfo.js +0 -402
- package/src/api/users/getUserInfoV2.js +0 -134
- package/src/core/sendReqMqtt.js +0 -96
- package/src/database/helpers.js +0 -53
- package/src/database/models/index.js +0 -88
- package/src/database/models/thread.js +0 -50
- package/src/database/models/user.js +0 -46
- package/src/database/threadData.js +0 -94
- package/src/database/userData.js +0 -98
- package/src/remote/remoteClient.js +0 -123
- package/src/utils/broadcast.js +0 -51
- package/src/utils/client.js +0 -10
- package/src/utils/constants.js +0 -23
- package/src/utils/cookies.js +0 -68
- package/src/utils/format/attachment.js +0 -357
- package/src/utils/format/cookie.js +0 -9
- package/src/utils/format/date.js +0 -50
- package/src/utils/format/decode.js +0 -44
- package/src/utils/format/delta.js +0 -194
- package/src/utils/format/ids.js +0 -64
- package/src/utils/format/index.js +0 -64
- package/src/utils/format/message.js +0 -88
- package/src/utils/format/presence.js +0 -132
- package/src/utils/format/readTyp.js +0 -44
- package/src/utils/format/thread.js +0 -42
- package/src/utils/format/utils.js +0 -141
- package/src/utils/headers.js +0 -115
- package/src/utils/loginParser/autoLogin.js +0 -125
- package/src/utils/loginParser/helpers.js +0 -43
- package/src/utils/loginParser/index.js +0 -10
- package/src/utils/loginParser/parseAndCheckLogin.js +0 -220
- package/src/utils/loginParser/textUtils.js +0 -28
- package/src/utils/request/client.js +0 -26
- package/src/utils/request/config.js +0 -23
- package/src/utils/request/defaults.js +0 -46
- package/src/utils/request/helpers.js +0 -46
- package/src/utils/request/index.js +0 -17
- package/src/utils/request/methods.js +0 -163
- package/src/utils/request/proxy.js +0 -21
- package/src/utils/request/retry.js +0 -77
- package/src/utils/request/sanitize.js +0 -49
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const logger = require("../../../func/logger");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Message Scheduler System
|
|
6
|
-
* Allows scheduling messages to be sent at a specific time in the future
|
|
7
|
-
*/
|
|
8
|
-
module.exports = function (defaultFuncs, api, ctx) {
|
|
9
|
-
// Initialize scheduler on first call
|
|
10
|
-
if (!ctx._scheduler) {
|
|
11
|
-
ctx._scheduler = createSchedulerInstance(api);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return ctx._scheduler;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function createSchedulerInstance(api) {
|
|
18
|
-
const scheduledMessages = new Map(); // Map<id, ScheduledMessage>
|
|
19
|
-
let nextId = 1;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Scheduled Message Object
|
|
23
|
-
* @typedef {Object} ScheduledMessage
|
|
24
|
-
* @property {string} id - Unique identifier
|
|
25
|
-
* @property {string|Object} message - Message content
|
|
26
|
-
* @property {string|string[]} threadID - Target thread ID(s)
|
|
27
|
-
* @property {number} timestamp - When to send (Unix timestamp in ms)
|
|
28
|
-
* @property {number} createdAt - When scheduled (Unix timestamp in ms)
|
|
29
|
-
* @property {Object} options - Additional options (replyMessageID, isGroup, etc.)
|
|
30
|
-
* @property {Function} callback - Optional callback when sent
|
|
31
|
-
* @property {NodeJS.Timeout} timeout - Timeout reference
|
|
32
|
-
* @property {boolean} cancelled - Whether cancelled
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Schedule a message to be sent at a specific time
|
|
37
|
-
* @param {string|Object} message - Message content
|
|
38
|
-
* @param {string|string[]} threadID - Target thread ID(s)
|
|
39
|
-
* @param {Date|number|string} when - When to send (Date, timestamp, or ISO string)
|
|
40
|
-
* @param {Object} [options] - Additional options
|
|
41
|
-
* @param {string} [options.replyMessageID] - Message ID to reply to
|
|
42
|
-
* @param {boolean} [options.isGroup] - Whether it's a group chat
|
|
43
|
-
* @param {Function} [options.callback] - Callback when sent
|
|
44
|
-
* @returns {string} Scheduled message ID
|
|
45
|
-
*/
|
|
46
|
-
function scheduleMessage(message, threadID, when, options = {}) {
|
|
47
|
-
let timestamp;
|
|
48
|
-
|
|
49
|
-
// Parse when parameter
|
|
50
|
-
if (when instanceof Date) {
|
|
51
|
-
timestamp = when.getTime();
|
|
52
|
-
} else if (typeof when === "number") {
|
|
53
|
-
timestamp = when;
|
|
54
|
-
} else if (typeof when === "string") {
|
|
55
|
-
timestamp = new Date(when).getTime();
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error("Invalid 'when' parameter. Must be Date, number (timestamp), or ISO string");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Validate timestamp
|
|
61
|
-
if (isNaN(timestamp)) {
|
|
62
|
-
throw new Error("Invalid date/time");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const now = Date.now();
|
|
66
|
-
if (timestamp <= now) {
|
|
67
|
-
throw new Error("Scheduled time must be in the future");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const id = `scheduled_${nextId++}_${Date.now()}`;
|
|
71
|
-
const delay = timestamp - now;
|
|
72
|
-
|
|
73
|
-
// Create scheduled message object
|
|
74
|
-
const scheduled = {
|
|
75
|
-
id,
|
|
76
|
-
message,
|
|
77
|
-
threadID,
|
|
78
|
-
timestamp,
|
|
79
|
-
createdAt: now,
|
|
80
|
-
options: {
|
|
81
|
-
replyMessageID: options.replyMessageID,
|
|
82
|
-
isGroup: options.isGroup,
|
|
83
|
-
callback: options.callback
|
|
84
|
-
},
|
|
85
|
-
cancelled: false
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// Set timeout to send message
|
|
89
|
-
scheduled.timeout = setTimeout(() => {
|
|
90
|
-
if (scheduled.cancelled) return;
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
logger(`Sending scheduled message ${id}`, "info");
|
|
94
|
-
|
|
95
|
-
// Send message
|
|
96
|
-
api.sendMessage(
|
|
97
|
-
message,
|
|
98
|
-
threadID,
|
|
99
|
-
scheduled.options.callback || (() => {}),
|
|
100
|
-
scheduled.options.replyMessageID,
|
|
101
|
-
scheduled.options.isGroup
|
|
102
|
-
).then(() => {
|
|
103
|
-
logger(`Scheduled message ${id} sent successfully`, "info");
|
|
104
|
-
scheduledMessages.delete(id);
|
|
105
|
-
}).catch(err => {
|
|
106
|
-
logger(`Error sending scheduled message ${id}: ${err && err.message ? err.message : String(err)}`, "error");
|
|
107
|
-
if (scheduled.options.callback) {
|
|
108
|
-
scheduled.options.callback(err);
|
|
109
|
-
}
|
|
110
|
-
scheduledMessages.delete(id);
|
|
111
|
-
});
|
|
112
|
-
} catch (err) {
|
|
113
|
-
logger(`Error in scheduled message ${id}: ${err && err.message ? err.message : String(err)}`, "error");
|
|
114
|
-
scheduledMessages.delete(id);
|
|
115
|
-
}
|
|
116
|
-
}, delay);
|
|
117
|
-
|
|
118
|
-
scheduledMessages.set(id, scheduled);
|
|
119
|
-
logger(`Message scheduled: ${id} (in ${Math.round(delay / 1000)}s)`, "info");
|
|
120
|
-
|
|
121
|
-
return id;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Cancel a scheduled message
|
|
126
|
-
* @param {string} id - Scheduled message ID
|
|
127
|
-
* @returns {boolean} True if cancelled, false if not found
|
|
128
|
-
*/
|
|
129
|
-
function cancelScheduledMessage(id) {
|
|
130
|
-
const scheduled = scheduledMessages.get(id);
|
|
131
|
-
if (!scheduled) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (scheduled.cancelled) {
|
|
136
|
-
return false; // Already cancelled
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
clearTimeout(scheduled.timeout);
|
|
140
|
-
scheduled.cancelled = true;
|
|
141
|
-
scheduledMessages.delete(id);
|
|
142
|
-
logger(`Scheduled message ${id} cancelled`, "info");
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Get scheduled message info
|
|
148
|
-
* @param {string} id - Scheduled message ID
|
|
149
|
-
* @returns {ScheduledMessage|null} Scheduled message or null if not found
|
|
150
|
-
*/
|
|
151
|
-
function getScheduledMessage(id) {
|
|
152
|
-
const scheduled = scheduledMessages.get(id);
|
|
153
|
-
if (!scheduled || scheduled.cancelled) {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Return a copy without internal properties
|
|
158
|
-
return {
|
|
159
|
-
id: scheduled.id,
|
|
160
|
-
message: scheduled.message,
|
|
161
|
-
threadID: scheduled.threadID,
|
|
162
|
-
timestamp: scheduled.timestamp,
|
|
163
|
-
createdAt: scheduled.createdAt,
|
|
164
|
-
options: { ...scheduled.options },
|
|
165
|
-
timeUntilSend: scheduled.timestamp - Date.now()
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* List all scheduled messages
|
|
171
|
-
* @returns {ScheduledMessage[]} Array of scheduled messages
|
|
172
|
-
*/
|
|
173
|
-
function listScheduledMessages() {
|
|
174
|
-
const now = Date.now();
|
|
175
|
-
const list = [];
|
|
176
|
-
|
|
177
|
-
for (const scheduled of scheduledMessages.values()) {
|
|
178
|
-
if (scheduled.cancelled) continue;
|
|
179
|
-
|
|
180
|
-
list.push({
|
|
181
|
-
id: scheduled.id,
|
|
182
|
-
message: scheduled.message,
|
|
183
|
-
threadID: scheduled.threadID,
|
|
184
|
-
timestamp: scheduled.timestamp,
|
|
185
|
-
createdAt: scheduled.createdAt,
|
|
186
|
-
options: { ...scheduled.options },
|
|
187
|
-
timeUntilSend: scheduled.timestamp - now
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Sort by timestamp
|
|
192
|
-
return list.sort((a, b) => a.timestamp - b.timestamp);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Cancel all scheduled messages
|
|
197
|
-
* @returns {number} Number of cancelled messages
|
|
198
|
-
*/
|
|
199
|
-
function cancelAllScheduledMessages() {
|
|
200
|
-
let count = 0;
|
|
201
|
-
for (const id of scheduledMessages.keys()) {
|
|
202
|
-
if (cancelScheduledMessage(id)) {
|
|
203
|
-
count++;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
logger(`Cancelled ${count} scheduled messages`, "info");
|
|
207
|
-
return count;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Get count of scheduled messages
|
|
212
|
-
* @returns {number} Count
|
|
213
|
-
*/
|
|
214
|
-
function getScheduledCount() {
|
|
215
|
-
return scheduledMessages.size;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Clear expired/cancelled messages from memory
|
|
220
|
-
*/
|
|
221
|
-
function cleanup() {
|
|
222
|
-
const now = Date.now();
|
|
223
|
-
let cleaned = 0;
|
|
224
|
-
|
|
225
|
-
for (const [id, scheduled] of scheduledMessages.entries()) {
|
|
226
|
-
if (scheduled.cancelled || scheduled.timestamp < now) {
|
|
227
|
-
scheduledMessages.delete(id);
|
|
228
|
-
cleaned++;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (cleaned > 0) {
|
|
233
|
-
logger(`Cleaned up ${cleaned} scheduled messages`, "info");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Auto cleanup every 5 minutes
|
|
238
|
-
const cleanupInterval = setInterval(cleanup, 5 * 60 * 1000);
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Destroy scheduler and cleanup all resources
|
|
242
|
-
* @returns {number} Number of cancelled messages
|
|
243
|
-
*/
|
|
244
|
-
function destroy() {
|
|
245
|
-
clearInterval(cleanupInterval);
|
|
246
|
-
const count = cancelAllScheduledMessages();
|
|
247
|
-
logger("Scheduler destroyed and all resources cleaned up", "info");
|
|
248
|
-
return count;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Return scheduler API
|
|
252
|
-
return {
|
|
253
|
-
scheduleMessage,
|
|
254
|
-
cancelScheduledMessage,
|
|
255
|
-
getScheduledMessage,
|
|
256
|
-
listScheduledMessages,
|
|
257
|
-
cancelAllScheduledMessages,
|
|
258
|
-
getScheduledCount,
|
|
259
|
-
cleanup,
|
|
260
|
-
destroy,
|
|
261
|
-
// Cleanup interval reference for manual cleanup if needed
|
|
262
|
-
_cleanupInterval: cleanupInterval
|
|
263
|
-
};
|
|
264
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { parseAndCheckLogin } = require("../../utils/client");
|
|
4
|
-
const { formatThread } = require("../../utils/format");
|
|
5
|
-
module.exports = function(defaultFuncs, api, ctx) {
|
|
6
|
-
return function searchForThread(name, callback) {
|
|
7
|
-
let resolveFunc = function() {};
|
|
8
|
-
let rejectFunc = function() {};
|
|
9
|
-
const returnPromise = new Promise(function(resolve, reject) {
|
|
10
|
-
resolveFunc = resolve;
|
|
11
|
-
rejectFunc = reject;
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
if (!callback) {
|
|
15
|
-
callback = function(err, friendList) {
|
|
16
|
-
if (err) {
|
|
17
|
-
return rejectFunc(err);
|
|
18
|
-
}
|
|
19
|
-
resolveFunc(friendList);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const tmpForm = {
|
|
24
|
-
client: "web_messenger",
|
|
25
|
-
query: name,
|
|
26
|
-
offset: 0,
|
|
27
|
-
limit: 21,
|
|
28
|
-
index: "fbid"
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
defaultFuncs
|
|
32
|
-
.post(
|
|
33
|
-
"https://www.facebook.com/ajax/mercury/search_threads.php",
|
|
34
|
-
ctx.jar,
|
|
35
|
-
tmpForm
|
|
36
|
-
)
|
|
37
|
-
.then(parseAndCheckLogin(ctx, defaultFuncs))
|
|
38
|
-
.then(function(resData) {
|
|
39
|
-
if (resData.error) {
|
|
40
|
-
throw resData;
|
|
41
|
-
}
|
|
42
|
-
if (!resData.payload.mercury_payload.threads) {
|
|
43
|
-
return callback({ error: "Could not find thread `" + name + "`." });
|
|
44
|
-
}
|
|
45
|
-
return callback(
|
|
46
|
-
null,
|
|
47
|
-
resData.payload.mercury_payload.threads.map(formatThread)
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return returnPromise;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create by Donix-VN (DongDev)
|
|
3
|
-
* Don't change credit
|
|
4
|
-
* Send a message using MQTT.
|
|
5
|
-
* @param {string} text - The text of the message to send.
|
|
6
|
-
* @param {string} threadID - The ID of the thread to send the message to.
|
|
7
|
-
* @param {string} [msgReplace] - Optional. The message ID of the message to replace.
|
|
8
|
-
* @param {Array<Buffer|Stream>} [attachments] - Optional. The attachments to send with the message.
|
|
9
|
-
* @param {function} [callback] - Optional. The callback function to call when the message is sent.
|
|
10
|
-
* @returns {Promise<object>} A promise that resolves with the bodies of the sent message.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
"use strict";
|
|
14
|
-
const log = require("../../../func/logAdapter");
|
|
15
|
-
const { getType } = require("../../utils/format");
|
|
16
|
-
const { isReadableStream } = require("../../utils/constants");
|
|
17
|
-
const { generateOfflineThreadingID } = require("../../utils/format");
|
|
18
|
-
|
|
19
|
-
module.exports = function (defaultFuncs, api, ctx) {
|
|
20
|
-
const uploadAttachment = require("./uploadAttachment")(defaultFuncs, api, ctx);
|
|
21
|
-
const hasLinks = s => typeof s === "string" && /(https?:\/\/|www\.|t\.me\/|fb\.me\/|youtu\.be\/|facebook\.com\/|youtube\.com\/)/i.test(s);
|
|
22
|
-
const emojiSizes = { small: 1, medium: 2, large: 3 };
|
|
23
|
-
|
|
24
|
-
function extractIdsFromPayload(payload) {
|
|
25
|
-
let messageID = null;
|
|
26
|
-
let threadID = null;
|
|
27
|
-
function walk(n) {
|
|
28
|
-
if (Array.isArray(n)) {
|
|
29
|
-
if (n[0] === 5 && (n[1] === "replaceOptimsiticMessage" || n[1] === "replaceOptimisticMessage")) {
|
|
30
|
-
messageID = String(n[3]);
|
|
31
|
-
}
|
|
32
|
-
if (n[0] === 5 && n[1] === "writeCTAIdToThreadsTable") {
|
|
33
|
-
const a = n[2];
|
|
34
|
-
if (Array.isArray(a) && a[0] === 19) threadID = String(a[1]);
|
|
35
|
-
}
|
|
36
|
-
for (const x of n) walk(x);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
walk(payload?.step);
|
|
40
|
-
return { threadID, messageID };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function publishWithAck(content, text, reqID, callback) {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
// Ensure MQTT client is available before using it
|
|
46
|
-
if (!ctx.mqttClient || typeof ctx.mqttClient.on !== "function" || typeof ctx.mqttClient.publish !== "function") {
|
|
47
|
-
const err = new Error("MQTT client is not initialized");
|
|
48
|
-
log.error("sendMessageMqtt", err);
|
|
49
|
-
callback && callback(err);
|
|
50
|
-
return reject(err);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Remove default max listeners limit to avoid MaxListenersExceededWarning
|
|
54
|
-
if (typeof ctx.mqttClient.setMaxListeners === "function") {
|
|
55
|
-
ctx.mqttClient.setMaxListeners(0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let done = false;
|
|
59
|
-
const cleanup = () => {
|
|
60
|
-
if (done) return;
|
|
61
|
-
done = true;
|
|
62
|
-
ctx.mqttClient.removeListener("message", handleRes);
|
|
63
|
-
};
|
|
64
|
-
const handleRes = (topic, message) => {
|
|
65
|
-
if (topic !== "/ls_resp") return;
|
|
66
|
-
let jsonMsg;
|
|
67
|
-
try {
|
|
68
|
-
jsonMsg = JSON.parse(message.toString());
|
|
69
|
-
jsonMsg.payload = JSON.parse(jsonMsg.payload);
|
|
70
|
-
} catch {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
if (jsonMsg.request_id !== reqID) return;
|
|
74
|
-
const { threadID, messageID } = extractIdsFromPayload(jsonMsg.payload);
|
|
75
|
-
const bodies = { body: text || null, messageID, threadID };
|
|
76
|
-
cleanup();
|
|
77
|
-
callback && callback(undefined, bodies);
|
|
78
|
-
resolve(bodies);
|
|
79
|
-
};
|
|
80
|
-
ctx.mqttClient.on("message", handleRes);
|
|
81
|
-
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), { qos: 1, retain: false }, err => {
|
|
82
|
-
if (err) {
|
|
83
|
-
cleanup();
|
|
84
|
-
callback && callback(err);
|
|
85
|
-
reject(err);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
setTimeout(() => {
|
|
89
|
-
if (done) return;
|
|
90
|
-
cleanup();
|
|
91
|
-
const err = { error: "Timeout waiting for ACK" };
|
|
92
|
-
callback && callback(err);
|
|
93
|
-
reject(err);
|
|
94
|
-
}, 15000);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function buildMentionData(msg, baseBody) {
|
|
99
|
-
if (!msg.mentions || !Array.isArray(msg.mentions) || !msg.mentions.length) return null;
|
|
100
|
-
const base = typeof baseBody === "string" ? baseBody : "";
|
|
101
|
-
const ids = [];
|
|
102
|
-
const offsets = [];
|
|
103
|
-
const lengths = [];
|
|
104
|
-
const types = [];
|
|
105
|
-
let cursor = 0;
|
|
106
|
-
for (const m of msg.mentions) {
|
|
107
|
-
const raw = String(m.tag || "");
|
|
108
|
-
const name = raw.replace(/^@+/, "");
|
|
109
|
-
const start = Number.isInteger(m.fromIndex) ? m.fromIndex : cursor;
|
|
110
|
-
let idx = base.indexOf(raw, start);
|
|
111
|
-
let adj = 0;
|
|
112
|
-
if (idx === -1) {
|
|
113
|
-
idx = base.indexOf(name, start);
|
|
114
|
-
adj = 0;
|
|
115
|
-
} else {
|
|
116
|
-
adj = raw.length - name.length;
|
|
117
|
-
}
|
|
118
|
-
if (idx < 0) {
|
|
119
|
-
idx = 0;
|
|
120
|
-
adj = 0;
|
|
121
|
-
}
|
|
122
|
-
const off = idx + adj;
|
|
123
|
-
ids.push(String(m.id || 0));
|
|
124
|
-
offsets.push(off);
|
|
125
|
-
lengths.push(name.length);
|
|
126
|
-
types.push("p");
|
|
127
|
-
cursor = off + name.length;
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
mention_ids: ids.join(","),
|
|
131
|
-
mention_offsets: offsets.join(","),
|
|
132
|
-
mention_lengths: lengths.join(","),
|
|
133
|
-
mention_types: types.join(",")
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function coerceMsg(x) {
|
|
138
|
-
if (x == null) return { body: "" };
|
|
139
|
-
if (typeof x === "string") return { body: x };
|
|
140
|
-
if (typeof x === "object") return x;
|
|
141
|
-
return { body: String(x) };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return async function sendMessageMqtt(msg, threadID, callback, replyToMessage) {
|
|
145
|
-
if (typeof threadID === "function") return threadID({ error: "Pass a threadID as a second argument." });
|
|
146
|
-
if (typeof callback === "string" && !replyToMessage) {
|
|
147
|
-
replyToMessage = callback;
|
|
148
|
-
callback = () => { };
|
|
149
|
-
}
|
|
150
|
-
if (typeof callback !== "function") callback = () => { };
|
|
151
|
-
if (!threadID) {
|
|
152
|
-
const err = { error: "threadID is required" };
|
|
153
|
-
callback(err);
|
|
154
|
-
throw err;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const m = coerceMsg(msg);
|
|
158
|
-
const baseBody = m.body != null ? String(m.body) : "";
|
|
159
|
-
const reqID = Math.floor(100 + Math.random() * 900);
|
|
160
|
-
const epoch = (BigInt(Date.now()) << 22n).toString();
|
|
161
|
-
|
|
162
|
-
const payload0 = {
|
|
163
|
-
thread_id: String(threadID),
|
|
164
|
-
otid: generateOfflineThreadingID(),
|
|
165
|
-
source: 2097153,
|
|
166
|
-
send_type: 1,
|
|
167
|
-
sync_group: 1,
|
|
168
|
-
mark_thread_read: 1,
|
|
169
|
-
text: baseBody === "" ? null : baseBody,
|
|
170
|
-
initiating_source: 0,
|
|
171
|
-
skip_url_preview_gen: 0,
|
|
172
|
-
text_has_links: hasLinks(baseBody) ? 1 : 0,
|
|
173
|
-
multitab_env: 0,
|
|
174
|
-
metadata_dataclass: JSON.stringify({ media_accessibility_metadata: { alt_text: null } })
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const mentionData = buildMentionData(m, baseBody);
|
|
178
|
-
if (mentionData) payload0.mention_data = mentionData;
|
|
179
|
-
|
|
180
|
-
if (m.sticker) {
|
|
181
|
-
payload0.send_type = 2;
|
|
182
|
-
payload0.sticker_id = m.sticker;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (m.emoji) {
|
|
186
|
-
const size = !isNaN(m.emojiSize) ? Number(m.emojiSize) : emojiSizes[m.emojiSize || "small"] || 1;
|
|
187
|
-
payload0.send_type = 1;
|
|
188
|
-
payload0.text = m.emoji;
|
|
189
|
-
payload0.hot_emoji_size = Math.min(3, Math.max(1, size));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (m.location && m.location.latitude != null && m.location.longitude != null) {
|
|
193
|
-
payload0.send_type = 1;
|
|
194
|
-
payload0.location_data = {
|
|
195
|
-
coordinates: { latitude: m.location.latitude, longitude: m.location.longitude },
|
|
196
|
-
is_current_location: !!m.location.current,
|
|
197
|
-
is_live_location: !!m.location.live
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (replyToMessage) {
|
|
202
|
-
payload0.reply_metadata = { reply_source_id: replyToMessage, reply_source_type: 1, reply_type: 0 };
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (m.attachment) {
|
|
206
|
-
payload0.send_type = 3;
|
|
207
|
-
if (payload0.text === "") payload0.text = null;
|
|
208
|
-
payload0.attachment_fbids = [];
|
|
209
|
-
let list = m.attachment;
|
|
210
|
-
if (getType(list) !== "Array") list = [list];
|
|
211
|
-
const idsFromPairs = [];
|
|
212
|
-
const streams = [];
|
|
213
|
-
for (const it of list) {
|
|
214
|
-
if (Array.isArray(it) && typeof it[0] === "string") {
|
|
215
|
-
idsFromPairs.push(String(it[1]));
|
|
216
|
-
} else if (isReadableStream(it)) {
|
|
217
|
-
streams.push(it);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (idsFromPairs.length) payload0.attachment_fbids.push(...idsFromPairs);
|
|
221
|
-
if (streams.length) {
|
|
222
|
-
try {
|
|
223
|
-
const files = await uploadAttachment(streams);
|
|
224
|
-
for (const file of files) {
|
|
225
|
-
const key = Object.keys(file)[0];
|
|
226
|
-
payload0.attachment_fbids.push(file[key]);
|
|
227
|
-
}
|
|
228
|
-
} catch (err) {
|
|
229
|
-
log.error("uploadAttachment", err);
|
|
230
|
-
callback(err);
|
|
231
|
-
throw err;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const content = {
|
|
237
|
-
app_id: "2220391788200892",
|
|
238
|
-
payload: {
|
|
239
|
-
tasks: [
|
|
240
|
-
{
|
|
241
|
-
label: "46",
|
|
242
|
-
payload: payload0,
|
|
243
|
-
queue_name: String(threadID),
|
|
244
|
-
task_id: 400,
|
|
245
|
-
failure_count: null
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
label: "21",
|
|
249
|
-
payload: {
|
|
250
|
-
thread_id: String(threadID),
|
|
251
|
-
last_read_watermark_ts: Date.now(),
|
|
252
|
-
sync_group: 1
|
|
253
|
-
},
|
|
254
|
-
queue_name: String(threadID),
|
|
255
|
-
task_id: 401,
|
|
256
|
-
failure_count: null
|
|
257
|
-
}
|
|
258
|
-
],
|
|
259
|
-
epoch_id: epoch,
|
|
260
|
-
version_id: "24804310205905615",
|
|
261
|
-
data_trace_id: "#" + Buffer.from(String(Math.random())).toString("base64").replace(/=+$/g, "")
|
|
262
|
-
},
|
|
263
|
-
request_id: reqID,
|
|
264
|
-
type: 3
|
|
265
|
-
};
|
|
266
|
-
content.payload.tasks.forEach(t => (t.payload = JSON.stringify(t.payload)));
|
|
267
|
-
content.payload = JSON.stringify(content.payload);
|
|
268
|
-
return publishWithAck(content, baseBody, reqID, callback);
|
|
269
|
-
};
|
|
270
|
-
};
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const { getType } = require("../../utils/format");
|
|
3
|
-
module.exports = function (defaultFuncs, api, ctx) {
|
|
4
|
-
return function sendTyping(threadID, isTyping, options, callback) {
|
|
5
|
-
var resolveFunc = function () { };
|
|
6
|
-
var rejectFunc = function () { };
|
|
7
|
-
var returnPromise = new Promise(function (resolve, reject) {
|
|
8
|
-
resolveFunc = resolve;
|
|
9
|
-
rejectFunc = reject;
|
|
10
|
-
});
|
|
11
|
-
if (getType(options) == "Function" || getType(options) == "AsyncFunction") {
|
|
12
|
-
callback = options;
|
|
13
|
-
options = {};
|
|
14
|
-
}
|
|
15
|
-
options = options || {};
|
|
16
|
-
if (!callback || getType(callback) != "Function" && getType(callback) != "AsyncFunction") {
|
|
17
|
-
callback = function (err, data) {
|
|
18
|
-
if (err) return rejectFunc(err);
|
|
19
|
-
resolveFunc(data);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
if (!threadID) {
|
|
23
|
-
return callback(new Error("threadID is required"));
|
|
24
|
-
}
|
|
25
|
-
if (!ctx.mqttClient) {
|
|
26
|
-
const err = new Error("Not connected to MQTT");
|
|
27
|
-
callback(err);
|
|
28
|
-
rejectFunc(err);
|
|
29
|
-
return returnPromise;
|
|
30
|
-
}
|
|
31
|
-
if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
|
|
32
|
-
const threadIDs = Array.isArray(threadID) ? threadID : [threadID];
|
|
33
|
-
threadIDs.forEach(tid => {
|
|
34
|
-
var isGroupThread = getType(tid) === "Array" ? 0 : 1;
|
|
35
|
-
var threadType = isGroupThread ? 2 : 1;
|
|
36
|
-
var duration = options.duration || 10000;
|
|
37
|
-
var autoStop = options.autoStop !== false;
|
|
38
|
-
var attribution = options.type || 0;
|
|
39
|
-
const publishTypingStatus = (isTypingStatus) => {
|
|
40
|
-
ctx.mqttClient.publish('/ls_req',
|
|
41
|
-
JSON.stringify({
|
|
42
|
-
app_id: "772021112871879",
|
|
43
|
-
payload: JSON.stringify({
|
|
44
|
-
label: "3",
|
|
45
|
-
payload: JSON.stringify({
|
|
46
|
-
"thread_key": parseInt(tid),
|
|
47
|
-
"is_group_thread": isGroupThread,
|
|
48
|
-
"is_typing": isTypingStatus ? 1 : 0,
|
|
49
|
-
"attribution": attribution,
|
|
50
|
-
"sync_group": 1,
|
|
51
|
-
"thread_type": threadType
|
|
52
|
-
}),
|
|
53
|
-
version: "8965252033599983"
|
|
54
|
-
}),
|
|
55
|
-
request_id: ++ctx.wsReqNumber,
|
|
56
|
-
type: 4
|
|
57
|
-
}),
|
|
58
|
-
{
|
|
59
|
-
qos: 1,
|
|
60
|
-
retain: false,
|
|
61
|
-
}
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
publishTypingStatus(isTyping);
|
|
65
|
-
if (isTyping && autoStop) {
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
publishTypingStatus(false);
|
|
68
|
-
}, duration);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
callback(null, true);
|
|
72
|
-
return returnPromise;
|
|
73
|
-
};
|
|
74
|
-
};
|