Pular para o conteúdo

Permissões e escopos

16 permissões nomeadas, e não um bitfield. Para um App, 11 delas podem ser autorizadas numa instalação — e o que ele de fato pode é a interseção dos cargos com esses escopos.

Nomes, não bits

O vocabulário é fechado e viaja como texto. Um cargo carrega "permissions": ["gerenciar_canais", "criar_convite"], e é isso que você lê no retrato do servidor e no evento de cargo. Não há máscara para montar nem potência de dois para somar.

Os nomes são as únicas palavras em português que atravessam a fronteira, e a razão é simples: eles são gravados no banco, aparecem na tela de autorização que uma pessoa lê e são o mesmo texto nos dois lados. Traduzi-los na borda criaria duas nomenclaturas para manter.

O vocabulário

PermissãoInstalávelO que ela permite
administradorsimTudo, sem exceção. Só o dono do servidor consegue conceder.
gerenciar_cargossimCriar, editar, apagar e reordenar cargos — dentro do próprio alcance.
gerenciar_canaissimCriar, editar, mover entre categorias e arquivar canais.
gerenciar_mensagenssimApagar mensagem de qualquer pessoa no servidor.
publicar_avisossimPublicar comunicado do servidor.
mencionar_todossimUsar @everyone e @here. É permissão própria porque chamar o servidor inteiro é barulho que não se desfaz.
criar_convitesimGerar link de convite.
gerenciar_convitessimVer e revogar os convites que existem.
gerenciar_servidorsimNome, ícone, banner, categorias, descoberta e ordenação.
gerenciar_appssimAutorizar App, ajustar o que ele pode pedir e removê-lo.
expulsar_membrosnãoTirar alguém do servidor.
banir_membrosnãoBarrar a volta de alguém.
desconectar_de_chamadanãoTirar alguém de um canal de voz.
mutar_microfonenãoSilenciar o microfone de alguém em chamada.
mutar_audionãoEnsurdecer alguém em chamada.
ver_registrosimLer o registro de auditoria do servidor.

Por que a moderação fica de fora

As 5 permissões marcadas como não instaláveis são as de agir sobre pessoas: expulsar_membros, banir_membros, desconectar_de_chamada, mutar_microfone, mutar_audio. Elas não são escopo que uma instalação possa autorizar. Um cargo ainda pode carregá-las, e a interseção as zera — conceder o que não produz nada é armadilha; deixar de fora é honesto. O motivo completo, junto das outras três fronteiras, está em O que um App nunca alcança.

Escopo, cargo, e a regra que os junta

Um App instalado tem duas coisas, e elas não são a mesma:

  • Escopos — o teto da instalação. É o que quem autorizou disse que este App pode chegar a ter naquele servidor.
  • Cargos — o que ele de fato recebeu, como qualquer membro recebe.
capacidade = permissões dos cargos ∩ escopos autorizados

O cargo automático

Toda instalação tem um cargo próprio, que nasce e morre com ela. Ele não é editável pelas rotas de cargo (a recusa é APP_ROLE_IMMUTABLE) — o que se ajusta é o conjunto de escopos, na tela de autorização.

Hierarquia

  • Posto é separado de permissão. O dono está acima de tudo; depois vem a posição do cargo mais alto de cada um.
  • O posto de um App é o do cargo mais alto que ele tiver, como o de qualquer membro — não existe posto fixo. Quem impede a escalada não é a posição, é o teto: sem o escopo autorizado na instalação, cargo nenhum, por mais alto, produz capacidade. Quando a capacidade existe nos dois eixos, a hierarquia vale como para gente — e chegar lá exige duas decisões humanas conscientes: alguém autorizou o escopo e alguém deu o cargo.
  • Ninguém concede o que não tem. Uma tentativa de dar mais do que o próprio alcance volta como CANNOT_GRANT_PERMISSION.
  • Só o dono liga administrador. Vale para ligar a permissão num cargo e para atribuir um cargo que já a tem.

O canal, que é o terceiro eixo

As permissões acima valem no servidor inteiro. Dentro de um canal existe um eixo próprio, e ele alcança o seu App do mesmo jeito que alcança gente — quer dizer: um App instalado pode não ver um canal, e pode ver e não poder escrever nele. São duas perguntas separadas, com respostas separadas.

Visibilidade: uma lista de quem alcança

Um canal restrito não é "todo mundo menos alguns": é uma lista aditiva de contas e cargos que o alcançam. Quem não está nela não vê o canal, e para o seu App isso tem duas consequências que valem mais do que qualquer parágrafo:

  • O Gateway não entrega o que o App não vê. O corte é o mesmo funil que vale para gente, e ele acompanha a mudança sem reconexão: quando o App perde o alcance de um canal, os eventos daquele canal param de chegar no fluxo que já estava aberto.
  • Na REST, a resposta é CHANNEL_NOT_FOUND, e não uma recusa de permissão. É o mesmo 404 de canal inexistente, de propósito: distinguir contaria a quem sonda que existe um canal ali.

Quem dá alcance a um App é quem administra o servidor, pelos cargos da instalação ou nomeando o App direto. Não há gesto seu nisso, e não há rota para o App pedir alcance.

Capacidades: o que dá para fazer lá dentro

Visto o canal, cada gesto passa por uma capacidade própria dele — ler, enviar, anexar, reagir, mencionar_todos, gerenciar_mensagens nos canais de texto; entrar, falar, video e transmitir_tela nos de voz. A recusa aqui é MISSING_PERMISSION, com 403: o canal existe, e o gesto é que não passa.

Sem ninguém ter escrito nada sobre aquele canal, as capacidades de membro comum respondem sim — falar continua sendo o piso de quem está lá dentro. O que as tira é um deny escrito, no canal ou na categoria que o contém; o do canal vence o da categoria.

Ver o canal e ler nele são duas perguntas

Duas delas decidem o que o gateway entrega, cada uma na sua superfície: ler nos canais de texto e entrar nos de voz. Negada a capacidade da superfície, o canal continua existindo para o seu App — ele segue na lista e segue mandando channel_update e channel_delete —, e nada do que acontece dentro chega: mensagem, reação, digitação e, numa sala de voz, a ocupação.

Cada canal do retrato traz canRead e canSend: a resposta por canal, antes de tentar. canRead: false com canSend: true é configuração legítima — a caixa de sugestões: o App posta e não recebe nem o próprio eco.

const { channels } = await app.rest.getServer({ serverId });
const mudos = channels.filter((canal) => !canal.canRead);
const restritos = channels.filter((canal) => canal.visibility === "restricted");

A lista que volta já é só o que o App alcança: um canal que ele nem vê não aparece nela. Um bot mudo num canal específico quase sempre é isto, e não bug.

Uma diferença que quem vem de fora não espera: administrador não é curinga dentro do canal. Ele concede tudo no degrau do servidor e não fura um deny de canal — é o que faz canal restrito significar alguma coisa.

Falar não está nesta lista

Não existe permissão de "enviar mensagens": falar é o piso de quem é membro. O equivalente para o App é estar instalado. A consequência é que um App com o conjunto de escopos vazio ainda escreve — e quem instala já é quem tem gerenciar_apps, com o gesto de desinstalar à mão, que cala na hora.

O que não existe

Não há escopo OAuth de usuário — nem consentimento individual, nem redirect URI: o App age sempre como ele mesmo. E não há escopo por canal: o teto da instalação é do servidor inteiro. O que existe por canal é o eixo da seção acima, e ele só TIRA — nenhum override amplia um App além dos escopos autorizados. O resto do que não existe, e por quê, está em O que um App nunca alcança.