Skip to main content

Lavalink

Превратите своего бота в профессионального диджея с помощью возможностей экосистемы Lavalink. Этот пакет использует lavalink-client за кулисами, обеспечивая высокопроизводительное и эффективное решение для управления аудиопотоками в Discord. Используя Lavalink, ваш бот получает возможность управлять воспроизведением аудио, очередями и элементами управления в реальном времени с минимальной задержкой, превращая его в полноценную профессиональную музыкальную систему.

Установка

npm i 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

Название событияОписание
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

Название событияОписание
SegmentsLoadedВыдается при загрузке сегментов.
SegmentSkippedВыдается всякий раз, когда определенный сегмент был пропущен.
ChapterStartedВыдается всякий раз, когда начинается воспроизведение определенной главы.
ChaptersLoadedВыдается при загрузке глав.

События плагина LavaLyrics

Название событияОписание
LyricsLineВыдается при получении линии Lyrics.
LyricsFoundВыдается всякий раз, когда найдена лирика.
LyricsNotFoundВыдается всякий раз, когда лирика не найдена.

События NodeManager

Название событияОписание
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,
) {}
}
КлассСвойствоОписание
LavalinkManagerlavalinkManager
NodeManagerlavalinkManager.nodeManagerNode Manager
PlayerManagerlavalinkManager (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 })),
);
}
}