@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.cjs CHANGED
@@ -25142,13 +25142,15 @@ function getHeaders(url2, options, ctx, customHeader) {
25142
25142
  headers["priority"] = "u=0, i";
25143
25143
  }
25144
25144
  if (isXhr) {
25145
- headers["X-Requested-With"] = "XMLHttpRequest";
25146
- headers["Pragma"] = "no-cache";
25147
- headers["Cache-Control"] = "no-cache";
25145
+ if (!isGraphQl) {
25146
+ headers["X-Requested-With"] = "XMLHttpRequest";
25147
+ headers["Pragma"] = "no-cache";
25148
+ headers["Cache-Control"] = "no-cache";
25149
+ }
25148
25150
  headers["priority"] = "u=1, i";
25149
25151
  }
25150
25152
  if (isGraphQl) {
25151
- headers["x-asbd-id"] = "129477";
25153
+ headers["x-asbd-id"] = String(ctx?.asbdId || DEFAULT_ASBD_ID);
25152
25154
  const lsd = ctx?.lsd || ctx?.fb_lsd;
25153
25155
  if (lsd) headers["x-fb-lsd"] = sanitizeHeaderValue2(lsd);
25154
25156
  const friendlyName = customHeader?.["x-fb-friendly-name"] || customHeader?.["X-Fb-Friendly-Name"];
@@ -25219,10 +25221,11 @@ function getFingerprintParams(options) {
25219
25221
  dpr: String(profile.devicePixelRatio)
25220
25222
  };
25221
25223
  }
25222
- var meta;
25224
+ var DEFAULT_ASBD_ID, meta;
25223
25225
  var init_headers = __esm({
25224
25226
  "src/utils/headers.ts"() {
25225
25227
  init_user_agents();
25228
+ DEFAULT_ASBD_ID = "359341";
25226
25229
  meta = (prop2) => new RegExp(`<meta property="${prop2}" content="([^"]*)"`);
25227
25230
  }
25228
25231
  });
@@ -75560,9 +75563,13 @@ __export(utils_exports2, {
75560
75563
  NUM_TO_MONTH: () => NUM_TO_MONTH,
75561
75564
  NetworkError: () => NetworkError,
75562
75565
  NotFoundError: () => NotFoundError,
75566
+ PING_GRACE_MS: () => PING_GRACE_MS,
75563
75567
  PermissionError: () => PermissionError,
75564
75568
  RateLimitError: () => RateLimitError,
75569
+ RealtimeHealth: () => RealtimeHealth,
75570
+ RecoverySupervisor: () => RecoverySupervisor,
75565
75571
  SESSION_COOKIE_NAMES: () => SESSION_COOKIE_NAMES,
75572
+ SILENT_WINDOW_MS: () => SILENT_WINDOW_MS,
75566
75573
  TimeoutError: () => TimeoutError,
75567
75574
  ValidationError: () => ValidationError,
75568
75575
  WINDOW_MS: () => WINDOW_MS,
@@ -75587,6 +75594,7 @@ __export(utils_exports2, {
75587
75594
  delay: () => delay2,
75588
75595
  ensureCometTokens: () => ensureCometTokens,
75589
75596
  error: () => error,
75597
+ extractAsbdId: () => extractAsbdId,
75590
75598
  extractAttachmentIds: () => extractAttachmentIds,
75591
75599
  extractCookies: () => extractCookies,
75592
75600
  extractCookiesFromBody: () => extractCookiesFromBody,
@@ -75637,6 +75645,7 @@ __export(utils_exports2, {
75637
75645
  invalidateCometTokens: () => invalidateCometTokens,
75638
75646
  isAuthFailure: () => isAuthFailure,
75639
75647
  isBreakerOpen: () => isBreakerOpen,
75648
+ isLoggedOutDocument: () => isLoggedOutDocument,
75640
75649
  isReadableStream: () => isReadableStream2,
75641
75650
  isStaleTokenError: () => isStaleTokenError,
75642
75651
  json: () => json2,
@@ -75674,6 +75683,8 @@ __export(utils_exports2, {
75674
75683
  setProxy: () => setProxy2,
75675
75684
  sleep: () => sleep,
75676
75685
  startLiveness: () => startLiveness,
75686
+ startRealtimeSupervisor: () => startRealtimeSupervisor,
75687
+ startRecovery: () => startRecovery,
75677
75688
  success: () => success,
75678
75689
  tripBreaker: () => tripBreaker,
75679
75690
  uploadGroupImage: () => uploadGroupImage,
@@ -91030,6 +91041,9 @@ function extractLsd(html3) {
91030
91041
  function extractDtsg(html3) {
91031
91042
  return getFrom3(html3, '["DTSGInitialData",[],{"token":"', '"}') || getFrom3(html3, 'name="fb_dtsg" value="', '"') || (html3.match(/"DTSGInitialData",\[\],\{"token":"([^"]+)"/) || [])[1] || "";
91032
91043
  }
91044
+ function extractAsbdId(html3) {
91045
+ return (html3.match(/"asbd_id":(\d+)/) || [])[1] || (html3.match(/x-asbd-id["']?\s*[:=]\s*["']?(\d+)/i) || [])[1] || (html3.match(/"ASBD_ID":(\d+)/) || [])[1] || "";
91046
+ }
91033
91047
  function extractSpin(html3) {
91034
91048
  const out = {};
91035
91049
  const spinB = (html3.match(/"__spin_b":"([^"]+)"/) || [])[1];
@@ -91096,6 +91110,8 @@ async function ensureCometTokens(ctx, defaultFuncs, force = false) {
91096
91110
  const spin = extractSpin(html3);
91097
91111
  if (Object.keys(spin).length) ctx.__spin = { ...ctx.__spin || {}, ...spin };
91098
91112
  if (spin.__rev) ctx.revision = spin.__rev;
91113
+ const asbd = extractAsbdId(html3);
91114
+ if (asbd) ctx.asbdId = asbd;
91099
91115
  ctx.lsd = lsd;
91100
91116
  ctx.fb_dtsg = dtsg;
91101
91117
  ctx.jazoest = recomputeJazoest(dtsg);
@@ -91366,6 +91382,13 @@ function isAuthFailure(value) {
91366
91382
  }
91367
91383
  return false;
91368
91384
  }
91385
+ function isLoggedOutDocument(html3) {
91386
+ if (typeof html3 !== "string" || html3.length < 200) return false;
91387
+ if (!/<html/i.test(html3)) return false;
91388
+ const hasAuthedTokens = /DTSGInitialData|"LSD",\[\]/.test(html3);
91389
+ 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);
91390
+ return looksLikeLogin && !hasAuthedTokens;
91391
+ }
91369
91392
  async function withSessionRetry(ctx, defaultFuncs, operation, options = {}) {
91370
91393
  const attempts = Math.max(1, options.attempts ?? 2);
91371
91394
  let lastError;
@@ -91472,6 +91495,8 @@ async function warmUpSession(ctx, getter, options = {}) {
91472
91495
  const spin = extractSpin(html3);
91473
91496
  if (Object.keys(spin).length) ctx.__spin = { ...ctx.__spin || {}, ...spin };
91474
91497
  if (spin.__rev) ctx.revision = spin.__rev;
91498
+ const asbd = extractAsbdId(html3);
91499
+ if (asbd) ctx.asbdId = asbd;
91475
91500
  tokensSeeded = true;
91476
91501
  }
91477
91502
  } else if (url2.includes("/messages")) {
@@ -91570,6 +91595,339 @@ async function awaitProactiveSlot(ctx, maxWaitMs = 15 * 60 * 1e3) {
91570
91595
  return false;
91571
91596
  }
91572
91597
 
91598
+ // src/utils/recovery.ts
91599
+ init_pacing();
91600
+ init_safety();
91601
+ var DEFAULTS = {
91602
+ intervalMs: 10 * 60 * 1e3,
91603
+ failureThreshold: 3,
91604
+ maxRecoveries: 5
91605
+ };
91606
+ function randJitter(base, spread3 = 0.3) {
91607
+ return Math.floor(base * (1 - spread3 / 2 + Math.random() * spread3));
91608
+ }
91609
+ var RecoverySupervisor = class {
91610
+ constructor(runtime, options = {}) {
91611
+ this.timer = null;
91612
+ this.stopped = true;
91613
+ this.running = false;
91614
+ this.runtime = runtime;
91615
+ this.opts = {
91616
+ intervalMs: options.intervalMs ?? DEFAULTS.intervalMs,
91617
+ failureThreshold: options.failureThreshold ?? DEFAULTS.failureThreshold,
91618
+ maxRecoveries: options.maxRecoveries ?? DEFAULTS.maxRecoveries,
91619
+ noRelogin: options.noRelogin ?? false
91620
+ };
91621
+ this.backoffMs = this.opts.intervalMs;
91622
+ this.state = {
91623
+ phase: "healthy",
91624
+ checks: 0,
91625
+ consecutiveFailures: 0,
91626
+ healCount: 0,
91627
+ attempts: 0,
91628
+ reloginCount: 0,
91629
+ givenUp: 0,
91630
+ lastHealthyAt: Date.now(),
91631
+ nextCheckMs: 0,
91632
+ canRelogin: typeof runtime.relogin === "function" && !this.opts.noRelogin && (runtime.canRelogin !== void 0 ? runtime.canRelogin : true)
91633
+ };
91634
+ }
91635
+ /** A point-in-time copy of the supervisor state. */
91636
+ snapshot() {
91637
+ return { ...this.state, nextCheckMs: Math.max(0, this.nextCheckAt() - Date.now()) };
91638
+ }
91639
+ isRunning() {
91640
+ return !this.stopped;
91641
+ }
91642
+ /** Start the supervisor. Idempotent. */
91643
+ start() {
91644
+ if (!this.stopped) return;
91645
+ this.stopped = false;
91646
+ if (this.state.phase === "stopped") this.state.phase = "healthy";
91647
+ this.startTimer(false);
91648
+ this.emit("check", "recovery supervisor started");
91649
+ }
91650
+ /** Stop the supervisor and clear any pending timer. */
91651
+ stop() {
91652
+ this.stopped = true;
91653
+ if (this.timer) {
91654
+ clearTimeout(this.timer);
91655
+ this.timer = null;
91656
+ }
91657
+ this.state.phase = "stopped";
91658
+ this.emit("stopped", "recovery supervisor stopped");
91659
+ }
91660
+ /**
91661
+ * Run one health check immediately (used by tests and by `checkNow()`).
91662
+ * Returns the resulting state. Never throws.
91663
+ */
91664
+ async checkNow() {
91665
+ if (this.running) return this.snapshot();
91666
+ this.running = true;
91667
+ try {
91668
+ await this.runOnce();
91669
+ } catch {
91670
+ } finally {
91671
+ this.running = false;
91672
+ }
91673
+ return this.snapshot();
91674
+ }
91675
+ nextCheckAt() {
91676
+ return this.state.nextCheckMs && this.timer ? Date.now() + this.state.nextCheckMs : 0;
91677
+ }
91678
+ startTimer(delayOverride) {
91679
+ if (this.stopped) return;
91680
+ if (this.timer) {
91681
+ clearTimeout(this.timer);
91682
+ this.timer = null;
91683
+ }
91684
+ const delay3 = delayOverride === false ? this.backoffMs : delayOverride ?? this.backoffMs;
91685
+ const jittered = randJitter(delay3);
91686
+ this.state.nextCheckMs = jittered;
91687
+ this.timer = setTimeout(() => {
91688
+ this.timer = null;
91689
+ this.state.nextCheckMs = 0;
91690
+ void this.checkNow().finally(() => {
91691
+ if (!this.stopped) this.startTimer();
91692
+ });
91693
+ }, jittered);
91694
+ if (this.timer && typeof this.timer.unref === "function") this.timer.unref();
91695
+ }
91696
+ async runOnce() {
91697
+ this.state.checks += 1;
91698
+ this.emit("check", `health check #${this.state.checks}`);
91699
+ let healthy = this.runtime.isAuthenticated();
91700
+ if (healthy && this.runtime.probeLiveness) {
91701
+ try {
91702
+ healthy = await this.runtime.probeLiveness();
91703
+ } catch {
91704
+ healthy = true;
91705
+ }
91706
+ }
91707
+ if (healthy) {
91708
+ this.markHealthy("session authenticated");
91709
+ return;
91710
+ }
91711
+ this.state.consecutiveFailures += 1;
91712
+ this.state.phase = "refreshing";
91713
+ this.emit("unhealthy", `unauthenticated (${this.state.consecutiveFailures}/${this.opts.failureThreshold})`);
91714
+ if (this.state.consecutiveFailures < this.opts.failureThreshold) {
91715
+ return;
91716
+ }
91717
+ if (isBreakerOpen(this.runtime.ctx)) {
91718
+ this.state.phase = "cooling";
91719
+ this.emit("cooling", "safety breaker open; delaying recovery");
91720
+ return;
91721
+ }
91722
+ if (this.state.attempts >= this.opts.maxRecoveries) {
91723
+ this.state.phase = "cooling";
91724
+ this.state.givenUp += 1;
91725
+ this.emit("gave-up", "recovery budget exhausted");
91726
+ return;
91727
+ }
91728
+ this.state.attempts += 1;
91729
+ let healed = false;
91730
+ try {
91731
+ healed = await this.runtime.refresh();
91732
+ } catch {
91733
+ healed = false;
91734
+ }
91735
+ if (healed && this.runtime.isAuthenticated()) {
91736
+ this.state.healCount += 1;
91737
+ this.markHealthy("cookie refresh restored the session");
91738
+ this.afterRecovery();
91739
+ return;
91740
+ }
91741
+ if (!this.state.canRelogin || !this.runtime.relogin) {
91742
+ this.state.phase = "cooling";
91743
+ this.state.givenUp += 1;
91744
+ this.emit(
91745
+ "gave-up",
91746
+ "session unauthenticated and no credentials available for re-login; provide email+password or a fresh appState"
91747
+ );
91748
+ return;
91749
+ }
91750
+ this.state.phase = "relogin";
91751
+ let fresh = null;
91752
+ try {
91753
+ fresh = await this.runtime.relogin();
91754
+ } catch {
91755
+ fresh = null;
91756
+ }
91757
+ if (Array.isArray(fresh) && fresh.length > 0) {
91758
+ this.state.reloginCount += 1;
91759
+ try {
91760
+ this.runtime.persistAppState?.(fresh);
91761
+ } catch {
91762
+ }
91763
+ this.markHealthy("credential re-login succeeded");
91764
+ this.afterRecovery();
91765
+ return;
91766
+ }
91767
+ this.state.phase = "cooling";
91768
+ this.state.givenUp += 1;
91769
+ this.emit("gave-up", "credential re-login did not return a fresh session");
91770
+ }
91771
+ markHealthy(detail) {
91772
+ this.state.phase = "healthy";
91773
+ this.state.consecutiveFailures = 0;
91774
+ this.state.lastHealthyAt = Date.now();
91775
+ this.state.lastDetail = detail;
91776
+ this.backoffMs = this.opts.intervalMs;
91777
+ this.emit("healed", detail);
91778
+ }
91779
+ afterRecovery() {
91780
+ try {
91781
+ this.runtime.restartMqtt?.();
91782
+ } catch {
91783
+ }
91784
+ }
91785
+ emit(type, detail) {
91786
+ if (!this.runtime.onEvent) return;
91787
+ try {
91788
+ this.runtime.onEvent({ type, detail, state: this.snapshot() });
91789
+ } catch {
91790
+ }
91791
+ }
91792
+ };
91793
+ function startRecovery(ctx, runtime, options = {}) {
91794
+ if (ctx.__recovery && typeof ctx.__recovery.stop === "function") {
91795
+ try {
91796
+ ctx.__recovery.stop();
91797
+ } catch {
91798
+ }
91799
+ }
91800
+ const supervisor = new RecoverySupervisor(runtime, options);
91801
+ ctx.__recovery = supervisor;
91802
+ supervisor.start();
91803
+ return supervisor;
91804
+ }
91805
+
91806
+ // src/utils/realtime-health.ts
91807
+ init_pacing();
91808
+ var SILENT_WINDOW_MS = 6 * 60 * 1e3;
91809
+ var PING_GRACE_MS = 45 * 1e3;
91810
+ var RealtimeHealth = class {
91811
+ constructor(now = () => Date.now()) {
91812
+ this.now = now;
91813
+ this.frames = 0;
91814
+ this.lastFrameAt = 0;
91815
+ this.connectedAt = 0;
91816
+ this.probes = 0;
91817
+ this.forcedReconnects = 0;
91818
+ this.lastPingAt = 0;
91819
+ this.startedAt = Date.now();
91820
+ this.startedAt = this.now();
91821
+ }
91822
+ /** Record any inbound frame (message, pong, anything from the broker). */
91823
+ noteFrame() {
91824
+ this.frames += 1;
91825
+ this.lastFrameAt = this.now();
91826
+ }
91827
+ /** Record a successful connect. */
91828
+ noteConnected() {
91829
+ this.connectedAt = this.now();
91830
+ this.lastFrameAt = this.now();
91831
+ }
91832
+ /** Record that the transport reports itself disconnected. */
91833
+ noteDisconnected() {
91834
+ this.lastFrameAt = 0;
91835
+ }
91836
+ /** Record an active ping being sent. */
91837
+ notePing() {
91838
+ this.lastPingAt = this.now();
91839
+ }
91840
+ sinceLastFrame() {
91841
+ const base = this.lastFrameAt || this.connectedAt || this.startedAt;
91842
+ return this.now() - base;
91843
+ }
91844
+ /**
91845
+ * Decide what to do about the current socket.
91846
+ *
91847
+ * - `disconnected` — the transport already knows it is down; reconnect.
91848
+ * - `dead` — connected on paper, silent past the window, and a ping raised no
91849
+ * traffic within the grace period; reconnect.
91850
+ * - `suspect` — silent past the window but a ping was only just sent; wait.
91851
+ * - `quiet` — silent but inside the window; normal.
91852
+ * - `healthy` — recent traffic.
91853
+ */
91854
+ verdict(connected) {
91855
+ if (!connected) return "disconnected";
91856
+ const silent = this.sinceLastFrame();
91857
+ if (silent < SILENT_WINDOW_MS / 2) return "healthy";
91858
+ if (silent < SILENT_WINDOW_MS) return "quiet";
91859
+ if (this.lastPingAt && this.now() - this.lastPingAt < PING_GRACE_MS) return "suspect";
91860
+ return "dead";
91861
+ }
91862
+ /** A snapshot for diagnostics. */
91863
+ state(connected) {
91864
+ return {
91865
+ connected,
91866
+ sinceLastFrameMs: this.sinceLastFrame(),
91867
+ frames: this.frames,
91868
+ probes: this.probes,
91869
+ forcedReconnects: this.forcedReconnects,
91870
+ verdict: this.verdict(connected)
91871
+ };
91872
+ }
91873
+ noteProbe() {
91874
+ this.probes += 1;
91875
+ }
91876
+ noteForcedReconnect() {
91877
+ this.forcedReconnects += 1;
91878
+ }
91879
+ };
91880
+ function startRealtimeSupervisor(options) {
91881
+ const health = new RealtimeHealth();
91882
+ const interval = options.intervalMs ?? 3 * 60 * 1e3;
91883
+ let stopped = false;
91884
+ let timer = null;
91885
+ const evaluate = () => {
91886
+ const connected = options.isConnected();
91887
+ const verdict = health.verdict(connected);
91888
+ health.noteProbe();
91889
+ if (verdict === "dead") {
91890
+ const pingSent = options.ping ? options.ping() : false;
91891
+ if (pingSent) {
91892
+ health.notePing();
91893
+ } else {
91894
+ health.noteForcedReconnect();
91895
+ options.reconnect("realtime socket silent and unpinnable");
91896
+ }
91897
+ }
91898
+ const state = health.state(connected);
91899
+ try {
91900
+ options.onVerdict?.(state);
91901
+ } catch {
91902
+ }
91903
+ return state;
91904
+ };
91905
+ const schedule = () => {
91906
+ if (stopped) return;
91907
+ timer = setTimeout(
91908
+ () => {
91909
+ timer = null;
91910
+ evaluate();
91911
+ schedule();
91912
+ },
91913
+ Math.max(1e3, humanDelay(interval, interval * 0.3))
91914
+ );
91915
+ if (timer && typeof timer.unref === "function") timer.unref();
91916
+ };
91917
+ schedule();
91918
+ return {
91919
+ health,
91920
+ checkNow: evaluate,
91921
+ stop: () => {
91922
+ stopped = true;
91923
+ if (timer) {
91924
+ clearTimeout(timer);
91925
+ timer = null;
91926
+ }
91927
+ }
91928
+ };
91929
+ }
91930
+
91573
91931
  // src/utils/index.ts
91574
91932
  async function json2(url2, jar2, qs2, options, ctx, customHeader) {
91575
91933
  try {
@@ -91787,6 +92145,7 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91787
92145
  const lsd = lsdConfig && lsdConfig.token || extractLsd(html3) || void 0;
91788
92146
  const spin = extractSpin(html3);
91789
92147
  const revision = spin.__rev || getFrom(html3, 'revision":', ",").trim();
92148
+ const asbdId = extractAsbdId(html3) || void 0;
91790
92149
  const clientIDData = findConfig("MqttWebDeviceID");
91791
92150
  const clientID = clientIDData ? clientIDData.clientID : void 0;
91792
92151
  const mqttConfigData = findConfig("MqttWebConfig");
@@ -91828,7 +92187,8 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91828
92187
  ...dtsgResult,
91829
92188
  ...lsd ? { lsd } : {},
91830
92189
  ...Object.keys(spin).length ? { __spin: spin } : {},
91831
- ...revision ? { revision } : {}
92190
+ ...revision ? { revision } : {},
92191
+ ...asbdId ? { asbdId } : {}
91832
92192
  };
91833
92193
  if (lsd && dtsg) {
91834
92194
  ctx.__cometTokens = { lsd, fb_dtsg: dtsg, fetchedAt: Date.now(), failures: 0 };
@@ -91837,8 +92197,95 @@ async function buildAPI(html3, jar2, netData, globalOptions, fbLinkFunc, errorRe
91837
92197
  return [ctx, defaultFuncs, {}];
91838
92198
  }
91839
92199
 
91840
- // src/core/models/loginHelper.ts
92200
+ // src/core/models/acquireCookies.ts
91841
92201
  init_axios2();
92202
+ function cookieStringFromObject(cookie, index3) {
92203
+ const name = cookie && (cookie.name || cookie.key);
92204
+ if (!name || cookie.value === void 0 || cookie.value === null) {
92205
+ throw new Error(`Invalid appState cookie at index ${index3}.`);
92206
+ }
92207
+ return formatAppStateCookie(cookie);
92208
+ }
92209
+ function writeCookie(jar2, cookieString) {
92210
+ const normalized = cookieString.replace(/;\s*domain=facebook\.com/i, "; domain=.facebook.com").replace(/;\s*domain=messenger\.com/i, "; domain=.messenger.com");
92211
+ const str = /(^|;)\s*domain=/i.test(normalized) ? normalized : `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`;
92212
+ try {
92213
+ jar2.setCookieSync(str, "https://facebook.com");
92214
+ return true;
92215
+ } catch {
92216
+ try {
92217
+ jar2.setCookieSync(
92218
+ `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`,
92219
+ "https://www.facebook.com"
92220
+ );
92221
+ return true;
92222
+ } catch {
92223
+ return false;
92224
+ }
92225
+ }
92226
+ }
92227
+ async function acquireCookies(jar2, credentials, globalOptions) {
92228
+ const appState = credentials.appState;
92229
+ if (Array.isArray(appState)) {
92230
+ let loaded = 0;
92231
+ appState.forEach((cookie, index3) => {
92232
+ const str = cookieStringFromObject(cookie, index3);
92233
+ if (str.indexOf("=") <= 0) throw new Error(`Invalid appState cookie at index ${index3}.`);
92234
+ if (writeCookie(jar2, str)) loaded++;
92235
+ });
92236
+ return { source: "appState", loaded };
92237
+ }
92238
+ if (typeof appState === "string") {
92239
+ const parts = appState.split(";").map((s) => s.trim()).filter(Boolean);
92240
+ let loaded = 0;
92241
+ parts.forEach((str, index3) => {
92242
+ if (str.indexOf("=") <= 0) throw new Error(`Invalid appState cookie at index ${index3}.`);
92243
+ if (writeCookie(jar2, str)) loaded++;
92244
+ });
92245
+ return { source: "appStateString", loaded };
92246
+ }
92247
+ if (credentials.email && credentials.password) {
92248
+ const url2 = "https://api.facebook.com/method/auth.login";
92249
+ const params = {
92250
+ access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
92251
+ format: "json",
92252
+ sdk_version: 2,
92253
+ email: credentials.email,
92254
+ locale: "en_US",
92255
+ password: credentials.password,
92256
+ generate_session_cookies: 1,
92257
+ sig: "c1c640010993db92e5afd11634ced864"
92258
+ };
92259
+ const xurl = `${url2}?${qs__default.default.stringify(params)}`;
92260
+ try {
92261
+ const legacyHeaders = getHeaders(url2, globalOptions, null, {
92262
+ "Content-Type": "application/x-www-form-urlencoded"
92263
+ });
92264
+ const resp = await axios_default.get(xurl, {
92265
+ headers: legacyHeaders,
92266
+ timeout: 6e4,
92267
+ validateStatus: (status) => status >= 200 && status < 600
92268
+ });
92269
+ if (resp.status !== 200) throw new Error("Wrong password / email");
92270
+ const sessionCookies = resp.data["session_cookies"] || [];
92271
+ if (!sessionCookies.length) throw new Error("No session cookies returned");
92272
+ let loaded = 0;
92273
+ for (const c of sessionCookies) {
92274
+ const str = `${c.name}=${c.value}; domain=.facebook.com; path=/; Secure; SameSite=None`;
92275
+ try {
92276
+ jar2.setCookieSync(str, "https://facebook.com");
92277
+ loaded++;
92278
+ } catch {
92279
+ }
92280
+ }
92281
+ if (loaded === 0) throw new Error("No session cookies could be stored");
92282
+ return { source: "credentials", loaded };
92283
+ } catch (e) {
92284
+ throw new Error(e && e.message ? e.message : "Wrong password / email");
92285
+ }
92286
+ }
92287
+ throw new Error("No cookie or credentials found. Please provide cookies or credentials.");
92288
+ }
91842
92289
 
91843
92290
  // src/deltas/apis/extra/addExternalModule.ts
91844
92291
  function addExternalModule_default(defaultFuncs, api, ctx) {
@@ -100439,7 +100886,9 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100439
100886
  attempts: 0,
100440
100887
  delay: MQTT_CONFIG.INITIAL_RETRY_DELAY,
100441
100888
  throttleDelay: MQTT_CONFIG.THROTTLE_DELAY,
100442
- generation: 0
100889
+ generation: 0,
100890
+ health: null,
100891
+ healthSupervisor: null
100443
100892
  };
100444
100893
  const log2 = (...args) => log(...args);
100445
100894
  function clearReconnectTimer() {
@@ -100453,6 +100902,13 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100453
100902
  clearInterval(lifecycle.presenceInterval);
100454
100903
  lifecycle.presenceInterval = null;
100455
100904
  }
100905
+ if (lifecycle.healthSupervisor) {
100906
+ try {
100907
+ lifecycle.healthSupervisor.stop();
100908
+ } catch (_3) {
100909
+ }
100910
+ lifecycle.healthSupervisor = null;
100911
+ }
100456
100912
  clearReconnectTimer();
100457
100913
  }
100458
100914
  function destroyClient() {
@@ -100580,6 +101036,8 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100580
101036
  });
100581
101037
  lifecycle.client = client2;
100582
101038
  ctx.mqttClient = client2;
101039
+ const health = new RealtimeHealth();
101040
+ lifecycle.health = health;
100583
101041
  let connected = false;
100584
101042
  let closing = false;
100585
101043
  let throttled = false;
@@ -100599,12 +101057,14 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100599
101057
  if (generation !== lifecycle.generation) return;
100600
101058
  connected = false;
100601
101059
  ctx.mqttConnected = false;
101060
+ health.noteDisconnected();
100602
101061
  });
100603
101062
  client2.on("close", () => {
100604
101063
  if (generation !== lifecycle.generation) return;
100605
101064
  if (connected) log2("MQTT connection closed.");
100606
101065
  connected = false;
100607
101066
  ctx.mqttConnected = false;
101067
+ health.noteDisconnected();
100608
101068
  if (closing) return;
100609
101069
  closing = true;
100610
101070
  if (authFailure) {
@@ -100622,6 +101082,7 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100622
101082
  client2.on("connect", () => {
100623
101083
  if (generation !== lifecycle.generation) return;
100624
101084
  connected = true;
101085
+ health.noteConnected();
100625
101086
  client2.subscribe(MQTT_TOPICS, (subErr) => {
100626
101087
  if (subErr) {
100627
101088
  error("Failed to subscribe to MQTT topics:", subErr);
@@ -100658,6 +101119,49 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100658
101119
  ctx.mqttConnected = true;
100659
101120
  ctx.lastMqttError = null;
100660
101121
  resetBackoff();
101122
+ if (lifecycle.healthSupervisor) {
101123
+ try {
101124
+ lifecycle.healthSupervisor.stop();
101125
+ } catch (_3) {
101126
+ }
101127
+ }
101128
+ lifecycle.healthSupervisor = startRealtimeSupervisor({
101129
+ isConnected: () => generation === lifecycle.generation && client2.connected,
101130
+ reconnect: (reason) => {
101131
+ if (generation !== lifecycle.generation) return;
101132
+ if (lifecycle.stopped || ctx.loggedIn === false) return;
101133
+ log2(`MQTT ${reason}; recycling the socket`);
101134
+ try {
101135
+ client2.end(true);
101136
+ } catch (_3) {
101137
+ }
101138
+ const gen = generation;
101139
+ setTimeout(() => {
101140
+ if (gen !== lifecycle.generation) return;
101141
+ if (lifecycle.stopped || ctx.loggedIn === false) return;
101142
+ if (lifecycle.client && lifecycle.client.connected) return;
101143
+ if (lifecycle.reconnectTimer) return;
101144
+ scheduleReconnect("realtime socket recycled");
101145
+ }, 5e3).unref?.();
101146
+ },
101147
+ ping: () => {
101148
+ try {
101149
+ client2.ping();
101150
+ return true;
101151
+ } catch {
101152
+ return false;
101153
+ }
101154
+ },
101155
+ onVerdict: (state) => {
101156
+ if (state.verdict === "dead") {
101157
+ warn(
101158
+ "MQTT health",
101159
+ `silent for ${Math.round(state.sinceLastFrameMs / 1e3)}s; probing`
101160
+ );
101161
+ }
101162
+ }
101163
+ });
101164
+ ctx.__mqttHealth = () => lifecycle.health ? lifecycle.health.state(generation === lifecycle.generation && !!client2.connected) : null;
100661
101165
  api.getBotInitialData().then((botData) => {
100662
101166
  if (botData) {
100663
101167
  log(`Logged in as: ${botData.name || "Facebook User"} (${botData.uid || ctx.userID})`);
@@ -100668,6 +101172,7 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100668
101172
  });
100669
101173
  client2.on("message", async (topic, message, _packet) => {
100670
101174
  try {
101175
+ health.noteFrame();
100671
101176
  ctx.lastMessageTime = Date.now();
100672
101177
  ctx.messageCount = (ctx.messageCount || 0) + 1;
100673
101178
  const jsonMessage = JSON.parse(message.toString());
@@ -100753,6 +101258,16 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
100753
101258
  this.stop();
100754
101259
  if (typeof cb === "function") cb();
100755
101260
  }
101261
+ /** Live connection-health snapshot, for diagnostics/alerting. */
101262
+ getHealth() {
101263
+ if (!lifecycle.health) return null;
101264
+ const connected = !lifecycle.stopped && !!lifecycle.client && !!lifecycle.client.connected;
101265
+ return lifecycle.health.state(connected);
101266
+ }
101267
+ /** Is this listener currently stopped? */
101268
+ isStopped() {
101269
+ return lifecycle.stopped;
101270
+ }
100756
101271
  }
100757
101272
  const msgEmitter = new MessageEmitter();
100758
101273
  lifecycle.stopped = false;
@@ -100921,76 +101436,8 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
100921
101436
  const jar2 = getJar();
100922
101437
  log("Logging in...");
100923
101438
  const appState = credentials.appState;
100924
- if (appState) {
100925
- let cookieStrings = [];
100926
- if (Array.isArray(appState)) {
100927
- cookieStrings = appState.map((c, index3) => {
100928
- const name = c && (c.name || c.key);
100929
- if (!name || c.value === void 0 || c.value === null) {
100930
- throw new Error(`Invalid appState cookie at index ${index3}.`);
100931
- }
100932
- return formatAppStateCookie(c);
100933
- });
100934
- } else if (typeof appState === "string") {
100935
- cookieStrings = appState.split(";").map((s) => s.trim()).filter(Boolean);
100936
- } else {
100937
- throw new Error(
100938
- "Invalid appState format. Please provide an array of cookie objects or a cookie string."
100939
- );
100940
- }
100941
- cookieStrings.forEach((cookieString, index3) => {
100942
- if (cookieString.indexOf("=") <= 0) {
100943
- throw new Error(`Invalid appState cookie at index ${index3}.`);
100944
- }
100945
- const normalized = cookieString.replace(/;\s*domain=facebook\.com/i, "; domain=.facebook.com").replace(/;\s*domain=messenger\.com/i, "; domain=.messenger.com");
100946
- const str = /(^|;)\s*domain=/i.test(normalized) ? normalized : `${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`;
100947
- try {
100948
- jar2.setCookieSync(str, "https://facebook.com");
100949
- } catch {
100950
- try {
100951
- jar2.setCookieSync(`${normalized}; domain=.facebook.com; path=/; Secure; SameSite=None`, "https://www.facebook.com");
100952
- } catch {
100953
- }
100954
- }
100955
- });
100956
- } else if (credentials.email && credentials.password) {
100957
- const url2 = "https://api.facebook.com/method/auth.login";
100958
- const params = {
100959
- access_token: "350685531728|62f8ce9f74b12f84c123cc23437a4a32",
100960
- format: "json",
100961
- sdk_version: 2,
100962
- email: credentials.email,
100963
- locale: "en_US",
100964
- password: credentials.password,
100965
- generate_session_cookies: 1,
100966
- sig: "c1c640010993db92e5afd11634ced864"
100967
- };
100968
- const query = qs__default.default.stringify(params);
100969
- const xurl = `${url2}?${query}`;
100970
- try {
100971
- const legacyHeaders = getHeaders(
100972
- url2,
100973
- globalOptions,
100974
- null,
100975
- { "Content-Type": "application/x-www-form-urlencoded" }
100976
- );
100977
- const resp2 = await axios_default.get(xurl, {
100978
- headers: legacyHeaders,
100979
- timeout: 6e4,
100980
- validateStatus: (status) => status >= 200 && status < 600
100981
- });
100982
- if (resp2.status !== 200) {
100983
- throw new Error("Wrong password / email");
100984
- }
100985
- const cstrs = resp2.data["session_cookies"].map((c) => `${c.name}=${c.value}`);
100986
- cstrs.forEach((cstr) => {
100987
- const domain = ".facebook.com";
100988
- const str = `${cstr}; domain=${domain}; path=/; Secure; SameSite=None`;
100989
- jar2.setCookieSync(str, "https://facebook.com");
100990
- });
100991
- } catch (e) {
100992
- throw new Error("Wrong password / email");
100993
- }
101439
+ if (appState || credentials.email && credentials.password) {
101440
+ await acquireCookies(jar2, credentials, globalOptions);
100994
101441
  } else {
100995
101442
  throw new Error("No cookie or credentials found. Please provide cookies or credentials.");
100996
101443
  }
@@ -101129,6 +101576,81 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
101129
101576
  }
101130
101577
  }
101131
101578
  ctx.__stopCookieKeepAlive = scheduleSessionKeepAlive(ctx, defaultFuncs, 15 * 60 * 1e3);
101579
+ ctx.__credentials = {
101580
+ email: credentials.email,
101581
+ password: credentials.password
101582
+ };
101583
+ ctx.__loginGlobalOptions = globalOptions;
101584
+ ctx.__persistPath = globalOptions.appStatePath;
101585
+ if (globalOptions.recovery !== false) {
101586
+ const recoveryOptions = typeof globalOptions.recovery === "object" ? globalOptions.recovery : {};
101587
+ const supervisor = startRecovery(
101588
+ ctx,
101589
+ {
101590
+ ctx,
101591
+ isAuthenticated: () => hasAuthenticatedSession(ctx.jar),
101592
+ probeLiveness: async () => {
101593
+ try {
101594
+ const res = await defaultFuncs.get(
101595
+ "https://www.facebook.com/",
101596
+ ctx.jar,
101597
+ null,
101598
+ ctx.globalOptions,
101599
+ { noRef: true }
101600
+ );
101601
+ const html3 = typeof res === "string" ? res : res && res.body;
101602
+ if (typeof html3 !== "string" || !html3) return true;
101603
+ return !isLoggedOutDocument(html3);
101604
+ } catch {
101605
+ return true;
101606
+ }
101607
+ },
101608
+ refresh: () => repairSession(ctx, defaultFuncs),
101609
+ canRelogin: Boolean(credentials.email && credentials.password),
101610
+ relogin: async () => {
101611
+ if (!credentials.email || !credentials.password) return null;
101612
+ await sleep(retryDelay(0, 2e3, 15e3));
101613
+ await acquireCookies(ctx.jar, credentials, globalOptions);
101614
+ await repairSession(ctx, defaultFuncs);
101615
+ if (!hasAuthenticatedSession(ctx.jar)) return null;
101616
+ return getAppState(ctx.jar);
101617
+ },
101618
+ persistAppState: (state) => {
101619
+ const path2 = globalOptions.appStatePath;
101620
+ if (!path2) return;
101621
+ try {
101622
+ const fs2 = __require("fs");
101623
+ fs2.writeFileSync(path2, JSON.stringify(state, null, 2));
101624
+ log(`Recovery: refreshed appState written to ${path2}`);
101625
+ } catch (persistError) {
101626
+ warn("Recovery: could not persist appState:", persistError?.message);
101627
+ }
101628
+ },
101629
+ restartMqtt: () => {
101630
+ try {
101631
+ if (typeof ctx.__restartMqtt === "function") {
101632
+ ctx.__restartMqtt();
101633
+ } else if (typeof ctx.reconnectMqtt === "function" && activeListener) {
101634
+ void ctx.reconnectMqtt();
101635
+ }
101636
+ } catch {
101637
+ }
101638
+ },
101639
+ onEvent: (event) => {
101640
+ if (event.type === "gave-up") {
101641
+ warn("Recovery", event.detail);
101642
+ } else if (event.type === "relogin" || event.type === "healed") {
101643
+ log(`Recovery: ${event.detail}`);
101644
+ }
101645
+ }
101646
+ },
101647
+ recoveryOptions
101648
+ );
101649
+ api.getRecoveryState = () => supervisor.snapshot();
101650
+ api.checkRecoveryNow = () => supervisor.checkNow();
101651
+ api.stopRecovery = () => supervisor.stop();
101652
+ api.startRecovery = () => supervisor.start();
101653
+ }
101132
101654
  if (globalOptions.liveness !== false) {
101133
101655
  const liveness = startLiveness(ctx, {
101134
101656
  foregroundPing: () => log("Session active"),
@@ -101311,6 +101833,11 @@ lodash/lodash.js:
101311
101833
  *)
101312
101834
  */
101313
101835
 
101836
+ exports.PING_GRACE_MS = PING_GRACE_MS;
101837
+ exports.RealtimeHealth = RealtimeHealth;
101838
+ exports.RecoverySupervisor = RecoverySupervisor;
101839
+ exports.SILENT_WINDOW_MS = SILENT_WINDOW_MS;
101840
+ exports.acquireCookies = acquireCookies;
101314
101841
  exports.applySetCookie = applySetCookie;
101315
101842
  exports.awaitProactiveSlot = awaitProactiveSlot;
101316
101843
  exports.canActProactively = canActProactively;
@@ -101330,6 +101857,7 @@ exports.humanDelay = humanDelay;
101330
101857
  exports.invalidateCometTokens = invalidateCometTokens;
101331
101858
  exports.isAuthFailure = isAuthFailure;
101332
101859
  exports.isBreakerOpen = isBreakerOpen;
101860
+ exports.isLoggedOutDocument = isLoggedOutDocument;
101333
101861
  exports.isStaleTokenError = isStaleTokenError;
101334
101862
  exports.login = login;
101335
101863
  exports.missingSessionCookies = missingSessionCookies;
@@ -101350,6 +101878,8 @@ exports.retryDelay = retryDelay;
101350
101878
  exports.safetySnapshot = safetySnapshot;
101351
101879
  exports.sleep = sleep;
101352
101880
  exports.startLiveness = startLiveness;
101881
+ exports.startRealtimeSupervisor = startRealtimeSupervisor;
101882
+ exports.startRecovery = startRecovery;
101353
101883
  exports.tripBreaker = tripBreaker;
101354
101884
  exports.warmUpSession = warmUpSession;
101355
101885
  exports.withCometTokens = withCometTokens;