KassinãoDocumentação
PT EN
GitHub

Coloque o Kassinão no seu Discord.

Instale e opere o bot de Discord que grava calls, transcreve cada pessoa e gera atas, decisões e tarefas.

Início rápido

O caminho mínimo entre um servidor novo e a primeira call gravada.

  1. Crie o appCopie Application ID, token do bot e Client Secret.
  2. Prepare o servidorClone o projeto e preencha as quatro variáveis obrigatórias.
  3. Suba o DockerAcompanhe os logs até aparecer que o Kassinão está online.
  4. Grave uma callEntre num canal de voz e use /gravar.
Terminal
git clone https://github.com/resolvicomai/kassinao.git
cd kassinao
cp .env.example .env && chmod 600 .env
mkdir -p recordings && chmod 700 recordings
.env
DISCORD_TOKEN=cole_o_token_do_bot
APPLICATION_ID=cole_o_id_da_aplicacao
DISCORD_CLIENT_SECRET=cole_o_client_secret
APP_URL=https://kassinao.seu-dominio.com
Terminal
docker compose up -d --build
docker compose logs -f
O bot já grava sem IA.

Transcrição e ata são opcionais. Configure um provider depois de validar a gravação, o login e os downloads.

Requisitos

O Kassinão é um bot persistente de voz. Ele precisa ficar conectado ao Discord.

Obrigatório para operar

  • Servidor ou computador com Docker e Docker Compose.
  • Aplicação criada no Discord Developer Portal. Nenhuma privileged intent é necessária.
  • URL HTTPS pública para login e downloads em produção.
  • Volume persistente para o diretório recordings.

Opcional

  • Cloudflare Tunnel para publicar HTTPS sem abrir portas.
  • Chave de um provider de transcrição e de ata.
  • Node.js 20+ no computador que usar o conector MCP.
  • Node.js 22+ apenas para desenvolver fora do Docker.
Não use serverless.

Vercel e Netlify não mantêm o gateway de voz WebSocket ativo. Use Docker numa máquina persistente.

Instalação com Docker

Configure primeiro o Discord, depois a URL pública e só então suba o container.

Crie a aplicação

No Discord Developer Portal, crie uma aplicação. Copie o Application ID, gere o token do bot e copie o Client Secret em OAuth2.

Cadastre exatamente APP_URL/auth/callback em OAuth2 Redirects.

Convide o bot

Use os scopes bot e applications.commands. O número de permissões usado pelo projeto é 68242432.

URL de convite
https://discord.com/oauth2/authorize?client_id=SEU_APP_ID&scope=bot%20applications.commands&permissions=68242432

Permissões: Ver Canais, Enviar Mensagens, Inserir Links, Ler Histórico, Conectar e Alterar Apelido.

Publique HTTPS

Com Cloudflare Tunnel, aponte o hostname público para kassinao:8080. Defina TUNNEL_TOKEN e COMPOSE_PROFILES=tunnel.

IP direto serve apenas para teste. O OAuth do Discord aceita HTTP somente em localhost.

Suba e valide

Suba o compose, acompanhe o log e abra /health. As gravações ficam no volume ./recordings.

Terminal
docker compose up -d --build
docker compose logs -f

Variáveis e configuração

Comece pelo bloco obrigatório. Abra os grupos seguintes apenas quando precisar da função.

Discord e acesso webIdentidade do bot, OAuth, URL pública e idioma.
DISCORD_TOKEN
Padrão: obrigatória Token do bot criado no Discord Developer Portal.
APPLICATION_ID
Padrão: obrigatória ID da aplicação usado para registrar os comandos.
DISCORD_CLIENT_SECRET
Padrão: obrigatória Client Secret do OAuth usado no login das páginas privadas.
APP_URL
Padrão: BASE_URL ou http://localhost:8080 Origem privada do app, OAuth, gravações e downloads. Cadastre APP_URL/auth/callback como redirect do Discord.
BASE_URL
Padrão: vazio Alias retrocompatível de APP_URL. Instalações novas devem preferir APP_URL.
PUBLIC_URL
Padrão: APP_URL Origem da landing e da demo pública. Deixe igual ao app quando usar um único domínio.
DOCS_URL
Padrão: PUBLIC_URL Origem da documentação. Quando separada, português fica em / e inglês em /en.
MCP_URL
Padrão: APP_URL Origem pública da API MCP. O conector usa este valor em KASSINAO_URL.
GUILD_ID
Padrão: vazio Opcional. Limita o registro imediato de comandos a um servidor.
PORT
Padrão: 8080 Porta HTTP interna do servidor Express.
TUNNEL_TOKEN
Padrão: vazio Token do Cloudflare Tunnel. Ative também o profile tunnel.
COMPOSE_PROFILES
Padrão: vazio Use tunnel para subir o Cloudflare Tunnel junto com o bot.
COOKIE_SECRET
Padrão: gerado e persistido Segredo de sessão com no mínimo 32 bytes. Se vazio, o bot cria um no volume.
REPO_PUBLIC
Padrão: false Libera links públicos para o repositório na interface.
DEFAULT_LOCALE
Padrão: en Idioma de fallback quando o Discord não fornece o locale.
TZ
Padrão: America/Sao_Paulo Fuso de fallback para datas. Na web, o navegador tem prioridade.
Gravação, retenção e discoArquivos, duração, qualidade, expiração e guardas operacionais.
RECORDINGS_DIR
Padrão: ./recordings Diretório persistente das gravações. No Docker, usa /app/recordings.
RETENTION_DAYS
Padrão: 7 Dias até o áudio expirar. Zero desliga toda expiração automática.
TEXT_RETENTION_DAYS
Padrão: 90 Retenção de transcrição, ata e notas. Nunca fica menor que a retenção do áudio.
MAX_RECORDING_HOURS
Padrão: 6 Duração máxima de cada gravação.
MANUAL_RECORD_USER_COOLDOWN_SEC
Padrão: 60 Cooldown global por membro comum entre inícios manuais. Admins ignoram.
MANUAL_RECORD_GUILD_COOLDOWN_SEC
Padrão: 15 Cooldown do servidor entre inícios manuais de membros comuns.
MANUAL_RECORD_GUILD_STARTS_PER_24H
Padrão: 48 Teto móvel de 24 horas para inícios manuais por servidor. Admins não consomem a quota.
MP3_BITRATE
Padrão: 192k Bitrate dos MP3 individuais e do mix.
MIN_FREE_MB_START
Padrão: 500 Espaço livre mínimo para iniciar uma gravação.
MIN_FREE_MB_ABORT
Padrão: 150 Espaço livre que força uma parada segura durante a gravação.
DISK_ALERT_PCT
Padrão: 85 Percentual de uso que envia alerta por DM aos OWNER_IDS.
Transcrição e ataProvider de voz, vocabulário, modelo local e geração da ata.
TRANSCRIBE_PROVIDER
Padrão: none none, assemblyai, openai, groq, gemini ou command.
TRANSCRIBE_MODEL
Padrão: padrão do provider Sobrescreve o modelo do provider escolhido.
TRANSCRIBE_LANGUAGE
Padrão: pt Idioma falado nas calls.
TRANSCRIBE_PROMPT
Padrão: contexto neutro pt-BR Contexto de nomes, vocabulário e estilo para o ASR.
TRANSCRIBE_KEYTERMS
Padrão: vazio Vocabulário fixo separado por vírgulas para AssemblyAI Universal-3.5-Pro.
ASSEMBLYAI_API_KEY / OPENAI_API_KEY / GROQ_API_KEY / GEMINI_API_KEY
Padrão: vazio Defina apenas as chaves dos providers usados.
TRANSCRIBE_COMMAND
Padrão: vazio Comando local com os placeholders {input} e {output}.
TRANSCRIBE_TIMEOUT_FACTOR
Padrão: 5 Multiplicador de timeout do transcritor local.
WHISPER_MODEL
Padrão: small Modelo usado pelo wrapper local faster-whisper.
MINUTES_ENABLED
Padrão: auto auto, true ou false. Auto liga com uma chave OpenRouter ou Groq.
MINUTES_PROVIDER / MINUTES_MODEL
Padrão: openrouter ou groq Provider e modelo usados para resumo, decisões e tarefas.
OPENROUTER_API_KEY
Padrão: vazio Chave para a ata via OpenRouter.
MINUTES_MAX_TOKENS
Padrão: 8192 Teto de tokens de saída da ata.
MINUTES_WEBHOOK_URL
Padrão: vazio Webhook definido só por env. Recebe minutes.ready quando a ata fica pronta.
Conector MCPAtivação deliberada, allowlist e validade dos tokens.
MCP_SECRET
Padrão: desligado Segredo dedicado com no mínimo 32 bytes. Ativa a API e o conector.
OWNER_IDS
Padrão: vazio IDs do Discord autorizados a usar /mcp e receber alertas de disco.
MCP_ACCESS_TTL_MIN
Padrão: 15 Validade do token curto de acesso, em minutos.
MCP_REFRESH_TTL_DAYS
Padrão: 30 Validade do refresh token rotativo, em dias.
Segredos não entram no Git.

O .env já é ignorado. Gere COOKIE_SECRET e MCP_SECRET com openssl rand -hex 32 e nunca use o mesmo valor nos dois.

Comandos

O Discord mostra automaticamente o nome em português ou inglês conforme o idioma do cliente.

/gravar [canal]

Entra no seu canal de voz e começa uma gravação com uma faixa separada por pessoa. Admins podem indicar outro canal visível.

Acesso: Qualquer membro no próprio canal
/parar

Encerra a gravação, libera o link privado e inicia a fila de transcrição e ata.

Acesso: Iniciador, quem esteve na call ou admin atual
/nota <texto>

Salva uma nota no segundo atual. O painel também oferece ações para marcar um momento ou escrever uma nota.

Acesso: Iniciador, quem esteve na call ou admin atual
/status

Mostra o estado da gravação em andamento que você tem permissão para acompanhar.

Acesso: Membro do servidor com acesso
/gravacoes

Lista gravações acessíveis e abre a central privada com busca em transcrições, atas e notas.

Acesso: Resultados filtrados por acesso
/perguntar <pergunta> [dias]

Busca por tema, pessoa, data da call ou prazo e responde só para você com evidências e links para o segundo exato.

Acesso: Somente reuniões que você pode abrir
/autorecord ligar|desligar|ver

Configura a gravação automática por canal e o mínimo de pessoas para iniciar.

Acesso: Gerenciar Servidor
/config ata-canal|ver

Escolhe o canal do aviso genérico de processamento ou consulta a configuração atual. Detalhes e links ficam nas DMs autorizadas.

Acesso: Gerenciar Servidor
/mcp novo|revogar-tudo

Gera um código de conexão ou revoga conectores. Só aparece quando o MCP está habilitado. Membros comuns usam a página de conexão.

Acesso: Somente IDs em OWNER_IDS
/ajuda

Abre o guia interativo do bot com gravação, downloads, perguntas, privacidade e auto-record.

Acesso: Qualquer membro
/sobre

Mostra autor, licença AGPL-3.0 e código-fonte.

Acesso: Qualquer membro
Use comandos dentro do servidor.

É ali que o bot consegue validar servidor, canal e permissões. As respostas de /perguntar são efêmeras e só aparecem para quem perguntou.

Fluxo de gravação

Do comando no canal de voz até a central privada.

  1. 1

    O aviso aparece antes do áudio

    O bot entra no canal, publica o painel e usa o prefixo [GRAVANDO] no apelido. A captura só começa depois do aviso.

  2. 2

    Cada pessoa ganha uma faixa

    Os pacotes Opus são decodificados para PCM e um ffmpeg por pessoa grava FLAC contínuo e sincronizado. Não há diarização para adivinhar o falante.

  3. 3

    Notas preservam o segundo exato

    Use /nota ou os botões do painel. As marcações entram na página, na transcrição e nos labels do Audacity.

  4. 4

    A gravação encerra com segurança

    Use /parar. O bot também encerra quando o canal esvazia, passa do limite ou é desconectado. Silêncio prolongado gera aviso, não parada.

  5. 5

    O áudio fica disponível primeiro

    O mix pré-processado alimenta o player imediatamente. MP3, FLAC, mix e projeto do Audacity são gerados sob demanda e ficam em cache.

  6. 6

    Transcrição e ata entram na fila

    O VAD normalmente envia só os trechos com fala; se a detecção falhar, usa blocos fixos para não perder a call. Depois, a ata gera resumo, decisões e tarefas.

Transcrição e IA

A gravação funciona sem IA. Quando ativada, a IA entra depois da call e nunca decide quem falou.

AssemblyAIUniversal-3.5-Pro, keyterms e fallback automático para Groq quando configurado.
GroqWhisper Large V3. Útil para começar com free tier. Ative Zero Data Retention.
OpenAIWhisper com segmentos e timestamps.
GeminiÁudio via Gemini. Revise a política de retenção do tier usado.
Comando localfaster-whisper, whisper.cpp ou outro comando que gere o JSON esperado.

Transcrição totalmente local

Construa a imagem com LOCAL_TRANSCRIBE=1 e use o wrapper incluído. O comando precisa escrever em {output} um array JSON com start, end e text.

.env
TRANSCRIBE_PROVIDER=command
TRANSCRIBE_COMMAND=python3 ./scripts/transcribe-local.py {input} {output}
WHISPER_MODEL=small
Terminal
docker compose build --build-arg LOCAL_TRANSCRIBE=1
docker compose up -d

Privacidade e permissões

Voz é dado pessoal. O controle de acesso é aplicado no servidor em toda abertura, busca e conexão.

Consentimento visível

O bot entra no canal, publica um painel e muda o apelido para [GRAVANDO] antes de capturar áudio.

Acesso revalidado

A página exige OAuth do Discord e participação atual no servidor. Sair do servidor encerra o acesso.

Histórico da gravação

Em qualquer canal, só abre para quem estava na call, mesmo mutado, quem iniciou ou um admin atual. Receber permissão depois não libera o passado.

Falha para o lado seguro

Se o Discord não consegue confirmar o acesso, a página nega. A API do MCP devolve erro temporário quando a checagem está indisponível.

Retenção em camadas

O áudio pode expirar antes da transcrição, ata e notas. O operador também pode apagar somente o áudio ou apagar tudo.

Segredos isolados

Cookies e MCP usam segredos diferentes. Girar MCP_SECRET revoga todos os conectores sem invalidar a regra de acesso.

Se uma credencial vazar, gire imediatamente.

Troque DISCORD_TOKEN, DISCORD_CLIENT_SECRET, TUNNEL_TOKEN e chaves de API. Problemas de segurança devem ser reportados em privado.

Conector MCP

Leve a memória das reuniões para Claude, Cursor ou outro cliente MCP sem copiar o acervo para a máquina.

MCP é opt-in e somente leitura.

Ele não entrega áudio, não apaga gravações e não amplia permissões. Cada chamada passa pela mesma checagem da web.

Ative no servidor

Defina um MCP_SECRET dedicado com no mínimo 32 bytes e reinicie. A página /app/conectar-ia e a API só existem quando esse segredo está presente.

Terminal
openssl rand -hex 32

Conecte cada pessoa

Abra /app/conectar-ia, entre com Discord e gere uma conexão nomeada. Copie o código descartável e execute o comando exibido: ele pede o código com a entrada oculta, salva o token em um arquivo local protegido (0600 no macOS/Linux; ACL herdada do perfil no Windows) e imprime uma configuração sem segredo. O computador precisa de Node.js 20 ou superior.

JSON
{
  "mcpServers": {
    "kassinao": {
      "command": "npx",
      "args": ["-y", "kassinao-mcp@1.0.5"],
      "env": {
        "KASSINAO_URL": "https://SEU-KASSINAO",
        "KASSINAO_PROFILE": "PERFIL_IMPRESSO_PELO_COMANDO"
      }
    }
  }
}

O mesmo fluxo funciona sem navegador: um ID presente em OWNER_IDS gera um código com /mcp novo e faz a troca pelo terminal.

Terminal
npx -y kassinao-mcp@1.0.5 exchange --stdin --url https://SEU-KASSINAO

Ferramentas disponíveis

list_meetings

Lista reuniões num período.

pending_actions

Cruza pendências e prazos.

search_meetings

Busca em transcrições, atas e notas.

who_said

Encontra o que uma pessoa disse sobre um tema.

get_meeting

Abre o dossiê completo de uma reunião.

Tokens e revogação

O refresh token fica em ~/.config/kassinao-mcp, protegido por modo 0600 no macOS/Linux e pelas ACLs herdadas do perfil no Windows, e gira a cada renovação. Revogue uma conexão na página, use /mcp revogar-tudo ou gire MCP_SECRET para revogar todos.

Troubleshooting

Comece sempre por docker compose logs -f. O bot valida a configuração no boot e explica as variáveis inválidas.

O container não fica online

Confirme DISCORD_TOKEN, APPLICATION_ID e DISCORD_CLIENT_SECRET. Verifique também se APP_URL é uma origem HTTP ou HTTPS sem caminho, query ou hash.

docker compose logs --tail=200 kassinao

Os comandos não aparecem

Confirme que o convite incluiu applications.commands e que o bot já está no servidor. Reinicie o bot. Se GUILD_ID estiver definido, os comandos só são registrados naquele servidor.

O login do Discord volta com erro

Cadastre exatamente APP_URL/auth/callback em OAuth2 Redirects. Em produção, APP_URL precisa usar HTTPS. Depois de mudar a origem, atualize o redirect e reinicie.

O Cloudflare Tunnel não sobe

O serviço fica num profile. Defina COMPOSE_PROFILES=tunnel ou execute docker compose --profile tunnel up -d. No painel da Cloudflare, o destino interno é kassinao:8080.

A gravação existe, mas não há transcrição

Confirme que TRANSCRIBE_PROVIDER não está como none e que a chave do provider existe. Consulte o log da fila. A gravação e os downloads continuam válidos mesmo sem IA.

A transcrição saiu, mas a ata não

Com MINUTES_ENABLED=auto, a ata só liga quando OPENROUTER_API_KEY ou GROQ_API_KEY está definida. Confirme também MINUTES_PROVIDER e o limite do provider em calls longas.

A página de MCP retorna 404

Isso é esperado quando MCP_SECRET está vazio. Gere um segredo dedicado com 32 bytes ou mais, diferente de COOKIE_SECRET, e reinicie o bot.

Uma pessoa recebeu acesso negado

Confirme que ela continua no servidor. Em qualquer canal, ela precisa ter estado na call, ter iniciado a gravação ou ser admin atual. Ganhar acesso ao canal depois não libera o histórico.

O bot recusou ou encerrou por espaço

Libere espaço no host ou ajuste MIN_FREE_MB_START e MIN_FREE_MB_ABORT com cuidado. O limite de abortar não pode ser maior que o limite de iniciar.

O áudio sumiu, mas a ata continua

É o comportamento da retenção em camadas. RETENTION_DAYS controla o áudio e TEXT_RETENTION_DAYS controla transcrição, ata e notas. Use zero para não expirar automaticamente.