UNPKG

@warriorteam/messenger-sdk

Version:

TypeScript SDK for Facebook Messenger Platform API

486 lines (383 loc) 12.4 kB
# @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).