#!/usr/bin/env node 'use strict'; /** * 配置 Meta App 级 WhatsApp webhook(cloudflared URL 变化后需要重跑)。 * 用法:node scripts/set-wa-webhook.js [https://public-host] * 依赖 .env: WA_APP_ID / WA_APP_SECRET / WA_VERIFY_TOKEN / WA_INBOX_ID */ const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); function loadEnv(file) { const out = {}; for (const line of fs.readFileSync(file, 'utf8').split('\n')) { const t = line.trim(); if (!t || t.startsWith('#')) continue; const eq = t.indexOf('='); if (eq < 0) continue; let v = t.slice(eq + 1).trim(); if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1); out[t.slice(0, eq).trim()] = v; } return out; } async function main() { const env = loadEnv(path.join(ROOT, '.env')); const publicURL = String(process.argv[2] || env.PUBLIC_BASE_URL || '').trim().replace(/\/+$/, ''); const appId = env.WA_APP_ID; const appSecret = env.WA_APP_SECRET; const verify = env.WA_VERIFY_TOKEN; const inboxId = env.WA_INBOX_ID; const missing = ['WA_APP_ID', 'WA_APP_SECRET', 'WA_VERIFY_TOKEN', 'WA_INBOX_ID'].filter((k) => !env[k]); if (missing.length) throw new Error(`.env 缺少: ${missing.join(', ')}`); if (!/^https:\/\//.test(publicURL)) throw new Error('需要公网 HTTPS 地址'); const callback = `${publicURL}/webhooks/whatsapp/${inboxId}`; const appToken = `${appId}|${appSecret}`; const body = new URLSearchParams({ object: 'whatsapp_business_account', callback_url: callback, verify_token: verify, fields: 'messages', access_token: appToken, }); const res = await fetch(`https://graph.facebook.com/v26.0/${appId}/subscriptions`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body, }); const text = await res.text(); if (!res.ok) throw new Error(`Meta webhook 配置失败: HTTP ${res.status} ${text.slice(0, 300)}`); console.log(`Meta WhatsApp webhook 已更新: ${callback}`); } main().catch((err) => { console.error('[set-wa-webhook]', err.message); process.exit(1); });