feat: async GChat webhook with Tailscale Funnel support
- Change from execSync to exec for non-blocking AI calls - Acknowledge messages immediately, send response via Chat API - Add 5-minute timeout (vs 25s) since async doesn't block - Add start-gchat-tailscale.sh launcher for Tailscale Funnel - Remove ngrok dependency Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
26a7b915fb
commit
42d26a364a
@ -1,11 +1,27 @@
|
|||||||
#!/usr/bin/env npx tsx
|
#!/usr/bin/env npx tsx
|
||||||
import { execSync } from "node:child_process";
|
import { exec } from "node:child_process";
|
||||||
import express, { type Request, type Response } from "express";
|
import express, { type Request, type Response } from "express";
|
||||||
|
|
||||||
const PORT = 18792;
|
const PORT = 18792;
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Path to the gchat sender script
|
||||||
|
const GCHAT_SENDER = "/Users/justinmassa/chief-of-staff/scripts/gchat_sender.py";
|
||||||
|
const PYTHON = "/Users/justinmassa/chief-of-staff/.venv/bin/python";
|
||||||
|
|
||||||
|
// Send message via Chat API (async, no timeout concerns)
|
||||||
|
function sendChatMessage(spaceId: string, text: string): void {
|
||||||
|
const escapedText = text.replace(/'/g, "'\\''").replace(/\n/g, "\\n");
|
||||||
|
exec(
|
||||||
|
`${PYTHON} -c "import sys; sys.path.insert(0, '/Users/justinmassa/chief-of-staff/scripts'); from gchat_sender import send_message; send_message('${escapedText}', '${spaceId}')"`,
|
||||||
|
{ timeout: 30000 },
|
||||||
|
(err) => {
|
||||||
|
if (err) console.error("[googlechat] Failed to send response:", err.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
app.get("/health", (_req: Request, res: Response) => {
|
app.get("/health", (_req: Request, res: Response) => {
|
||||||
res.json({ ok: true, provider: "googlechat" });
|
res.json({ ok: true, provider: "googlechat" });
|
||||||
@ -17,15 +33,10 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
|
|||||||
const event = req.body;
|
const event = req.body;
|
||||||
const chat = event.chat || {};
|
const chat = event.chat || {};
|
||||||
|
|
||||||
// Detect event type from payload structure
|
|
||||||
const isAddedToSpace = !!chat.addedToSpacePayload;
|
const isAddedToSpace = !!chat.addedToSpacePayload;
|
||||||
const isMessage = !!chat.messagePayload;
|
const isMessage = !!chat.messagePayload;
|
||||||
|
|
||||||
const eventType = isAddedToSpace
|
const eventType = isAddedToSpace ? "ADDED_TO_SPACE" : isMessage ? "MESSAGE" : "UNKNOWN";
|
||||||
? "ADDED_TO_SPACE"
|
|
||||||
: isMessage
|
|
||||||
? "MESSAGE"
|
|
||||||
: "UNKNOWN";
|
|
||||||
console.log(`[googlechat] Received event: ${eventType}`);
|
console.log(`[googlechat] Received event: ${eventType}`);
|
||||||
|
|
||||||
if (isAddedToSpace) {
|
if (isAddedToSpace) {
|
||||||
@ -35,7 +46,7 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
|
|||||||
chatDataAction: {
|
chatDataAction: {
|
||||||
createMessageAction: {
|
createMessageAction: {
|
||||||
message: {
|
message: {
|
||||||
text: `Hello ${user}! I'm Clawdbot, your AI assistant. Send me a message and I'll respond!`,
|
text: `Hello ${user}! I'm Clawdette, your AI assistant. Send me a message and I'll respond!`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -52,48 +63,36 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
console.log(`[googlechat] Message from ${senderName}: ${text}`);
|
console.log(`[googlechat] Message from ${senderName}: ${text}`);
|
||||||
|
|
||||||
let responseText: string;
|
// Acknowledge immediately - no blocking!
|
||||||
try {
|
res.json({});
|
||||||
// Use clawdbot CLI to get AI response
|
|
||||||
// Escape the text for shell
|
|
||||||
const escapedText = text.replace(/'/g, "'\\''");
|
|
||||||
const sessionId = `googlechat:${spaceId}`;
|
|
||||||
const result = execSync(
|
|
||||||
`clawdbot agent --message '${escapedText}' --session-id '${sessionId}' --local`,
|
|
||||||
{
|
|
||||||
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: 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.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
// Process AI response asynchronously (can take minutes, that's fine)
|
||||||
hostAppDataAction: {
|
const escapedText = text.replace(/'/g, "'\\''");
|
||||||
chatDataAction: {
|
const sessionId = `googlechat:${spaceId}`;
|
||||||
createMessageAction: {
|
|
||||||
message: {
|
console.log(`[googlechat] Processing async for space ${spaceId}...`);
|
||||||
text: responseText,
|
|
||||||
},
|
exec(
|
||||||
},
|
`clawdbot agent --message '${escapedText}' --session-id '${sessionId}' --local`,
|
||||||
},
|
{
|
||||||
|
timeout: 300000, // 5 minute timeout
|
||||||
|
maxBuffer: 1024 * 1024,
|
||||||
},
|
},
|
||||||
});
|
(err, stdout, stderr) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`[googlechat] AI error:`, err.message);
|
||||||
|
sendChatMessage(spaceId, "Sorry, I encountered an error processing your message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseText = stdout.trim() || "I processed your message but have no response.";
|
||||||
|
console.log(`[googlechat] AI Response (${responseText.length} chars): ${responseText.slice(0, 100)}...`);
|
||||||
|
|
||||||
|
// Send response via Chat API
|
||||||
|
sendChatMessage(spaceId, responseText);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +105,5 @@ app.post("/webhook/googlechat", async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`[googlechat] Webhook server running on port ${PORT}`);
|
console.log(`[googlechat] Webhook server running on port ${PORT}`);
|
||||||
console.log(
|
console.log(`[googlechat] Mode: ASYNC (responds via Chat API, no timeout issues)`);
|
||||||
`[googlechat] Local: http://localhost:${PORT}/webhook/googlechat`,
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`[googlechat] Use ngrok URL + /webhook/googlechat for Google Chat config`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|||||||
26
start-gchat-tailscale.sh
Executable file
26
start-gchat-tailscale.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start Clawdbot Google Chat with Tailscale Funnel
|
||||||
|
# Runs at login via LaunchAgent
|
||||||
|
|
||||||
|
# Set PATH for launchd (which has minimal environment)
|
||||||
|
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$HOME/Library/pnpm:$PATH"
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
LOG_FILE="/tmp/googlechat-webhook.log"
|
||||||
|
|
||||||
|
echo "$(date): Starting Clawdbot GChat..." >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Kill any existing webhook server
|
||||||
|
pkill -f "run-webhook" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Start webhook server
|
||||||
|
nohup npx tsx src/googlechat/run-webhook.ts >> "$LOG_FILE" 2>&1 &
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Ensure Tailscale Funnel is running on the right port
|
||||||
|
tailscale funnel --bg 18792 >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
echo "$(date): GChat webhook started on port 18792" >> "$LOG_FILE"
|
||||||
|
echo "$(date): Funnel: https://justins-laptop.tail73ba30.ts.net/webhook/googlechat" >> "$LOG_FILE"
|
||||||
Loading…
Reference in New Issue
Block a user