Migrando um App
Um mapa de conceitos para quem já escreveu bot em outra plataforma. Você vai reconhecer o terreno; o código, você reescreve.
Identidade e credencial
| Lá | Aqui | O que muda de verdade |
|---|---|---|
Application | App | — |
Bot user | O App instalado | Lá são duas entidades (application e bot user); aqui é uma. O App instalado num servidor é um membro: aparece na lista, recebe cargos, assina as mensagens que escreve, reage e responde à hierarquia. O que some é o segundo id — o client_id e o "id do bot" viram um número só, o Application ID. |
discord.js / biblioteca de comunidade | @trivo/sdk | Oficial e única: não existe "a biblioteca X ou a Y", e um App de produção não é construído por fora dela. Ela sobe junto com o servidor, e o contrato que ela expõe É o contrato. |
Bot token | trivo_app.<AppID>.<segredo> | Uma por App, mostrada uma vez, esquema Bearer. |
Client secret | Não existe — o que existe é a segunda credencial | Lá ele serve à troca de código por token, e aqui não há o que trocar: não existe delegação de usuário, e o App age sempre como ele mesmo. O que o Trivo tem é uma segunda vaga de credencial do mesmo tipo, para rotacionar sem janela de queda — emita a nova, suba o processo com ela, revogue a velha. Não é um segredo com outro poder, e o nome não finge que é. |
Snowflake | Id público | Mesma ideia, e a mesma serialização em string. |
Guild | Servidor | — |
Permissões e escopos
| Lá | Aqui | O que muda de verdade |
|---|---|---|
Bitfield de permissões | Permissões nomeadas | Nomes em texto, não bits. Nada de somar potências de dois. |
Escopos OAuth | Escopos da instalação | Eles são o teto. E há uma regra que não existe lá: capacidade = cargos ∩ escopos. |
Permissão por canal | Visibilidade + capacidades do canal | São dois eixos, e não um: ver o canal é lista aditiva de contas e cargos (quem está fora nem recebe os eventos daquele canal), e o que se faz lá dentro é uma escada de capacidades com allow e deny, herdada da categoria. Duas diferenças de lá: deny de cargo nomeado vence allow de outro cargo, e administrador não é curinga dentro do canal. O teto continua valendo: nenhum override amplia um App além dos escopos da instalação. |
Gateway
| Lá | Aqui | O que muda de verdade |
|---|---|---|
Identify / Hello / Ready | Uma requisição | Bearer no cabeçalho, e o primeiro quadro já é evento. Sem aperto de mão. |
Heartbeat com confirmação | Batimento do servidor a cada 25 s | Sentido inverso: não há nada a responder, e quem detecta o silêncio é você. |
Sequência de evento | O campo id: do SSE | Do protocolo, não nosso. |
Resume | Last-Event-ID | Também do protocolo. Janela de 500 eventos ou 120 segundos. |
Códigos de fechamento | connection_close | Um quadro antes do fim, com code, motivo legível e reconnect. Texto, não número. |
Bitfield de intents | ?intents= com nomes | Ausente entrega o padrão; presente e vazio é recusa que ensina. |
Intents privilegiadas | Nenhuma família é sensível hoje | A intent aqui é otimização de banda, e a fronteira é a instalação mais o funil de visibilidade — nunca a declaração do cliente. A sensibilidade é um eixo próprio no desenho, separado de escopo e de permissão; quando a primeira família sensível existir, ela pedirá aprovação num bloco à parte da autorização. |
Comandos pelo gateway | Não existem | Tudo o que o App faz, faz pela REST. |
Sharding | Não existe | — |
Interações
| Lá | Aqui | O que muda de verdade |
|---|---|---|
Deferred response | Resposta diferida | Prazo menor, e o marcador "pensando" só quem acionou vê. |
Followup | Seguimento | Janela de 10 minutos, teto de 5. |
Ephemeral flag | Resposta privada | Não é mensagem: é evento endereçado, e não persiste. |
Interactions endpoint + assinatura | O mesmo conceito | Chave pública da plataforma — uma só, não uma por App. Assine sobre o corpo cru, como lá. O que muda: o destinatário está DENTRO do material assinado, e é isso que impede um App de tratar como sua uma interação assinada para outro. |
Webhook de entrada | Não existe | — |
Comandos e componentes
| Lá | Aqui | O que muda de verdade |
|---|---|---|
Slash command | Comando de barra | Registrado por app.rest.createCommand(…), e vale na hora: não há a espera de propagação global que você conhece. |
Command options | Argumentos | Mesmos tetos, por familiaridade. Tipo, obrigatoriedade, faixa e lista fechada. |
Subcommand group | Grupo de subcomando | Mesma profundidade: um nível. |
Autocomplete | Autocompletar | Prazo bem menor — 1 s, não 3 —, e a resposta é suggest com choices. Nada que você faça ali vira erro na tela de quem digita. |
User/message context command | Comando sobre pessoa / sobre mensagem | Nos menus de clique direito que já existem. |
Buttons / select menus | Componentes | Vocabulário fechado, e quem renderiza é o cliente. Nada de HTML nem CSS — o catálogo está em Componentes e formulários. |
Modal | Formulário | A definição viaja na resposta e fica gravada na interação — é contra ela que o envio é conferido. |
Embed | Componente card | Um vocabulário rico só no produto inteiro, e não dois: o cartão é o mesmo bloco que a pessoa vê quando alguém compartilha um link. |
Monetização, diretório de Apps, Apps de usuário, Activities | Não existem | E não há data prometida para nenhum deles. |
As seis coisas que vão te surpreender
1. Não existe conta de robô — mas existe membro
Não há conta, senha, e-mail nem login — e ainda assim o seu App é membro de verdade do servidor onde foi instalado: aparece na lista, recebe cargos, assina o que escreve e responde à hierarquia como qualquer um.
O selo APP na tela deriva de um booleano que o servidor manda, e não do nome que você escolheu: chamar o seu App de "Fulano" não engana ninguém, e nenhum nome de exibição produz a pílula.
2. Cargo poderoso não amplia credencial
Um admin pode dar administrador ao seu App. Sem o escopo autorizado na instalação, isso não produz nada. É a regra da interseção, e ela existe para que "instalei com escopo pequeno" continue valendo depois que alguém mexe nos cargos.
3. Você não alcança conversa privada. Nunca.
E não é configuração: são três camadas independentes — nenhuma moldura de /api/me/* aceita Bearer, o Gateway roteia DM por id de conta (que App não tem), e a DM é cifrada ponta a ponta. O metadado de quem fala com quem é justamente o que o funil protege.
4. A resposta privada não é uma mensagem
Ela não fica no histórico de ninguém, nem no seu. Não há retenção, não há exclusão de conta que a alcance, não há moderação sobre ela — porque não há linha.
5. O 404 não distingue
"Servidor não existe", "você não está instalado nele" e "o id está torto" respondem a mesma coisa. É anti-oráculo, e é de propósito: distinguir entregaria informação a quem sonda.
6. Se você só responde a comandos, prefira o endpoint HTTP
O Gateway é uma conexão viva num processo só, e são 2 vagas por App — elas são suas, e ninguém as disputa com você. O que é dividido com todo mundo é o teto global de 50 conexões no gateway inteiro. Quem responde a comando não precisa de fluxo aberto: o caminho é Endpoint de interações.
O que esta página NÃO promete
- Nenhuma tradução automática de payload, nem adaptador de biblioteca.
- Nenhuma paridade de campo: os nomes são outros, e não vão mudar para se parecer com os de fora.
- Nenhum cronograma para o que não existe.
O que hoje não existe, e está registrado como decisão e não como esquecimento: monetização, diretório de Apps, Apps instaláveis por usuário, atividades embutidas, webhook de saída, hierarquia de App e verificação de App.
O que existe e ninguém espera encontrar aqui: a moderação da plataforma pode suspender ou banir um App, e pode revogar a credencial dele sem excluí-lo. Nos dois casos o Gateway fecha com reconnect: false — APP_SANCTIONED ou CREDENTIAL_REVOKED — e a REST passa a responder 401 igual a token inválido. Trate os dois como "pare e avise um humano".