Lavalink
Transforme seu bot em um DJ profissional com o poder do ecossistema Lavalink. Este pacote usa lavalink-client nos bastidores, oferecendo uma solução de alto desempenho e eficiente para o gerenciamento de fluxos de áudio no Discord. Aproveitando o Lavalink, o seu bot ganha a capacidade de gerenciar a reprodução de áudio, filas, e controles em tempo real, com latência mínima, transformando-o em um sistema de música totalmente capaz e profissional.
Instalação
- npm
- Yarn
- pnpm
npm i @necord/lavalink necord discord.js lavalink-client
yarn add @necord/lavalink necord discord.js lavalink-client
pnpm add @necord/lavalink necord discord.js lavalink-client
Utilização
Uma vez que o processo de instalação estiver concluído, nós podemos importar o NecordLavalinkModule
com o seu NecordModule
para a raiz AppModule
:
import { NecordLavalinkModule } from '@necord/lavalink';
import { Module } from '@nestjs/common';
import { Client } from 'discord.js';
import { AppService } from './app.service';
@Module({
imports: [
NecordModule.forRoot({
token: process.env.DISCORD_TOKEN,
intents: [
IntentsBitField.Flags.Guilds,
IntentsBitField.Flags.GuildVoiceStates
],
}),
NecordLavalinkModule.forRoot({
// Pelo menos 1 node é necessário
nodes: [
{
authorization: 'youshallnotpass',
host: 'localhost',
port: 2333,
}
]
})
],
providers: [AppService]
})
export class AppModule {}
Confira mais opções do módulo na documentação oficial lavalink-client.
Ouvintes
Em '@necord/lavalink' o manuseio de eventos funciona o mesmo que os ouvintes Necord padrão com algumas alterações.
- Em vez de decoradores
On
/Once
eContextOf
padrão você pode usarOnLavalinkManager
/OnceLavalinkManager
eLavalinkManagerContextOf
para Eventos do LavalinkManager.OnNodeManager
/OnceNodeManager
eNodeManagerContextOf
para Eventos do NodeManager.
import { Injectable, Logger } from '@nestjs/common';
import { Context } from 'necord';
import { OnLavalinkManager, OnNodeManager, LavalinkManagerContextOf, NodeManagerContextOf } from '@necord/lavalink';
@Injectable()
export class AppService {
private readonly logger = new Logger(AppService.name);
@OnNodeManager('connect')
public onConnect(@Context() [node]: NodeManagerContextOf<'connect'>) {
this.logger.log(`Node: ${node.options.id} Conectado`);
}
@OnLavalinkManager('playerCreate')
public onPlayerCreate(@Context() [player]: LavalinkManagerContextOf<'playerCreate'>) {
this.logger.log(`Player criado em ${player.guildId}`);
}
}
Eventos do LavalinkManager
- Veja na Documentação Oficial lavalink-client.
Nome do evento | Descrição |
---|---|
trackStart | Emitido sempre que uma Faixa é reproduzida. |
trackEnd | Emitido quando uma Faixa terminou de tocar. |
trackStuck | Emitido quando uma Faixa travou. |
trackError | Emitido sempre que ocorre um erro em uma Faixa. |
queueEnd | Emitido quando a faixa terminou, mas não há mais faixas na fila. (trackEnd , NÃO é executado) |
playerCreate | Emitido sempre que um Player é criado. |
playerMove | Emitido sempre que um Player é movido entre os canais de voz. |
playerDisconnect | Emitido sempre que um Player é desconectado de um canal. |
playerSocketClose | Emitido quando um Node-Socket foi fechado para um Player específico. |
playerDestroy | Emitido sempre que um Player é destruído. |
playerUpdate | Emitido sempre que um Player recebe uma atualização do evento playerUpdate do Lavalink. |
playerMuteChange | Emitido sempre que o estado de voz do Player relacionado ao silenciamento é alterado. |
playerDeafChange | Emitido sempre que o estado de voz do Player relacionado ao ensurdecimento é alterado. |
playerSuppressChange | Emitido sempre que o estado de voz do Player relacionado à supressão é alterado. |
playerQueueEmptyStart | Emitido sempre que o manipulador vazio da fila começou (timeout). |
playerQueueEmptyEnd | Emitido quando o manipulador vazio da fila terminou (com sucesso) e destruiu o Player. |
playerQueueEmptyCancel | Emitido sempre que o manipulador vazio da fila foi cancelado (por exemplo, porque uma nova faixa foi adicionada). |
playerVoiceJoin | Emitido sempre que um usuário entra em um canal de voz do Player. |
playerVoiceLeave | Emitido sempre que um usuário sai do canal de voz do Player. |
debug | Emitido por vários erros, e logs dentro de lavalink-client, se managerOptions.advancedOptions.enableDebugEvents é true . |
Eventos do Plugin SponsorBlock
- Estes eventos fazem parte de eventos do LavalinkManager.
- Isso requer o Plugin SponsorBlock adicionado no servidor Lavalink.
Nome do evento | Descrição |
---|---|
SegmentsLoaded | Emitido quando Segmentos são carregados. |
SegmentSkipped | Emitido quando um Segmento específico foi ignorado. |
ChapterStarted | Emitido quando um Capítulo específico começa a ser reproduzido. |
ChaptersLoaded | Emitido quando Capítulos são carregados. |
Eventos do Plugin LavaLyrics
- Estes eventos fazem parte de eventos do LavalinkManager.
- Isso requer o Plugin LavaLyrics adicionado no servidor Lavalink.
Nome do evento | Descrição |
---|---|
LyricsLine | Emitido quando uma linha de Letra é recebida. |
LyricsFound | Emitido quando uma Letra é encontrada. |
LyricsNotFound | Emitido quando uma Letra não é encontrada. |
Eventos do NodeManager
- Veja na Documentação Oficial lavalink-client.
Nome do evento | Descrição |
---|---|
create | Emitido sempre que um Node é criado. |
destroy | Emitido sempre que um Node é destruído. |
connect | Emitido sempre que um Node é conectado. |
reconnecting | Emitido sempre que um Node está se reconectando. |
reconnectinprogress | Emitido sempre que um Node começa a reconectar. (se você tiver um atraso de reconexão, o evento de reconexão será emitido após retryDelay .) Útil para verificar se o sistema de reconexão de Node interno funciona ou não. |
disconnect | Emitido sempre que um Node é desconectado. |
error | Emitido sempre que ocorre um erro em um Node. |
raw | Emite cada evento do Node. |
Provedores
@necord/lavalink
tem snippets para acessar os métodos do cliente lavalink. Você pode injetar os gerenciadores usando construtor.
import { Injectable } from '@nestjs/common';
import { LavalinkManager, NodeManager } from 'lavalink-client';
@Injectable()
export class AppService {
public constructor(
private readonly lavalinkManager: LavalinkManager,
private readonly nodeManager: NodeManager,
) {}
}
Classe (Tipo para ser Injetado) | Propriedade do Gerenciador (será acesso) | Descrição |
---|---|---|
LavalinkManager | lavalinkManager | Lavalink Manager |
NodeManager | lavalinkManager.nodeManager | Node Manager |
PlayerManager | lavalinkManager (player functions) | Gerenciador de Player |
Reproduzir
- Aqui você verá um exemplo de PlayCommand usando as melhores técnicas do Tutorial de Slash Commands.
import { Injectable, UseInterceptors } from '@nestjs/common';
import { NecordLavalinkService, PlayerManager } from 'lavalink-client';
import { Context, Options, SlashCommand, SlashCommandContext } from 'necord';
import { QueryDto } from './query.dto';
import { SourceAutocompleteInterceptor } from 'source.autocomplete';
@Injectable()
export class AppCommands {
public constructor(
private readonly playerManager: PlayerManager,
private readonly lavalinkService: NecordLavalinkService
) {}
@UseInterceptors(SourceAutocompleteInterceptor)
@SlashCommand({
name: 'tocar',
description: 'toca uma faixa',
})
public async onPlay(
@Context() [interaction]: SlashCommandContext,
@Options() { query, source }: QueryDto,
) {
const player =
this.playerManager.get(interaction.guild.id) ??
this.playerManager.create({
...this.lavalinkService.extractInfoForPlayer(interaction),
// configurações opcionais:
selfDeaf: true,
selfMute: false,
volume: 100,
});
await player.connect();
const res = await player.search(
{
query,
source: source ?? 'soundcloud'
},
interaction.user.id,
);
await player.queue.add(res.tracks[0]);
if (!player.playing) await player.play();
// É extremamente recomendado utilizar o evento `trackStart` para realizar esse anuncio
return interaction.reply({
content: `Tocando agora ${res.tracks[0].info.title}`,
});
}
}
import { SearchPlatform } from 'lavalink-client';
import { StringOption } from 'necord';
export class QueryDto {
@StringOption({
name: 'busca',
description: '<nome | url> da faixa requerida'
required: true
})
public readonly query!: string;
@StringOption({
name: 'fonte',
description: 'fonte da faixa',
autocomplete: true,
required: false,
})
public readonly source?: SourcePlatform;
}
import { Injectable } from '@nestjs/common';
import { AutocompleteInteraction } from 'discord.js';
import { DefaultSources } from 'lavalink-client';
import { AutocompleteInterceptor } from 'necord';
@Injectable()
export class SourceAutocompleteInterceptor extends AutocompleteInterceptor {
public transformOptions(interaction: AutocompleteInteraction) {
const focused = interaction.options.getFocused(true);
let choices: string[];
if (focused.name === 'source') {
choices = [DefaultSources.soundcloud] // Note que alguns Sources requerem plugins ou configurações extras para funcionar corretamente
}
return interaction.respond(
choices
.filter((choice) => choice.startsWith(focused.value.toString()))
.map((choice) => ({ name: choice, value: choice })),
);
}
}
Você pode ver um exemplo funcional aqui.