@lazyneoaz/metachat 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25450,7 +25450,11 @@ function looksLikeCheckpoint(res) {
25450
25450
  return true;
25451
25451
  }
25452
25452
  const body = typeof res?.body === "string" ? res.body : "";
25453
- return /"error":1357004|account has been (?:disabled|suspended)|confirm your identity|security check/i.test(body.slice(0, 400));
25453
+ if (!body) return false;
25454
+ if (/"isNotCritical"\s*:\s*1/.test(body.slice(0, 400))) return false;
25455
+ return /account has been (?:disabled|suspended)|confirm your identity|security check|we (?:suspended|disabled) your account/i.test(
25456
+ body.slice(0, 400)
25457
+ );
25454
25458
  }
25455
25459
  async function requestWithRetry(requestFunction, retries = 3, baseDelay = 1e3, ctx) {
25456
25460
  const emit2 = (event, payload) => {
@@ -90895,16 +90899,53 @@ function normalizeFilename(filename, mime) {
90895
90899
  }
90896
90900
  async function uploadMercuryAttachment(ctx, defaultFuncs, attachment) {
90897
90901
  const normalized = await normalizeAttachmentInput(attachment, ctx);
90902
+ const buffer = await readAll(normalized.stream);
90898
90903
  const url2 = "https://www.facebook.com/ajax/mercury/upload.php";
90904
+ let lastError;
90905
+ for (let attempt = 0; attempt < 2; attempt++) {
90906
+ if (attempt > 0) await refreshUploadTokens(ctx, defaultFuncs);
90907
+ await ensureUploadTokens(ctx, defaultFuncs);
90908
+ const formData = new import_form_data3.default();
90909
+ formData.append("farr", stream3.Readable.from(buffer), {
90910
+ filename: normalized.filename,
90911
+ contentType: normalized.contentType || void 0
90912
+ });
90913
+ let data2;
90914
+ try {
90915
+ const res = await defaultFuncs.postMultipart(url2, ctx.jar, formData, buildUploadQuery(ctx));
90916
+ data2 = cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
90917
+ } catch (error2) {
90918
+ lastError = error2;
90919
+ if (attempt === 0 && isTransientUploadError(error2)) continue;
90920
+ throw error2;
90921
+ }
90922
+ if (extractAttachmentIds(data2).length) return finalizeUpload(data2);
90923
+ lastError = buildNoMetadataError(data2);
90924
+ if (attempt === 0 && isTransientUploadError(lastError)) continue;
90925
+ throw lastError;
90926
+ }
90927
+ throw lastError || buildNoMetadataError(null);
90928
+ }
90929
+ function buildNoMetadataError(data2) {
90930
+ const err = new Error("UploadFb returned no metadata/ids");
90931
+ err.code = "NO_METADATA";
90932
+ err.body = data2;
90933
+ return err;
90934
+ }
90935
+ function isTransientUploadError(value) {
90936
+ const body = value && (value.body || value);
90937
+ if (!body || typeof body !== "object") return false;
90938
+ const num = Number(body.error ?? body.errorCode);
90939
+ if (num === 1357004 || num === 1357001) return true;
90940
+ const text3 = String(body.errorDescription || body.errorSummary || value?.message || "");
90941
+ return /re-?open(ing)? your browser|try again|temporarily unavailable/i.test(text3);
90942
+ }
90943
+ async function refreshUploadTokens(ctx, defaultFuncs) {
90944
+ ctx.lsd = void 0;
90945
+ ctx.master = ctx.master || {};
90946
+ ctx.master.__rev = void 0;
90947
+ if (typeof ctx.__cometTokens === "object") ctx.__cometTokens = void 0;
90899
90948
  await ensureUploadTokens(ctx, defaultFuncs);
90900
- const formData = new import_form_data3.default();
90901
- formData.append("farr", normalized.stream, {
90902
- filename: normalized.filename,
90903
- contentType: normalized.contentType || void 0
90904
- });
90905
- const res = await defaultFuncs.postMultipart(url2, ctx.jar, formData, buildUploadQuery(ctx));
90906
- const data2 = cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
90907
- return finalizeUpload(data2);
90908
90949
  }
90909
90950
  async function normalizeAttachmentInput(attachment, ctx) {
90910
90951
  if (Buffer.isBuffer(attachment)) {
@@ -91805,10 +91846,10 @@ function startRecovery(ctx, runtime, options = {}) {
91805
91846
 
91806
91847
  // src/utils/realtime-health.ts
91807
91848
  init_pacing();
91808
- var SILENT_WINDOW_MS = 6 * 60 * 1e3;
91809
- var PING_GRACE_MS = 45 * 1e3;
91849
+ var SILENT_WINDOW_MS = 30 * 60 * 1e3;
91850
+ var PING_GRACE_MS = 60 * 1e3;
91810
91851
  var RealtimeHealth = class {
91811
- constructor(now = () => Date.now()) {
91852
+ constructor(now = () => Date.now(), options = {}) {
91812
91853
  this.now = now;
91813
91854
  this.frames = 0;
91814
91855
  this.lastFrameAt = 0;
@@ -91817,17 +91858,23 @@ var RealtimeHealth = class {
91817
91858
  this.forcedReconnects = 0;
91818
91859
  this.lastPingAt = 0;
91819
91860
  this.startedAt = Date.now();
91861
+ /** Consecutive pings sent without any resulting frame. */
91862
+ this.silentPings = 0;
91820
91863
  this.startedAt = this.now();
91864
+ this.silentWindowMs = options.silentWindowMs ?? SILENT_WINDOW_MS;
91865
+ this.pingGraceMs = options.pingGraceMs ?? PING_GRACE_MS;
91821
91866
  }
91822
91867
  /** Record any inbound frame (message, pong, anything from the broker). */
91823
91868
  noteFrame() {
91824
91869
  this.frames += 1;
91825
91870
  this.lastFrameAt = this.now();
91871
+ this.silentPings = 0;
91826
91872
  }
91827
91873
  /** Record a successful connect. */
91828
91874
  noteConnected() {
91829
91875
  this.connectedAt = this.now();
91830
91876
  this.lastFrameAt = this.now();
91877
+ this.silentPings = 0;
91831
91878
  }
91832
91879
  /** Record that the transport reports itself disconnected. */
91833
91880
  noteDisconnected() {
@@ -91836,6 +91883,7 @@ var RealtimeHealth = class {
91836
91883
  /** Record an active ping being sent. */
91837
91884
  notePing() {
91838
91885
  this.lastPingAt = this.now();
91886
+ this.silentPings += 1;
91839
91887
  }
91840
91888
  sinceLastFrame() {
91841
91889
  const base = this.lastFrameAt || this.connectedAt || this.startedAt;
@@ -91845,8 +91893,8 @@ var RealtimeHealth = class {
91845
91893
  * Decide what to do about the current socket.
91846
91894
  *
91847
91895
  * - `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.
91896
+ * - `dead` — connected on paper, silent well past the window, and repeated
91897
+ * pings raised no traffic at all; reconnect.
91850
91898
  * - `suspect` — silent past the window but a ping was only just sent; wait.
91851
91899
  * - `quiet` — silent but inside the window; normal.
91852
91900
  * - `healthy` — recent traffic.
@@ -91854,9 +91902,10 @@ var RealtimeHealth = class {
91854
91902
  verdict(connected) {
91855
91903
  if (!connected) return "disconnected";
91856
91904
  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";
91905
+ if (silent < this.silentWindowMs / 2) return "healthy";
91906
+ if (silent < this.silentWindowMs) return "quiet";
91907
+ if (this.lastPingAt && this.now() - this.lastPingAt < this.pingGraceMs) return "suspect";
91908
+ if (this.silentPings < 3) return "suspect";
91860
91909
  return "dead";
91861
91910
  }
91862
91911
  /** A snapshot for diagnostics. */
@@ -91878,8 +91927,11 @@ var RealtimeHealth = class {
91878
91927
  }
91879
91928
  };
91880
91929
  function startRealtimeSupervisor(options) {
91881
- const health = new RealtimeHealth();
91882
- const interval = options.intervalMs ?? 3 * 60 * 1e3;
91930
+ const health = options.health || new RealtimeHealth(void 0, {
91931
+ silentWindowMs: options.silentWindowMs,
91932
+ pingGraceMs: options.pingGraceMs
91933
+ });
91934
+ const interval = options.intervalMs ?? 10 * 60 * 1e3;
91883
91935
  let stopped = false;
91884
91936
  let timer = null;
91885
91937
  const evaluate = () => {
@@ -101127,6 +101179,9 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
101127
101179
  }
101128
101180
  lifecycle.healthSupervisor = startRealtimeSupervisor({
101129
101181
  isConnected: () => generation === lifecycle.generation && client2.connected,
101182
+ // Share the per-connection tracker so this supervisor sees
101183
+ // every inbound frame the listener records.
101184
+ health,
101130
101185
  reconnect: (reason) => {
101131
101186
  if (generation !== lifecycle.generation) return;
101132
101187
  if (lifecycle.stopped || ctx.loggedIn === false) return;
@@ -101841,6 +101896,7 @@ exports.acquireCookies = acquireCookies;
101841
101896
  exports.applySetCookie = applySetCookie;
101842
101897
  exports.awaitProactiveSlot = awaitProactiveSlot;
101843
101898
  exports.canActProactively = canActProactively;
101899
+ exports.cleanJsonResponse = cleanJsonResponse;
101844
101900
  exports.collectSessionCookies = collectSessionCookies;
101845
101901
  exports.default = index_default;
101846
101902
  exports.ensureCometTokens = ensureCometTokens;
@@ -101881,6 +101937,8 @@ exports.startLiveness = startLiveness;
101881
101937
  exports.startRealtimeSupervisor = startRealtimeSupervisor;
101882
101938
  exports.startRecovery = startRecovery;
101883
101939
  exports.tripBreaker = tripBreaker;
101940
+ exports.uploadGroupImage = uploadGroupImage;
101941
+ exports.uploadMercuryAttachment = uploadMercuryAttachment;
101884
101942
  exports.warmUpSession = warmUpSession;
101885
101943
  exports.withCometTokens = withCometTokens;
101886
101944
  exports.withSessionRetry = withSessionRetry;
package/dist/index.d.mts CHANGED
@@ -1128,6 +1128,12 @@ declare function startRecovery(ctx: any, runtime: RecoveryRuntime, options?: Rec
1128
1128
  declare const SILENT_WINDOW_MS: number;
1129
1129
  /** How long to wait for traffic after an active ping before declaring death. */
1130
1130
  declare const PING_GRACE_MS: number;
1131
+ interface RealtimeHealthOptions {
1132
+ /** Silent window before a socket is considered suspect (default 30 min). */
1133
+ silentWindowMs?: number;
1134
+ /** Grace after a ping before the socket may be declared dead (default 60 s). */
1135
+ pingGraceMs?: number;
1136
+ }
1131
1137
  interface RealtimeHealthState {
1132
1138
  /** Whether the transport currently reports itself connected. */
1133
1139
  connected: boolean;
@@ -1155,7 +1161,11 @@ declare class RealtimeHealth {
1155
1161
  private forcedReconnects;
1156
1162
  private lastPingAt;
1157
1163
  private startedAt;
1158
- constructor(now?: () => number);
1164
+ private silentWindowMs;
1165
+ private pingGraceMs;
1166
+ /** Consecutive pings sent without any resulting frame. */
1167
+ private silentPings;
1168
+ constructor(now?: () => number, options?: RealtimeHealthOptions);
1159
1169
  /** Record any inbound frame (message, pong, anything from the broker). */
1160
1170
  noteFrame(): void;
1161
1171
  /** Record a successful connect. */
@@ -1169,8 +1179,8 @@ declare class RealtimeHealth {
1169
1179
  * Decide what to do about the current socket.
1170
1180
  *
1171
1181
  * - `disconnected` — the transport already knows it is down; reconnect.
1172
- * - `dead` — connected on paper, silent past the window, and a ping raised no
1173
- * traffic within the grace period; reconnect.
1182
+ * - `dead` — connected on paper, silent well past the window, and repeated
1183
+ * pings raised no traffic at all; reconnect.
1174
1184
  * - `suspect` — silent past the window but a ping was only just sent; wait.
1175
1185
  * - `quiet` — silent but inside the window; normal.
1176
1186
  * - `healthy` — recent traffic.
@@ -1182,7 +1192,7 @@ declare class RealtimeHealth {
1182
1192
  noteForcedReconnect(): void;
1183
1193
  }
1184
1194
  interface RealtimeSupervisorOptions {
1185
- /** How often to evaluate the socket. Jittered. Default 3 min. */
1195
+ /** How often to evaluate the socket. Jittered. Default 10 min. */
1186
1196
  intervalMs?: number;
1187
1197
  /** Ask the transport to reconnect (fresh socket) — must be idempotent. */
1188
1198
  reconnect: (reason: string) => void;
@@ -1192,6 +1202,11 @@ interface RealtimeSupervisorOptions {
1192
1202
  isConnected: () => boolean;
1193
1203
  /** Observe verdict changes (logging). */
1194
1204
  onVerdict?: (state: RealtimeHealthState) => void;
1205
+ /** Silent window / ping grace overrides. */
1206
+ silentWindowMs?: number;
1207
+ pingGraceMs?: number;
1208
+ /** Use an existing health tracker instead of creating a private one. */
1209
+ health?: RealtimeHealth;
1195
1210
  }
1196
1211
  /**
1197
1212
  * A lightweight supervisor that keeps ONE realtime socket honest: it pings a
@@ -1235,6 +1250,17 @@ interface AcquireResult {
1235
1250
  */
1236
1251
  declare function acquireCookies(jar: any, credentials: LoginCredentials$1, globalOptions: LoginOptions$1): Promise<AcquireResult>;
1237
1252
 
1253
+ interface UploadedAttachment {
1254
+ [key: string]: any;
1255
+ }
1256
+ declare function cleanJsonResponse(value: any): any;
1257
+ declare function uploadMercuryAttachment(ctx: any, defaultFuncs: any, attachment: any): Promise<UploadedAttachment>;
1258
+ /**
1259
+ * Upload a single image for use as a group/thread image. Uses the Mercury
1260
+ * `images_only=true` endpoint so Facebook returns an `image_id`.
1261
+ */
1262
+ declare function uploadGroupImage(ctx: any, defaultFuncs: any, image: any): Promise<any>;
1263
+
1238
1264
  type ApiContext = ApiContext$1;
1239
1265
  type ApiInternal = Api;
1240
1266
  type DefaultFuncs = DefaultFuncs$1;
@@ -1245,4 +1271,4 @@ declare const _default: typeof login$1 & {
1245
1271
  default: typeof login$1;
1246
1272
  };
1247
1273
 
1248
- export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
1274
+ export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, cleanJsonResponse, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, uploadGroupImage, uploadMercuryAttachment, warmUpSession, withCometTokens, withSessionRetry };
package/dist/index.d.ts CHANGED
@@ -1128,6 +1128,12 @@ declare function startRecovery(ctx: any, runtime: RecoveryRuntime, options?: Rec
1128
1128
  declare const SILENT_WINDOW_MS: number;
1129
1129
  /** How long to wait for traffic after an active ping before declaring death. */
1130
1130
  declare const PING_GRACE_MS: number;
1131
+ interface RealtimeHealthOptions {
1132
+ /** Silent window before a socket is considered suspect (default 30 min). */
1133
+ silentWindowMs?: number;
1134
+ /** Grace after a ping before the socket may be declared dead (default 60 s). */
1135
+ pingGraceMs?: number;
1136
+ }
1131
1137
  interface RealtimeHealthState {
1132
1138
  /** Whether the transport currently reports itself connected. */
1133
1139
  connected: boolean;
@@ -1155,7 +1161,11 @@ declare class RealtimeHealth {
1155
1161
  private forcedReconnects;
1156
1162
  private lastPingAt;
1157
1163
  private startedAt;
1158
- constructor(now?: () => number);
1164
+ private silentWindowMs;
1165
+ private pingGraceMs;
1166
+ /** Consecutive pings sent without any resulting frame. */
1167
+ private silentPings;
1168
+ constructor(now?: () => number, options?: RealtimeHealthOptions);
1159
1169
  /** Record any inbound frame (message, pong, anything from the broker). */
1160
1170
  noteFrame(): void;
1161
1171
  /** Record a successful connect. */
@@ -1169,8 +1179,8 @@ declare class RealtimeHealth {
1169
1179
  * Decide what to do about the current socket.
1170
1180
  *
1171
1181
  * - `disconnected` — the transport already knows it is down; reconnect.
1172
- * - `dead` — connected on paper, silent past the window, and a ping raised no
1173
- * traffic within the grace period; reconnect.
1182
+ * - `dead` — connected on paper, silent well past the window, and repeated
1183
+ * pings raised no traffic at all; reconnect.
1174
1184
  * - `suspect` — silent past the window but a ping was only just sent; wait.
1175
1185
  * - `quiet` — silent but inside the window; normal.
1176
1186
  * - `healthy` — recent traffic.
@@ -1182,7 +1192,7 @@ declare class RealtimeHealth {
1182
1192
  noteForcedReconnect(): void;
1183
1193
  }
1184
1194
  interface RealtimeSupervisorOptions {
1185
- /** How often to evaluate the socket. Jittered. Default 3 min. */
1195
+ /** How often to evaluate the socket. Jittered. Default 10 min. */
1186
1196
  intervalMs?: number;
1187
1197
  /** Ask the transport to reconnect (fresh socket) — must be idempotent. */
1188
1198
  reconnect: (reason: string) => void;
@@ -1192,6 +1202,11 @@ interface RealtimeSupervisorOptions {
1192
1202
  isConnected: () => boolean;
1193
1203
  /** Observe verdict changes (logging). */
1194
1204
  onVerdict?: (state: RealtimeHealthState) => void;
1205
+ /** Silent window / ping grace overrides. */
1206
+ silentWindowMs?: number;
1207
+ pingGraceMs?: number;
1208
+ /** Use an existing health tracker instead of creating a private one. */
1209
+ health?: RealtimeHealth;
1195
1210
  }
1196
1211
  /**
1197
1212
  * A lightweight supervisor that keeps ONE realtime socket honest: it pings a
@@ -1235,6 +1250,17 @@ interface AcquireResult {
1235
1250
  */
1236
1251
  declare function acquireCookies(jar: any, credentials: LoginCredentials$1, globalOptions: LoginOptions$1): Promise<AcquireResult>;
1237
1252
 
1253
+ interface UploadedAttachment {
1254
+ [key: string]: any;
1255
+ }
1256
+ declare function cleanJsonResponse(value: any): any;
1257
+ declare function uploadMercuryAttachment(ctx: any, defaultFuncs: any, attachment: any): Promise<UploadedAttachment>;
1258
+ /**
1259
+ * Upload a single image for use as a group/thread image. Uses the Mercury
1260
+ * `images_only=true` endpoint so Facebook returns an `image_id`.
1261
+ */
1262
+ declare function uploadGroupImage(ctx: any, defaultFuncs: any, image: any): Promise<any>;
1263
+
1238
1264
  type ApiContext = ApiContext$1;
1239
1265
  type ApiInternal = Api;
1240
1266
  type DefaultFuncs = DefaultFuncs$1;
@@ -1245,4 +1271,4 @@ declare const _default: typeof login$1 & {
1245
1271
  default: typeof login$1;
1246
1272
  };
1247
1273
 
1248
- export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, warmUpSession, withCometTokens, withSessionRetry };
1274
+ export { type API, type AddedStickerPackInfo, type AiTheme, type AnyAttachment, type ApiContext, type ApiInternal, type Attachment, type AudioAttachment, type BrowserProfile, type Callback, type CommentMessage, type CommentResult, type Coordinates, type DefaultFuncs, type EmojiEvent, type Event, type FileAttachment, type GroupNameEvent, type ListenEvent, type LivenessHandle, type LivenessState, type LoginCredentials, type LoginOptions, type Mention, type Message, type MessageID, type MessageObject, type MessageReply, type MusicTag, type MusicTrack, type NicknameEvent, PING_GRACE_MS, type PhotoAttachment, type Reaction, RealtimeHealth, type RealtimeHealthState, type RecoveryEvent, type RecoveryOptions, type RecoveryRuntime, type RecoveryState, RecoverySupervisor, SILENT_WINDOW_MS, type SearchMusicOptions, type SearchMusicResult, type ShareAttachment, type ShareResult, type StickerAttachment, type StickerInfo, type StickerPackInfo, type ThreadID, type ThreadInfo, type ThreadThemeEvent, type TypingIndicator, type UnsendMessageEvent, type UserID, type UserInfo, type VideoAttachment, type WarmUpResult, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, cleanJsonResponse, collectSessionCookies, _default as default, defaultBrowserProfile, ensureCometTokens, extractCookies, extractCookiesFromBody, extractCookiesFromJar, extractCookiesFromSetCookie, extractCurrentUser, getFingerprintParams, getHeaders, getWebSocketHeaders, hasAuthenticatedSession, humanDelay, invalidateCometTokens, isAuthFailure, isBreakerOpen, isLoggedOutDocument, isStaleTokenError, login$1 as login, missingSessionCookies, normalizeBrowserProfile, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, safetySnapshot, sleep, startLiveness, startRealtimeSupervisor, startRecovery, tripBreaker, uploadGroupImage, uploadMercuryAttachment, warmUpSession, withCometTokens, withSessionRetry };
package/dist/index.mjs CHANGED
@@ -25415,7 +25415,11 @@ function looksLikeCheckpoint(res) {
25415
25415
  return true;
25416
25416
  }
25417
25417
  const body = typeof res?.body === "string" ? res.body : "";
25418
- return /"error":1357004|account has been (?:disabled|suspended)|confirm your identity|security check/i.test(body.slice(0, 400));
25418
+ if (!body) return false;
25419
+ if (/"isNotCritical"\s*:\s*1/.test(body.slice(0, 400))) return false;
25420
+ return /account has been (?:disabled|suspended)|confirm your identity|security check|we (?:suspended|disabled) your account/i.test(
25421
+ body.slice(0, 400)
25422
+ );
25419
25423
  }
25420
25424
  async function requestWithRetry(requestFunction, retries = 3, baseDelay = 1e3, ctx) {
25421
25425
  const emit2 = (event, payload) => {
@@ -90860,16 +90864,53 @@ function normalizeFilename(filename, mime) {
90860
90864
  }
90861
90865
  async function uploadMercuryAttachment(ctx, defaultFuncs, attachment) {
90862
90866
  const normalized = await normalizeAttachmentInput(attachment, ctx);
90867
+ const buffer = await readAll(normalized.stream);
90863
90868
  const url2 = "https://www.facebook.com/ajax/mercury/upload.php";
90869
+ let lastError;
90870
+ for (let attempt = 0; attempt < 2; attempt++) {
90871
+ if (attempt > 0) await refreshUploadTokens(ctx, defaultFuncs);
90872
+ await ensureUploadTokens(ctx, defaultFuncs);
90873
+ const formData = new import_form_data3.default();
90874
+ formData.append("farr", Readable.from(buffer), {
90875
+ filename: normalized.filename,
90876
+ contentType: normalized.contentType || void 0
90877
+ });
90878
+ let data2;
90879
+ try {
90880
+ const res = await defaultFuncs.postMultipart(url2, ctx.jar, formData, buildUploadQuery(ctx));
90881
+ data2 = cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
90882
+ } catch (error2) {
90883
+ lastError = error2;
90884
+ if (attempt === 0 && isTransientUploadError(error2)) continue;
90885
+ throw error2;
90886
+ }
90887
+ if (extractAttachmentIds(data2).length) return finalizeUpload(data2);
90888
+ lastError = buildNoMetadataError(data2);
90889
+ if (attempt === 0 && isTransientUploadError(lastError)) continue;
90890
+ throw lastError;
90891
+ }
90892
+ throw lastError || buildNoMetadataError(null);
90893
+ }
90894
+ function buildNoMetadataError(data2) {
90895
+ const err = new Error("UploadFb returned no metadata/ids");
90896
+ err.code = "NO_METADATA";
90897
+ err.body = data2;
90898
+ return err;
90899
+ }
90900
+ function isTransientUploadError(value) {
90901
+ const body = value && (value.body || value);
90902
+ if (!body || typeof body !== "object") return false;
90903
+ const num = Number(body.error ?? body.errorCode);
90904
+ if (num === 1357004 || num === 1357001) return true;
90905
+ const text3 = String(body.errorDescription || body.errorSummary || value?.message || "");
90906
+ return /re-?open(ing)? your browser|try again|temporarily unavailable/i.test(text3);
90907
+ }
90908
+ async function refreshUploadTokens(ctx, defaultFuncs) {
90909
+ ctx.lsd = void 0;
90910
+ ctx.master = ctx.master || {};
90911
+ ctx.master.__rev = void 0;
90912
+ if (typeof ctx.__cometTokens === "object") ctx.__cometTokens = void 0;
90864
90913
  await ensureUploadTokens(ctx, defaultFuncs);
90865
- const formData = new import_form_data3.default();
90866
- formData.append("farr", normalized.stream, {
90867
- filename: normalized.filename,
90868
- contentType: normalized.contentType || void 0
90869
- });
90870
- const res = await defaultFuncs.postMultipart(url2, ctx.jar, formData, buildUploadQuery(ctx));
90871
- const data2 = cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
90872
- return finalizeUpload(data2);
90873
90914
  }
90874
90915
  async function normalizeAttachmentInput(attachment, ctx) {
90875
90916
  if (Buffer.isBuffer(attachment)) {
@@ -91770,10 +91811,10 @@ function startRecovery(ctx, runtime, options = {}) {
91770
91811
 
91771
91812
  // src/utils/realtime-health.ts
91772
91813
  init_pacing();
91773
- var SILENT_WINDOW_MS = 6 * 60 * 1e3;
91774
- var PING_GRACE_MS = 45 * 1e3;
91814
+ var SILENT_WINDOW_MS = 30 * 60 * 1e3;
91815
+ var PING_GRACE_MS = 60 * 1e3;
91775
91816
  var RealtimeHealth = class {
91776
- constructor(now = () => Date.now()) {
91817
+ constructor(now = () => Date.now(), options = {}) {
91777
91818
  this.now = now;
91778
91819
  this.frames = 0;
91779
91820
  this.lastFrameAt = 0;
@@ -91782,17 +91823,23 @@ var RealtimeHealth = class {
91782
91823
  this.forcedReconnects = 0;
91783
91824
  this.lastPingAt = 0;
91784
91825
  this.startedAt = Date.now();
91826
+ /** Consecutive pings sent without any resulting frame. */
91827
+ this.silentPings = 0;
91785
91828
  this.startedAt = this.now();
91829
+ this.silentWindowMs = options.silentWindowMs ?? SILENT_WINDOW_MS;
91830
+ this.pingGraceMs = options.pingGraceMs ?? PING_GRACE_MS;
91786
91831
  }
91787
91832
  /** Record any inbound frame (message, pong, anything from the broker). */
91788
91833
  noteFrame() {
91789
91834
  this.frames += 1;
91790
91835
  this.lastFrameAt = this.now();
91836
+ this.silentPings = 0;
91791
91837
  }
91792
91838
  /** Record a successful connect. */
91793
91839
  noteConnected() {
91794
91840
  this.connectedAt = this.now();
91795
91841
  this.lastFrameAt = this.now();
91842
+ this.silentPings = 0;
91796
91843
  }
91797
91844
  /** Record that the transport reports itself disconnected. */
91798
91845
  noteDisconnected() {
@@ -91801,6 +91848,7 @@ var RealtimeHealth = class {
91801
91848
  /** Record an active ping being sent. */
91802
91849
  notePing() {
91803
91850
  this.lastPingAt = this.now();
91851
+ this.silentPings += 1;
91804
91852
  }
91805
91853
  sinceLastFrame() {
91806
91854
  const base = this.lastFrameAt || this.connectedAt || this.startedAt;
@@ -91810,8 +91858,8 @@ var RealtimeHealth = class {
91810
91858
  * Decide what to do about the current socket.
91811
91859
  *
91812
91860
  * - `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.
91861
+ * - `dead` — connected on paper, silent well past the window, and repeated
91862
+ * pings raised no traffic at all; reconnect.
91815
91863
  * - `suspect` — silent past the window but a ping was only just sent; wait.
91816
91864
  * - `quiet` — silent but inside the window; normal.
91817
91865
  * - `healthy` — recent traffic.
@@ -91819,9 +91867,10 @@ var RealtimeHealth = class {
91819
91867
  verdict(connected) {
91820
91868
  if (!connected) return "disconnected";
91821
91869
  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";
91870
+ if (silent < this.silentWindowMs / 2) return "healthy";
91871
+ if (silent < this.silentWindowMs) return "quiet";
91872
+ if (this.lastPingAt && this.now() - this.lastPingAt < this.pingGraceMs) return "suspect";
91873
+ if (this.silentPings < 3) return "suspect";
91825
91874
  return "dead";
91826
91875
  }
91827
91876
  /** A snapshot for diagnostics. */
@@ -91843,8 +91892,11 @@ var RealtimeHealth = class {
91843
91892
  }
91844
91893
  };
91845
91894
  function startRealtimeSupervisor(options) {
91846
- const health = new RealtimeHealth();
91847
- const interval = options.intervalMs ?? 3 * 60 * 1e3;
91895
+ const health = options.health || new RealtimeHealth(void 0, {
91896
+ silentWindowMs: options.silentWindowMs,
91897
+ pingGraceMs: options.pingGraceMs
91898
+ });
91899
+ const interval = options.intervalMs ?? 10 * 60 * 1e3;
91848
91900
  let stopped = false;
91849
91901
  let timer = null;
91850
91902
  const evaluate = () => {
@@ -101092,6 +101144,9 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
101092
101144
  }
101093
101145
  lifecycle.healthSupervisor = startRealtimeSupervisor({
101094
101146
  isConnected: () => generation === lifecycle.generation && client2.connected,
101147
+ // Share the per-connection tracker so this supervisor sees
101148
+ // every inbound frame the listener records.
101149
+ health,
101095
101150
  reconnect: (reason) => {
101096
101151
  if (generation !== lifecycle.generation) return;
101097
101152
  if (lifecycle.stopped || ctx.loggedIn === false) return;
@@ -101798,4 +101853,4 @@ lodash/lodash.js:
101798
101853
  *)
101799
101854
  */
101800
101855
 
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 };
101856
+ export { PING_GRACE_MS, RealtimeHealth, RecoverySupervisor, SILENT_WINDOW_MS, acquireCookies, applySetCookie, awaitProactiveSlot, canActProactively, cleanJsonResponse, 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, uploadGroupImage, uploadMercuryAttachment, warmUpSession, withCometTokens, withSessionRetry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "A modern TypeScript Facebook Chat API client for building reliable Messenger bots, with built-in anti-detection protection, automatic cookie refresh, music search, and AI theme support.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -57,7 +57,7 @@
57
57
  "prepublishOnly": "npm run build",
58
58
  "lint": "npm run typecheck",
59
59
  "format": "prettier --write \"src/**/*.ts\" \"index.ts\"",
60
- "test": "npm run build && node test/smoke.test.cjs && node test/smoke.test.mjs && node test/music-search.test.cjs && node test/mqtt-lifecycle.test.cjs && node test/session-reliability.test.cjs && node test/anti-detection.test.cjs && node test/account-safety.test.cjs && node test/liveness.test.cjs && node test/recovery.test.cjs && node test/cookie-acquisition.test.cjs && node test/realtime-health.test.cjs",
60
+ "test": "npm run build && node test/smoke.test.cjs && node test/smoke.test.mjs && node test/music-search.test.cjs && node test/mqtt-lifecycle.test.cjs && node test/session-reliability.test.cjs && node test/anti-detection.test.cjs && node test/account-safety.test.cjs && node test/liveness.test.cjs && node test/recovery.test.cjs && node test/cookie-acquisition.test.cjs && node test/realtime-health.test.cjs && node test/attachment-resilience.test.cjs",
61
61
  "audit": "npm audit",
62
62
  "audit:fix": "npm audit fix"
63
63
  },