Blog

Reddit API: Messaggi Privati e Integrazione Inbox

Scopri come integrare i messaggi dell'API Reddit nella tua app. Guida completa per inviare DM, recuperare l'inbox e gestire le conversazioni con esempi in TypeScript.

Di

+8

Pubblica ovunque. Una API.

Try Free

Costruire un'integrazione Reddit che gestisca i messaggi privati richiede la comprensione dell'approccio unico di Reddit alla messaggistica. Il sistema reddit api messages utilizza una combinazione di endpoint inbox, identificatori fullname e scope OAuth che differiscono dalle tipiche API di messaggistica. Questa guida ti accompagna attraverso tutto ciò che devi implementare per un'integrazione completa dei DM Reddit.

Il sistema di messaggistica di Reddit gestisce sia i messaggi privati tra utenti che le risposte ai commenti in un inbox unificato. La tua applicazione deve distinguere tra questi tipi, gestire i thread delle conversazioni e gestire appropriatamente i rate limit di Reddit.

Introduzione all'API Reddit

Reddit fornisce un'API completa per accedere ai dati degli utenti, pubblicare contenuti e gestire i messaggi privati. L'API utilizza OAuth 2.0 per l'autenticazione e richiede scope specifici per diverse operazioni.

Per le funzionalità di messaggistica, lavorerai con questi endpoint principali:

EndpointScopoMetodo
/message/inboxTutti gli elementi inbox (messaggi + risposte ai commenti)GET
/message/messagesSolo messaggi privatiGET
/message/sentMessaggi inviatiGET
/api/composeInvia nuovo messaggioPOST
/api/commentRispondi al messaggioPOST
/api/read_messageSegna come lettoPOST

La reddit inbox api restituisce i messaggi in un formato listing con supporto alla paginazione. Ogni messaggio include metadati sul mittente, destinatario, oggetto e thread della conversazione.

Architettura API Reddit

Autenticazione OAuth 2.0 per Reddit

Prima di accedere alla reddit private messages api, devi autenticare gli utenti con gli scope OAuth corretti. Lo scope privatemessages garantisce l'accesso per leggere e inviare messaggi diretti.

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;
}

Nota: Reddit richiede un header User-Agent descrittivo per tutte le richieste API. Usare un User-Agent generico o mancante risulterà in rate limiting o richieste bloccate.

Comprendere i Fullname (t1_, t3_, t4_)

Reddit utilizza un sistema "fullname" per identificare tutti gli oggetti nell'API. Comprendere questi prefissi è fondamentale quando si lavora con la reddit dm api:

PrefissoTipo di OggettoEsempio
t1_Commentot1_abc123
t2_Accountt2_xyz789
t3_Link/Postt3_def456
t4_Messaggiot4_ghi012
t5_Subredditt5_jkl345

Quando rispondi ai messaggi o li segni come letti, devi usare il fullname completo incluso il prefisso:

function normalizeFullname(id: string, type: 't1' | 't3' | 't4'): string {
  if (id.startsWith(`${type}_`)) {
    return id;
  }
  return `${type}_${id}`;
}

// Esempi di utilizzo
const messageFullname = normalizeFullname('abc123', 't4'); // Restituisce: t4_abc123
const commentFullname = normalizeFullname('t1_xyz789', 't1'); // Restituisce: t1_xyz789

Recuperare i Messaggi dell'Inbox

L'endpoint inbox restituisce tutti gli elementi inclusi i messaggi privati e le risposte ai commenti. Per recuperare solo i messaggi privati, usa invece 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) // Filtra le risposte ai commenti
    .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,
  };
}

Il campo first_message_name è importante per il threading. I messaggi nella stessa conversazione condividono questo valore, permettendoti di raggrupparli insieme.

Build faster with Late

One API call to post everywhere. No OAuth headaches. No platform-specific code.

Free tier • No credit card • 99.97% uptime

Inviare Messaggi Privati con l'API Reddit

Per inviare un nuovo messaggio privato, usa l'endpoint /api/compose. Questo endpoint richiede il nome utente del destinatario, un oggetto e il corpo del messaggio.

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;
  
  // Valida gli input
  if (!to || !subject || !text) {
    return {
      success: false,
      errors: ['Destinatario, oggetto e testo del messaggio sono obbligatori'],
    };
  }
  
  if (subject.length > 100) {
    return {
      success: false,
      errors: ['L\'oggetto deve essere di 100 caratteri o meno'],
    };
  }
  
  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 };
}

Nota: Reddit limita i nuovi account dall'invio di messaggi privati. Gli utenti potrebbero dover verificare la loro email e avere una certa anzianità dell'account prima che la funzionalità di messaggistica diventi disponibile.

Rispondere ai Messaggi

Rispondere a un messaggio esistente usa l'endpoint /api/comment (lo stesso endpoint usato per le risposte ai commenti). Hai bisogno del fullname del messaggio a cui stai rispondendo.

interface ReplyResult {
  success: boolean;
  replyId?: string;
  errors?: string[];
}

async function replyToMessage(
  accessToken: string,
  messageFullname: string,
  text: string
): Promise<ReplyResult> {
  // Assicura il formato fullname corretto
  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 e Raggruppamento dei Messaggi

Reddit raggruppa i messaggi in conversazioni usando il campo first_message_name. Costruire una vista conversazione richiede il recupero dei messaggi sia dalla cartella inbox che da quella dei messaggi inviati, per poi raggrupparli per thread.

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[]> {
  // Recupera sia i messaggi inbox che quelli inviati
  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 || []),
  ];
  
  // Raggruppa per conversazione
  const conversationsMap = new Map<string, Conversation>();
  const seenMessageIds = new Set<string>();
  
  for (const item of allMessages) {
    const msg = item.data;
    
    // Salta i duplicati e le risposte ai commenti
    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;
      }
    }
  }
  
  // Ordina i messaggi all'interno di ogni conversazione e ordina le conversazioni per ultimo aggiornamento
  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;
}

Segnare i Messaggi come Letti

L'endpoint /api/read_message accetta una lista di fullname separati da virgola, permettendoti di segnare più messaggi come letti in una singola richiesta.

async function markMessagesAsRead(
  acc

Una API. 13+ piattaforme.

Integra i social in minuti, non settimane.

Progettato per sviluppatori. Apprezzato dalle agenzie. Fidato da 6.325 utenti.