plazbot-cli 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -111
- package/dist/cli.js +26 -11
- package/dist/commands/agent/ai-config.js +264 -63
- package/dist/commands/agent/chat.js +16 -11
- package/dist/commands/agent/copy.js +49 -13
- package/dist/commands/agent/create.js +62 -17
- package/dist/commands/agent/delete.js +50 -39
- package/dist/commands/agent/enable-widget.js +47 -27
- package/dist/commands/agent/export.js +180 -91
- package/dist/commands/agent/files.js +155 -73
- package/dist/commands/agent/get.js +25 -19
- package/dist/commands/agent/index.js +12 -10
- package/dist/commands/agent/list.js +20 -11
- package/dist/commands/agent/monitor.js +103 -57
- package/dist/commands/agent/on-message.js +55 -32
- package/dist/commands/agent/set.js +230 -82
- package/dist/commands/agent/templates.js +259 -156
- package/dist/commands/agent/tools.js +356 -123
- package/dist/commands/agent/update.js +69 -28
- package/dist/commands/agent/validate.js +72 -40
- package/dist/commands/agent/wizard.js +240 -329
- package/dist/commands/auth/browser-login.js +37 -31
- package/dist/commands/auth/login.js +49 -45
- package/dist/commands/auth/logout.js +20 -11
- package/dist/commands/auth/status.js +36 -12
- package/dist/commands/auth/workspace.js +61 -27
- package/dist/commands/portal/add-agent.js +106 -45
- package/dist/commands/portal/add-link.js +25 -28
- package/dist/commands/portal/clear-links.js +39 -19
- package/dist/commands/portal/create.js +66 -37
- package/dist/commands/portal/delete.js +40 -45
- package/dist/commands/portal/get.js +21 -54
- package/dist/commands/portal/index.js +12 -12
- package/dist/commands/portal/list.js +34 -52
- package/dist/commands/portal/shared.js +92 -0
- package/dist/commands/portal/update.js +100 -70
- package/dist/commands/whatsapp/broadcast.js +169 -77
- package/dist/commands/whatsapp/channels.js +167 -65
- package/dist/commands/whatsapp/chat.js +45 -46
- package/dist/commands/whatsapp/connect-flow.js +16 -15
- package/dist/commands/whatsapp/connect.js +36 -21
- package/dist/commands/whatsapp/delete-webhook.js +42 -25
- package/dist/commands/whatsapp/index.js +15 -16
- package/dist/commands/whatsapp/register-webhook.js +43 -26
- package/dist/commands/whatsapp/send-template.js +62 -61
- package/dist/commands/whatsapp/send.js +46 -33
- package/dist/commands/whatsapp/setup.js +62 -52
- package/dist/commands/whatsapp/shared.js +75 -0
- package/dist/commands/whatsapp/widget.js +82 -45
- package/dist/commands/workers/deploy.js +80 -58
- package/dist/commands/workers/index.js +8 -8
- package/dist/commands/workers/list.js +42 -20
- package/dist/commands/workers/logs.js +139 -36
- package/dist/commands/workers/remove.js +45 -47
- package/dist/commands/workers/secret.js +159 -83
- package/dist/commands/workers/test.js +108 -60
- package/dist/schemas/agent.config.schema.json +159 -8
- package/dist/studio/api/sseClient.js +1 -1
- package/dist/studio/api/studioApi.js +8 -0
- package/dist/studio/components/App.js +12 -0
- package/dist/studio/components/Footer.js +1 -1
- package/dist/studio/index.js +74 -14
- package/dist/studio/runOneShot.js +62 -23
- package/dist/studio/runRepl.js +5 -5
- package/dist/studio/slash/handlers.js +11 -8
- package/dist/studio/state/store.js +1 -1
- package/dist/utils/agent-errors.js +14 -11
- package/dist/utils/agent-patch.js +60 -0
- package/dist/utils/banner.js +9 -5
- package/dist/utils/confirm.js +31 -0
- package/dist/utils/credentials.js +13 -3
- package/dist/utils/logger.js +13 -10
- package/dist/utils/output.js +78 -0
- package/dist/utils/phone.js +22 -0
- package/dist/utils/ui.js +14 -11
- package/package.json +3 -3
|
@@ -1,75 +1,74 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
4
|
-
import { logger } from '../../utils/logger.js';
|
|
5
|
-
import { addExamples } from '../../utils/help.js';
|
|
2
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
6
3
|
import { createSpinner } from '../../utils/ui.js';
|
|
4
|
+
import { fail, describeError } from '../../utils/output.js';
|
|
5
|
+
import { requireInteractive } from '../../utils/confirm.js';
|
|
6
|
+
import { phoneOption, normalizePhone } from '../../utils/phone.js';
|
|
7
|
+
import { ensureApiSuccess, getMessageClient } from './shared.js';
|
|
7
8
|
import readline from 'readline';
|
|
8
9
|
import chalk from 'chalk';
|
|
10
|
+
const green = chalk.hex('#25D366');
|
|
9
11
|
export const whatsappChatCommand = new Command('chat')
|
|
10
|
-
.description('
|
|
11
|
-
.
|
|
12
|
+
.description('Send messages to a WhatsApp number from an interactive prompt (send-only)')
|
|
13
|
+
.addOption(phoneOption('Recipient number in international format, e.g. +51912345678'))
|
|
14
|
+
.argument('[phone]', 'Recipient number (legacy positional form of --phone)')
|
|
12
15
|
.option('--dev', 'Use development environment', false)
|
|
13
|
-
.action(async (
|
|
16
|
+
.action(async (positional, options) => {
|
|
14
17
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
...(options.dev && { customUrl: "http://localhost:5090" })
|
|
21
|
-
});
|
|
22
|
-
const rl = readline.createInterface({
|
|
23
|
-
input: process.stdin,
|
|
24
|
-
output: process.stdout,
|
|
25
|
-
});
|
|
26
|
-
console.clear();
|
|
18
|
+
const phone = normalizePhone(options.phone ?? positional);
|
|
19
|
+
requireInteractive('WhatsApp chat', `Send a single message instead: plazbot whatsapp send-message -p +${phone} -m "Hello"`);
|
|
20
|
+
const messageClient = await getMessageClient(options.dev);
|
|
21
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
22
|
+
const line = (text) => green(' │') + text.padEnd(60) + green('│');
|
|
27
23
|
console.log();
|
|
28
|
-
console.log(
|
|
29
|
-
console.log(
|
|
30
|
-
console.log(
|
|
31
|
-
console.log(
|
|
32
|
-
console.log(
|
|
24
|
+
console.log(green(' ┌' + '─'.repeat(50) + '┐'));
|
|
25
|
+
console.log(line(chalk.bold(' WhatsApp chat (send-only)')));
|
|
26
|
+
console.log(line(chalk.gray(` To: +${phone}`)));
|
|
27
|
+
console.log(line(chalk.gray(' Type /exit to quit')));
|
|
28
|
+
console.log(green(' └' + '─'.repeat(50) + '┘'));
|
|
29
|
+
console.log(chalk.gray(" Replies from the contact are not shown here; see them in the Plazbot inbox."));
|
|
30
|
+
console.log(chalk.gray(" Free-form text only works within 24h of the contact's last message."));
|
|
33
31
|
console.log();
|
|
32
|
+
rl.on('close', () => process.exit(0));
|
|
34
33
|
const ask = () => {
|
|
35
|
-
rl.question(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return;
|
|
39
|
-
|
|
40
|
-
if (message.toLowerCase() === '/exit') {
|
|
34
|
+
rl.question(green(' > '), async (message) => {
|
|
35
|
+
const text = message.trim();
|
|
36
|
+
if (!text)
|
|
37
|
+
return ask();
|
|
38
|
+
if (text.toLowerCase() === '/exit' || text.toLowerCase() === '/quit') {
|
|
41
39
|
console.log(chalk.gray('\n Chat ended.\n'));
|
|
42
40
|
rl.close();
|
|
43
41
|
return;
|
|
44
42
|
}
|
|
43
|
+
const spinner = createSpinner('Sending...').start();
|
|
45
44
|
try {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
await messageClient.onWhatsappMessage({
|
|
49
|
-
message,
|
|
50
|
-
to: phone,
|
|
51
|
-
});
|
|
45
|
+
const response = await messageClient.onWhatsappMessage({ message: text, to: phone });
|
|
46
|
+
ensureApiSuccess(response, 'The message was not sent', 'Outside the 24h window use: plazbot whatsapp send-template');
|
|
52
47
|
spinner.succeed('Sent');
|
|
53
|
-
ask();
|
|
54
48
|
}
|
|
55
49
|
catch (error) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
const { message: msg, hint } = describeError(error);
|
|
51
|
+
spinner.fail(msg);
|
|
52
|
+
if (hint)
|
|
53
|
+
console.log(chalk.gray(` ${hint}`));
|
|
59
54
|
}
|
|
55
|
+
ask();
|
|
60
56
|
});
|
|
61
57
|
};
|
|
62
58
|
ask();
|
|
63
59
|
}
|
|
64
60
|
catch (error) {
|
|
65
|
-
|
|
66
|
-
logger.error(message);
|
|
67
|
-
process.exit(1);
|
|
61
|
+
fail(error);
|
|
68
62
|
}
|
|
69
63
|
});
|
|
70
64
|
addExamples(whatsappChatCommand, [
|
|
71
|
-
{ description: 'Open
|
|
65
|
+
{ description: 'Open a send-only chat with a number',
|
|
66
|
+
command: 'plazbot whatsapp chat -p +51912345678' },
|
|
67
|
+
{ description: 'Legacy positional form (still supported)',
|
|
72
68
|
command: 'plazbot whatsapp chat 51912345678' },
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
]);
|
|
70
|
+
addNotes(whatsappChatCommand, [
|
|
71
|
+
'Send-only: incoming replies are not shown in the terminal. Read them in the Plazbot inbox or via your webhook.',
|
|
72
|
+
"WhatsApp only allows free-form text within 24 hours of the contact's last message; outside it use send-template.",
|
|
73
|
+
'Needs an interactive terminal. For scripts use plazbot whatsapp send-message.',
|
|
75
74
|
]);
|
|
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { apiBaseUrl } from '../../utils/credentials.js';
|
|
5
5
|
import { openBrowser } from '../../utils/open-browser.js';
|
|
6
|
+
import { CliError } from '../../utils/output.js';
|
|
6
7
|
/**
|
|
7
8
|
* Lógica compartida de conexión de números de WhatsApp (connect y setup).
|
|
8
9
|
* Mismo flujo que el panel: link de onboarding co.plzb.link → el usuario completa
|
|
@@ -33,9 +34,9 @@ export async function fetchIntegrations(creds, dev = false) {
|
|
|
33
34
|
}
|
|
34
35
|
catch (error) {
|
|
35
36
|
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
|
36
|
-
throw new
|
|
37
|
+
throw new CliError('Your session is invalid or was revoked.', 'unauthorized', "Run 'plazbot login' to sign in again.");
|
|
37
38
|
}
|
|
38
|
-
throw new
|
|
39
|
+
throw new CliError('Could not load the workspace integrations.', 'api_error', 'Try again in a few seconds.');
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
export function whatsappNumbers(all) {
|
|
@@ -67,13 +68,13 @@ export async function getOnboardingLink(creds, business, dev = false) {
|
|
|
67
68
|
}
|
|
68
69
|
catch (error) {
|
|
69
70
|
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
|
70
|
-
throw new
|
|
71
|
+
throw new CliError('Your session is invalid or was revoked.', 'unauthorized', "Run 'plazbot login' to sign in again.");
|
|
71
72
|
}
|
|
72
73
|
link = null;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
if (!link?.token)
|
|
76
|
-
throw new
|
|
77
|
+
throw new CliError('Could not get the connection link.', 'api_error', 'Try again, or connect the number from the panel → Integrations.');
|
|
77
78
|
return { url: link.shortUrl || `${ONBOARDING_SHORT_DOMAIN}/${link.token}`, expiresAt: link.expiresAt };
|
|
78
79
|
}
|
|
79
80
|
export async function activateIntegration(creds, integrationId, dev = false) {
|
|
@@ -97,15 +98,15 @@ export async function connectNewNumber(creds, opts) {
|
|
|
97
98
|
const opened = opts.openInBrowser !== false && openBrowser(url);
|
|
98
99
|
const border = chalk.hex('#25D366');
|
|
99
100
|
console.log();
|
|
100
|
-
console.log(border(' ┃ ') + chalk.bold(opened ? '
|
|
101
|
+
console.log(border(' ┃ ') + chalk.bold(opened ? 'We opened the Meta sign-up in your browser.' : 'Open this link in your browser:'));
|
|
101
102
|
console.log(border(' ┃ ') + chalk.hex('#22d3ee').underline(url));
|
|
102
103
|
console.log(border(' ┃ ') + chalk.gray(opts.business
|
|
103
|
-
? '
|
|
104
|
-
: '
|
|
104
|
+
? 'Sign in with Facebook and link your WhatsApp Business app by scanning the QR code.'
|
|
105
|
+
: 'Sign in with Facebook, choose or create your WhatsApp Business account and verify the number.'));
|
|
105
106
|
if (expiresAt)
|
|
106
|
-
console.log(border(' ┃ ') + chalk.gray(`
|
|
107
|
+
console.log(border(' ┃ ') + chalk.gray(`The link expires on ${new Date(expiresAt).toLocaleDateString('en-US', { month: 'long', day: 'numeric' })}.`));
|
|
107
108
|
console.log();
|
|
108
|
-
const spinner = ora({ text: chalk.gray('
|
|
109
|
+
const spinner = ora({ text: chalk.gray('Waiting for the connection... (Ctrl+C to cancel)'), spinner: 'dots', color: 'cyan' }).start();
|
|
109
110
|
const intervalMs = 5000;
|
|
110
111
|
const deadline = Date.now() + (opts.timeoutMs ?? 10 * 60 * 1000);
|
|
111
112
|
const startedAt = Date.now();
|
|
@@ -115,14 +116,14 @@ export async function connectNewNumber(creds, opts) {
|
|
|
115
116
|
const fresh = whatsappNumbers(await fetchIntegrations(creds, dev)).filter(ig => !prevIds.has(ig.id));
|
|
116
117
|
if (fresh.length > 0) {
|
|
117
118
|
const detected = fresh[0];
|
|
118
|
-
spinner.succeed(chalk.hex('#4ade80')(`
|
|
119
|
-
const act = ora({ text: chalk.gray('
|
|
119
|
+
spinner.succeed(chalk.hex('#4ade80')(`Number detected: ${integrationLabel(detected)}`));
|
|
120
|
+
const act = ora({ text: chalk.gray('Activating the number...'), spinner: 'dots', color: 'green' }).start();
|
|
120
121
|
if (await activateIntegration(creds, detected.id, dev)) {
|
|
121
|
-
act.succeed(chalk.hex('#4ade80')('
|
|
122
|
+
act.succeed(chalk.hex('#4ade80')('Number active'));
|
|
122
123
|
detected.isActive = 1;
|
|
123
124
|
}
|
|
124
125
|
else {
|
|
125
|
-
act.warn(chalk.hex('#FFA726')('
|
|
126
|
+
act.warn(chalk.hex('#FFA726')('Could not activate it automatically. Activate it in the panel → Integrations.'));
|
|
126
127
|
}
|
|
127
128
|
return detected;
|
|
128
129
|
}
|
|
@@ -130,8 +131,8 @@ export async function connectNewNumber(creds, opts) {
|
|
|
130
131
|
catch {
|
|
131
132
|
// error puntual: seguir esperando
|
|
132
133
|
}
|
|
133
|
-
spinner.text = chalk.gray(`
|
|
134
|
+
spinner.text = chalk.gray(`Waiting for the connection... ${Math.floor((Date.now() - startedAt) / 1000)}s (Ctrl+C to cancel)`);
|
|
134
135
|
}
|
|
135
|
-
spinner.warn(chalk.hex('#FFA726')('No
|
|
136
|
+
spinner.warn(chalk.hex('#FFA726')('No new connection was detected in time.'));
|
|
136
137
|
return null;
|
|
137
138
|
}
|
|
@@ -2,26 +2,31 @@ import { Command } from 'commander';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
5
|
-
import {
|
|
6
|
-
import { addExamples } from '../../utils/help.js';
|
|
5
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
7
6
|
import { theme, section } from '../../utils/ui.js';
|
|
7
|
+
import { fail } from '../../utils/output.js';
|
|
8
8
|
import { connectNewNumber, fetchIntegrations, integrationLabel, whatsappNumbers } from './connect-flow.js';
|
|
9
9
|
export const whatsappConnectCommand = new Command('connect')
|
|
10
|
-
.description('
|
|
11
|
-
.option('--business', '
|
|
12
|
-
.option('--no-browser', '
|
|
13
|
-
.option('--dev', '
|
|
10
|
+
.description('Connect a WhatsApp number to the workspace (Meta sign-up in the browser)')
|
|
11
|
+
.option('--business', 'Connect your existing WhatsApp Business app (coexistence, linked by QR)', false)
|
|
12
|
+
.option('--no-browser', 'Print the link instead of opening the browser')
|
|
13
|
+
.option('--dev', 'Use development environment', false)
|
|
14
14
|
.action(async (options) => {
|
|
15
15
|
try {
|
|
16
16
|
const credentials = await getStoredCredentials();
|
|
17
|
-
console.log(section(options.business ? '
|
|
18
|
-
const loadSpinner = ora({ text: chalk.gray('
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
console.log(section(options.business ? 'Connect WhatsApp Business' : 'Connect WhatsApp'));
|
|
18
|
+
const loadSpinner = ora({ text: chalk.gray('Loading connected numbers...'), spinner: 'dots', color: 'green' }).start();
|
|
19
|
+
let existing;
|
|
20
|
+
try {
|
|
21
|
+
existing = whatsappNumbers(await fetchIntegrations(credentials, options.dev));
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
loadSpinner.stop();
|
|
25
|
+
}
|
|
21
26
|
if (existing.length > 0) {
|
|
22
|
-
console.log(theme.muted(` ${existing.length}
|
|
27
|
+
console.log(theme.muted(` ${existing.length} number(s) already connected:`));
|
|
23
28
|
existing.forEach(ig => {
|
|
24
|
-
const status = ig.isActive ? chalk.hex('#66BB6A')('
|
|
29
|
+
const status = ig.isActive ? chalk.hex('#66BB6A')('active') : chalk.hex('#EF5350')('inactive');
|
|
25
30
|
console.log(` ${theme.muted('•')} ${integrationLabel(ig)} ${status}`);
|
|
26
31
|
});
|
|
27
32
|
}
|
|
@@ -32,22 +37,32 @@ export const whatsappConnectCommand = new Command('connect')
|
|
|
32
37
|
});
|
|
33
38
|
console.log();
|
|
34
39
|
if (detected) {
|
|
35
|
-
console.log(theme.success(` WhatsApp
|
|
40
|
+
console.log(theme.success(` WhatsApp connected: ${integrationLabel(detected)}`));
|
|
41
|
+
if (!detected.isActive) {
|
|
42
|
+
console.log(theme.muted(' The number is not active yet. Activate it in the panel → Integrations.'));
|
|
43
|
+
}
|
|
44
|
+
console.log();
|
|
45
|
+
console.log(theme.muted(' Next step:'));
|
|
46
|
+
console.log(` ${chalk.cyan('plazbot whatsapp setup')} ${theme.muted('Send and receive your first message')}`);
|
|
36
47
|
console.log();
|
|
37
|
-
console.log(theme.muted(' Siguiente paso:'));
|
|
38
|
-
console.log(` ${chalk.cyan('plazbot whatsapp setup')} ${theme.muted('Prueba enviar y recibir tu primer mensaje')}`);
|
|
39
48
|
}
|
|
40
49
|
else {
|
|
41
|
-
console.log(theme.muted('
|
|
50
|
+
console.log(theme.muted(' Try again with: plazbot whatsapp connect'));
|
|
51
|
+
console.log();
|
|
52
|
+
process.exit(1);
|
|
42
53
|
}
|
|
43
|
-
console.log();
|
|
44
54
|
}
|
|
45
55
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
process.exit(1);
|
|
56
|
+
fail(error);
|
|
48
57
|
}
|
|
49
58
|
});
|
|
50
59
|
addExamples(whatsappConnectCommand, [
|
|
51
|
-
{ description: '
|
|
52
|
-
{ description: '
|
|
60
|
+
{ description: 'Connect a number with the WhatsApp API', command: 'plazbot whatsapp connect' },
|
|
61
|
+
{ description: 'Connect your existing WhatsApp Business app', command: 'plazbot whatsapp connect --business' },
|
|
62
|
+
{ description: 'Print the link (e.g. to open it on another device)', command: 'plazbot whatsapp connect --no-browser' },
|
|
63
|
+
]);
|
|
64
|
+
addNotes(whatsappConnectCommand, [
|
|
65
|
+
'The CLI waits up to 10 minutes for the new number to appear, then activates it.',
|
|
66
|
+
'If automatic activation fails, activate the number in the panel → Integrations.',
|
|
67
|
+
'The connection link is reused while it is valid and regenerated when it expires.',
|
|
53
68
|
]);
|
|
@@ -1,34 +1,51 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { Message } from 'plazbot';
|
|
3
|
-
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
4
2
|
import { logger } from '../../utils/logger.js';
|
|
5
|
-
import { addExamples } from '../../utils/help.js';
|
|
3
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
4
|
+
import { createSpinner } from '../../utils/ui.js';
|
|
5
|
+
import { jsonOption, isJsonMode, printJson, fail } from '../../utils/output.js';
|
|
6
|
+
import { addYesOption, confirmAction } from '../../utils/confirm.js';
|
|
7
|
+
import { phoneOption, normalizePhone } from '../../utils/phone.js';
|
|
8
|
+
import { ensureApiSuccess, getMessageClient } from './shared.js';
|
|
6
9
|
export const deleteWebhookCommand = new Command('delete-webhook')
|
|
7
|
-
.description('
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.
|
|
10
|
+
.description('Remove the incoming-messages webhook from a WhatsApp number')
|
|
11
|
+
.addOption(phoneOption('Your connected WhatsApp number in international format, e.g. +51912345678'))
|
|
12
|
+
.addOption(new Option('-n, --number <phone>', 'Alias of --phone').hideHelp())
|
|
13
|
+
.addOption(jsonOption())
|
|
14
|
+
.option('--dev', 'Use development environment', false);
|
|
15
|
+
addYesOption(deleteWebhookCommand);
|
|
16
|
+
deleteWebhookCommand.action(async (options) => {
|
|
11
17
|
try {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
const phone = normalizePhone(options.phone ?? options.number);
|
|
19
|
+
if (!(await confirmAction(`Remove the webhook from +${phone}? Your server will stop receiving its messages.`, options))) {
|
|
20
|
+
logger.dim('Aborted.');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const messageClient = await getMessageClient(options.dev);
|
|
24
|
+
const spinner = createSpinner(`Removing webhook from +${phone}...`).start();
|
|
25
|
+
try {
|
|
26
|
+
const response = await messageClient.deleteWebhook({ number: phone });
|
|
27
|
+
ensureApiSuccess(response, 'The webhook was not removed');
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
spinner.stop();
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
spinner.succeed(`Webhook removed from +${phone}`);
|
|
34
|
+
if (isJsonMode())
|
|
35
|
+
printJson({ phone, deleted: true });
|
|
25
36
|
}
|
|
26
37
|
catch (error) {
|
|
27
|
-
|
|
28
|
-
process.exit(1);
|
|
38
|
+
fail(error);
|
|
29
39
|
}
|
|
30
40
|
});
|
|
31
41
|
addExamples(deleteWebhookCommand, [
|
|
32
|
-
{ description: 'Remove the webhook
|
|
33
|
-
command: 'plazbot whatsapp delete-webhook -
|
|
42
|
+
{ description: 'Remove the webhook (asks for confirmation)',
|
|
43
|
+
command: 'plazbot whatsapp delete-webhook -p +51912345678' },
|
|
44
|
+
{ description: 'Remove it without prompting (scripts and CI)',
|
|
45
|
+
command: 'plazbot whatsapp delete-webhook -p +51912345678 --yes' },
|
|
46
|
+
{ description: 'Machine-readable output',
|
|
47
|
+
command: 'plazbot whatsapp delete-webhook -p +51912345678 --yes --json' },
|
|
48
|
+
]);
|
|
49
|
+
addNotes(deleteWebhookCommand, [
|
|
50
|
+
'Only the forwarding to your URL is removed; the number stays connected to the workspace.',
|
|
34
51
|
]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { addExamples } from '../../utils/help.js';
|
|
2
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
3
3
|
import { sendMessageCommand } from './send.js';
|
|
4
4
|
import { sendTemplateCommand } from './send-template.js';
|
|
5
5
|
import { registerWebhookCommand } from './register-webhook.js';
|
|
@@ -11,29 +11,28 @@ import { whatsappSetupCommand } from './setup.js';
|
|
|
11
11
|
import { widgetCommand } from './widget.js';
|
|
12
12
|
import { channelsCommand, assignCommand } from './channels.js';
|
|
13
13
|
export const whatsappCommands = new Command('whatsapp')
|
|
14
|
-
.description('WhatsApp
|
|
14
|
+
.description('WhatsApp: connect numbers, send messages and templates, broadcast, webhooks')
|
|
15
15
|
.addCommand(whatsappSetupCommand)
|
|
16
16
|
.addCommand(whatsappConnectCommand)
|
|
17
17
|
.addCommand(sendMessageCommand)
|
|
18
18
|
.addCommand(sendTemplateCommand)
|
|
19
|
-
.addCommand(registerWebhookCommand)
|
|
20
|
-
.addCommand(deleteWebhookCommand)
|
|
21
19
|
.addCommand(broadcastCommand)
|
|
22
20
|
.addCommand(whatsappChatCommand)
|
|
23
|
-
.addCommand(
|
|
21
|
+
.addCommand(registerWebhookCommand)
|
|
22
|
+
.addCommand(deleteWebhookCommand)
|
|
24
23
|
.addCommand(channelsCommand)
|
|
25
|
-
.addCommand(assignCommand)
|
|
24
|
+
.addCommand(assignCommand)
|
|
25
|
+
.addCommand(widgetCommand);
|
|
26
26
|
addExamples(whatsappCommands, [
|
|
27
|
-
{ description: '
|
|
27
|
+
{ description: 'Guided setup: connect WhatsApp and send your first message',
|
|
28
28
|
command: 'plazbot whatsapp setup' },
|
|
29
|
-
{ description: '
|
|
30
|
-
command: 'plazbot whatsapp
|
|
31
|
-
{ description: 'Send a quick message',
|
|
32
|
-
command: 'plazbot whatsapp send-message -t 51912345678 -m "Hello"' },
|
|
29
|
+
{ description: 'Send a text message (within the 24h window)',
|
|
30
|
+
command: 'plazbot whatsapp send-message -p +51912345678 -m "Hello"' },
|
|
33
31
|
{ description: 'Send a template with variables',
|
|
34
|
-
command: 'plazbot whatsapp send-template -p 51912345678 -t welcome --var name=Anna' },
|
|
35
|
-
{ description: '
|
|
36
|
-
command: 'plazbot whatsapp
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
command: 'plazbot whatsapp send-template -p +51912345678 -t welcome --var name=Anna' },
|
|
33
|
+
{ description: 'Make an agent answer a number',
|
|
34
|
+
command: 'plazbot whatsapp assign -p +51912345678 -a age_AbcDef123' },
|
|
35
|
+
]);
|
|
36
|
+
addNotes(whatsappCommands, [
|
|
37
|
+
'Every command takes the number as -p, --phone in international format (+51912345678, "+51 912 345 678" or 51912345678).',
|
|
39
38
|
]);
|
|
@@ -1,37 +1,54 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { Message } from 'plazbot';
|
|
3
|
-
import { getStoredCredentials } from '../../utils/credentials.js';
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
4
2
|
import { logger } from '../../utils/logger.js';
|
|
5
|
-
import { addExamples } from '../../utils/help.js';
|
|
3
|
+
import { addExamples, addNotes } from '../../utils/help.js';
|
|
4
|
+
import { createSpinner } from '../../utils/ui.js';
|
|
5
|
+
import { jsonOption, isJsonMode, printJson, fail, CliError } from '../../utils/output.js';
|
|
6
|
+
import { phoneOption, normalizePhone } from '../../utils/phone.js';
|
|
7
|
+
import { ensureApiSuccess, getMessageClient } from './shared.js';
|
|
6
8
|
export const registerWebhookCommand = new Command('register-webhook')
|
|
7
|
-
.description('Register
|
|
8
|
-
.
|
|
9
|
-
.
|
|
9
|
+
.description('Register the webhook URL that receives incoming messages for a WhatsApp number')
|
|
10
|
+
.addOption(phoneOption('Your connected WhatsApp number in international format, e.g. +51912345678'))
|
|
11
|
+
.addOption(new Option('-n, --number <phone>', 'Alias of --phone').hideHelp())
|
|
12
|
+
.requiredOption('-u, --url <url>', 'Public https URL that will receive the incoming messages')
|
|
13
|
+
.addOption(jsonOption())
|
|
10
14
|
.option('--dev', 'Use development environment', false)
|
|
11
15
|
.action(async (options) => {
|
|
12
16
|
try {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const phone = normalizePhone(options.phone ?? options.number);
|
|
18
|
+
const url = String(options.url || '').trim();
|
|
19
|
+
if (!/^https?:\/\/\S+$/i.test(url)) {
|
|
20
|
+
throw new CliError(`Invalid webhook URL "${options.url}".`, 'invalid_url', 'Use a public URL, e.g. -u https://api.example.com/webhooks/whatsapp');
|
|
21
|
+
}
|
|
22
|
+
const messageClient = await getMessageClient(options.dev);
|
|
23
|
+
if (!isJsonMode()) {
|
|
24
|
+
logger.title('Registering WhatsApp webhook');
|
|
25
|
+
logger.label('Number', `+${phone}`);
|
|
26
|
+
logger.label('URL', url);
|
|
27
|
+
}
|
|
28
|
+
const spinner = createSpinner('Registering...').start();
|
|
29
|
+
try {
|
|
30
|
+
const response = await messageClient.registerWebhook({ number: phone, webhookUrl: url });
|
|
31
|
+
ensureApiSuccess(response, 'The webhook was not registered', "Check that the number is connected to this workspace ('plazbot whatsapp connect').");
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
spinner.stop();
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
spinner.succeed('Webhook registered');
|
|
38
|
+
if (isJsonMode())
|
|
39
|
+
printJson({ phone, url, registered: true });
|
|
28
40
|
}
|
|
29
41
|
catch (error) {
|
|
30
|
-
|
|
31
|
-
process.exit(1);
|
|
42
|
+
fail(error);
|
|
32
43
|
}
|
|
33
44
|
});
|
|
34
45
|
addExamples(registerWebhookCommand, [
|
|
35
|
-
{ description: 'Register an
|
|
36
|
-
command: 'plazbot whatsapp register-webhook -
|
|
46
|
+
{ description: 'Register an incoming-messages webhook for a number',
|
|
47
|
+
command: 'plazbot whatsapp register-webhook -p +51912345678 -u https://api.example.com/webhooks/whatsapp' },
|
|
48
|
+
{ description: 'Same, with machine-readable output',
|
|
49
|
+
command: 'plazbot whatsapp register-webhook -p +51912345678 -u https://api.example.com/webhooks/whatsapp --json' },
|
|
50
|
+
]);
|
|
51
|
+
addNotes(registerWebhookCommand, [
|
|
52
|
+
'The number must be connected to the active workspace (see plazbot whatsapp connect).',
|
|
53
|
+
'Registering again replaces the previous URL. Remove it with plazbot whatsapp delete-webhook.',
|
|
37
54
|
]);
|