chore(googlechat): fix lint and formatting issues

- Add node: protocol to imports
- Remove unused imports
- Fix any type to unknown with proper typing
- Fix non-null assertions with optional chaining
- Change let to const where appropriate
- Apply formatting and import organization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Justin Massa 2026-01-12 09:16:25 -06:00
parent 6208f7da36
commit 34fb5ca9c6
8 changed files with 90 additions and 48 deletions

View File

@ -16,7 +16,9 @@ export type ResolvedGoogleChatAccount = {
};
function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] {
const googlechat = (cfg as { googlechat?: { accounts?: Record<string, unknown> } }).googlechat;
const googlechat = (
cfg as { googlechat?: { accounts?: Record<string, unknown> } }
).googlechat;
const accounts = googlechat?.accounts;
if (!accounts || typeof accounts !== "object") return [];
return Object.keys(accounts).filter(Boolean);
@ -38,7 +40,11 @@ function resolveAccountConfig(
cfg: ClawdbotConfig,
accountId: string,
): GoogleChatAccountConfig | undefined {
const googlechat = (cfg as { googlechat?: { accounts?: Record<string, GoogleChatAccountConfig> } }).googlechat;
const googlechat = (
cfg as {
googlechat?: { accounts?: Record<string, GoogleChatAccountConfig> };
}
).googlechat;
const accounts = googlechat?.accounts;
if (!accounts || typeof accounts !== "object") return undefined;
return accounts[accountId];
@ -48,7 +54,9 @@ function mergeGoogleChatAccountConfig(
cfg: ClawdbotConfig,
accountId: string,
): GoogleChatAccountConfig {
const googlechat = (cfg as { googlechat?: GoogleChatAccountConfig & { accounts?: unknown } }).googlechat ?? {};
const googlechat =
(cfg as { googlechat?: GoogleChatAccountConfig & { accounts?: unknown } })
.googlechat ?? {};
const { accounts: _ignored, ...base } = googlechat;
const account = resolveAccountConfig(cfg, accountId) ?? {};
return { ...base, ...account };
@ -58,7 +66,8 @@ export function resolveGoogleChatAccount(params: {
cfg: ClawdbotConfig;
accountId?: string | null;
}): ResolvedGoogleChatAccount {
const googlechat = (params.cfg as { googlechat?: { enabled?: boolean } }).googlechat;
const googlechat = (params.cfg as { googlechat?: { enabled?: boolean } })
.googlechat;
const baseEnabled = googlechat?.enabled !== false;
const resolve = (accountId: string) => {

View File

@ -1,8 +1,8 @@
import { PubSub, type Message } from "@google-cloud/pubsub";
import { type Message, PubSub } from "@google-cloud/pubsub";
import type { ClawdbotConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
import type { GoogleChatEvent, GoogleChatMessage } from "./types.js";
import type { GoogleChatEvent } from "./types.js";
export type GoogleChatMonitorOptions = {
account: ResolvedGoogleChatAccount;
@ -86,8 +86,8 @@ function checkMessagePolicy(
): boolean {
const policy =
message.chat.type === "dm"
? account.config.dmPolicy ?? "pairing"
: account.config.spacePolicy ?? "disabled";
? (account.config.dmPolicy ?? "pairing")
: (account.config.spacePolicy ?? "disabled");
switch (policy) {
case "disabled":
@ -157,7 +157,10 @@ export async function monitorGoogleChatProvider(
};
const errorHandler = (error: Error) => {
console.error(`[googlechat:${account.accountId}] Subscription error:`, error);
console.error(
`[googlechat:${account.accountId}] Subscription error:`,
error,
);
};
subscription.on("message", messageHandler);

View File

@ -1,6 +1,6 @@
#!/usr/bin/env npx tsx
import { execSync } from "node:child_process";
import express, { type Request, type Response } from "express";
import { execSync } from "child_process";
const PORT = 18792;
const app = express();
@ -21,7 +21,11 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
const isAddedToSpace = !!chat.addedToSpacePayload;
const isMessage = !!chat.messagePayload;
const eventType = isAddedToSpace ? "ADDED_TO_SPACE" : isMessage ? "MESSAGE" : "UNKNOWN";
const eventType = isAddedToSpace
? "ADDED_TO_SPACE"
: isMessage
? "MESSAGE"
: "UNKNOWN";
console.log(`[googlechat] Received event: ${eventType}`);
if (isAddedToSpace) {
@ -55,21 +59,27 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
const escapedText = text.replace(/'/g, "'\\''");
const sessionId = `googlechat:${spaceId}`;
const result = execSync(
`clawdbot agent --message '${escapedText}' --session-id '${sessionId}' --local 2>&1`,
`clawdbot agent --message '${escapedText}' --session-id '${sessionId}' --local`,
{
timeout: 25000, // 25 second timeout (Google Chat times out at ~30s)
encoding: 'utf-8',
timeout: 25000, // 25 second timeout (Google Chat times out at ~30s)
encoding: "utf-8",
maxBuffer: 1024 * 1024,
}
},
);
responseText = result.trim() || "I processed your message but have no response.";
console.log(`[googlechat] AI Response: ${responseText.slice(0, 100)}...`);
} catch (err: any) {
console.error(`[googlechat] CLI error:`, err.message);
if (err.killed) {
responseText = "Sorry, the request timed out. Please try a simpler question.";
responseText =
result.trim() || "I processed your message but have no response.";
console.log(
`[googlechat] AI Response: ${responseText.slice(0, 100)}...`,
);
} catch (err: unknown) {
const error = err as { message?: string; killed?: boolean };
console.error(`[googlechat] CLI error:`, error.message);
if (error.killed) {
responseText =
"Sorry, the request timed out. Please try a simpler question.";
} else {
responseText = "Sorry, I encountered an error processing your message.";
responseText =
"Sorry, I encountered an error processing your message.";
}
}
@ -96,6 +106,10 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
app.listen(PORT, () => {
console.log(`[googlechat] Webhook server running on port ${PORT}`);
console.log(`[googlechat] Local: http://localhost:${PORT}/webhook/googlechat`);
console.log(`[googlechat] Use ngrok URL + /webhook/googlechat for Google Chat config`);
console.log(
`[googlechat] Local: http://localhost:${PORT}/webhook/googlechat`,
);
console.log(
`[googlechat] Use ngrok URL + /webhook/googlechat for Google Chat config`,
);
});

View File

@ -1,9 +1,11 @@
import { google, type chat_v1 } from "googleapis";
import { type chat_v1, google } from "googleapis";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
let chatClients: Map<string, chat_v1.Chat> = new Map();
const chatClients: Map<string, chat_v1.Chat> = new Map();
async function getChatClient(account: ResolvedGoogleChatAccount): Promise<chat_v1.Chat> {
async function getChatClient(
account: ResolvedGoogleChatAccount,
): Promise<chat_v1.Chat> {
const cacheKey = `${account.accountId}:${account.credentialsPath ?? "default"}`;
const cached = chatClients.get(cacheKey);
if (cached) return cached;
@ -127,8 +129,8 @@ export async function sendGoogleChatMedia(
},
};
if (options.caption) {
card.card!.sections![0].widgets!.unshift({
if (options.caption && card.card?.sections?.[0]?.widgets) {
card.card.sections[0].widgets.unshift({
textParagraph: { text: options.caption },
});
}

View File

@ -59,11 +59,7 @@ export type GoogleChatMessage = {
};
export type GoogleChatEvent = {
type:
| "MESSAGE"
| "ADDED_TO_SPACE"
| "REMOVED_FROM_SPACE"
| "CARD_CLICKED";
type: "MESSAGE" | "ADDED_TO_SPACE" | "REMOVED_FROM_SPACE" | "CARD_CLICKED";
eventTime: string;
message?: GoogleChatMessage;
user?: {

View File

@ -83,7 +83,11 @@ function normalizeMessage(
export async function startGoogleChatWebhookServer(
options: GoogleChatWebhookOptions,
): Promise<{ server: ReturnType<typeof express>; port: number; stop: () => void }> {
): Promise<{
server: ReturnType<typeof express>;
port: number;
stop: () => void;
}> {
const { account, port = 18792 } = options;
const app = express();
@ -91,7 +95,11 @@ export async function startGoogleChatWebhookServer(
// Health check endpoint
app.get("/health", (_req: Request, res: Response) => {
res.json({ ok: true, provider: "googlechat", accountId: account.accountId });
res.json({
ok: true,
provider: "googlechat",
accountId: account.accountId,
});
});
// Google Chat webhook endpoint
@ -99,7 +107,9 @@ export async function startGoogleChatWebhookServer(
try {
const event = req.body as GoogleChatEvent;
console.log(`[googlechat:${account.accountId}] Received event: ${event.type}`);
console.log(
`[googlechat:${account.accountId}] Received event: ${event.type}`,
);
// Handle different event types
if (event.type === "ADDED_TO_SPACE") {
@ -142,8 +152,12 @@ export async function startGoogleChatWebhookServer(
// Start server
const server = app.listen(port, () => {
console.log(`[googlechat:${account.accountId}] Webhook server listening on port ${port}`);
console.log(`[googlechat:${account.accountId}] Webhook URL: http://localhost:${port}/webhook/googlechat`);
console.log(
`[googlechat:${account.accountId}] Webhook server listening on port ${port}`,
);
console.log(
`[googlechat:${account.accountId}] Webhook URL: http://localhost:${port}/webhook/googlechat`,
);
});
const stop = () => {

View File

@ -1,6 +1,3 @@
import { chunkMarkdownText } from "../../auto-reply/chunk.js";
import type { ClawdbotConfig } from "../../config/config.js";
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
import {
listGoogleChatAccountIds,
type ResolvedGoogleChatAccount,
@ -13,6 +10,7 @@ import {
sendGoogleChatMedia,
sendGoogleChatText,
} from "../../googlechat/send.js";
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
import { getChatProviderMeta } from "../registry.js";
import {
deleteAccountFromConfigSection,
@ -57,7 +55,12 @@ export const googlechatPlugin: ProviderPlugin<ResolvedGoogleChatAccount> = {
cfg,
sectionKey: "googlechat",
accountId,
clearBaseFields: ["projectId", "subscriptionName", "credentialsPath", "name"],
clearBaseFields: [
"projectId",
"subscriptionName",
"credentialsPath",
"name",
],
}),
isConfigured: (account) =>
Boolean(account.projectId?.trim() && account.subscriptionName?.trim()),
@ -78,7 +81,9 @@ export const googlechatPlugin: ProviderPlugin<ResolvedGoogleChatAccount> = {
},
security: {
resolveDmPolicy: ({ cfg, accountId, account }) => {
const googlechat = (cfg as { googlechat?: { accounts?: Record<string, unknown> } }).googlechat;
const googlechat = (
cfg as { googlechat?: { accounts?: Record<string, unknown> } }
).googlechat;
const resolvedAccountId =
accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
const useAccountPath = Boolean(googlechat?.accounts?.[resolvedAccountId]);
@ -107,9 +112,7 @@ export const googlechatPlugin: ProviderPlugin<ResolvedGoogleChatAccount> = {
if (!trimmed) {
return {
ok: false,
error: new Error(
"Delivering to Google Chat requires --to <spaceId>",
),
error: new Error("Delivering to Google Chat requires --to <spaceId>"),
};
}
return { ok: true, to: trimmed };

View File

@ -101,7 +101,8 @@ const CHAT_PROVIDER_META: Record<ChatProviderId, ChatProviderMeta> = {
selectionLabel: "Google Chat (Pub/Sub)",
docsPath: "/googlechat",
docsLabel: "googlechat",
blurb: "Google Workspace only; uses Pub/Sub for firewall-friendly operation.",
blurb:
"Google Workspace only; uses Pub/Sub for firewall-friendly operation.",
},
};