Lavalink
Превратите своего бота в профессионального диджея с помощью возможностей экосистемы Lavalink. Этот пакет использует lavalink-client за кулисами, обеспечивая высокопроизводительное и эффективное решение для управления аудиопотоками в Discord. Используя Lavalink, ваш бот получает возможность управлять воспроизведением аудио, очередями и элементами управления в реальном времени с минимальной задержкой, превращая его в полноценную профессиональную музыкальную систему.
Установка
- npm
- Yarn
- pnpm
- Bun
npm i lavalink-client
yarn add lavalink-client
pnpm add lavalink-client
bun add lavalink-client
Использование
После завершения процесса установки мы можем импортировать NestCordLavalinkModule
вместе с вашим NestCordModule
в корневой AppModule
:
app.module.ts
import { NestCordLavalinkModule } from '@globalart/nestcord';
import { Module } from '@nestjs/common';
import { Client } from 'discord.js';
import { AppService } from './app.service';
@Module({
imports: [
NestCordModule.forRoot({
token: process.env.DISCORD_TOKEN,
intents: [
IntentsBitField.Flags.Guilds,
IntentsBitField.Flags.GuildVoiceStates
],
}),
NestCordLavalinkModule.forRoot({
// At least 1 node is required
nodes: [
{
authorization: 'youshallnotpass',
host: '127.0.0.1',
port: 2333,
}
]
})
],
providers: [AppService]
})
export class AppModule {}
Ознакомьтесь с дополнительными опциями модуля в официальной lavalink-client Documentation.
Слушатели
app.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Context, OnLavalinkManager, OnNodeManager, LavalinkManagerContextOf, NodeManagerContextOf } from '@globalart/nestcord';
@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} Connected`);
}
@OnLavalinkManager('playerCreate')
public onPlayerCreate(@Context() [player]: LavalinkManagerContextOf<'playerCreate'>) {
this.logger.log(`Player created at ${player.guildId}`);
}
}
События LavalinkManager
- Посмотреть в официальной lavalink-client Documentation.
Название события | Описание |
---|---|
trackStart | Выдается при воспроизведении трека. |
trackEnd | Выдается каждый раз, когда трек завершает воспроизведение. |
trackStuck | Выдается, когда трек застревает во время игры. |
trackError | Выдается при ошибке трека. |
queueEnd | Выдается, когда трек закончился, но в очереди больше нет треков. (trackEnd , НЕ выполняется) |
playerCreate | Выдается каждый раз, когда создается игрок. |
playerMove | Издается всякий раз, когда игрок перемещается между голосовыми каналами. |
playerDisconnect | Выдается всякий раз, когда игрок отключается от канала. |
playerSocketClose | Выдается всякий раз, когда Node-Socket был закрыт для определенного игрока. |
playerDestroy | Испускается всякий раз, когда игрок уничтожен. |
playerUpdate | Выдается всякий раз, когда игрок получает обновление от события playerUpdate в Lavalink. |
playerMuteChange | Выдается при изменении состояния голоса игрока, связанного с отключением звука. |
playerDeafChange | Выдается при изменении состояния голоса игрока, связанного с глухотой. |
playerSuppressChange | Выдается при изменении состояния голоса игрока, связанного с подавлением. |
playerQueueEmptyStart | Выдается при запуске обработчика опустошения очереди (таймаут). |
playerQueueEmptyEnd | Выдается всякий раз, когда обработчик опустошения очереди завершает работу (успешно) и уничтожает игрока. |
playerQueueEmptyCancel | Выдается всякий раз, когда обработчик опустошения очереди отменяется (например, из-за добавления нового трека). |
playerVoiceJoin | Выдается всякий раз, когда пользователь присоединяется к голосовому каналу игрока. |
playerVoiceLeave | Выдается всякий раз, когда пользователь покидает голосовой канал игрока. |
debug | Выдается при нескольких ошибках и логах в lavalink-client, если managerOptions.advancedOptions.enableDebugEvents имеет значение true . |
События плагина SponsorBlock
- Это событие является частью События LavalinkManager.
- Для этого требуется SponsorBlock Plugin, добавленный в Lavalink Server.
Название события | Описание |
---|---|
SegmentsLoaded | Выдается при загрузке сегментов. |
SegmentSkipped | Выдается всякий раз, когда определенный сегмент был пропущен. |
ChapterStarted | Выдается всякий раз, когда начинается воспроизведение определенной главы. |
ChaptersLoaded | Выдается при загрузке глав. |
События плагина LavaLyrics
- Это событие является частью События LavalinkManager.
- Для этого требуется LavaLyrics Plugin, добавленный в Lavalink Server.
Название события | Описание |
---|---|
LyricsLine | Выдается при получении линии Lyrics. |
LyricsFound | Выдается всякий раз, когда найдена лирика. |
LyricsNotFound | Выдается всякий раз, когда лирика не найдена. |
События NodeManager
- Посмотреть в официальной lavalink-client Documentation.
Название события | Описание |
---|---|
create | Выдается при создании узла. |
destroy | Выдается при уничтожении узла. |
connect | Выдается при подключении узла. |
reconnecting | Выдается при повторном подключении узла. |
reconnectinprogress | Выдается всякий раз, когда узел начинает переподключаться. (если у вас есть задержка на повторное подключение, событие повторного подключения будет выдано после retryDelay ). Полезно проверить, работает ли система переподключения внутренних узлов. |
disconnect | Выдается всякий раз, когда узел отключается. |
error | Выдается при ошибке узла. |
raw | Выдает каждое отдельное событие узла. |
Провайдеры
app.service.ts
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,
) {}
}
Класс | Свойство | Описание |
---|---|---|
LavalinkManager | lavalinkManager | |
NodeManager | lavalinkManager.nodeManager | Node Manager |
PlayerManager | lavalinkManager (player functions) | Player Manager |
Play Tracks
app.commands.ts
import { Injectable, UseInterceptors } from '@nestjs/common';
import { NestCordLavalinkService, PlayerManager } from 'lavalink-client';
import { Context, Options, SlashCommand, SlashCommandContext } from '@globalart/nestcord';
import { QueryDto } from './query.dto';
import { SourceAutocompleteInterceptor } from 'source.autocomplete';
@Injectable()
export class AppCommands {
public constructor(
private readonly playerManager: PlayerManager,
private readonly lavalinkService: NestCordLavalinkService
) {}
@UseInterceptors(SourceAutocompleteInterceptor)
@SlashCommand({
name: 'play',
description: 'play a track',
})
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),
// optional configurations:
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();
// It's extremely recommended to use `trackStart` event for this announcement
return interaction.reply({
content: `Now playing ${res.tracks[0].info.title}`,
});
}
}
query.dto.ts
import { SearchPlatform } from 'lavalink-client';
import { StringOption } from '@globalart/nestcord';
export class QueryDto {
@StringOption({
name: 'query',
description: '<name | url> of the requested track'
required: true
})
public readonly query!: string;
@StringOption({
name: 'source',
description: 'source of the track',
autocomplete: true,
required: false,
})
public readonly source?: SourcePlatform;
}
source.autocomplete.ts
import { Injectable } from '@nestjs/common';
import { AutocompleteInteraction } from 'discord.js';
import { DefaultSources } from 'lavalink-client';
import { AutocompleteInterceptor } from '@globalart/nestcord';
@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 that some Sources needs extra plugins/configuration to property work
}
return interaction.respond(
choices
.filter((choice) => choice.startsWith(focused.value.toString()))
.map((choice) => ({ name: choice, value: choice })),
);
}
}