fb-messenger-e2ee 0.1.5 → 0.1.6

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/DOCS.md CHANGED
@@ -18,8 +18,8 @@ new FBClient(options: ClientOptions)
18
18
 
19
19
  | Option | Type | Description |
20
20
  |---|---|---|
21
- | `appStatePath` | `string` | Path to Facebook appState/cookies JSON. |
22
- | `appState` | `any[] \| string` | Optional in-memory appState alternative. |
21
+ | `appStatePath` | `string` | Path to Facebook appState JSON (contains the active login cookies/session data). |
22
+ | `appState` | `any[] \| string` | Optional in-memory appState/cookies alternative (cookies string or array). |
23
23
  | `sessionStorePath` | `string` | Optional path for login/session metadata used by E2EE bootstrap. |
24
24
  | `platform` | `"facebook" \| "messenger"` | Login platform hint. Defaults to `"facebook"`. |
25
25
 
package/README.md CHANGED
@@ -28,7 +28,7 @@
28
28
  - **Language**: [TypeScript](https://www.typescriptlang.org/)
29
29
  - **Encryption**: [@signalapp/libsignal-client](https://github.com/signalapp/libsignal-client)
30
30
  - **Protocol**: ProtobufJS + manual WA-binary/protobuf encoders
31
- - **Auth bootstrap bridge**: [fca-unofficial](https://github.com/VangBanLaNhat/fca-unofficial) is used internally only for appState login/CAT bootstrap
31
+ - **Auth bootstrap bridge**: [fca-unofficial](https://github.com/VangBanLaNhat/fca-unofficial) is used internally only for appState login/CAT bootstrap (where appState represents the active Facebook login cookies)
32
32
 
33
33
  ---
34
34
 
@@ -60,7 +60,7 @@ const { FBClient } = require("fb-messenger-e2ee");
60
60
  import { FBClient } from "fb-messenger-e2ee";
61
61
 
62
62
  const client = new FBClient({
63
- appStatePath: "./appstate.json",
63
+ appStatePath: "./appstate.json", // Path to Facebook appState JSON (contains active login cookies/session data)
64
64
  sessionStorePath: "./session.json",
65
65
  platform: "facebook",
66
66
  });
package/dist/index.cjs CHANGED
@@ -5968,6 +5968,9 @@ var EncryptedFrameSocket = class {
5968
5968
  const len = header.readUIntBE(0, 3);
5969
5969
  logger.debug("FacebookE2EESocket", `RAW frame header: ${header.toString("hex")} (len=${len})`);
5970
5970
  const payload = await this.ws.readRaw(len);
5971
+ if (!payload) {
5972
+ throw new Error("Socket closed while reading frame payload");
5973
+ }
5971
5974
  try {
5972
5975
  const decrypted = this.decryptFrame(payload);
5973
5976
  logger.debug("FacebookE2EESocket", `Decrypt successful, result length: ${decrypted.length}`);
@@ -6107,11 +6110,12 @@ var FacebookE2EESocket = class extends import_node_events2.EventEmitter {
6107
6110
  url;
6108
6111
  heartbeatInterval = null;
6109
6112
  isConnected = false;
6113
+ cookieHeader = "";
6110
6114
  constructor(endpoint) {
6111
6115
  super();
6112
6116
  this.url = endpoint;
6113
6117
  }
6114
- async connect(noisePrivKey, authPayload) {
6118
+ async connect(noisePrivKey, authPayload, cookies) {
6115
6119
  return new Promise((resolve, reject) => {
6116
6120
  try {
6117
6121
  let readFromBuffer2 = function(len) {
@@ -6136,19 +6140,25 @@ var FacebookE2EESocket = class extends import_node_events2.EventEmitter {
6136
6140
  return res;
6137
6141
  };
6138
6142
  var readFromBuffer = readFromBuffer2;
6143
+ if (cookies) this.cookieHeader = cookies;
6139
6144
  const wsUrl = new URL(this.url);
6140
6145
  wsUrl.searchParams.set("cid", "client-" + Date.now());
6141
6146
  const UserAgentStr = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36";
6147
+ const wsHeaders = {
6148
+ "Origin": "https://www.facebook.com",
6149
+ "User-Agent": UserAgentStr
6150
+ };
6151
+ if (this.cookieHeader) {
6152
+ wsHeaders["Cookie"] = this.cookieHeader;
6153
+ }
6142
6154
  this.ws = new WebSocket(wsUrl.toString(), void 0, {
6143
- headers: {
6144
- "Origin": "https://www.facebook.com",
6145
- "User-Agent": UserAgentStr
6146
- }
6155
+ headers: wsHeaders
6147
6156
  });
6148
6157
  this.ws.binaryType = "arraybuffer";
6149
6158
  let handshakeResolved = false;
6150
6159
  let streamBuffer = [];
6151
6160
  let streamLen = 0;
6161
+ let waitingRejectFn = null;
6152
6162
  let waitingResolver = null;
6153
6163
  let waitingLen = 0;
6154
6164
  this.ws.addEventListener("message", (ev) => {
@@ -6173,8 +6183,9 @@ var FacebookE2EESocket = class extends import_node_events2.EventEmitter {
6173
6183
  const targetLen = len || 0;
6174
6184
  if (targetLen === 0) {
6175
6185
  if (streamLen > 0) return Promise.resolve(readFromBuffer2(streamLen));
6176
- return new Promise((resolve2) => {
6186
+ return new Promise((resolve2, reject2) => {
6177
6187
  waitingLen = 1;
6188
+ waitingRejectFn = reject2;
6178
6189
  waitingResolver = () => resolve2(readFromBuffer2(streamLen));
6179
6190
  });
6180
6191
  }
@@ -6184,8 +6195,9 @@ var FacebookE2EESocket = class extends import_node_events2.EventEmitter {
6184
6195
  if (this.ws?.readyState !== WebSocket.OPEN) {
6185
6196
  return Promise.reject(new Error("WebSocket not open"));
6186
6197
  }
6187
- return new Promise((resolve2) => {
6198
+ return new Promise((resolve2, reject2) => {
6188
6199
  waitingLen = targetLen;
6200
+ waitingRejectFn = reject2;
6189
6201
  waitingResolver = resolve2;
6190
6202
  });
6191
6203
  },
@@ -6194,10 +6206,11 @@ var FacebookE2EESocket = class extends import_node_events2.EventEmitter {
6194
6206
  this.ws.addEventListener("close", () => {
6195
6207
  this.isConnected = false;
6196
6208
  this.stopHeartbeat();
6197
- if (waitingResolver) {
6198
- waitingResolver(null);
6199
- waitingResolver = null;
6209
+ if (waitingRejectFn) {
6210
+ waitingRejectFn(new Error("WebSocket closed while waiting for data"));
6211
+ waitingRejectFn = null;
6200
6212
  }
6213
+ waitingResolver = null;
6201
6214
  this.emit("disconnected");
6202
6215
  });
6203
6216
  this.ws.addEventListener("open", async () => {
@@ -8049,10 +8062,10 @@ var ClientController = class {
8049
8062
  logger.debug("ClientController", "Fetching CAT...");
8050
8063
  const fbCat = await this.gateway.fetchCAT(this.requireApi());
8051
8064
  if (!ds.jidDevice) {
8052
- const api = this.requireApi();
8053
- const appState = api.getAppState?.() || [];
8054
- const cookieStr = appState.map((c) => `${c.key}=${c.value}`).join("; ");
8055
- this.icdcService.setCookies(cookieStr);
8065
+ const api2 = this.requireApi();
8066
+ const appState2 = api2.getAppState?.() || [];
8067
+ const cookieStr2 = appState2.map((c) => `${c.key}=${c.value}`).join("; ");
8068
+ this.icdcService.setCookies(cookieStr2);
8056
8069
  logger.info("ClientController", "Registering new device via ICDC...");
8057
8070
  const waDeviceId = await this.icdcService.register(userId, fbCat, "2220391788200892", ds);
8058
8071
  ds.jidDevice = waDeviceId;
@@ -8101,7 +8114,12 @@ var ClientController = class {
8101
8114
  logger.error("E2EE", "Frame error:", err);
8102
8115
  }
8103
8116
  });
8104
- await noiseSocket.connect(ds.noiseKeyPriv, clientPayload);
8117
+ const api = this.requireApi();
8118
+ const appState = api.getAppState?.() || [];
8119
+ const cookieStr = appState.map((c) => `${c.key}=${c.value}`).join("; ");
8120
+ logger.info("ClientController", "FCA appState length:", appState.length);
8121
+ logger.info("ClientController", "Cookie string snippet:", cookieStr.substring(0, 100));
8122
+ await noiseSocket.connect(ds.noiseKeyPriv, clientPayload, cookieStr || void 0);
8105
8123
  this.e2eeSocket = noiseSocket;
8106
8124
  await new Promise((resolve, reject) => {
8107
8125
  const timeout = setTimeout(() => reject(new Error("Handshake timeout")), 1e4);