chore: apply extension lint cleanups

This commit is contained in:
Peter Steinberger
2026-04-23 05:28:17 +01:00
parent 596b88986d
commit 0b0662b1c9
51 changed files with 141 additions and 155 deletions

View File

@@ -178,7 +178,7 @@ async function queryBlueBubblesChats(params: {
return [];
}
const payload = (await res.json().catch(() => null)) as Record<string, unknown> | null;
const data = payload && typeof payload.data !== "undefined" ? (payload.data as unknown) : null;
const data = payload && payload.data !== undefined ? (payload.data as unknown) : null;
return Array.isArray(data) ? (data as BlueBubblesChatRecord[]) : [];
}

View File

@@ -226,7 +226,7 @@ async function queryChats(params: {
return [];
}
const payload = (await res.json().catch(() => null)) as Record<string, unknown> | null;
const data = payload && typeof payload.data !== "undefined" ? (payload.data as unknown) : null;
const data = payload && payload.data !== undefined ? (payload.data as unknown) : null;
return Array.isArray(data) ? (data as BlueBubblesChatRecord[]) : [];
}

View File

@@ -189,7 +189,7 @@ function readNativeCompactionCompletion(
function resolveCompactionWaitTimeoutMs(): number {
const raw = process.env.OPENCLAW_CODEX_COMPACTION_WAIT_TIMEOUT_MS?.trim();
const parsed = raw ? Number.parseInt(raw, 10) : NaN;
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
if (Number.isFinite(parsed) && parsed > 0) {
return parsed;
}

View File

@@ -68,8 +68,8 @@ export async function getAudioDuration(filePath: string): Promise<number> {
"csv=p=0",
filePath,
]);
const duration = parseFloat(stdout.trim());
if (isNaN(duration)) {
const duration = Number.parseFloat(stdout.trim());
if (Number.isNaN(duration)) {
throw new Error("Could not parse duration");
}
return Math.round(duration * 100) / 100; // Round to 2 decimal places

View File

@@ -2,8 +2,8 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
const DECRYPT_FAILURE_WINDOW_MS = 30_000;
const DECRYPT_FAILURE_RECONNECT_THRESHOLD = 3;
const DECRYPT_FAILURE_PATTERN = /DecryptionFailed\(/;
const DAVE_PASSTHROUGH_DISABLED_PATTERN = /UnencryptedWhenPassthroughDisabled/;
const DECRYPT_FAILURE_MARKER = "DecryptionFailed(";
const DAVE_PASSTHROUGH_DISABLED_MARKER = "UnencryptedWhenPassthroughDisabled";
export const DAVE_RECEIVE_PASSTHROUGH_INITIAL_EXPIRY_SECONDS = 30;
export const DAVE_RECEIVE_PASSTHROUGH_REARM_EXPIRY_SECONDS = 15;
@@ -80,12 +80,12 @@ export function isAbortLikeReceiveError(err: unknown): boolean {
export function analyzeVoiceReceiveError(err: unknown): VoiceReceiveErrorAnalysis {
const message = formatErrorMessage(err);
const shouldAttemptPassthrough = DAVE_PASSTHROUGH_DISABLED_PATTERN.test(message);
const shouldAttemptPassthrough = message.includes(DAVE_PASSTHROUGH_DISABLED_MARKER);
return {
message,
isAbortLike: isAbortLikeReceiveError(err),
shouldAttemptPassthrough,
countsAsDecryptFailure: DECRYPT_FAILURE_PATTERN.test(message) || shouldAttemptPassthrough,
countsAsDecryptFailure: message.includes(DECRYPT_FAILURE_MARKER) || shouldAttemptPassthrough,
};
}

View File

@@ -49,7 +49,7 @@ function decodeHtmlEntities(text: string): string {
.replace(/&mdash;/g, "--")
.replace(/&hellip;/g, "...")
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
.replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCodePoint(parseInt(code, 16)));
.replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCodePoint(Number.parseInt(code, 16)));
}
function stripHtml(html: string): string {

View File

@@ -38,9 +38,7 @@ type FeishuMessageLike = {
export type GroupSessionScope = "group" | "group_sender" | "group_topic" | "group_topic_sender";
type FeishuLogger = {
(...args: unknown[]): void;
};
type FeishuLogger = (...args: unknown[]) => void;
export type ResolvedFeishuGroupSession = {
peerId: string;
@@ -215,7 +213,7 @@ export function parseMergeForwardContent(params: { content: string; log?: Feishu
log?.(`feishu: merge_forward contains ${subMessages.length} sub-messages`);
subMessages.sort(
(a, b) => parseInt(a.create_time || "0", 10) - parseInt(b.create_time || "0", 10),
(a, b) => Number.parseInt(a.create_time || "0", 10) - Number.parseInt(b.create_time || "0", 10),
);
const lines = ["[Merged and Forwarded Messages]"];

View File

@@ -17,9 +17,7 @@ type FeishuContactUserGetResponse = Awaited<
ReturnType<ReturnType<typeof createFeishuClient>["contact"]["user"]["get"]>
>;
type FeishuLogger = {
(...args: unknown[]): void;
};
type FeishuLogger = (...args: unknown[]) => void;
const IGNORED_PERMISSION_SCOPE_TOKENS = ["contact:contact.base:readonly"];
const FEISHU_SCOPE_CORRECTIONS: Record<string, string> = {

View File

@@ -409,7 +409,7 @@ export async function handleFeishuMessage(params: {
// instead of the delivery/processing time. Feishu uses a millisecond
// epoch string; fall back to Date.now() only when the field is absent.
const messageCreateTimeMs = event.message.create_time
? parseInt(event.message.create_time, 10)
? Number.parseInt(event.message.create_time, 10)
: Date.now();
let requireMention = false; // DMs never require mention; groups may override below

View File

@@ -1,6 +1,6 @@
import fs from "fs";
import path from "path";
import { Readable } from "stream";
import fs from "node:fs";
import path from "node:path";
import { Readable } from "node:stream";
import type * as Lark from "@larksuiteoapi/node-sdk";
import { mediaKindFromMime } from "openclaw/plugin-sdk/media-runtime";
import { withTempDownloadPath } from "openclaw/plugin-sdk/temp-path";
@@ -627,22 +627,21 @@ export async function sendMediaFeishu(params: {
if (routing.msgType === "image") {
const { imageKey } = await uploadImageFeishu({ cfg, image: buffer, accountId });
return sendImageFeishu({ cfg, to, imageKey, replyToMessageId, replyInThread, accountId });
} else {
const { fileKey } = await uploadFileFeishu({
cfg,
file: buffer,
fileName: name,
fileType: routing.fileType ?? "stream",
accountId,
});
return sendFileFeishu({
cfg,
to,
fileKey,
msgType: routing.msgType,
replyToMessageId,
replyInThread,
accountId,
});
}
const { fileKey } = await uploadFileFeishu({
cfg,
file: buffer,
fileName: name,
fileType: routing.fileType ?? "stream",
accountId,
});
return sendFileFeishu({
cfg,
to,
fileKey,
msgType: routing.msgType,
replyToMessageId,
replyInThread,
accountId,
});
}

View File

@@ -52,11 +52,10 @@ export function isMentionForwardRequest(event: FeishuMessageEvent, botOpenId?: s
if (isDirectMessage) {
// DM: trigger if any non-bot user is mentioned
return hasOtherMention;
} else {
// Group: need to mention both bot and other users
const hasBotMention = mentions.some((m) => m.id.open_id === botOpenId);
return hasBotMention && hasOtherMention;
}
// Group: need to mention both bot and other users
const hasBotMention = mentions.some((m) => m.id.open_id === botOpenId);
return hasBotMention && hasOtherMention;
}
/**

View File

@@ -1,4 +1,4 @@
import * as crypto from "crypto";
import * as crypto from "node:crypto";
import type * as Lark from "@larksuiteoapi/node-sdk";
import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "../runtime-api.js";
import { resolveFeishuAccount } from "./accounts.js";

View File

@@ -1,4 +1,4 @@
import * as http from "http";
import * as http from "node:http";
import type * as Lark from "@larksuiteoapi/node-sdk";
import {
createFixedWindowRateLimiter,

View File

@@ -1,5 +1,5 @@
import * as http from "http";
import crypto from "node:crypto";
import * as http from "node:http";
import * as Lark from "@larksuiteoapi/node-sdk";
import { createFeishuWSClient } from "./client.js";
import {

View File

@@ -1,5 +1,5 @@
import fs from "fs";
import path from "path";
import fs from "node:fs";
import path from "node:path";
import { createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { resolveFeishuAccount } from "./accounts.js";

View File

@@ -276,7 +276,7 @@ function parseFeishuMessageItem(
senderType: item.sender?.sender_type,
content: parseFeishuMessageContent(rawContent, msgType),
contentType: msgType,
createTime: item.create_time ? parseInt(item.create_time, 10) : undefined,
createTime: item.create_time ? Number.parseInt(item.create_time, 10) : undefined,
threadId: item.thread_id || undefined,
};
}

View File

@@ -281,7 +281,7 @@ async function shouldProcessLineEvent(
logVerbose(`Blocked line group ${groupId ?? roomId ?? "unknown"} (group disabled)`);
return denied;
}
if (typeof groupAllowOverride !== "undefined") {
if (groupAllowOverride !== undefined) {
if (!senderId) {
logVerbose("Blocked line group message (group allowFrom override, no sender ID)");
return denied;

View File

@@ -64,9 +64,9 @@ export function parseLineDirectives(payload: ReplyPayload): ReplyPayload {
const parts = locationMatch[1].split("|").map((s) => s.trim());
if (parts.length >= 4) {
const [title, address, latStr, lonStr] = parts;
const latitude = parseFloat(latStr);
const longitude = parseFloat(lonStr);
if (!isNaN(latitude) && !isNaN(longitude)) {
const latitude = Number.parseFloat(latStr);
const longitude = Number.parseFloat(lonStr);
if (!Number.isNaN(latitude) && !Number.isNaN(longitude)) {
lineData.location = {
title: title || "Location",
address: address || "",

View File

@@ -85,7 +85,7 @@ export function moveSingleMatrixAccountConfigToNamedAccount(cfg: CoreConfig): Co
typeof base.accounts === "object" && base.accounts
? (base.accounts as Record<string, Record<string, unknown>>)
: {};
const hasNamedAccounts = Object.keys(accounts).filter(Boolean).length > 0;
const hasNamedAccounts = Object.keys(accounts).some(Boolean);
const keysToMove = Object.entries(base)
.filter(([key, value]) => {
if (key === "accounts" || key === "enabled" || value === undefined) {

View File

@@ -377,7 +377,7 @@ function isRetryableError(error: Error): boolean {
if (!clientErrorMatch) {
continue;
}
const statusCode = parseInt(clientErrorMatch[1], 10);
const statusCode = Number.parseInt(clientErrorMatch[1], 10);
if (statusCode >= 400 && statusCode < 500) {
return false;
}

View File

@@ -129,7 +129,7 @@ export const memoryConfigSchema = {
}
const dreaming =
typeof cfg.dreaming === "undefined"
cfg.dreaming === undefined
? undefined
: cfg.dreaming && typeof cfg.dreaming === "object" && !Array.isArray(cfg.dreaming)
? (cfg.dreaming as Record<string, unknown>)

View File

@@ -551,7 +551,7 @@ export default definePluginEntry({
.option("--limit <n>", "Max results", "5")
.action(async (query, opts) => {
const vector = await embeddings.embed(query);
const results = await db.search(vector, parseInt(opts.limit), 0.3);
const results = await db.search(vector, Number.parseInt(opts.limit, 10), 0.3);
// Strip vectors for output
const output = results.map((r) => ({
id: r.entry.id,

View File

@@ -299,10 +299,10 @@ describe("Metrics fuzz", () => {
describe("extreme values", () => {
it("handles NaN value", () => {
const metrics = createPlainMetrics();
expect(() => metrics.emit("event.received", NaN)).not.toThrow();
expect(() => metrics.emit("event.received", Number.NaN)).not.toThrow();
const snapshot = metrics.getSnapshot();
expect(isNaN(snapshot.eventsReceived)).toBe(true);
expect(Number.isNaN(snapshot.eventsReceived)).toBe(true);
});
it("handles Infinity value", () => {

View File

@@ -23,7 +23,7 @@ export function validatePrivateKey(key: string): Uint8Array {
// Convert hex string to Uint8Array
const bytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
bytes[i] = parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
bytes[i] = Number.parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
}
return bytes;
}

View File

@@ -13,7 +13,7 @@ export const TEST_SETUP_RELAY_URLS = ["wss://relay.damus.io", "wss://relay.prima
export const TEST_RESOLVED_PRIVATE_KEY = "resolved-nostr-private-key";
export const TEST_HEX_PRIVATE_KEY_BYTES = new Uint8Array(
TEST_HEX_PRIVATE_KEY.match(/.{2}/g)!.map((byte) => parseInt(byte, 16)),
TEST_HEX_PRIVATE_KEY.match(/.{2}/g)!.map((byte) => Number.parseInt(byte, 16)),
);
export function createConfiguredNostrCfg(overrides: Record<string, unknown> = {}): {

View File

@@ -1531,7 +1531,7 @@ export async function createQaLabApp(root: HTMLDivElement) {
const currentIndex = markers.findIndex(
(node) => (node.dataset.captureEvent ?? null) === state.selectedCaptureEventKey,
);
let nextIndex = currentIndex >= 0 ? currentIndex : 0;
let nextIndex = Math.max(currentIndex, 0);
if (event.key === "Home") {
nextIndex = 0;
} else if (event.key === "End") {

View File

@@ -94,7 +94,7 @@ registerCommand({
handler: (ctx) => {
const now = Date.now();
const eventTime = new Date(ctx.eventTimestamp).getTime();
if (isNaN(eventTime)) {
if (Number.isNaN(eventTime)) {
return `✅ pong!`;
}
const totalMs = now - eventTime;

View File

@@ -153,7 +153,8 @@ export async function processAttachments(
if (att.content_type?.startsWith("image/")) {
log?.debug?.(`Downloaded attachment to: ${localPath}`);
return { localPath, type: "image" as const, contentType: att.content_type, meta };
} else if (isVoice) {
}
if (isVoice) {
log?.debug?.(`Downloaded attachment to: ${localPath}`);
return processVoiceAttachment(
localPath,
@@ -164,37 +165,35 @@ export async function processAttachments(
downloadDir,
log,
);
} else {
log?.debug?.(`Downloaded attachment to: ${localPath}`);
return { localPath, type: "other" as const, filename: att.filename, meta };
}
} else {
log?.error(`Failed to download: ${attUrl}`);
if (att.content_type?.startsWith("image/")) {
return {
localPath: null,
type: "image-fallback" as const,
attUrl,
contentType: att.content_type,
meta,
};
} else if (isVoice && asrReferText) {
log?.info(`Voice attachment download failed, using asr_refer_text fallback`);
return {
localPath: null,
type: "voice-fallback" as const,
transcript: asrReferText,
meta,
};
} else {
return {
localPath: null,
type: "other-fallback" as const,
filename: att.filename ?? att.content_type,
meta,
};
}
log?.debug?.(`Downloaded attachment to: ${localPath}`);
return { localPath, type: "other" as const, filename: att.filename, meta };
}
log?.error(`Failed to download: ${attUrl}`);
if (att.content_type?.startsWith("image/")) {
return {
localPath: null,
type: "image-fallback" as const,
attUrl,
contentType: att.content_type,
meta,
};
}
if (isVoice && asrReferText) {
log?.info(`Voice attachment download failed, using asr_refer_text fallback`);
return {
localPath: null,
type: "voice-fallback" as const,
transcript: asrReferText,
meta,
};
}
return {
localPath: null,
type: "other-fallback" as const,
filename: att.filename ?? att.content_type,
meta,
};
},
);

View File

@@ -55,7 +55,7 @@ export function decodeMediaPath(raw: string, log?: EngineLogger): string {
if (!isWinLocal && (hasOctal || hasNonASCII)) {
log?.debug?.(`Decoding path with mixed encoding: ${mediaPath}`);
const decoded = mediaPath.replace(/\\([0-7]{1,3})/g, (_: string, octal: string) => {
return String.fromCharCode(parseInt(octal, 8));
return String.fromCharCode(Number.parseInt(octal, 8));
});
const bytes: number[] = [];
for (let i = 0; i < decoded.length; i++) {

View File

@@ -1,5 +1,5 @@
import * as fs from "node:fs";
import * as path from "path";
import * as path from "node:path";
import { formatErrorMessage } from "../utils/format.js";
import {
normalizeLowercaseStringOrEmpty,
@@ -401,16 +401,15 @@ export async function sendPhoto(
localPath,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
if (isHttp) {
const r = await senderSendText(target, `![](${mediaPath})`, creds, {
msgId: ctx.replyToId,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
}
debugLog(`sendPhoto: channel does not support local/Base64 images`);
return { channel: "qqbot", error: "Channel does not support local/Base64 images" };
}
if (isHttp) {
const r = await senderSendText(target, `![](${mediaPath})`, creds, {
msgId: ctx.replyToId,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
}
debugLog(`sendPhoto: channel does not support local/Base64 images`);
return { channel: "qqbot", error: "Channel does not support local/Base64 images" };
} catch (err) {
const msg = formatErrorMessage(err);
@@ -482,10 +481,9 @@ export async function sendVoice(
msgId: ctx.replyToId,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendVoice: voice not supported in channel`);
return { channel: "qqbot", error: "Voice not supported in channel" };
}
debugLog(`sendVoice: voice not supported in channel`);
return { channel: "qqbot", error: "Voice not supported in channel" };
} catch (err) {
const msg = formatErrorMessage(err);
debugWarn(
@@ -561,10 +559,9 @@ async function sendVoiceFromLocal(
filePath: safeMediaPath,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendVoice: voice not supported in channel`);
return { channel: "qqbot", error: "Voice not supported in channel" };
}
debugLog(`sendVoice: voice not supported in channel`);
return { channel: "qqbot", error: "Voice not supported in channel" };
} catch (err) {
const msg = formatErrorMessage(err);
debugError(`sendVoice (local) failed: ${msg}`);
@@ -603,10 +600,9 @@ export async function sendVideoMsg(
msgId: ctx.replyToId,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendVideoMsg: video not supported in channel`);
return { channel: "qqbot", error: "Video not supported in channel" };
}
debugLog(`sendVideoMsg: video not supported in channel`);
return { channel: "qqbot", error: "Video not supported in channel" };
}
return await sendVideoFromLocal(ctx, mediaPath);
@@ -656,10 +652,9 @@ async function sendVideoFromLocal(
localPath: mediaPath,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendVideoMsg: video not supported in channel`);
return { channel: "qqbot", error: "Video not supported in channel" };
}
debugLog(`sendVideoMsg: video not supported in channel`);
return { channel: "qqbot", error: "Video not supported in channel" };
} catch (err) {
const msg = formatErrorMessage(err);
debugError(`sendVideoMsg (local) failed: ${msg}`);
@@ -706,10 +701,9 @@ export async function sendDocument(
fileName,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendDocument: file not supported in channel`);
return { channel: "qqbot", error: "File not supported in channel" };
}
debugLog(`sendDocument: file not supported in channel`);
return { channel: "qqbot", error: "File not supported in channel" };
}
return await sendDocumentFromLocal(ctx, mediaPath);
@@ -764,10 +758,9 @@ async function sendDocumentFromLocal(
localFilePath: mediaPath,
});
return { channel: "qqbot", messageId: r.id, timestamp: r.timestamp };
} else {
debugLog(`sendDocument: file not supported in channel`);
return { channel: "qqbot", error: "File not supported in channel" };
}
debugLog(`sendDocument: file not supported in channel`);
return { channel: "qqbot", error: "File not supported in channel" };
} catch (err) {
const msg = formatErrorMessage(err);
debugError(`sendDocument (local) failed: ${msg}`);
@@ -891,7 +884,7 @@ export async function sendText(ctx: OutboundContext): Promise<OutboundResult> {
debugLog(`[qqbot] sendText: Decoding path with mixed encoding: ${mediaPath}`);
let decoded = mediaPath.replace(/\\([0-7]{1,3})/g, (_: string, octal: string) => {
return String.fromCharCode(parseInt(octal, 8));
return String.fromCharCode(Number.parseInt(octal, 8));
});
const bytes: number[] = [];

View File

@@ -94,7 +94,7 @@ export const RemindSchema = {
export function parseRelativeTime(timeStr: string): number | null {
const s = timeStr.toLowerCase();
if (/^\d+$/.test(s)) {
return parseInt(s, 10) * 60_000;
return Number.parseInt(s, 10) * 60_000;
}
let totalMs = 0;
@@ -103,7 +103,7 @@ export function parseRelativeTime(timeStr: string): number | null {
let match: RegExpExecArray | null;
while ((match = regex.exec(s)) !== null) {
matched = true;
const value = parseFloat(match[1]);
const value = Number.parseFloat(match[1]);
const unit = match[2];
switch (unit) {
case "d":

View File

@@ -1,4 +1,4 @@
import { Buffer } from "buffer";
import { Buffer } from "node:buffer";
import { beforeEach, describe, expect, it, vi } from "vitest";
const adapterMocks = vi.hoisted(() => ({

View File

@@ -4,7 +4,7 @@
* QQ Bot markdown images use `![#widthpx #heightpx](url)`.
*/
import { Buffer } from "buffer";
import { Buffer } from "node:buffer";
import { getPlatformAdapter } from "../adapter/index.js";
import type { SsrfPolicyConfig } from "../adapter/types.js";
import { formatErrorMessage } from "./format.js";
@@ -252,7 +252,7 @@ export function hasQQBotImageSize(markdownImage: string): boolean {
export function extractQQBotImageSize(markdownImage: string): ImageSize | null {
const match = markdownImage.match(/!\[#(\d+)px\s+#(\d+)px\]/);
if (match) {
return { width: parseInt(match[1], 10), height: parseInt(match[2], 10) };
return { width: Number.parseInt(match[1], 10), height: Number.parseInt(match[2], 10) };
}
return null;
}

View File

@@ -146,7 +146,7 @@ export const signalMessageActions: ChannelMessageActionAdapter = {
const emoji = readStringParam(params, "emoji", { allowEmpty: true });
const remove = typeof params.remove === "boolean" ? params.remove : undefined;
const timestamp = parseInt(messageId, 10);
const timestamp = Number.parseInt(messageId, 10);
if (!Number.isFinite(timestamp)) {
throw new Error(`Invalid messageId: ${messageId}. Expected numeric timestamp.`);
}

View File

@@ -33,7 +33,7 @@ export type SlackChannelConfigEntries = Record<string, SlackChannelConfigEntry>;
function firstDefined<T>(...values: Array<T | undefined>) {
for (const value of values) {
if (typeof value !== "undefined") {
if (value !== undefined) {
return value;
}
}

View File

@@ -281,7 +281,7 @@ function parseNumericUserId(userId?: string | number): number | undefined {
if (userId === undefined) {
return undefined;
}
const numericId = typeof userId === "number" ? userId : parseInt(userId, 10);
const numericId = typeof userId === "number" ? userId : Number.parseInt(userId, 10);
return Number.isNaN(numericId) ? undefined : numericId;
}

View File

@@ -262,7 +262,7 @@ export const buildTelegramMessageContext = async ({
});
// Group sender checks are explicit and must not inherit DM pairing-store entries.
const effectiveGroupAllow = normalizeAllowFrom(groupAllowOverride ?? groupAllowFrom);
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
const hasGroupAllowOverride = groupAllowOverride !== undefined;
const senderUsername = msg.from?.username ?? "";
const baseAccess = evaluateTelegramGroupBaseAccess({
isGroup,

View File

@@ -52,7 +52,7 @@ export const buildTelegramUpdateKey = (ctx: TelegramUpdateKeyContext) => {
ctx.callbackQuery?.message;
const chatId = msg?.chat?.id;
const messageId = msg?.message_id;
if (typeof chatId !== "undefined" && typeof messageId === "number") {
if (chatId !== undefined && typeof messageId === "number") {
return `message:${chatId}:${messageId}`;
}
return undefined;

View File

@@ -47,7 +47,7 @@ import {
type DeliveryProgress as ReplyThreadDeliveryProgress,
} from "./reply-threading.js";
const VOICE_FORBIDDEN_RE = /VOICE_MESSAGES_FORBIDDEN/;
const VOICE_FORBIDDEN_MARKER = "VOICE_MESSAGES_FORBIDDEN";
const CAPTION_TOO_LONG_RE = /caption is too long/i;
const GrammyErrorCtor: typeof GrammyError | undefined =
typeof GrammyError === "function" ? GrammyError : undefined;
@@ -193,9 +193,9 @@ async function sendPendingFollowUpText(params: {
function isVoiceMessagesForbidden(err: unknown): boolean {
if (GrammyErrorCtor && err instanceof GrammyErrorCtor) {
return VOICE_FORBIDDEN_RE.test(err.description);
return err.description.includes(VOICE_FORBIDDEN_MARKER);
}
return VOICE_FORBIDDEN_RE.test(formatErrorMessage(err));
return formatErrorMessage(err).includes(VOICE_FORBIDDEN_MARKER);
}
function isCaptionTooLong(err: unknown): boolean {

View File

@@ -144,7 +144,7 @@ export async function resolveTelegramGroupAllowFromContext(params: {
// Group sender access must remain explicit (groupAllowFrom/per-group allowFrom only).
// DM pairing store entries are not a group authorization source.
const effectiveGroupAllow = normalizeAllowFrom(groupAllowOverride ?? params.groupAllowFrom);
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
const hasGroupAllowOverride = groupAllowOverride !== undefined;
return {
resolvedThreadId,
dmThreadId,

View File

@@ -165,7 +165,7 @@ export function extractTelegramAllowedEmojiReactions(
return undefined;
}
const availableReactions = chat.available_reactions;
if (typeof availableReactions === "undefined") {
if (availableReactions === undefined) {
return undefined;
}
if (availableReactions == null) {

View File

@@ -65,7 +65,7 @@ function truncate(text: string, maxLength: number): string {
if (text.length <= maxLength) {
return text;
}
return text.substring(0, maxLength - 3) + "...";
return text.slice(0, maxLength - 3) + "...";
}
/**

View File

@@ -788,7 +788,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
type: "channel",
requestingShip: senderShip,
channelNest: nest,
messagePreview: rawText.substring(0, 100),
messagePreview: rawText.slice(0, 100),
originalMessage: {
messageId: messageId ?? "",
messageText: rawText,
@@ -991,7 +991,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
const approval = createPendingApproval({
type: "dm",
requestingShip: senderShip,
messagePreview: messageText.substring(0, 100),
messagePreview: messageText.slice(0, 100),
originalMessage: {
messageId: messageId ?? "",
messageText,

View File

@@ -163,7 +163,7 @@ function assertSafeUploadResultUrl(rawUrl: string, label: string): string {
}
function prefixEndpoint(endpoint: string): string {
return endpoint.match(/https?:\/\//) ? endpoint : `https://${endpoint}`;
return /https?:\/\//.test(endpoint) ? endpoint : `https://${endpoint}`;
}
function sanitizeFileName(fileName: string): string {

View File

@@ -222,8 +222,8 @@ export class UrbitSSEClient {
buffer += chunk.toString();
let eventEnd;
while ((eventEnd = buffer.indexOf("\n\n")) !== -1) {
const eventData = buffer.substring(0, eventEnd);
buffer = buffer.substring(eventEnd + 2);
const eventData = buffer.slice(0, eventEnd);
buffer = buffer.slice(eventEnd + 2);
this.processEvent(eventData);
}
}
@@ -249,10 +249,10 @@ export class UrbitSSEClient {
for (const line of lines) {
if (line.startsWith("id: ")) {
eventId = parseInt(line.substring(4), 10);
eventId = Number.parseInt(line.slice(4), 10);
}
if (line.startsWith("data: ")) {
data = line.substring(6);
data = line.slice(6);
}
}
@@ -261,7 +261,7 @@ export class UrbitSSEClient {
}
// Track event ID and send ack if needed
if (eventId !== null && !isNaN(eventId)) {
if (eventId !== null && !Number.isNaN(eventId)) {
if (eventId > this.lastHeardEventId) {
this.lastHeardEventId = eventId;
if (eventId - this.lastAcknowledgedEventId > this.ackThreshold) {

View File

@@ -317,7 +317,7 @@ export class TwilioProvider implements VoiceCallProvider {
type: "call.speech",
transcript: speechResult,
isFinal: true,
confidence: parseFloat(params.get("Confidence") || "0.9"),
confidence: Number.parseFloat(params.get("Confidence") || "0.9"),
};
}

View File

@@ -190,7 +190,7 @@ function extractHostname(hostHeader: string): string | null {
if (endBracket === -1) {
return null; // Malformed IPv6
}
hostname = hostHeader.substring(1, endBracket);
hostname = hostHeader.slice(1, endBracket);
return normalizeLowercaseStringOrEmpty(hostname);
}
@@ -520,7 +520,7 @@ export function verifyTelnyxWebhook(
return { ok: false, reason: "Missing signature or timestamp header" };
}
const eventTimeSec = parseInt(timestamp, 10);
const eventTimeSec = Number.parseInt(timestamp, 10);
if (!Number.isFinite(eventTimeSec)) {
return { ok: false, reason: "Invalid timestamp header" };
}

View File

@@ -47,7 +47,7 @@ export function createEchoTracker(params: {
}
if (opts.logVerboseMessage) {
params.logVerbose?.(
`Added to echo detection set (size now: ${recentlySent.size}): ${text.substring(0, 50)}...`,
`Added to echo detection set (size now: ${recentlySent.size}): ${text.slice(0, 50)}...`,
);
}
trim();

View File

@@ -641,7 +641,7 @@ export async function attachWebInboxToSocket(
if (upsert.type === "append") {
const APPEND_RECENT_GRACE_MS = 60_000;
const msgTsRaw = msg.messageTimestamp;
const msgTsNum = msgTsRaw != null ? Number(msgTsRaw) : NaN;
const msgTsNum = msgTsRaw != null ? Number(msgTsRaw) : Number.NaN;
const msgTsMs = Number.isFinite(msgTsNum) ? msgTsNum * 1000 : 0;
if (msgTsMs < connectedAtMs - APPEND_RECENT_GRACE_MS) {
continue;

View File

@@ -69,7 +69,7 @@ describe("append upsert handling (#20952)", () => {
{
key: { id: "nan-1", fromMe: false, remoteJid: "120363@g.us" },
message: { conversation: "bad timestamp" },
messageTimestamp: NaN,
messageTimestamp: Number.NaN,
pushName: "BadTs",
},
],

View File

@@ -12,9 +12,9 @@ type WhatsAppSetupConfig = {
};
type WizardPromptHarness = {
text: { (...args: unknown[]): unknown };
select: { (...args: unknown[]): unknown };
note: { (...args: unknown[]): unknown };
text: (...args: unknown[]) => unknown;
select: (...args: unknown[]) => unknown;
note: (...args: unknown[]) => unknown;
};
type QueuedWizardPrompterFactory<T extends WizardPromptHarness> = (params: {