Bot privado de música para Discord, construído com TypeScript estrito, SDD/TDD, Discord.js, Lavalink v4 e SQLite integrado no Node.js.
- Pesquisa e reprodução por nome ou URL, com autocomplete no
/play - Links de Spotify (metadados via LavaSrc, áudio espelhado pelo YouTube)
- Fila, pause, resume, skip, stop, volume, loop, seek, shuffle, remove e clear
- Posicionamento no fim, a seguir ou imediatamente
- Painel público com botões, capa e estado de reprodução, atualizado sozinho
- Playlists partilhadas e definições persistentes em SQLite
- Comandos de voz opcionais (Vosk offline) — ver Reconhecimento de voz
- Allowlist obrigatória de um único servidor Discord
- Timeout de voz, logs estruturados, healthcheck, watchdog e shutdown gracioso
Requisitos: Node.js 24.15+, npm 11 e Docker Compose.
- Copia
.env.examplepara.enve preenche os valores reais.BOT_STATUS_TEXTcontrola a atividade apresentada no perfil do bot. - Arranca apenas o Lavalink, expondo-o localmente:
docker compose -f compose.yml -f compose.dev.yml up -d lavalink
npm install
npm run register:commands
npm run devComandos de qualidade:
npm run format:check
npm run lint
npm run typecheck
npm test
npm run buildO deployment de produção executa o bot e Lavalink numa rede Docker interna, sem publicar as respetivas portas:
docker compose up -d --build
docker compose psA base de dados fica no volume dijay-data. Para criar um backup consistente:
docker compose exec bot npm run db:backupAs instruções completas de instalação, atualização, rollback e restauro estão em
docs/canonical/operations.md.
Opcional e desligado por defeito. Usa o motor open-source Vosk (offline, sem enviar
áudio para lado nenhum) e a captura @discordjs/voice.
O reconhecimento corre num processo separado — o sidecar voice-listener — com uma
segunda identidade Discord ("DiJayMic"). O motivo é estrutural: o Discord dá uma
ligação de voz por bot e por servidor, e essa pertence ao Lavalink para enviar áudio.
Uma segunda identidade tem a sua própria ligação, por isso pode receber áudio em
permanência sem interromper a reprodução.
O que suporta:
- Mãos-livres por wake word — diz
dj <comando>no canal, sem escrever nada: "dj pausa", "dj salta", "dj volume 40", "dj toca daft punk". /listen— captura pontual (push-to-talk) a partir do bot principal, sem sidecar.- Sons por palavra-gatilho — certas palavras disparam um som (soundboard ou clipe
local), sem prefixo
dj. - Saudações ao entrar em canal ou ao entrar um membro no servidor.
- PT/EN em runtime via
/settings voice-language, sem reiniciar.
Toggles independentes, por servidor, todos em /settings:
voice-commands, voice-sounds, voice-language e voice-join-greeting.
Lê isto antes de ligares a escuta mãos-livres — passa a haver transcrição contínua no canal de voz:
- O áudio é processado em memória e nunca escrito em disco, e a transcrição é local (Vosk offline). Nada é enviado para serviços externos.
- Em
LOG_LEVEL=info(o valor por omissão) o conteúdo do que é dito nunca é registado — os logs guardam apenas se um comando correu, não o que foi falado. A transcrição só aparece emLOG_LEVEL=debug, uma escolha explícita do operador para diagnosticar reconhecimento; não a deixes ligada em produção. - Tudo é opt-in:
VOICE_ENABLEDestá afalse, o sidecar exige o seu próprio deployment, e cada funcionalidade tem um toggle por servidor. - A escuta mãos-livres implica que o sidecar transcreve as falas no canal para detetar o wake word. Avisa os membros do servidor antes de a ativares.
- Instala as dependências opcionais (nativas, fora da imagem Alpine de produção):
npm install --include=optional- Descarrega um modelo Vosk para o caminho de
VOICE_STT_MODEL_PATH(ex.:./models/vosk-model-small-pt-0.3). - No
.env, defineVOICE_ENABLED=truee arranca comnpm run dev. - Num canal de voz, usa
/listene diz "salta", "pausa" ou "volume 40".
O /listen no bot principal assume a ligação de voz numa janela curta (é o Lavalink que
a detém durante a reprodução); a escuta mãos-livres do sidecar não tem esse efeito. O
deployment do sidecar e do modelo está em
docs/canonical/operations.md. A infraestrutura de voz é
verificada por npm run typecheck:voice.
Dentro de cada processo, as dependências apontam para dentro:
presentation -> application -> domain
^
|
infrastructure
Discord, Poru/Lavalink, HTTP e SQLite são adaptadores. Os casos de uso e os repositórios da aplicação não dependem desses SDKs.
Em produção correm processos separados, numa rede Docker privada e sem portas publicadas:
bot (DiJay) ──────────────► lavalink ──► yt-cipher
│ ▲ spotify-tokener
│ │ IPC HTTP (segredo partilhado)
│ │
│ └────────── voice-listener (DiJayMic)
└─ SQLite
O voice-listener é uma identidade Discord distinta que só ouve; reencaminha os comandos
reconhecidos para o bot principal por um endpoint HTTP interno autenticado com segredo
partilhado, e é o bot principal que valida a allowlist e o canal de voz. Os sidecars
yt-cipher e spotify-tokener servem o Lavalink — ver
docs/canonical/operations.md.
Especificações e critérios encontram-se em docs/work-items e docs/work-specs.
Respeita os termos e direitos aplicáveis às fontes de áudio e reproduz apenas conteúdo que estejas autorizado a transmitir.
