vsyncDOCUMENTAÇÃO

Fundamentos de Apps

Interações e comandos

Registre comandos de barra, entenda o fluxo de uma interação e responda com callbacks, follow-ups e respostas efêmeras.

Uma interação acontece quando alguém usa um comando de barra do seu bot, clica num botão ou menu ou envia um modal. O bot recebe o evento INTERACTION_CREATE pelo gateway (ou por HTTP) e tem 3 segundos para dar o primeiro retorno.

Como o usuário vê#

No chat de um servidor, ao digitar / aparece a lista de comandos de todos os bots daquele servidor (globais e do servidor). Ao escolher um, o app mostra um formulário com as opções acima da caixa de mensagem. Ao enviar, o bot responde e a mensagem aparece com a linha "fulano usou /comando". O comando só aparece se o bot estiver no servidor, e só em canais de texto onde a pessoa pode ver e escrever e o bot pode ver.

Registrar comandos#

Comandos são globais (valem em todos os servidores do bot, /applications/:appId/commands) ou de servidor (/applications/:appId/guilds/:g/commands, o bot precisa estar nele). Se existir um comando de servidor com o mesmo nome de um global, o do servidor é o que roda.

Registre tudo de uma vez com PUT, que sobrescreve a lista do escopo (comandos que você não enviar são apagados; os ids dos que continuam são preservados):

Bash
curl -s -X PUT $API/applications/$APP_ID/commands \
  -H "Authorization: Bot $TOKEN" -H "Content-Type: application/json" \
  -d '[
    {"name":"ola","description":"Diz olá","options":[
      {"name":"nome","description":"Como te chamar","type":"string","required":false}
    ]},
    {"name":"cor","description":"Escolha uma cor","options":[
      {"name":"cor","description":"A cor","type":"string","required":true,
       "choices":[{"name":"Azul","value":"azul"},{"name":"Verde","value":"verde"}]}
    ]}
  ]'

Para criar ou atualizar um só, use POST (cria com 201; se o nome já existe no escopo, atualiza com 200).

Campos de um comando:

CampoRegra
name1 a 32 caracteres: letras minúsculas, números, - ou _
description1 a 100 caracteres
optionsaté 25 opções (opcional)

Campos de uma opção: name (mesma regra do comando, sem repetir), description (1 a 100), type, required (opcional) e choices (opcional).

typeO que chega no INTERACTION_CREATE
stringtexto de até 2000 caracteres
integernúmero inteiro
numbernúmero decimal
booleantrue ou false
userid de um membro do servidor
channelid de um canal do servidor que a pessoa vê
roleid de um cargo do servidor

choices só existe para string, integer e number: até 25 itens { name, value } (valores de texto até 100 caracteres, ou números do tipo da opção). Limites: 100 comandos por escopo (global, ou cada servidor). Os valores são validados antes de chegarem ao bot; opção obrigatória ausente, valor inválido e opção desconhecida devolvem 400 para quem usou o comando.

Para opções user, channel e role, o data traz também resolved com os detalhes:

JSON
"data": {
  "name": "dar-cargo",
  "options": { "alvo": "6a7c6c99-...", "cargo": "efb6b62a-..." },
  "resolved": {
    "users": { "6a7c6c99-...": { "id": "6a7c6c99-...", "username": "betov78h", "displayName": "beto", "createdAt": 1791300000000, "prismaActive": false, "prismaSince": null, "pulsing": false, "pulsingSince": null, "pulseBadge": null, "nextPulseBadge": null, "nickname": null, "roleIds": [], "joinedAt": 1791420369512 } },
    "roles": { "efb6b62a-...": { "id": "efb6b62a-...", "name": "Moderador", "color": null } }
  }
}

Cada entrada de resolved.users é o objeto de usuário completo (com createdAt, Prisma, Pulse e presença) mais nickname, roleIds, joinedAt e o pulsingSince daquele servidor: o bastante para um comando userinfo sem outra chamada à API.

Comandos registrados aparecem na aba Comandos do aplicativo (onde dá para excluí-los). Cada pessoa pode usar até 5 comandos a cada 5 segundos.

Fluxo de uma interação#

  1. A pessoa envia o comando. A API responde 202 a ela e entrega INTERACTION_CREATE ao bot pelo gateway (ou por HTTP, veja abaixo).
  2. O bot tem 3 segundos para dar o primeiro retorno. Sem retorno, a pessoa vê (só ela) a mensagem efêmera "O aplicativo não respondeu." e o token fica inutilizável (410).
  3. O retorno é um callback: POST /interactions/:id/:token/callback (sem cabeçalho Bot).
    • { "type": "message", "data": { "body": "texto", "embeds": [...], "attachments": ["att:..."], "ephemeral": false } } responde na hora, com uma mensagem normal do bot (guardada no canal). attachments são arquivos preparados antes no canal da interação (veja Anexos em respostas de interação).
    • { "type": "defer" } mostra "NomeDoBot está pensando…" e libera você de pressa. Depois conclua com o primeiro follow-up (ou com PATCH @original).
    • Cliques em botões e menus e envios de modais usam os mesmos callbacks, mais update, defer_update e modal (veja as seções seguintes).
  4. Follow-ups: POST /webhooks/:appId/:token com { body, embeds, components, attachments, ephemeral }. Se a interação estava em defer, o primeiro follow-up substitui o "pensando…" (resposta 200); os seguintes criam mensagens novas (201), no máximo 5 por interação.
  5. Editar a resposta: PATCH /webhooks/:appId/:token/messages/@original com { body, embeds, components, attachments } (qualquer um deles; attachments substitui a lista de anexos). Em interação ainda em defer, funciona como a primeira resposta.
  6. O token da interação vale 15 minutos. Depois disso as rotas respondem 410 (e 404 quando a limpeza já removeu).
Bash
# responder
curl -s -X POST $API/interactions/$ID/$ITOKEN/callback -H "Content-Type: application/json" \
  -d '{"type":"message","data":{"body":"Olá!"}}'

# deferir, e concluir depois
curl -s -X POST $API/interactions/$ID/$ITOKEN/callback -H "Content-Type: application/json" -d '{"type":"defer"}'
curl -s -X POST $API/webhooks/$APP_ID/$ITOKEN -H "Content-Type: application/json" -d '{"body":"Pronto!"}'

# editar a resposta original
curl -s -X PATCH $API/webhooks/$APP_ID/$ITOKEN/messages/@original -H "Content-Type: application/json" -d '{"body":"Texto novo"}'

Erros dos callbacks e follow-ups: 400 corpo inválido, resposta vazia ou anexo inválido; 404 token errado; 409 a interação já foi respondida (no callback) ou ainda não foi respondida (em follow-up ou edição); 410 expirou ou "não respondeu"; 429 limite de follow-ups ou limite de envio do bot; 403 o bot perdeu a permissão de enviar naquele canal (ou O bot não pode anexar arquivos aqui. sem ATTACH_FILES).

Respostas efêmeras: com "ephemeral": true a mensagem só chega a quem usou o comando, não é guardada (some ao recarregar) e não conta no limite de envio do canal. Ela pode ser editada via @original, mas não aceita anexos. Se você usou defer com ephemeral: true, repita ephemeral: true no primeiro follow-up.

Tipos de interação e respostas (callbacks)#

O INTERACTION_CREATE tem sempre id, token, applicationId, type, guildId, channelId, user e member; o conteúdo de data depende do type:

typeQuando acontecedata
commandalguém usou um comando de barra{ name, options, resolved? }
componentalguém clicou num botão ou escolheu num menu{ customId, componentType, values, message }
modal_submitalguém enviou um modal aberto pelo bot{ customId, fields, message? }

user é quem interagiu (objeto de usuário, com createdAt, Prisma, Pulse e presença) e member é o objeto de membro dela no servidor: { nickname, roleIds, joinedAt, pulsingSince, timeoutUntil }. Como toda interação acontece num servidor, member está sempre presente (vale também na entrega por HTTP).

Em component, componentType é "button" ou "select", values é a lista de value escolhidos (vazia em botões) e message é a mensagem onde o clique aconteceu (com id, body, embeds, components...). Em modal_submit, fields mapeia o customId de cada campo ao texto digitado, e message aparece quando o modal foi aberto por um clique.

JSON
{
  "op": "dispatch", "seq": 21, "t": "INTERACTION_CREATE",
  "d": {
    "id": "5c1e...", "token": "9f3a...", "applicationId": "1af40b9e-...", "type": "component",
    "guildId": "682f1017-...", "channelId": "a8c57340-...",
    "user": { "id": "bc86cc02-...", "username": "anav78h", "displayName": "ana", "bot": false, "createdAt": 1791400000000 },
    "member": { "nickname": null, "roleIds": [], "joinedAt": 1791420368416, "pulsingSince": null, "timeoutUntil": null },
    "data": { "customId": "painel:interesses", "componentType": "select", "values": ["jogos", "musica"], "message": { "id": "f251ec04-...", "body": "", "embeds": [], "components": [] } }
  }
}

Tipos de resposta (POST /interactions/:id/:token/callback, sem cabeçalho Bot; vale uma resposta por interação, a segunda devolve 409):

typePara que servedata
messageresponde com uma mensagem nova{ body?, embeds?, components?, attachments?, ephemeral? }
defermostra "pensando…" e dá mais tempo; conclua com follow-up ou PATCH @original{ ephemeral? }
updateem cliques: troca o conteúdo da mensagem do clique{ body?, embeds?, components?, attachments? } (o que não vier fica como estava)
defer_updateem cliques: confirma o clique sem mudar nadanenhum
modalabre um formulário para a pessoa (veja a próxima seção){ customId, title, fields }
  • update e defer_update precisam de uma mensagem de origem (um clique, ou o envio de um modal aberto por um clique). Em comandos devolvem 400. Depois de defer_update você ainda pode editar a mensagem do clique com PATCH @original; follow-ups criam mensagens novas (até 5 por interação).
  • Para sumir com os botões depois de usados, responda com update e "components": [].
  • Como sempre, o primeiro retorno tem de sair em 3 segundos, em qualquer tipo de interação.
  • Na entrega por HTTP (URL de interações), o corpo da resposta também pode ser qualquer um destes callbacks.

Veja também#