@warriorteam/messenger-sdk
Version:
TypeScript SDK for Facebook Messenger Platform API
486 lines (383 loc) • 12.4 kB
Markdown
# @warriorteam/messenger-sdk
A modern TypeScript SDK for the Facebook Messenger Platform API, designed with simplicity and type safety in mind.
## Features
- 🚀 **Zero runtime dependencies** - Built with native fetch
- 📝 **Full TypeScript support** - Complete type definitions with discriminated unions
- 🔄 **Dual module support** - ESM and CommonJS
- ✅ **Comprehensive validation** - Built-in message and template validation
- 🛡️ **Error handling** - Detailed error types and messages
- 📚 **Complete API coverage** - Send API, Templates, Attachments, Moderation, Profile
- 🔐 **Webhook utilities** - Complete webhook type system and signature verification
- 🎯 **Token override** - Per-method access token override support
- 🔧 **Type-safe webhooks** - Discriminated unions for proper TypeScript narrowing
## Installation
```bash
npm install @warriorteam/messenger-sdk
```
## Requirements
- Node.js 18+ (for native fetch support)
- A Facebook Page Access Token
## Quick Start
```typescript
import { Messenger } from '@warriorteam/messenger-sdk';
const messenger = new Messenger({
accessToken: 'YOUR_PAGE_ACCESS_TOKEN',
version: 'v23.0'
});
// Send a text message
const result = await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: { text: 'Hello from RedAI Messenger SDK!' }
});
console.log('Message sent:', result.message_id);
```
## API Reference
### Client Configuration
```typescript
const messenger = new Messenger({
accessToken?: string; // Optional: Default page access token
version?: string; // Optional: API version (default: 'v23.0')
baseUrl?: string; // Optional: Custom base URL
timeout?: number; // Optional: Request timeout in ms (default: 30000)
maxRetries?: number; // Optional: Max retry attempts (default: 3)
});
```
### Token Override Support
Every API method supports per-call access token override, perfect for multi-tenant applications:
```typescript
const messenger = new Messenger({
accessToken: 'default_page_token'
});
// Use default token
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: { text: 'Hello from default page!' }
});
// Override token for this specific call
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: { text: 'Hello from different page!' }
}, {
accessToken: 'other_page_token'
});
// Works with all API methods
const profile = await messenger.profile.getBasic('USER_PSID', {
accessToken: 'specific_token'
});
```
### Send API
#### Send Text Message
```typescript
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: { text: 'Hello World!' }
});
```
#### Send Message with Quick Replies
```typescript
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: {
text: 'Pick a color:',
quick_replies: [
{ content_type: 'text', title: 'Red', payload: 'PICKED_RED' },
{ content_type: 'text', title: 'Blue', payload: 'PICKED_BLUE' }
]
}
});
```
#### Send Typing Indicators
```typescript
await messenger.send.typingOn('USER_PSID');
await messenger.send.typingOff('USER_PSID');
await messenger.send.markSeen('USER_PSID');
```
#### Response Format
The Send API response includes a `message_id` and optionally a `recipient_id`:
```typescript
const result = await messenger.send.message({...});
console.log('Message ID:', result.message_id); // Always present
console.log('Recipient ID:', result.recipient_id || 'Not provided'); // Optional
```
**Note**: `recipient_id` is only included when using `recipient.id` (PSID). It's not included when using `recipient.user_ref` or `recipient.phone_number`.
### Attachments API
#### Upload Attachment from URL
```typescript
const attachment = await messenger.attachments.upload({
type: 'image',
url: 'https://example.com/image.jpg',
is_reusable: true
});
// Use the attachment ID to send
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: {
attachment: {
type: 'image',
payload: { attachment_id: attachment.attachment_id }
}
}
});
```
#### Send Attachment Directly
```typescript
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: {
attachment: {
type: 'image',
payload: { url: 'https://example.com/image.jpg' }
}
}
});
```
### Templates API
#### Generic Template
```typescript
await messenger.templates.generic({
recipient: { id: 'USER_PSID' },
elements: [{
title: 'Welcome',
subtitle: 'Check out our products',
image_url: 'https://example.com/image.jpg',
buttons: [{
type: 'web_url',
title: 'Shop Now',
url: 'https://example.com/shop'
}]
}]
});
```
#### Button Template
```typescript
await messenger.templates.button({
recipient: { id: 'USER_PSID' },
text: 'What would you like to do?',
buttons: [{
type: 'postback',
title: 'Get Started',
payload: 'GET_STARTED'
}]
});
```
### Moderation API
#### User Moderation
```typescript
// Block a user from messaging (can still see page content)
await messenger.moderation.blockUser('USER_PSID');
// Ban a user completely (no messaging + no Facebook interactions)
await messenger.moderation.banUser('USER_PSID');
// Move conversation to spam folder
await messenger.moderation.moveToSpam('USER_PSID');
// Block and spam (common combo)
await messenger.moderation.blockAndSpam('USER_PSID');
// Multiple users at once
await messenger.moderation.blockUser(['USER_1', 'USER_2', 'USER_3']);
// Unblock/unban users
await messenger.moderation.unblockUser('USER_PSID');
await messenger.moderation.unbanUser('USER_PSID');
```
### Profile API
#### Get User Profile Information
```typescript
// Get basic profile info (first_name, last_name, profile_pic)
const profile = await messenger.profile.getBasic('USER_PSID');
console.log(`Hello ${profile.first_name}!`);
// Get comprehensive profile with all fields
const fullProfile = await messenger.profile.getFull('USER_PSID');
console.log('Profile:', fullProfile);
// Returns: { id, name, first_name, last_name, profile_pic, locale, timezone, gender }
// Get specific fields only
const customProfile = await messenger.profile.get({
psid: 'USER_PSID',
fields: ['first_name', 'locale', 'timezone']
});
// Helper methods for common use cases
const nameInfo = await messenger.profile.getName('USER_PSID');
const profilePic = await messenger.profile.getProfilePicture('USER_PSID');
```
#### Personalize Messages
```typescript
// Use profile info to personalize messages
const profile = await messenger.profile.getName('USER_PSID');
const greeting = profile.first_name
? `Hello ${profile.first_name}! Welcome back!`
: 'Hello! Welcome back!';
await messenger.send.message({
recipient: { id: 'USER_PSID' },
messaging_type: 'RESPONSE',
message: { text: greeting }
});
```
**Note**: Profile API requires "Advanced User Profile Access" feature and user interaction to grant permissions.
## Webhook Support
The SDK provides comprehensive webhook support with full TypeScript safety through discriminated unions.
### Webhook Types and Processing
```typescript
import {
processWebhookEvents,
extractWebhookEvents,
getWebhookEventType,
getWebhookPayloadEventTypes,
WebhookEventType,
GenericWebhookPayload
} from '@warriorteam/messenger-sdk';
// Process webhook with type-safe handlers
app.post('/webhook', express.json(), async (req, res) => {
const payload: GenericWebhookPayload = req.body;
await processWebhookEvents(payload, {
onMessage: async (event) => {
// TypeScript knows this is MessageWebhookEvent
console.log(`Received message: ${event.message.text}`);
},
onMessageEdit: async (event) => {
// TypeScript knows this is MessageEditWebhookEvent
console.log(`Message edited to: ${event.message_edit.text}`);
},
onMessageReaction: async (event) => {
// TypeScript knows this is MessageReactionWebhookEvent
console.log(`Reaction: ${event.reaction.reaction}`);
},
onMessagingPostback: async (event) => {
// TypeScript knows this is MessagingPostbackWebhookEvent
console.log(`Postback: ${event.postback.payload}`);
}
});
res.sendStatus(200);
});
```
### Manual Event Processing
```typescript
// Extract events manually
const events = extractWebhookEvents(payload);
for (const event of events) {
const eventType = getWebhookEventType(event);
switch (eventType) {
case WebhookEventType.MESSAGE:
// Handle message event
break;
case WebhookEventType.MESSAGE_EDIT:
// Handle edit event
break;
// ... other cases
}
}
// Check what event types are in the payload
const eventTypes = getWebhookPayloadEventTypes(payload);
console.log('Received event types:', eventTypes);
```
### Webhook Verification
The SDK provides utilities for both subscription verification and signature validation:
```typescript
import {
verifyWebhookSubscription,
verifyWebhookSignature
} from '@warriorteam/messenger-sdk';
// Subscription verification (GET request)
app.get('/webhook', (req, res) => {
const challenge = verifyWebhookSubscription(
req.query as any,
process.env.VERIFY_TOKEN!
);
if (challenge) {
res.send(challenge);
} else {
res.status(403).send('Forbidden');
}
});
// Signature verification (POST request)
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const signature = req.get('X-Hub-Signature-256');
const result = await verifyWebhookSignature(
req.body,
signature,
process.env.APP_SECRET!
);
if (!result.isValid) {
return res.status(401).json({error: result.error});
}
// Process webhook events...
const payload = JSON.parse(req.body.toString());
// ... handle events
});
```
### Type-Safe Event Handling
The SDK uses discriminated unions for perfect TypeScript support:
```typescript
import {
MessengerWebhookEvent,
isMessageEvent,
isMessageEditEvent,
isMessagingPostbackEvent
} from '@warriorteam/messenger-sdk';
function handleWebhookEvent(event: MessengerWebhookEvent) {
if (isMessageEvent(event)) {
// TypeScript knows event.message exists
console.log(`Message: ${event.message.text}`);
} else if (isMessageEditEvent(event)) {
// TypeScript knows event.message_edit exists
console.log(`Edit: ${event.message_edit.text}`);
} else if (isMessagingPostbackEvent(event)) {
// TypeScript knows event.postback exists
console.log(`Postback: ${event.postback.payload}`);
}
}
```
### Supported Webhook Event Types
- `MESSAGE` - User sends a message
- `MESSAGE_EDIT` - User edits a sent message
- `MESSAGE_REACTION` - User reacts to a message
- `MESSAGE_READ` - User reads messages (read receipts)
- `MESSAGING_FEEDBACK` - User submits feedback via templates
- `MESSAGING_POSTBACK` - User clicks buttons/quick replies
## Error Handling
The SDK provides specific error types for different scenarios:
```typescript
import {
MessengerAPIError,
MessengerNetworkError,
MessengerTimeoutError,
MessengerConfigError
} from '@warriorteam/messenger-sdk';
try {
await messenger.send.message({
recipient: { id: 'USER_PSID' },
message: { text: 'Hello!' }
});
} catch (error) {
if (error instanceof MessengerAPIError) {
console.error('API Error:', error.message, error.code);
} else if (error instanceof MessengerNetworkError) {
console.error('Network Error:', error.message);
} else if (error instanceof MessengerTimeoutError) {
console.error('Timeout Error:', error.timeout);
}
}
```
## Examples
Check the `examples/` directory for complete usage examples:
- `send-message.ts` - Basic message sending
- `upload-attachment.ts` - Attachment handling
- `send-template.ts` - Template messages
- `user-profile.ts` - Profile API usage
- `moderation.ts` - User moderation
- `webhook-handler.ts` - Complete webhook setup with type safety
- `multi-tenant.ts` - Token override for multi-tenant applications
- `signature-verification.ts` - Webhook security implementation
## Development
```bash
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
# Lint code
npm run lint
# Format code
npm run format
```
## License
MIT
## Support
For issues and questions, please visit our [GitHub repository](https://github.com/warriorteam/messenger-sdk).