diff --git a/extensions/bluebubbles/src/monitor-processing.ts b/extensions/bluebubbles/src/monitor-processing.ts index 0e671c8adf5..28a68890d45 100644 --- a/extensions/bluebubbles/src/monitor-processing.ts +++ b/extensions/bluebubbles/src/monitor-processing.ts @@ -178,7 +178,7 @@ async function queryBlueBubblesChats(params: { return []; } const payload = (await res.json().catch(() => null)) as Record | 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[]) : []; } diff --git a/extensions/bluebubbles/src/send.ts b/extensions/bluebubbles/src/send.ts index 24eefe01a3a..f6e6685944a 100644 --- a/extensions/bluebubbles/src/send.ts +++ b/extensions/bluebubbles/src/send.ts @@ -226,7 +226,7 @@ async function queryChats(params: { return []; } const payload = (await res.json().catch(() => null)) as Record | 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[]) : []; } diff --git a/extensions/codex/src/app-server/compact.ts b/extensions/codex/src/app-server/compact.ts index f842532138e..965f31c9911 100644 --- a/extensions/codex/src/app-server/compact.ts +++ b/extensions/codex/src/app-server/compact.ts @@ -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; } diff --git a/extensions/discord/src/voice-message.ts b/extensions/discord/src/voice-message.ts index d1188da86a2..64e4b9a8f96 100644 --- a/extensions/discord/src/voice-message.ts +++ b/extensions/discord/src/voice-message.ts @@ -68,8 +68,8 @@ export async function getAudioDuration(filePath: string): Promise { "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 diff --git a/extensions/discord/src/voice/receive-recovery.ts b/extensions/discord/src/voice/receive-recovery.ts index af5fba2cfbb..3ff86e003e0 100644 --- a/extensions/discord/src/voice/receive-recovery.ts +++ b/extensions/discord/src/voice/receive-recovery.ts @@ -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, }; } diff --git a/extensions/duckduckgo/src/ddg-client.ts b/extensions/duckduckgo/src/ddg-client.ts index a2771dcc3e7..4e6d3c08d36 100644 --- a/extensions/duckduckgo/src/ddg-client.ts +++ b/extensions/duckduckgo/src/ddg-client.ts @@ -49,7 +49,7 @@ function decodeHtmlEntities(text: string): string { .replace(/—/g, "--") .replace(/…/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 { diff --git a/extensions/feishu/src/bot-content.ts b/extensions/feishu/src/bot-content.ts index 22212ca67aa..ce166c99fc6 100644 --- a/extensions/feishu/src/bot-content.ts +++ b/extensions/feishu/src/bot-content.ts @@ -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]"]; diff --git a/extensions/feishu/src/bot-sender-name.ts b/extensions/feishu/src/bot-sender-name.ts index 74bd264b3a0..756d93f7c6e 100644 --- a/extensions/feishu/src/bot-sender-name.ts +++ b/extensions/feishu/src/bot-sender-name.ts @@ -17,9 +17,7 @@ type FeishuContactUserGetResponse = Awaited< ReturnType["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 = { diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index d377d3ea5b0..e03a82a4231 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -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 diff --git a/extensions/feishu/src/media.ts b/extensions/feishu/src/media.ts index 5386ad223db..e6caf511b77 100644 --- a/extensions/feishu/src/media.ts +++ b/extensions/feishu/src/media.ts @@ -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, + }); } diff --git a/extensions/feishu/src/mention.ts b/extensions/feishu/src/mention.ts index f604d10a97d..313b0751037 100644 --- a/extensions/feishu/src/mention.ts +++ b/extensions/feishu/src/mention.ts @@ -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; } /** diff --git a/extensions/feishu/src/monitor.account.ts b/extensions/feishu/src/monitor.account.ts index 6437b757445..69ee33979f2 100644 --- a/extensions/feishu/src/monitor.account.ts +++ b/extensions/feishu/src/monitor.account.ts @@ -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"; diff --git a/extensions/feishu/src/monitor.state.ts b/extensions/feishu/src/monitor.state.ts index 08bf28b245d..63a439554c7 100644 --- a/extensions/feishu/src/monitor.state.ts +++ b/extensions/feishu/src/monitor.state.ts @@ -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, diff --git a/extensions/feishu/src/monitor.transport.ts b/extensions/feishu/src/monitor.transport.ts index 0de68b5c9d3..5f617145466 100644 --- a/extensions/feishu/src/monitor.transport.ts +++ b/extensions/feishu/src/monitor.transport.ts @@ -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 { diff --git a/extensions/feishu/src/outbound.ts b/extensions/feishu/src/outbound.ts index 8e9f972fbf6..d451786730b 100644 --- a/extensions/feishu/src/outbound.ts +++ b/extensions/feishu/src/outbound.ts @@ -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"; diff --git a/extensions/feishu/src/send.ts b/extensions/feishu/src/send.ts index c94f0ff48f0..df7388d917b 100644 --- a/extensions/feishu/src/send.ts +++ b/extensions/feishu/src/send.ts @@ -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, }; } diff --git a/extensions/line/src/bot-handlers.ts b/extensions/line/src/bot-handlers.ts index a5ce88ade06..c9d58d01c58 100644 --- a/extensions/line/src/bot-handlers.ts +++ b/extensions/line/src/bot-handlers.ts @@ -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; diff --git a/extensions/line/src/reply-payload-transform.ts b/extensions/line/src/reply-payload-transform.ts index 12a95c44381..2b11aee86bc 100644 --- a/extensions/line/src/reply-payload-transform.ts +++ b/extensions/line/src/reply-payload-transform.ts @@ -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 || "", diff --git a/extensions/matrix/src/setup-config.ts b/extensions/matrix/src/setup-config.ts index f1e1bb4a0bf..6531ac9217e 100644 --- a/extensions/matrix/src/setup-config.ts +++ b/extensions/matrix/src/setup-config.ts @@ -85,7 +85,7 @@ export function moveSingleMatrixAccountConfigToNamedAccount(cfg: CoreConfig): Co typeof base.accounts === "object" && base.accounts ? (base.accounts as Record>) : {}; - 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) { diff --git a/extensions/mattermost/src/mattermost/client.ts b/extensions/mattermost/src/mattermost/client.ts index f668c0f14b2..ff048333521 100644 --- a/extensions/mattermost/src/mattermost/client.ts +++ b/extensions/mattermost/src/mattermost/client.ts @@ -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; } diff --git a/extensions/memory-lancedb/config.ts b/extensions/memory-lancedb/config.ts index fa9d7cc7199..79716ae573b 100644 --- a/extensions/memory-lancedb/config.ts +++ b/extensions/memory-lancedb/config.ts @@ -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) diff --git a/extensions/memory-lancedb/index.ts b/extensions/memory-lancedb/index.ts index 03c20d45864..53fda51a7f2 100644 --- a/extensions/memory-lancedb/index.ts +++ b/extensions/memory-lancedb/index.ts @@ -551,7 +551,7 @@ export default definePluginEntry({ .option("--limit ", "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, diff --git a/extensions/nostr/src/nostr-bus.fuzz.test.ts b/extensions/nostr/src/nostr-bus.fuzz.test.ts index 8065a377e2d..9eab7fae3ea 100644 --- a/extensions/nostr/src/nostr-bus.fuzz.test.ts +++ b/extensions/nostr/src/nostr-bus.fuzz.test.ts @@ -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", () => { diff --git a/extensions/nostr/src/nostr-key-utils.ts b/extensions/nostr/src/nostr-key-utils.ts index 5c5d335ad10..a9c1d2696e4 100644 --- a/extensions/nostr/src/nostr-key-utils.ts +++ b/extensions/nostr/src/nostr-key-utils.ts @@ -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; } diff --git a/extensions/nostr/src/test-fixtures.ts b/extensions/nostr/src/test-fixtures.ts index c5d7c58ec87..d3bd1b665e8 100644 --- a/extensions/nostr/src/test-fixtures.ts +++ b/extensions/nostr/src/test-fixtures.ts @@ -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 = {}): { diff --git a/extensions/qa-lab/web/src/app.ts b/extensions/qa-lab/web/src/app.ts index eb27511140a..40562724db3 100644 --- a/extensions/qa-lab/web/src/app.ts +++ b/extensions/qa-lab/web/src/app.ts @@ -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") { diff --git a/extensions/qqbot/src/engine/commands/slash-commands-impl.ts b/extensions/qqbot/src/engine/commands/slash-commands-impl.ts index 055583ad490..ee74d3fb204 100644 --- a/extensions/qqbot/src/engine/commands/slash-commands-impl.ts +++ b/extensions/qqbot/src/engine/commands/slash-commands-impl.ts @@ -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; diff --git a/extensions/qqbot/src/engine/gateway/inbound-attachments.ts b/extensions/qqbot/src/engine/gateway/inbound-attachments.ts index 2b39777489b..723f4e01864 100644 --- a/extensions/qqbot/src/engine/gateway/inbound-attachments.ts +++ b/extensions/qqbot/src/engine/gateway/inbound-attachments.ts @@ -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, + }; }, ); diff --git a/extensions/qqbot/src/engine/messaging/decode-media-path.ts b/extensions/qqbot/src/engine/messaging/decode-media-path.ts index bf5fa8cb452..b48a2e25bb4 100644 --- a/extensions/qqbot/src/engine/messaging/decode-media-path.ts +++ b/extensions/qqbot/src/engine/messaging/decode-media-path.ts @@ -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++) { diff --git a/extensions/qqbot/src/engine/messaging/outbound.ts b/extensions/qqbot/src/engine/messaging/outbound.ts index 929ced9be44..da646140724 100644 --- a/extensions/qqbot/src/engine/messaging/outbound.ts +++ b/extensions/qqbot/src/engine/messaging/outbound.ts @@ -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 { 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[] = []; diff --git a/extensions/qqbot/src/engine/tools/remind-logic.ts b/extensions/qqbot/src/engine/tools/remind-logic.ts index ebcc6a243b9..6b57c509dd0 100644 --- a/extensions/qqbot/src/engine/tools/remind-logic.ts +++ b/extensions/qqbot/src/engine/tools/remind-logic.ts @@ -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": diff --git a/extensions/qqbot/src/engine/utils/image-size.test.ts b/extensions/qqbot/src/engine/utils/image-size.test.ts index 47727a630c1..1f29cf3d8e9 100644 --- a/extensions/qqbot/src/engine/utils/image-size.test.ts +++ b/extensions/qqbot/src/engine/utils/image-size.test.ts @@ -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(() => ({ diff --git a/extensions/qqbot/src/engine/utils/image-size.ts b/extensions/qqbot/src/engine/utils/image-size.ts index 9838eda84ac..865f2e3befd 100644 --- a/extensions/qqbot/src/engine/utils/image-size.ts +++ b/extensions/qqbot/src/engine/utils/image-size.ts @@ -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; } diff --git a/extensions/signal/src/message-actions.ts b/extensions/signal/src/message-actions.ts index 162089e6324..2640ba986bb 100644 --- a/extensions/signal/src/message-actions.ts +++ b/extensions/signal/src/message-actions.ts @@ -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.`); } diff --git a/extensions/slack/src/monitor/channel-config.ts b/extensions/slack/src/monitor/channel-config.ts index 24c8ff64f6f..abbec18665b 100644 --- a/extensions/slack/src/monitor/channel-config.ts +++ b/extensions/slack/src/monitor/channel-config.ts @@ -33,7 +33,7 @@ export type SlackChannelConfigEntries = Record; function firstDefined(...values: Array) { for (const value of values) { - if (typeof value !== "undefined") { + if (value !== undefined) { return value; } } diff --git a/extensions/synology-chat/src/client.ts b/extensions/synology-chat/src/client.ts index 6652bf29988..e963e522751 100644 --- a/extensions/synology-chat/src/client.ts +++ b/extensions/synology-chat/src/client.ts @@ -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; } diff --git a/extensions/telegram/src/bot-message-context.ts b/extensions/telegram/src/bot-message-context.ts index 75c2d979530..2c81c5a07b2 100644 --- a/extensions/telegram/src/bot-message-context.ts +++ b/extensions/telegram/src/bot-message-context.ts @@ -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, diff --git a/extensions/telegram/src/bot-updates.ts b/extensions/telegram/src/bot-updates.ts index 4b08c747f8f..68d714e21e2 100644 --- a/extensions/telegram/src/bot-updates.ts +++ b/extensions/telegram/src/bot-updates.ts @@ -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; diff --git a/extensions/telegram/src/bot/delivery.replies.ts b/extensions/telegram/src/bot/delivery.replies.ts index 6b1737f4ac1..95835c6ea21 100644 --- a/extensions/telegram/src/bot/delivery.replies.ts +++ b/extensions/telegram/src/bot/delivery.replies.ts @@ -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 { diff --git a/extensions/telegram/src/bot/helpers.ts b/extensions/telegram/src/bot/helpers.ts index 70da79d41b3..67f43d02833 100644 --- a/extensions/telegram/src/bot/helpers.ts +++ b/extensions/telegram/src/bot/helpers.ts @@ -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, diff --git a/extensions/telegram/src/status-reaction-variants.ts b/extensions/telegram/src/status-reaction-variants.ts index 32e974c8168..2074ed40679 100644 --- a/extensions/telegram/src/status-reaction-variants.ts +++ b/extensions/telegram/src/status-reaction-variants.ts @@ -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) { diff --git a/extensions/tlon/src/monitor/approval.ts b/extensions/tlon/src/monitor/approval.ts index 8e25d72a089..e1d5416d2f0 100644 --- a/extensions/tlon/src/monitor/approval.ts +++ b/extensions/tlon/src/monitor/approval.ts @@ -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) + "..."; } /** diff --git a/extensions/tlon/src/monitor/index.ts b/extensions/tlon/src/monitor/index.ts index 3692daba2c0..46d825bc0a5 100644 --- a/extensions/tlon/src/monitor/index.ts +++ b/extensions/tlon/src/monitor/index.ts @@ -788,7 +788,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise this.lastHeardEventId) { this.lastHeardEventId = eventId; if (eventId - this.lastAcknowledgedEventId > this.ackThreshold) { diff --git a/extensions/voice-call/src/providers/twilio.ts b/extensions/voice-call/src/providers/twilio.ts index f573f1bfdff..44520c63d0c 100644 --- a/extensions/voice-call/src/providers/twilio.ts +++ b/extensions/voice-call/src/providers/twilio.ts @@ -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"), }; } diff --git a/extensions/voice-call/src/webhook-security.ts b/extensions/voice-call/src/webhook-security.ts index d6d5a59feca..d9a9321c3a8 100644 --- a/extensions/voice-call/src/webhook-security.ts +++ b/extensions/voice-call/src/webhook-security.ts @@ -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" }; } diff --git a/extensions/whatsapp/src/auto-reply/monitor/echo.ts b/extensions/whatsapp/src/auto-reply/monitor/echo.ts index ca13a98e908..25b2708d8e8 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/echo.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/echo.ts @@ -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(); diff --git a/extensions/whatsapp/src/inbound/monitor.ts b/extensions/whatsapp/src/inbound/monitor.ts index 3ea6654cfd1..a9766c14b3a 100644 --- a/extensions/whatsapp/src/inbound/monitor.ts +++ b/extensions/whatsapp/src/inbound/monitor.ts @@ -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; diff --git a/extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts b/extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts index af106a10a42..ec14d0fb20c 100644 --- a/extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts +++ b/extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts @@ -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", }, ], diff --git a/extensions/whatsapp/src/setup-test-helpers.ts b/extensions/whatsapp/src/setup-test-helpers.ts index ad2f14d6841..941c225d6f4 100644 --- a/extensions/whatsapp/src/setup-test-helpers.ts +++ b/extensions/whatsapp/src/setup-test-helpers.ts @@ -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 = (params: {