Pular para o conteúdo principal

Instalação

Requisitos​

O quêVersãoPor quê
Node.js24 ou maisO SDK usa fetch e WebSocket globais, e o pacote de voz depende de APIs de stream recentes
TypeScript5.6 ou maisDecorators com metadados e satisfies
pnpm10 (ou npm 10)O monorepo do nescord usa workspaces do pnpm; para consumir o SDK, qualquer um serve
ffmpeg6 ou maisSó para voz. Transcodifica qualquer entrada para Opus
yt-dlprecenteSó se o bot for tocar de sites de vídeo

Confira o que já tem:

node --version # v24.x
pnpm --version # 10.x
ffmpeg -version # só se for usar voz
yt-dlp --version # só se for baixar de sites de vídeo
ffmpeg e yt-dlp não vêm no pacote

São binários do sistema, não dependências npm. O concord.js/voice os chama por processo filho e falha com mensagem clara se não achar.

Instalando​

Os pacotes ainda não estão no npm

Os nomes concord.js e nescord já estão ocupados no registro público por pacotes de terceiros, sem relação nenhuma com este projeto: pnpm add concord.js nescord baixa o pacote errado, e o import { Client } from 'concord.js' vira erro de export inexistente.

Enquanto não publicamos (provavelmente sob escopo, @concord/concord.js e @concord/nescord), o caminho é trabalhar dentro do monorepo: pnpm install na raiz e pnpm --filter @nescord/music dev para ver o bot de exemplo de pé. Os comandos abaixo valem para quando a publicação sair.

pnpm add concord.js nescord @nestjs/common @nestjs/core reflect-metadata
npm install concord.js nescord @nestjs/common @nestjs/core reflect-metadata

Se o bot é só de texto e você não quer o NestJS, o concord.js roda sozinho:

pnpm add concord.js

Só o pacote de voz​

A parte de voz fica num subcaminho próprio, concord.js/voice, justamente para não pesar num bot de texto: ela carrega a stack WebRTC e fala com o ffmpeg.

import { createAudioResource } from 'concord.js/voice';

Nada mais a instalar — o subcaminho faz parte do mesmo pacote. O que precisa existir é o ffmpeg no PATH — ou, se ele estiver em outro lugar, a variável CONCORD_FFMPEG apontando para o binário:

CONCORD_FFMPEG=/opt/ffmpeg/bin/ffmpeg

É essa a variável que o concord.js lê (voice/AudioResource.ts); não existe outra.

TypeScript​

O seu app tem de ser ESM

O concord.js publica só ESM: o exports dele não tem condição require, então um app CommonJS estoura ERR_PACKAGE_PATH_NOT_EXPORTED ao importá-lo — inclusive no Node 24, porque a resolução ainda passa pela condição require. Como npm init cria um pacote CommonJS por padrão, declare:

package.json
{
"type": "module"
}

E, com module: nodenext + ESM, import relativo leva extensão: ./app.module.js, não ./app.module.

Os decorators do nescord (e do NestJS) precisam de duas opções ligadas. Sem elas, @On e @SlashCommand compilam mas não registram nada:

tsconfig.json
{
"compilerOptions": {
"target": "ES2023",
"module": "nodenext",
"moduleResolution": "nodenext",
"lib": ["ES2023"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
}
}

E reflect-metadata carregado uma vez, antes de tudo:

src/main.ts
import 'reflect-metadata';
Por que experimentalDecorators, e não os decorators padrão

O NestJS ainda usa os decorators legados com emitDecoratorMetadata — é assim que ele descobre os tipos dos parâmetros para injetar dependência. O nescord segue o mesmo modelo para não exigir dois regimes de decorator no mesmo arquivo.

Variáveis de ambiente​

O SDK lê três variáveis, e nenhuma delas tem valor padrão secreto:

VariávelPara quêPadrão
CONCORD_TOKENToken de sessão da conta do botnenhum — sem ele, login() falha
CONCORD_URLBase HTTP do servidorhttps://app.concord.win
CONCORD_INSTALL_TOKENCredencial de instalação, para rest.botsnenhum — opcional
.env — e no .gitignore
CONCORD_TOKEN=cole-aqui-o-token-de-sessao
CONCORD_URL=http://localhost:3000
Token no repositório, nunca

O token de sessão dá acesso total à conta do bot por 30 dias. Ele vive em variável de ambiente, no cofre do seu provedor de deploy, e no .env que está no .gitignore. Se vazar, faça logout da conta — isso invalida a sessão na hora.

Conferindo que funcionou​

Um arquivo, sem NestJS, só para ver o handshake acontecer:

smoke.ts
import { Client } from 'concord.js';

const client = new Client({ token: process.env.CONCORD_TOKEN });

// Sem um listener de `error` o processo cai na primeira falha de socket.
client.on('error', (err) => console.error('erro:', err.message));
client.on('ready', (c) => {
console.log(`conectado como @${c.user!.username}`);
console.log(`servidores: ${c.servers.size}, canais: ${c.channels.size}`);
console.log(`SFU ligado no servidor: ${c.sfuEnabled}`);
void c.destroy();
});

await client.login();
node --experimental-strip-types smoke.ts

Saiu o nome do bot? O token está certo e o gateway está aberto. Agora vá para Primeiro bot.

Deu Sessão inválida, entre de novo? O token não é um token de sessão válido — veja como obter um em Primeiro bot.