@ariaflowagents/messaging-meta 0.8.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 +236 -0
- package/dist/graph-api/client.d.ts +125 -0
- package/dist/graph-api/client.d.ts.map +1 -0
- package/dist/graph-api/client.js +204 -0
- package/dist/graph-api/client.js.map +1 -0
- package/dist/graph-api/errors.d.ts +41 -0
- package/dist/graph-api/errors.d.ts.map +1 -0
- package/dist/graph-api/errors.js +72 -0
- package/dist/graph-api/errors.js.map +1 -0
- package/dist/graph-api/index.d.ts +15 -0
- package/dist/graph-api/index.d.ts.map +1 -0
- package/dist/graph-api/index.js +11 -0
- package/dist/graph-api/index.js.map +1 -0
- package/dist/graph-api/rate-limiter.d.ts +90 -0
- package/dist/graph-api/rate-limiter.d.ts.map +1 -0
- package/dist/graph-api/rate-limiter.js +172 -0
- package/dist/graph-api/rate-limiter.js.map +1 -0
- package/dist/graph-api/retry.d.ts +55 -0
- package/dist/graph-api/retry.d.ts.map +1 -0
- package/dist/graph-api/retry.js +103 -0
- package/dist/graph-api/retry.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/instagram/client.d.ts +365 -0
- package/dist/instagram/client.d.ts.map +1 -0
- package/dist/instagram/client.js +857 -0
- package/dist/instagram/client.js.map +1 -0
- package/dist/instagram/format.d.ts +62 -0
- package/dist/instagram/format.d.ts.map +1 -0
- package/dist/instagram/format.js +92 -0
- package/dist/instagram/format.js.map +1 -0
- package/dist/instagram/ice-breakers.d.ts +63 -0
- package/dist/instagram/ice-breakers.d.ts.map +1 -0
- package/dist/instagram/ice-breakers.js +87 -0
- package/dist/instagram/ice-breakers.js.map +1 -0
- package/dist/instagram/index.d.ts +44 -0
- package/dist/instagram/index.d.ts.map +1 -0
- package/dist/instagram/index.js +46 -0
- package/dist/instagram/index.js.map +1 -0
- package/dist/instagram/types.d.ts +188 -0
- package/dist/instagram/types.d.ts.map +1 -0
- package/dist/instagram/types.js +19 -0
- package/dist/instagram/types.js.map +1 -0
- package/dist/messenger/client.d.ts +339 -0
- package/dist/messenger/client.d.ts.map +1 -0
- package/dist/messenger/client.js +782 -0
- package/dist/messenger/client.js.map +1 -0
- package/dist/messenger/format.d.ts +69 -0
- package/dist/messenger/format.d.ts.map +1 -0
- package/dist/messenger/format.js +98 -0
- package/dist/messenger/format.js.map +1 -0
- package/dist/messenger/index.d.ts +34 -0
- package/dist/messenger/index.d.ts.map +1 -0
- package/dist/messenger/index.js +35 -0
- package/dist/messenger/index.js.map +1 -0
- package/dist/messenger/types.d.ts +181 -0
- package/dist/messenger/types.d.ts.map +1 -0
- package/dist/messenger/types.js +10 -0
- package/dist/messenger/types.js.map +1 -0
- package/dist/server.d.ts +31 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +29 -0
- package/dist/server.js.map +1 -0
- package/dist/webhook/index.d.ts +10 -0
- package/dist/webhook/index.d.ts.map +1 -0
- package/dist/webhook/index.js +8 -0
- package/dist/webhook/index.js.map +1 -0
- package/dist/webhook/normalizer.d.ts +169 -0
- package/dist/webhook/normalizer.d.ts.map +1 -0
- package/dist/webhook/normalizer.js +301 -0
- package/dist/webhook/normalizer.js.map +1 -0
- package/dist/webhook/verifier.d.ts +45 -0
- package/dist/webhook/verifier.d.ts.map +1 -0
- package/dist/webhook/verifier.js +62 -0
- package/dist/webhook/verifier.js.map +1 -0
- package/dist/whatsapp/client.d.ts +481 -0
- package/dist/whatsapp/client.d.ts.map +1 -0
- package/dist/whatsapp/client.js +1043 -0
- package/dist/whatsapp/client.js.map +1 -0
- package/dist/whatsapp/flows.d.ts +74 -0
- package/dist/whatsapp/flows.d.ts.map +1 -0
- package/dist/whatsapp/flows.js +77 -0
- package/dist/whatsapp/flows.js.map +1 -0
- package/dist/whatsapp/format.d.ts +78 -0
- package/dist/whatsapp/format.d.ts.map +1 -0
- package/dist/whatsapp/format.js +195 -0
- package/dist/whatsapp/format.js.map +1 -0
- package/dist/whatsapp/index.d.ts +39 -0
- package/dist/whatsapp/index.d.ts.map +1 -0
- package/dist/whatsapp/index.js +42 -0
- package/dist/whatsapp/index.js.map +1 -0
- package/dist/whatsapp/split.d.ts +35 -0
- package/dist/whatsapp/split.d.ts.map +1 -0
- package/dist/whatsapp/split.js +76 -0
- package/dist/whatsapp/split.js.map +1 -0
- package/dist/whatsapp/templates.d.ts +129 -0
- package/dist/whatsapp/templates.d.ts.map +1 -0
- package/dist/whatsapp/templates.js +125 -0
- package/dist/whatsapp/templates.js.map +1 -0
- package/dist/whatsapp/types.d.ts +440 -0
- package/dist/whatsapp/types.d.ts.map +1 -0
- package/dist/whatsapp/types.js +11 -0
- package/dist/whatsapp/types.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# @ariaflowagents/messaging-meta
|
|
2
|
+
|
|
3
|
+
Meta platform clients (WhatsApp, Messenger, Instagram) for AriaFlow messaging. Each client implements the `PlatformClient` interface from `@ariaflowagents/messaging`.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ariaflowagents/messaging-meta @ariaflowagents/messaging
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## WhatsApp
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createWhatsAppClient } from '@ariaflowagents/messaging-meta/whatsapp';
|
|
15
|
+
|
|
16
|
+
const whatsapp = createWhatsAppClient({
|
|
17
|
+
accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
|
|
18
|
+
appSecret: process.env.META_APP_SECRET!,
|
|
19
|
+
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,
|
|
20
|
+
verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Listen for messages
|
|
24
|
+
whatsapp.onMessage(async (msg) => {
|
|
25
|
+
await whatsapp.markAsRead(msg.id);
|
|
26
|
+
await whatsapp.sendText(msg.from.phone!, `You said: ${msg.text}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Mount webhook (Hono)
|
|
30
|
+
app.route('/whatsapp', whatsapp.webhookRouter());
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Templates and Interactive Messages
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { buildTemplateSendPayload } from '@ariaflowagents/messaging-meta/whatsapp';
|
|
37
|
+
|
|
38
|
+
// Template
|
|
39
|
+
await whatsapp.sendTemplate('+1234567890', buildTemplateSendPayload({
|
|
40
|
+
name: 'order_confirmation',
|
|
41
|
+
language: { code: 'en_US' },
|
|
42
|
+
components: [{ type: 'body', parameters: [{ type: 'text', text: 'ORD-1234' }] }],
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// Buttons
|
|
46
|
+
await whatsapp.sendInteractiveButtons('+1234567890', {
|
|
47
|
+
body: 'How can we help?',
|
|
48
|
+
buttons: [{ id: 'sales', title: 'Sales' }, { id: 'support', title: 'Support' }],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// List
|
|
52
|
+
await whatsapp.sendListMessage('+1234567890', {
|
|
53
|
+
body: 'Select a department',
|
|
54
|
+
button: 'View Options',
|
|
55
|
+
sections: [{ title: 'Departments', rows: [{ id: 'eng', title: 'Engineering' }] }],
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### WhatsApp-Specific Methods
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `sendText(to, text)` | Send text (auto-splits at 4096 chars) |
|
|
64
|
+
| `sendMedia(to, media)` | Send image, video, audio, document, sticker |
|
|
65
|
+
| `sendTemplate(to, template)` | Send approved template message |
|
|
66
|
+
| `sendTextOrTemplate(to, opts)` | Send text if window open, template if closed |
|
|
67
|
+
| `sendInteractiveButtons(to, msg)` | Send button message (max 3) |
|
|
68
|
+
| `sendListMessage(to, list)` | Send list picker |
|
|
69
|
+
| `sendCTAButton(to, cta)` | Send call-to-action URL button |
|
|
70
|
+
| `sendInteractiveFlow(to, flow)` | Launch a WhatsApp Flow |
|
|
71
|
+
| `sendReaction(to, messageId, emoji)` | React to a message |
|
|
72
|
+
| `sendLocation(to, location)` | Send a location pin |
|
|
73
|
+
| `sendContacts(to, contacts)` | Send contact cards |
|
|
74
|
+
| `uploadMedia(file, options)` | Upload media, get `MediaHandle` |
|
|
75
|
+
| `downloadMedia(mediaId)` | Download media by ID |
|
|
76
|
+
| `markAsRead(messageId)` | Mark message as read |
|
|
77
|
+
|
|
78
|
+
## Messenger
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { createMessengerClient } from '@ariaflowagents/messaging-meta/messenger';
|
|
82
|
+
|
|
83
|
+
const messenger = createMessengerClient({
|
|
84
|
+
pageAccessToken: process.env.MESSENGER_PAGE_ACCESS_TOKEN!,
|
|
85
|
+
appSecret: process.env.META_APP_SECRET!,
|
|
86
|
+
pageId: process.env.MESSENGER_PAGE_ID!,
|
|
87
|
+
verifyToken: process.env.MESSENGER_VERIFY_TOKEN!,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
messenger.onMessage(async (msg) => {
|
|
91
|
+
await messenger.sendText(msg.from.id, `Echo: ${msg.text}`);
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Templates and Quick Replies
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
// Button template
|
|
99
|
+
await messenger.sendButtonTemplate(psid, {
|
|
100
|
+
text: 'What would you like to do?',
|
|
101
|
+
buttons: [{ type: 'postback', title: 'Track Order', payload: 'TRACK' }],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Generic template (carousel)
|
|
105
|
+
await messenger.sendGenericTemplate(psid, {
|
|
106
|
+
elements: [{ title: 'Product A', subtitle: '$29.99', image_url: 'https://example.com/a.jpg' }],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Quick replies
|
|
110
|
+
await messenger.sendQuickReplies(psid, 'Choose one:', [
|
|
111
|
+
{ content_type: 'text', title: 'Yes', payload: 'YES' },
|
|
112
|
+
{ content_type: 'text', title: 'No', payload: 'NO' },
|
|
113
|
+
]);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Messenger-Specific Methods
|
|
117
|
+
|
|
118
|
+
| Method | Description |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `sendText(to, text)` | Send text (max 2000 chars) |
|
|
121
|
+
| `sendMedia(to, media)` | Send image, video, audio, or file |
|
|
122
|
+
| `sendButtonTemplate(to, template)` | Send button template (max 3 buttons) |
|
|
123
|
+
| `sendGenericTemplate(to, template)` | Send carousel (max 10 elements) |
|
|
124
|
+
| `sendQuickReplies(to, text, replies)` | Send quick reply buttons |
|
|
125
|
+
| `sendSenderAction(to, action)` | Send typing_on, typing_off, mark_seen |
|
|
126
|
+
| `getUserProfile(psid)` | Fetch user's name and profile picture |
|
|
127
|
+
| `uploadMedia(file, options)` | Upload media for reuse |
|
|
128
|
+
| `downloadMedia(mediaUrl)` | Download media by URL |
|
|
129
|
+
|
|
130
|
+
## Instagram
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { createInstagramClient } from '@ariaflowagents/messaging-meta/instagram';
|
|
134
|
+
|
|
135
|
+
const instagram = createInstagramClient({
|
|
136
|
+
accessToken: process.env.INSTAGRAM_ACCESS_TOKEN!,
|
|
137
|
+
appSecret: process.env.META_APP_SECRET!,
|
|
138
|
+
igId: process.env.INSTAGRAM_ACCOUNT_ID!,
|
|
139
|
+
verifyToken: process.env.INSTAGRAM_VERIFY_TOKEN!,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
instagram.onMessage(async (msg) => {
|
|
143
|
+
await instagram.sendText(msg.from.id, `Thanks for your message!`);
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Ice Breakers
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// Set conversation starters
|
|
151
|
+
await instagram.iceBreakers.set([
|
|
152
|
+
{ question: 'What are your hours?', payload: 'HOURS' },
|
|
153
|
+
{ question: 'Track my order', payload: 'TRACK' },
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
// Get current ice breakers
|
|
157
|
+
const breakers = await instagram.iceBreakers.get();
|
|
158
|
+
|
|
159
|
+
// Delete all
|
|
160
|
+
await instagram.iceBreakers.delete();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Instagram-Specific Methods
|
|
164
|
+
|
|
165
|
+
| Method | Description |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `sendText(to, text)` | Send text (auto-splits at 1000 bytes) |
|
|
168
|
+
| `sendMedia(to, media)` | Send image attachment only |
|
|
169
|
+
| `sendQuickReplies(to, text, replies)` | Send quick reply buttons (max 13) |
|
|
170
|
+
| `sendGenericTemplate(to, template)` | Send carousel template |
|
|
171
|
+
| `sendButtonTemplate(to, template)` | Send button template |
|
|
172
|
+
| `sendPrivateReply(options)` | Reply privately to a comment |
|
|
173
|
+
| `sendTextWithTag(to, text, tag)` | Send with message tag (e.g. HUMAN_AGENT) |
|
|
174
|
+
| `iceBreakers.set(breakers)` | Set conversation starters |
|
|
175
|
+
| `iceBreakers.get()` | Get current ice breakers |
|
|
176
|
+
| `iceBreakers.delete()` | Remove all ice breakers |
|
|
177
|
+
|
|
178
|
+
## Standalone Webhook Utilities
|
|
179
|
+
|
|
180
|
+
For server-side webhook handling without the full client:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { verifySignature, normalizeWebhook } from '@ariaflowagents/messaging-meta/server';
|
|
184
|
+
|
|
185
|
+
app.post('/webhook', async (c) => {
|
|
186
|
+
const rawBody = await c.req.text();
|
|
187
|
+
const sig = c.req.header('x-hub-signature-256') ?? '';
|
|
188
|
+
|
|
189
|
+
if (!verifySignature({ appSecret, rawBody, signatureHeader: sig })) {
|
|
190
|
+
return c.text('Invalid signature', 403);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const events = normalizeWebhook(JSON.parse(rawBody));
|
|
194
|
+
// events.messages, events.statuses, events.reactions
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Platform Comparison
|
|
199
|
+
|
|
200
|
+
| Feature | WhatsApp | Messenger | Instagram |
|
|
201
|
+
|---|:---:|:---:|:---:|
|
|
202
|
+
| Text limit | 4096 chars | 2000 chars | 1000 bytes |
|
|
203
|
+
| Media types | all | all | image only |
|
|
204
|
+
| Buttons / List / Carousel | 3 / yes / no | 3 / no / 10 | 3 / no / 10 |
|
|
205
|
+
| Quick replies | no | 13 | 13 |
|
|
206
|
+
| Templates | yes | yes | yes |
|
|
207
|
+
| Typing + reactions | yes | yes | yes |
|
|
208
|
+
| Message window | 24h | none | 7d (tag) |
|
|
209
|
+
| Unique features | Flows, CTA, lists | Personas, profiles | Ice breakers, private reply |
|
|
210
|
+
|
|
211
|
+
## AriaFlow Integration
|
|
212
|
+
|
|
213
|
+
Use with `@ariaflowagents/messaging` to connect clients to the AriaFlow runtime:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { createMessagingRouter } from '@ariaflowagents/messaging';
|
|
217
|
+
|
|
218
|
+
const router = createMessagingRouter({ runtime, platforms: { whatsapp, messenger, instagram } });
|
|
219
|
+
app.route('/messaging', router);
|
|
220
|
+
// Webhooks: /messaging/{whatsapp,messenger,instagram}/webhook
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Subpath Exports
|
|
224
|
+
|
|
225
|
+
| Import path | Contents |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `@ariaflowagents/messaging-meta` | GraphAPIClient, errors, webhook utilities |
|
|
228
|
+
| `@ariaflowagents/messaging-meta/whatsapp` | WhatsAppClient, templates, flows, format converter |
|
|
229
|
+
| `@ariaflowagents/messaging-meta/messenger` | MessengerClient, format converter |
|
|
230
|
+
| `@ariaflowagents/messaging-meta/instagram` | InstagramClient, ice breakers, format converter |
|
|
231
|
+
| `@ariaflowagents/messaging-meta/server` | `verifySignature` + `normalizeWebhook` only |
|
|
232
|
+
|
|
233
|
+
## Related
|
|
234
|
+
|
|
235
|
+
- [`@ariaflowagents/messaging`](../ariaflow-messaging) -- Core interfaces and runtime adapter
|
|
236
|
+
- [`@ariaflowagents/core`](../ariaflow-core) -- Runtime, agents, flows
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module graph-api/client
|
|
3
|
+
*
|
|
4
|
+
* Typed HTTP client for Meta's Graph API.
|
|
5
|
+
*
|
|
6
|
+
* Shared by WhatsApp, Messenger, and Instagram platform clients. Handles
|
|
7
|
+
* authentication, retry, rate limiting, and error classification transparently.
|
|
8
|
+
*/
|
|
9
|
+
import type { RetryConfig } from './retry.js';
|
|
10
|
+
import type { RateLimiterConfig } from './rate-limiter.js';
|
|
11
|
+
/** Minimal structured logger accepted by the client. */
|
|
12
|
+
export interface Logger {
|
|
13
|
+
debug(message: string, ...args: unknown[]): void;
|
|
14
|
+
info(message: string, ...args: unknown[]): void;
|
|
15
|
+
warn(message: string, ...args: unknown[]): void;
|
|
16
|
+
error(message: string, ...args: unknown[]): void;
|
|
17
|
+
}
|
|
18
|
+
/** Configuration for {@link GraphAPIClient}. */
|
|
19
|
+
export interface GraphAPIClientConfig {
|
|
20
|
+
/** Long-lived or system-user access token for the Graph API. */
|
|
21
|
+
accessToken: string;
|
|
22
|
+
/** App secret used for webhook signature verification and appsecret_proof. */
|
|
23
|
+
appSecret: string;
|
|
24
|
+
/** Graph API version (e.g. `"v21.0"`). Default `"v21.0"`. */
|
|
25
|
+
apiVersion?: string;
|
|
26
|
+
/** Base URL for the Graph API. Default `"https://graph.facebook.com"`. */
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
/** Retry configuration. Uses sensible defaults when omitted. */
|
|
29
|
+
retry?: Partial<RetryConfig>;
|
|
30
|
+
/** Rate limiter configuration. Uses sensible defaults when omitted. */
|
|
31
|
+
rateLimiter?: Partial<RateLimiterConfig>;
|
|
32
|
+
/** Optional logger for request/response tracing. */
|
|
33
|
+
logger?: Logger;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Typed HTTP client for Meta's Graph API.
|
|
37
|
+
*
|
|
38
|
+
* Provides `get`, `post`, `postFormData`, and `fetchBinary` methods that
|
|
39
|
+
* handle authentication, exponential-backoff retry, token-bucket rate
|
|
40
|
+
* limiting, and error classification automatically.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const client = new GraphAPIClient({
|
|
45
|
+
* accessToken: process.env.META_ACCESS_TOKEN!,
|
|
46
|
+
* appSecret: process.env.META_APP_SECRET!,
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* const profile = await client.get<{ name: string }>('me', { fields: 'name' });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare class GraphAPIClient {
|
|
53
|
+
private readonly accessToken;
|
|
54
|
+
private readonly appSecret;
|
|
55
|
+
private readonly apiVersion;
|
|
56
|
+
private readonly baseUrl;
|
|
57
|
+
private readonly retryQueue;
|
|
58
|
+
private readonly rateLimiter;
|
|
59
|
+
private readonly logger?;
|
|
60
|
+
constructor(config: GraphAPIClientConfig);
|
|
61
|
+
/**
|
|
62
|
+
* Perform an authenticated GET request against the Graph API.
|
|
63
|
+
*
|
|
64
|
+
* @typeParam T - Expected response shape.
|
|
65
|
+
* @param endpoint - API endpoint relative to the version root (e.g. `"me/messages"`).
|
|
66
|
+
* @param params - Optional query-string parameters.
|
|
67
|
+
* @returns Parsed JSON response body.
|
|
68
|
+
*/
|
|
69
|
+
get<T>(endpoint: string, params?: Record<string, string>): Promise<T>;
|
|
70
|
+
/**
|
|
71
|
+
* Perform an authenticated POST request with a JSON body.
|
|
72
|
+
*
|
|
73
|
+
* @typeParam T - Expected response shape.
|
|
74
|
+
* @param endpoint - API endpoint relative to the version root.
|
|
75
|
+
* @param body - JSON-serializable request body.
|
|
76
|
+
* @returns Parsed JSON response body.
|
|
77
|
+
*/
|
|
78
|
+
post<T>(endpoint: string, body: unknown): Promise<T>;
|
|
79
|
+
/**
|
|
80
|
+
* Perform an authenticated POST request with form-data (multipart).
|
|
81
|
+
*
|
|
82
|
+
* Primarily used for media uploads where the payload includes binary data.
|
|
83
|
+
*
|
|
84
|
+
* @typeParam T - Expected response shape.
|
|
85
|
+
* @param endpoint - API endpoint relative to the version root.
|
|
86
|
+
* @param formData - `FormData` instance containing the fields/files to upload.
|
|
87
|
+
* @returns Parsed JSON response body.
|
|
88
|
+
*/
|
|
89
|
+
postFormData<T>(endpoint: string, formData: FormData): Promise<T>;
|
|
90
|
+
/**
|
|
91
|
+
* Download a binary resource from a direct URL (e.g. media CDN link).
|
|
92
|
+
*
|
|
93
|
+
* This method does NOT prepend the Graph API base URL — pass the full URL
|
|
94
|
+
* returned by the media endpoint.
|
|
95
|
+
*
|
|
96
|
+
* @param url - Absolute URL to fetch.
|
|
97
|
+
* @returns Raw binary content as a `Buffer`.
|
|
98
|
+
*/
|
|
99
|
+
fetchBinary(url: string): Promise<Buffer>;
|
|
100
|
+
/**
|
|
101
|
+
* Build a fully-qualified Graph API URL.
|
|
102
|
+
*
|
|
103
|
+
* @param endpoint - Relative endpoint (e.g. `"12345/messages"`).
|
|
104
|
+
* @param params - Additional query-string parameters.
|
|
105
|
+
* @returns A `URL` instance with access token and params applied.
|
|
106
|
+
*/
|
|
107
|
+
private buildUrl;
|
|
108
|
+
/** Standard headers included on every JSON request. */
|
|
109
|
+
private defaultHeaders;
|
|
110
|
+
/**
|
|
111
|
+
* Core request executor with retry and rate limiting.
|
|
112
|
+
*
|
|
113
|
+
* Wraps `fetch` in the retry queue and acquires/releases rate limiter
|
|
114
|
+
* tokens around each attempt.
|
|
115
|
+
*/
|
|
116
|
+
private executeRequest;
|
|
117
|
+
/**
|
|
118
|
+
* Map a non-2xx Graph API response to a typed {@link MessagingError}.
|
|
119
|
+
*
|
|
120
|
+
* Delegates to {@link classifyMetaError} which maps Meta error codes
|
|
121
|
+
* and HTTP statuses to the appropriate error subclass.
|
|
122
|
+
*/
|
|
123
|
+
private classifyError;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/graph-api/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAQ3D,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD;AAMD,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7B,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;gBAErB,MAAM,EAAE,oBAAoB;IAcxC;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAU3E;;;;;;;OAOG;IACG,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAc1D;;;;;;;;;OASG;IACG,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC;IAcvE;;;;;;;;OAQG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgC/C;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;IAchB,uDAAuD;IACvD,OAAO,CAAC,cAAc;IAOtB;;;;;OAKG;YACW,cAAc;IAoB5B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;CAGtB"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module graph-api/client
|
|
3
|
+
*
|
|
4
|
+
* Typed HTTP client for Meta's Graph API.
|
|
5
|
+
*
|
|
6
|
+
* Shared by WhatsApp, Messenger, and Instagram platform clients. Handles
|
|
7
|
+
* authentication, retry, rate limiting, and error classification transparently.
|
|
8
|
+
*/
|
|
9
|
+
import { RetryQueue } from './retry.js';
|
|
10
|
+
import { RateLimiter } from './rate-limiter.js';
|
|
11
|
+
import { classifyMetaError } from './errors.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// GraphAPIClient
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Typed HTTP client for Meta's Graph API.
|
|
17
|
+
*
|
|
18
|
+
* Provides `get`, `post`, `postFormData`, and `fetchBinary` methods that
|
|
19
|
+
* handle authentication, exponential-backoff retry, token-bucket rate
|
|
20
|
+
* limiting, and error classification automatically.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const client = new GraphAPIClient({
|
|
25
|
+
* accessToken: process.env.META_ACCESS_TOKEN!,
|
|
26
|
+
* appSecret: process.env.META_APP_SECRET!,
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const profile = await client.get<{ name: string }>('me', { fields: 'name' });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class GraphAPIClient {
|
|
33
|
+
accessToken;
|
|
34
|
+
appSecret;
|
|
35
|
+
apiVersion;
|
|
36
|
+
baseUrl;
|
|
37
|
+
retryQueue;
|
|
38
|
+
rateLimiter;
|
|
39
|
+
logger;
|
|
40
|
+
constructor(config) {
|
|
41
|
+
this.accessToken = config.accessToken;
|
|
42
|
+
this.appSecret = config.appSecret;
|
|
43
|
+
this.apiVersion = config.apiVersion ?? 'v21.0';
|
|
44
|
+
this.baseUrl = (config.baseUrl ?? 'https://graph.facebook.com').replace(/\/+$/, '');
|
|
45
|
+
this.retryQueue = new RetryQueue(config.retry);
|
|
46
|
+
this.rateLimiter = new RateLimiter(config.rateLimiter);
|
|
47
|
+
this.logger = config.logger;
|
|
48
|
+
}
|
|
49
|
+
// -----------------------------------------------------------------------
|
|
50
|
+
// Public API
|
|
51
|
+
// -----------------------------------------------------------------------
|
|
52
|
+
/**
|
|
53
|
+
* Perform an authenticated GET request against the Graph API.
|
|
54
|
+
*
|
|
55
|
+
* @typeParam T - Expected response shape.
|
|
56
|
+
* @param endpoint - API endpoint relative to the version root (e.g. `"me/messages"`).
|
|
57
|
+
* @param params - Optional query-string parameters.
|
|
58
|
+
* @returns Parsed JSON response body.
|
|
59
|
+
*/
|
|
60
|
+
async get(endpoint, params) {
|
|
61
|
+
const url = this.buildUrl(endpoint, params);
|
|
62
|
+
this.logger?.debug('[GraphAPI] GET %s', url.toString());
|
|
63
|
+
return this.executeRequest(url.toString(), {
|
|
64
|
+
method: 'GET',
|
|
65
|
+
headers: this.defaultHeaders(),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Perform an authenticated POST request with a JSON body.
|
|
70
|
+
*
|
|
71
|
+
* @typeParam T - Expected response shape.
|
|
72
|
+
* @param endpoint - API endpoint relative to the version root.
|
|
73
|
+
* @param body - JSON-serializable request body.
|
|
74
|
+
* @returns Parsed JSON response body.
|
|
75
|
+
*/
|
|
76
|
+
async post(endpoint, body) {
|
|
77
|
+
const url = this.buildUrl(endpoint);
|
|
78
|
+
this.logger?.debug('[GraphAPI] POST %s', url.toString());
|
|
79
|
+
return this.executeRequest(url.toString(), {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
...this.defaultHeaders(),
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify(body),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Perform an authenticated POST request with form-data (multipart).
|
|
90
|
+
*
|
|
91
|
+
* Primarily used for media uploads where the payload includes binary data.
|
|
92
|
+
*
|
|
93
|
+
* @typeParam T - Expected response shape.
|
|
94
|
+
* @param endpoint - API endpoint relative to the version root.
|
|
95
|
+
* @param formData - `FormData` instance containing the fields/files to upload.
|
|
96
|
+
* @returns Parsed JSON response body.
|
|
97
|
+
*/
|
|
98
|
+
async postFormData(endpoint, formData) {
|
|
99
|
+
const url = this.buildUrl(endpoint);
|
|
100
|
+
this.logger?.debug('[GraphAPI] POST (form-data) %s', url.toString());
|
|
101
|
+
// Do NOT set Content-Type manually — `fetch` sets the boundary automatically.
|
|
102
|
+
return this.executeRequest(url.toString(), {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
106
|
+
},
|
|
107
|
+
body: formData,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Download a binary resource from a direct URL (e.g. media CDN link).
|
|
112
|
+
*
|
|
113
|
+
* This method does NOT prepend the Graph API base URL — pass the full URL
|
|
114
|
+
* returned by the media endpoint.
|
|
115
|
+
*
|
|
116
|
+
* @param url - Absolute URL to fetch.
|
|
117
|
+
* @returns Raw binary content as a `Buffer`.
|
|
118
|
+
*/
|
|
119
|
+
async fetchBinary(url) {
|
|
120
|
+
this.logger?.debug('[GraphAPI] FETCH_BINARY %s', url);
|
|
121
|
+
return this.retryQueue.execute(async () => {
|
|
122
|
+
await this.rateLimiter.acquire();
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetch(url, {
|
|
125
|
+
method: 'GET',
|
|
126
|
+
headers: {
|
|
127
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
this.rateLimiter.updateFromHeaders(response.headers);
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
const body = await response.json().catch(() => null);
|
|
133
|
+
throw this.classifyError(response, body);
|
|
134
|
+
}
|
|
135
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
136
|
+
return Buffer.from(arrayBuffer);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
this.rateLimiter.release();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// -----------------------------------------------------------------------
|
|
144
|
+
// Private helpers
|
|
145
|
+
// -----------------------------------------------------------------------
|
|
146
|
+
/**
|
|
147
|
+
* Build a fully-qualified Graph API URL.
|
|
148
|
+
*
|
|
149
|
+
* @param endpoint - Relative endpoint (e.g. `"12345/messages"`).
|
|
150
|
+
* @param params - Additional query-string parameters.
|
|
151
|
+
* @returns A `URL` instance with access token and params applied.
|
|
152
|
+
*/
|
|
153
|
+
buildUrl(endpoint, params) {
|
|
154
|
+
const cleanEndpoint = endpoint.replace(/^\/+/, '');
|
|
155
|
+
const url = new URL(`${this.baseUrl}/${this.apiVersion}/${cleanEndpoint}`);
|
|
156
|
+
url.searchParams.set('access_token', this.accessToken);
|
|
157
|
+
if (params) {
|
|
158
|
+
for (const [key, value] of Object.entries(params)) {
|
|
159
|
+
url.searchParams.set(key, value);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return url;
|
|
163
|
+
}
|
|
164
|
+
/** Standard headers included on every JSON request. */
|
|
165
|
+
defaultHeaders() {
|
|
166
|
+
return {
|
|
167
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
168
|
+
Accept: 'application/json',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Core request executor with retry and rate limiting.
|
|
173
|
+
*
|
|
174
|
+
* Wraps `fetch` in the retry queue and acquires/releases rate limiter
|
|
175
|
+
* tokens around each attempt.
|
|
176
|
+
*/
|
|
177
|
+
async executeRequest(url, init) {
|
|
178
|
+
return this.retryQueue.execute(async () => {
|
|
179
|
+
await this.rateLimiter.acquire();
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(url, init);
|
|
182
|
+
this.rateLimiter.updateFromHeaders(response.headers);
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
const body = await response.json().catch(() => null);
|
|
185
|
+
throw this.classifyError(response, body);
|
|
186
|
+
}
|
|
187
|
+
return (await response.json());
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
this.rateLimiter.release();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Map a non-2xx Graph API response to a typed {@link MessagingError}.
|
|
196
|
+
*
|
|
197
|
+
* Delegates to {@link classifyMetaError} which maps Meta error codes
|
|
198
|
+
* and HTTP statuses to the appropriate error subclass.
|
|
199
|
+
*/
|
|
200
|
+
classifyError(response, body) {
|
|
201
|
+
return classifyMetaError(response.status, body, 'meta');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/graph-api/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module graph-api/errors
|
|
3
|
+
*
|
|
4
|
+
* Meta-specific error classification helper.
|
|
5
|
+
*
|
|
6
|
+
* Imports the canonical error hierarchy from `@ariaflowagents/messaging` and
|
|
7
|
+
* re-exports it alongside the {@link classifyMetaError} function. This ensures
|
|
8
|
+
* that `instanceof` checks work correctly across the entire SDK — there is only
|
|
9
|
+
* ONE `WindowClosedError` class, ONE `RateLimitError` class, etc.
|
|
10
|
+
*/
|
|
11
|
+
import { MessagingError, RateLimitError, AuthenticationError, PermissionError, RecipientError, WindowClosedError, TemplateError, MediaError, WebhookVerificationError } from '@ariaflowagents/messaging';
|
|
12
|
+
export { MessagingError, RateLimitError, AuthenticationError, PermissionError, RecipientError, WindowClosedError, TemplateError, MediaError, WebhookVerificationError, };
|
|
13
|
+
/** Shape of a Meta Graph API error response body. */
|
|
14
|
+
export interface MetaErrorResponse {
|
|
15
|
+
error?: {
|
|
16
|
+
message?: string;
|
|
17
|
+
type?: string;
|
|
18
|
+
code?: number;
|
|
19
|
+
error_subcode?: number;
|
|
20
|
+
fbtrace_id?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify a Meta Graph API error response into a typed {@link MessagingError}.
|
|
25
|
+
*
|
|
26
|
+
* The mapping follows Meta's documented error codes:
|
|
27
|
+
* - 429 / codes 4, 32, 613 -> {@link RateLimitError}
|
|
28
|
+
* - 401 / code 190 -> {@link AuthenticationError}
|
|
29
|
+
* - 403 / codes 10, 200 -> {@link PermissionError}
|
|
30
|
+
* - 404 -> {@link RecipientError}
|
|
31
|
+
* - code 131047 -> {@link WindowClosedError} (24-hour window expired)
|
|
32
|
+
* - code 131026 -> {@link RecipientError} (not on WhatsApp)
|
|
33
|
+
* - codes 132000-132999 -> {@link TemplateError}
|
|
34
|
+
*
|
|
35
|
+
* @param status - HTTP status code from the response.
|
|
36
|
+
* @param body - Parsed JSON body (may be `null` if parsing failed).
|
|
37
|
+
* @param platform - Platform identifier (e.g. `"whatsapp"`, `"messenger"`).
|
|
38
|
+
* @returns A typed {@link MessagingError} subclass.
|
|
39
|
+
*/
|
|
40
|
+
export declare function classifyMetaError(status: number, body: unknown, platform: string): MessagingError;
|
|
41
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/graph-api/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,wBAAwB,EACzB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,wBAAwB,GACzB,CAAC;AAMF,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GACf,cAAc,CA6ChB"}
|