Download extensions/googlechat/src/monitor-webhook.ts from Darochin/openskynet: direct link, hf CLI and curl.
- Browser
- Download file 6.45 kB
-
https://huggingface.co/Darochin/openskynet/resolve/main/extensions/googlechat/src/monitor-webhook.ts
- Command line
-
hf download hf://Darochin/openskynet/extensions/googlechat/src/monitor-webhook.ts
-
curl -L -o monitor-webhook.ts https://huggingface.co/Darochin/openskynet/resolve/main/extensions/googlechat/src/monitor-webhook.ts
6.45 kB
| import type { IncomingMessage, ServerResponse } from "node:http"; | |
| import { | |
| readJsonWebhookBodyOrReject, | |
| resolveWebhookTargetWithAuthOrReject, | |
| withResolvedWebhookRequestPipeline, | |
| type WebhookInFlightLimiter, | |
| } from "openclaw/plugin-sdk/googlechat"; | |
| import { verifyGoogleChatRequest } from "./auth.js"; | |
| import type { WebhookTarget } from "./monitor-types.js"; | |
| import type { | |
| GoogleChatEvent, | |
| GoogleChatMessage, | |
| GoogleChatSpace, | |
| GoogleChatUser, | |
| } from "./types.js"; | |
| function extractBearerToken(header: unknown): string { | |
| const authHeader = Array.isArray(header) ? String(header[0] ?? "") : String(header ?? ""); | |
| return authHeader.toLowerCase().startsWith("bearer ") | |
| ? authHeader.slice("bearer ".length).trim() | |
| : ""; | |
| } | |
| type ParsedGoogleChatInboundPayload = | |
| | { ok: true; event: GoogleChatEvent; addOnBearerToken: string } | |
| | { ok: false }; | |
| type ParsedGoogleChatInboundSuccess = Extract<ParsedGoogleChatInboundPayload, { ok: true }>; | |
| function parseGoogleChatInboundPayload( | |
| raw: unknown, | |
| res: ServerResponse, | |
| ): ParsedGoogleChatInboundPayload { | |
| if (!raw || typeof raw !== "object" || Array.isArray(raw)) { | |
| res.statusCode = 400; | |
| res.end("invalid payload"); | |
| return { ok: false }; | |
| } | |
| let eventPayload = raw; | |
| let addOnBearerToken = ""; | |
| // Transform Google Workspace Add-on format to standard Chat API format. | |
| const rawObj = raw as { | |
| commonEventObject?: { hostApp?: string }; | |
| chat?: { | |
| messagePayload?: { space?: GoogleChatSpace; message?: GoogleChatMessage }; | |
| user?: GoogleChatUser; | |
| eventTime?: string; | |
| }; | |
| authorizationEventObject?: { systemIdToken?: string }; | |
| }; | |
| if (rawObj.commonEventObject?.hostApp === "CHAT" && rawObj.chat?.messagePayload) { | |
| const chat = rawObj.chat; | |
| const messagePayload = chat.messagePayload; | |
| eventPayload = { | |
| type: "MESSAGE", | |
| space: messagePayload?.space, | |
| message: messagePayload?.message, | |
| user: chat.user, | |
| eventTime: chat.eventTime, | |
| }; | |
| addOnBearerToken = String(rawObj.authorizationEventObject?.systemIdToken ?? "").trim(); | |
| } | |
| const event = eventPayload as GoogleChatEvent; | |
| const eventType = event.type ?? (eventPayload as { eventType?: string }).eventType; | |
| if (typeof eventType !== "string") { | |
| res.statusCode = 400; | |
| res.end("invalid payload"); | |
| return { ok: false }; | |
| } | |
| if (!event.space || typeof event.space !== "object" || Array.isArray(event.space)) { | |
| res.statusCode = 400; | |
| res.end("invalid payload"); | |
| return { ok: false }; | |
| } | |
| if (eventType === "MESSAGE") { | |
| if (!event.message || typeof event.message !== "object" || Array.isArray(event.message)) { | |
| res.statusCode = 400; | |
| res.end("invalid payload"); | |
| return { ok: false }; | |
| } | |
| } | |
| return { ok: true, event, addOnBearerToken }; | |
| } | |
| export function createGoogleChatWebhookRequestHandler(params: { | |
| webhookTargets: Map<string, WebhookTarget[]>; | |
| webhookInFlightLimiter: WebhookInFlightLimiter; | |
| processEvent: (event: GoogleChatEvent, target: WebhookTarget) => Promise<void>; | |
| }): (req: IncomingMessage, res: ServerResponse) => Promise<boolean> { | |
| return async (req: IncomingMessage, res: ServerResponse): Promise<boolean> => { | |
| return await withResolvedWebhookRequestPipeline({ | |
| req, | |
| res, | |
| targetsByPath: params.webhookTargets, | |
| allowMethods: ["POST"], | |
| requireJsonContentType: true, | |
| inFlightLimiter: params.webhookInFlightLimiter, | |
| handle: async ({ targets }) => { | |
| const headerBearer = extractBearerToken(req.headers.authorization); | |
| let selectedTarget: WebhookTarget | null = null; | |
| let parsedEvent: GoogleChatEvent | null = null; | |
| const readAndParseEvent = async ( | |
| profile: "pre-auth" | "post-auth", | |
| ): Promise<ParsedGoogleChatInboundSuccess | null> => { | |
| const body = await readJsonWebhookBodyOrReject({ | |
| req, | |
| res, | |
| profile, | |
| emptyObjectOnEmpty: false, | |
| invalidJsonMessage: "invalid payload", | |
| }); | |
| if (!body.ok) { | |
| return null; | |
| } | |
| const parsed = parseGoogleChatInboundPayload(body.value, res); | |
| return parsed.ok ? parsed : null; | |
| }; | |
| if (headerBearer) { | |
| selectedTarget = await resolveWebhookTargetWithAuthOrReject({ | |
| targets, | |
| res, | |
| isMatch: async (target) => { | |
| const verification = await verifyGoogleChatRequest({ | |
| bearer: headerBearer, | |
| audienceType: target.audienceType, | |
| audience: target.audience, | |
| }); | |
| return verification.ok; | |
| }, | |
| }); | |
| if (!selectedTarget) { | |
| return true; | |
| } | |
| const parsed = await readAndParseEvent("post-auth"); | |
| if (!parsed) { | |
| return true; | |
| } | |
| parsedEvent = parsed.event; | |
| } else { | |
| const parsed = await readAndParseEvent("pre-auth"); | |
| if (!parsed) { | |
| return true; | |
| } | |
| parsedEvent = parsed.event; | |
| if (!parsed.addOnBearerToken) { | |
| res.statusCode = 401; | |
| res.end("unauthorized"); | |
| return true; | |
| } | |
| selectedTarget = await resolveWebhookTargetWithAuthOrReject({ | |
| targets, | |
| res, | |
| isMatch: async (target) => { | |
| const verification = await verifyGoogleChatRequest({ | |
| bearer: parsed.addOnBearerToken, | |
| audienceType: target.audienceType, | |
| audience: target.audience, | |
| }); | |
| return verification.ok; | |
| }, | |
| }); | |
| if (!selectedTarget) { | |
| return true; | |
| } | |
| } | |
| if (!selectedTarget || !parsedEvent) { | |
| res.statusCode = 401; | |
| res.end("unauthorized"); | |
| return true; | |
| } | |
| const dispatchTarget = selectedTarget; | |
| dispatchTarget.statusSink?.({ lastInboundAt: Date.now() }); | |
| params.processEvent(parsedEvent, dispatchTarget).catch((err) => { | |
| dispatchTarget.runtime.error?.( | |
| `[${dispatchTarget.account.accountId}] Google Chat webhook failed: ${String(err)}`, | |
| ); | |
| }); | |
| res.statusCode = 200; | |
| res.setHeader("Content-Type", "application/json"); | |
| res.end("{}"); | |
| return true; | |
| }, | |
| }); | |
| }; | |
| } | |