@dongdev/fca-unofficial 3.0.11 → 3.0.15

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/CHANGELOG.md CHANGED
@@ -161,3 +161,9 @@ Too lazy to write changelog, sorry! (will write changelog in the next release, t
161
161
 
162
162
  ## v3.0.10 - 2025-12-05
163
163
  - Hotfix / auto bump
164
+
165
+ ## v3.0.11 - 2025-12-05
166
+ - Hotfix / auto bump
167
+
168
+ ## v3.0.12 - 2025-12-05
169
+ - Hotfix / auto bump
@@ -284,14 +284,31 @@ async function setJarCookies(j, appstate) {
284
284
  const cookieName = c.name || c.key;
285
285
  const cookieValue = c.value;
286
286
  if (!cookieName || cookieValue === undefined) continue;
287
- const dom = (c.domain || ".facebook.com").replace(/^\./, "");
288
- const path = c.path || "/";
289
- const base1 = `https://${dom}${path}`;
290
- const base2 = `https://www.${dom}${path}`;
291
- const domain = c.domain || ".facebook.com";
292
- const str = `${cookieName}=${cookieValue}; Domain=${domain}; Path=${path};`;
293
- tasks.push(j.setCookie(str, base1));
294
- tasks.push(j.setCookie(str, base2));
287
+
288
+ const cookieDomain = c.domain || ".facebook.com";
289
+ const cookiePath = c.path || "/";
290
+ const dom = cookieDomain.replace(/^\./, "");
291
+
292
+ // Format expires if provided
293
+ let expiresStr = "";
294
+ if (c.expires) {
295
+ const expiresDate = typeof c.expires === "number" ? new Date(c.expires) : new Date(c.expires);
296
+ expiresStr = `; expires=${expiresDate.toUTCString()}`;
297
+ }
298
+
299
+ // Build cookie string
300
+ const str = `${cookieName}=${cookieValue}${expiresStr}; Domain=${cookieDomain}; Path=${cookiePath};`;
301
+
302
+ // Set cookie for both http and https, with and without www
303
+ const base1 = `http://${dom}${cookiePath}`;
304
+ const base2 = `https://${dom}${cookiePath}`;
305
+ const base3 = `http://www.${dom}${cookiePath}`;
306
+ const base4 = `https://www.${dom}${cookiePath}`;
307
+
308
+ tasks.push(j.setCookie(str, base1).catch(() => { }));
309
+ tasks.push(j.setCookie(str, base2).catch(() => { }));
310
+ tasks.push(j.setCookie(str, base3).catch(() => { }));
311
+ tasks.push(j.setCookie(str, base4).catch(() => { }));
295
312
  }
296
313
  await Promise.all(tasks);
297
314
  }
@@ -577,23 +594,47 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
577
594
  (async () => {
578
595
  try {
579
596
  if (appState) {
580
- if (typeof appState === "string") {
597
+ // Check and convert cookie to appState format
598
+ if (Array.isArray(appState) && appState.some(c => c.name)) {
599
+ // Convert name to key if needed
600
+ appState = appState.map(c => {
601
+ if (c.name && !c.key) {
602
+ c.key = c.name;
603
+ delete c.name;
604
+ }
605
+ return c;
606
+ });
607
+ } else if (typeof appState === "string") {
608
+ // Try to parse as JSON first
581
609
  let parsed = appState;
582
610
  try {
583
611
  parsed = JSON.parse(appState);
584
612
  } catch { }
613
+
585
614
  if (Array.isArray(parsed)) {
586
- // Use setJarCookies to properly handle individual cookie domains/paths
587
- await setJarCookies(jar, parsed);
588
- } else if (typeof parsed === "string") {
589
- const pairs = normalizeCookieHeaderString(parsed);
590
- if (!pairs.length) throw new Error("Empty appState cookie header");
591
- setJarFromPairs(jar, pairs, domain);
615
+ // Already parsed as array, use it
616
+ appState = parsed;
592
617
  } else {
593
- throw new Error("Invalid appState format");
618
+ // Parse string cookie format (key=value; key2=value2)
619
+ const arrayAppState = [];
620
+ appState.split(';').forEach(c => {
621
+ const [key, value] = c.split('=');
622
+ if (key && value) {
623
+ arrayAppState.push({
624
+ key: key.trim(),
625
+ value: value.trim(),
626
+ domain: ".facebook.com",
627
+ path: "/",
628
+ expires: new Date().getTime() + 1000 * 60 * 60 * 24 * 365
629
+ });
630
+ }
631
+ });
632
+ appState = arrayAppState;
594
633
  }
595
- } else if (Array.isArray(appState)) {
596
- // Use setJarCookies to properly handle individual cookie domains/paths
634
+ }
635
+
636
+ // Set cookies into jar with individual domain/path
637
+ if (Array.isArray(appState)) {
597
638
  await setJarCookies(jar, appState);
598
639
  } else {
599
640
  throw new Error("Invalid appState format");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongdev/fca-unofficial",
3
- "version": "3.0.11",
3
+ "version": "3.0.15",
4
4
  "description": "Unofficial Facebook Chat API for Node.js - Interact with Facebook Messenger programmatically",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -13,8 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "test": "mocha",
16
- "lint": "eslint .",
17
- "prepublishOnly": "npm test"
16
+ "lint": "eslint ."
18
17
  },
19
18
  "repository": {
20
19
  "type": "git",
@@ -60,61 +60,34 @@ module.exports = function createParseDelta(deps) {
60
60
  };
61
61
  globalCallback(null, messageUnsend);
62
62
  } else if (d.deltaMessageReply) {
63
- const mdata = d.deltaMessageReply.message === undefined ? [] : d.deltaMessageReply.message.data === undefined ? [] : d.deltaMessageReply.message.data.prng === undefined ? [] : JSON.parse(d.deltaMessageReply.message.data.prng);
64
- const m_id = mdata.map(u => u.i);
65
- const m_offset = mdata.map(u => u.o);
66
- const m_length = mdata.map(u => u.l);
67
- const mentions = {};
68
- for (let i = 0; i < m_id.length; i++) {
69
- mentions[m_id[i]] = (d.deltaMessageReply.message.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
70
- }
71
- const callbackToReturn = {
72
- type: "message_reply",
73
- threadID: (d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ? d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : d.deltaMessageReply.message.messageMetadata.threadKey.otherUserFbId).toString(),
74
- messageID: d.deltaMessageReply.message.messageMetadata.messageId,
75
- senderID: d.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
76
- attachments: (d.deltaMessageReply.message.attachments || []).map(att => {
77
- const mercury = JSON.parse(att.mercuryJSON);
78
- Object.assign(att, mercury);
79
- return att;
80
- }).map(att => {
81
- let x;
82
- try {
83
- x = _formatAttachment(att);
84
- } catch (ex) {
85
- x = att;
86
- x.error = ex;
87
- x.type = "unknown";
88
- }
89
- return x;
90
- }),
91
- args: (d.deltaMessageReply.message.body || "").trim().split(/\s+/),
92
- body: d.deltaMessageReply.message.body || "",
93
- isGroup: !!d.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
94
- mentions,
95
- timestamp: parseInt(d.deltaMessageReply.message.messageMetadata.timestamp),
96
- participantIDs: (d.deltaMessageReply.message.participants || []).map(e => e.toString())
97
- };
98
- if (d.deltaMessageReply.repliedToMessage) {
99
- const mdata2 = d.deltaMessageReply.repliedToMessage === undefined ? [] : d.deltaMessageReply.repliedToMessage.data === undefined ? [] : d.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] : JSON.parse(d.deltaMessageReply.repliedToMessage.data.prng);
100
- const m_id2 = mdata2.map(u => u.i);
101
- const m_offset2 = mdata2.map(u => u.o);
102
- const m_length2 = mdata2.map(u => u.l);
103
- const rmentions = {};
104
- for (let i = 0; i < m_id2.length; i++) {
105
- rmentions[m_id2[i]] = (d.deltaMessageReply.repliedToMessage.body || "").substring(m_offset2[i], m_offset2[i] + m_length2[i]);
63
+ let callbackToReturn;
64
+ try {
65
+ const msg = d.deltaMessageReply.message;
66
+ if (!msg || !msg.messageMetadata) {
67
+ logger("parseDelta: deltaMessageReply.message or messageMetadata is missing", "warn");
68
+ return;
106
69
  }
107
- callbackToReturn.messageReply = {
108
- threadID: (d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ? d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.otherUserFbId).toString(),
109
- messageID: d.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
110
- senderID: d.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
111
- attachments: d.deltaMessageReply.repliedToMessage.attachments.map(att => {
112
- let mercury;
70
+ const mdata = msg === undefined ? [] : msg.data === undefined ? [] : msg.data.prng === undefined ? [] : JSON.parse(msg.data.prng);
71
+ const m_id = mdata.map(u => u.i);
72
+ const m_offset = mdata.map(u => u.o);
73
+ const m_length = mdata.map(u => u.l);
74
+ const mentions = {};
75
+ for (let i = 0; i < m_id.length; i++) {
76
+ mentions[m_id[i]] = (msg.body || "").substring(m_offset[i], m_offset[i] + m_length[i]);
77
+ }
78
+ const msgMetadata = msg.messageMetadata;
79
+ const threadKey = msgMetadata.threadKey || {};
80
+ callbackToReturn = {
81
+ type: "message_reply",
82
+ threadID: (threadKey.threadFbId ? threadKey.threadFbId : threadKey.otherUserFbId || "").toString(),
83
+ messageID: msgMetadata.messageId || "",
84
+ senderID: (msgMetadata.actorFbId || "").toString(),
85
+ attachments: (msg.attachments || []).map(att => {
113
86
  try {
114
- mercury = JSON.parse(att.mercuryJSON);
87
+ const mercury = JSON.parse(att.mercuryJSON);
115
88
  Object.assign(att, mercury);
116
89
  } catch (ex) {
117
- mercury = {};
90
+ // Ignore parsing errors
118
91
  }
119
92
  return att;
120
93
  }).map(att => {
@@ -128,13 +101,62 @@ module.exports = function createParseDelta(deps) {
128
101
  }
129
102
  return x;
130
103
  }),
131
- args: (d.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
132
- body: d.deltaMessageReply.repliedToMessage.body || "",
133
- isGroup: !!d.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
134
- mentions: rmentions,
135
- timestamp: parseInt(d.deltaMessageReply.repliedToMessage.messageMetadata.timestamp),
136
- participantIDs: (d.deltaMessageReply.repliedToMessage.participants || []).map(e => e.toString())
104
+ args: (msg.body || "").trim().split(/\s+/),
105
+ body: msg.body || "",
106
+ isGroup: !!threadKey.threadFbId,
107
+ mentions,
108
+ timestamp: parseInt(msgMetadata.timestamp || 0),
109
+ participantIDs: (msg.participants || []).map(e => e.toString())
137
110
  };
111
+ if (d.deltaMessageReply.repliedToMessage) {
112
+ try {
113
+ const repliedTo = d.deltaMessageReply.repliedToMessage;
114
+ const mdata2 = repliedTo === undefined ? [] : repliedTo.data === undefined ? [] : repliedTo.data.prng === undefined ? [] : JSON.parse(repliedTo.data.prng);
115
+ const m_id2 = mdata2.map(u => u.i);
116
+ const m_offset2 = mdata2.map(u => u.o);
117
+ const m_length2 = mdata2.map(u => u.l);
118
+ const rmentions = {};
119
+ for (let i = 0; i < m_id2.length; i++) {
120
+ rmentions[m_id2[i]] = (repliedTo.body || "").substring(m_offset2[i], m_offset2[i] + m_length2[i]);
121
+ }
122
+ const msgMetadata = repliedTo.messageMetadata;
123
+ if (msgMetadata && msgMetadata.threadKey) {
124
+ callbackToReturn.messageReply = {
125
+ threadID: (msgMetadata.threadKey.threadFbId ? msgMetadata.threadKey.threadFbId : msgMetadata.threadKey.otherUserFbId || "").toString(),
126
+ messageID: msgMetadata.messageId || "",
127
+ senderID: (msgMetadata.actorFbId || "").toString(),
128
+ attachments: (repliedTo.attachments || []).map(att => {
129
+ let mercury;
130
+ try {
131
+ mercury = JSON.parse(att.mercuryJSON);
132
+ Object.assign(att, mercury);
133
+ } catch (ex) {
134
+ mercury = {};
135
+ }
136
+ return att;
137
+ }).map(att => {
138
+ let x;
139
+ try {
140
+ x = _formatAttachment(att);
141
+ } catch (ex) {
142
+ x = att;
143
+ x.error = ex;
144
+ x.type = "unknown";
145
+ }
146
+ return x;
147
+ }),
148
+ args: (repliedTo.body || "").trim().split(/\s+/),
149
+ body: repliedTo.body || "",
150
+ isGroup: !!msgMetadata.threadKey.threadFbId,
151
+ mentions: rmentions,
152
+ timestamp: parseInt(msgMetadata.timestamp || 0),
153
+ participantIDs: (repliedTo.participants || []).map(e => e.toString())
154
+ };
155
+ }
156
+ } catch (err) {
157
+ const errMsg = err && err.message ? err.message : String(err || "Unknown error");
158
+ logger(`parseDelta message_reply repliedToMessage error: ${errMsg}`, "warn");
159
+ }
138
160
  } else if (d.deltaMessageReply.replyToMessageId) {
139
161
  return defaultFuncs.post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, {
140
162
  av: ctx.globalOptions.pageID,
@@ -173,20 +195,29 @@ module.exports = function createParseDelta(deps) {
173
195
  const errMsg = err && err.message ? err.message : String(err || "Unknown error");
174
196
  logger(`parseDelta message_reply fetch error: ${errMsg}`, "warn");
175
197
  }).finally(() => {
176
- if (ctx.globalOptions.autoMarkDelivery) {
177
- markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
198
+ if (callbackToReturn) {
199
+ if (ctx.globalOptions.autoMarkDelivery) {
200
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
201
+ }
202
+ if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
203
+ globalCallback(null, callbackToReturn);
178
204
  }
179
- if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
180
- globalCallback(null, callbackToReturn);
181
205
  });
182
206
  } else {
183
- callbackToReturn.delta = d;
207
+ if (callbackToReturn) callbackToReturn.delta = d;
184
208
  }
185
- if (ctx.globalOptions.autoMarkDelivery) {
186
- markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
209
+ } catch (err) {
210
+ const errMsg = err && err.message ? err.message : String(err || "Unknown error");
211
+ logger(`parseDelta message_reply error: ${errMsg}`, "warn");
212
+ return;
213
+ }
214
+ if (callbackToReturn) {
215
+ if (ctx.globalOptions.autoMarkDelivery) {
216
+ markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
217
+ }
218
+ if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
219
+ globalCallback(null, callbackToReturn);
187
220
  }
188
- if (!ctx.globalOptions.selfListen && callbackToReturn.senderID === ctx.userID) return;
189
- globalCallback(null, callbackToReturn);
190
221
  }
191
222
  }
192
223
  return;