154 lines
6.0 KiB
JavaScript
154 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
||
'use strict';
|
||
|
||
/**
|
||
* 创建/更新 WhatsApp inbox,并配置「新会话 → AI Assistant」automation。
|
||
*
|
||
* .env 需要:
|
||
* WA_PHONE_NUMBER_ID / WA_WABA_ID / WA_ACCESS_TOKEN / WA_APP_SECRET
|
||
* WA_VERIFY_TOKEN / WA_INBOX_NAME(可选)
|
||
* WA_API_VERSION(可选,默认 v26.0)
|
||
*
|
||
* 前提:LibreDesk app.root_url 已是公网 HTTPS(scripts/public-https.sh),
|
||
* 否则 Meta 无法回调,自动订阅也会失败。
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const ROOT = path.resolve(__dirname, '..');
|
||
const RULE_PREFIX = 'Assign WhatsApp conversations to AI Assistant';
|
||
|
||
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;
|
||
}
|
||
|
||
function createClient(baseURL) {
|
||
const cookies = new Map();
|
||
let csrf = '';
|
||
const cookieHeader = () => [...cookies.entries()].map(([k, v]) => `${k}=${v}`).join('; ');
|
||
function store(res) {
|
||
const list = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : [res.headers.get('set-cookie')].filter(Boolean);
|
||
for (const c of list) {
|
||
const [pair] = c.split(';');
|
||
const eq = pair.indexOf('=');
|
||
if (eq > 0) {
|
||
const k = pair.slice(0, eq).trim();
|
||
const v = pair.slice(eq + 1).trim();
|
||
cookies.set(k, v);
|
||
if (k === 'csrf_token') csrf = v;
|
||
}
|
||
}
|
||
}
|
||
async function req(method, p, body) {
|
||
const headers = { Accept: 'application/json' };
|
||
if (cookies.size) headers.Cookie = cookieHeader();
|
||
if (body !== undefined) headers['Content-Type'] = 'application/json';
|
||
if (csrf && method !== 'GET') headers['X-CSRFTOKEN'] = csrf;
|
||
const res = await fetch(baseURL + p, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
|
||
store(res);
|
||
const text = await res.text();
|
||
let json = null;
|
||
try { json = text ? JSON.parse(text) : null; } catch { /* ignore */ }
|
||
if (!res.ok) throw new Error(`${method} ${p} -> HTTP ${res.status}: ${(json && (json.message || json.error)) || text.slice(0, 300)}`);
|
||
return json;
|
||
}
|
||
return { get: (p) => req('GET', p), post: (p, b) => req('POST', p, b), put: (p, b) => req('PUT', p, b) };
|
||
}
|
||
|
||
const data = (j) => (j && j.data !== undefined ? j.data : j);
|
||
|
||
async function main() {
|
||
const env = loadEnv(path.join(ROOT, '.env'));
|
||
const base = (env.LIBREDESK_BASE_URL || 'http://192.168.31.61:9000').replace(/\/+$/, '');
|
||
const required = ['WA_PHONE_NUMBER_ID', 'WA_WABA_ID', 'WA_ACCESS_TOKEN', 'WA_APP_SECRET', 'WA_VERIFY_TOKEN'];
|
||
const missing = required.filter((k) => !env[k]);
|
||
if (missing.length) throw new Error(`.env 缺少: ${missing.join(', ')}`);
|
||
|
||
const c = createClient(base);
|
||
await c.post('/api/v1/auth/login', { email: env.LIBREDESK_SYSTEM_EMAIL || 'System', password: env.LIBREDESK_SYSTEM_USER_PASSWORD });
|
||
console.log('登录成功');
|
||
|
||
const inboxName = env.WA_INBOX_NAME || 'WhatsApp US';
|
||
const payload = {
|
||
name: inboxName,
|
||
channel: 'whatsapp',
|
||
enabled: true,
|
||
config: {
|
||
phone_number_id: env.WA_PHONE_NUMBER_ID,
|
||
waba_id: env.WA_WABA_ID,
|
||
access_token: env.WA_ACCESS_TOKEN,
|
||
app_secret: env.WA_APP_SECRET,
|
||
webhook_verify_token: env.WA_VERIFY_TOKEN,
|
||
api_version: env.WA_API_VERSION || 'v26.0',
|
||
},
|
||
};
|
||
|
||
const inboxes = data(await c.get('/api/v1/inboxes')) || [];
|
||
let inbox = inboxes.find((i) => i.channel === 'whatsapp' && i.name === inboxName);
|
||
if (!inbox) {
|
||
inbox = data(await c.post('/api/v1/inboxes', payload));
|
||
console.log(`创建 WhatsApp Inbox #${inbox.id}`);
|
||
} else {
|
||
inbox = data(await c.put(`/api/v1/inboxes/${inbox.id}`, payload));
|
||
console.log(`更新 WhatsApp Inbox #${inbox.id}`);
|
||
}
|
||
|
||
// automation: WhatsApp inbox 新会话 -> AI Assistant
|
||
const assistants = data(await c.get('/api/v1/ai/assistants')) || [];
|
||
const assistant = assistants.find((a) => a.name === (env.AI_ASSISTANT_NAME || 'Yehwang Assistant'));
|
||
if (!assistant) throw new Error('未找到 AI Assistant,先运行 scripts/setup-defaults.js');
|
||
|
||
const ruleName = `${RULE_PREFIX} #${inbox.id}`;
|
||
const rulesRaw = data(await c.get('/api/v1/automations/rules?type=new_conversation')) || [];
|
||
const rules = Array.isArray(rulesRaw) ? rulesRaw : (rulesRaw.rules || []);
|
||
const rulePayload = {
|
||
name: ruleName,
|
||
description: 'Route new WhatsApp conversations to the AI assistant',
|
||
type: 'new_conversation',
|
||
events: [],
|
||
enabled: true,
|
||
weight: 0,
|
||
rules: [{
|
||
type: 'new_conversation',
|
||
execution_mode: 'all',
|
||
event: [],
|
||
group_operator: 'AND',
|
||
groups: [{
|
||
logical_op: 'AND',
|
||
rules: [{ field: 'inbox', field_type: 'conversation', operator: 'equals', value: String(inbox.id), case_sensitive_match: false }],
|
||
}],
|
||
actions: [{ type: 'assign_user', value: [String(assistant.user_id)], display_value: [assistant.name] }],
|
||
}],
|
||
};
|
||
const existing = rules.find((r) => r.name === ruleName);
|
||
if (!existing) {
|
||
const rule = data(await c.post('/api/v1/automations/rules', rulePayload));
|
||
console.log(`创建 Automation #${rule.id}(inbox ${inbox.id} → user ${assistant.user_id})`);
|
||
} else {
|
||
const rule = data(await c.put(`/api/v1/automations/rules/${existing.id}`, rulePayload));
|
||
console.log(`更新 Automation #${rule.id}(inbox ${inbox.id} → user ${assistant.user_id})`);
|
||
}
|
||
|
||
const root = env.PUBLIC_BASE_URL || '<app.root_url>';
|
||
console.log('\nWhatsApp Inbox 配置完成');
|
||
console.log(`Inbox ID: ${inbox.id}`);
|
||
console.log(`Callback URL: ${root}/webhooks/whatsapp/${inbox.id}`);
|
||
console.log(`Verify Token: ${env.WA_VERIFY_TOKEN}`);
|
||
console.log('Meta -> WhatsApp -> Configuration -> Webhook 里填上面两项,订阅 messages');
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error('[add-whatsapp-inbox]', err.message);
|
||
process.exit(1);
|
||
});
|