L'API Messenger de Facebook permet aux développeurs de créer des expériences conversationnelles qui touchent plus de 1,3 milliard d'utilisateurs actifs par mois. Que vous développiez un bot de support client, un assistant e-commerce ou une campagne marketing interactive, l'API de la plateforme Messenger vous fournit les outils nécessaires pour engager les utilisateurs dans des conversations significatives.
Ce guide vous accompagne depuis la configuration initiale jusqu'aux fonctionnalités avancées telles que les modèles de messages, les réponses rapides et le protocole de transfert humain. Vous trouverez des exemples fonctionnels en TypeScript tout au long du document, prêts à être adaptés à vos propres projets.
Introduction à la plateforme Messenger
La plateforme Messenger est le cadre de Meta pour créer des bots et des intégrations qui communiquent avec les utilisateurs via Facebook Messenger. Contrairement aux interfaces web traditionnelles, l'API de chat Facebook crée une expérience conversationnelle où les utilisateurs interagissent à travers des messages, des boutons et des médias enrichis.
Les principales fonctionnalités de l'API du bot de messagerie incluent :
| Feature | Description | Cas d'utilisation |
|---|---|---|
| Messages Textuels | Envoi et réception de messages de base | Demandes des clients, notifications |
| Modèles de messages | Mises en page structurées avec images et boutons | Catalogues de produits, reçus |
| Réponses rapides | Boutons de réponse suggérés | Conversations guidées, enquêtes |
| Menu Persistant | Options de navigation toujours disponibles | Navigation des bots, actions courantes |
| Messages multimédias | Images, vidéos, audio et fichiers | Images de produits, tutoriels |
| Protocole de transfert | Transfert entre bots et agents humains | Escalade de support complexe |
La plateforme fonctionne sur une architecture basée sur des webhooks. Votre serveur reçoit des messages entrants via des webhooks, les traite et répond en utilisant l'API Send. Ce modèle asynchrone vous permet de créer des expériences réactives sans avoir à maintenir les connexions ouvertes.
Configurer votre application Meta
Avant de pouvoir utiliser l'API de Facebook Messenger, vous devez créer et configurer une application Meta. Ce processus établit l'identité de votre application et vous donne accès à la plateforme Messenger.
Étape 1 : Créez une application Meta
Naviguez vers le Meta pour les Développeurs portail et créez une nouvelle application. Sélectionnez « Entreprise » comme type d'application, ce qui donne accès aux fonctionnalités de Messenger.
```typescript
// Variables d'environnement nécessaires après la configuration
interface MessengerConfig {
FACEBOOK_APP_ID: string;
FACEBOOK_APP_SECRET: string;
FACEBOOK_PAGE_ID: string;
FACEBOOK_PAGE_ACCESS_TOKEN: string;
MESSENGER_VERIFY_TOKEN: string;
}
// Validez votre configuration au démarrage
function validateConfig(): MessengerConfig {
const required = [
'FACEBOOK_APP_ID',
'FACEBOOK_APP_SECRET',
'FACEBOOK_PAGE_ID',
'FACEBOOK_PAGE_ACCESS_TOKEN',
'MESSENGER_VERIFY_TOKEN'
];
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(`Variables d'environnement manquantes : ${missing.join(', ')}`);
}
return {
FACEBOOK_APP_ID: process.env.FACEBOOK_APP_ID!,
FACEBOOK_APP_SECRET: process.env.FACEBOOK_APP_SECRET!,
FACEBOOK_PAGE_ID: process.env.FACEBOOK_PAGE_ID!,
FACEBOOK_PAGE_ACCESS_TOKEN: process.env.FACEBOOK_PAGE_ACCESS_TOKEN!,
MESSENGER_VERIFY_TOKEN: process.env.MESSENGER_VERIFY_TOKEN!
};
}
```
Étape 2 : Ajouter le produit Messenger
Dans le tableau de bord de votre application, cliquez sur "Ajouter un produit" et sélectionnez Messenger. Cela active les fonctionnalités de la plateforme Messenger pour votre application.
Étape 3 : Connecter une Page Facebook
Votre bot a besoin d'une Page Facebook pour envoyer et recevoir des messages. Dans les paramètres de Messenger, cliquez sur « Ajouter ou supprimer des pages » et sélectionnez la page que vous souhaitez connecter. Générez un jeton d'accès à la page, que vous utiliserez pour authentifier les requêtes API.
Remarque : Les jetons d'accès aux pages peuvent être de courte ou de longue durée. Pour les applications en production, échangez votre jeton contre une version à long terme qui dure environ 60 jours.
interface TokenExchangeResponse {
access_token: string;
token_type: string;
expires_in?: number;
}
async function exchangeForLongLivedToken(
shortLivedToken: string,
appId: string,
appSecret: string
): Promise {
const baseUrl = 'https://graph.facebook.com/v18.0';
const params = new URLSearchParams({
grant_type: 'fb_exchange_token',
client_id: appId,
client_secret: appSecret,
fb_exchange_token: shortLivedToken,
});
const response = await fetch(`${baseUrl}/oauth/access_token?${params.toString()}`);
if (!response.ok) {
const error = await response.text();
throw new Error(`Échec de l'échange de token : ${error}`);
}
const data = await response.json();
if (!data.access_token) {
throw new Error('Aucun token d\'accès retourné lors de l\'échange de token');
}
const expiresInDays = data.expires_in
? Math.floor(data.expires_in / 86400)
: 'inconnu';
console.log(`Token échangé avec succès (expire dans ${expiresInDays} jours)`);
return {
access_token: data.access_token,
token_type: data.token_type || 'bearer',
expires_in: data.expires_in,
};
}
Étape 4 : Configurer les autorisations de l'application
Demandez les autorisations nécessaires pour votre bot. Au minimum, vous aurez besoin de :
pages_messagingEnvoyer et recevoir des messagespages_gérer_métadonnées: Abonnez-vous aux webhookspages_lues_engagementAccéder aux données de conversation
Configuration et Vérification des Webhooks
Les webhooks sont la colonne vertébrale de l'API de la plateforme de messagerie. Ils permettent à Facebook de notifier votre serveur lorsque des événements se produisent, comme des messages entrants ou des livraisons de messages.
Configuration de votre point de terminaison Webhook
Créez un point de terminaison qui gère à la fois les requêtes GET (pour la vérification) et les requêtes POST (pour la réception d'événements).
import express, { Request, Response } from 'express';
import crypto from 'crypto';
const app = express();
// Analyse le corps brut pour la vérification de la signature
app.use(express.json({
verify: (req: any, res, buf) => {
req.rawBody = buf;
}
}));
// Point de terminaison de vérification du webhook
app.get('/webhook', (req: Request, res: Response) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
const verifyToken = process.env.MESSENGER_VERIFY_TOKEN;
if (mode === 'subscribe' && token === verifyToken) {
console.log('Webhook vérifié avec succès');
res.status(200).send(challenge);
} else {
console.error('Échec de la vérification du webhook');
res.sendStatus(403);
}
});
// Récepteur d'événements du webhook
app.post('/webhook', (req: Request, res: Response) => {
// Vérifie la signature de la requête
const signature = req.headers['x-hub-signature-256'] as string;
if (!verifySignature(req, signature)) {
console.error('Signature invalide');
return res.sendStatus(401);
}
const body = req.body;
if (body.object !== 'page') {
return res.sendStatus(404);
}
// Traite chaque entrée
body.entry?.forEach((entry: any) => {
entry.messaging?.forEach((event: any) => {
handleMessagingEvent(event);
});
});
// Répond toujours rapidement avec 200 OK
res.sendStatus(200);
});
function verifySignature(req: any, signature: string): boolean {
if (!signature) return false;
const appSecret = process.env.FACEBOOK_APP_SECRET!;
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', appSecret)
.update(req.rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
async function handleMessagingEvent(event: any): Promise {
const senderId = event.sender.id;
if (event
Abonnement aux événements Webhook
Après avoir configuré votre point de terminaison, abonnez-vous aux événements que vous souhaitez recevoir. Dans le tableau de bord de l'application Meta, configurez votre URL de webhook et sélectionnez les champs d'abonnement pertinents :
messages: Messages entrantsmessaging_postbacks: Clics sur les boutons et sélections de menuoptions_de_messagerie: Consent des utilisateurslivraisons_de_messages: Confirmations de livraisonlectures_de_message: Accusés de réception
Réception des messages
Lorsque les utilisateurs envoient des messages à votre bot, l'API de chat Facebook les transmet à votre webhook. Les messages peuvent contenir du texte, des pièces jointes, ou les deux.
interface MessengerMessage {
mid: string;
text?: string;
attachments?: Array<{
type: 'image' | 'video' | 'audio' | 'file' | 'location' | 'fallback';
payload: {
url?: string;
coordinates?: {
lat: number;
long: number;
};
};
}>;
quick_reply?: {
payload: string;
};
reply_to?: {
mid: string;
};
}
async function handleMessage(
senderId: string,
message: MessengerMessage
): Promise {
console.log(`Message reçu de ${senderId} :`, message);
// Gérer les réponses rapides
if (message.quick_reply) {
await handleQuickReply(senderId, message.quick_reply.payload);
return;
}
// Gérer les pièces jointes
if (message.attachments && message.attachments.length > 0) {
for (const attachment of message.attachments) {
await handleAttachment(senderId, attachment);
}
return;
}
// Gérer les messages texte
if (message.text) {
await processTextMessage(senderId, message.text);
}
}
async function handleAttachment(
senderId: string,
attachment: MessengerMessage['attachments'][0]
): Promise {
switch (attachment.type) {
case 'image':
await sendTextMessage(senderId, `Merci pour l'image ! J'ai reçu : ${attachment.payload.url}`);
break;
case 'location':
const { lat, long } = attachment.payload.coordinates!;
await sendTextMessage(senderId, `J'ai reçu votre localisation : ${lat}, ${long}`);
break;
default:
await sendTextMessage(senderId, `J'ai reçu votre ${attachment.type}`);
}
}
async function processTextMessage(
senderId: string,
text: string
): Promise {
const lowerText = text.toLowerCase();
if (lowerText.includes('help')) {
await sendHelpMessage(senderId);
} else if (lowerText.includes('products')) {
await sendProductCatalog(senderId);
} else {
await sendTextMessage(senderId, `Vous avez dit : "${text}". Comment puis-je vous aider aujourd'hui ?`);
}
}
Envoi de messages texte
L'API Send est votre outil principal pour répondre aux utilisateurs via l'API de bot de messagerie. Les messages texte sont la forme de réponse la plus simple.
const GRAPH_API_URL = 'https://graph.facebook.com/v18.0';
interface SendMessageResponse {
recipient_id: string;
message_id: string;
}
interface SendMessageError {
message: string;
type: string;
code: number;
error_subcode?: number;
fbtrace_id: string;
}
async function sendTextMessage(
recipientId: string,
text: string
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: { text },
messaging_type: 'RESPONSE'
}),
}
);
const data = await response.json();
if (!response.ok) {
const error = data.error as SendMessageError;
throw new Error(`Échec de l'envoi : ${error.message} (code : ${error.code})`);
}
return data as SendMessageResponse;
}
// Envoyer un indicateur de saisie pour une meilleure expérience utilisateur
async function sendTypingIndicator(
recipientId: string,
isTyping: boolean
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
sender_action: isTyping ? 'typing_on' : 'typing_off'
}),
}
);
}
// Fonction d'aide pour envoyer des messages avec indicateur de saisie
async function sendMessageWithTyping(
recipientId: string,
text: string,
typingDuration: number = 1000
): Promise {
await sendTypingIndicator(recipientId, true);
await new Promise(resolve => setTimeout(resolve, typingDuration));
return sendTextMessage(recipientId, text);
}
Modèles de messages (Générique, Bouton, Reçu)
Les modèles de messages créent des expériences riches et interactives qui vont au-delà du simple texte. L'API de Facebook Messenger prend en charge plusieurs types de modèles, chacun conçu pour des cas d'utilisation spécifiques.
Modèle Générique
Le modèle générique affiche un carrousel d'éléments, idéal pour les listes de produits ou les fils de contenu.
interface GenericTemplateElement {
title: string;
subtitle?: string;
image_url?: string;
default_action?: {
type: 'web_url';
url: string;
webview_height_ratio?: 'compact' | 'tall' | 'full';
};
buttons?: Array;
}
type TemplateButton =
| { type: 'web_url'; url: string; title: string }
| { type: 'postback'; title: string; payload: string }
| { type: 'phone_number'; title: string; payload: string }
| { type: 'account_link'; url: string }
| { type: 'account_unlink' };
async function sendGenericTemplate(
recipientId: string,
elements: GenericTemplateElement[]
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limiter à 10 éléments par carrousel
const limitedElements = elements.slice(0, 10);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'generic',
elements: limitedElements
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`L'envoi du modèle a échoué : ${error.error?.message}`);
}
return response.json();
}
// Exemple : Catalogue de produits
async function sendProductCatalog(recipientId: string): Promise {
const products: GenericTemplateElement[] = [
{
title: 'Casque sans fil',
subtitle: '149,99 € - Qualité sonore premium',
image_url: 'https://example.com/headphones.jpg',
default_action: {
type: 'web_url',
url: 'https://example.com/products/headphones'
},
buttons: [
{ type: 'postback', title: 'Acheter maintenant', payload: 'BUY_HEADPHONES' },
{ type
Modèle de bouton
Le modèle de bouton présente un message texte avec jusqu'à trois boutons, idéal pour des choix simples.
async function sendButtonTemplate(
recipientId: string,
text: string,
buttons: TemplateButton[]
): Promise<SendMessageResponse> {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limiter à 3 boutons
const limitedButtons = buttons.slice(0, 3);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'button',
text,
buttons: limitedButtons
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Échec du modèle de bouton : ${error.error?.message}`);
}
return response.json();
}
// Exemple : Menu d'aide
async function sendHelpMessage(recipientId: string): Promise<void> {
await sendButtonTemplate(
recipientId,
'Comment puis-je vous aider aujourd'hui ?',
[
{ type: 'postback', title: 'Parcourir les produits', payload: 'BROWSE_PRODUCTS' },
{ type: 'postback', title: 'Suivre la commande', payload: 'TRACK_ORDER' },
{ type: 'postback', title: 'Contacter le support', payload: 'CONTACT_SUPPORT' }
]
);
}
Modèle de reçu
Le modèle de reçu affiche les confirmations de commande avec les détails des articles.
interface ReceiptElement {
title: string;
subtitle?: string;
quantity?: number;
price: number;
currency?: string;
image_url?: string;
}
interface ReceiptSummary {
subtotal?: number;
shipping_cost?: number;
total_tax?: number;
total_cost: number;
}
interface ReceiptAddress {
street_1: string;
street_2?: string;
city: string;
postal_code: string;
state: string;
country: string;
}
async function sendReceiptTemplate(
recipientId: string,
receipt: {
recipientName: string;
orderNumber: string;
currency: string;
paymentMethod: string;
orderUrl?: string;
timestamp?: string;
address?: ReceiptAddress;
elements: ReceiptElement[];
summary: ReceiptSummary;
}
): Promise<SendMessageResponse> {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'receipt',
recipient_name: receipt.recipientName,
order_number: receipt.orderNumber,
currency: receipt.currency,
payment_method: receipt.paymentMethod,
order_url: receipt.orderUrl,
timestamp: receipt.timestamp,
address: receipt.address,
elements: receipt.elements,
summary: receipt.summary
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Échec de l'envoi du modèle de reçu : ${error.error?.message}`);
}
return response.json();
}
// Exemple : Confirmation de commande
async function sendOrderConfirmation(
recipientId: string,
orderId: string
): Promise<void> {
await sendReceiptTemplate(recipientId, {
recipientName: 'Jean Dupont'
Réponses rapides et menu persistant
Les Réponses Rapides et le Menu Persistant guident les utilisateurs à travers les conversations en proposant des options claires.
Réponses rapides
Les réponses rapides apparaissent sous forme de boutons au-dessus du clavier, offrant des réponses suggérées qui disparaissent après sélection.
interface QuickReply {
content_type: 'text' | 'user_phone_number' | 'user_email';
title?: string;
payload?: string;
image_url?: string;
}
async function sendQuickReplies(
recipientId: string,
text: string,
quickReplies: QuickReply[]
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limiter à 13 réponses rapides
const limitedReplies = quickReplies.slice(0, 13);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
text,
quick_replies: limitedReplies
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`L'envoi des réponses rapides a échoué : ${error.error?.message}`);
}
return response.json();
}
// Exemple : Enquête de satisfaction
async function sendSatisfactionSurvey(recipientId: string): Promise {
await sendQuickReplies(
recipientId,
'Comment évalueriez-vous votre expérience aujourd\'hui ?',
[
{ content_type: 'text', title: '😄 Excellent', payload: 'RATING_5' },
{ content_type: 'text', title: '🙂 Bon', payload: 'RATING_4' },
{ content_type: 'text', title: '😐 Correct', payload: 'RATING_3' },
{ content_type: 'text', title: '😕 Médiocre', payload: 'RATING_2' },
{ content_type: 'text', title: '😞 Mauvais', payload: 'RATING_1' }
]
);
}
async function handleQuickReply(
senderId: string,
payload: string
): Promise {
if (payload.startsWith('RATING_')) {
const rating = parseInt(payload.split('_')[1]);
await sendTextMessage(
senderId,
`Merci pour votre retour ! Vous nous avez notés ${rating}/5
Menu Persistant
Le Menu Persistant offre une navigation toujours accessible via l'icône hamburger.
interface MenuItem {
type: 'postback' | 'web_url' | 'nested';
title: string;
payload?: string;
url?: string;
webview_height_ratio?: 'compact' | 'tall' | 'full';
call_to_actions?: MenuItem[];
}
async function setPersistentMenu(
menuItems: MenuItem[],
locale: string = 'default'
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messenger_profile?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
persistent_menu: [
{
locale,
composer_input_disabled: false,
call_to_actions: menuItems
}
]
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Échec de la configuration du menu persistant : ${error.error?.message}`);
}
console.log('Menu persistant configuré avec succès');
}
// Exemple : Menu du bot e-commerce
async function setupBotMenu(): Promise {
await setPersistentMenu([
{
type: 'postback',
title: '🛍️ Acheter Maintenant',
payload: 'SHOP_NOW'
},
{
type: 'nested',
title: '📦 Mes Commandes',
call_to_actions: [
{ type: 'postback', title: 'Suivre la Commande', payload: 'TRACK_ORDER' },
{ type: 'postback', title: 'Historique des Commandes', payload: 'ORDER_HISTORY' },
{ type: 'postback', title: 'Retours', payload: 'RETURNS' }
]
},
{
type: 'postback',
title: '💬 Contacter le Support',
payload: 'CONTACT_SUPPORT'
}
]);
}
Messages multimédias (Images, Vidéos, Fichiers)
L'API de la plateforme de messagerie prend en charge les pièces jointes multimédias, vous permettant d'envoyer des images, des vidéos, des fichiers audio et des documents.
type AttachmentType = 'image' | 'video' | 'audio' | 'file';
interface AttachmentPayload {
url?: string;
is_reusable?: boolean;
attachment_id?: string;
}
async function sendMediaAttachment(
recipientId: string,
type: AttachmentType,
payload: AttachmentPayload
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type,
payload
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`L'envoi du média a échoué : ${error.error?.message}`);
}
return response.json();
}
// Envoyer une image par URL
async function sendImage(
recipientId: string,
imageUrl: string,
reusable: boolean = true
): Promise {
return sendMediaAttachment(recipientId, 'image', {
url: imageUrl,
is_reusable: reusable
});
}
// Envoyer une vidéo par URL
async function sendVideo(
recipientId: string,
videoUrl: string
): Promise {
return sendMediaAttachment(recipientId, 'video', {
url: videoUrl,
is_reusable: true
});
}
// Envoyer un fichier/document
async function sendFile(
recipientId: string,
fileUrl: string
): Promise {
return sendMediaAttachment(recipientId, 'file', {
url: fileUrl,
is_reusable: true
});
}
// Télécharger un fichier pour réutilisation
async function uploadAttachment(
type: AttachmentType,
url: string
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/message_attachments?access_token=${pageAccessToken}`,
{
method: 'POST',
headers
Remarque : Les fichiers multimédias doivent être accessibles via HTTPS. Facebook télécharge le fichier depuis votre URL, veillez donc à ce que votre serveur puisse gérer le trafic. Pour les médias utilisés fréquemment, téléchargez-les une fois et réutilisez l'ID de l'attachement.
Protocole de transfert humain
Le Protocole de Transmission permet des transitions fluides entre les bots automatisés et les agents humains. Cela est essentiel pour des scénarios de support complexes nécessitant une intervention humaine.
type ThreadOwner = 'principal' | 'secondaire';
interface HandoverMetadata {
raison?: string;
historiqueConversation?: string;
donnéesPersonnalisées?: Record;
}
// Transférer le contrôle du fil à une autre application (par exemple, boîte de réception d'un agent humain)
async function passThreadControl(
recipientId: string,
targetAppId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/pass_thread_control?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
target_app_id: targetAppId,
metadata: metadata ? JSON.stringify(metadata) : undefined
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Échec du transfert de contrôle du fil : ${error.error?.message}`);
}
console.log(`Contrôle du fil transféré à l'application ${targetAppId}`);
}
// Reprendre le contrôle du fil depuis le récepteur secondaire
async function takeThreadControl(
recipientId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/take_thread_control?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
metadata: metadata ? JSON.stringify(metadata) : undefined
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Échec de la reprise du contrôle du fil : ${error.error?.message}`);
}
console.log('Contrôle du fil repris');
}
// Demander le contrôle du fil au récepteur principal
async function requestThreadControl(
recipientId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
Étiquettes de message pour les relances
En dehors de la fenêtre de messagerie de 24 heures, vous devez utiliser des Étiquettes de Message pour envoyer des messages de suivi. Ces étiquettes indiquent l'objectif de votre message et doivent être utilisées de manière appropriée.
type MessageTag =
| 'CONFIRMED_EVENT_UPDATE'
| 'POST_PURCHASE_UPDATE'
| 'ACCOUNT_UPDATE'
| 'HUMAN_AGENT';;
async function sendTaggedMessage(
recipientId: string,
text: string,
tag: MessageTag
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: { text },
messaging_type: 'MESSAGE_TAG',
tag
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`L'envoi du message tagué a échoué : ${error.error?.message}`);
}
return response.json();
}
// Exemple : Mise à jour de l'expédition de commande
async function sendShippingUpdate(
recipientId: string,
orderId: string,
trackingNumber: string
): Promise {
await sendTaggedMessage(
recipientId,
`Bonne nouvelle ! Votre commande #${orderId} a été expédiée. Suivez-la ici : https://example.com/track/${trackingNumber}`,
'POST_PURCHASE_UPDATE'
);
}
// Exemple : Alerte de sécurité de compte
async function sendSecurityAlert(
recipientId: string,
alertType: string
): Promise {
await sendTaggedMessage(
recipientId,
`Alerte de sécurité : ${alertType}. Si ce n'était pas vous, veuillez sécuriser votre compte immédiatement.`,
'ACCOUNT_UPDATE'
);
}
// Exemple : Suivi par un agent humain (dans les 7 jours suivant le message de l'utilisateur)
async function sendHumanAgentFollowup(
recipientId: string,
message: string
): Promise {
await sendTaggedMessage(
recipientId,
message,
'HUMAN_AGENT'
);
}
Remarque : Les balises de message ont des politiques d'utilisation strictes. Un usage abusif peut entraîner des restrictions ou l'interdiction de votre bot. La balise HUMAN_AGENT n'est disponible que dans les 7 jours suivant le dernier message de l'utilisateur et nécessite une intervention humaine dans la réponse.
Limites de taux et meilleures pratiques
L'API de Facebook Messenger impose des limites de fréquence pour garantir la stabilité de la plateforme. Comprendre ces limites vous aide à créer des bots fiables.
Aperçu des limites de taux
| Type de Limite | Threshold | Window |
|---|---|---|
| Appels par page | 200 appels | Par heure et par utilisateur |
| Demandes groupées | 50 requêtes | Par lot |
| Envoyer l'API | 250 messages | par seconde par page |
Gestion des limites de taux
interface RateLimitState {
requestCount: number;
windowStart: number;
isThrottled: boolean;
}
class RateLimiter {
private state: Map = new Map();
private readonly maxRequests: number;
private readonly windowMs: number;
constructor(maxRequests: number = 200, windowMs: number = 3600000) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
}
async checkLimit(pageId: string): Promise {
const now = Date.now();
let state = this.state.get(pageId);
if (!state || now - state.windowStart > this.windowMs) {
state = { requestCount: 0, windowStart: now, isThrottled: false };
}
if (state.requestCount >= this.maxRequests) {
state.isThrottled = true;
this.state.set(pageId, state);
return false;
}
state.requestCount++;
this.state.set(pageId, state);
return true;
}
getResetTime(pageId: string): number {
const state = this.state.get(pageId);
if (!state) return 0;
return Math.max(0, this.windowMs - (Date.now() - state.windowStart));
}
}
const rateLimiter = new RateLimiter();
async function sendWithRateLimiting(
pageId: string,
recipientId: string,
text: string
): Promise {
const canProceed = await rateLimiter.checkLimit(pageId);
if (!canProceed) {
const resetTime = rateLimiter.getResetTime(pageId);
console.warn(`Limite de fréquence atteinte. Réinitialisation dans ${Math.ceil(resetTime / 1000)}s`);
return null;
}
return sendTextMessage(recipientId, text);
}
Meilleures Pratiques
- Répondez rapidement.Accuse réception des messages dans les 20 secondes pour éviter les erreurs de délai d'attente.
- Utilisez des indicateurs de saisieFaites savoir à vos utilisateurs que votre bot est en train de traiter leur demande.
- Gérez les erreurs avec éléganceFournissez des messages d'erreur utiles lorsque des problèmes surviennent.
- Respectez les préférences des utilisateursRespectez les demandes de désinscription et les actions de désabonnement.
- Mettre en cache les identifiants des pièces jointesTéléchargez les médias une fois et réutilisez l'ID de l'attachement.
- Opérations par lotRegroupez plusieurs demandes lorsque c'est possible.
- Implémentez une logique de nouvelle tentativeGérez les échecs transitoires avec un retour exponentiel.
async function sendWithRetry(
recipientId: string,
text: string,
maxRetries: number = 3
): Promise<SendMessageResponse> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await sendTextMessage(recipientId, text);
} catch (error) {
lastError = error as Error;
// Vérifier si l'erreur est réessayable
const errorMessage = lastError.message.toLowerCase();
const isRetryable =
errorMessage.includes('timeout') ||
errorMessage.includes('temporarily unavailable') ||
errorMessage.includes('rate limit');
if (!isRetryable || attempt === maxRetries) {
throw lastError;
}
// Backoff exponentiel
const delay = Math.pow(2, attempt) * 1000;
console.log(`Tentative de réessai ${attempt}/${maxRetries} dans ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
Utiliser Late pour l'intégration de Messenger
La création et la maintenance d'une intégration Messenger nécessitent de gérer des webhooks, de gérer des tokens, de traiter différents types de messages et de se tenir au courant des évolutions de l'API. Late simplifie l'ensemble du processus grâce à une API unifiée qui fonctionne sur plusieurs plateformes de messagerie.
Pourquoi utiliser Late pour Messenger ?
Au lieu de créer des gestionnaires de webhook personnalisés, de gérer les tokens et de formater les messages pour chaque plateforme, Late propose :
- API unifiéePoint d'accès unique pour envoyer des messages vers Messenger, WhatsApp, Instagram, et bien plus encore.
- Rafraîchissement automatique du jeton: Jetons de longue durée gérés automatiquement
- Agrégation de WebhooksUn point de terminaison webhook pour toutes les plateformes
- Abstraction de modèleÉcrivez une fois, affichez correctement sur chaque plateforme.
- Limitation de débit intégréeGestion automatique des limites de la plateforme
- Normalisation des erreursRéponses d'erreur cohérentes sur toutes les plateformes
Démarrage rapide avec Late
```javascript
import { LateClient } from '@anthropic/late-sdk';
const late = new LateClient({
apiKey: process.env.LATE_API_KEY!
});
// Envoyer un message sur Messenger (ou toute autre plateforme)
async function sendMessage(
channelId: string,
recipientId: string,
content: string
): Promise {
await late.messages.send({
channel: channelId,
recipient: recipientId,
content: {
type: 'text',
text: content
}
});
}
// Envoyer un modèle qui fonctionne sur plusieurs plateformes
async function sendProductCard(
channelId: string,
recipientId: string,
product: { name: string; price: number; imageUrl: string }
): Promise {
await late.messages.send({
channel: channelId,
recipient: recipientId,
content: {
type: 'card',
title: product.name,
subtitle: `${product.price.toFixed(2)} €`,
imageUrl: product.imageUrl,
buttons: [
{ type: 'postback', title: 'Acheter maintenant', payload: 'BUY' },
{ type: 'url', title: 'Détails', url: 'https://example.com' }
]
}
});
}
```
Late gère automatiquement le formatage spécifique à chaque plateforme. Le même code fonctionne pour Messenger, Instagram Direct, WhatsApp Business et d'autres plateformes prises en charge.
Gestion des messages entrants
Late regroupe les webhooks de toutes les plateformes connectées en un seul point de terminaison :
// Gestionnaire de webhook unique pour toutes les plateformes
app.post('/late-webhook', async (req, res) => {
const event = req.body;
// Gestion des messages indépendante de la plateforme
if (event.type === 'message') {
const { platform, channelId, senderId, content } = event;
console.log(`Message de ${platform} : ${content.text}`);
// Répondre en utilisant l'API unifiée de Late
await late.messages.send({
channel: channelId,
recipient: senderId,
content: {
type: 'text',
text: `Merci pour votre message !`
}
});
}
res.sendStatus(200);
});
Commencer
- Inscrivez-vous sur getlate.dev
- Connectez votre Page Facebook dans le tableau de bord.
- Utilisez l'API unifiée de Late pour envoyer et recevoir des messages.
La documentation de Late comprend des guides détaillés pour Intégration de Messenger, configuration du webhook, et modèles de messages.
Créer des expériences conversationnelles ne devrait pas nécessiter de maintenir des bases de code distinctes pour chaque plateforme. Avec Late, vous écrivez la logique de votre bot une seule fois et atteignez les utilisateurs là où ils préfèrent communiquer.