chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -35,24 +35,17 @@ export async function checkInboundAccessControl(params: {
});
const dmPolicy = cfg.channels?.whatsapp?.dmPolicy ?? "pairing";
const configuredAllowFrom = account.allowFrom;
const storeAllowFrom = await readChannelAllowFromStore("whatsapp").catch(
() => [],
);
const storeAllowFrom = await readChannelAllowFromStore("whatsapp").catch(() => []);
// Without user config, default to self-only DM access so the owner can talk to themselves.
const combinedAllowFrom = Array.from(
new Set([...(configuredAllowFrom ?? []), ...storeAllowFrom]),
);
const defaultAllowFrom =
combinedAllowFrom.length === 0 && params.selfE164
? [params.selfE164]
: undefined;
const allowFrom =
combinedAllowFrom.length > 0 ? combinedAllowFrom : defaultAllowFrom;
combinedAllowFrom.length === 0 && params.selfE164 ? [params.selfE164] : undefined;
const allowFrom = combinedAllowFrom.length > 0 ? combinedAllowFrom : defaultAllowFrom;
const groupAllowFrom =
account.groupAllowFrom ??
(configuredAllowFrom && configuredAllowFrom.length > 0
? configuredAllowFrom
: undefined);
(configuredAllowFrom && configuredAllowFrom.length > 0 ? configuredAllowFrom : undefined);
const isSamePhone = params.from === params.selfE164;
const isSelfChat = isSelfChatMode(params.selfE164, configuredAllowFrom);
@@ -84,9 +77,7 @@ export async function checkInboundAccessControl(params: {
}
if (params.group && groupPolicy === "allowlist") {
if (!groupAllowFrom || groupAllowFrom.length === 0) {
logVerbose(
"Blocked group message (groupPolicy: allowlist, no groupAllowFrom)",
);
logVerbose("Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
return {
allowed: false,
shouldMarkRead: false,
@@ -96,8 +87,7 @@ export async function checkInboundAccessControl(params: {
}
const senderAllowed =
groupHasWildcard ||
(params.senderE164 != null &&
normalizedGroupAllowFrom.includes(params.senderE164));
(params.senderE164 != null && normalizedGroupAllowFrom.includes(params.senderE164));
if (!senderAllowed) {
logVerbose(
`Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`,
@@ -135,8 +125,7 @@ export async function checkInboundAccessControl(params: {
const candidate = params.from;
const allowed =
dmHasWildcard ||
(normalizedAllowFrom.length > 0 &&
normalizedAllowFrom.includes(candidate));
(normalizedAllowFrom.length > 0 && normalizedAllowFrom.includes(candidate));
if (!allowed) {
if (dmPolicy === "pairing") {
const { code, created } = await upsertChannelPairingRequest({
@@ -157,15 +146,11 @@ export async function checkInboundAccessControl(params: {
}),
});
} catch (err) {
logVerbose(
`whatsapp pairing reply failed for ${candidate}: ${String(err)}`,
);
logVerbose(`whatsapp pairing reply failed for ${candidate}: ${String(err)}`);
}
}
} else {
logVerbose(
`Blocked unauthorized sender ${candidate} (dmPolicy=${dmPolicy})`,
);
logVerbose(`Blocked unauthorized sender ${candidate} (dmPolicy=${dmPolicy})`);
}
return {
allowed: false,

View File

@@ -4,31 +4,20 @@ import {
getContentType,
normalizeMessageContent,
} from "@whiskeysockets/baileys";
import {
formatLocationText,
type NormalizedLocation,
} from "../../channels/location.js";
import { formatLocationText, type NormalizedLocation } from "../../channels/location.js";
import { logVerbose } from "../../globals.js";
import { jidToE164 } from "../../utils.js";
import { parseVcard } from "../vcard.js";
function unwrapMessage(
message: proto.IMessage | undefined,
): proto.IMessage | undefined {
const normalized = normalizeMessageContent(
message as proto.IMessage | undefined,
);
function unwrapMessage(message: proto.IMessage | undefined): proto.IMessage | undefined {
const normalized = normalizeMessageContent(message as proto.IMessage | undefined);
return normalized as proto.IMessage | undefined;
}
function extractContextInfo(
message: proto.IMessage | undefined,
): proto.IContextInfo | undefined {
function extractContextInfo(message: proto.IMessage | undefined): proto.IContextInfo | undefined {
if (!message) return undefined;
const contentType = getContentType(message);
const candidate = contentType
? (message as Record<string, unknown>)[contentType]
: undefined;
const candidate = contentType ? (message as Record<string, unknown>)[contentType] : undefined;
const contextInfo =
candidate && typeof candidate === "object" && "contextInfo" in candidate
? (candidate as { contextInfo?: proto.IContextInfo }).contextInfo
@@ -51,23 +40,20 @@ function extractContextInfo(
for (const value of Object.values(message)) {
if (!value || typeof value !== "object") continue;
if (!("contextInfo" in value)) continue;
const candidateContext = (value as { contextInfo?: proto.IContextInfo })
.contextInfo;
const candidateContext = (value as { contextInfo?: proto.IContextInfo }).contextInfo;
if (candidateContext) return candidateContext;
}
return undefined;
}
export function extractMentionedJids(
rawMessage: proto.IMessage | undefined,
): string[] | undefined {
export function extractMentionedJids(rawMessage: proto.IMessage | undefined): string[] | undefined {
const message = unwrapMessage(rawMessage);
if (!message) return undefined;
const candidates: Array<string[] | null | undefined> = [
message.extendedTextMessage?.contextInfo?.mentionedJid,
message.extendedTextMessage?.contextInfo?.quotedMessage?.extendedTextMessage
?.contextInfo?.mentionedJid,
message.extendedTextMessage?.contextInfo?.quotedMessage?.extendedTextMessage?.contextInfo
?.mentionedJid,
message.imageMessage?.contextInfo?.mentionedJid,
message.videoMessage?.contextInfo?.mentionedJid,
message.documentMessage?.contextInfo?.mentionedJid,
@@ -82,22 +68,14 @@ export function extractMentionedJids(
return Array.from(new Set(flattened));
}
export function extractText(
rawMessage: proto.IMessage | undefined,
): string | undefined {
export function extractText(rawMessage: proto.IMessage | undefined): string | undefined {
const message = unwrapMessage(rawMessage);
if (!message) return undefined;
const extracted = extractMessageContent(message);
const candidates = [
message,
extracted && extracted !== message ? extracted : undefined,
];
const candidates = [message, extracted && extracted !== message ? extracted : undefined];
for (const candidate of candidates) {
if (!candidate) continue;
if (
typeof candidate.conversation === "string" &&
candidate.conversation.trim()
) {
if (typeof candidate.conversation === "string" && candidate.conversation.trim()) {
return candidate.conversation.trim();
}
const extended = candidate.extendedTextMessage?.text;
@@ -130,9 +108,7 @@ export function extractMediaPlaceholder(
return undefined;
}
function extractContactPlaceholder(
rawMessage: proto.IMessage | undefined,
): string | undefined {
function extractContactPlaceholder(rawMessage: proto.IMessage | undefined): string | undefined {
const message = unwrapMessage(rawMessage);
if (!message) return undefined;
const contact = message.contactMessage ?? undefined;
@@ -146,18 +122,16 @@ function extractContactPlaceholder(
const contactsArray = message.contactsArrayMessage?.contacts ?? undefined;
if (!contactsArray || contactsArray.length === 0) return undefined;
const labels = contactsArray
.map((entry) =>
describeContact({ displayName: entry.displayName, vcard: entry.vcard }),
)
.map((entry) => describeContact({ displayName: entry.displayName, vcard: entry.vcard }))
.map((entry) => formatContactLabel(entry.name, entry.phones))
.filter((value): value is string => Boolean(value));
return formatContactsPlaceholder(labels, contactsArray.length);
}
function describeContact(input: {
displayName?: string | null;
vcard?: string | null;
}): { name?: string; phones: string[] } {
function describeContact(input: { displayName?: string | null; vcard?: string | null }): {
name?: string;
phones: string[];
} {
const displayName = (input.displayName ?? "").trim();
const parsed = parseVcard(input.vcard ?? undefined);
const name = displayName || parsed.name;
@@ -181,14 +155,9 @@ function formatContactsPlaceholder(labels: string[], total: number): string {
return `<contacts: ${cleaned.join(", ")}${suffix}>`;
}
function formatContactLabel(
name?: string,
phones?: string[],
): string | undefined {
function formatContactLabel(name?: string, phones?: string[]): string | undefined {
const phoneLabel = formatPhoneList(phones);
const parts = [name, phoneLabel].filter((value): value is string =>
Boolean(value),
);
const parts = [name, phoneLabel].filter((value): value is string => Boolean(value));
if (parts.length === 0) return undefined;
return parts.join(", ");
}
@@ -255,11 +224,7 @@ export function extractLocationData(
name: location.name ?? undefined,
address: location.address ?? undefined,
caption: location.comment ?? undefined,
source: isLive
? "live"
: location.name || location.address
? "place"
: "pin",
source: isLive ? "live" : location.name || location.address ? "place" : "pin",
isLive,
};
}
@@ -284,24 +249,17 @@ export function describeReplyContext(rawMessage: proto.IMessage | undefined): {
const location = extractLocationData(quoted);
const locationText = location ? formatLocationText(location) : undefined;
const text = extractText(quoted);
let body: string | undefined = [text, locationText]
.filter(Boolean)
.join("\n")
.trim();
let body: string | undefined = [text, locationText].filter(Boolean).join("\n").trim();
if (!body) body = extractMediaPlaceholder(quoted);
if (!body) {
const quotedType = quoted ? getContentType(quoted) : undefined;
logVerbose(
`Quoted message missing extractable body${
quotedType ? ` (type ${quotedType})` : ""
}`,
`Quoted message missing extractable body${quotedType ? ` (type ${quotedType})` : ""}`,
);
return null;
}
const senderJid = contextInfo?.participant ?? undefined;
const senderE164 = senderJid
? (jidToE164(senderJid) ?? senderJid)
: undefined;
const senderE164 = senderJid ? (jidToE164(senderJid) ?? senderJid) : undefined;
const sender = senderE164 ?? "unknown sender";
return {
id: contextInfo?.stanzaId ? String(contextInfo.stanzaId) : undefined,

View File

@@ -1,17 +1,10 @@
import type { proto, WAMessage } from "@whiskeysockets/baileys";
import {
downloadMediaMessage,
normalizeMessageContent,
} from "@whiskeysockets/baileys";
import { downloadMediaMessage, normalizeMessageContent } from "@whiskeysockets/baileys";
import { logVerbose } from "../../globals.js";
import type { createWaSocket } from "../session.js";
function unwrapMessage(
message: proto.IMessage | undefined,
): proto.IMessage | undefined {
const normalized = normalizeMessageContent(
message as proto.IMessage | undefined,
);
function unwrapMessage(message: proto.IMessage | undefined): proto.IMessage | undefined {
const normalized = normalizeMessageContent(message as proto.IMessage | undefined);
return normalized as proto.IMessage | undefined;
}

View File

@@ -1,8 +1,4 @@
import type {
AnyMessageContent,
proto,
WAMessage,
} from "@whiskeysockets/baileys";
import type { AnyMessageContent, proto, WAMessage } from "@whiskeysockets/baileys";
import { DisconnectReason, isJidGroup } from "@whiskeysockets/baileys";
import { formatLocationText } from "../../channels/location.js";
import { logVerbose, shouldLogVerbose } from "../../globals.js";
@@ -10,11 +6,7 @@ import { recordChannelActivity } from "../../infra/channel-activity.js";
import { createSubsystemLogger, getChildLogger } from "../../logging.js";
import { saveMediaBuffer } from "../../media/store.js";
import { jidToE164, resolveJidToE164 } from "../../utils.js";
import {
createWaSocket,
getStatusCode,
waitForWaConnection,
} from "../session.js";
import { createWaSocket, getStatusCode, waitForWaConnection } from "../session.js";
import { checkInboundAccessControl } from "./access-control.js";
import { isRecentInboundMessage } from "./dedupe.js";
import {
@@ -36,9 +28,7 @@ export async function monitorWebInbox(options: {
mediaMaxMb?: number;
}) {
const inboundLogger = getChildLogger({ module: "web-inbound" });
const inboundConsoleLog = createSubsystemLogger(
"gateway/channels/whatsapp",
).child("inbound");
const inboundConsoleLog = createSubsystemLogger("gateway/channels/whatsapp").child("inbound");
const sock = await createWaSocket(false, options.verbose, {
authDir: options.authDir,
});
@@ -57,12 +47,9 @@ export async function monitorWebInbox(options: {
try {
await sock.sendPresenceUpdate("available");
if (shouldLogVerbose())
logVerbose("Sent global 'available' presence on connect");
if (shouldLogVerbose()) logVerbose("Sent global 'available' presence on connect");
} catch (err) {
logVerbose(
`Failed to send 'available' presence on connect: ${String(err)}`,
);
logVerbose(`Failed to send 'available' presence on connect: ${String(err)}`);
}
const selfJid = sock.user?.id;
@@ -74,9 +61,7 @@ export async function monitorWebInbox(options: {
const GROUP_META_TTL_MS = 5 * 60 * 1000; // 5 minutes
const lidLookup = sock.signalRepository?.lidMapping;
const resolveInboundJid = async (
jid: string | null | undefined,
): Promise<string | null> =>
const resolveInboundJid = async (jid: string | null | undefined): Promise<string | null> =>
resolveJidToE164(jid, { authDir: options.authDir, lidLookup });
const getGroupMeta = async (jid: string) => {
@@ -106,10 +91,7 @@ export async function monitorWebInbox(options: {
}
};
const handleMessagesUpsert = async (upsert: {
type?: string;
messages?: Array<WAMessage>;
}) => {
const handleMessagesUpsert = async (upsert: { type?: string; messages?: Array<WAMessage> }) => {
if (upsert.type !== "notify" && upsert.type !== "append") return;
for (const msg of upsert.messages ?? []) {
recordChannelActivity({
@@ -120,8 +102,7 @@ export async function monitorWebInbox(options: {
const id = msg.key?.id ?? undefined;
const remoteJid = msg.key?.remoteJid;
if (!remoteJid) continue;
if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast"))
continue;
if (remoteJid.endsWith("@status") || remoteJid.endsWith("@broadcast")) continue;
const group = isJidGroup(remoteJid) === true;
if (id) {
@@ -161,14 +142,10 @@ export async function monitorWebInbox(options: {
if (id && !access.isSelfChat) {
const participant = msg.key?.participant;
try {
await sock.readMessages([
{ remoteJid, id, participant, fromMe: false },
]);
await sock.readMessages([{ remoteJid, id, participant, fromMe: false }]);
if (shouldLogVerbose()) {
const suffix = participant ? ` (participant ${participant})` : "";
logVerbose(
`Marked message ${id} as read for ${remoteJid}${suffix}`,
);
logVerbose(`Marked message ${id} as read for ${remoteJid}${suffix}`);
}
} catch (err) {
logVerbose(`Failed to mark message ${id} read: ${String(err)}`);
@@ -191,17 +168,12 @@ export async function monitorWebInbox(options: {
body = extractMediaPlaceholder(msg.message ?? undefined);
if (!body) continue;
}
const replyContext = describeReplyContext(
msg.message as proto.IMessage | undefined,
);
const replyContext = describeReplyContext(msg.message as proto.IMessage | undefined);
let mediaPath: string | undefined;
let mediaType: string | undefined;
try {
const inboundMedia = await downloadInboundMedia(
msg as proto.IWebMessageInfo,
sock,
);
const inboundMedia = await downloadInboundMedia(msg as proto.IWebMessageInfo, sock);
if (inboundMedia) {
const maxMb =
typeof options.mediaMaxMb === "number" && options.mediaMaxMb > 0
@@ -235,12 +207,8 @@ export async function monitorWebInbox(options: {
const sendMedia = async (payload: AnyMessageContent) => {
await sock.sendMessage(chatJid, payload);
};
const timestamp = msg.messageTimestamp
? Number(msg.messageTimestamp) * 1000
: undefined;
const mentionedJids = extractMentionedJids(
msg.message as proto.IMessage | undefined,
);
const timestamp = msg.messageTimestamp ? Number(msg.messageTimestamp) * 1000 : undefined;
const mentionedJids = extractMentionedJids(msg.message as proto.IMessage | undefined);
const senderName = msg.pushName ?? undefined;
inboundLogger.info(
@@ -280,22 +248,12 @@ export async function monitorWebInbox(options: {
}),
);
void task.catch((err) => {
inboundLogger.error(
{ error: String(err) },
"failed handling inbound web message",
);
inboundConsoleLog.error(
`Failed handling inbound web message: ${String(err)}`,
);
inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`);
});
} catch (err) {
inboundLogger.error(
{ error: String(err) },
"failed handling inbound web message",
);
inboundConsoleLog.error(
`Failed handling inbound web message: ${String(err)}`,
);
inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`);
}
}
};
@@ -314,10 +272,7 @@ export async function monitorWebInbox(options: {
});
}
} catch (err) {
inboundLogger.error(
{ error: String(err) },
"connection.update handler error",
);
inboundLogger.error({ error: String(err) }, "connection.update handler error");
resolveClose({ status: undefined, isLoggedOut: false, error: err });
}
};
@@ -325,10 +280,8 @@ export async function monitorWebInbox(options: {
const sendApi = createWebSendApi({
sock: {
sendMessage: (jid: string, content: AnyMessageContent) =>
sock.sendMessage(jid, content),
sendPresenceUpdate: (presence, jid?: string) =>
sock.sendPresenceUpdate(presence, jid),
sendMessage: (jid: string, content: AnyMessageContent) => sock.sendMessage(jid, content),
sendPresenceUpdate: (presence, jid?: string) => sock.sendPresenceUpdate(presence, jid),
},
defaultAccountId: options.accountId,
});
@@ -338,10 +291,7 @@ export async function monitorWebInbox(options: {
try {
const ev = sock.ev as unknown as {
off?: (event: string, listener: (...args: unknown[]) => void) => void;
removeListener?: (
event: string,
listener: (...args: unknown[]) => void,
) => void;
removeListener?: (event: string, listener: (...args: unknown[]) => void) => void;
};
const messagesUpsertHandler = handleMessagesUpsert as unknown as (
...args: unknown[]
@@ -363,9 +313,7 @@ export async function monitorWebInbox(options: {
},
onClose,
signalClose: (reason?: WebListenerCloseReason) => {
resolveClose(
reason ?? { status: undefined, isLoggedOut: false, error: "closed" },
);
resolveClose(reason ?? { status: undefined, isLoggedOut: false, error: "closed" });
},
// IPC surface (sendMessage/sendPoll/sendReaction/sendComposingTo)
...sendApi,

View File

@@ -6,10 +6,7 @@ import type { ActiveWebSendOptions } from "../active-listener.js";
export function createWebSendApi(params: {
sock: {
sendMessage: (jid: string, content: AnyMessageContent) => Promise<unknown>;
sendPresenceUpdate: (
presence: WAPresence,
jid?: string,
) => Promise<unknown>;
sendPresenceUpdate: (presence: WAPresence, jid?: string) => Promise<unknown>;
};
defaultAccountId: string;
}) {