@lazyneoaz/metachat 5.0.0 → 5.1.0
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 +313 -102
- package/dist/index.d.mts +50 -5
- package/dist/index.d.ts +50 -5
- package/dist/index.mjs +305 -103
- package/package.json +2 -2
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) => {
|
|
@@ -75519,6 +75523,7 @@ __export(utils_exports2, {
|
|
|
75519
75523
|
BASE_COOLDOWN_MS: () => BASE_COOLDOWN_MS,
|
|
75520
75524
|
BRAND: () => BRAND,
|
|
75521
75525
|
CHROME_MAJORS: () => CHROME_MAJORS,
|
|
75526
|
+
CORE_REACTIONS: () => CORE_REACTIONS,
|
|
75522
75527
|
ConfigurationError: () => ConfigurationError,
|
|
75523
75528
|
DESKTOP_PLATFORMS: () => DESKTOP_PLATFORMS,
|
|
75524
75529
|
FCAError: () => FCAError,
|
|
@@ -75530,6 +75535,7 @@ __export(utils_exports2, {
|
|
|
75530
75535
|
NotFoundError: () => NotFoundError,
|
|
75531
75536
|
PING_GRACE_MS: () => PING_GRACE_MS,
|
|
75532
75537
|
PermissionError: () => PermissionError,
|
|
75538
|
+
REACTION_ALIASES: () => REACTION_ALIASES,
|
|
75533
75539
|
RateLimitError: () => RateLimitError,
|
|
75534
75540
|
RealtimeHealth: () => RealtimeHealth,
|
|
75535
75541
|
RecoverySupervisor: () => RecoverySupervisor,
|
|
@@ -75611,6 +75617,8 @@ __export(utils_exports2, {
|
|
|
75611
75617
|
isAuthFailure: () => isAuthFailure,
|
|
75612
75618
|
isBreakerOpen: () => isBreakerOpen,
|
|
75613
75619
|
isLoggedOutDocument: () => isLoggedOutDocument,
|
|
75620
|
+
isNonCriticalError: () => isNonCriticalError,
|
|
75621
|
+
isReactionEmoji: () => isReactionEmoji,
|
|
75614
75622
|
isReadableStream: () => isReadableStream2,
|
|
75615
75623
|
isStaleTokenError: () => isStaleTokenError,
|
|
75616
75624
|
json: () => json2,
|
|
@@ -75621,6 +75629,7 @@ __export(utils_exports2, {
|
|
|
75621
75629
|
meta: () => meta,
|
|
75622
75630
|
missingSessionCookies: () => missingSessionCookies,
|
|
75623
75631
|
normalizeBrowserProfile: () => normalizeBrowserProfile,
|
|
75632
|
+
normalizeReaction: () => normalizeReaction,
|
|
75624
75633
|
noteAction: () => noteAction,
|
|
75625
75634
|
noteAuthFailure: () => noteAuthFailure,
|
|
75626
75635
|
noteCheckpoint: () => noteCheckpoint,
|
|
@@ -75642,6 +75651,7 @@ __export(utils_exports2, {
|
|
|
75642
75651
|
resetBreaker: () => resetBreaker,
|
|
75643
75652
|
resolveProfile: () => resolveProfile,
|
|
75644
75653
|
retryDelay: () => retryDelay,
|
|
75654
|
+
retryOnNonCritical: () => retryOnNonCritical,
|
|
75645
75655
|
safetySnapshot: () => safetySnapshot,
|
|
75646
75656
|
saveCookies: () => saveCookies,
|
|
75647
75657
|
scheduleSessionKeepAlive: () => scheduleSessionKeepAlive,
|
|
@@ -90718,6 +90728,30 @@ function buildWsTaskContent(ctx, tasks, versionId) {
|
|
|
90718
90728
|
var import_form_data3 = __toESM(require_form_data2());
|
|
90719
90729
|
init_constants();
|
|
90720
90730
|
init_user_agents();
|
|
90731
|
+
|
|
90732
|
+
// src/utils/transient.ts
|
|
90733
|
+
var NON_CRITICAL_CODES = /* @__PURE__ */ new Set([1357004, 1357001, 1357031]);
|
|
90734
|
+
function isNonCriticalError(value) {
|
|
90735
|
+
if (!value || typeof value !== "object") return false;
|
|
90736
|
+
const body = value.body && typeof value.body === "object" ? value.body : value;
|
|
90737
|
+
if (body.isNotCritical === 1 || body.is_not_critical === 1) return true;
|
|
90738
|
+
const num = Number(body.error ?? body.errorCode ?? body.code);
|
|
90739
|
+
if (NON_CRITICAL_CODES.has(num)) return true;
|
|
90740
|
+
const text3 = String(body.errorDescription || body.errorSummary || body.message || "");
|
|
90741
|
+
return /re-?open(ing)? your browser|try again(?: later)?|temporarily unavailable/i.test(text3);
|
|
90742
|
+
}
|
|
90743
|
+
async function retryOnNonCritical(operation, refresh) {
|
|
90744
|
+
try {
|
|
90745
|
+
const first2 = await operation();
|
|
90746
|
+
if (!isNonCriticalError(first2)) return first2;
|
|
90747
|
+
} catch (error2) {
|
|
90748
|
+
if (!isNonCriticalError(error2)) throw error2;
|
|
90749
|
+
}
|
|
90750
|
+
await refresh();
|
|
90751
|
+
return operation();
|
|
90752
|
+
}
|
|
90753
|
+
|
|
90754
|
+
// src/utils/attachments.ts
|
|
90721
90755
|
function cleanJsonResponse(value) {
|
|
90722
90756
|
if (typeof value !== "string") return value;
|
|
90723
90757
|
const normalized = value.replace(/^for\s*\(;;\);\s*/i, "");
|
|
@@ -90810,18 +90844,76 @@ function buildUploadQuery(ctx) {
|
|
|
90810
90844
|
return qs2;
|
|
90811
90845
|
}
|
|
90812
90846
|
var EXTENSION_BY_MIME = {
|
|
90847
|
+
// video
|
|
90813
90848
|
"video/mp4": "mp4",
|
|
90849
|
+
"video/quicktime": "mov",
|
|
90850
|
+
"video/webm": "webm",
|
|
90851
|
+
"video/x-matroska": "mkv",
|
|
90852
|
+
"video/x-msvideo": "avi",
|
|
90853
|
+
"video/mpeg": "mpeg",
|
|
90854
|
+
"video/3gpp": "3gp",
|
|
90855
|
+
// audio
|
|
90814
90856
|
"audio/mp4": "m4a",
|
|
90857
|
+
"audio/x-m4a": "m4a",
|
|
90815
90858
|
"audio/mpeg": "mp3",
|
|
90816
90859
|
"audio/mp3": "mp3",
|
|
90817
90860
|
"audio/aac": "aac",
|
|
90818
90861
|
"audio/ogg": "ogg",
|
|
90862
|
+
"audio/opus": "opus",
|
|
90819
90863
|
"audio/wav": "wav",
|
|
90864
|
+
"audio/x-wav": "wav",
|
|
90865
|
+
"audio/webm": "weba",
|
|
90866
|
+
"audio/flac": "flac",
|
|
90867
|
+
"audio/x-flac": "flac",
|
|
90868
|
+
"audio/3gpp": "3gp",
|
|
90869
|
+
// image
|
|
90820
90870
|
"image/jpeg": "jpg",
|
|
90871
|
+
"image/jpg": "jpg",
|
|
90821
90872
|
"image/png": "png",
|
|
90822
90873
|
"image/gif": "gif",
|
|
90823
|
-
"image/webp": "webp"
|
|
90874
|
+
"image/webp": "webp",
|
|
90875
|
+
"image/bmp": "bmp",
|
|
90876
|
+
"image/tiff": "tiff",
|
|
90877
|
+
"image/svg+xml": "svg",
|
|
90878
|
+
"image/heic": "heic",
|
|
90879
|
+
"image/heif": "heif",
|
|
90880
|
+
"image/avif": "avif",
|
|
90881
|
+
// documents / archives
|
|
90882
|
+
"application/pdf": "pdf",
|
|
90883
|
+
"application/zip": "zip",
|
|
90884
|
+
"application/x-zip-compressed": "zip",
|
|
90885
|
+
"application/x-rar-compressed": "rar",
|
|
90886
|
+
"application/vnd.rar": "rar",
|
|
90887
|
+
"application/x-7z-compressed": "7z",
|
|
90888
|
+
"application/gzip": "gz",
|
|
90889
|
+
"application/x-tar": "tar",
|
|
90890
|
+
"text/plain": "txt",
|
|
90891
|
+
"text/csv": "csv",
|
|
90892
|
+
"text/html": "html",
|
|
90893
|
+
"text/calendar": "ics",
|
|
90894
|
+
"application/json": "json",
|
|
90895
|
+
"application/xml": "xml",
|
|
90896
|
+
"text/xml": "xml",
|
|
90897
|
+
"application/msword": "doc",
|
|
90898
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
90899
|
+
"application/vnd.ms-excel": "xls",
|
|
90900
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
90901
|
+
"application/vnd.ms-powerpoint": "ppt",
|
|
90902
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
|
|
90903
|
+
"application/vnd.android.package-archive": "apk",
|
|
90904
|
+
"application/x-msdownload": "exe",
|
|
90905
|
+
"application/octet-stream": "bin"
|
|
90824
90906
|
};
|
|
90907
|
+
function extensionForMime(mime) {
|
|
90908
|
+
if (!mime) return "";
|
|
90909
|
+
const normalized = String(mime).toLowerCase().split(";")[0].trim();
|
|
90910
|
+
if (EXTENSION_BY_MIME[normalized]) return EXTENSION_BY_MIME[normalized];
|
|
90911
|
+
const subtype = normalized.split("/")[1] || "";
|
|
90912
|
+
if (!subtype) return "";
|
|
90913
|
+
if (/\+([a-z0-9]+)$/.test(subtype)) return subtype.replace(/.*\+/, "");
|
|
90914
|
+
const cleaned = subtype.replace(/^vnd\./, "").replace(/^x-/, "").replace(/[-.]/g, "");
|
|
90915
|
+
return /^[a-z0-9]{1,10}$/.test(cleaned) ? cleaned : "";
|
|
90916
|
+
}
|
|
90825
90917
|
function sniffMime(buffer) {
|
|
90826
90918
|
if (buffer.length < 12) return "";
|
|
90827
90919
|
const b = buffer;
|
|
@@ -90846,11 +90938,11 @@ function sniffMime(buffer) {
|
|
|
90846
90938
|
}
|
|
90847
90939
|
function normalizeFilename(filename, mime) {
|
|
90848
90940
|
const base = filename && String(filename).trim() ? String(filename).trim() : `file-${Date.now()}`;
|
|
90849
|
-
const ext =
|
|
90850
|
-
const match = /\.[a-z0-9]{1,
|
|
90941
|
+
const ext = extensionForMime(mime);
|
|
90942
|
+
const match = /\.[a-z0-9]{1,6}$/i.exec(base);
|
|
90851
90943
|
const currentExt = match ? match[0].slice(1).toLowerCase() : "";
|
|
90852
90944
|
if (ext && currentExt !== ext) {
|
|
90853
|
-
const audioOverride = (ext === "m4a" || ext === "mp3" || ext === "aac" || ext === "ogg" || ext === "wav") && (currentExt === "mp4" || currentExt === "m4v" || currentExt === "");
|
|
90945
|
+
const audioOverride = (ext === "m4a" || ext === "mp3" || ext === "aac" || ext === "ogg" || ext === "wav" || ext === "opus" || ext === "flac") && (currentExt === "mp4" || currentExt === "m4v" || currentExt === "");
|
|
90854
90946
|
if (audioOverride || !currentExt) {
|
|
90855
90947
|
return currentExt ? `${base.slice(0, base.length - currentExt.length - 1)}.${ext}` : `${base}.${ext}`;
|
|
90856
90948
|
}
|
|
@@ -90860,16 +90952,45 @@ function normalizeFilename(filename, mime) {
|
|
|
90860
90952
|
}
|
|
90861
90953
|
async function uploadMercuryAttachment(ctx, defaultFuncs, attachment) {
|
|
90862
90954
|
const normalized = await normalizeAttachmentInput(attachment, ctx);
|
|
90955
|
+
const buffer = await readAll(normalized.stream);
|
|
90863
90956
|
const url2 = "https://www.facebook.com/ajax/mercury/upload.php";
|
|
90957
|
+
let lastError;
|
|
90958
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
90959
|
+
if (attempt > 0) await refreshUploadTokens(ctx, defaultFuncs);
|
|
90960
|
+
await ensureUploadTokens(ctx, defaultFuncs);
|
|
90961
|
+
const formData = new import_form_data3.default();
|
|
90962
|
+
formData.append("farr", Readable.from(buffer), {
|
|
90963
|
+
filename: normalized.filename,
|
|
90964
|
+
contentType: normalized.contentType || void 0
|
|
90965
|
+
});
|
|
90966
|
+
let data2;
|
|
90967
|
+
try {
|
|
90968
|
+
const res = await defaultFuncs.postMultipart(url2, ctx.jar, formData, buildUploadQuery(ctx));
|
|
90969
|
+
data2 = cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
|
|
90970
|
+
} catch (error2) {
|
|
90971
|
+
lastError = error2;
|
|
90972
|
+
if (attempt === 0 && isNonCriticalError(error2)) continue;
|
|
90973
|
+
throw error2;
|
|
90974
|
+
}
|
|
90975
|
+
if (extractAttachmentIds(data2).length) return finalizeUpload(data2);
|
|
90976
|
+
lastError = buildNoMetadataError(data2);
|
|
90977
|
+
if (attempt === 0 && isNonCriticalError(lastError)) continue;
|
|
90978
|
+
throw lastError;
|
|
90979
|
+
}
|
|
90980
|
+
throw lastError || buildNoMetadataError(null);
|
|
90981
|
+
}
|
|
90982
|
+
function buildNoMetadataError(data2) {
|
|
90983
|
+
const err = new Error("UploadFb returned no metadata/ids");
|
|
90984
|
+
err.code = "NO_METADATA";
|
|
90985
|
+
err.body = data2;
|
|
90986
|
+
return err;
|
|
90987
|
+
}
|
|
90988
|
+
async function refreshUploadTokens(ctx, defaultFuncs) {
|
|
90989
|
+
ctx.lsd = void 0;
|
|
90990
|
+
ctx.master = ctx.master || {};
|
|
90991
|
+
ctx.master.__rev = void 0;
|
|
90992
|
+
if (typeof ctx.__cometTokens === "object") ctx.__cometTokens = void 0;
|
|
90864
90993
|
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
90994
|
}
|
|
90874
90995
|
async function normalizeAttachmentInput(attachment, ctx) {
|
|
90875
90996
|
if (Buffer.isBuffer(attachment)) {
|
|
@@ -90976,6 +91097,68 @@ async function uploadGroupImage(ctx, defaultFuncs, image) {
|
|
|
90976
91097
|
return cleanJsonResponse(res && res.body !== void 0 ? res.body : res);
|
|
90977
91098
|
}
|
|
90978
91099
|
|
|
91100
|
+
// src/utils/reactions.ts
|
|
91101
|
+
var CORE_REACTIONS = [
|
|
91102
|
+
"\u{1F60D}",
|
|
91103
|
+
"\u{1F606}",
|
|
91104
|
+
"\u{1F62E}",
|
|
91105
|
+
"\u{1F622}",
|
|
91106
|
+
"\u{1F620}",
|
|
91107
|
+
"\u{1F44D}",
|
|
91108
|
+
"\u{1F44E}",
|
|
91109
|
+
"\u2764",
|
|
91110
|
+
"\u{1F497}",
|
|
91111
|
+
""
|
|
91112
|
+
];
|
|
91113
|
+
var REACTION_ALIASES = {
|
|
91114
|
+
":heart_eyes:": "\u{1F60D}",
|
|
91115
|
+
":love:": "\u{1F60D}",
|
|
91116
|
+
":laughing:": "\u{1F606}",
|
|
91117
|
+
":haha:": "\u{1F606}",
|
|
91118
|
+
":open_mouth:": "\u{1F62E}",
|
|
91119
|
+
":wow:": "\u{1F62E}",
|
|
91120
|
+
":cry:": "\u{1F622}",
|
|
91121
|
+
":sad:": "\u{1F622}",
|
|
91122
|
+
":angry:": "\u{1F620}",
|
|
91123
|
+
":thumbsup:": "\u{1F44D}",
|
|
91124
|
+
":like:": "\u{1F44D}",
|
|
91125
|
+
":thumbsdown:": "\u{1F44E}",
|
|
91126
|
+
":dislike:": "\u{1F44E}",
|
|
91127
|
+
":heart:": "\u2764",
|
|
91128
|
+
":glowingheart:": "\u{1F497}"
|
|
91129
|
+
};
|
|
91130
|
+
function isReactionEmoji(value) {
|
|
91131
|
+
if (value == null) return false;
|
|
91132
|
+
const text3 = String(value).trim();
|
|
91133
|
+
if (!text3) return false;
|
|
91134
|
+
if (CORE_REACTIONS.indexOf(text3) !== -1) return true;
|
|
91135
|
+
let clusters;
|
|
91136
|
+
try {
|
|
91137
|
+
const seg = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
91138
|
+
clusters = Array.from(seg.segment(text3), (s) => s.segment);
|
|
91139
|
+
} catch {
|
|
91140
|
+
clusters = Array.from(text3);
|
|
91141
|
+
}
|
|
91142
|
+
if (clusters.length !== 1) return false;
|
|
91143
|
+
const only = clusters[0];
|
|
91144
|
+
if (!new RegExp("\\p{Extended_Pictographic}", "u").test(only) && !/\uFE0F|\u20E3/u.test(only)) return false;
|
|
91145
|
+
if (!/[\p{Extended_Pictographic}\uFE0F\u20E3]/u.test(only)) return false;
|
|
91146
|
+
if (/[\u0000-\u001F\u007F]/.test(only)) return false;
|
|
91147
|
+
return true;
|
|
91148
|
+
}
|
|
91149
|
+
function normalizeReaction(value, options = {}) {
|
|
91150
|
+
const raw = value == null ? "" : String(value).trim();
|
|
91151
|
+
if (raw === "") {
|
|
91152
|
+
if (options.allowRemove) return "";
|
|
91153
|
+
throw new Error("Reaction is not a valid emoji.");
|
|
91154
|
+
}
|
|
91155
|
+
const resolved = REACTION_ALIASES[raw] !== void 0 ? REACTION_ALIASES[raw] : raw;
|
|
91156
|
+
if (!options.forceCustom && !isReactionEmoji(resolved)) {
|
|
91157
|
+
throw new Error(`Reaction is not a valid emoji: ${JSON.stringify(raw)}`);
|
|
91158
|
+
}
|
|
91159
|
+
return resolved;
|
|
91160
|
+
}
|
|
91161
|
+
|
|
90979
91162
|
// src/utils/lsd.ts
|
|
90980
91163
|
var TOKEN_TTL_MS = 10 * 60 * 1e3;
|
|
90981
91164
|
var STALE_TOKEN_ERRORS = /* @__PURE__ */ new Set([
|
|
@@ -91770,10 +91953,10 @@ function startRecovery(ctx, runtime, options = {}) {
|
|
|
91770
91953
|
|
|
91771
91954
|
// src/utils/realtime-health.ts
|
|
91772
91955
|
init_pacing();
|
|
91773
|
-
var SILENT_WINDOW_MS =
|
|
91774
|
-
var PING_GRACE_MS =
|
|
91956
|
+
var SILENT_WINDOW_MS = 30 * 60 * 1e3;
|
|
91957
|
+
var PING_GRACE_MS = 60 * 1e3;
|
|
91775
91958
|
var RealtimeHealth = class {
|
|
91776
|
-
constructor(now = () => Date.now()) {
|
|
91959
|
+
constructor(now = () => Date.now(), options = {}) {
|
|
91777
91960
|
this.now = now;
|
|
91778
91961
|
this.frames = 0;
|
|
91779
91962
|
this.lastFrameAt = 0;
|
|
@@ -91782,17 +91965,23 @@ var RealtimeHealth = class {
|
|
|
91782
91965
|
this.forcedReconnects = 0;
|
|
91783
91966
|
this.lastPingAt = 0;
|
|
91784
91967
|
this.startedAt = Date.now();
|
|
91968
|
+
/** Consecutive pings sent without any resulting frame. */
|
|
91969
|
+
this.silentPings = 0;
|
|
91785
91970
|
this.startedAt = this.now();
|
|
91971
|
+
this.silentWindowMs = options.silentWindowMs ?? SILENT_WINDOW_MS;
|
|
91972
|
+
this.pingGraceMs = options.pingGraceMs ?? PING_GRACE_MS;
|
|
91786
91973
|
}
|
|
91787
91974
|
/** Record any inbound frame (message, pong, anything from the broker). */
|
|
91788
91975
|
noteFrame() {
|
|
91789
91976
|
this.frames += 1;
|
|
91790
91977
|
this.lastFrameAt = this.now();
|
|
91978
|
+
this.silentPings = 0;
|
|
91791
91979
|
}
|
|
91792
91980
|
/** Record a successful connect. */
|
|
91793
91981
|
noteConnected() {
|
|
91794
91982
|
this.connectedAt = this.now();
|
|
91795
91983
|
this.lastFrameAt = this.now();
|
|
91984
|
+
this.silentPings = 0;
|
|
91796
91985
|
}
|
|
91797
91986
|
/** Record that the transport reports itself disconnected. */
|
|
91798
91987
|
noteDisconnected() {
|
|
@@ -91801,6 +91990,7 @@ var RealtimeHealth = class {
|
|
|
91801
91990
|
/** Record an active ping being sent. */
|
|
91802
91991
|
notePing() {
|
|
91803
91992
|
this.lastPingAt = this.now();
|
|
91993
|
+
this.silentPings += 1;
|
|
91804
91994
|
}
|
|
91805
91995
|
sinceLastFrame() {
|
|
91806
91996
|
const base = this.lastFrameAt || this.connectedAt || this.startedAt;
|
|
@@ -91810,8 +92000,8 @@ var RealtimeHealth = class {
|
|
|
91810
92000
|
* Decide what to do about the current socket.
|
|
91811
92001
|
*
|
|
91812
92002
|
* - `disconnected` — the transport already knows it is down; reconnect.
|
|
91813
|
-
* - `dead` — connected on paper, silent past the window, and
|
|
91814
|
-
*
|
|
92003
|
+
* - `dead` — connected on paper, silent well past the window, and repeated
|
|
92004
|
+
* pings raised no traffic at all; reconnect.
|
|
91815
92005
|
* - `suspect` — silent past the window but a ping was only just sent; wait.
|
|
91816
92006
|
* - `quiet` — silent but inside the window; normal.
|
|
91817
92007
|
* - `healthy` — recent traffic.
|
|
@@ -91819,9 +92009,10 @@ var RealtimeHealth = class {
|
|
|
91819
92009
|
verdict(connected) {
|
|
91820
92010
|
if (!connected) return "disconnected";
|
|
91821
92011
|
const silent = this.sinceLastFrame();
|
|
91822
|
-
if (silent <
|
|
91823
|
-
if (silent <
|
|
91824
|
-
if (this.lastPingAt && this.now() - this.lastPingAt <
|
|
92012
|
+
if (silent < this.silentWindowMs / 2) return "healthy";
|
|
92013
|
+
if (silent < this.silentWindowMs) return "quiet";
|
|
92014
|
+
if (this.lastPingAt && this.now() - this.lastPingAt < this.pingGraceMs) return "suspect";
|
|
92015
|
+
if (this.silentPings < 3) return "suspect";
|
|
91825
92016
|
return "dead";
|
|
91826
92017
|
}
|
|
91827
92018
|
/** A snapshot for diagnostics. */
|
|
@@ -91843,8 +92034,11 @@ var RealtimeHealth = class {
|
|
|
91843
92034
|
}
|
|
91844
92035
|
};
|
|
91845
92036
|
function startRealtimeSupervisor(options) {
|
|
91846
|
-
const health = new RealtimeHealth(
|
|
91847
|
-
|
|
92037
|
+
const health = options.health || new RealtimeHealth(void 0, {
|
|
92038
|
+
silentWindowMs: options.silentWindowMs,
|
|
92039
|
+
pingGraceMs: options.pingGraceMs
|
|
92040
|
+
});
|
|
92041
|
+
const interval = options.intervalMs ?? 10 * 60 * 1e3;
|
|
91848
92042
|
let stopped = false;
|
|
91849
92043
|
let timer = null;
|
|
91850
92044
|
const evaluate = () => {
|
|
@@ -93561,7 +93755,8 @@ var allowedProperties = {
|
|
|
93561
93755
|
emojiSize: true,
|
|
93562
93756
|
body: true,
|
|
93563
93757
|
mentions: true,
|
|
93564
|
-
location: true
|
|
93758
|
+
location: true,
|
|
93759
|
+
edit: true
|
|
93565
93760
|
};
|
|
93566
93761
|
var sendMessage_default = (defaultFuncs, api, ctx) => {
|
|
93567
93762
|
async function uploadAttachment(attachments) {
|
|
@@ -93683,6 +93878,18 @@ var sendMessage_default = (defaultFuncs, api, ctx) => {
|
|
|
93683
93878
|
if (msgType === "String") {
|
|
93684
93879
|
msg = { body: msg };
|
|
93685
93880
|
}
|
|
93881
|
+
if (Array.isArray(msg.edit) && msg.edit.length > 0) {
|
|
93882
|
+
let body = String(msg.body || "");
|
|
93883
|
+
const edits = [...msg.edit].sort((a, b) => Number(b?.[1] ?? 0) - Number(a?.[1] ?? 0));
|
|
93884
|
+
for (const entry of edits) {
|
|
93885
|
+
const text3 = Array.isArray(entry) ? String(entry[0] ?? "") : String(entry ?? "");
|
|
93886
|
+
const rawOffset = Array.isArray(entry) ? Number(entry[1]) : NaN;
|
|
93887
|
+
const offset = Number.isFinite(rawOffset) ? Math.max(0, Math.min(body.length, rawOffset)) : body.length;
|
|
93888
|
+
body = body.slice(0, offset) + text3 + body.slice(offset);
|
|
93889
|
+
}
|
|
93890
|
+
msg.body = body;
|
|
93891
|
+
delete msg.edit;
|
|
93892
|
+
}
|
|
93686
93893
|
const finish = (err, data2) => {
|
|
93687
93894
|
settle2(err, data2);
|
|
93688
93895
|
return data2;
|
|
@@ -93956,45 +94163,6 @@ function sendTypingIndicator_default(defaultFuncs, api, ctx) {
|
|
|
93956
94163
|
}
|
|
93957
94164
|
|
|
93958
94165
|
// src/deltas/apis/messaging/setMessageReaction.ts
|
|
93959
|
-
var VALID_REACTIONS = [
|
|
93960
|
-
"\u{1F60D}",
|
|
93961
|
-
// :heart_eyes:
|
|
93962
|
-
"\u{1F606}",
|
|
93963
|
-
// :laughing:
|
|
93964
|
-
"\u{1F62E}",
|
|
93965
|
-
// :open_mouth:
|
|
93966
|
-
"\u{1F622}",
|
|
93967
|
-
// :cry:
|
|
93968
|
-
"\u{1F620}",
|
|
93969
|
-
// :angry:
|
|
93970
|
-
"\u{1F44D}",
|
|
93971
|
-
// :thumbsup:
|
|
93972
|
-
"\u{1F44E}",
|
|
93973
|
-
// :thumbsdown:
|
|
93974
|
-
"\u2764",
|
|
93975
|
-
// :heart:
|
|
93976
|
-
"\u{1F497}",
|
|
93977
|
-
// :glowingheart:
|
|
93978
|
-
""
|
|
93979
|
-
// remove
|
|
93980
|
-
];
|
|
93981
|
-
var ALIASES = {
|
|
93982
|
-
":heart_eyes:": "\u{1F60D}",
|
|
93983
|
-
":love:": "\u{1F60D}",
|
|
93984
|
-
":laughing:": "\u{1F606}",
|
|
93985
|
-
":haha:": "\u{1F606}",
|
|
93986
|
-
":open_mouth:": "\u{1F62E}",
|
|
93987
|
-
":wow:": "\u{1F62E}",
|
|
93988
|
-
":cry:": "\u{1F622}",
|
|
93989
|
-
":sad:": "\u{1F622}",
|
|
93990
|
-
":angry:": "\u{1F620}",
|
|
93991
|
-
":thumbsup:": "\u{1F44D}",
|
|
93992
|
-
":like:": "\u{1F44D}",
|
|
93993
|
-
":thumbsdown:": "\u{1F44E}",
|
|
93994
|
-
":dislike:": "\u{1F44E}",
|
|
93995
|
-
":heart:": "\u2764",
|
|
93996
|
-
":glowingheart:": "\u{1F497}"
|
|
93997
|
-
};
|
|
93998
94166
|
function setMessageReaction_default(defaultFuncs, _api, ctx) {
|
|
93999
94167
|
return async function setMessageReaction(reaction, messageID, callback, forceCustomReaction) {
|
|
94000
94168
|
const cb = typeof callback === "function" ? callback : void 0;
|
|
@@ -94011,11 +94179,14 @@ function setMessageReaction_default(defaultFuncs, _api, ctx) {
|
|
|
94011
94179
|
if (!messageID) {
|
|
94012
94180
|
throw { error: "setMessageReaction: messageID is required." };
|
|
94013
94181
|
}
|
|
94014
|
-
let value
|
|
94015
|
-
|
|
94016
|
-
value =
|
|
94017
|
-
|
|
94018
|
-
|
|
94182
|
+
let value;
|
|
94183
|
+
try {
|
|
94184
|
+
value = normalizeReaction(reaction, {
|
|
94185
|
+
allowRemove: true,
|
|
94186
|
+
forceCustom: !!forceCustomReaction
|
|
94187
|
+
});
|
|
94188
|
+
} catch (validationError) {
|
|
94189
|
+
throw { error: validationError?.message || "Reaction is not a valid emoji." };
|
|
94019
94190
|
}
|
|
94020
94191
|
const variables = {
|
|
94021
94192
|
data: {
|
|
@@ -94807,33 +94978,35 @@ function searchMusic_default(defaultFuncs, _api, ctx) {
|
|
|
94807
94978
|
}
|
|
94808
94979
|
const page = collectTracks(parsed);
|
|
94809
94980
|
const tracks = page.tracks.slice(0, count);
|
|
94810
|
-
if (tracks.length
|
|
94981
|
+
if (tracks.length) {
|
|
94982
|
+
const result = tracks.slice();
|
|
94983
|
+
result.tracks = tracks;
|
|
94984
|
+
result.endCursor = page.endCursor;
|
|
94985
|
+
result.hasNextPage = page.hasNextPage;
|
|
94986
|
+
result.query = text3;
|
|
94987
|
+
return result;
|
|
94988
|
+
}
|
|
94989
|
+
for (let retry = 0; retry < 3; retry++) {
|
|
94811
94990
|
invalidateCometTokens(ctx);
|
|
94991
|
+
await sleep(600 + retry * 900);
|
|
94812
94992
|
const retried = await attempt(true);
|
|
94813
|
-
if (
|
|
94814
|
-
|
|
94815
|
-
|
|
94816
|
-
|
|
94817
|
-
|
|
94818
|
-
|
|
94819
|
-
|
|
94820
|
-
|
|
94821
|
-
|
|
94822
|
-
|
|
94823
|
-
}
|
|
94993
|
+
if (isStaleTokenError(retried) || retried?.error || (retried?.errors || []).length) continue;
|
|
94994
|
+
const retryPage = collectTracks(retried);
|
|
94995
|
+
const retryTracks = retryPage.tracks.slice(0, count);
|
|
94996
|
+
if (retryTracks.length) {
|
|
94997
|
+
const result = retryTracks.slice();
|
|
94998
|
+
result.tracks = retryTracks;
|
|
94999
|
+
result.endCursor = retryPage.endCursor;
|
|
95000
|
+
result.hasNextPage = retryPage.hasNextPage;
|
|
95001
|
+
result.query = text3;
|
|
95002
|
+
return result;
|
|
94824
95003
|
}
|
|
94825
|
-
throw {
|
|
94826
|
-
error: `No music results found for "${text3}". The Facebook music catalog may be unavailable for this account or region.`,
|
|
94827
|
-
code: "NO_RESULTS",
|
|
94828
|
-
query: text3
|
|
94829
|
-
};
|
|
94830
95004
|
}
|
|
94831
|
-
|
|
94832
|
-
|
|
94833
|
-
|
|
94834
|
-
|
|
94835
|
-
|
|
94836
|
-
return result;
|
|
95005
|
+
throw {
|
|
95006
|
+
error: `No music results found for "${text3}". The Facebook music catalog may be unavailable for this account or region.`,
|
|
95007
|
+
code: "NO_RESULTS",
|
|
95008
|
+
query: text3
|
|
95009
|
+
};
|
|
94837
95010
|
}
|
|
94838
95011
|
return function searchMusic(query = "", options = {}, callback) {
|
|
94839
95012
|
const selectedOptions = typeof options === "function" ? {} : options || {};
|
|
@@ -98990,15 +99163,22 @@ function story_default(defaultFuncs, api, ctx) {
|
|
|
98990
99163
|
}
|
|
98991
99164
|
async function sendStoryReply(storyIdOrUrl, message, isReaction) {
|
|
98992
99165
|
try {
|
|
98993
|
-
const allowedReactions = ["\u2764\uFE0F", "\u{1F44D}", "\u{1F917}", "\u{1F606}", "\u{1F621}", "\u{1F622}", "\u{1F62E}"];
|
|
98994
99166
|
if (!storyIdOrUrl) throw new Error("Story ID or URL is required.");
|
|
98995
99167
|
if (!message) throw new Error("A message or reaction is required.");
|
|
99168
|
+
let reactionValue = message;
|
|
99169
|
+
if (isReaction) {
|
|
99170
|
+
try {
|
|
99171
|
+
reactionValue = normalizeReaction(message);
|
|
99172
|
+
} catch (validationError) {
|
|
99173
|
+
throw new Error(validationError?.message || "Reaction is not a valid emoji.");
|
|
99174
|
+
}
|
|
99175
|
+
}
|
|
98996
99176
|
let storyID = getStoryIDFromURL(storyIdOrUrl);
|
|
98997
99177
|
if (!storyID) storyID = storyIdOrUrl;
|
|
98998
99178
|
const variables = {
|
|
98999
99179
|
input: {
|
|
99000
99180
|
attribution_id_v2: "StoriesCometSuspenseRoot.react,comet.stories.viewer,via_cold_start",
|
|
99001
|
-
message,
|
|
99181
|
+
message: reactionValue,
|
|
99002
99182
|
story_id: storyID,
|
|
99003
99183
|
story_reply_type: isReaction ? "LIGHT_WEIGHT" : "TEXT",
|
|
99004
99184
|
actor_id: ctx.userID,
|
|
@@ -99006,12 +99186,9 @@ function story_default(defaultFuncs, api, ctx) {
|
|
|
99006
99186
|
}
|
|
99007
99187
|
};
|
|
99008
99188
|
if (isReaction) {
|
|
99009
|
-
if (!allowedReactions.includes(message)) {
|
|
99010
|
-
throw new Error(`Invalid reaction. Please use one of: ${allowedReactions.join(" ")}`);
|
|
99011
|
-
}
|
|
99012
99189
|
variables.input.lightweight_reaction_actions = {
|
|
99013
99190
|
offsets: [0],
|
|
99014
|
-
reaction:
|
|
99191
|
+
reaction: reactionValue
|
|
99015
99192
|
};
|
|
99016
99193
|
}
|
|
99017
99194
|
const form = {
|
|
@@ -99420,14 +99597,32 @@ function getThreadInfo_default(defaultFuncs, api, ctx) {
|
|
|
99420
99597
|
throw new Error("getThreadInfo: malformed response from GraphQL.");
|
|
99421
99598
|
}
|
|
99422
99599
|
const threadInfos = {};
|
|
99423
|
-
|
|
99600
|
+
let matched = 0;
|
|
99601
|
+
for (let i2 = 0; i2 < resData.length; i2++) {
|
|
99424
99602
|
const res = resData[i2];
|
|
99425
|
-
if (!res || res
|
|
99426
|
-
const
|
|
99603
|
+
if (!res || typeof res !== "object" || res.error_results) continue;
|
|
99604
|
+
const keys = Object.keys(res);
|
|
99605
|
+
const queryKey = keys.find((k) => /^o\d+$/.test(k));
|
|
99606
|
+
let index3;
|
|
99607
|
+
if (queryKey) {
|
|
99608
|
+
index3 = Number(queryKey.slice(1));
|
|
99609
|
+
} else if (typeof res.__ar === "number" || res.error !== void 0) {
|
|
99610
|
+
continue;
|
|
99611
|
+
} else {
|
|
99612
|
+
index3 = i2;
|
|
99613
|
+
}
|
|
99614
|
+
if (index3 < 0 || index3 >= threadIDs.length) continue;
|
|
99615
|
+
const data2 = res[queryKey || keys[0]];
|
|
99616
|
+
if (!data2 || !data2.data) continue;
|
|
99617
|
+
const threadInfo = formatThreadGraphQLResponse(data2.data);
|
|
99427
99618
|
if (threadInfo) {
|
|
99428
|
-
threadInfos[threadInfo.threadID || threadIDs[
|
|
99619
|
+
threadInfos[threadInfo.threadID || threadIDs[index3]] = threadInfo;
|
|
99620
|
+
matched++;
|
|
99429
99621
|
}
|
|
99430
99622
|
}
|
|
99623
|
+
if (matched === 0) {
|
|
99624
|
+
throw new Error("getThreadInfo: malformed response from GraphQL.");
|
|
99625
|
+
}
|
|
99431
99626
|
return Array.isArray(threadID) ? threadInfos : Object.values(threadInfos)[0] || null;
|
|
99432
99627
|
} catch (err) {
|
|
99433
99628
|
error("getThreadInfo", err);
|
|
@@ -99966,7 +100161,10 @@ var getUserInfo_default = (defaultFuncs, api, ctx) => {
|
|
|
99966
100161
|
form[`ids[${i2}]`] = v;
|
|
99967
100162
|
});
|
|
99968
100163
|
const getGenderString = (code) => code === 1 ? "male" : code === 2 ? "female" : "no specific gender";
|
|
99969
|
-
defaultFuncs.post("https://www.facebook.com/chat/user_info/", ctx.jar, form).then((resData) => parseAndCheckLogin(ctx, defaultFuncs)(resData))
|
|
100164
|
+
const performFetch = () => defaultFuncs.post("https://www.facebook.com/chat/user_info/", ctx.jar, form).then((resData) => parseAndCheckLogin(ctx, defaultFuncs)(resData));
|
|
100165
|
+
retryOnNonCritical(performFetch, () => {
|
|
100166
|
+
invalidateCometTokens?.(ctx);
|
|
100167
|
+
}).then((resData) => {
|
|
99970
100168
|
if (resData?.error && resData?.error !== 3252001) throw resData;
|
|
99971
100169
|
const retObj = {};
|
|
99972
100170
|
const profiles = resData?.payload?.profiles;
|
|
@@ -101092,6 +101290,9 @@ var listenMqtt_default = (defaultFuncs, api, ctx) => {
|
|
|
101092
101290
|
}
|
|
101093
101291
|
lifecycle.healthSupervisor = startRealtimeSupervisor({
|
|
101094
101292
|
isConnected: () => generation === lifecycle.generation && client2.connected,
|
|
101293
|
+
// Share the per-connection tracker so this supervisor sees
|
|
101294
|
+
// every inbound frame the listener records.
|
|
101295
|
+
health,
|
|
101095
101296
|
reconnect: (reason) => {
|
|
101096
101297
|
if (generation !== lifecycle.generation) return;
|
|
101097
101298
|
if (lifecycle.stopped || ctx.loggedIn === false) return;
|
|
@@ -101474,6 +101675,7 @@ async function loginHelper(credentials, globalOptions, callback, setOptionsFunc,
|
|
|
101474
101675
|
}
|
|
101475
101676
|
if (listenMqtt_default) {
|
|
101476
101677
|
api["listenMqtt"] = listenMqtt_default(defaultFuncs, api, ctx);
|
|
101678
|
+
api["listen"] = api["listenMqtt"];
|
|
101477
101679
|
}
|
|
101478
101680
|
};
|
|
101479
101681
|
api.getCurrentUserID = () => ctx.userID;
|
|
@@ -101798,4 +102000,4 @@ lodash/lodash.js:
|
|
|
101798
102000
|
*)
|
|
101799
102001
|
*/
|
|
101800
102002
|
|
|
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 };
|
|
102003
|
+
export { CORE_REACTIONS, PING_GRACE_MS, REACTION_ALIASES, 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, isNonCriticalError, isReactionEmoji, isStaleTokenError, login, missingSessionCookies, normalizeBrowserProfile, normalizeReaction, noteAction, noteAuthFailure, noteCheckpoint, noteRateLimit, paceRequest, public_exports as publicTypes, randomUserAgent, refreshSessionCookies, repairSession, reserveRequest, resetBreaker, resolveProfile, retryDelay, retryOnNonCritical, 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.
|
|
3
|
+
"version": "5.1.0",
|
|
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 && node test/reaction-validation.test.cjs && node test/api-surface-fixes.test.cjs && node test/threadinfo-regression.test.cjs",
|
|
61
61
|
"audit": "npm audit",
|
|
62
62
|
"audit:fix": "npm audit fix"
|
|
63
63
|
},
|