Construire une intégration Reddit qui gère les messages privés nécessite de comprendre l'approche unique de Reddit en matière de messagerie. Le système reddit api messages utilise une combinaison d'endpoints de boîte de réception, d'identifiants fullname et de scopes OAuth qui diffèrent des API de messagerie typiques. Ce guide vous accompagne à travers tout ce dont vous avez besoin pour implémenter une intégration complète des DM Reddit.
Le système de messagerie de Reddit gère à la fois les messages privés entre utilisateurs et les réponses aux commentaires dans une boîte de réception unifiée. Votre application doit distinguer ces types, gérer les fils de conversation et gérer les limites de débit de Reddit de manière appropriée.
Introduction à l'API Reddit
Reddit fournit une API complète pour accéder aux données utilisateur, publier du contenu et gérer les messages privés. L'API utilise OAuth 2.0 pour l'authentification et nécessite des scopes spécifiques pour différentes opérations.
Pour la fonctionnalité de messagerie, vous travaillerez avec ces endpoints principaux :
| Endpoint | Objectif | Méthode |
|---|---|---|
/message/inbox | Tous les éléments de la boîte de réception (messages + réponses aux commentaires) | GET |
/message/messages | Messages privés uniquement | GET |
/message/sent | Messages envoyés | GET |
/api/compose | Envoyer un nouveau message | POST |
/api/comment | Répondre à un message | POST |
/api/read_message | Marquer comme lu | POST |
L'API reddit inbox retourne les messages dans un format de listing avec support de pagination. Chaque message inclut des métadonnées sur l'expéditeur, le destinataire, le sujet et le fil de conversation.

Authentification OAuth 2.0 pour Reddit
Avant d'accéder à l'API reddit private messages, vous devez authentifier les utilisateurs avec les scopes OAuth corrects. Le scope privatemessages accorde l'accès pour lire et envoyer des messages directs.
interface RedditAuthConfig {
clientId: string;
clientSecret: string;
redirectUri: string;
scopes: string[];
}
const config: RedditAuthConfig = {
clientId: process.env.REDDIT_CLIENT_ID || '',
clientSecret: process.env.REDDIT_CLIENT_SECRET || '',
redirectUri: process.env.REDDIT_REDIRECT_URI || '',
scopes: ['identity', 'privatemessages', 'read', 'history']
};
function getAuthUrl(state: string): string {
const params = new URLSearchParams({
client_id: config.clientId,
response_type: 'code',
state: state,
redirect_uri: config.redirectUri,
duration: 'permanent',
scope: config.scopes.join(' '),
});
return `https://www.reddit.com/api/v1/authorize?${params.toString()}`;
}
async function exchangeCodeForToken(code: string): Promise<TokenResponse> {
const credentials = Buffer.from(
`${config.clientId}:${config.clientSecret}`
).toString('base64');
const response = await fetch('https://www.reddit.com/api/v1/access_token', {
method: 'POST',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'YourApp/1.0.0',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: config.redirectUri,
}).toString(),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Token exchange failed: ${response.status} ${error}`);
}
return response.json();
}
interface TokenResponse {
access_token: string;
refresh_token: string;
expires_in: number;
scope: string;
token_type: string;
}
Note : Reddit exige un en-tête User-Agent descriptif pour toutes les requêtes API. Utiliser un User-Agent générique ou manquant entraînera une limitation de débit ou des requêtes bloquées.
Comprendre les Fullnames (t1_, t3_, t4_)
Reddit utilise un système de "fullname" pour identifier tous les objets dans l'API. Comprendre ces préfixes est essentiel lorsque vous travaillez avec l'API reddit dm :
| Préfixe | Type d'objet | Exemple |
|---|---|---|
t1_ | Commentaire | t1_abc123 |
t2_ | Compte | t2_xyz789 |
t3_ | Lien/Publication | t3_def456 |
t4_ | Message | t4_ghi012 |
t5_ | Subreddit | t5_jkl345 |
Lorsque vous répondez à des messages ou les marquez comme lus, vous devez utiliser le fullname complet incluant le préfixe :
function normalizeFullname(id: string, type: 't1' | 't3' | 't4'): string {
if (id.startsWith(`${type}_`)) {
return id;
}
return `${type}_${id}`;
}
// Exemples d'utilisation
const messageFullname = normalizeFullname('abc123', 't4'); // Retourne: t4_abc123
const commentFullname = normalizeFullname('t1_xyz789', 't1'); // Retourne: t1_xyz789
Récupérer les messages de la boîte de réception
L'endpoint inbox retourne tous les éléments incluant les messages privés et les réponses aux commentaires. Pour récupérer uniquement les messages privés, utilisez plutôt l'endpoint /message/messages.
interface RedditMessage {
id: string;
name: string; // Fullname (t4_xxx)
author: string;
dest: string;
subject: string;
body: string;
created_utc: number;
new: boolean;
was_comment: boolean;
first_message_name: string | null;
}
interface InboxResponse {
messages: RedditMessage[];
after: string | null;
}
async function fetchInboxMessages(
accessToken: string,
options: { limit?: number; after?: string } = {}
): Promise<InboxResponse> {
const limit = Math.min(options.limit || 25, 100);
let url = `https://oauth.reddit.com/message/messages?limit=${limit}&raw_json=1`;
if (options.after) {
url += `&after=${options.after}`;
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'User-Agent': 'YourApp/1.0.0',
},
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to fetch inbox: ${response.status} ${error}`);
}
const data = await response.json();
const messages: RedditMessage[] = (data.data?.children || [])
.filter((item: any) => !item.data.was_comment) // Filtrer les réponses aux commentaires
.map((item: any) => ({
id: item.data.id,
name: item.data.name,
author: item.data.author,
dest: item.data.dest,
subject: item.data.subject,
body: item.data.body,
created_utc: item.data.created_utc,
new: item.data.new,
was_comment: item.data.was_comment,
first_message_name: item.data.first_message_name,
}));
return {
messages,
after: data.data?.after || null,
};
}
Le champ first_message_name est important pour le threading. Les messages dans la même conversation partagent cette valeur, vous permettant de les regrouper.
Envoyer des messages privés avec l'API Reddit
Pour envoyer un nouveau message privé, utilisez l'endpoint /api/compose. Cet endpoint nécessite le nom d'utilisateur du destinataire, une ligne de sujet et le corps du message.
interface SendMessageParams {
to: string;
subject: string;
text: string;
}
interface SendMessageResult {
success: boolean;
errors?: string[];
}
async function sendPrivateMessage(
accessToken: string,
params: SendMessageParams
): Promise<SendMessageResult> {
const { to, subject, text } = params;
// Valider les entrées
if (!to || !subject || !text) {
return {
success: false,
errors: ['Destinataire, sujet et texte du message sont requis'],
};
}
if (subject.length > 100) {
return {
success: false,
errors: ['Le sujet doit faire 100 caractères ou moins'],
};
}
const response = await fetch('https://oauth.reddit.com/api/compose', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'User-Agent': 'YourApp/1.0.0',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
api_type: 'json',
to,
subject,
text,
}).toString(),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to send message: ${response.status} ${error}`);
}
const data = await response.json();
const errors = data.json?.errors || [];
if (errors.length > 0) {
return {
success: false,
errors: errors.map((e: any[]) => e.join(': ')),
};
}
return { success: true };
}
Note : Reddit limite les nouveaux comptes pour l'envoi de messages privés. Les utilisateurs peuvent avoir besoin de vérifier leur email et d'avoir un certain âge de compte avant que la fonctionnalité de messagerie ne devienne disponible.
Répondre aux messages
Répondre à un message existant utilise l'endpoint /api/comment (le même endpoint utilisé pour les réponses aux commentaires). Vous avez besoin du fullname du message auquel vous répondez.
interface ReplyResult {
success: boolean;
replyId?: string;
errors?: string[];
}
async function replyToMessage(
accessToken: string,
messageFullname: string,
text: string
): Promise<ReplyResult> {
// Assurer le format fullname correct
const fullname = messageFullname.startsWith('t4_')
? messageFullname
: `t4_${messageFullname}`;
const response = await fetch('https://oauth.reddit.com/api/comment', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'User-Agent': 'YourApp/1.0.0',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
api_type: 'json',
thing_id: fullname,
text,
}).toString(),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to reply: ${response.status} ${error}`);
}
const data = await response.json();
const errors = data.json?.errors || [];
if (errors.length > 0) {
return {
success: false,
errors: errors.map((e: any[]) => e.join(': ')),
};
}
const replyData = data.json?.data?.things?.[0]?.data;
return {
success: true,
replyId: replyData?.id,
};
}
Threading et regroupement des messages
Reddit regroupe les messages en conversations en utilisant le champ first_message_name. Construire une vue de conversation nécessite de récupérer les messages des dossiers boîte de réception et envoyés, puis de les regrouper par fil.
interface Conversation {
id: string;
participantUsername: string;
subject: string;
messages: ThreadMessage[];
lastUpdated: Date;
hasUnread: boolean;
}
interface ThreadMessage {
id: string;
fullname: string;
text: string;
sentAt: Date;
isFromMe: boolean;
senderUsername: string;
}
async function getConversations(
accessToken: string,
myUsername: string
): Promise<Conversation[]> {
// Récupérer à la fois les messages de la boîte de réception et les messages envoyés
const [inboxResponse, sentResponse] = await Promise.all([
fetch('https://oauth.reddit.com/message/messages?limit=100&raw_json=1', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'User-Agent': 'YourApp/1.0.0',
},
}),
fetch('https://oauth.reddit.com/message/sent?limit=100&raw_json=1', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'User-Agent': 'YourApp/1.0.0',
},
}),
]);
const inboxData = await inboxResponse.json();
const sentData = await sentResponse.json();
const allMessages: any[] = [
...(inboxData.data?.children || []),
...(sentData.data?.children || []),
];
// Regrouper par conversation
const conversationsMap = new Map<string, Conversation>();
const seenMessageIds = new Set<string>();
for (const item of allMessages) {
const msg = item.data;
// Ignorer les doublons et les réponses aux commentaires
if (seenMessageIds.has(msg.name) || msg.was_comment) {
continue;
}
seenMessageIds.add(msg.name);
const conversationId = msg.first_message_name || msg.name;
const isFromMe = msg.author?.toLowerCase() === myUsername.toLowerCase();
const otherParticipant = isFromMe ? msg.dest : msg.author;
const threadMessage: ThreadMessage = {
id: msg.id,
fullname: msg.name,
text: msg.body,
sentAt: new Date(msg.created_utc * 1000),
isFromMe,
senderUsername: msg.author,
};
if (!conversationsMap.has(conversationId)) {
conversationsMap.set(conversationId, {
id: conversationId,
participantUsername: otherParticipant,
subject: msg.subject,
messages: [threadMessage],
lastUpdated: threadMessage.sentAt,
hasUnread: msg.new && !isFromMe,
});
} else {
const conversation = conversationsMap.get(conversationId)!;
conversation.messages.push(threadMessage);
if (threadMessage.sentAt > conversation.lastUpdated) {
conversation.lastUpdated = threadMessage.sentAt;
}
if (msg.new && !isFromMe) {
conversation.hasUnread = true;
}
}
}
// Trier les messages dans chaque conversation et trier les conversations par dernière mise à jour
const conversations = Array.from(conversationsMap.values());
for (const conv of conversations) {
conv.messages.sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime());
}
conversations.sort((a, b) => b.lastUpdated.getTime() - a.lastUpdated.getTime());
return conversations;
}
Marquer les messages comme lus
L'endpoint /api/read_message accepte une liste de fullnames séparés par des virgules, vous permettant de marquer plusieurs messages comme lus en une seule requête.
async function markMessagesAsRead(
acc