Gérer les commentaires sur plusieurs publications Instagram devient accablant à mesure que votre audience grandit. La modération des commentaires Instagram via le Graph API vous permet de créer des systèmes automatisés qui récupèrent, filtrent, répondent et modèrent les commentaires par programmation. Ce guide vous accompagne dans l'implémentation d'une solution complète de gestion des commentaires en utilisant TypeScript.
Que vous construisiez un tableau de bord pour les réseaux sociaux, un outil de support client ou un système d'engagement automatisé, comprendre l'API de commentaires Instagram est essentiel. Nous couvrirons tout, de l'authentification de base aux workflows de modération avancés avec analyse de sentiment.
Exigences d'accès à l'API
Avant de pouvoir automatiser les commentaires Instagram, vous devez configurer correctement l'accès à l'API. Le Instagram Graph API nécessite un compte Facebook Developer et une application approuvée avec des permissions spécifiques.
Permissions requises
Votre application a besoin de ces scopes OAuth pour travailler avec les commentaires :
const INSTAGRAM_SCOPES = [
'instagram_business_basic', // Basic account access
'instagram_business_content_publish', // Publish content
'instagram_business_manage_comments', // Read and manage comments
'instagram_business_manage_messages', // Required for private replies
];
Configuration de l'environnement
Créez un fichier .env avec vos identifiants :
INSTAGRAM_CLIENT_ID=your_client_id
INSTAGRAM_CLIENT_SECRET=your_client_secret
INSTAGRAM_ACCESS_TOKEN=your_long_lived_token
INSTAGRAM_USER_ID=your_business_account_id
Implémentation de l'échange de token
Les tokens Instagram commencent comme des tokens à courte durée de vie (1 heure) et doivent être échangés contre des tokens à longue durée de vie (60 jours) :
interface TokenResponse {
access_token: string;
token_type: string;
expires_in: number;
}
async function exchangeForLongLivedToken(
shortLivedToken: string,
clientSecret: string
): Promise<TokenResponse> {
const baseUrl = 'https://graph.instagram.com';
const response = await fetch(
`${baseUrl}/access_token?` +
`grant_type=ig_exchange_token&` +
`client_secret=${clientSecret}&` +
`access_token=${shortLivedToken}`,
{ method: 'GET' }
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Token exchange failed: ${response.status} - ${errorText}`);
}
return response.json();
}
async function refreshLongLivedToken(
currentToken: string
): Promise<TokenResponse> {
const baseUrl = 'https://graph.instagram.com';
const response = await fetch(
`${baseUrl}/refresh_access_token?` +
`grant_type=ig_refresh_token&` +
`access_token=${currentToken}`,
{ method: 'GET' }
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Token refresh failed: ${response.status} - ${errorText}`);
}
return response.json();
}
Note : Les tokens à longue durée de vie doivent être rafraîchis avant leur expiration. Configurez une tâche cron pour rafraîchir les tokens au moins 7 jours avant l'expiration.
Récupération des commentaires sur les publications
La base de tout système de modération des commentaires Instagram est la récupération efficace des commentaires. L'API prend en charge la pagination pour gérer les publications avec des milliers de commentaires.
Récupération basique des commentaires
interface InstagramUser {
id: string;
username: string;
}
interface InstagramComment {
commentId: string;
comment: string;
created: string;
from: InstagramUser;
likeCount: number;
replyCount: number;
hidden: boolean;
platform: 'instagram';
replies: InstagramComment[];
}
interface CommentResponse {
comments: InstagramComment[];
pagination: {
hasMore: boolean;
cursor?: string;
};
}
async function getMediaComments(
accessToken: string,
mediaId: string,
options?: { limit?: number; cursor?: string }
): Promise<CommentResponse> {
const baseUrl = 'https://graph.instagram.com';
const limit = Math.min(options?.limit || 25, 50);
let url = `${baseUrl}/${mediaId}/comments?` +
`fields=id,text,timestamp,username,hidden,` +
`from{id,username},like_count,` +
`replies{id,text,timestamp,username,hidden,from{id,username},like_count}&` +
`limit=${limit}&access_token=${accessToken}`;
if (options?.cursor) {
url += `&after=${options.cursor}`;
}
const response = await fetch(url);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to fetch comments: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Build lookup map for complete comment data
const commentDataById = new Map<string, any>();
(data.data || []).forEach((comment: any) => {
commentDataById.set(comment.id, comment);
});
// Identify reply IDs to filter from top-level
const allReplyIds = new Set<string>();
(data.data || []).forEach((comment: any) => {
(comment.replies?.data || []).forEach((reply: any) => {
allReplyIds.add(reply.id);
});
});
// Filter to only top-level comments
const topLevelComments = (data.data || [])
.filter((comment: any) => !allReplyIds.has(comment.id));
const comments: InstagramComment[] = topLevelComments.map((comment: any) => ({
commentId: comment.id,
comment: comment.text,
created: comment.timestamp,
from: {
id: comment.from?.id,
username: comment.from?.username || comment.username,
},
likeCount: comment.like_count || 0,
replyCount: comment.replies?.data?.length || 0,
hidden: comment.hidden || false,
platform: 'instagram',
replies: (comment.replies?.data || []).map((reply: any) => {
const fullReplyData = commentDataById.get(reply.id) || reply;
return {
commentId: reply.id,
comment: fullReplyData.text || reply.text,
created: fullReplyData.timestamp || reply.timestamp,
from: {
id: fullReplyData.from?.id || reply.from?.id,
username: fullReplyData.from?.username || reply.from?.username,
},
likeCount: fullReplyData.like_count || reply.like_count || 0,
hidden: fullReplyData.hidden || false,
platform: 'instagram',
replies: [],
replyCount: 0,
};
}),
}));
return {
comments,
pagination: {
hasMore: !!data.paging?.next,
cursor: data.paging?.cursors?.after,
},
};
}
Récupération de tous les commentaires avec pagination
Pour les publications avec de nombreux commentaires, implémentez la pagination pour tout récupérer :
async function getAllComments(
accessToken: string,
mediaId: string,
maxComments: number = 1000
): Promise<InstagramComment[]> {
const allComments: InstagramComment[] = [];
let cursor: string | undefined;
while (allComments.length < maxComments) {
const response = await getMediaComments(accessToken, mediaId, {
limit: 50,
cursor,
});
allComments.push(...response.comments);
if (!response.pagination.hasMore || !response.pagination.cursor) {
break;
}
cursor = response.pagination.cursor;
// Rate limiting: wait between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
return allComments.slice(0, maxComments);
}
Répondre aux commentaires par programmation
La capacité de répondre aux commentaires Instagram via l'API par programmation permet l'engagement automatisé, les bots de support client et la gestion de communauté à grande échelle.
Implémentation de réponse publique
interface ReplyResult {
replyId: string;
success: boolean;
error?: string;
}
async function replyToComment(
accessToken: string,
commentId: string,
message: string
): Promise<ReplyResult> {
const baseUrl = 'https://graph.instagram.com';
try {
const response = await fetch(
`${baseUrl}/${commentId}/replies`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message,
access_token: accessToken,
}),
}
);
if (!response.ok) {
const errorText = await response.text();
return {
replyId: '',
success: false,
error: `Reply failed: ${response.status} - ${errorText}`,
};
}
const data = await response.json();
return {
replyId: data.id,
success: true,
};
} catch (error) {
return {
replyId: '',
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
Réponse privée (Message direct)
Instagram permet d'envoyer un DM privé aux auteurs de commentaires. C'est utile pour les scénarios de support client :
interface PrivateReplyResult {
messageId: string;
success: boolean;
error?: string;
}
async function sendPrivateReply(
accessToken: string,
igUserId: string,
commentId: string,
message: string
): Promise<PrivateReplyResult> {
const baseUrl = 'https://graph.instagram.com';
try {
const response = await fetch(
`${baseUrl}/${igUserId}/messages`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify({
recipient: {
comment_id: commentId,
},
message: {
text: message,
},
}),
}
);
if (!response.ok) {
const errorText = await response.text();
// Handle specific error cases
if (errorText.includes('551') || errorText.toLowerCase().includes('already')) {
return {
messageId: '',
success: false,
error: 'A private reply has already been sent to this comment. Instagram only allows one private reply per comment.',
};
}
if (errorText.includes('10903')) {
return {
messageId: '',
success: false,
error: 'Comment is older than 7 days. Private replies must be sent within 7 days.',
};
}
return {
messageId: '',
success: false,
error: `Private reply failed: ${response.status} - ${errorText}`,
};
}
const data = await response.json();
return {
messageId: data.message_id || data.id,
success: true,
};
} catch (error) {
return {
messageId: '',
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
Note : Instagram n'autorise qu'UNE SEULE réponse privée par commentaire, et elle doit être envoyée dans les 7 jours suivant la publication du commentaire. Planifiez votre automatisation en conséquence.
Masquer et supprimer des commentaires
Une modération efficace des commentaires Instagram nécessite la capacité de masquer les commentaires inappropriés ou de supprimer vos propres réponses.
Masquer et afficher les commentaires
async function hideComment(
accessToken: string,
commentId: string
): Promise<{ success: boolean; error?: string }> {
const baseUrl = 'https://graph.instagram.com';
try {
const response = await fetch(
`${baseUrl}/${commentId}?hide=true&access_token=${accessToken}`,
{ method: 'POST' }
);
if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `Hide failed: ${response.status} - ${errorText}`,
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
async function unhideComment(
accessToken: string,
commentId: string
): Promise<{ success: boolean; error?: string }> {
const baseUrl = 'https://graph.instagram.com';
try {
const response = await fetch(
`${baseUrl}/${commentId}?hide=false&access_token=${accessToken}`,
{ method: 'POST' }
);
if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `Unhide failed: ${response.status} - ${errorText}`,
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
Supprimer des commentaires
Vous ne pouvez supprimer que les commentaires que vous avez faits (vos propres réponses) :
async function deleteComment(
accessToken: string,
commentId: string
): Promise<{ success: boolean; error?: string }> {
const baseUrl = 'https://graph.instagram.com';
try {
const response = await fetch(
`${baseUrl}/${commentId}?access_token=${accessToken}`,
{ method: 'DELETE' }
);
if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `Delete failed: ${response.status} - ${errorText}`,
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
Filtrage des commentaires et règles de modération
Construire un système de modération automatisé nécessite de définir des règles pour déterminer quel contenu signaler, masquer ou auquel répondre.
Moteur de règles de modération
interface ModerationRule {
id: string;
name: string;
type: 'keyword' | 'regex' | 'sentiment' | 'spam';
pattern?: string;
keywords?: string[];
action: 'hide' | 'flag' | 'delete' | 'reply';
replyTemplate?: string;
enabled: boolean;
}
interface ModerationResult {
commentId: string;
triggered: boolean;
matchedRules: string[];
action: 'hide' | 'flag' | 'delete' | 'reply' | 'none';
replyMessage?: string;
}
class CommentModerator {
private rules: ModerationRule[];
constructor(rules: ModerationRule[]) {
this.rules = rules.filter(r => r.enabled);
}
evaluate(comment: InstagramComment): ModerationResult {
const matchedRules: string[]