@lazyneoaz/metachat 4.0.6 → 4.0.8

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/dist/index.mjs CHANGED
@@ -25107,13 +25107,15 @@ function getHeaders(url2, options, ctx, customHeader) {
25107
25107
  headers["priority"] = "u=0, i";
25108
25108
  }
25109
25109
  if (isXhr) {
25110
- headers["X-Requested-With"] = "XMLHttpRequest";
25111
- headers["Pragma"] = "no-cache";
25112
- headers["Cache-Control"] = "no-cache";
25110
+ if (!isGraphQl) {
25111
+ headers["X-Requested-With"] = "XMLHttpRequest";
25112
+ headers["Pragma"] = "no-cache";
25113
+ headers["Cache-Control"] = "no-cache";
25114
+ }
25113
25115
  headers["priority"] = "u=1, i";
25114
25116
  }
25115
25117
  if (isGraphQl) {
25116
- headers["x-asbd-id"] = "129477";
25118
+ headers["x-asbd-id"] = String(ctx?.asbdId || DEFAULT_ASBD_ID);
25117
25119
  const lsd = ctx?.lsd || ctx?.fb_lsd;
25118
25120
  if (lsd) headers["x-fb-lsd"] = sanitizeHeaderValue2(lsd);
25119
25121
  const friendlyName = customHeader?.["x-fb-friendly-name"] || customHeader?.["X-Fb-Friendly-Name"];
@@ -25184,10 +25186,11 @@ function getFingerprintParams(options) {
25184
25186
  dpr: String(profile.devicePixelRatio)
25185
25187
  };
25186
25188
  }
25187
- var meta;
25189
+ var DEFAULT_ASBD_ID, meta;
25188
25190
  var init_headers = __esm({
25189
25191
  "src/utils/headers.ts"() {
25190
25192
  init_user_agents();
25193
+ DEFAULT_ASBD_ID = "359341";
25191
25194
  meta = (prop2) => new RegExp(`<meta property="${prop2}" content="([^"]*)"`);
25192
25195
  }
25193
25196
  });
@@ -75525,9 +75528,13 @@ __export(utils_exports2, {
75525
75528
  NUM_TO_MONTH: () => NUM_TO_MONTH,
75526
75529
  NetworkError: () => NetworkError,
75527
75530
  NotFoundError: () => NotFoundError,
75531
+ PING_GRACE_MS: () => PING_GRACE_MS,
75528
75532
  PermissionError: () => PermissionError,
75529
75533
  RateLimitError: () => RateLimitError,
75534
+ RealtimeHealth: () => RealtimeHealth,
75535
+ RecoverySupervisor: () => RecoverySupervisor,
75530
75536
  SESSION_COOKIE_NAMES: () => SESSION_COOKIE_NAMES,
75537
+ SILENT_WINDOW_MS: () => SILENT_WINDOW_MS,
75531
75538
  TimeoutError: () => TimeoutError,
75532
75539
  ValidationError: () => ValidationError,
75533
75540
  WINDOW_MS: () => WINDOW_MS,
@@ -75552,6 +75559,7 @@ __export(utils_exports2, {
75552
75559
  delay: () => delay2,
75553
75560
  ensureCometTokens: () => ensureCometTokens,
75554
75561
  error: () => error,
75562
+ extractAsbdId: () => extractAsbdId,
75555
75563
  extractAttachmentIds: () => extractAttachmentIds,
75556
75564
  extractCookies: () => extractCookies,
75557
75565
  extractCookiesFromBody: () => extractCookiesFromBody,
@@ -75602,6 +75610,7 @@ __export(utils_exports2, {
75602
75610
  invalidateCometTokens: () => invalidateCometTokens,
75603
75611
  isAuthFailure: () => isAuthFailure,
75604
75612
  isBreakerOpen: () => isBreakerOpen,
75613
+ isLoggedOutDocument: () => isLoggedOutDocument,
75605
75614
  isReadableStream: () => isReadableStream2,
75606
75615
  isStaleTokenError: () => isStaleTokenError,
75607
75616
  json: () => json2,
@@ -75639,6 +75648,8 @@ __export(utils_exports2, {
75639
75648
  setProxy: () => setProxy2,
75640
75649
  sleep: () => sleep,
75641
75650
  startLiveness: () => startLiveness,
75651
+ startRealtimeSupervisor: () => startRealtimeSupervisor,
75652
+ startRecovery: () => startRecovery,
75642
75653
  success: () => success,
75643
75654
  tripBreaker: () => tripBreaker,
75644
75655
  uploadGroupImage: () => uploadGroupImage,
@@ -90995,6 +91006,9 @@ function extractLsd(html3) {
90995
91006
  function extractDtsg(html3) {
90996
91007
  return getFrom3(html3, '["DTSGInitialData",[],{"token":"', '"}') || getFrom3(html3, 'name="fb_dtsg" value="', '"') || (html3.match(/"DTSGInitialData",\[\],\{"token":"([^"]+)"/) || [])[1] || "";
90997
91008
  }
91009
+ function extractAsbdId(html3) {
91010
+ return (html3.match(/"asbd_id":(\d+)/) || [])[1] || (html3.match(/x-asbd-id["']?\s*[:=]\s*["']?(\d+)/i) || [])[1] || (html3.match(/"ASBD_ID":(\d+)/) || [])[1] || "";
91011
+ }
90998
91012
  function extractSpin(html3) {
90999
91013
  const out = {};
91000
91014
  const spinB = (html3.match(/"__spin_b":"([^"]+)"/) || [])[1];
@@ -91061,6 +91075,8 @@ async function ensureCometTokens(ctx, defaultFuncs, force = false) {
91061
91075
  const spin = extractSpin(html3);
91062
91076
  if (Object.keys(spin).length) ctx.__spin = { ...ctx.__spin || {}, ...spin };
91063
91077
  if (spin.__rev) ctx.revision = spin.__rev;
91078
+ const asbd = extractAsbdId(html3);
91079
+ if (asbd) ctx.asbdId = asbd;
91064
91080
  ctx.lsd = lsd;
91065
91081
  ctx.fb_dtsg = dtsg;
91066
91082
  ctx.jazoest = recomputeJazoest(dtsg);
@@ -91331,6 +91347,13 @@ function isAuthFailure(value) {
91331
91347
  }
91332
91348
  return false;
91333
91349
  }
91350
+ function isLoggedOutDocument(html3) {
91351
+ if (typeof html3 !== "string" || html3.length < 200) return false;
91352
+ if (!/<html/i.test(html3)) return false;
91353
+ const hasAuthedTokens = /DTSGInitialData|"LSD",\[\]/.test(html3);
91354
+ const looksLikeLogin = /name="(?:email|pass)"|id="email"|"login_form"|LoginForm|login_form/i.test(html3) || /Continue as |Use another profile|Create new account/i.test(html3) || /You must log in to continue|Please log in|log in to continue/i.test(html3);
91355
+ return looksLikeLogin && !hasAuthedTokens;
91356
+ }
91334
91357
  async function withSessionRetry(ctx, defaultFuncs, operation, options = {}) {
91335
91358
  const attempts = Math.max(1, options.attempts ?? 2);
91336
91359
  let lastError;
@@ -91437,6 +91460,8 @@ async function warmUpSession(ctx, getter, options = {}) {
91437
91460
  const spin = extractSpin(html3);
91438
91461
  if (Object.keys(spin).length) ctx.__spin = { ...ctx.__spin || {}, ...spin };
91439
91462
  if (spin.__rev) ctx.revision = spin.__rev;
91463
+ const asbd = extractAsbdId(html3);
91464
+ if (asbd) ctx.asbdId = asbd;
91440
91465
  tokensSeeded = true;
91441
91466
  }
91442
91467
  } else if (url2.includes("/messages")) {
@@ -91535,6 +91560,339 @@ async function awaitProactiveSlot(ctx, maxWaitMs = 15 * 60 * 1e3) {
91535
91560
  return false;
91536
91561
  }
91537
91562
 
91563
+ // src/utils/recovery.ts
91564
+ init_pacing();
91565
+ init_safety();
91566
+ var DEFAULTS = {
91567
+ intervalMs: 10 * 60 * 1e3,
91568
+ failureThreshold: 3,
91569
+ maxRecoveries: 5
91570
+ };
91571
+ function randJitter(base, spread3 = 0.3) {
91572
+ return Math.floor(base * (1 - spread3 / 2 + Math.random() * spread3));
91573
+ }
91574
+ var RecoverySupervisor = class {
91575
+ constructor(runtime, options = {}) {
91576
+ this.timer = null;
91577
+ this.stopped = true;
91578
+ this.running = false;
91579
+ this.runtime = runtime;
91580
+ this.opts = {
91581
+ intervalMs: options.intervalMs ?? DEFAULTS.intervalMs,
91582
+ failureThreshold: options.failureThreshold ?? DEFAULTS.failureThreshold,
91583
+ maxRecoveries: options.maxRecoveries ?? DEFAULTS.maxRecoveries,
91584
+ noRelogin: options.noRelogin ?? false
91585
+ };
91586
+ this.backoffMs = this.opts.intervalMs;
91587
+ this.state = {
91588
+ phase: "healthy",
91589
+ checks: 0,
91590
+ consecutiveFailures: 0,
91591
+ healCount: 0,
91592
+ attempts: 0,
91593
+ reloginCount: 0,
91594
+ givenUp: 0,
91595
+ lastHealthyAt: Date.now(),
91596
+ nextCheckMs: 0,
91597
+ canRelogin: typeof runtime.relogin === "function" && !this.opts.noRelogin && (runtime.canRelogin !== void 0 ? runtime.canRelogin : true)
91598
+ };
91599
+ }
91600
+ /** A point-in-time copy of the supervisor state. */
91601
+ snapshot() {
91602
+ return { ...this.state, nextCheckMs: Math.max(0, this.nextCheckAt() - Date.now()) };
91603
+ }
91604
+ isRunning() {
91605
+ return !this.stopped;
91606
+ }
91607
+ /** Start the supervisor. Idempotent. */
91608
+ start() {
91609
+ if (!this.stopped) return;
91610
+ this.stopped = false;
91611
+ if (this.state.phase === "stopped") this.state.phase = "healthy";
91612
+ this.startTimer(false);
91613
+ this.emit("check", "recovery supervisor started");
91614
+ }
91615
+ /** Stop the supervisor and clear any pending timer. */
91616
+ stop() {
91617
+ this.stopped = true;
91618
+ if (this.timer) {
91619
+ clearTimeout(this.timer);
91620
+ this.timer = null;
91621
+ }
91622
+ this.state.phase = "stopped";
91623
+ this.emit("stopped", "recovery supervisor stopped");
91624
+ }
91625
+ /**
91626
+ * Run one health check immediately (used by tests and by `checkNow()`).
91627
+ * Returns the resulting state. Never throws.
91628
+ */
91629
+ async checkNow() {
91630
+ if (this.running) return this.snapshot();
91631
+ this.running = true;
91632
+ try {
91633
+ await this.runOnce();
91634
+ } catch {
91635
+ } finally {
91636
+ this.running = false;
91637
+ }
91638
+ return this.snapshot();
91639
+ }
91640
+ nextCheckAt() {
91641
+ return this.state.nextCheckMs && this.timer ? Date.now() + this.state.nextCheckMs : 0;
91642
+ }
91643
+ startTimer(delayOverride) {
91644
+ if (this.stopped) return;
91645
+ if (this.timer) {
91646
+ clearTimeout(this.timer);
91647
+ this.timer = null;
91648
+ }
91649
+ const delay3 = delayOverride === false ? this.backoffMs : delayOverride ?? this.backoffMs;
91650
+ const jittered = randJitter(delay3);
91651
+ this.state.nextCheckMs = jittered;
91652
+ this.timer = setTimeout(() => {
91653
+ this.timer = null;
91654
+ this.state.nextCheckMs = 0;
91655
+ void this.checkNow().finally(() => {
91656
+ if (!this.stopped) this.startTimer();
91657
+ });
91658
+ }, jittered);
91659
+ if (this.timer && typeof this.timer.unref === "function") this.timer.unref();
91660
+ }
91661
+ async runOnce() {
91662
+ this.state.checks += 1;
91663
+ this.emit("check", `health check #${this.state.checks}`);
91664
+ let healthy = this.runtime.isAuthenticated();
91665
+ if (healthy && this.runtime.probeLiveness) {
91666
+ try {
91667
+ healthy = await this.runtime.probeLiveness();
91668
+ } catch {
91669
+ healthy = true;
91670
+ }
91671
+ }
91672
+ if (healthy) {
91673
+ this.markHealthy("session authenticated");
91674
+ return;
91675
+ }
91676
+ this.state.consecutiveFailures += 1;
91677
+ this.state.phase = "refreshing";
91678
+ this.emit("unhealthy", `unauthenticated (${this.state.consecutiveFailures}/${this.opts.failureThreshold})`);
91679
+ if (this.state.consecutiveFailures < this.opts.failureThreshold) {
91680
+ return;
91681
+ }
91682
+ if (isBreakerOpen(this.runtime.ctx)) {
91683
+ this.state.phase = "cooling";
91684
+ this.emit("cooling", "safety breaker open; delaying recovery");
91685
+ return;
91686
+ }
91687
+ if (this.state.attempts >= this.opts.maxRecoveries) {
91688
+ this.state.phase = "cooling";
91689
+ this.state.givenUp += 1;
91690
+ this.emit("gave-up", "recovery budget exhausted");
91691
+ return;
91692
+ }
91693
+ this.state.attempts += 1;
91694
+ let healed = false;
91695
+ try {
91696
+ healed = await this.runtime.refresh();
91697
+ } catch {
91698
+ healed = false;
91699
+ }
91700
+ if (healed && this.runtime.isAuthenticated()) {
91701
+ this.state.healCount += 1;
91702
+ this.markHealthy("cookie refresh restored the session");
91703
+ this.afterRecovery();
91704
+ return;
91705
+ }
91706
+ if (!this.state.canRelogin || !this.runtime.relogin) {
91707
+ this.state.phase = "cooling";
91708
+ this.state.givenUp += 1;
91709
+ this.emit(
91710
+ "gave-up",
91711
+ "session unauthenticated and no credentials available for re-login; provide email+password or a fresh appState"
91712
+ );
91713
+ return;
91714
+ }
91715
+ this.state.phase = "relogin";
91716
+ let fresh = null;
91717
+ try {
91718
+ fresh = await this.runtime.relogin();
91719
+ } catch {
91720
+ fresh = null;
91721
+ }
91722
+ if (Array.isArray(fresh) && fresh.length > 0) {
91723
+ this.state.reloginCount += 1;
91724
+ try {
91725
+ this.runtime.persistAppState?.(fresh);
91726
+ } catch {
91727
+ }
91728
+ this.markHealthy("credential re-login succeeded");
91729
+ this.afterRecovery();
91730
+ return;
91731
+ }
91732
+ this.state.phase = "cooling";
91733
+ this.state.givenUp += 1;
91734
+ this.emit("gave-up", "credential re-login did not return a fresh session");
91735
+ }
91736
+ markHealthy(detail) {
91737
+ this.state.phase = "healthy";
91738
+ this.state.consecutiveFailures = 0;
91739
+ this.state.lastHealthyAt = Date.now();
91740
+ this.state.lastDetail = detail;
91741
+ this.backoffMs = this.opts.intervalMs;
91742
+ this.emit("healed", detail);
91743
+ }
91744
+ afterRecovery() {
91745
+ try {
91746
+ this.runtime.restartMqtt?.();
91747
+ } catch {
91748
+ }
91749
+ }
91750
+ emit(type, detail) {
91751
+ if (!this.runtime.onEvent) return;
91752
+ try {
91753
+ this.runtime.onEvent({ type, detail, state: this.snapshot() });
91754
+ } catch {
91755
+ }
91756
+ }
91757
+ };
91758
+ function startRecovery(ctx, runtime, options = {}) {
91759
+ if (ctx.__recovery && typeof ctx.__recovery.stop === "function") {
91760
+ try {
91761
+ ctx.__recovery.stop();
91762
+ } catch {
91763
+ }
91764
+ }
91765
+ const supervisor = new RecoverySupervisor(runtime, options);
91766
+ ctx.__recovery = supervisor;
91767
+ supervisor.start();
91768
+ return supervisor;
91769
+ }
91770
+
91771
+ // src/utils/realtime-health.ts
91772
+ init_pacing();
91773
+ var SILENT_WINDOW_MS = 6 * 60 * 1e3;
91774
+ var PING_GRACE_MS = 45 * 1e3;
91775
+ var RealtimeHealth = class {
91776
+ constructor(now = () => Date.now()) {
91777
+ this.now = now;
91778
+ this.frames = 0;
91779
+ this.lastFrameAt = 0;
91780
+ this.connectedAt = 0;
91781
+ this.probes = 0;
91782
+ this.forcedReconnects = 0;
91783
+ this.lastPingAt = 0;
91784
+ this.startedAt = Date.now();
91785
+ this.startedAt = this.now();
91786
+ }
91787
+ /** Record any inbound frame (message, pong, anything from the broker). */
91788
+ noteFrame() {
91789
+ this.frames += 1;
91790
+ this.lastFrameAt = this.now();
91791
+ }
91792
+ /** Record a successful connect. */
91793
+ noteConnected() {
91794
+ this.connectedAt = this.now();
91795
+ this.lastFrameAt = this.now();
91796
+ }
91797
+ /** Record that the transport reports itself disconnected. */
91798
+ noteDisconnected() {
91799
+ this.lastFrameAt = 0;
91800
+ }
91801
+ /** Record an active ping being sent. */
91802
+ notePing() {
91803
+ this.lastPingAt = this.now();
91804
+ }
91805
+ sinceLastFrame() {
91806
+ const base = this.lastFrameAt || this.connectedAt || this.startedAt;
91807
+ return this.now() - base;
91808
+ }
91809
+ /**
91810
+ * Decide what to do about the current socket.
91811
+ *
91812
+ * - `disconnected` — the transport already knows it is down; reconnect.
91813
+ * - `dead` — connected on paper, silent past the window, and a ping raised no
91814
+ * traffic within the grace period; reconnect.
91815
+ * - `suspect` — silent past the window but a ping was only just sent; wait.
91816
+ * - `quiet` — silent but inside the window; normal.
91817
+ * - `healthy` — recent traffic.
91818
+ */
91819
+ verdict(connected) {
91820
+ if (!connected) return "disconnected";
91821
+ const silent = this.sinceLastFrame();
91822
+ if (silent < SILENT_WINDOW_MS / 2) return "healthy";
91823
+ if (silent < SILENT_WINDOW_MS) return "quiet";
91824
+ if (this.lastPingAt && this.now() - this.lastPingAt < PING_GRACE_MS) return "suspect";
91825
+ return "dead";
91826
+ }
91827
+ /** A snapshot for diagnostics. */
91828
+ state(connected) {
91829
+ return {
91830
+ connected,
91831
+ sinceLastFrameMs: this.sinceLastFrame(),
91832
+ frames: this.frames,
91833
+ probes: this.probes,
91834
+ forcedReconnects: this.forcedReconnects,
91835
+ verdict: this.verdict(connected)
91836
+ };
91837
+ }
91838
+ noteProbe() {
91839
+ this.probes += 1;
91840
+ }
91841
+ noteForcedReconnect() {
91842
+ this.forcedReconnects += 1;
91843
+ }
91844
+ };
91845
+ function startRealtimeSupervisor(options) {
91846
+ const health = new RealtimeHealth();
91847
+ const interval = options.intervalMs ?? 3 * 60 * 1e3;
91848
+ let stopped = false;
91849
+ let timer = null;
91850
+ const evaluate = () => {
91851
+ const connected = options.isConnected();
91852
+ const verdict = health.verdict(connected);
91853
+ health.noteProbe();
91854
+ if (verdict === "dead") {
91855
+ const pingSent = options.ping ? options.ping() : false;
91856
+ if (pingSent) {
91857
+ health.notePing();
91858
+ } else {
91859
+ health.noteForcedReconnect();
91860
+ options.reconnect("realtime socket silent and unpinnable");
91861
+ }
91862
+ }
91863
+ const state = health.state(connected);
91864
+ try {
91865
+ options.onVerdict?.(state);
91866
+ } catch {
91867
+ }
91868
+ return state;
91869
+ };
91870
+ const schedule = () => {
91871
+ if (stopped) return;
91872
+ timer = setTimeout(
91873
+ () => {
91874
+ timer = null;
91875
+ evaluate();
91876
+ schedule();
91877
+ },
91878
+ Math.max(1e3, humanDelay(interval, interval * 0.3))
91879
+ );
91880
+ if (timer && typeof timer.unref === "function") timer.unref();
91881
+ };
91882
+ schedule();
91883
+ return {
91884
+ health,
91885
+ checkNow: evaluate,
91886
+ stop: () => {
91887
+ stopped = true;
91888
+ if (timer) {
91889
+ clearTimeout(timer);
91890
+ timer = null;
91891
+ }
91892
+ }
91893
+ };
91894
+ }
91895
+
91538
91896
  // src/utils/index.ts
91539
91897
  async function json2(url2, jar2, qs2, options, ctx, customHeader) {
91540
91898
  try {
@@ -91752,6 +92110,7 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91752
92110
  const lsd = lsdConfig && lsdConfig.token || extractLsd(html3) || void 0;
91753
92111
  const spin = extractSpin(html3);
91754
92112
  const revision = spin.__rev || getFrom(html3, 'revision":', ",").trim();
92113
+ const asbdId = extractAsbdId(html3) || void 0;
91755
92114
  const clientIDData = findConfig("MqttWebDeviceID");
91756
92115
  const clientID = clientIDData ? clientIDData.clientID : void 0;
91757
92116
  const mqttConfigData = findConfig("MqttWebConfig");
@@ -91793,7 +92152,8 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91793
92152
  ...dtsgResult,
91794
92153
  ...lsd ? { lsd } : {},
91795
92154
  ...Object.keys(spin).length ? { __spin: spin } : {},
91796
- ...revision ? { revision } : {}
92155
+ ...revision ? { revision } : {},
92156
+ ...asbdId ? { asbdId } : {}
91797
92157
  };
91798
92158
  if (lsd && dtsg) {
91799
92159
  ctx.__cometTokens = { lsd, fb_dtsg: dtsg, fetchedAt: Date.now(), failures: 0 };
@@ -91802,8 +92162,95 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91802
92162
  return [ctx, defaultFuncs, {}];
91803
92163
  }
91804
92164
 
91805
- // src/core/models/loginHelper.ts
92165
+ // src/core/models/acquireCookies.ts
91806
92166
  init_axios2();
92167
+ function cookieStringFromObject(cookie, index3) {
92168
+ const name = cookie && (cookie.name || cookie.key);
92169
+ if (!name || cookie.value === void 0 || cookie.value === null) {
92170
+ throw new Error(`Invalid appState cookie at index ${index3}.`);
92171
+ }
92172
+ return formatAppStateCookie(cookie);
92173
+ }
92174
+ function writeCookie(jar2, cookieString) {
92175
+ const normalized = cookieString.replace(/;\s*domain=facebook\.com/i, "; domain=.facebook.com").replace(/;\s*domain=messenger\.com/i, "; domain=.messenger.com");
92176
+ const str = /(^|;)\s*domain=/i.test(normalized) ? normalized : `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`;
92177
+ try {
92178
+ jar2.setCookieSync(str, "https://facebook.com");
92179
+ return true;
92180
+ } catch {
92181
+ try {
92182
+ jar2.setCookieSync(
92183
+ `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`,
92184
+ "https://www.facebook.com"
92185
+ );
92186
+ return true;
92187
+ } catch {
92188
+ return false;
92189
+ }
92190
+ }
92191
+ }
92192
+ async function acquireCookies(jar2, credentials, globalOptions) {
92193
+ const appState = credentials.appState;
92194
+ if (Array.isArray(appState)) {
92195
+ let loaded = 0;
92196
+ appState.forEach((cookie, index3) => {
92197
+ const str = cookieStringFromObject(cookie, index3);
92198
+ if (str.indexOf("=") <= 0) throw new Error(`Invalid appState cookie at index ${index3}.`);
92199
+ if (writeCookie(jar2, str)) loaded++;
92200
+ });
92201
+ return { source: "appState", loaded };
92202
+ }
92203
+ if (typeof appState === "string") {
92204
+ const parts = appState.split(";").map((s) => s.trim()).filter(Boolean);
92205
+ let loaded = 0;
92206
+ parts.forEach((str, index3) => {
92207
+ if (str.indexOf("=") <= 0) throw new Error(`Invalid appState cookie at index ${index3}.`);
92208
+ if (writeCookie(jar2, str)) loaded++;
92209
+ });
92210
+ return { source: "appStateString", loaded };
92211
+ }
92212
+ if (credentials.email && credentials.password) {
92213
+ const url2 = "https://api.facebook.com/method/auth.login";
92214
+ const params = {
92215
+ access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
92216
+ format: "json",
92217
+ sdk_version: 2,
92218
+ email: credentials.email,
92219
+ locale: "en_US",
92220
+ password: credentials.password,
92221
+ generate_session_cookies: 1,
92222
+ sig: "c1c640010993db92e5afd11634ced864"
92223
+ };
92224
+ const xurl = `${url2}?${qs.stringify(params)}`;
92225
+ try {
92226
+ const legacyHeaders = getHeaders(url2, globalOptions, null, {
92227
+ "Content-Type": "application/x-www-form-urlencoded"
92228
+ });
92229
+ const resp = await axios_default.get(xurl, {
92230
+ headers: legacyHeaders,
92231
+ timeout: 6e4,
92232
+ validateStatus: (status) => status >= 200 && status < 600
92233
+ });
92234
+ if (resp.status !== 200) throw new Error("Wrong password / email");
92235
+ const sessionCookies = resp.data["session_cookies"] || [];
92236
+ if (!sessionCookies.length) throw new Error("No session cookies returned");
92237
+ let loaded = 0;
92238
+ for (const c of sessionCookies) {
92239
+ const str = `${c.name}=${c.value}; domain=.facebook.com; path=/; Secure; SameSite=None`;
92240
+ try {
92241
+ jar2.setCookieSync(str, "https://facebook.com");
92242
+ loaded++;
92243
+ } catch {
92244
+ }
92245
+ }
92246
+ if (loaded === 0) throw new Error("No session cookies could be stored");
92247
+ return { source: "credentials", loaded };
92248
+ } catch (e) {
92249
+ throw new Error(e && e.message ? e.message : "Wrong password / email");
92250
+ }
92251
+ }
92252
+ throw new Error("No cookie or credentials found. Please provide cookies or credentials.");
92253
+ }
91807
92254
 
91808
92255
  // src/deltas/apis/extra/addExternalModule.ts
91809
92256
  function addExternalModule_default(defaultFuncs, api, ctx) {
@@ -100404,7 +100851,9 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100404
100851
  attempts: 0,
100405
100852
  delay: MQTT_CONFIG.INITIAL_RETRY_DELAY,
100406
100853
  throttleDelay: MQTT_CONFIG.THROTTLE_DELAY,
100407
- generation: 0
100854
+ generation: 0,
100855
+ health: null,
100856
+ healthSupervisor: null
100408
100857
  };
100409
100858
  const log2 = (...args) => log(...args);
100410
100859
  function clearReconnectTimer() {
@@ -100418,6 +100867,13 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100418
100867
  clearInterval(lifecycle.presenceInterval);
100419
100868
  lifecycle.presenceInterval = null;
100420
100869
  }
100870
+ if (lifecycle.healthSupervisor) {
100871
+ try {
100872
+ lifecycle.healthSupervisor.stop();
100873
+ } catch (_3) {
100874
+ }
100875
+ lifecycle.healthSupervisor = null;
100876
+ }
100421
100877
  clearReconnectTimer();
100422
100878
  }
100423
100879
  function destroyClient() {
@@ -100545,6 +101001,8 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100545
101001
  });
100546
101002
  lifecycle.client = client2;
100547
101003
  ctx.mqttClient = client2;
101004
+ const health = new RealtimeHealth();
101005
+ lifecycle.health = health;
100548
101006
  let connected = false;
100549
101007
  let closing = false;
100550
101008
  let throttled = false;
@@ -100564,12 +101022,14 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100564
101022
  if (generation !== lifecycle.generation) return;
100565
101023
  connected = false;
100566
101024
  ctx.mqttConnected = false;
101025
+ health.noteDisconnected();
100567
101026
  });
100568
101027
  client2.on("close", () => {
100569
101028
  if (generation !== lifecycle.generation) return;
100570
101029
  if (connected) log2("MQTT connection closed.");
100571
101030
  connected = false;
100572
101031
  ctx.mqttConnected = false;
101032
+ health.noteDisconnected();
100573
101033
  if (closing) return;
100574
101034
  closing = true;
100575
101035
  if (authFailure) {
@@ -100587,6 +101047,7 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100587
101047
  client2.on("connect", () => {
100588
101048
  if (generation !== lifecycle.generation) return;
100589
101049
  connected = true;
101050
+ health.noteConnected();
100590
101051
  client2.subscribe(MQTT_TOPICS, (subErr) => {
100591
101052
  if (subErr) {
100592
101053
  error("Failed to subscribe to MQTT topics:", subErr);
@@ -100623,6 +101084,49 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100623
101084
  ctx.mqttConnected = true;
100624
101085
  ctx.lastMqttError = null;
100625
101086
  resetBackoff();
101087
+ if (lifecycle.healthSupervisor) {
101088
+ try {
101089
+ lifecycle.healthSupervisor.stop();
101090
+ } catch (_3) {
101091
+ }
101092
+ }
101093
+ lifecycle.healthSupervisor = startRealtimeSupervisor({
101094
+ isConnected: () => generation === lifecycle.generation && client2.connected,
101095
+ reconnect: (reason) => {
101096
+ if (generation !== lifecycle.generation) return;
101097
+ if (lifecycle.stopped || ctx.loggedIn === false) return;
101098
+ log2(`MQTT ${reason}; recycling the socket`);
101099
+ try {
101100
+ client2.end(true);
101101
+ } catch (_3) {
101102
+ }
101103
+ const gen = generation;
101104
+ setTimeout(() => {
101105
+ if (gen !== lifecycle.generation) return;
101106
+ if (lifecycle.stopped || ctx.loggedIn === false) return;
101107
+ if (lifecycle.client && lifecycle.client.connected) return;
101108
+ if (lifecycle.reconnectTimer) return;
101109
+ scheduleReconnect("realtime socket recycled");
101110
+ }, 5e3).unref?.();
101111
+ },
101112
+ ping: () => {
101113
+ try {
101114
+ client2.ping();
101115
+ return true;
101116
+ } catch {
101117
+ return false;
101118
+ }
101119
+ },
101120
+ onVerdict: (state) => {
101121
+ if (state.verdict === "dead") {
101122
+ warn(
101123
+ "MQTT health",
101124
+ `silent for ${Math.round(state.sinceLastFrameMs / 1e3)}s; probing`
101125
+ );
101126
+ }
101127
+ }
101128
+ });
101129
+ ctx.__mqttHealth = () => lifecycle.health ? lifecycle.health.state(generation === lifecycle.generation && !!client2.connected) : null;
100626
101130
  api.getBotInitialData().then((botData) => {
100627
101131
  if (botData) {
100628
101132
  log(`Logged in as: ${botData.name || "Facebook User"} (${botData.uid || ctx.userID})`);
@@ -100633,6 +101137,7 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100633
101137
  });
100634
101138
  client2.on("message", async (topic, message, _packet) => {
100635
101139
  try {
101140
+ health.noteFrame();
100636
101141
  ctx.lastMessageTime = Date.now();
100637
101142
  ctx.messageCount = (ctx.messageCount || 0) + 1;
100638
101143
  const jsonMessage = JSON.parse(message.toString());
@@ -100718,6 +101223,16 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100718
101223
  this.stop();
100719
101224
  if (typeof cb === "function") cb();
100720
101225
  }
101226
+ /** Live connection-health snapshot, for diagnostics/alerting. */
101227
+ getHealth() {
101228
+ if (!lifecycle.health) return null;
101229
+ const connected = !lifecycle.stopped && !!lifecycle.client && !!lifecycle.client.connected;
101230
+ return lifecycle.health.state(connected);
101231
+ }
101232
+ /** Is this listener currently stopped? */
101233
+ isStopped() {
101234
+ return lifecycle.stopped;
101235
+ }
100721
101236
  }
100722
101237
  const msgEmitter = new MessageEmitter();
100723
101238
  lifecycle.stopped = false;
@@ -100886,76 +101401,8 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
100886
101401
  const jar2 = getJar();
100887
101402
  log("Logging in...");
100888
101403
  const appState = credentials.appState;
100889
- if (appState) {
100890
- let cookieStrings = [];
100891
- if (Array.isArray(appState)) {
100892
- cookieStrings = appState.map((c, index3) => {
100893
- const name = c && (c.name || c.key);
100894
- if (!name || c.value === void 0 || c.value === null) {
100895
- throw new Error(`Invalid appState cookie at index ${index3}.`);
100896
- }
100897
- return formatAppStateCookie(c);
100898
- });
100899
- } else if (typeof appState === "string") {
100900
- cookieStrings = appState.split(";").map((s) => s.trim()).filter(Boolean);
100901
- } else {
100902
- throw new Error(
100903
- "Invalid appState format. Please provide an array of cookie objects or a cookie string."
100904
- );
100905
- }
100906
- cookieStrings.forEach((cookieString, index3) => {
100907
- if (cookieString.indexOf("=") <= 0) {
100908
- throw new Error(`Invalid appState cookie at index ${index3}.`);
100909
- }
100910
- const normalized = cookieString.replace(/;\s*domain=facebook\.com/i, "; domain=.facebook.com").replace(/;\s*domain=messenger\.com/i, "; domain=.messenger.com");
100911
- const str = /(^|;)\s*domain=/i.test(normalized) ? normalized : `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`;
100912
- try {
100913
- jar2.setCookieSync(str, "https://facebook.com");
100914
- } catch {
100915
- try {
100916
- jar2.setCookieSync(`${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`, "https://www.facebook.com");
100917
- } catch {
100918
- }
100919
- }
100920
- });
100921
- } else if (credentials.email && credentials.password) {
100922
- const url2 = "https://api.facebook.com/method/auth.login";
100923
- const params = {
100924
- access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
100925
- format: "json",
100926
- sdk_version: 2,
100927
- email: credentials.email,
100928
- locale: "en_US",
100929
- password: credentials.password,
100930
- generate_session_cookies: 1,
100931
- sig: "c1c640010993db92e5afd11634ced864"
100932
- };
100933
- const query = qs.stringify(params);
100934
- const xurl = `${url2}?${query}`;
100935
- try {
100936
- const legacyHeaders = getHeaders(
100937
- url2,
100938
- globalOptions,
100939
- null,
100940
- { "Content-Type": "application/x-www-form-urlencoded" }
100941
- );
100942
- const resp2 = await axios_default.get(xurl, {
100943
- headers: legacyHeaders,
100944
- timeout: 6e4,
100945
- validateStatus: (status) => status >= 200 && status < 600
100946
- });
100947
- if (resp2.status !== 200) {
100948
- throw new Error("Wrong password / email");
100949
- }
100950
- const cstrs = resp2.data["session_cookies"].map((c) => `${c.name}=${c.value}`);
100951
- cstrs.forEach((cstr) => {
100952
- const domain = ".facebook.com";
100953
- const str = `${cstr}; domain=${domain}; path=/; Secure; SameSite=None`;
100954
- jar2.setCookieSync(str, "https://facebook.com");
100955
- });
100956
- } catch (e) {
100957
- throw new Error("Wrong password / email");
100958
- }
101404
+ if (appState || credentials.email && credentials.password) {
101405
+ await acquireCookies(jar2, credentials, globalOptions);
100959
101406
  } else {
100960
101407
  throw new Error("No cookie or credentials found. Please provide cookies or credentials.");
100961
101408
  }
@@ -101094,6 +101541,81 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
101094
101541
  }
101095
101542
  }
101096
101543
  ctx.__stopCookieKeepAlive = scheduleSessionKeepAlive(ctx, defaultFuncs, 15 * 60 * 1e3);
101544
+ ctx.__credentials = {
101545
+ email: credentials.email,
101546
+ password: credentials.password
101547
+ };
101548
+ ctx.__loginGlobalOptions = globalOptions;
101549
+ ctx.__persistPath = globalOptions.appStatePath;
101550
+ if (globalOptions.recovery !== false) {
101551
+ const recoveryOptions = typeof globalOptions.recovery === "object" ? globalOptions.recovery : {};
101552
+ const supervisor = startRecovery(
101553
+ ctx,
101554
+ {
101555
+ ctx,
101556
+ isAuthenticated: () => hasAuthenticatedSession(ctx.jar),
101557
+ probeLiveness: async () => {
101558
+ try {
101559
+ const res = await defaultFuncs.get(
101560
+ "https://www.facebook.com/",
101561
+ ctx.jar,
101562
+ null,
101563
+ ctx.globalOptions,
101564
+ { noRef: true }
101565
+ );
101566
+ const html3 = typeof res === "string" ? res : res && res.body;
101567
+ if (typeof html3 !== "string" || !html3) return true;
101568
+ return !isLoggedOutDocument(html3);
101569
+ } catch {
101570
+ return true;
101571
+ }
101572
+ },
101573
+ refresh: () => repairSession(ctx, defaultFuncs),
101574
+ canRelogin: Boolean(credentials.email && credentials.password),
101575
+ relogin: async () => {
101576
+ if (!credentials.email || !credentials.password) return null;
101577
+ await sleep(retryDelay(0, 2e3, 15e3));
101578
+ await acquireCookies(ctx.jar, credentials, globalOptions);
101579
+ await repairSession(ctx, defaultFuncs);
101580
+ if (!hasAuthenticatedSession(ctx.jar)) return null;
101581
+ return getAppState(ctx.jar);
101582
+ },
101583
+ persistAppState: (state) => {
101584
+ const path2 = globalOptions.appStatePath;
101585
+ if (!path2) return;
101586
+ try {
101587
+ const fs2 = __require("fs");
101588
+ fs2.writeFileSync(path2, JSON.stringify(state, null, 2));
101589
+ log(`Recovery: refreshed appState written to ${path2}`);
101590
+ } catch (persistError) {
101591
+ warn("Recovery: could not persist appState:", persistError?.message);
101592
+ }
101593
+ },
101594
+ restartMqtt: () => {
101595
+ try {
101596
+ if (typeof ctx.__restartMqtt === "function") {
101597
+ ctx.__restartMqtt();
101598
+ } else if (typeof ctx.reconnectMqtt === "function" && activeListener) {
101599
+ void ctx.reconnectMqtt();
101600
+ }
101601
+ } catch {
101602
+ }
101603
+ },
101604
+ onEvent: (event) => {
101605
+ if (event.type === "gave-up") {
101606
+ warn("Recovery", event.detail);
101607
+ } else if (event.type === "relogin" || event.type === "healed") {
101608
+ log(`Recovery: ${event.detail}`);
101609
+ }
101610
+ }
101611
+ },
101612
+ recoveryOptions
101613
+ );
101614
+ api.getRecoveryState = () => supervisor.snapshot();
101615
+ api.checkRecoveryNow = () => supervisor.checkNow();
101616
+ api.stopRecovery = () => supervisor.stop();
101617
+ api.startRecovery = () => supervisor.start();
101618
+ }
101097
101619
  if (globalOptions.liveness !== false) {
101098
101620
  const liveness = startLiveness(ctx, {
101099
101621
  foregroundPing: () => log("Session active"),
@@ -101276,4 +101798,4 @@ lodash/lodash.js:
101276
101798
  *)
101277
101799
  */
101278
101800
 
101279
- export { applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, index_default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isStaleTokenError, login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, public_exports as publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
101801
+ export { PING_GRACE_MS, RealtimeHealth, RecoverySupervisor, SILENT_WINDOW_MS, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, index_default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, public_exports as publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };