mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
fix: proper Telegram/WhatsApp channel setup using config + pairing (#2605)
Telegram is a built-in channel, not a plugin. Replace broken `openclaw plugins enable telegram` (OOM) and `openclaw channels add` (doesn't exist) with proper setup: - Write channel config (botToken, dmPolicy: pairing, groups) directly into the atomic JSON config file during setup - After gateway starts, prompt user to pair via `openclaw pairing approve <channel> <CODE>` - WhatsApp: QR scan via `openclaw channels login`, then pairing - Bump version to 0.17.16 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f1f8b53dde
commit
ca5fe851cd
3 changed files with 109 additions and 12 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.17.15",
|
||||
"version": "0.17.16",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { join } from "node:path";
|
|||
import { getTmpDir } from "./paths";
|
||||
import { asyncTryCatch, asyncTryCatchIf, isOperationalError, tryCatchIf } from "./result.js";
|
||||
import { getErrorMessage } from "./type-guards";
|
||||
import { Err, jsonEscape, logError, logInfo, logStep, logWarn, Ok, shellQuote, withRetry } from "./ui";
|
||||
import { Err, jsonEscape, logError, logInfo, logStep, logWarn, Ok, prompt, shellQuote, withRetry } from "./ui";
|
||||
|
||||
/**
|
||||
* Wrap an SSH-based async operation into a Result for use with withRetry.
|
||||
|
|
@ -324,10 +324,28 @@ async function setupOpenclawConfig(
|
|||
await installChromeBrowser(runner);
|
||||
}
|
||||
|
||||
// Prompt for Telegram bot token before building the config JSON so we can
|
||||
// include it in a single atomic write.
|
||||
let telegramBotToken = "";
|
||||
if (enabledSteps?.has("telegram")) {
|
||||
logStep("Setting up Telegram...");
|
||||
const envToken = process.env.TELEGRAM_BOT_TOKEN ?? process.env.SPAWN_TELEGRAM_BOT_TOKEN ?? "";
|
||||
if (!envToken) {
|
||||
logInfo("To get a bot token:");
|
||||
logInfo(" 1. Open Telegram and search for @BotFather");
|
||||
logInfo(" 2. Send /newbot and follow the prompts");
|
||||
logInfo(" 3. Copy the token (looks like 123456:ABC-DEF...)");
|
||||
logInfo(" Press Enter to skip if you don't have one yet.");
|
||||
}
|
||||
telegramBotToken = (envToken || (await prompt("Telegram bot token: "))).trim();
|
||||
if (!telegramBotToken) {
|
||||
logInfo("No token entered — set up Telegram via the web dashboard after launch");
|
||||
}
|
||||
}
|
||||
|
||||
const gatewayToken = token ?? crypto.randomUUID().replace(/-/g, "");
|
||||
|
||||
// Build config object for atomic JSON write — base config only (API key, gateway, model).
|
||||
// Channel setup (Telegram, WhatsApp) is handled by `openclaw onboard` in orchestrate.ts.
|
||||
// Build config object for atomic JSON write
|
||||
const configObj: Record<string, unknown> = {
|
||||
env: {
|
||||
OPENROUTER_API_KEY: apiKey,
|
||||
|
|
@ -347,6 +365,36 @@ async function setupOpenclawConfig(
|
|||
},
|
||||
};
|
||||
|
||||
// Channel config — written directly to the config file.
|
||||
// Both use dmPolicy "pairing" so users must approve new senders.
|
||||
const channels: Record<string, unknown> = {};
|
||||
|
||||
if (telegramBotToken) {
|
||||
channels.telegram = {
|
||||
enabled: true,
|
||||
botToken: telegramBotToken,
|
||||
dmPolicy: "pairing",
|
||||
groups: {
|
||||
"*": {
|
||||
requireMention: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
logInfo("Telegram bot token configured");
|
||||
}
|
||||
|
||||
if (enabledSteps?.has("whatsapp")) {
|
||||
channels.whatsapp = {
|
||||
dmPolicy: "pairing",
|
||||
groupPolicy: "allowlist",
|
||||
sendReadReceipts: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (Object.keys(channels).length > 0) {
|
||||
configObj.channels = channels;
|
||||
}
|
||||
|
||||
const config = JSON.stringify(configObj, null, 2);
|
||||
await uploadConfigFile(runner, config, "$HOME/.openclaw/openclaw.json");
|
||||
|
||||
|
|
@ -378,7 +426,7 @@ async function setupOpenclawConfig(
|
|||
logWarn("Gateway token re-assertion failed (non-fatal) — dashboard may show Unauthorized");
|
||||
}
|
||||
|
||||
// Channel setup (Telegram, WhatsApp) is handled by `openclaw onboard` in orchestrate.ts.
|
||||
// Channel pairing (Telegram/WhatsApp) happens in orchestrate.ts after the gateway starts.
|
||||
|
||||
// Write USER.md bootstrap file — guides users to the web dashboard for
|
||||
// visual tasks like WhatsApp QR code scanning that don't work in the TUI.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import {
|
|||
logWarn,
|
||||
openBrowser,
|
||||
prepareStdinForHandoff,
|
||||
prompt,
|
||||
shellQuote,
|
||||
validateModelId,
|
||||
withRetry,
|
||||
} from "./ui";
|
||||
|
|
@ -291,14 +293,61 @@ export async function runOrchestration(
|
|||
}
|
||||
}
|
||||
|
||||
// 11c. Channel setup — delegate to OpenClaw's built-in onboard wizard.
|
||||
// `openclaw onboard` interactively guides the user through Telegram, WhatsApp,
|
||||
// and other channel configuration. Runs after the gateway starts.
|
||||
if (enabledSteps?.has("telegram") || enabledSteps?.has("whatsapp")) {
|
||||
const ocPath = "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH";
|
||||
logStep("Running OpenClaw channel setup...");
|
||||
// 11c. Channel setup (runs after gateway is up)
|
||||
const ocPath = "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH";
|
||||
|
||||
if (enabledSteps?.has("telegram")) {
|
||||
logStep("Telegram pairing...");
|
||||
logInfo("DM your Telegram bot to get a pairing code, then enter it below.");
|
||||
logInfo("Waiting for pairing code...");
|
||||
process.stderr.write("\n");
|
||||
const pairingCode = (await prompt("Telegram pairing code: ")).trim();
|
||||
if (pairingCode) {
|
||||
const escaped = shellQuote(pairingCode);
|
||||
const result = await asyncTryCatchIf(isOperationalError, () =>
|
||||
cloud.runner.runServer(
|
||||
`source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw pairing approve telegram ${escaped}`,
|
||||
),
|
||||
);
|
||||
if (result.ok) {
|
||||
logInfo("Telegram paired successfully");
|
||||
} else {
|
||||
logWarn("Pairing failed — you can pair later via: openclaw pairing approve telegram <CODE>");
|
||||
}
|
||||
} else {
|
||||
logInfo("No code entered — pair later via: openclaw pairing approve telegram <CODE>");
|
||||
}
|
||||
}
|
||||
|
||||
if (enabledSteps?.has("whatsapp")) {
|
||||
// Step 1: QR code scan to link the WhatsApp device
|
||||
logStep("Linking WhatsApp — scan the QR code with your phone...");
|
||||
logInfo("Open WhatsApp > Settings > Linked Devices > Link a Device");
|
||||
process.stderr.write("\n");
|
||||
const whatsappCmd = `source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw channels login --channel whatsapp`;
|
||||
prepareStdinForHandoff();
|
||||
await cloud.interactiveSession(`source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw onboard`);
|
||||
await cloud.interactiveSession(whatsappCmd);
|
||||
|
||||
// Step 2: Pairing — approve your own number so the bot responds to you
|
||||
logStep("WhatsApp pairing...");
|
||||
logInfo("Send a message to your bot on WhatsApp to get a pairing code, then enter it below.");
|
||||
process.stderr.write("\n");
|
||||
const pairingCode = (await prompt("WhatsApp pairing code: ")).trim();
|
||||
if (pairingCode) {
|
||||
const escaped = shellQuote(pairingCode);
|
||||
const result = await asyncTryCatchIf(isOperationalError, () =>
|
||||
cloud.runner.runServer(
|
||||
`source ~/.spawnrc 2>/dev/null; ${ocPath}; openclaw pairing approve whatsapp ${escaped}`,
|
||||
),
|
||||
);
|
||||
if (result.ok) {
|
||||
logInfo("WhatsApp paired successfully");
|
||||
} else {
|
||||
logWarn("Pairing failed — you can pair later via: openclaw pairing approve whatsapp <CODE>");
|
||||
}
|
||||
} else {
|
||||
logInfo("No code entered — pair later via: openclaw pairing approve whatsapp <CODE>");
|
||||
}
|
||||
}
|
||||
|
||||
// 11d. Agent-specific pre-launch tip (e.g. channel setup ordering hint)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue