@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 +77 -19
- package/dist/index.d.mts +31 -5
- package/dist/index.d.ts +31 -5
- package/dist/index.mjs +75 -20
- package/package.json +2 -2
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
|
-
|
|
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 =
|
|
91809
|
-
var PING_GRACE_MS =
|
|
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
|
|
91849
|
-
*
|
|
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 <
|
|
91858
|
-
if (silent <
|
|
91859
|
-
if (this.lastPingAt && this.now() - this.lastPingAt <
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1173
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
1173
|
-
*
|
|
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
|
|
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
|
-
|
|
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 =
|
|
91774
|
-
var PING_GRACE_MS =
|
|
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
|
|
91814
|
-
*
|
|
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 <
|
|
91823
|
-
if (silent <
|
|
91824
|
-
if (this.lastPingAt && this.now() - this.lastPingAt <
|
|
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
|
-
|
|
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.
|
|
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
|
},
|