chore: Enable "curly" rule to avoid single-statement if confusion/errors.

This commit is contained in:
cpojer
2026-01-31 16:19:20 +09:00
parent 009b16fab8
commit 5ceff756e1
1266 changed files with 27871 additions and 9393 deletions

View File

@@ -12,19 +12,25 @@ export type ResolvedIMessageAccount = {
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
const accounts = cfg.channels?.imessage?.accounts;
if (!accounts || typeof accounts !== "object") return [];
if (!accounts || typeof accounts !== "object") {
return [];
}
return Object.keys(accounts).filter(Boolean);
}
export function listIMessageAccountIds(cfg: OpenClawConfig): string[] {
const ids = listConfiguredAccountIds(cfg);
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
if (ids.length === 0) {
return [DEFAULT_ACCOUNT_ID];
}
return ids.toSorted((a, b) => a.localeCompare(b));
}
export function resolveDefaultIMessageAccountId(cfg: OpenClawConfig): string {
const ids = listIMessageAccountIds(cfg);
if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID;
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
return DEFAULT_ACCOUNT_ID;
}
return ids[0] ?? DEFAULT_ACCOUNT_ID;
}
@@ -33,7 +39,9 @@ function resolveAccountConfig(
accountId: string,
): IMessageAccountConfig | undefined {
const accounts = cfg.channels?.imessage?.accounts;
if (!accounts || typeof accounts !== "object") return undefined;
if (!accounts || typeof accounts !== "object") {
return undefined;
}
return accounts[accountId] as IMessageAccountConfig | undefined;
}

View File

@@ -60,7 +60,9 @@ export class IMessageRpcClient {
}
async start(): Promise<void> {
if (this.child) return;
if (this.child) {
return;
}
const args = ["rpc"];
if (this.dbPath) {
args.push("--db", this.dbPath);
@@ -73,14 +75,18 @@ export class IMessageRpcClient {
this.reader.on("line", (line) => {
const trimmed = line.trim();
if (!trimmed) return;
if (!trimmed) {
return;
}
this.handleLine(trimmed);
});
child.stderr?.on("data", (chunk) => {
const lines = chunk.toString().split(/\r?\n/);
for (const line of lines) {
if (!line.trim()) continue;
if (!line.trim()) {
continue;
}
this.runtime?.error?.(`imsg rpc: ${line.trim()}`);
}
});
@@ -102,7 +108,9 @@ export class IMessageRpcClient {
}
async stop(): Promise<void> {
if (!this.child) return;
if (!this.child) {
return;
}
this.reader?.close();
this.reader = null;
this.child.stdin?.end();
@@ -113,7 +121,9 @@ export class IMessageRpcClient {
this.closed,
new Promise<void>((resolve) => {
setTimeout(() => {
if (!child.killed) child.kill("SIGTERM");
if (!child.killed) {
child.kill("SIGTERM");
}
resolve();
}, 500);
}),
@@ -175,8 +185,12 @@ export class IMessageRpcClient {
if (parsed.id !== undefined && parsed.id !== null) {
const key = String(parsed.id);
const pending = this.pending.get(key);
if (!pending) return;
if (pending.timer) clearTimeout(pending.timer);
if (!pending) {
return;
}
if (pending.timer) {
clearTimeout(pending.timer);
}
this.pending.delete(key);
if (parsed.error) {
@@ -184,11 +198,15 @@ export class IMessageRpcClient {
const details = parsed.error.data;
const code = parsed.error.code;
const suffixes = [] as string[];
if (typeof code === "number") suffixes.push(`code=${code}`);
if (typeof code === "number") {
suffixes.push(`code=${code}`);
}
if (details !== undefined) {
const detailText =
typeof details === "string" ? details : JSON.stringify(details, null, 2);
if (detailText) suffixes.push(detailText);
if (detailText) {
suffixes.push(detailText);
}
}
const msg = suffixes.length > 0 ? `${baseMessage}: ${suffixes.join(" ")}` : baseMessage;
pending.reject(new Error(msg));
@@ -208,7 +226,9 @@ export class IMessageRpcClient {
private failAll(err: Error) {
for (const [key, pending] of this.pending.entries()) {
if (pending.timer) clearTimeout(pending.timer);
if (pending.timer) {
clearTimeout(pending.timer);
}
pending.reject(err);
this.pending.delete(key);
}

View File

@@ -64,7 +64,9 @@ const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
async function waitForSubscribe() {
for (let i = 0; i < 5; i += 1) {
if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe")) return;
if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe")) {
return;
}
await flush();
}
}
@@ -84,7 +86,9 @@ beforeEach(() => {
},
};
requestMock.mockReset().mockImplementation((method: string) => {
if (method === "watch.subscribe") return Promise.resolve({ subscription: 1 });
if (method === "watch.subscribe") {
return Promise.resolve({ subscription: 1 });
}
return Promise.resolve({});
});
stopMock.mockReset().mockResolvedValue(undefined);

View File

@@ -64,7 +64,9 @@ const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
async function waitForSubscribe() {
for (let i = 0; i < 5; i += 1) {
if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe")) return;
if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe")) {
return;
}
await flush();
}
}
@@ -84,7 +86,9 @@ beforeEach(() => {
},
};
requestMock.mockReset().mockImplementation((method: string) => {
if (method === "watch.subscribe") return Promise.resolve({ subscription: 1 });
if (method === "watch.subscribe") {
return Promise.resolve({ subscription: 1 });
}
return Promise.resolve({});
});
stopMock.mockReset().mockResolvedValue(undefined);
@@ -133,8 +137,12 @@ describe("monitorIMessageProvider", () => {
it("does not trigger unhandledRejection when aborting during shutdown", async () => {
requestMock.mockImplementation((method: string) => {
if (method === "watch.subscribe") return Promise.resolve({ subscription: 1 });
if (method === "watch.unsubscribe") return Promise.reject(new Error("imsg rpc closed"));
if (method === "watch.subscribe") {
return Promise.resolve({ subscription: 1 });
}
if (method === "watch.unsubscribe") {
return Promise.reject(new Error("imsg rpc closed"));
}
return Promise.resolve({});
});

View File

@@ -28,7 +28,9 @@ export async function deliverReplies(params: {
const mediaList = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
const rawText = payload.text ?? "";
const text = convertMarkdownTables(rawText, tableMode);
if (!text && mediaList.length === 0) continue;
if (!text && mediaList.length === 0) {
continue;
}
if (mediaList.length === 0) {
for (const chunk of chunkTextWithMode(text, textLimit, chunkMode)) {
await sendMessageIMessage(target, chunk, {

View File

@@ -72,7 +72,9 @@ async function detectRemoteHostFromCliPath(cliPath: string): Promise<string | un
// Match user@host pattern first (e.g., openclaw@192.168.64.3)
const userHostMatch = content.match(/\bssh\b[^\n]*?\s+([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+)/);
if (userHostMatch) return userHostMatch[1];
if (userHostMatch) {
return userHostMatch[1];
}
// Fallback: match host-only before imsg command (e.g., ssh -T mac-mini imsg)
const hostOnlyMatch = content.match(/\bssh\b[^\n]*?\s+([a-zA-Z][a-zA-Z0-9._-]*)\s+\S*\bimsg\b/);
@@ -93,13 +95,17 @@ function normalizeReplyField(value: unknown): string | undefined {
const trimmed = value.trim();
return trimmed ? trimmed : undefined;
}
if (typeof value === "number") return String(value);
if (typeof value === "number") {
return String(value);
}
return undefined;
}
function describeReplyContext(message: IMessagePayload): IMessageReplyContext | null {
const body = normalizeReplyField(message.reply_to_text);
if (!body) return null;
if (!body) {
return null;
}
const id = normalizeReplyField(message.reply_to_id);
const sender = normalizeReplyField(message.reply_to_sender);
return { body, id, sender };
@@ -149,7 +155,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
debounceMs: inboundDebounceMs,
buildKey: (entry) => {
const sender = entry.message.sender?.trim();
if (!sender) return null;
if (!sender) {
return null;
}
const conversationId =
entry.message.chat_id != null
? `chat:${entry.message.chat_id}`
@@ -158,13 +166,19 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
},
shouldDebounce: (entry) => {
const text = entry.message.text?.trim() ?? "";
if (!text) return false;
if (entry.message.attachments && entry.message.attachments.length > 0) return false;
if (!text) {
return false;
}
if (entry.message.attachments && entry.message.attachments.length > 0) {
return false;
}
return !hasControlCommand(text, cfg);
},
onFlush: async (entries) => {
const last = entries.at(-1);
if (!last) return;
if (!last) {
return;
}
if (entries.length === 1) {
await handleMessageNow(last.message);
return;
@@ -188,9 +202,13 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
async function handleMessageNow(message: IMessagePayload) {
const senderRaw = message.sender ?? "";
const sender = senderRaw.trim();
if (!sender) return;
if (!sender) {
return;
}
const senderNormalized = normalizeIMessageHandle(sender);
if (message.is_from_me) return;
if (message.is_from_me) {
return;
}
const chatId = message.chat_id ?? undefined;
const chatGuid = message.chat_guid ?? undefined;
@@ -220,7 +238,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
);
const isGroup = Boolean(message.is_group) || treatAsGroupByConfig;
if (isGroup && !chatId) return;
if (isGroup && !chatId) {
return;
}
const groupId = isGroup ? groupIdCandidate : undefined;
const storeAllowFrom = await readChannelAllowFromStore("imessage").catch(() => []);
@@ -273,7 +293,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
chatIdentifier,
}));
if (!isGroup) {
if (dmPolicy === "disabled") return;
if (dmPolicy === "disabled") {
return;
}
if (!dmAuthorized) {
if (dmPolicy === "pairing") {
const senderId = normalizeIMessageHandle(sender);
@@ -336,7 +358,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
const kind = mediaKindFromMime(mediaType ?? undefined);
const placeholder = kind ? `<media:${kind}>` : attachments?.length ? "<media:attachment>" : "";
const bodyText = messageText || placeholder;
if (!bodyText) return;
if (!bodyText) {
return;
}
const replyContext = describeReplyContext(message);
const createdAt = message.created_at ? Date.parse(message.created_at) : undefined;
const historyKey = isGroup
@@ -580,7 +604,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
const handleMessage = async (raw: unknown) => {
const params = raw as { message?: IMessagePayload | null };
const message = params?.message ?? null;
if (!message) return;
if (!message) {
return;
}
await inboundDebouncer.enqueue({ message });
};
@@ -594,7 +620,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
runtime,
check: async () => {
const probe = await probeIMessage(2000, { cliPath, dbPath, runtime });
if (probe.ok) return { ok: true };
if (probe.ok) {
return { ok: true };
}
if (probe.fatal) {
throw new Error(probe.error ?? "imsg rpc unavailable");
}
@@ -602,7 +630,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
},
});
if (opts.abortSignal?.aborted) return;
if (opts.abortSignal?.aborted) {
return;
}
const client = await createIMessageRpcClient({
cliPath,
@@ -644,7 +674,9 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
subscriptionId = result?.subscription ?? null;
await client.waitForClose();
} catch (err) {
if (abort?.aborted) return;
if (abort?.aborted) {
return;
}
runtime.error?.(danger(`imessage: monitor failed: ${String(err)}`));
throw err;
} finally {

View File

@@ -26,7 +26,9 @@ const rpcSupportCache = new Map<string, RpcSupportResult>();
async function probeRpcSupport(cliPath: string): Promise<RpcSupportResult> {
const cached = rpcSupportCache.get(cliPath);
if (cached) return cached;
if (cached) {
return cached;
}
try {
const result = await runCommandWithTimeout([cliPath, "rpc", "--help"], { timeoutMs: 2000 });
const combined = `${result.stdout}\n${result.stderr}`.trim();

View File

@@ -26,7 +26,9 @@ export type IMessageSendResult = {
};
function resolveMessageId(result: Record<string, unknown> | null | undefined): string | null {
if (!result) return null;
if (!result) {
return null;
}
const raw =
(typeof result.messageId === "string" && result.messageId.trim()) ||
(typeof result.message_id === "string" && result.message_id.trim()) ||
@@ -83,7 +85,9 @@ export async function sendMessageIMessage(
filePath = resolved.path;
if (!message.trim()) {
const kind = mediaKindFromMime(resolved.contentType ?? undefined);
if (kind) message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
if (kind) {
message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
}
}
}
@@ -104,7 +108,9 @@ export async function sendMessageIMessage(
service: service || "auto",
region,
};
if (filePath) params.file = filePath;
if (filePath) {
params.file = filePath;
}
if (target.kind === "chat_id") {
params.chat_id = target.chatId;

View File

@@ -29,11 +29,19 @@ function stripPrefix(value: string, prefix: string): string {
export function normalizeIMessageHandle(raw: string): string {
const trimmed = raw.trim();
if (!trimmed) return "";
if (!trimmed) {
return "";
}
const lowered = trimmed.toLowerCase();
if (lowered.startsWith("imessage:")) return normalizeIMessageHandle(trimmed.slice(9));
if (lowered.startsWith("sms:")) return normalizeIMessageHandle(trimmed.slice(4));
if (lowered.startsWith("auto:")) return normalizeIMessageHandle(trimmed.slice(5));
if (lowered.startsWith("imessage:")) {
return normalizeIMessageHandle(trimmed.slice(9));
}
if (lowered.startsWith("sms:")) {
return normalizeIMessageHandle(trimmed.slice(4));
}
if (lowered.startsWith("auto:")) {
return normalizeIMessageHandle(trimmed.slice(5));
}
// Normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively
for (const prefix of CHAT_ID_PREFIXES) {
@@ -55,21 +63,29 @@ export function normalizeIMessageHandle(raw: string): string {
}
}
if (trimmed.includes("@")) return trimmed.toLowerCase();
if (trimmed.includes("@")) {
return trimmed.toLowerCase();
}
const normalized = normalizeE164(trimmed);
if (normalized) return normalized;
if (normalized) {
return normalized;
}
return trimmed.replace(/\s+/g, "");
}
export function parseIMessageTarget(raw: string): IMessageTarget {
const trimmed = raw.trim();
if (!trimmed) throw new Error("iMessage target is required");
if (!trimmed) {
throw new Error("iMessage target is required");
}
const lower = trimmed.toLowerCase();
for (const { prefix, service } of SERVICE_PREFIXES) {
if (lower.startsWith(prefix)) {
const remainder = stripPrefix(trimmed, prefix);
if (!remainder) throw new Error(`${prefix} target is required`);
if (!remainder) {
throw new Error(`${prefix} target is required`);
}
const remainderLower = remainder.toLowerCase();
const isChatTarget =
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
@@ -96,7 +112,9 @@ export function parseIMessageTarget(raw: string): IMessageTarget {
for (const prefix of CHAT_GUID_PREFIXES) {
if (lower.startsWith(prefix)) {
const value = stripPrefix(trimmed, prefix);
if (!value) throw new Error("chat_guid is required");
if (!value) {
throw new Error("chat_guid is required");
}
return { kind: "chat_guid", chatGuid: value };
}
}
@@ -104,7 +122,9 @@ export function parseIMessageTarget(raw: string): IMessageTarget {
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
if (lower.startsWith(prefix)) {
const value = stripPrefix(trimmed, prefix);
if (!value) throw new Error("chat_identifier is required");
if (!value) {
throw new Error("chat_identifier is required");
}
return { kind: "chat_identifier", chatIdentifier: value };
}
}
@@ -114,13 +134,17 @@ export function parseIMessageTarget(raw: string): IMessageTarget {
export function parseIMessageAllowTarget(raw: string): IMessageAllowTarget {
const trimmed = raw.trim();
if (!trimmed) return { kind: "handle", handle: "" };
if (!trimmed) {
return { kind: "handle", handle: "" };
}
const lower = trimmed.toLowerCase();
for (const { prefix } of SERVICE_PREFIXES) {
if (lower.startsWith(prefix)) {
const remainder = stripPrefix(trimmed, prefix);
if (!remainder) return { kind: "handle", handle: "" };
if (!remainder) {
return { kind: "handle", handle: "" };
}
return parseIMessageAllowTarget(remainder);
}
}
@@ -129,21 +153,27 @@ export function parseIMessageAllowTarget(raw: string): IMessageAllowTarget {
if (lower.startsWith(prefix)) {
const value = stripPrefix(trimmed, prefix);
const chatId = Number.parseInt(value, 10);
if (Number.isFinite(chatId)) return { kind: "chat_id", chatId };
if (Number.isFinite(chatId)) {
return { kind: "chat_id", chatId };
}
}
}
for (const prefix of CHAT_GUID_PREFIXES) {
if (lower.startsWith(prefix)) {
const value = stripPrefix(trimmed, prefix);
if (value) return { kind: "chat_guid", chatGuid: value };
if (value) {
return { kind: "chat_guid", chatGuid: value };
}
}
}
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
if (lower.startsWith(prefix)) {
const value = stripPrefix(trimmed, prefix);
if (value) return { kind: "chat_identifier", chatIdentifier: value };
if (value) {
return { kind: "chat_identifier", chatIdentifier: value };
}
}
}
@@ -158,8 +188,12 @@ export function isAllowedIMessageSender(params: {
chatIdentifier?: string | null;
}): boolean {
const allowFrom = params.allowFrom.map((entry) => String(entry).trim());
if (allowFrom.length === 0) return true;
if (allowFrom.includes("*")) return true;
if (allowFrom.length === 0) {
return true;
}
if (allowFrom.includes("*")) {
return true;
}
const senderNormalized = normalizeIMessageHandle(params.sender);
const chatId = params.chatId ?? undefined;
@@ -167,22 +201,34 @@ export function isAllowedIMessageSender(params: {
const chatIdentifier = params.chatIdentifier?.trim();
for (const entry of allowFrom) {
if (!entry) continue;
if (!entry) {
continue;
}
const parsed = parseIMessageAllowTarget(entry);
if (parsed.kind === "chat_id" && chatId !== undefined) {
if (parsed.chatId === chatId) return true;
if (parsed.chatId === chatId) {
return true;
}
} else if (parsed.kind === "chat_guid" && chatGuid) {
if (parsed.chatGuid === chatGuid) return true;
if (parsed.chatGuid === chatGuid) {
return true;
}
} else if (parsed.kind === "chat_identifier" && chatIdentifier) {
if (parsed.chatIdentifier === chatIdentifier) return true;
if (parsed.chatIdentifier === chatIdentifier) {
return true;
}
} else if (parsed.kind === "handle" && senderNormalized) {
if (parsed.handle === senderNormalized) return true;
if (parsed.handle === senderNormalized) {
return true;
}
}
}
return false;
}
export function formatIMessageChatTarget(chatId?: number | null): string {
if (!chatId || !Number.isFinite(chatId)) return "";
if (!chatId || !Number.isFinite(chatId)) {
return "";
}
return `chat_id:${chatId}`;
}