meta-messenger.js 0.0.1
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 +661 -0
- package/README.md +166 -0
- package/dist/index.d.ts +1052 -0
- package/dist/index.js +1098 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
- package/scripts/build-go.mjs +30 -0
- package/scripts/detect-platform.mjs +32 -0
- package/scripts/download-prebuilt.mjs +88 -0
- package/scripts/package.mjs +6 -0
- package/scripts/postinstall.mjs +73 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/client.ts
|
|
5
|
+
import { EventEmitter } from "events";
|
|
6
|
+
|
|
7
|
+
// src/native.ts
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import koffi from "koffi";
|
|
12
|
+
function resolveDirname() {
|
|
13
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
}
|
|
15
|
+
__name(resolveDirname, "resolveDirname");
|
|
16
|
+
function libPath() {
|
|
17
|
+
const base = path.join(resolveDirname(), "..", "build");
|
|
18
|
+
if (process.platform === "win32") return path.join(base, "messagix.dll");
|
|
19
|
+
if (process.platform === "darwin") return path.join(base, "messagix.dylib");
|
|
20
|
+
return path.join(base, "messagix.so");
|
|
21
|
+
}
|
|
22
|
+
__name(libPath, "libPath");
|
|
23
|
+
var LIB_FILE = libPath();
|
|
24
|
+
if (!fs.existsSync(LIB_FILE)) {
|
|
25
|
+
throw new Error(`Native library not found at ${LIB_FILE}. Run: npm run build:go`);
|
|
26
|
+
}
|
|
27
|
+
var lib = koffi.load(LIB_FILE);
|
|
28
|
+
var mk = /* @__PURE__ */ __name((ret, name, args) => lib.func(name, ret, args), "mk");
|
|
29
|
+
var fns = {
|
|
30
|
+
MxFreeCString: mk("void", "MxFreeCString", ["char*"]),
|
|
31
|
+
MxNewClient: mk("str", "MxNewClient", ["str"]),
|
|
32
|
+
MxConnect: mk("str", "MxConnect", ["str"]),
|
|
33
|
+
MxConnectE2EE: mk("str", "MxConnectE2EE", ["str"]),
|
|
34
|
+
MxDisconnect: mk("str", "MxDisconnect", ["str"]),
|
|
35
|
+
MxIsConnected: mk("str", "MxIsConnected", ["str"]),
|
|
36
|
+
MxSendMessage: mk("str", "MxSendMessage", ["str"]),
|
|
37
|
+
MxSendReaction: mk("str", "MxSendReaction", ["str"]),
|
|
38
|
+
MxEditMessage: mk("str", "MxEditMessage", ["str"]),
|
|
39
|
+
MxUnsendMessage: mk("str", "MxUnsendMessage", ["str"]),
|
|
40
|
+
MxSendTyping: mk("str", "MxSendTyping", ["str"]),
|
|
41
|
+
MxMarkRead: mk("str", "MxMarkRead", ["str"]),
|
|
42
|
+
MxUploadMedia: mk("str", "MxUploadMedia", ["str"]),
|
|
43
|
+
MxSendImage: mk("str", "MxSendImage", ["str"]),
|
|
44
|
+
MxSendVideo: mk("str", "MxSendVideo", ["str"]),
|
|
45
|
+
MxSendVoice: mk("str", "MxSendVoice", ["str"]),
|
|
46
|
+
MxSendFile: mk("str", "MxSendFile", ["str"]),
|
|
47
|
+
MxSendSticker: mk("str", "MxSendSticker", ["str"]),
|
|
48
|
+
MxCreateThread: mk("str", "MxCreateThread", ["str"]),
|
|
49
|
+
MxGetUserInfo: mk("str", "MxGetUserInfo", ["str"]),
|
|
50
|
+
MxSetGroupPhoto: mk("str", "MxSetGroupPhoto", ["str"]),
|
|
51
|
+
MxRenameThread: mk("str", "MxRenameThread", ["str"]),
|
|
52
|
+
MxMuteThread: mk("str", "MxMuteThread", ["str"]),
|
|
53
|
+
MxDeleteThread: mk("str", "MxDeleteThread", ["str"]),
|
|
54
|
+
MxSearchUsers: mk("str", "MxSearchUsers", ["str"]),
|
|
55
|
+
MxPollEvents: mk("str", "MxPollEvents", ["str"]),
|
|
56
|
+
MxSendE2EEMessage: mk("str", "MxSendE2EEMessage", ["str"]),
|
|
57
|
+
MxSendE2EEReaction: mk("str", "MxSendE2EEReaction", ["str"]),
|
|
58
|
+
MxSendE2EETyping: mk("str", "MxSendE2EETyping", ["str"]),
|
|
59
|
+
MxEditE2EEMessage: mk("str", "MxEditE2EEMessage", ["str"]),
|
|
60
|
+
MxUnsendE2EEMessage: mk("str", "MxUnsendE2EEMessage", ["str"]),
|
|
61
|
+
MxGetDeviceData: mk("str", "MxGetDeviceData", ["str"]),
|
|
62
|
+
// E2EE Media functions
|
|
63
|
+
MxSendE2EEImage: mk("str", "MxSendE2EEImage", ["str"]),
|
|
64
|
+
MxSendE2EEVideo: mk("str", "MxSendE2EEVideo", ["str"]),
|
|
65
|
+
MxSendE2EEAudio: mk("str", "MxSendE2EEAudio", ["str"]),
|
|
66
|
+
MxSendE2EEDocument: mk("str", "MxSendE2EEDocument", ["str"]),
|
|
67
|
+
MxSendE2EESticker: mk("str", "MxSendE2EESticker", ["str"])
|
|
68
|
+
};
|
|
69
|
+
function call(fn, payload) {
|
|
70
|
+
const input = JSON.stringify(payload);
|
|
71
|
+
const bound = fns[fn];
|
|
72
|
+
const out = bound(input);
|
|
73
|
+
const data = JSON.parse(out);
|
|
74
|
+
if (!data.ok) throw new Error(data.error || "Unknown error");
|
|
75
|
+
return data.data;
|
|
76
|
+
}
|
|
77
|
+
__name(call, "call");
|
|
78
|
+
function callAsync(fn, payload) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
try {
|
|
82
|
+
const result = call(fn, payload);
|
|
83
|
+
resolve(result);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
reject(err);
|
|
86
|
+
}
|
|
87
|
+
}, 0);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
__name(callAsync, "callAsync");
|
|
91
|
+
var native = {
|
|
92
|
+
newClient: /* @__PURE__ */ __name((cfg) => call("MxNewClient", cfg), "newClient"),
|
|
93
|
+
connect: /* @__PURE__ */ __name((handle) => call("MxConnect", { handle }), "connect"),
|
|
94
|
+
connectE2EE: /* @__PURE__ */ __name((handle) => callAsync("MxConnectE2EE", { handle }), "connectE2EE"),
|
|
95
|
+
disconnect: /* @__PURE__ */ __name((handle) => call("MxDisconnect", { handle }), "disconnect"),
|
|
96
|
+
isConnected: /* @__PURE__ */ __name((handle) => call("MxIsConnected", { handle }), "isConnected"),
|
|
97
|
+
sendMessage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendMessage", { handle, options }), "sendMessage"),
|
|
98
|
+
sendReaction: /* @__PURE__ */ __name((handle, threadId, messageId, emoji) => callAsync("MxSendReaction", { handle, threadId, messageId, emoji }), "sendReaction"),
|
|
99
|
+
editMessage: /* @__PURE__ */ __name((handle, messageId, newText) => callAsync("MxEditMessage", { handle, messageId, newText }), "editMessage"),
|
|
100
|
+
unsendMessage: /* @__PURE__ */ __name((handle, messageId) => callAsync("MxUnsendMessage", { handle, messageId }), "unsendMessage"),
|
|
101
|
+
sendTyping: /* @__PURE__ */ __name((handle, threadId, isTyping, isGroup, threadType) => callAsync("MxSendTyping", { handle, threadId, isTyping, isGroup, threadType }), "sendTyping"),
|
|
102
|
+
markRead: /* @__PURE__ */ __name((handle, threadId, watermarkTs) => callAsync("MxMarkRead", { handle, threadId, watermarkTs: watermarkTs || 0 }), "markRead"),
|
|
103
|
+
uploadMedia: /* @__PURE__ */ __name((handle, options) => callAsync("MxUploadMedia", { handle, options }), "uploadMedia"),
|
|
104
|
+
sendImage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendImage", { handle, options }), "sendImage"),
|
|
105
|
+
sendVideo: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendVideo", { handle, options }), "sendVideo"),
|
|
106
|
+
sendVoice: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendVoice", { handle, options }), "sendVoice"),
|
|
107
|
+
sendFile: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendFile", { handle, options }), "sendFile"),
|
|
108
|
+
sendSticker: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendSticker", { handle, options }), "sendSticker"),
|
|
109
|
+
createThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxCreateThread", { handle, options }), "createThread"),
|
|
110
|
+
getUserInfo: /* @__PURE__ */ __name((handle, options) => callAsync("MxGetUserInfo", { handle, options }), "getUserInfo"),
|
|
111
|
+
setGroupPhoto: /* @__PURE__ */ __name((handle, threadId, data, mimeType) => callAsync("MxSetGroupPhoto", { handle, threadId, data, mimeType }), "setGroupPhoto"),
|
|
112
|
+
renameThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxRenameThread", { handle, options }), "renameThread"),
|
|
113
|
+
muteThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxMuteThread", { handle, options }), "muteThread"),
|
|
114
|
+
deleteThread: /* @__PURE__ */ __name((handle, options) => callAsync("MxDeleteThread", { handle, options }), "deleteThread"),
|
|
115
|
+
searchUsers: /* @__PURE__ */ __name((handle, options) => callAsync("MxSearchUsers", {
|
|
116
|
+
handle,
|
|
117
|
+
options
|
|
118
|
+
}), "searchUsers"),
|
|
119
|
+
pollEvents: /* @__PURE__ */ __name((handle, timeoutMs) => callAsync("MxPollEvents", { handle, timeoutMs }), "pollEvents"),
|
|
120
|
+
// E2EE functions
|
|
121
|
+
sendE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, text, replyToId, replyToSenderJid) => callAsync("MxSendE2EEMessage", {
|
|
122
|
+
handle,
|
|
123
|
+
chatJid,
|
|
124
|
+
text,
|
|
125
|
+
replyToId,
|
|
126
|
+
replyToSenderJid
|
|
127
|
+
}), "sendE2EEMessage"),
|
|
128
|
+
sendE2EEReaction: /* @__PURE__ */ __name((handle, chatJid, messageId, senderJid, emoji) => callAsync("MxSendE2EEReaction", { handle, chatJid, messageId, senderJid, emoji }), "sendE2EEReaction"),
|
|
129
|
+
sendE2EETyping: /* @__PURE__ */ __name((handle, chatJid, isTyping) => callAsync("MxSendE2EETyping", { handle, chatJid, isTyping }), "sendE2EETyping"),
|
|
130
|
+
editE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, messageId, newText) => callAsync("MxEditE2EEMessage", { handle, chatJid, messageId, newText }), "editE2EEMessage"),
|
|
131
|
+
unsendE2EEMessage: /* @__PURE__ */ __name((handle, chatJid, messageId) => callAsync("MxUnsendE2EEMessage", { handle, chatJid, messageId }), "unsendE2EEMessage"),
|
|
132
|
+
getDeviceData: /* @__PURE__ */ __name((handle) => call("MxGetDeviceData", { handle }), "getDeviceData"),
|
|
133
|
+
// E2EE Media functions
|
|
134
|
+
sendE2EEImage: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEImage", { handle, options }), "sendE2EEImage"),
|
|
135
|
+
sendE2EEVideo: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEVideo", { handle, options }), "sendE2EEVideo"),
|
|
136
|
+
sendE2EEAudio: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEAudio", { handle, options }), "sendE2EEAudio"),
|
|
137
|
+
sendE2EEDocument: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EEDocument", { handle, options }), "sendE2EEDocument"),
|
|
138
|
+
sendE2EESticker: /* @__PURE__ */ __name((handle, options) => callAsync("MxSendE2EESticker", { handle, options }), "sendE2EESticker"),
|
|
139
|
+
unload: /* @__PURE__ */ __name(() => lib.unload(), "unload")
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/client.ts
|
|
143
|
+
var Client = class extends EventEmitter {
|
|
144
|
+
static {
|
|
145
|
+
__name(this, "Client");
|
|
146
|
+
}
|
|
147
|
+
handle = null;
|
|
148
|
+
options;
|
|
149
|
+
cookies;
|
|
150
|
+
_user = null;
|
|
151
|
+
_initialData = null;
|
|
152
|
+
eventLoopRunning = false;
|
|
153
|
+
eventLoopAbort = null;
|
|
154
|
+
_socketReady = false;
|
|
155
|
+
_e2eeConnected = false;
|
|
156
|
+
_fullyReadyEmitted = false;
|
|
157
|
+
pendingEvents = [];
|
|
158
|
+
/**
|
|
159
|
+
* Create a new Messenger client
|
|
160
|
+
*
|
|
161
|
+
* @param cookies - Authentication cookies
|
|
162
|
+
* @param options - Client options
|
|
163
|
+
*/
|
|
164
|
+
constructor(cookies, options = {}) {
|
|
165
|
+
super();
|
|
166
|
+
this.cookies = cookies;
|
|
167
|
+
this.options = {
|
|
168
|
+
// ! todo: detect platform automatically
|
|
169
|
+
platform: "facebook",
|
|
170
|
+
logLevel: "none",
|
|
171
|
+
enableE2EE: true,
|
|
172
|
+
autoReconnect: true,
|
|
173
|
+
...options
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get the current user info
|
|
178
|
+
*/
|
|
179
|
+
get user() {
|
|
180
|
+
return this._user;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get the current user's Facebook ID
|
|
184
|
+
*/
|
|
185
|
+
get currentUserId() {
|
|
186
|
+
return this._user?.id ?? null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get initial sync data (threads and messages)
|
|
190
|
+
*/
|
|
191
|
+
get initialData() {
|
|
192
|
+
return this._initialData;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Check if client is fully ready (socket ready + E2EE connected if enabled)
|
|
196
|
+
*/
|
|
197
|
+
get isFullyReady() {
|
|
198
|
+
if (!this._socketReady) return false;
|
|
199
|
+
if (this.options.enableE2EE && !this._e2eeConnected) return false;
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if client is connected
|
|
204
|
+
*/
|
|
205
|
+
get isConnected() {
|
|
206
|
+
if (!this.handle) return false;
|
|
207
|
+
try {
|
|
208
|
+
const status = native.isConnected(this.handle);
|
|
209
|
+
return status.connected;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if E2EE is connected
|
|
216
|
+
*/
|
|
217
|
+
get isE2EEConnected() {
|
|
218
|
+
if (!this.handle) return false;
|
|
219
|
+
try {
|
|
220
|
+
const status = native.isConnected(this.handle);
|
|
221
|
+
return status.e2eeConnected;
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Connect to Messenger
|
|
228
|
+
*
|
|
229
|
+
* @returns User info and initial data
|
|
230
|
+
*/
|
|
231
|
+
async connect() {
|
|
232
|
+
const { handle } = native.newClient({
|
|
233
|
+
cookies: this.cookies,
|
|
234
|
+
platform: this.options.platform,
|
|
235
|
+
devicePath: this.options.devicePath,
|
|
236
|
+
deviceData: this.options.deviceData,
|
|
237
|
+
logLevel: this.options.logLevel
|
|
238
|
+
});
|
|
239
|
+
this.handle = handle;
|
|
240
|
+
const result = native.connect(handle);
|
|
241
|
+
this._user = result.user;
|
|
242
|
+
this._initialData = result.initialData;
|
|
243
|
+
this.startEventLoop();
|
|
244
|
+
if (this.options.enableE2EE) {
|
|
245
|
+
this.connectE2EE().catch((err) => {
|
|
246
|
+
this.emit("error", err);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
user: this._user,
|
|
251
|
+
initialData: this._initialData
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Connect E2EE (end-to-end encryption)
|
|
256
|
+
* @warn This Promise is not resolved after the connection setup is completed; instead, it is resolved after the function finishes executing.\
|
|
257
|
+
* You should not rely on this Promise to wait for the E2EE connection to be fully established.
|
|
258
|
+
*/
|
|
259
|
+
async connectE2EE() {
|
|
260
|
+
if (!this.handle) throw new Error("Not connected");
|
|
261
|
+
await native.connectE2EE(this.handle);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Disconnect from Messenger
|
|
265
|
+
*/
|
|
266
|
+
async disconnect() {
|
|
267
|
+
this.stopEventLoop();
|
|
268
|
+
if (this.handle) {
|
|
269
|
+
native.disconnect(this.handle);
|
|
270
|
+
this.handle = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Send a text message
|
|
275
|
+
*
|
|
276
|
+
* @param threadId - Thread ID to send to
|
|
277
|
+
* @param options - Message options (text, reply, mentions)
|
|
278
|
+
* @returns Send result with message ID
|
|
279
|
+
*/
|
|
280
|
+
async sendMessage(threadId, options) {
|
|
281
|
+
if (!this.handle) throw new Error("Not connected");
|
|
282
|
+
const opts = typeof options === "string" ? { text: options } : options;
|
|
283
|
+
return native.sendMessage(this.handle, {
|
|
284
|
+
threadId,
|
|
285
|
+
text: opts.text,
|
|
286
|
+
replyToId: opts.replyToId,
|
|
287
|
+
mentionIds: opts.mentions?.map((m) => m.userId),
|
|
288
|
+
mentionOffsets: opts.mentions?.map((m) => m.offset),
|
|
289
|
+
mentionLengths: opts.mentions?.map((m) => m.length)
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Send / Remove a reaction to a message
|
|
294
|
+
*
|
|
295
|
+
* @param threadId - Thread ID
|
|
296
|
+
* @param messageId - Message ID to react to
|
|
297
|
+
* @param emoji - Reaction emoji (to remove, simply omit this parameter)
|
|
298
|
+
*/
|
|
299
|
+
async sendReaction(threadId, messageId, emoji) {
|
|
300
|
+
if (!this.handle) throw new Error("Not connected");
|
|
301
|
+
await native.sendReaction(this.handle, threadId, messageId, emoji || "");
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Edit a message
|
|
305
|
+
*
|
|
306
|
+
* @param messageId - Message ID to edit
|
|
307
|
+
* @param newText - New text content
|
|
308
|
+
*/
|
|
309
|
+
async editMessage(messageId, newText) {
|
|
310
|
+
if (!this.handle) throw new Error("Not connected");
|
|
311
|
+
await native.editMessage(this.handle, messageId, newText);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Unsend/delete a message
|
|
315
|
+
*
|
|
316
|
+
* @param messageId - Message ID to unsend
|
|
317
|
+
*/
|
|
318
|
+
async unsendMessage(messageId) {
|
|
319
|
+
if (!this.handle) throw new Error("Not connected");
|
|
320
|
+
await native.unsendMessage(this.handle, messageId);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Send typing indicator
|
|
324
|
+
*
|
|
325
|
+
* @param threadId - Thread ID
|
|
326
|
+
* @param isTyping - Whether typing or not
|
|
327
|
+
* @param isGroup - Whether it's a group chat
|
|
328
|
+
*/
|
|
329
|
+
async sendTypingIndicator(threadId, isTyping = true, isGroup = false) {
|
|
330
|
+
if (!this.handle) throw new Error("Not connected");
|
|
331
|
+
await native.sendTyping(this.handle, threadId, isTyping, isGroup, isGroup ? 2 : 1);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Mark messages as read
|
|
335
|
+
*
|
|
336
|
+
* @param threadId - Thread ID
|
|
337
|
+
* @param watermarkTs - Timestamp to mark read up to (optional)
|
|
338
|
+
*/
|
|
339
|
+
async markAsRead(threadId, watermarkTs) {
|
|
340
|
+
if (!this.handle) throw new Error("Not connected");
|
|
341
|
+
await native.markRead(this.handle, threadId, watermarkTs);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Upload media to Messenger
|
|
345
|
+
*
|
|
346
|
+
* @param threadId - Thread ID
|
|
347
|
+
* @param data - File data as Buffer
|
|
348
|
+
* @param filename - Filename
|
|
349
|
+
* @param mimeType - MIME type
|
|
350
|
+
* @param isVoice - Whether it's a voice message
|
|
351
|
+
* @returns Upload result with Facebook ID
|
|
352
|
+
*/
|
|
353
|
+
async uploadMedia(threadId, data, filename, mimeType, isVoice = false) {
|
|
354
|
+
if (!this.handle) throw new Error("Not connected");
|
|
355
|
+
return native.uploadMedia(this.handle, {
|
|
356
|
+
threadId,
|
|
357
|
+
filename,
|
|
358
|
+
mimeType,
|
|
359
|
+
data: Array.from(data),
|
|
360
|
+
isVoice
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Send an image
|
|
365
|
+
*
|
|
366
|
+
* @param threadId - Thread ID
|
|
367
|
+
* @param data - Image data as Buffer
|
|
368
|
+
* @param filename - Filename
|
|
369
|
+
* @param caption - Optional caption
|
|
370
|
+
*/
|
|
371
|
+
async sendImage(threadId, data, filename, caption) {
|
|
372
|
+
if (!this.handle) throw new Error("Not connected");
|
|
373
|
+
return native.sendImage(this.handle, {
|
|
374
|
+
threadId,
|
|
375
|
+
data: Array.from(data),
|
|
376
|
+
filename,
|
|
377
|
+
caption
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Send a video
|
|
382
|
+
*
|
|
383
|
+
* @param threadId - Thread ID
|
|
384
|
+
* @param data - Video data as Buffer
|
|
385
|
+
* @param filename - Filename
|
|
386
|
+
* @param caption - Optional caption
|
|
387
|
+
*/
|
|
388
|
+
async sendVideo(threadId, data, filename, caption) {
|
|
389
|
+
if (!this.handle) throw new Error("Not connected");
|
|
390
|
+
return native.sendVideo(this.handle, {
|
|
391
|
+
threadId,
|
|
392
|
+
data: Array.from(data),
|
|
393
|
+
filename,
|
|
394
|
+
caption
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Send a voice message
|
|
399
|
+
*
|
|
400
|
+
* @param threadId - Thread ID
|
|
401
|
+
* @param data - Audio data as Buffer
|
|
402
|
+
* @param filename - Filename
|
|
403
|
+
*/
|
|
404
|
+
async sendVoice(threadId, data, filename) {
|
|
405
|
+
if (!this.handle) throw new Error("Not connected");
|
|
406
|
+
return native.sendVoice(this.handle, {
|
|
407
|
+
threadId,
|
|
408
|
+
data: Array.from(data),
|
|
409
|
+
filename
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Send a file
|
|
414
|
+
*
|
|
415
|
+
* @param threadId - Thread ID
|
|
416
|
+
* @param data - File data as Buffer
|
|
417
|
+
* @param filename - Filename
|
|
418
|
+
* @param mimeType - MIME type
|
|
419
|
+
* @param caption - Optional caption
|
|
420
|
+
*/
|
|
421
|
+
async sendFile(threadId, data, filename, mimeType, caption) {
|
|
422
|
+
if (!this.handle) throw new Error("Not connected");
|
|
423
|
+
return native.sendFile(this.handle, {
|
|
424
|
+
threadId,
|
|
425
|
+
data: Array.from(data),
|
|
426
|
+
filename,
|
|
427
|
+
mimeType,
|
|
428
|
+
caption
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Send a sticker
|
|
433
|
+
*
|
|
434
|
+
* @param threadId - Thread ID
|
|
435
|
+
* @param stickerId - Sticker ID
|
|
436
|
+
*/
|
|
437
|
+
async sendSticker(threadId, stickerId) {
|
|
438
|
+
if (!this.handle) throw new Error("Not connected");
|
|
439
|
+
return native.sendSticker(this.handle, { threadId, stickerId });
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Create a 1:1 thread with a user
|
|
443
|
+
*
|
|
444
|
+
* @param userId - User ID to create thread with
|
|
445
|
+
* @returns Created thread info
|
|
446
|
+
*/
|
|
447
|
+
async createThread(userId) {
|
|
448
|
+
if (!this.handle) throw new Error("Not connected");
|
|
449
|
+
return native.createThread(this.handle, { userId });
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get detailed information about a user
|
|
453
|
+
*
|
|
454
|
+
* @param userId - User ID
|
|
455
|
+
* @returns User info
|
|
456
|
+
*/
|
|
457
|
+
async getUserInfo(userId) {
|
|
458
|
+
if (!this.handle) throw new Error("Not connected");
|
|
459
|
+
return native.getUserInfo(this.handle, { userId });
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Set group photo/avatar
|
|
463
|
+
*
|
|
464
|
+
* @param threadId - Thread ID
|
|
465
|
+
* @param data - Image data as Buffer or base64 string
|
|
466
|
+
* @param mimeType - MIME type (e.g., 'image/jpeg', 'image/png')
|
|
467
|
+
*
|
|
468
|
+
* @warn Cannot remove group photo. Messenger web doesn't have a remove option?
|
|
469
|
+
*/
|
|
470
|
+
async setGroupPhoto(threadId, data, mimeType = "image/jpeg") {
|
|
471
|
+
if (!this.handle) throw new Error("Not connected");
|
|
472
|
+
const base64 = Buffer.isBuffer(data) ? data.toString("base64") : data;
|
|
473
|
+
await native.setGroupPhoto(this.handle, threadId, base64, mimeType);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Rename a group thread
|
|
477
|
+
*
|
|
478
|
+
* @param threadId - Thread ID
|
|
479
|
+
* @param newName - New name
|
|
480
|
+
*/
|
|
481
|
+
async renameThread(threadId, newName) {
|
|
482
|
+
if (!this.handle) throw new Error("Not connected");
|
|
483
|
+
native.renameThread(this.handle, { threadId, newName });
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Mute a thread
|
|
487
|
+
*
|
|
488
|
+
* @param threadId - Thread ID
|
|
489
|
+
* @param muteSeconds - Duration in seconds (-1 for forever, 0 to unmute)
|
|
490
|
+
*/
|
|
491
|
+
async muteThread(threadId, muteSeconds = -1) {
|
|
492
|
+
if (!this.handle) throw new Error("Not connected");
|
|
493
|
+
native.muteThread(this.handle, { threadId, muteSeconds });
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Unmute a thread
|
|
497
|
+
*
|
|
498
|
+
* @param threadId - Thread ID
|
|
499
|
+
*/
|
|
500
|
+
async unmuteThread(threadId) {
|
|
501
|
+
return this.muteThread(threadId, 0);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Delete a thread
|
|
505
|
+
*
|
|
506
|
+
* @param threadId - Thread ID
|
|
507
|
+
*/
|
|
508
|
+
async deleteThread(threadId) {
|
|
509
|
+
if (!this.handle) throw new Error("Not connected");
|
|
510
|
+
native.deleteThread(this.handle, { threadId });
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Search for users
|
|
514
|
+
*
|
|
515
|
+
* @param query - Search query
|
|
516
|
+
* @returns List of matching users
|
|
517
|
+
*/
|
|
518
|
+
async searchUsers(query) {
|
|
519
|
+
if (!this.handle) throw new Error("Not connected");
|
|
520
|
+
const result = await native.searchUsers(this.handle, { query });
|
|
521
|
+
return result.users;
|
|
522
|
+
}
|
|
523
|
+
// ========== E2EE Methods ==========
|
|
524
|
+
/**
|
|
525
|
+
* Send an E2EE message
|
|
526
|
+
*
|
|
527
|
+
* @param chatJid - Chat JID
|
|
528
|
+
* @param text - Message text
|
|
529
|
+
* @param options - Optional: replyToId and replyToSenderJid for replies
|
|
530
|
+
*/
|
|
531
|
+
async sendE2EEMessage(chatJid, text, options) {
|
|
532
|
+
if (!this.handle) throw new Error("Not connected");
|
|
533
|
+
return native.sendE2EEMessage(this.handle, chatJid, text, options?.replyToId, options?.replyToSenderJid);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Send / Remove an E2EE reaction
|
|
537
|
+
*
|
|
538
|
+
* @param chatJid - Chat JID
|
|
539
|
+
* @param messageId - Message ID
|
|
540
|
+
* @param senderJid - Sender JID
|
|
541
|
+
* @param emoji - Reaction emoji (To remove it, simply omit this parameter)
|
|
542
|
+
*/
|
|
543
|
+
async sendE2EEReaction(chatJid, messageId, senderJid, emoji) {
|
|
544
|
+
if (!this.handle) throw new Error("Not connected");
|
|
545
|
+
await native.sendE2EEReaction(this.handle, chatJid, messageId, senderJid, emoji || "");
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Send E2EE typing indicator
|
|
549
|
+
*
|
|
550
|
+
* @param chatJid - Chat JID
|
|
551
|
+
* @param isTyping - Whether typing
|
|
552
|
+
*/
|
|
553
|
+
async sendE2EETyping(chatJid, isTyping = true) {
|
|
554
|
+
if (!this.handle) throw new Error("Not connected");
|
|
555
|
+
await native.sendE2EETyping(this.handle, chatJid, isTyping);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Edit an E2EE message
|
|
559
|
+
*
|
|
560
|
+
* @param chatJid - Chat JID
|
|
561
|
+
* @param messageId - Message ID to edit
|
|
562
|
+
* @param newText - New message text
|
|
563
|
+
*/
|
|
564
|
+
async editE2EEMessage(chatJid, messageId, newText) {
|
|
565
|
+
if (!this.handle) throw new Error("Not connected");
|
|
566
|
+
await native.editE2EEMessage(this.handle, chatJid, messageId, newText);
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Unsend/delete an E2EE message
|
|
570
|
+
*
|
|
571
|
+
* @param chatJid - Chat JID
|
|
572
|
+
* @param messageId - Message ID to unsend
|
|
573
|
+
*/
|
|
574
|
+
async unsendE2EEMessage(chatJid, messageId) {
|
|
575
|
+
if (!this.handle) throw new Error("Not connected");
|
|
576
|
+
await native.unsendE2EEMessage(this.handle, chatJid, messageId);
|
|
577
|
+
}
|
|
578
|
+
// ========== E2EE Media Methods ==========
|
|
579
|
+
/**
|
|
580
|
+
* Send an E2EE image
|
|
581
|
+
*
|
|
582
|
+
* @param chatJid - Chat JID
|
|
583
|
+
* @param data - Image data as Buffer
|
|
584
|
+
* @param mimeType - MIME type (e.g., image/jpeg, image/png)
|
|
585
|
+
* @param options - Optional caption, dimensions, and reply options
|
|
586
|
+
*/
|
|
587
|
+
async sendE2EEImage(chatJid, data, mimeType = "image/jpeg", options) {
|
|
588
|
+
if (!this.handle) throw new Error("Not connected");
|
|
589
|
+
return native.sendE2EEImage(this.handle, {
|
|
590
|
+
chatJid,
|
|
591
|
+
data: Array.from(data),
|
|
592
|
+
mimeType,
|
|
593
|
+
caption: options?.caption,
|
|
594
|
+
width: options?.width,
|
|
595
|
+
height: options?.height,
|
|
596
|
+
replyToId: options?.replyToId,
|
|
597
|
+
replyToSenderJid: options?.replyToSenderJid
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Send an E2EE video
|
|
602
|
+
*
|
|
603
|
+
* @param chatJid - Chat JID
|
|
604
|
+
* @param data - Video data as Buffer
|
|
605
|
+
* @param mimeType - MIME type (default: video/mp4)
|
|
606
|
+
* @param options - Optional caption, dimensions, duration, and reply options
|
|
607
|
+
*/
|
|
608
|
+
async sendE2EEVideo(chatJid, data, mimeType = "video/mp4", options) {
|
|
609
|
+
if (!this.handle) throw new Error("Not connected");
|
|
610
|
+
return native.sendE2EEVideo(this.handle, {
|
|
611
|
+
chatJid,
|
|
612
|
+
data: Array.from(data),
|
|
613
|
+
mimeType,
|
|
614
|
+
caption: options?.caption,
|
|
615
|
+
width: options?.width,
|
|
616
|
+
height: options?.height,
|
|
617
|
+
duration: options?.duration,
|
|
618
|
+
replyToId: options?.replyToId,
|
|
619
|
+
replyToSenderJid: options?.replyToSenderJid
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Send an E2EE audio/voice message
|
|
624
|
+
*
|
|
625
|
+
* @param chatJid - Chat JID
|
|
626
|
+
* @param data - Audio data as Buffer
|
|
627
|
+
* @param mimeType - MIME type (default: audio/ogg)
|
|
628
|
+
* @param options - Optional PTT (push-to-talk/voice message), duration, and reply options
|
|
629
|
+
*/
|
|
630
|
+
async sendE2EEAudio(chatJid, data, mimeType = "audio/ogg", options) {
|
|
631
|
+
if (!this.handle) throw new Error("Not connected");
|
|
632
|
+
return native.sendE2EEAudio(this.handle, {
|
|
633
|
+
chatJid,
|
|
634
|
+
data: Array.from(data),
|
|
635
|
+
mimeType,
|
|
636
|
+
ptt: options?.ptt ?? false,
|
|
637
|
+
duration: options?.duration,
|
|
638
|
+
replyToId: options?.replyToId,
|
|
639
|
+
replyToSenderJid: options?.replyToSenderJid
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Send an E2EE document/file
|
|
644
|
+
*
|
|
645
|
+
* @param chatJid - Chat JID
|
|
646
|
+
* @param data - File data as Buffer
|
|
647
|
+
* @param filename - Filename
|
|
648
|
+
* @param mimeType - MIME type
|
|
649
|
+
* @param options - Optional reply options
|
|
650
|
+
*/
|
|
651
|
+
async sendE2EEDocument(chatJid, data, filename, mimeType, options) {
|
|
652
|
+
if (!this.handle) throw new Error("Not connected");
|
|
653
|
+
return native.sendE2EEDocument(this.handle, {
|
|
654
|
+
chatJid,
|
|
655
|
+
data: Array.from(data),
|
|
656
|
+
filename,
|
|
657
|
+
mimeType,
|
|
658
|
+
replyToId: options?.replyToId,
|
|
659
|
+
replyToSenderJid: options?.replyToSenderJid
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Send an E2EE sticker
|
|
664
|
+
*
|
|
665
|
+
* @param chatJid - Chat JID
|
|
666
|
+
* @param data - Sticker data as Buffer (WebP format)
|
|
667
|
+
* @param mimeType - MIME type (default: image/webp)
|
|
668
|
+
* @param options - Optional reply options
|
|
669
|
+
*/
|
|
670
|
+
async sendE2EESticker(chatJid, data, mimeType = "image/webp", options) {
|
|
671
|
+
if (!this.handle) throw new Error("Not connected");
|
|
672
|
+
return native.sendE2EESticker(this.handle, {
|
|
673
|
+
chatJid,
|
|
674
|
+
data: Array.from(data),
|
|
675
|
+
mimeType,
|
|
676
|
+
replyToId: options?.replyToId,
|
|
677
|
+
replyToSenderJid: options?.replyToSenderJid
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Get E2EE device data as JSON string
|
|
682
|
+
*
|
|
683
|
+
* Use this to persist device data externally (e.g., in a database)
|
|
684
|
+
*
|
|
685
|
+
* @returns Device data as JSON string
|
|
686
|
+
*/
|
|
687
|
+
getDeviceData() {
|
|
688
|
+
if (!this.handle) throw new Error("Not connected");
|
|
689
|
+
const result = native.getDeviceData(this.handle);
|
|
690
|
+
return result.deviceData;
|
|
691
|
+
}
|
|
692
|
+
startEventLoop() {
|
|
693
|
+
if (this.eventLoopRunning) return;
|
|
694
|
+
this.eventLoopRunning = true;
|
|
695
|
+
this.eventLoopAbort = new AbortController();
|
|
696
|
+
const loop = /* @__PURE__ */ __name(async () => {
|
|
697
|
+
while (this.eventLoopRunning && this.handle) {
|
|
698
|
+
try {
|
|
699
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
700
|
+
const event = await native.pollEvents(this.handle, 1e3);
|
|
701
|
+
if (!event || event.type === "timeout") continue;
|
|
702
|
+
if (event.type === "closed") {
|
|
703
|
+
this.eventLoopRunning = false;
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
this.handleEvent(event);
|
|
707
|
+
} catch (err) {
|
|
708
|
+
if (this.eventLoopRunning) {
|
|
709
|
+
this.emit("error", err);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}, "loop");
|
|
714
|
+
setImmediate(loop).unref();
|
|
715
|
+
}
|
|
716
|
+
stopEventLoop() {
|
|
717
|
+
this.eventLoopRunning = false;
|
|
718
|
+
this.eventLoopAbort?.abort();
|
|
719
|
+
this.eventLoopAbort = null;
|
|
720
|
+
}
|
|
721
|
+
checkFullyReady() {
|
|
722
|
+
if (this.isFullyReady && !this._fullyReadyEmitted) {
|
|
723
|
+
this._fullyReadyEmitted = true;
|
|
724
|
+
this.emit("fullyReady");
|
|
725
|
+
const pending = this.pendingEvents;
|
|
726
|
+
this.pendingEvents = [];
|
|
727
|
+
for (const event of pending) {
|
|
728
|
+
this.emitEvent(event);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
handleEvent(event) {
|
|
733
|
+
switch (event.type) {
|
|
734
|
+
// System events
|
|
735
|
+
case "ready":
|
|
736
|
+
this._socketReady = true;
|
|
737
|
+
this.emit("ready", event.data);
|
|
738
|
+
this.checkFullyReady();
|
|
739
|
+
break;
|
|
740
|
+
case "reconnected":
|
|
741
|
+
this.emit("reconnected");
|
|
742
|
+
break;
|
|
743
|
+
case "disconnected":
|
|
744
|
+
this._socketReady = false;
|
|
745
|
+
this._e2eeConnected = false;
|
|
746
|
+
this._fullyReadyEmitted = false;
|
|
747
|
+
this.pendingEvents = [];
|
|
748
|
+
this.emit("disconnected", event.data || {});
|
|
749
|
+
break;
|
|
750
|
+
case "error":
|
|
751
|
+
this.emit("error", new Error(event.data.message));
|
|
752
|
+
break;
|
|
753
|
+
case "e2eeConnected":
|
|
754
|
+
this._e2eeConnected = true;
|
|
755
|
+
this.emit("e2eeConnected");
|
|
756
|
+
this.checkFullyReady();
|
|
757
|
+
break;
|
|
758
|
+
case "deviceDataChanged":
|
|
759
|
+
this.emit("deviceDataChanged", event.data);
|
|
760
|
+
break;
|
|
761
|
+
// queue until fullyReady
|
|
762
|
+
case "message":
|
|
763
|
+
case "messageEdit":
|
|
764
|
+
case "messageUnsend":
|
|
765
|
+
case "reaction":
|
|
766
|
+
case "typing":
|
|
767
|
+
case "readReceipt":
|
|
768
|
+
case "e2eeMessage":
|
|
769
|
+
case "e2eeReaction":
|
|
770
|
+
case "e2eeReceipt":
|
|
771
|
+
if (this._fullyReadyEmitted) {
|
|
772
|
+
this.emitEvent(event);
|
|
773
|
+
} else {
|
|
774
|
+
this.pendingEvents.push(event);
|
|
775
|
+
}
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
emitEvent(event) {
|
|
780
|
+
switch (event.type) {
|
|
781
|
+
case "message":
|
|
782
|
+
this.emit("message", event.data);
|
|
783
|
+
break;
|
|
784
|
+
case "messageEdit":
|
|
785
|
+
this.emit("messageEdit", event.data);
|
|
786
|
+
break;
|
|
787
|
+
case "messageUnsend":
|
|
788
|
+
this.emit("messageUnsend", event.data);
|
|
789
|
+
break;
|
|
790
|
+
case "reaction":
|
|
791
|
+
this.emit("reaction", event.data);
|
|
792
|
+
break;
|
|
793
|
+
case "typing":
|
|
794
|
+
this.emit("typing", event.data);
|
|
795
|
+
break;
|
|
796
|
+
case "readReceipt":
|
|
797
|
+
this.emit("readReceipt", event.data);
|
|
798
|
+
break;
|
|
799
|
+
case "e2eeMessage":
|
|
800
|
+
this.emit("e2eeMessage", event.data);
|
|
801
|
+
break;
|
|
802
|
+
case "e2eeReaction":
|
|
803
|
+
this.emit("e2eeReaction", event.data);
|
|
804
|
+
break;
|
|
805
|
+
case "e2eeReceipt":
|
|
806
|
+
this.emit("e2eeReceipt", event.data);
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Unload the native library (for cleanup)
|
|
812
|
+
* @warn Any attempt to find or call a function from this library after unloading it will crash.
|
|
813
|
+
* @returns void
|
|
814
|
+
*/
|
|
815
|
+
unloadLibrary() {
|
|
816
|
+
if (this.handle) {
|
|
817
|
+
native.unload();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
// src/types.ts
|
|
823
|
+
var ThreadType = /* @__PURE__ */ ((ThreadType2) => {
|
|
824
|
+
ThreadType2[ThreadType2["ONE_TO_ONE"] = 1] = "ONE_TO_ONE";
|
|
825
|
+
ThreadType2[ThreadType2["GROUP"] = 2] = "GROUP";
|
|
826
|
+
ThreadType2[ThreadType2["PAGE"] = 3] = "PAGE";
|
|
827
|
+
ThreadType2[ThreadType2["MARKETPLACE"] = 4] = "MARKETPLACE";
|
|
828
|
+
ThreadType2[ThreadType2["ENCRYPTED_ONE_TO_ONE"] = 7] = "ENCRYPTED_ONE_TO_ONE";
|
|
829
|
+
ThreadType2[ThreadType2["ENCRYPTED_GROUP"] = 8] = "ENCRYPTED_GROUP";
|
|
830
|
+
return ThreadType2;
|
|
831
|
+
})(ThreadType || {});
|
|
832
|
+
|
|
833
|
+
// src/utils.ts
|
|
834
|
+
var Utils = class _Utils extends null {
|
|
835
|
+
static {
|
|
836
|
+
__name(this, "Utils");
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Parse cookies from various formats
|
|
840
|
+
* Automatically detects the format and parses accordingly
|
|
841
|
+
*
|
|
842
|
+
* @param input - Cookie data in any supported format
|
|
843
|
+
* @returns Parsed cookies object
|
|
844
|
+
*/
|
|
845
|
+
static parseCookies(input) {
|
|
846
|
+
if (typeof input === "object" && !Array.isArray(input)) {
|
|
847
|
+
return input;
|
|
848
|
+
}
|
|
849
|
+
if (Array.isArray(input)) {
|
|
850
|
+
return _Utils.fromCookieArray(input);
|
|
851
|
+
}
|
|
852
|
+
if (typeof input === "string") {
|
|
853
|
+
const trimmed = input.trim();
|
|
854
|
+
if (_Utils.isBase64(trimmed)) {
|
|
855
|
+
try {
|
|
856
|
+
const decoded = Buffer.from(trimmed, "base64").toString("utf-8");
|
|
857
|
+
return _Utils.parseCookies(decoded);
|
|
858
|
+
} catch {
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
862
|
+
try {
|
|
863
|
+
const parsed = JSON.parse(trimmed);
|
|
864
|
+
return _Utils.parseCookies(parsed);
|
|
865
|
+
} catch {
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (trimmed.includes(" ") && (trimmed.startsWith("#") || trimmed.includes(".facebook.com") || trimmed.includes(".messenger.com"))) {
|
|
869
|
+
return _Utils.fromNetscape(trimmed);
|
|
870
|
+
}
|
|
871
|
+
if (trimmed.includes("=")) {
|
|
872
|
+
return _Utils.fromCookieString(trimmed);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
throw new Error("Unable to parse cookies: unknown format");
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Parse cookies from C3C UFC Utility / EditThisCookie array format
|
|
879
|
+
*
|
|
880
|
+
* @param cookies - Array of cookie objects
|
|
881
|
+
* @returns Parsed cookies object
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```typescript
|
|
885
|
+
* const cookies = Utils.fromCookieArray([
|
|
886
|
+
* { name: 'c_user', value: '123456' },
|
|
887
|
+
* { name: 'xs', value: 'abc...' }
|
|
888
|
+
* ])
|
|
889
|
+
* ```
|
|
890
|
+
*/
|
|
891
|
+
static fromCookieArray(cookies) {
|
|
892
|
+
const result = {};
|
|
893
|
+
for (const cookie of cookies) {
|
|
894
|
+
if (cookie.name && cookie.value !== void 0) {
|
|
895
|
+
result[cookie.name] = String(cookie.value);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return result;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Parse cookies from cookie header string format
|
|
902
|
+
*
|
|
903
|
+
* @param cookieString - Cookie string (e.g., "name1=value1; name2=value2")
|
|
904
|
+
* @returns Parsed cookies object
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```typescript
|
|
908
|
+
* const cookies = Utils.fromCookieString('c_user=123456; xs=abc...; datr=xyz...')
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
static fromCookieString(cookieString) {
|
|
912
|
+
const result = {};
|
|
913
|
+
const pairs = cookieString.split(/;\s*/);
|
|
914
|
+
for (const pair of pairs) {
|
|
915
|
+
const [name, ...valueParts] = pair.split("=");
|
|
916
|
+
if (name && valueParts.length > 0) {
|
|
917
|
+
const trimmedName = name.trim();
|
|
918
|
+
const value = valueParts.join("=").trim();
|
|
919
|
+
if (trimmedName && value) {
|
|
920
|
+
result[trimmedName] = value;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return result;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Parse cookies from Netscape/HTTP cookie file format
|
|
928
|
+
*
|
|
929
|
+
* @param content - Netscape cookie file content
|
|
930
|
+
* @returns Parsed cookies object
|
|
931
|
+
*
|
|
932
|
+
* @example
|
|
933
|
+
* ```typescript
|
|
934
|
+
* const cookies = Utils.fromNetscape(`
|
|
935
|
+
* # Netscape HTTP Cookie File
|
|
936
|
+
* .facebook.com TRUE / TRUE 1234567890 c_user 123456
|
|
937
|
+
* .facebook.com TRUE / TRUE 1234567890 xs abc...
|
|
938
|
+
* `)
|
|
939
|
+
* ```
|
|
940
|
+
*/
|
|
941
|
+
static fromNetscape(content) {
|
|
942
|
+
const result = {};
|
|
943
|
+
const lines = content.split("\n");
|
|
944
|
+
for (const line of lines) {
|
|
945
|
+
const trimmed = line.trim();
|
|
946
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const parts = trimmed.split(" ");
|
|
950
|
+
if (parts.length >= 7) {
|
|
951
|
+
const name = parts[5];
|
|
952
|
+
const value = parts[6];
|
|
953
|
+
if (name && value) {
|
|
954
|
+
result[name] = value;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return result;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Parse cookies from Base64 encoded string
|
|
962
|
+
*
|
|
963
|
+
* @param base64 - Base64 encoded cookie data
|
|
964
|
+
* @returns Parsed cookies object
|
|
965
|
+
*/
|
|
966
|
+
static fromBase64(base64) {
|
|
967
|
+
const decoded = Buffer.from(base64, "base64").toString("utf-8");
|
|
968
|
+
return _Utils.parseCookies(decoded);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Convert cookies object to cookie header string
|
|
972
|
+
*
|
|
973
|
+
* @param cookies - Cookies object
|
|
974
|
+
* @returns Cookie header string
|
|
975
|
+
*
|
|
976
|
+
* @example
|
|
977
|
+
* ```typescript
|
|
978
|
+
* const header = Utils.toCookieString({ c_user: '123456', xs: 'abc...' })
|
|
979
|
+
* // Returns: "c_user=123456; xs=abc..."
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
static toCookieString(cookies) {
|
|
983
|
+
return Object.entries(cookies).map(([name, value]) => `${name}=${value}`).join("; ");
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Convert cookies object to array format (C3C UFC Utility style)
|
|
987
|
+
*
|
|
988
|
+
* @param cookies - Cookies object
|
|
989
|
+
* @param domain - Cookie domain (default: .facebook.com)
|
|
990
|
+
* @returns Array of cookie objects
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```typescript
|
|
994
|
+
* const arr = Utils.toCookieArray({ c_user: '123456', xs: 'abc...' })
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
static toCookieArray(cookies, domain = ".facebook.com") {
|
|
998
|
+
return Object.entries(cookies).filter(([_, value]) => value !== void 0).map(([name, value]) => ({
|
|
999
|
+
name,
|
|
1000
|
+
value,
|
|
1001
|
+
domain,
|
|
1002
|
+
path: "/",
|
|
1003
|
+
secure: true,
|
|
1004
|
+
httpOnly: true
|
|
1005
|
+
}));
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Convert cookies object to Netscape format
|
|
1009
|
+
*
|
|
1010
|
+
* @param cookies - Cookies object
|
|
1011
|
+
* @param domain - Cookie domain (default: .facebook.com)
|
|
1012
|
+
* @returns Netscape cookie file content
|
|
1013
|
+
*/
|
|
1014
|
+
static toNetscape(cookies, domain = ".facebook.com") {
|
|
1015
|
+
const lines = ["# Netscape HTTP Cookie File", "# Generated by meta-messenger.js", ""];
|
|
1016
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
1017
|
+
const expiration = Math.floor(Date.now() / 1e3) + 365 * 24 * 60 * 60;
|
|
1018
|
+
lines.push(`${domain} TRUE / TRUE ${expiration} ${name} ${value}`);
|
|
1019
|
+
}
|
|
1020
|
+
return lines.join("\n");
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Convert cookies to Base64 encoded JSON
|
|
1024
|
+
*
|
|
1025
|
+
* @param cookies - Cookies object
|
|
1026
|
+
* @returns Base64 encoded string
|
|
1027
|
+
*/
|
|
1028
|
+
static toBase64(cookies) {
|
|
1029
|
+
return Buffer.from(JSON.stringify(cookies)).toString("base64");
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Filter cookies to only essential ones for Facebook/Messenger
|
|
1033
|
+
*
|
|
1034
|
+
* @param cookies - Cookies object
|
|
1035
|
+
* @returns Filtered cookies with only essential keys
|
|
1036
|
+
*/
|
|
1037
|
+
static filterEssential(cookies) {
|
|
1038
|
+
const essential = ["c_user", "xs", "datr", "fr", "sb", "wd", "presence"];
|
|
1039
|
+
const result = {};
|
|
1040
|
+
for (const key of essential) {
|
|
1041
|
+
if (cookies[key]) {
|
|
1042
|
+
result[key] = cookies[key];
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Validate that cookies contain required fields
|
|
1049
|
+
*
|
|
1050
|
+
* @param cookies - Cookies object
|
|
1051
|
+
* @returns True if cookies are valid
|
|
1052
|
+
*/
|
|
1053
|
+
static validate(cookies) {
|
|
1054
|
+
const required = ["c_user", "xs"];
|
|
1055
|
+
return required.every((key) => cookies[key] && cookies[key].length > 0);
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Get missing required cookies
|
|
1059
|
+
*
|
|
1060
|
+
* @param cookies - Cookies object
|
|
1061
|
+
* @returns Array of missing cookie names
|
|
1062
|
+
*/
|
|
1063
|
+
static getMissing(cookies) {
|
|
1064
|
+
const required = ["c_user", "xs"];
|
|
1065
|
+
return required.filter((key) => !cookies[key] || cookies[key].length === 0);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Check if a string is valid Base64
|
|
1069
|
+
*/
|
|
1070
|
+
static isBase64(str) {
|
|
1071
|
+
if (str.length < 4) return false;
|
|
1072
|
+
const base64Regex = /^[A-Za-z0-9+/]+=*$/;
|
|
1073
|
+
if (!base64Regex.test(str)) return false;
|
|
1074
|
+
return str.length > 20 && str.length % 4 === 0;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/index.ts
|
|
1079
|
+
async function login(cookies, options) {
|
|
1080
|
+
const client = new Client(cookies, options);
|
|
1081
|
+
await client.connect();
|
|
1082
|
+
return client;
|
|
1083
|
+
}
|
|
1084
|
+
__name(login, "login");
|
|
1085
|
+
function createClient(cookies, options) {
|
|
1086
|
+
return new Client(cookies, options);
|
|
1087
|
+
}
|
|
1088
|
+
__name(createClient, "createClient");
|
|
1089
|
+
var index_default = { Client, login, createClient };
|
|
1090
|
+
export {
|
|
1091
|
+
Client,
|
|
1092
|
+
ThreadType,
|
|
1093
|
+
Utils,
|
|
1094
|
+
createClient,
|
|
1095
|
+
index_default as default,
|
|
1096
|
+
login
|
|
1097
|
+
};
|
|
1098
|
+
//# sourceMappingURL=index.js.map
|