@hasna/connectors 0.3.16 → 0.4.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/bin/index.js +71 -1
- package/bin/mcp.js +71 -1
- package/bin/serve.js +70 -0
- package/connectors/connect-asana/.env.example +11 -0
- package/connectors/connect-asana/CLAUDE.md +128 -0
- package/connectors/connect-asana/README.md +193 -0
- package/connectors/connect-asana/package.json +52 -0
- package/connectors/connect-asana/src/api/client.ts +119 -0
- package/connectors/connect-asana/src/api/index.ts +319 -0
- package/connectors/connect-asana/src/cli/index.ts +731 -0
- package/connectors/connect-asana/src/index.ts +19 -0
- package/connectors/connect-asana/src/types/index.ts +270 -0
- package/connectors/connect-asana/src/utils/config.ts +171 -0
- package/connectors/connect-asana/src/utils/output.ts +119 -0
- package/connectors/connect-asana/tsconfig.json +16 -0
- package/connectors/connect-clickup/.env.example +11 -0
- package/connectors/connect-clickup/CLAUDE.md +128 -0
- package/connectors/connect-clickup/README.md +193 -0
- package/connectors/connect-clickup/package.json +52 -0
- package/connectors/connect-clickup/src/api/client.ts +116 -0
- package/connectors/connect-clickup/src/api/index.ts +400 -0
- package/connectors/connect-clickup/src/cli/index.ts +625 -0
- package/connectors/connect-clickup/src/index.ts +19 -0
- package/connectors/connect-clickup/src/types/index.ts +591 -0
- package/connectors/connect-clickup/src/utils/config.ts +157 -0
- package/connectors/connect-clickup/src/utils/output.ts +119 -0
- package/connectors/connect-clickup/tsconfig.json +16 -0
- package/connectors/connect-confluence/.env.example +11 -0
- package/connectors/connect-confluence/CLAUDE.md +272 -0
- package/connectors/connect-confluence/README.md +193 -0
- package/connectors/connect-confluence/package.json +53 -0
- package/connectors/connect-confluence/scripts/release.ts +179 -0
- package/connectors/connect-confluence/src/api/client.ts +213 -0
- package/connectors/connect-confluence/src/api/example.ts +48 -0
- package/connectors/connect-confluence/src/api/index.ts +51 -0
- package/connectors/connect-confluence/src/cli/index.ts +254 -0
- package/connectors/connect-confluence/src/index.ts +103 -0
- package/connectors/connect-confluence/src/types/index.ts +237 -0
- package/connectors/connect-confluence/src/utils/auth.ts +274 -0
- package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
- package/connectors/connect-confluence/src/utils/config.ts +326 -0
- package/connectors/connect-confluence/src/utils/output.ts +175 -0
- package/connectors/connect-confluence/src/utils/settings.ts +114 -0
- package/connectors/connect-confluence/src/utils/storage.ts +198 -0
- package/connectors/connect-confluence/tsconfig.json +16 -0
- package/connectors/connect-jira/.env.example +11 -0
- package/connectors/connect-jira/CLAUDE.md +128 -0
- package/connectors/connect-jira/README.md +193 -0
- package/connectors/connect-jira/package.json +53 -0
- package/connectors/connect-jira/src/api/client.ts +131 -0
- package/connectors/connect-jira/src/api/index.ts +266 -0
- package/connectors/connect-jira/src/cli/index.ts +653 -0
- package/connectors/connect-jira/src/index.ts +23 -0
- package/connectors/connect-jira/src/types/index.ts +448 -0
- package/connectors/connect-jira/src/utils/config.ts +179 -0
- package/connectors/connect-jira/src/utils/output.ts +119 -0
- package/connectors/connect-jira/tsconfig.json +16 -0
- package/connectors/connect-linear/CLAUDE.md +88 -0
- package/connectors/connect-linear/README.md +201 -0
- package/connectors/connect-linear/package.json +45 -0
- package/connectors/connect-linear/src/api/client.ts +62 -0
- package/connectors/connect-linear/src/api/index.ts +46 -0
- package/connectors/connect-linear/src/api/issues.ts +247 -0
- package/connectors/connect-linear/src/api/projects.ts +179 -0
- package/connectors/connect-linear/src/api/teams.ts +125 -0
- package/connectors/connect-linear/src/api/users.ts +112 -0
- package/connectors/connect-linear/src/cli/index.ts +560 -0
- package/connectors/connect-linear/src/index.ts +27 -0
- package/connectors/connect-linear/src/types/index.ts +275 -0
- package/connectors/connect-linear/src/utils/config.ts +249 -0
- package/connectors/connect-linear/src/utils/output.ts +119 -0
- package/connectors/connect-linear/tsconfig.json +16 -0
- package/connectors/connect-slack/.env.example +7 -0
- package/connectors/connect-slack/CLAUDE.md +69 -0
- package/connectors/connect-slack/README.md +150 -0
- package/connectors/connect-slack/package.json +44 -0
- package/connectors/connect-slack/src/api/channels.ts +112 -0
- package/connectors/connect-slack/src/api/client.ts +97 -0
- package/connectors/connect-slack/src/api/index.ts +42 -0
- package/connectors/connect-slack/src/api/messages.ts +127 -0
- package/connectors/connect-slack/src/api/users.ts +110 -0
- package/connectors/connect-slack/src/cli/index.ts +494 -0
- package/connectors/connect-slack/src/index.ts +21 -0
- package/connectors/connect-slack/src/types/index.ts +263 -0
- package/connectors/connect-slack/src/utils/config.ts +297 -0
- package/connectors/connect-slack/src/utils/output.ts +119 -0
- package/connectors/connect-slack/tsconfig.json +16 -0
- package/connectors/connect-telegram/.env.example +2 -0
- package/connectors/connect-telegram/package.json +49 -0
- package/connectors/connect-todoist/.env.example +11 -0
- package/connectors/connect-todoist/CLAUDE.md +104 -0
- package/connectors/connect-todoist/README.md +193 -0
- package/connectors/connect-todoist/package.json +52 -0
- package/connectors/connect-todoist/src/api/client.ts +117 -0
- package/connectors/connect-todoist/src/api/index.ts +188 -0
- package/connectors/connect-todoist/src/cli/index.ts +990 -0
- package/connectors/connect-todoist/src/index.ts +21 -0
- package/connectors/connect-todoist/src/types/index.ts +240 -0
- package/connectors/connect-todoist/src/utils/config.ts +157 -0
- package/connectors/connect-todoist/src/utils/output.ts +119 -0
- package/connectors/connect-todoist/tsconfig.json +16 -0
- package/connectors/connect-trello/.env.example +11 -0
- package/connectors/connect-trello/CLAUDE.md +128 -0
- package/connectors/connect-trello/README.md +193 -0
- package/connectors/connect-trello/package.json +53 -0
- package/connectors/connect-trello/src/api/client.ts +128 -0
- package/connectors/connect-trello/src/api/index.ts +278 -0
- package/connectors/connect-trello/src/cli/index.ts +737 -0
- package/connectors/connect-trello/src/index.ts +21 -0
- package/connectors/connect-trello/src/types/index.ts +314 -0
- package/connectors/connect-trello/src/utils/config.ts +182 -0
- package/connectors/connect-trello/src/utils/output.ts +119 -0
- package/connectors/connect-trello/tsconfig.json +16 -0
- package/connectors/connect-whatsapp/.env.example +11 -0
- package/connectors/connect-whatsapp/CLAUDE.md +113 -0
- package/connectors/connect-whatsapp/README.md +193 -0
- package/connectors/connect-whatsapp/package.json +53 -0
- package/connectors/connect-whatsapp/src/api/client.ts +133 -0
- package/connectors/connect-whatsapp/src/api/index.ts +365 -0
- package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
- package/connectors/connect-whatsapp/src/index.ts +25 -0
- package/connectors/connect-whatsapp/src/types/index.ts +502 -0
- package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
- package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
- package/connectors/connect-whatsapp/tsconfig.json +16 -0
- package/dist/index.js +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { WhatsApp } from '../api';
|
|
5
|
+
import type { WhatsAppConfig, OutputFormat } from '../types';
|
|
6
|
+
import {
|
|
7
|
+
getAccessToken,
|
|
8
|
+
setAccessToken,
|
|
9
|
+
getPhoneNumberId,
|
|
10
|
+
setPhoneNumberId,
|
|
11
|
+
getBusinessAccountId,
|
|
12
|
+
setBusinessAccountId,
|
|
13
|
+
clearConfig,
|
|
14
|
+
getConfigDir,
|
|
15
|
+
getCurrentProfile,
|
|
16
|
+
setCurrentProfile,
|
|
17
|
+
listProfiles,
|
|
18
|
+
createProfile,
|
|
19
|
+
deleteProfile,
|
|
20
|
+
loadProfile,
|
|
21
|
+
setProfileOverride,
|
|
22
|
+
getActiveProfileName,
|
|
23
|
+
} from '../utils/config';
|
|
24
|
+
|
|
25
|
+
const program = new Command();
|
|
26
|
+
|
|
27
|
+
function getClient(): WhatsApp {
|
|
28
|
+
const accessToken = getAccessToken();
|
|
29
|
+
const phoneNumberId = getPhoneNumberId();
|
|
30
|
+
|
|
31
|
+
if (!accessToken) {
|
|
32
|
+
console.error(chalk.red('Error: Access token not configured.'));
|
|
33
|
+
console.error(chalk.yellow('Run: connect-whatsapp config set-token <access-token>'));
|
|
34
|
+
console.error(chalk.yellow('Or set WHATSAPP_ACCESS_TOKEN environment variable'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!phoneNumberId) {
|
|
39
|
+
console.error(chalk.red('Error: Phone number ID not configured.'));
|
|
40
|
+
console.error(chalk.yellow('Run: connect-whatsapp config set-phone <phone-number-id>'));
|
|
41
|
+
console.error(chalk.yellow('Or set WHATSAPP_PHONE_NUMBER_ID environment variable'));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const config: WhatsAppConfig = {
|
|
46
|
+
accessToken,
|
|
47
|
+
phoneNumberId,
|
|
48
|
+
businessAccountId: getBusinessAccountId(),
|
|
49
|
+
};
|
|
50
|
+
return new WhatsApp(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function formatOutput(data: unknown, format: OutputFormat): void {
|
|
54
|
+
if (format === 'json') {
|
|
55
|
+
console.log(JSON.stringify(data, null, 2));
|
|
56
|
+
} else {
|
|
57
|
+
console.log(data);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================
|
|
62
|
+
// Profile Commands
|
|
63
|
+
// ============================================
|
|
64
|
+
|
|
65
|
+
const profileCmd = new Command('profile')
|
|
66
|
+
.description('Manage configuration profiles');
|
|
67
|
+
|
|
68
|
+
profileCmd
|
|
69
|
+
.command('list')
|
|
70
|
+
.description('List all profiles')
|
|
71
|
+
.action(() => {
|
|
72
|
+
const profiles = listProfiles();
|
|
73
|
+
const current = getCurrentProfile();
|
|
74
|
+
|
|
75
|
+
if (profiles.length === 0) {
|
|
76
|
+
console.log(chalk.yellow('No profiles configured.'));
|
|
77
|
+
console.log(chalk.gray('Create one with: connect-whatsapp profile create <name>'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(chalk.bold('Profiles:'));
|
|
82
|
+
for (const profile of profiles) {
|
|
83
|
+
const marker = profile === current ? chalk.green(' (active)') : '';
|
|
84
|
+
console.log(` ${profile}${marker}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
profileCmd
|
|
89
|
+
.command('current')
|
|
90
|
+
.description('Show current active profile')
|
|
91
|
+
.action(() => {
|
|
92
|
+
const current = getCurrentProfile();
|
|
93
|
+
console.log(current);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
profileCmd
|
|
97
|
+
.command('use <name>')
|
|
98
|
+
.description('Switch to a profile')
|
|
99
|
+
.action((name: string) => {
|
|
100
|
+
try {
|
|
101
|
+
setCurrentProfile(name);
|
|
102
|
+
console.log(chalk.green(`Switched to profile: ${name}`));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
profileCmd
|
|
110
|
+
.command('create <name>')
|
|
111
|
+
.description('Create a new profile')
|
|
112
|
+
.action((name: string) => {
|
|
113
|
+
try {
|
|
114
|
+
const created = createProfile(name);
|
|
115
|
+
if (created) {
|
|
116
|
+
console.log(chalk.green(`Created profile: ${name}`));
|
|
117
|
+
} else {
|
|
118
|
+
console.log(chalk.yellow(`Profile already exists: ${name}`));
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
profileCmd
|
|
127
|
+
.command('delete <name>')
|
|
128
|
+
.description('Delete a profile')
|
|
129
|
+
.action((name: string) => {
|
|
130
|
+
const deleted = deleteProfile(name);
|
|
131
|
+
if (deleted) {
|
|
132
|
+
console.log(chalk.green(`Deleted profile: ${name}`));
|
|
133
|
+
} else {
|
|
134
|
+
console.log(chalk.yellow(`Could not delete profile: ${name}`));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
profileCmd
|
|
139
|
+
.command('show [name]')
|
|
140
|
+
.description('Show profile configuration')
|
|
141
|
+
.action((name?: string) => {
|
|
142
|
+
const profile = loadProfile(name);
|
|
143
|
+
const profileName = name || getCurrentProfile();
|
|
144
|
+
console.log(chalk.bold(`Profile: ${profileName}`));
|
|
145
|
+
console.log(chalk.gray('Access Token:'), profile.accessToken ? '***configured***' : 'not set');
|
|
146
|
+
console.log(chalk.gray('Phone Number ID:'), profile.phoneNumberId || 'not set');
|
|
147
|
+
console.log(chalk.gray('Business Account ID:'), profile.businessAccountId || 'not set');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ============================================
|
|
151
|
+
// Config Commands
|
|
152
|
+
// ============================================
|
|
153
|
+
|
|
154
|
+
const configCmd = new Command('config')
|
|
155
|
+
.description('Manage configuration');
|
|
156
|
+
|
|
157
|
+
configCmd
|
|
158
|
+
.command('set-token <accessToken>')
|
|
159
|
+
.description('Set the access token for current profile')
|
|
160
|
+
.action((accessToken: string) => {
|
|
161
|
+
setAccessToken(accessToken);
|
|
162
|
+
const profile = getActiveProfileName();
|
|
163
|
+
console.log(chalk.green(`Access token saved to profile: ${profile}`));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
configCmd
|
|
167
|
+
.command('set-phone <phoneNumberId>')
|
|
168
|
+
.description('Set the phone number ID for current profile')
|
|
169
|
+
.action((phoneNumberId: string) => {
|
|
170
|
+
setPhoneNumberId(phoneNumberId);
|
|
171
|
+
const profile = getActiveProfileName();
|
|
172
|
+
console.log(chalk.green(`Phone number ID saved to profile: ${profile}`));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
configCmd
|
|
176
|
+
.command('set-business <businessAccountId>')
|
|
177
|
+
.description('Set the business account ID for current profile')
|
|
178
|
+
.action((businessAccountId: string) => {
|
|
179
|
+
setBusinessAccountId(businessAccountId);
|
|
180
|
+
const profile = getActiveProfileName();
|
|
181
|
+
console.log(chalk.green(`Business account ID saved to profile: ${profile}`));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
configCmd
|
|
185
|
+
.command('show')
|
|
186
|
+
.description('Show current configuration')
|
|
187
|
+
.action(() => {
|
|
188
|
+
const profile = getCurrentProfile();
|
|
189
|
+
const accessToken = getAccessToken();
|
|
190
|
+
const phoneNumberId = getPhoneNumberId();
|
|
191
|
+
const businessAccountId = getBusinessAccountId();
|
|
192
|
+
const configDir = getConfigDir();
|
|
193
|
+
|
|
194
|
+
console.log(chalk.bold('Current Configuration:'));
|
|
195
|
+
console.log(chalk.gray('Profile:'), profile);
|
|
196
|
+
console.log(chalk.gray('Config directory:'), configDir);
|
|
197
|
+
console.log(chalk.gray('Access Token:'), accessToken ? '***configured***' : 'not set');
|
|
198
|
+
console.log(chalk.gray('Phone Number ID:'), phoneNumberId || 'not set');
|
|
199
|
+
console.log(chalk.gray('Business Account ID:'), businessAccountId || 'not set');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
configCmd
|
|
203
|
+
.command('clear')
|
|
204
|
+
.description('Clear configuration for current profile')
|
|
205
|
+
.action(() => {
|
|
206
|
+
clearConfig();
|
|
207
|
+
console.log(chalk.green('Configuration cleared.'));
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
configCmd
|
|
211
|
+
.command('path')
|
|
212
|
+
.description('Show configuration directory path')
|
|
213
|
+
.action(() => {
|
|
214
|
+
console.log(getConfigDir());
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// ============================================
|
|
218
|
+
// Message Commands
|
|
219
|
+
// ============================================
|
|
220
|
+
|
|
221
|
+
const messageCmd = new Command('message')
|
|
222
|
+
.description('Send messages');
|
|
223
|
+
|
|
224
|
+
messageCmd
|
|
225
|
+
.command('text <to> <text>')
|
|
226
|
+
.description('Send a text message')
|
|
227
|
+
.option('--preview', 'Enable URL preview')
|
|
228
|
+
.option('-r, --reply-to <messageId>', 'Reply to message ID')
|
|
229
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
230
|
+
.action(async (to: string, text: string, options) => {
|
|
231
|
+
try {
|
|
232
|
+
const client = getClient();
|
|
233
|
+
const result = await client.sendText(to, text, {
|
|
234
|
+
previewUrl: options.preview,
|
|
235
|
+
replyToMessageId: options.replyTo,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (options.format === 'json') {
|
|
239
|
+
formatOutput(result, 'json');
|
|
240
|
+
} else {
|
|
241
|
+
console.log(chalk.green('Message sent.'));
|
|
242
|
+
if (result.messages && result.messages.length > 0) {
|
|
243
|
+
console.log(chalk.gray('Message ID:'), result.messages[0].id);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
messageCmd
|
|
253
|
+
.command('image <to> <urlOrId>')
|
|
254
|
+
.description('Send an image (URL or media ID)')
|
|
255
|
+
.option('-c, --caption <caption>', 'Image caption')
|
|
256
|
+
.option('-r, --reply-to <messageId>', 'Reply to message ID')
|
|
257
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
258
|
+
.action(async (to: string, urlOrId: string, options) => {
|
|
259
|
+
try {
|
|
260
|
+
const client = getClient();
|
|
261
|
+
const media = urlOrId.startsWith('http') ? { link: urlOrId, caption: options.caption } : { id: urlOrId, caption: options.caption };
|
|
262
|
+
const result = await client.sendImage(to, media, {
|
|
263
|
+
replyToMessageId: options.replyTo,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (options.format === 'json') {
|
|
267
|
+
formatOutput(result, 'json');
|
|
268
|
+
} else {
|
|
269
|
+
console.log(chalk.green('Image sent.'));
|
|
270
|
+
if (result.messages && result.messages.length > 0) {
|
|
271
|
+
console.log(chalk.gray('Message ID:'), result.messages[0].id);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
messageCmd
|
|
281
|
+
.command('document <to> <urlOrId>')
|
|
282
|
+
.description('Send a document (URL or media ID)')
|
|
283
|
+
.option('-c, --caption <caption>', 'Document caption')
|
|
284
|
+
.option('-n, --filename <filename>', 'File name')
|
|
285
|
+
.option('-r, --reply-to <messageId>', 'Reply to message ID')
|
|
286
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
287
|
+
.action(async (to: string, urlOrId: string, options) => {
|
|
288
|
+
try {
|
|
289
|
+
const client = getClient();
|
|
290
|
+
const media = urlOrId.startsWith('http')
|
|
291
|
+
? { link: urlOrId, caption: options.caption, filename: options.filename }
|
|
292
|
+
: { id: urlOrId, caption: options.caption, filename: options.filename };
|
|
293
|
+
const result = await client.sendDocument(to, media, {
|
|
294
|
+
replyToMessageId: options.replyTo,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (options.format === 'json') {
|
|
298
|
+
formatOutput(result, 'json');
|
|
299
|
+
} else {
|
|
300
|
+
console.log(chalk.green('Document sent.'));
|
|
301
|
+
if (result.messages && result.messages.length > 0) {
|
|
302
|
+
console.log(chalk.gray('Message ID:'), result.messages[0].id);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
messageCmd
|
|
312
|
+
.command('location <to> <latitude> <longitude>')
|
|
313
|
+
.description('Send a location')
|
|
314
|
+
.option('-n, --name <name>', 'Location name')
|
|
315
|
+
.option('-a, --address <address>', 'Location address')
|
|
316
|
+
.option('-r, --reply-to <messageId>', 'Reply to message ID')
|
|
317
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
318
|
+
.action(async (to: string, latitude: string, longitude: string, options) => {
|
|
319
|
+
try {
|
|
320
|
+
const client = getClient();
|
|
321
|
+
const result = await client.sendLocation(to, {
|
|
322
|
+
latitude: Number(latitude),
|
|
323
|
+
longitude: Number(longitude),
|
|
324
|
+
name: options.name,
|
|
325
|
+
address: options.address,
|
|
326
|
+
}, {
|
|
327
|
+
replyToMessageId: options.replyTo,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (options.format === 'json') {
|
|
331
|
+
formatOutput(result, 'json');
|
|
332
|
+
} else {
|
|
333
|
+
console.log(chalk.green('Location sent.'));
|
|
334
|
+
if (result.messages && result.messages.length > 0) {
|
|
335
|
+
console.log(chalk.gray('Message ID:'), result.messages[0].id);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
messageCmd
|
|
345
|
+
.command('template <to> <templateName> <languageCode>')
|
|
346
|
+
.description('Send a template message')
|
|
347
|
+
.option('-r, --reply-to <messageId>', 'Reply to message ID')
|
|
348
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
349
|
+
.action(async (to: string, templateName: string, languageCode: string, options) => {
|
|
350
|
+
try {
|
|
351
|
+
const client = getClient();
|
|
352
|
+
const result = await client.sendTemplate(to, {
|
|
353
|
+
name: templateName,
|
|
354
|
+
language: { code: languageCode },
|
|
355
|
+
}, {
|
|
356
|
+
replyToMessageId: options.replyTo,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (options.format === 'json') {
|
|
360
|
+
formatOutput(result, 'json');
|
|
361
|
+
} else {
|
|
362
|
+
console.log(chalk.green('Template message sent.'));
|
|
363
|
+
if (result.messages && result.messages.length > 0) {
|
|
364
|
+
console.log(chalk.gray('Message ID:'), result.messages[0].id);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
messageCmd
|
|
374
|
+
.command('react <to> <messageId> <emoji>')
|
|
375
|
+
.description('React to a message')
|
|
376
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
377
|
+
.action(async (to: string, messageId: string, emoji: string, options) => {
|
|
378
|
+
try {
|
|
379
|
+
const client = getClient();
|
|
380
|
+
const result = await client.sendReaction(to, {
|
|
381
|
+
message_id: messageId,
|
|
382
|
+
emoji,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
if (options.format === 'json') {
|
|
386
|
+
formatOutput(result, 'json');
|
|
387
|
+
} else {
|
|
388
|
+
console.log(chalk.green('Reaction sent.'));
|
|
389
|
+
}
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
messageCmd
|
|
397
|
+
.command('read <messageId>')
|
|
398
|
+
.description('Mark a message as read')
|
|
399
|
+
.action(async (messageId: string) => {
|
|
400
|
+
try {
|
|
401
|
+
const client = getClient();
|
|
402
|
+
await client.markAsRead(messageId);
|
|
403
|
+
console.log(chalk.green('Message marked as read.'));
|
|
404
|
+
} catch (error) {
|
|
405
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// ============================================
|
|
411
|
+
// Media Commands
|
|
412
|
+
// ============================================
|
|
413
|
+
|
|
414
|
+
const mediaCmd = new Command('media')
|
|
415
|
+
.description('Manage media');
|
|
416
|
+
|
|
417
|
+
mediaCmd
|
|
418
|
+
.command('get <mediaId>')
|
|
419
|
+
.description('Get media URL')
|
|
420
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
421
|
+
.action(async (mediaId: string, options) => {
|
|
422
|
+
try {
|
|
423
|
+
const client = getClient();
|
|
424
|
+
const result = await client.getMediaUrl(mediaId);
|
|
425
|
+
|
|
426
|
+
if (options.format === 'json') {
|
|
427
|
+
formatOutput(result, 'json');
|
|
428
|
+
} else {
|
|
429
|
+
console.log(chalk.bold('Media:'));
|
|
430
|
+
console.log(chalk.gray('ID:'), result.id);
|
|
431
|
+
console.log(chalk.gray('URL:'), result.url);
|
|
432
|
+
console.log(chalk.gray('MIME Type:'), result.mime_type);
|
|
433
|
+
console.log(chalk.gray('Size:'), result.file_size);
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
mediaCmd
|
|
442
|
+
.command('delete <mediaId>')
|
|
443
|
+
.description('Delete media')
|
|
444
|
+
.action(async (mediaId: string) => {
|
|
445
|
+
try {
|
|
446
|
+
const client = getClient();
|
|
447
|
+
await client.deleteMedia(mediaId);
|
|
448
|
+
console.log(chalk.green('Media deleted.'));
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// ============================================
|
|
456
|
+
// Business Profile Commands
|
|
457
|
+
// ============================================
|
|
458
|
+
|
|
459
|
+
const businessCmd = new Command('business')
|
|
460
|
+
.description('Manage business profile');
|
|
461
|
+
|
|
462
|
+
businessCmd
|
|
463
|
+
.command('get')
|
|
464
|
+
.description('Get business profile')
|
|
465
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
466
|
+
.action(async (options) => {
|
|
467
|
+
try {
|
|
468
|
+
const client = getClient();
|
|
469
|
+
const result = await client.getBusinessProfile();
|
|
470
|
+
|
|
471
|
+
if (options.format === 'json') {
|
|
472
|
+
formatOutput(result, 'json');
|
|
473
|
+
} else {
|
|
474
|
+
if (result.data && result.data.length > 0) {
|
|
475
|
+
const profile = result.data[0];
|
|
476
|
+
console.log(chalk.bold('Business Profile:'));
|
|
477
|
+
if (profile.about) console.log(chalk.gray('About:'), profile.about);
|
|
478
|
+
if (profile.address) console.log(chalk.gray('Address:'), profile.address);
|
|
479
|
+
if (profile.description) console.log(chalk.gray('Description:'), profile.description);
|
|
480
|
+
if (profile.email) console.log(chalk.gray('Email:'), profile.email);
|
|
481
|
+
if (profile.vertical) console.log(chalk.gray('Industry:'), profile.vertical);
|
|
482
|
+
if (profile.websites && profile.websites.length > 0) {
|
|
483
|
+
console.log(chalk.gray('Websites:'), profile.websites.join(', '));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
businessCmd
|
|
494
|
+
.command('update')
|
|
495
|
+
.description('Update business profile')
|
|
496
|
+
.option('-a, --about <about>', 'About text')
|
|
497
|
+
.option('--address <address>', 'Business address')
|
|
498
|
+
.option('-d, --description <description>', 'Business description')
|
|
499
|
+
.option('-e, --email <email>', 'Business email')
|
|
500
|
+
.option('-v, --vertical <vertical>', 'Business vertical/industry')
|
|
501
|
+
.action(async (options) => {
|
|
502
|
+
try {
|
|
503
|
+
const client = getClient();
|
|
504
|
+
const profile: Record<string, string | string[] | undefined> = {};
|
|
505
|
+
if (options.about) profile.about = options.about;
|
|
506
|
+
if (options.address) profile.address = options.address;
|
|
507
|
+
if (options.description) profile.description = options.description;
|
|
508
|
+
if (options.email) profile.email = options.email;
|
|
509
|
+
if (options.vertical) profile.vertical = options.vertical;
|
|
510
|
+
|
|
511
|
+
await client.updateBusinessProfile(profile);
|
|
512
|
+
console.log(chalk.green('Business profile updated.'));
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// ============================================
|
|
520
|
+
// Phone Number Commands
|
|
521
|
+
// ============================================
|
|
522
|
+
|
|
523
|
+
const phoneCmd = new Command('phone')
|
|
524
|
+
.description('Manage phone numbers');
|
|
525
|
+
|
|
526
|
+
phoneCmd
|
|
527
|
+
.command('get')
|
|
528
|
+
.description('Get current phone number info')
|
|
529
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
530
|
+
.action(async (options) => {
|
|
531
|
+
try {
|
|
532
|
+
const client = getClient();
|
|
533
|
+
const result = await client.getPhoneNumber();
|
|
534
|
+
|
|
535
|
+
if (options.format === 'json') {
|
|
536
|
+
formatOutput(result, 'json');
|
|
537
|
+
} else {
|
|
538
|
+
console.log(chalk.bold('Phone Number:'));
|
|
539
|
+
console.log(chalk.gray('ID:'), result.id);
|
|
540
|
+
console.log(chalk.gray('Display:'), result.display_phone_number);
|
|
541
|
+
console.log(chalk.gray('Verified Name:'), result.verified_name);
|
|
542
|
+
console.log(chalk.gray('Quality:'), result.quality_rating);
|
|
543
|
+
console.log(chalk.gray('Status:'), result.code_verification_status);
|
|
544
|
+
}
|
|
545
|
+
} catch (error) {
|
|
546
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
547
|
+
process.exit(1);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
phoneCmd
|
|
552
|
+
.command('list')
|
|
553
|
+
.description('List all phone numbers')
|
|
554
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
555
|
+
.action(async (options) => {
|
|
556
|
+
try {
|
|
557
|
+
const client = getClient();
|
|
558
|
+
const result = await client.listPhoneNumbers();
|
|
559
|
+
|
|
560
|
+
if (options.format === 'json') {
|
|
561
|
+
formatOutput(result, 'json');
|
|
562
|
+
} else {
|
|
563
|
+
if (result.data.length === 0) {
|
|
564
|
+
console.log(chalk.yellow('No phone numbers found.'));
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
console.log(chalk.bold(`Phone Numbers (${result.data.length}):\n`));
|
|
568
|
+
for (const phone of result.data) {
|
|
569
|
+
console.log(chalk.cyan(phone.display_phone_number));
|
|
570
|
+
console.log(chalk.gray(` ID: ${phone.id}`));
|
|
571
|
+
console.log(chalk.gray(` Name: ${phone.verified_name}`));
|
|
572
|
+
console.log(chalk.gray(` Quality: ${phone.quality_rating}`));
|
|
573
|
+
console.log();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
578
|
+
process.exit(1);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// ============================================
|
|
583
|
+
// Template Commands
|
|
584
|
+
// ============================================
|
|
585
|
+
|
|
586
|
+
const templateCmd = new Command('template')
|
|
587
|
+
.description('Manage message templates');
|
|
588
|
+
|
|
589
|
+
templateCmd
|
|
590
|
+
.command('list')
|
|
591
|
+
.description('List message templates')
|
|
592
|
+
.option('-l, --limit <limit>', 'Maximum number of templates', '25')
|
|
593
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
594
|
+
.action(async (options) => {
|
|
595
|
+
try {
|
|
596
|
+
const client = getClient();
|
|
597
|
+
const result = await client.listTemplates({
|
|
598
|
+
limit: parseInt(options.limit),
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
if (options.format === 'json') {
|
|
602
|
+
formatOutput(result, 'json');
|
|
603
|
+
} else {
|
|
604
|
+
if (result.data.length === 0) {
|
|
605
|
+
console.log(chalk.yellow('No templates found.'));
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
console.log(chalk.bold(`Templates (${result.data.length}):\n`));
|
|
609
|
+
for (const template of result.data) {
|
|
610
|
+
const statusColor = template.status === 'APPROVED' ? chalk.green : template.status === 'PENDING' ? chalk.yellow : chalk.red;
|
|
611
|
+
console.log(`${chalk.cyan(template.name)} ${statusColor(`[${template.status}]`)}`);
|
|
612
|
+
console.log(chalk.gray(` ID: ${template.id}`));
|
|
613
|
+
console.log(chalk.gray(` Category: ${template.category}`));
|
|
614
|
+
console.log(chalk.gray(` Language: ${template.language}`));
|
|
615
|
+
console.log();
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
} catch (error) {
|
|
619
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
620
|
+
process.exit(1);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
templateCmd
|
|
625
|
+
.command('get <templateId>')
|
|
626
|
+
.description('Get template details')
|
|
627
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
628
|
+
.action(async (templateId: string, options) => {
|
|
629
|
+
try {
|
|
630
|
+
const client = getClient();
|
|
631
|
+
const result = await client.getTemplate(templateId);
|
|
632
|
+
|
|
633
|
+
if (options.format === 'json') {
|
|
634
|
+
formatOutput(result, 'json');
|
|
635
|
+
} else {
|
|
636
|
+
console.log(chalk.bold(result.name));
|
|
637
|
+
console.log(chalk.gray('ID:'), result.id);
|
|
638
|
+
console.log(chalk.gray('Status:'), result.status);
|
|
639
|
+
console.log(chalk.gray('Category:'), result.category);
|
|
640
|
+
console.log(chalk.gray('Language:'), result.language);
|
|
641
|
+
}
|
|
642
|
+
} catch (error) {
|
|
643
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
644
|
+
process.exit(1);
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
templateCmd
|
|
649
|
+
.command('delete <templateName>')
|
|
650
|
+
.description('Delete a template')
|
|
651
|
+
.action(async (templateName: string) => {
|
|
652
|
+
try {
|
|
653
|
+
const client = getClient();
|
|
654
|
+
await client.deleteTemplate(templateName);
|
|
655
|
+
console.log(chalk.green('Template deleted.'));
|
|
656
|
+
} catch (error) {
|
|
657
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// ============================================
|
|
663
|
+
// Main Program
|
|
664
|
+
// ============================================
|
|
665
|
+
|
|
666
|
+
program
|
|
667
|
+
.name('connect-whatsapp')
|
|
668
|
+
.description('WhatsApp Business Cloud connector - Send messages, manage templates, and handle webhooks')
|
|
669
|
+
.version('0.0.1')
|
|
670
|
+
.option('--profile <profile>', 'Use a specific profile')
|
|
671
|
+
.hook('preAction', (thisCommand) => {
|
|
672
|
+
const opts = thisCommand.opts();
|
|
673
|
+
if (opts.profile) {
|
|
674
|
+
setProfileOverride(opts.profile);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
program.addCommand(profileCmd);
|
|
679
|
+
program.addCommand(configCmd);
|
|
680
|
+
program.addCommand(messageCmd);
|
|
681
|
+
program.addCommand(mediaCmd);
|
|
682
|
+
program.addCommand(businessCmd);
|
|
683
|
+
program.addCommand(phoneCmd);
|
|
684
|
+
program.addCommand(templateCmd);
|
|
685
|
+
|
|
686
|
+
program.parse();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// WhatsApp Business Cloud Connector
|
|
2
|
+
// Send messages, manage templates, and handle webhooks
|
|
3
|
+
|
|
4
|
+
export { WhatsApp, WhatsAppClient } from './api';
|
|
5
|
+
export * from './types';
|
|
6
|
+
|
|
7
|
+
// Export config utilities
|
|
8
|
+
export {
|
|
9
|
+
getAccessToken,
|
|
10
|
+
setAccessToken,
|
|
11
|
+
getPhoneNumberId,
|
|
12
|
+
setPhoneNumberId,
|
|
13
|
+
getBusinessAccountId,
|
|
14
|
+
setBusinessAccountId,
|
|
15
|
+
getCurrentProfile,
|
|
16
|
+
setCurrentProfile,
|
|
17
|
+
listProfiles,
|
|
18
|
+
createProfile,
|
|
19
|
+
deleteProfile,
|
|
20
|
+
loadProfile,
|
|
21
|
+
saveProfile,
|
|
22
|
+
clearConfig,
|
|
23
|
+
getConfigDir,
|
|
24
|
+
getActiveProfileName,
|
|
25
|
+
} from './utils/config';
|