Entrar

Bots no Samba

Este documento explica como os bots funcionam, como criar o seu e como o servidor é configurado para eles. Tudo aqui foi testado contra o servidor real; o exemplo da seção 8 roda como está.

1. Visão geral#

Um bot é uma conta do Samba operada por um programa em vez de uma pessoa. Ele aparece na lista de membros e nas mensagens com o selo BOT, e usa a mesma API que o aplicativo usa: a REST para agir (enviar mensagem, reagir...) e o Gateway (WebSocket) para ouvir o que acontece nos servidores onde está.

Existem dois tipos:

TipoOnde rodaComo é criado
Padrão (Caramelo e Zeca)Dentro do próprio servidor do SambaNasce com o servidor e entra automaticamente em todos os servidores, os novos e os já existentes
De desenvolvedorNo computador ou na hospedagem de quem o criouPela API POST /api/bots (seção 3) e adicionado a cada servidor pelo dono dele

O que um bot pode: ler e enviar mensagens nos canais de texto dos servidores onde foi adicionado, editar e apagar as próprias mensagens, reagir, mostrar "digitando…", enviar imagens e receber os eventos desses servidores.

O que um bot não pode: mandar pedido de amizade, abrir ou receber conversas diretas, criar servidores, entrar em servidor por convite, gerenciar outros bots, nem usar a conta como pessoa (não há login por senha nem pelo Google).

2. Caramelo, o bot padrão#

O Caramelo responde perguntas usando a API do Google Gemini. Em qualquer canal de texto de um servidor:

!c Que dia é hoje?

Ele mostra "digitando…" e responde citando a pergunta. Detalhes:

  • Memória do canal: ele lê as últimas mensagens do canal e entende continuações. Depois de !c Quanto é 5+5?, você pode perguntar !c E o dobro disso?. Só conta a conversa com o Caramelo; o resto do canal ele ignora.
  • Data e hora: ele sabe a data e a hora de Brasília, então responde "que dia é hoje" corretamente.
  • Limites: 5 perguntas por minuto por pessoa e 40 por minuto no servidor inteiro (para proteger seus créditos). Perguntas de até 1500 caracteres. Respostas grandes são divididas em até 3 mensagens.
  • Só responde a pessoas. Mensagens de outros bots são ignoradas, para dois bots não entrarem em loop.
  • !c sozinho mostra um exemplo de uso. Sem a chave do Gemini configurada, ele avisa que ainda não foi configurado.
  • Não dá para removê-lo de um servidor (ele é um bot padrão).

Zeca, o bot de música#

O Zeca toca o áudio de vídeos do YouTube dentro da chamada de voz de quem pedir. Em qualquer canal de texto do servidor, estando em uma call de voz desse mesmo servidor:

!m https://www.youtube.com/watch?v=dQw4w9WgXcQ

O Zeca entra na sua call (se ainda não estiver nela), anuncia a música no canal e toca. Detalhes:

ComandoO que faz
!m <link do YouTube>Toca a música, ou coloca na fila se já tiver uma tocando (até 20). Vale youtube.com/watch, youtu.be, shorts, music.youtube.com. Link de playlist toca só o vídeo do link
!m <nome da música>Procura no YouTube e toca o primeiro resultado (ele diz qual achou). Ex.: !m costa gold 155. Para ter certeza de qual versão toca, use o link
!m pausar / !m continuarPausa e retoma. Esquecer pausado por 10 minutos faz ele sair da call
!m pular (ou !skip)Vota para pular: a música pula quando metade de quem está na call pede
!m anteriorVolta para a música anterior. Se a atual já tocou mais de 5 segundos, recomeça ela
!m pararPara tudo, limpa a fila e sai da call
!m filaMostra o que está tocando e a fila
  • Só quem está numa call do servidor pode pedir (e só essa pessoa e quem está na mesma call manda pular ou parar).
  • Miniplayer: enquanto o Zeca toca na sua call, aparece um player acima de "Conectado" com voltar, pausar e avançar. Clicar nele (ou no Zeca) abre o player grande com a fila e o botão de parar. O chat avisa quem apertou (gus pausou ⏸️). Os botões chamam POST /api/music/<pause|resume|skip|previous|stop>.
  • Uma call por vez. O Zeca toca em um canal de voz de cada vez em todo o Samba. Se alguém de outra call pedir enquanto ele toca, ele avisa que está ocupado.
  • Sai sozinho: quando a fila acaba (30 s depois) ou quando a última pessoa sai da call (8 s depois).
  • Limites: vídeos de até 60 minutos (SAMBACORD_MUSIC_MAX_MINUTES), sem transmissões ao vivo, 6 pedidos por minuto por pessoa.
  • Cada pessoa pode ajustar o volume dele (ou silenciá-lo só para si) pelo perfil, como com qualquer participante.
  • O som é mono, 48 kHz, o suficiente para música de call, não para ouvir com qualidade de estúdio.

Algumas músicas podem não tocar: quando isso acontece, o Zeca avisa no chat.

Perfil dos bots padrão. Ao clicar num bot padrão (lista de membros ou mensagem) o cartão mostra uma descrição e a lista de comandos: passe o mouse sobre um comando (ou toque nele, no celular) para ver o que ele faz. Esse texto vem de src/shared/bots.ts; para um bot padrão novo, descreva-o ali.

3. Criar um bot#

Qualquer conta cria bots em Configurações → Meus bots: é lá que você pega o token, troca a foto, deixa o bot público e copia o link para colocar num servidor. Cada pessoa pode ter até 10 bots.

Pela API, com a sua conta (o token de sessão vem do login, POST /api/auth/login):

curl -X POST $URL/api/bots \
  -H "Authorization: Bearer $TOKEN_DA_SUA_SESSAO" \
  -H "Content-Type: application/json" \
  -d '{"name": "MeuBot", "public": false}'

Resposta:

{ "id": "1234567890123456", "username": "MeuBot", "public": false, "token": "1234567890123456.AbC...xyz" }
  • name: de 2 a 32 caracteres, só letras, números, _, . e - (sem espaços; isso impede que alguém se passe pelos bots padrão, que têm espaço no nome). O nome é único em todo o Samba.
  • public: false (padrão) = só você coloca o bot em servidores seus. true = o dono de qualquer servidor pode adicioná-lo.
  • token: aparece uma única vez. O servidor guarda só um hash, então ninguém consegue recuperá-lo depois. Se perder ou vazar, gere outro (abaixo); o antigo deixa de funcionar na hora.

Outras operações de gerenciamento (sempre com a sessão da pessoa dona, não com o token do bot):

OperaçãoChamada
Listar meus bots (e se posso criar)GET /api/bots → { "canCreate": true, "bots": [{ "id", "username", "public" }] }
Gerar um novo tokenPOST /api/bots/{botId}/token → o bot com o campo token
Apagar o botDELETE /api/bots/{botId}. Remove o bot de todos os servidores e apaga as mensagens dele

4. Autenticação#

Toda chamada do bot usa o token no cabeçalho Authorization, com o prefixo Bot (não Bearer):

Authorization: Bot 1234567890123456.AbC...xyz

No Gateway, o prefixo também vale: {"op":"identify","d":{"token":"Bot 1234567890123456.AbC...xyz"}}.

Trate o token como uma senha. Quem o tem controla o bot. Guarde-o em variável de ambiente, nunca no código nem no Git. Se vazou, gere outro com POST /api/bots/{botId}/token.

Um token válido de bot chamando um endpoint só para pessoas recebe 403; um token inválido ou de um bot apagado, 401.

5. Colocar o bot num servidor#

Abra o link do bot (https://sambachat.app/app?bot=ID) e escolha o servidor. Quem adiciona precisa de Gerenciar servidor (você só pode usar um bot seu ou um bot público). Pela API:

curl -X POST $URL/api/guilds/$ID_DO_SERVIDOR/bots \
  -H "Authorization: Bearer $TOKEN_DO_DONO" -H "Content-Type: application/json" \
  -d '{"botId": "1234567890123456"}'

Os membros recebem o evento GUILD_MEMBER_ADD e o bot recebe GUILD_CREATE com o servidor completo (canais e membros) pelo Gateway. Para remover: DELETE /api/guilds/{guildId}/bots/{botId} (também com Gerenciar servidor; os bots padrão não podem ser removidos).

Para saber em quais servidores o bot está sem usar o Gateway: GET /api/bot/me → { "user": {...}, "guilds": [{ "guild", "channels", "members" }] }.

6. API REST#

Base: a URL do servidor (por exemplo https://sambachat.app). Corpo e resposta em JSON; erros vêm como { "error": "mensagem" } com o código HTTP correspondente. Os IDs são strings numéricas (snowflakes); os mais novos têm IDs maiores, então ordenar por ID ordena por data.

Endpoints que bots podem usar:

Método e caminhoPara quê
GET /api/bot/meQuem sou eu e meus servidores (com canais e membros)
GET /api/channels/{id}/messages?limit=50&before={messageId}Histórico (1 a 100, do mais antigo ao mais novo)
POST /api/channels/{id}/messagesEnviar mensagem
PATCH /api/channels/{id}/messages/{messageId}Editar a própria mensagem: { "content": "..." }
DELETE /api/channels/{id}/messages/{messageId}Apagar a própria mensagem
PUT /api/channels/{id}/messages/{messageId}/reactionReagir: { "emoji": "👍" } (um emoji por pessoa por mensagem)
DELETE /api/channels/{id}/messages/{messageId}/reactionTirar a própria reação
POST /api/channels/{id}/typingMostrar "digitando…" (dura alguns segundos)
POST /api/channels/{id}/attachmentsEnviar uma imagem (corpo binário, cabeçalho X-Filename); use o id devolvido em attachmentIds

Enviar uma mensagem:

curl -X POST $URL/api/channels/$ID_DO_CANAL/messages \
  -H "Authorization: Bot $TOKEN_DO_BOT" -H "Content-Type: application/json" \
  -d '{"content": "Olá, pessoal!", "replyTo": "ID_DE_OUTRA_MENSAGEM"}'

Campos do envio: content (até 2000 caracteres), attachmentIds (até 4), replyTo (opcional, mensagem do mesmo canal). Uma mensagem precisa ter texto ou anexo.

Menções. No texto, uma pessoa é <@idDaPessoa> e um cargo é <@&idDoCargo> (o & diferencia). O servidor só considera (e só avisa) o que o autor pode mencionar: cargos "mencionáveis" por qualquer um, ou qualquer cargo e @todos/@aqui para quem tem a permissão Mencionar todos. Na mensagem recebida, mentions lista as pessoas, mentionRoles os cargos e mentionEveryone indica @todos. O bot só enxerga os canais dos servidores onde está; qualquer outro canal responde 404.

O formato de cada objeto (User, Message, Channel, GuildSnapshot...) está em src/shared/protocol.ts, o mesmo arquivo que o servidor e o aplicativo usam. Contas de bot têm "bot": true no User.

7. Gateway (eventos em tempo real)#

Para reagir ao que acontece (por exemplo, responder quando alguém escreve !ping), o bot mantém uma conexão WebSocket em wss://SERVIDOR/gateway. É a mesma conexão que o aplicativo usa.

bot                                servidor
 │ ── abre o WebSocket ───────────►│
 │ ◄── {"op":"hello","d":{"heartbeatInterval":30000}}
 │ ── {"op":"identify","d":{"token":"Bot ..."}} ──►│
 │ ◄── {"op":"dispatch","t":"READY","s":1,"d":{...}}      (quem sou eu, servidores, canais, membros)
 │ ── {"op":"heartbeat","d":{"seq":1}} ──► (a cada heartbeatInterval)
 │ ◄── {"op":"heartbeat_ack"}
 │ ◄── {"op":"dispatch","t":"MESSAGE_CREATE","s":2,"d":{...mensagem...}}

Regras:

  1. Depois do hello, envie o identify em até 10 segundos, ou a conexão é fechada.
  2. Envie um heartbeat a cada heartbeatInterval (30 s). Se o servidor ficar ~60 s sem receber, ele derruba a conexão. O campo seq é o s do último evento que você recebeu (ou null).
  3. Token inválido → {"op":"invalid_session","d":{"reason":"invalid token"}} e a conexão fecha. Não adianta reconectar com o mesmo token.
  4. Se a conexão cair, reconecte com espera crescente (1 s, 2 s, 4 s... até 30 s) e identifique de novo. Os eventos perdidos durante a queda não são reenviados: leia o histórico pela REST se precisar.
  5. Frames de no máximo 64 KB, só texto (JSON).

Eventos que um bot recebe (campo t dos frames dispatch; os de pessoas, como amizades e DMs, não chegam):

EventoQuandoDados
READYLogo após o identify{ user, guilds: [{ guild, channels, members }], presences, voiceStates, ... }
GUILD_CREATEO bot foi adicionado a um servidorO servidor completo
GUILD_MEMBER_ADD / GUILD_MEMBER_REMOVEAlguém (ou um bot) entrou/saiu do servidor{ guildId, user } / { guildId, userId }
CHANNEL_CREATECanal novoO canal
MESSAGE_CREATEMensagem novaA mensagem completa
MESSAGE_UPDATEMensagem editadaA mensagem completa, já editada
MESSAGE_DELETEMensagem apagada{ id, channelId }
MESSAGE_REACTIONSReações mudaram{ channelId, messageId, reactions } (a lista inteira)
TYPING_STARTAlguém começou a digitar{ channelId, userId, at }
PRESENCE_UPDATEAlguém ficou online/offline{ userId, status }
VOICE_STATE_UPDATEAlguém entrou/saiu/mudou na vozVeja protocol.ts

Os bots recebem os eventos de todos os servidores onde estão; confira o channelId/guildId do evento. (O bot também recebe as próprias mensagens: ignore as suas.)

8. Exemplo completo#

docs/bots/exemplos/ping-bot.mjs é um bot de ~70 linhas, sem dependências (usa o fetch e o WebSocket do Node 22+), que responde !ping com "pong" e !eco texto repetindo o texto. Ele conecta, faz o heartbeat, reconecta sozinho e ignora outros bots.

SAMBACORD_URL=https://sambachat.app \
SAMBACORD_BOT_TOKEN=1234567890123456.AbC...xyz \
node docs/bots/exemplos/ping-bot.mjs

Passo a passo para vê-lo funcionando: crie o bot (seção 3), adicione-o a um servidor seu (seção 5), rode o comando acima e escreva !ping num canal desse servidor.

O núcleo do bot é a função que trata cada mensagem; todo o resto é infraestrutura reaproveitável:

async function onMessage(message, me) {
  if (message.author.bot) return;                 // nunca responda bots
  if (message.content.trim() === '!ping') await reply(message, 'pong 🏓');
}

9. Regras, limites e boas práticas#

LimiteValor
Mensagens por segundo por bot5 (acima disso: 429 slow down)
Tamanho da mensagem2000 caracteres
Anexos por mensagem4 (imagens de até 8 MB)
Bots por pessoa10

Boas práticas:

  • Não responda a bots (message.author.bot) e nunca a si mesmo. Dois bots que respondem um ao outro geram um loop.
  • Use prefixo de comando (!ping, !c...) em vez de responder a toda mensagem.
  • Não faça spam. Quem cuida do servidor pode tirar a permissão de criar bots, e o dono de cada servidor pode remover o seu bot de lá.
  • Mostre typing (POST .../typing) em respostas que demoram, e responda citando a mensagem (replyTo).
  • Não guarde dados pessoais além do necessário; mensagens de um servidor são das pessoas dele.
  • O bot obedece às permissões como qualquer membro: as do @todos, as dos canais e as do cargo dele. Só adicione bots de quem confia.

Permissões do bot#

Em Meus bots, marque o que o bot pede. Quem adiciona vê a lista, pode recusar itens e não pode dar o que não tem. O bot recebe um cargo com o nome dele, no fim da lista: para agir sobre alguém, esse cargo precisa estar acima da pessoa. O cargo só serve a esse bot e some quando ele sai. Pela API: PATCH /api/bots/{botId} com { "permissions": ["kick_members"] }, e POST /api/guilds/{id}/bots com { "botId": "...", "permissions": [...] }.

Com a permissão, o bot também pode usar:

Método e caminhoPara quêPermissão
DELETE /api/guilds/{id}/members/{userId}Expulsarkick_members
PUT e DELETE /api/guilds/{id}/bans/{userId}, GET /api/guilds/{id}/bansBanir, desbanir, listarban_members
PUT /api/guilds/{id}/members/{userId}/timeoutCastigomoderate_members
PUT /api/guilds/{id}/members/{userId}/rolesDar e tirar cargos abaixo do delemanage_roles
PUT e DELETE /api/channels/{id}/pins/{messageId}Fixarmanage_messages

Webhooks#

Para só postar, sem manter um bot conectado: em Configurações do servidor → Webhooks (precisa de Gerenciar webhooks) escolha o bate-papo e copie o endereço. Ele aparece uma vez só; se vazar, gere outro.

curl -X POST "$ENDERECO_DO_WEBHOOK" -H "Content-Type: application/json" -d '{"content": "A live começou"}'
  • content: até 2000 caracteres. embeds também é aceito (até 10) e vira texto: autor, título, link, descrição, campos e rodapé.
  • username e avatar_url são ignorados: a mensagem sai com o nome do webhook, com o selo BOT.
  • @todos e cargos nunca avisam ninguém.
  • Limite: 5 mensagens a cada 2 segundos e 30 por minuto, por endereço (acima disso: 429).

10. O que ainda não existe#

  • Comandos com / (registrados pelo bot, com sugestões) e interações com botões.
  • Conversa direta com bots (por enquanto só nos canais dos servidores).
  • Bots de desenvolvedores na voz: o Zeca participa da chamada por dentro do servidor; bots externos ainda não têm API de voz.