@sendblue/cli 0.6.2 → 0.7.0
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 +105 -0
- package/dist/commands/lines.d.ts +1 -0
- package/dist/commands/lines.js +35 -0
- package/dist/commands/send-group.d.ts +5 -0
- package/dist/commands/send-group.js +33 -0
- package/dist/commands/send.d.ts +5 -1
- package/dist/commands/send.js +3 -4
- package/dist/commands/show-keys.d.ts +1 -0
- package/dist/commands/show-keys.js +16 -0
- package/dist/commands/typing.d.ts +1 -0
- package/dist/commands/typing.js +21 -0
- package/dist/commands/webhooks.d.ts +7 -0
- package/dist/commands/webhooks.js +78 -0
- package/dist/index.js +53 -9
- package/dist/lib/api.d.ts +17 -1
- package/dist/lib/api.js +104 -2
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Sendblue CLI
|
|
2
|
+
|
|
3
|
+
iMessage numbers for AI agents. Set up an iMessage-enabled phone number and start sending messages in under a minute.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @sendblue/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18+.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Create an account and get an iMessage number
|
|
17
|
+
sendblue setup
|
|
18
|
+
|
|
19
|
+
# Send a message
|
|
20
|
+
sendblue send +15551234567 'Hello from Sendblue!'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
### `sendblue setup`
|
|
26
|
+
|
|
27
|
+
Create a new Sendblue account. Walks you through email verification, company name, and adding your first contact.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Interactive (recommended for first time)
|
|
31
|
+
sendblue setup
|
|
32
|
+
|
|
33
|
+
# Non-interactive (for CI/scripts)
|
|
34
|
+
sendblue setup --email you@example.com # sends verification code, exits
|
|
35
|
+
sendblue setup --email you@example.com --code 12345678 --company my-co --contact +15551234567
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Flag | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `--email <email>` | Email address |
|
|
41
|
+
| `--code <code>` | 8-digit verification code |
|
|
42
|
+
| `--company <name>` | Company name (lowercase, hyphens/underscores, 3-64 chars) |
|
|
43
|
+
| `--contact <number>` | First contact phone number (E.164 format) |
|
|
44
|
+
|
|
45
|
+
### `sendblue login`
|
|
46
|
+
|
|
47
|
+
Log in to an existing account.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
sendblue login
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `sendblue send <number> <message>`
|
|
54
|
+
|
|
55
|
+
Send an iMessage.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
sendblue send +15551234567 'Hey, your order shipped!'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `sendblue messages`
|
|
62
|
+
|
|
63
|
+
View recent messages.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
sendblue messages
|
|
67
|
+
sendblue messages -n +15551234567 --limit 20
|
|
68
|
+
sendblue messages --inbound
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Flag | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `-n, --number <number>` | Filter by contact |
|
|
74
|
+
| `-l, --limit <count>` | Number of messages (default: 10) |
|
|
75
|
+
| `--outbound` | Show only sent messages |
|
|
76
|
+
| `--inbound` | Show only received messages |
|
|
77
|
+
|
|
78
|
+
### `sendblue add-contact <number>`
|
|
79
|
+
|
|
80
|
+
Add a contact to your account.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
sendblue add-contact +15551234567
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `sendblue contacts`
|
|
87
|
+
|
|
88
|
+
List all contacts and their verification status.
|
|
89
|
+
|
|
90
|
+
### `sendblue status`
|
|
91
|
+
|
|
92
|
+
Check your account status and plan.
|
|
93
|
+
|
|
94
|
+
### `sendblue whoami`
|
|
95
|
+
|
|
96
|
+
Show current credentials and verify they're valid.
|
|
97
|
+
|
|
98
|
+
## Credentials
|
|
99
|
+
|
|
100
|
+
Credentials are stored in `~/.sendblue/credentials.json` with `600` permissions (owner read/write only). Run `sendblue whoami` to see the current config path.
|
|
101
|
+
|
|
102
|
+
## Links
|
|
103
|
+
|
|
104
|
+
- [Sendblue](https://sendblue.co)
|
|
105
|
+
- [API Docs](https://docs.sendblue.co)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function linesCommand(): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getCredentials } from '../lib/config.js';
|
|
4
|
+
import { getLines } from '../lib/api.js';
|
|
5
|
+
import { formatPhoneNumber, printError } from '../lib/format.js';
|
|
6
|
+
export async function linesCommand() {
|
|
7
|
+
const creds = getCredentials();
|
|
8
|
+
if (!creds) {
|
|
9
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const spinner = ora({ text: 'Fetching lines...', indent: 2 }).start();
|
|
13
|
+
try {
|
|
14
|
+
const result = await getLines(creds.apiKey, creds.apiSecret);
|
|
15
|
+
spinner.stop();
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(chalk.bold(' Phone Lines'));
|
|
18
|
+
console.log();
|
|
19
|
+
if (!result.numbers || result.numbers.length === 0) {
|
|
20
|
+
console.log(chalk.dim(' No lines assigned yet.'));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
for (const number of result.numbers) {
|
|
24
|
+
console.log(` ${formatPhoneNumber(number)}`);
|
|
25
|
+
}
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(chalk.dim(` ${result.numbers.length} line${result.numbers.length === 1 ? '' : 's'} total`));
|
|
28
|
+
}
|
|
29
|
+
console.log();
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getCredentials } from '../lib/config.js';
|
|
4
|
+
import { sendGroupMessage } from '../lib/api.js';
|
|
5
|
+
import { normalizeNumber, formatPhoneNumber, printError } from '../lib/format.js';
|
|
6
|
+
export async function sendGroupCommand(numbers, opts) {
|
|
7
|
+
const creds = getCredentials();
|
|
8
|
+
if (!creds) {
|
|
9
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
// Last argument is the message, rest are numbers
|
|
13
|
+
if (numbers.length < 3) {
|
|
14
|
+
printError('Usage: sendblue send-group <number1> <number2> [number3...] <message>');
|
|
15
|
+
printError('At least 2 phone numbers and a message are required.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const message = numbers[numbers.length - 1];
|
|
19
|
+
const phoneNumbers = numbers.slice(0, -1).map(normalizeNumber);
|
|
20
|
+
const spinner = ora({ text: `Sending group message to ${phoneNumbers.length} recipients...`, indent: 2 }).start();
|
|
21
|
+
try {
|
|
22
|
+
const result = await sendGroupMessage(creds.apiKey, creds.apiSecret, phoneNumbers, message, creds.assignedNumber, opts.media);
|
|
23
|
+
spinner.succeed(`Group message sent to ${phoneNumbers.length} recipients`);
|
|
24
|
+
console.log(chalk.dim(` Recipients: ${phoneNumbers.map(formatPhoneNumber).join(', ')}`));
|
|
25
|
+
if (result.messageId) {
|
|
26
|
+
console.log(chalk.dim(` Message ID: ${result.messageId}`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
spinner.fail(`Send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
package/dist/commands/send.d.ts
CHANGED
package/dist/commands/send.js
CHANGED
|
@@ -2,9 +2,8 @@ import chalk from 'chalk';
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { getCredentials } from '../lib/config.js';
|
|
4
4
|
import { sendMessage } from '../lib/api.js';
|
|
5
|
-
import { normalizeNumber } from '../lib/format.js';
|
|
6
|
-
|
|
7
|
-
export async function sendCommand(number, message) {
|
|
5
|
+
import { normalizeNumber, printError } from '../lib/format.js';
|
|
6
|
+
export async function sendCommand(number, message, opts) {
|
|
8
7
|
const creds = getCredentials();
|
|
9
8
|
if (!creds) {
|
|
10
9
|
printError('No credentials found. Run `sendblue login` first.');
|
|
@@ -13,7 +12,7 @@ export async function sendCommand(number, message) {
|
|
|
13
12
|
const normalized = normalizeNumber(number);
|
|
14
13
|
const spinner = ora({ text: `Sending to ${normalized}...`, indent: 2 }).start();
|
|
15
14
|
try {
|
|
16
|
-
const result = await sendMessage(creds.apiKey, creds.apiSecret, normalized, message, creds.assignedNumber);
|
|
15
|
+
const result = await sendMessage(creds.apiKey, creds.apiSecret, normalized, message, creds.assignedNumber, opts.media);
|
|
17
16
|
spinner.succeed(`Message sent to ${normalized}`);
|
|
18
17
|
if (result.messageId) {
|
|
19
18
|
console.log(chalk.dim(` Message ID: ${result.messageId}`));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showKeysCommand(): Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getCredentials } from '../lib/config.js';
|
|
3
|
+
import { printError } from '../lib/format.js';
|
|
4
|
+
export async function showKeysCommand() {
|
|
5
|
+
const creds = getCredentials();
|
|
6
|
+
if (!creds) {
|
|
7
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
console.log();
|
|
11
|
+
console.log(chalk.bold(' API Keys'));
|
|
12
|
+
console.log();
|
|
13
|
+
console.log(` ${chalk.bold('API Key')}: ${creds.apiKey}`);
|
|
14
|
+
console.log(` ${chalk.bold('API Secret')}: ${creds.apiSecret}`);
|
|
15
|
+
console.log();
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function typingCommand(number: string): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import ora from 'ora';
|
|
2
|
+
import { getCredentials } from '../lib/config.js';
|
|
3
|
+
import { sendTypingIndicator } from '../lib/api.js';
|
|
4
|
+
import { normalizeNumber, printError } from '../lib/format.js';
|
|
5
|
+
export async function typingCommand(number) {
|
|
6
|
+
const creds = getCredentials();
|
|
7
|
+
if (!creds) {
|
|
8
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const normalized = normalizeNumber(number);
|
|
12
|
+
const spinner = ora({ text: `Sending typing indicator to ${normalized}...`, indent: 2 }).start();
|
|
13
|
+
try {
|
|
14
|
+
await sendTypingIndicator(creds.apiKey, creds.apiSecret, normalized, creds.assignedNumber);
|
|
15
|
+
spinner.succeed(`Typing indicator sent to ${normalized}`);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function webhooksListCommand(): Promise<void>;
|
|
2
|
+
export declare function webhooksAddCommand(url: string, opts: {
|
|
3
|
+
type: string;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function webhooksRemoveCommand(url: string, opts: {
|
|
6
|
+
type: string;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getCredentials } from '../lib/config.js';
|
|
4
|
+
import { getWebhooks, addWebhook, deleteWebhook } from '../lib/api.js';
|
|
5
|
+
import { printError } from '../lib/format.js';
|
|
6
|
+
const VALID_TYPES = ['receive', 'outbound', 'call_log', 'line_blocked', 'line_assigned', 'contact_created'];
|
|
7
|
+
export async function webhooksListCommand() {
|
|
8
|
+
const creds = getCredentials();
|
|
9
|
+
if (!creds) {
|
|
10
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const spinner = ora({ text: 'Fetching webhooks...', indent: 2 }).start();
|
|
14
|
+
try {
|
|
15
|
+
const result = await getWebhooks(creds.apiKey, creds.apiSecret);
|
|
16
|
+
spinner.stop();
|
|
17
|
+
console.log();
|
|
18
|
+
console.log(chalk.bold(' Webhooks'));
|
|
19
|
+
console.log();
|
|
20
|
+
if (!result.webhooks || result.webhooks.length === 0) {
|
|
21
|
+
console.log(chalk.dim(' No webhooks configured.'));
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.dim(' Add one with:'));
|
|
24
|
+
console.log(chalk.cyan(' sendblue webhooks add <url> --type receive'));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (const wh of result.webhooks) {
|
|
28
|
+
const type = wh.type ? chalk.dim(` (${wh.type})`) : '';
|
|
29
|
+
console.log(` ${chalk.cyan(wh.url)}${type}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
console.log();
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function webhooksAddCommand(url, opts) {
|
|
40
|
+
const creds = getCredentials();
|
|
41
|
+
if (!creds) {
|
|
42
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
if (!VALID_TYPES.includes(opts.type)) {
|
|
46
|
+
printError(`Invalid type. Must be one of: ${VALID_TYPES.join(', ')}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const spinner = ora({ text: 'Adding webhook...', indent: 2 }).start();
|
|
50
|
+
try {
|
|
51
|
+
await addWebhook(creds.apiKey, creds.apiSecret, url, opts.type);
|
|
52
|
+
spinner.succeed(`Webhook added: ${url} (${opts.type})`);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function webhooksRemoveCommand(url, opts) {
|
|
60
|
+
const creds = getCredentials();
|
|
61
|
+
if (!creds) {
|
|
62
|
+
printError('No credentials found. Run `sendblue login` first.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
if (!VALID_TYPES.includes(opts.type)) {
|
|
66
|
+
printError(`Invalid type. Must be one of: ${VALID_TYPES.join(', ')}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const spinner = ora({ text: 'Removing webhook...', indent: 2 }).start();
|
|
70
|
+
try {
|
|
71
|
+
await deleteWebhook(creds.apiKey, creds.apiSecret, url, opts.type);
|
|
72
|
+
spinner.succeed(`Webhook removed: ${url} (${opts.type})`);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
spinner.fail(`Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,15 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { setupCommand } from './commands/setup.js';
|
|
5
5
|
import { loginCommand } from './commands/login.js';
|
|
6
6
|
import { sendCommand } from './commands/send.js';
|
|
7
|
+
import { sendGroupCommand } from './commands/send-group.js';
|
|
7
8
|
import { messagesCommand } from './commands/messages.js';
|
|
8
9
|
import { statusCommand } from './commands/status.js';
|
|
9
10
|
import { whoamiCommand } from './commands/whoami.js';
|
|
10
11
|
import { addContactCommand, contactsCommand } from './commands/add-contact.js';
|
|
12
|
+
import { typingCommand } from './commands/typing.js';
|
|
13
|
+
import { linesCommand } from './commands/lines.js';
|
|
14
|
+
import { webhooksListCommand, webhooksAddCommand, webhooksRemoveCommand } from './commands/webhooks.js';
|
|
15
|
+
import { showKeysCommand } from './commands/show-keys.js';
|
|
11
16
|
import { getLogo } from './lib/format.js';
|
|
12
17
|
const require = createRequire(import.meta.url);
|
|
13
18
|
const { version } = require('../package.json');
|
|
@@ -28,21 +33,24 @@ program
|
|
|
28
33
|
.command('login')
|
|
29
34
|
.description('Log in to an existing Sendblue account')
|
|
30
35
|
.action(loginCommand);
|
|
31
|
-
program
|
|
32
|
-
.command('add-contact')
|
|
33
|
-
.description('Add a contact to your account')
|
|
34
|
-
.argument('<number>', 'Contact phone number (E.164 format)')
|
|
35
|
-
.action(addContactCommand);
|
|
36
|
-
program
|
|
37
|
-
.command('contacts')
|
|
38
|
-
.description('List contacts and verification status')
|
|
39
|
-
.action(contactsCommand);
|
|
40
36
|
program
|
|
41
37
|
.command('send')
|
|
42
38
|
.description('Send a message')
|
|
43
39
|
.argument('<number>', 'Recipient phone number (E.164 format)')
|
|
44
40
|
.argument('<message>', 'Message content')
|
|
41
|
+
.option('--media <url>', 'Attach a media URL (image, video, etc.)')
|
|
45
42
|
.action(sendCommand);
|
|
43
|
+
program
|
|
44
|
+
.command('send-group')
|
|
45
|
+
.description('Send a group message')
|
|
46
|
+
.argument('<args...>', 'Phone numbers followed by message: <num1> <num2> [...] <message>')
|
|
47
|
+
.option('--media <url>', 'Attach a media URL')
|
|
48
|
+
.action(sendGroupCommand);
|
|
49
|
+
program
|
|
50
|
+
.command('typing')
|
|
51
|
+
.description('Send a typing indicator')
|
|
52
|
+
.argument('<number>', 'Recipient phone number (E.164 format)')
|
|
53
|
+
.action(typingCommand);
|
|
46
54
|
program
|
|
47
55
|
.command('messages')
|
|
48
56
|
.description('View recent messages')
|
|
@@ -51,6 +59,38 @@ program
|
|
|
51
59
|
.option('--outbound', 'Show only outbound messages')
|
|
52
60
|
.option('--inbound', 'Show only inbound messages')
|
|
53
61
|
.action(messagesCommand);
|
|
62
|
+
program
|
|
63
|
+
.command('add-contact')
|
|
64
|
+
.description('Add a contact to your account')
|
|
65
|
+
.argument('<number>', 'Contact phone number (E.164 format)')
|
|
66
|
+
.action(addContactCommand);
|
|
67
|
+
program
|
|
68
|
+
.command('contacts')
|
|
69
|
+
.description('List contacts and verification status')
|
|
70
|
+
.action(contactsCommand);
|
|
71
|
+
program
|
|
72
|
+
.command('lines')
|
|
73
|
+
.description('List phone numbers on your account')
|
|
74
|
+
.action(linesCommand);
|
|
75
|
+
const webhooks = program
|
|
76
|
+
.command('webhooks')
|
|
77
|
+
.description('Manage webhooks');
|
|
78
|
+
webhooks
|
|
79
|
+
.command('list')
|
|
80
|
+
.description('List configured webhooks')
|
|
81
|
+
.action(webhooksListCommand);
|
|
82
|
+
webhooks
|
|
83
|
+
.command('add')
|
|
84
|
+
.description('Add a webhook')
|
|
85
|
+
.argument('<url>', 'Webhook URL')
|
|
86
|
+
.requiredOption('--type <type>', 'Event type (receive, outbound, call_log, line_blocked, line_assigned, contact_created)')
|
|
87
|
+
.action(webhooksAddCommand);
|
|
88
|
+
webhooks
|
|
89
|
+
.command('remove')
|
|
90
|
+
.description('Remove a webhook')
|
|
91
|
+
.argument('<url>', 'Webhook URL')
|
|
92
|
+
.requiredOption('--type <type>', 'Event type')
|
|
93
|
+
.action(webhooksRemoveCommand);
|
|
54
94
|
program
|
|
55
95
|
.command('status')
|
|
56
96
|
.description('Check your account status')
|
|
@@ -59,4 +99,8 @@ program
|
|
|
59
99
|
.command('whoami')
|
|
60
100
|
.description('Show current credentials')
|
|
61
101
|
.action(whoamiCommand);
|
|
102
|
+
program
|
|
103
|
+
.command('show-keys')
|
|
104
|
+
.description('Show your API key and secret')
|
|
105
|
+
.action(showKeysCommand);
|
|
62
106
|
program.parse();
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ interface AccountResponse {
|
|
|
22
22
|
export declare function sendCode(email: string): Promise<void>;
|
|
23
23
|
export declare function verifySetup(email: string, code: string, companyName: string): Promise<SetupResponse>;
|
|
24
24
|
export declare function verifyLogin(email: string, code: string): Promise<SetupResponse>;
|
|
25
|
-
export declare function sendMessage(apiKey: string, apiSecret: string, number: string, content: string, fromNumber?: string): Promise<SendMessageResponse>;
|
|
25
|
+
export declare function sendMessage(apiKey: string, apiSecret: string, number: string, content: string, fromNumber?: string, mediaUrl?: string): Promise<SendMessageResponse>;
|
|
26
26
|
export declare function getAccount(apiKey: string, apiSecret: string): Promise<AccountResponse>;
|
|
27
27
|
interface ContactRoute {
|
|
28
28
|
id: number;
|
|
@@ -67,5 +67,21 @@ export declare function getMessages(apiKey: string, apiSecret: string, opts: {
|
|
|
67
67
|
limit?: number;
|
|
68
68
|
isOutbound?: boolean;
|
|
69
69
|
}): Promise<MessagesResponse>;
|
|
70
|
+
export declare function sendTypingIndicator(apiKey: string, apiSecret: string, number: string, fromNumber?: string): Promise<void>;
|
|
71
|
+
export declare function sendGroupMessage(apiKey: string, apiSecret: string, numbers: string[], content: string, fromNumber?: string, mediaUrl?: string): Promise<SendMessageResponse>;
|
|
72
|
+
export declare function getLines(apiKey: string, apiSecret: string): Promise<{
|
|
73
|
+
numbers: string[];
|
|
74
|
+
}>;
|
|
75
|
+
interface Webhook {
|
|
76
|
+
url: string;
|
|
77
|
+
type?: string;
|
|
78
|
+
}
|
|
79
|
+
interface WebhooksResponse {
|
|
80
|
+
webhooks: Webhook[];
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
}
|
|
83
|
+
export declare function getWebhooks(apiKey: string, apiSecret: string): Promise<WebhooksResponse>;
|
|
84
|
+
export declare function addWebhook(apiKey: string, apiSecret: string, url: string, type: string): Promise<void>;
|
|
85
|
+
export declare function deleteWebhook(apiKey: string, apiSecret: string, url: string, type: string): Promise<void>;
|
|
70
86
|
export declare function testKeys(apiKey: string, apiSecret: string): Promise<boolean>;
|
|
71
87
|
export {};
|
package/dist/lib/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const API_BASE = 'https://api.sendblue.com';
|
|
2
|
-
const SETUP_BASE =
|
|
2
|
+
const SETUP_BASE = 'https://dashboard.sendblue.com';
|
|
3
3
|
export async function sendCode(email) {
|
|
4
4
|
const res = await fetch(`${SETUP_BASE}/api/v3/cli/setup`, {
|
|
5
5
|
method: 'POST',
|
|
@@ -35,10 +35,12 @@ export async function verifyLogin(email, code) {
|
|
|
35
35
|
}
|
|
36
36
|
return res.json();
|
|
37
37
|
}
|
|
38
|
-
export async function sendMessage(apiKey, apiSecret, number, content, fromNumber) {
|
|
38
|
+
export async function sendMessage(apiKey, apiSecret, number, content, fromNumber, mediaUrl) {
|
|
39
39
|
const body = { number, content };
|
|
40
40
|
if (fromNumber)
|
|
41
41
|
body.from_number = fromNumber;
|
|
42
|
+
if (mediaUrl)
|
|
43
|
+
body.media_url = mediaUrl;
|
|
42
44
|
const res = await fetch(`${API_BASE}/api/send-message`, {
|
|
43
45
|
method: 'POST',
|
|
44
46
|
headers: {
|
|
@@ -121,6 +123,106 @@ export async function getMessages(apiKey, apiSecret, opts) {
|
|
|
121
123
|
}
|
|
122
124
|
return res.json();
|
|
123
125
|
}
|
|
126
|
+
// --- Typing indicator ---
|
|
127
|
+
export async function sendTypingIndicator(apiKey, apiSecret, number, fromNumber) {
|
|
128
|
+
const body = { number };
|
|
129
|
+
if (fromNumber)
|
|
130
|
+
body.from_number = fromNumber;
|
|
131
|
+
const res = await fetch(`${API_BASE}/api/send-typing-indicator`, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'sb-api-key-id': apiKey,
|
|
136
|
+
'sb-api-secret-key': apiSecret
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify(body)
|
|
139
|
+
});
|
|
140
|
+
if (!res.ok) {
|
|
141
|
+
const body = await res.json().catch(() => ({}));
|
|
142
|
+
throw new Error(body.error || body.message || `Failed to send typing indicator (${res.status})`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// --- Group messaging ---
|
|
146
|
+
export async function sendGroupMessage(apiKey, apiSecret, numbers, content, fromNumber, mediaUrl) {
|
|
147
|
+
const body = { numbers, content };
|
|
148
|
+
if (fromNumber)
|
|
149
|
+
body.from_number = fromNumber;
|
|
150
|
+
if (mediaUrl)
|
|
151
|
+
body.media_url = mediaUrl;
|
|
152
|
+
const res = await fetch(`${API_BASE}/api/send-group-message`, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
'sb-api-key-id': apiKey,
|
|
157
|
+
'sb-api-secret-key': apiSecret
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify(body)
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
const body = await res.json().catch(() => ({}));
|
|
163
|
+
throw new Error(body.error || body.message || `Failed to send group message (${res.status})`);
|
|
164
|
+
}
|
|
165
|
+
return res.json();
|
|
166
|
+
}
|
|
167
|
+
// --- Lines ---
|
|
168
|
+
export async function getLines(apiKey, apiSecret) {
|
|
169
|
+
const res = await fetch(`${API_BASE}/api/lines`, {
|
|
170
|
+
method: 'GET',
|
|
171
|
+
headers: {
|
|
172
|
+
'sb-api-key-id': apiKey,
|
|
173
|
+
'sb-api-secret-key': apiSecret
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
if (!res.ok) {
|
|
177
|
+
const body = await res.json().catch(() => ({}));
|
|
178
|
+
throw new Error(body.error || body.message || `Failed to get lines (${res.status})`);
|
|
179
|
+
}
|
|
180
|
+
return res.json();
|
|
181
|
+
}
|
|
182
|
+
export async function getWebhooks(apiKey, apiSecret) {
|
|
183
|
+
const res = await fetch(`${API_BASE}/api/account/webhooks`, {
|
|
184
|
+
method: 'GET',
|
|
185
|
+
headers: {
|
|
186
|
+
'sb-api-key-id': apiKey,
|
|
187
|
+
'sb-api-secret-key': apiSecret
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
const body = await res.json().catch(() => ({}));
|
|
192
|
+
throw new Error(body.error || body.message || `Failed to get webhooks (${res.status})`);
|
|
193
|
+
}
|
|
194
|
+
return res.json();
|
|
195
|
+
}
|
|
196
|
+
export async function addWebhook(apiKey, apiSecret, url, type) {
|
|
197
|
+
const res = await fetch(`${API_BASE}/api/account/webhooks`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: {
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
'sb-api-key-id': apiKey,
|
|
202
|
+
'sb-api-secret-key': apiSecret
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify({ webhooks: [url], type })
|
|
205
|
+
});
|
|
206
|
+
if (!res.ok) {
|
|
207
|
+
const body = await res.json().catch(() => ({}));
|
|
208
|
+
throw new Error(body.error || body.message || `Failed to add webhook (${res.status})`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export async function deleteWebhook(apiKey, apiSecret, url, type) {
|
|
212
|
+
const res = await fetch(`${API_BASE}/api/account/webhooks`, {
|
|
213
|
+
method: 'DELETE',
|
|
214
|
+
headers: {
|
|
215
|
+
'Content-Type': 'application/json',
|
|
216
|
+
'sb-api-key-id': apiKey,
|
|
217
|
+
'sb-api-secret-key': apiSecret
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify({ webhooks: [url], type })
|
|
220
|
+
});
|
|
221
|
+
if (!res.ok) {
|
|
222
|
+
const body = await res.json().catch(() => ({}));
|
|
223
|
+
throw new Error(body.error || body.message || `Failed to delete webhook (${res.status})`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
124
226
|
export async function testKeys(apiKey, apiSecret) {
|
|
125
227
|
try {
|
|
126
228
|
const res = await fetch(`${API_BASE}/account`, {
|