Skip to content

Commit

Permalink
feat(music): initilize module with @necord/lavalink
Browse files Browse the repository at this point in the history
Commands
- play
- join
- leave
- stop
- pause
- resume
  • Loading branch information
NedcloarBR committed Oct 4, 2024
1 parent cfaa648 commit 1aee1ea
Show file tree
Hide file tree
Showing 38 changed files with 1,539 additions and 21 deletions.
4 changes: 2 additions & 2 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
version: "3.8"

include:
- path: ./Database/docker-compose.yml
env_file: .env
- path : ./Lavalink/docker-compose.yml
env_file: .env

services:
bot:
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.8"

services:
bot:
container_name: ndbot
Expand Down
4 changes: 2 additions & 2 deletions nest-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"deleteOutDir": true,
"assets": [
{
"include": "**/Languages/*.json",
"include": "**/Languages/**/*.json",
"watchAssets": true,
"outDir": "dist/src/common/Languages/"
"outDir": "dist/src/common/Languages"
},
{
"include": "**/assets/**/*",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"postinstall": "prisma generate && husky || true"
},
"dependencies": {
"@necord/lavalink": "^1.1.0",
"@necord/localization": "^4.0.0",
"@necord/pagination": "^2.0.0",
"@nestjs/common": "^10.4.1",
Expand All @@ -54,6 +55,7 @@
"discord-api-types": "^0.37.100",
"discord.js": "^14.16.2",
"ioredis": "^5.4.1",
"lavalink-client": "^2.4.0",
"moment": "^2.30.1",
"necord": "^6.8.6",
"nestjs-prisma": "^0.23.0",
Expand Down
2 changes: 1 addition & 1 deletion src/common/Languages
Submodule Languages updated 148 files
43 changes: 43 additions & 0 deletions src/modules/config/NecordConfig.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NecordLavalinkModuleOptions } from "@necord/lavalink";
import {
GuildResolver,
NecordLocalizationOptions,
Expand All @@ -12,6 +13,7 @@ import {
Options,
Partials,
} from "discord.js";
import { SourceLinksRegexes } from "lavalink-client";
import type { NecordModuleOptions } from "necord";
import { JSONLocaleLoader } from "./JSONLocale.loader";
import type { Config } from "./types";
Expand Down Expand Up @@ -107,4 +109,45 @@ export class NecordConfigService {
resolvers: GuildResolver,
};
}

public async createNecordLavalinkOptions(): Promise<NecordLavalinkModuleOptions> {
return {
nodes: [
{
regions: ["us-east", "us-central", "us-south", "us-west", "brazil"],
authorization: "youshallnotpass",
host: "localhost",
port: 2333,
id: "ndlavalink",
retryAmount: 4,
retryDelay: 4000,
},
],
autoSkip: true,
playerOptions: {
applyVolumeAsFilter: true,
clientBasedPositionUpdateInterval: 100,
defaultSearchPlatform: "ytmsearch",
volumeDecrementer:
this.config.getOrThrow<Config["Music"]>("Music").Volumes.Lavalink,
useUnresolvedData: true,
onDisconnect: {
autoReconnect: false,
destroyPlayer: false,
},
},
queueOptions: {
maxPreviousTracks: 10,
},
linksWhitelist: [
SourceLinksRegexes.YoutubeMusicRegex,
SourceLinksRegexes.YoutubeRegex,
SourceLinksRegexes.AllSpotifyRegex,
],
advancedOptions: {
enableDebugEvents:
this.config.getOrThrow<Config["Debug"]>("Debug").Lavalink,
},
};
}
}
5 changes: 5 additions & 0 deletions src/modules/core/NDB.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Modules from "@/modules";
import { NecordLavalinkModule } from "@necord/lavalink";
import { NecordLocalizationModule } from "@necord/localization";
import { NecordPaginationModule } from "@necord/pagination";
import { Module } from "@nestjs/common";
Expand All @@ -20,6 +21,10 @@ import { NDBServiceProvider } from "./provider/NDBService.provider";
inject: [ConfigService],
useClass: NecordConfigService,
}),
NecordLavalinkModule.forRootAsync({
inject: [ConfigService],
useClass: NecordConfigService,
}),
ConfigModule.forRoot({
isGlobal: true,
cache: true,
Expand Down
8 changes: 3 additions & 5 deletions src/modules/events/Events.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Module, type OnApplicationBootstrap } from "@nestjs/common";
import { EventEmitter2, EventEmitterModule } from "@nestjs/event-emitter";
import { REST } from "discord.js";
import { GatewayEvents } from "./Gateway";
import { GuildEvents } from "./Guild";
import { NDCEvents } from "./NDC";
import { ThreadEvents } from "./Thread";
import * as EventsMap from "./index";
const Events = Object.values(EventsMap);

@Module({
imports: [
Expand All @@ -14,7 +12,7 @@ import { ThreadEvents } from "./Thread";
global: true,
}),
],
providers: [GatewayEvents, GuildEvents, ThreadEvents, NDCEvents],
providers: [...Events],
})
export class EventsModule implements OnApplicationBootstrap {
public constructor(
Expand Down
64 changes: 64 additions & 0 deletions src/modules/events/VoiceStateUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Injectable } from "@nestjs/common";
import {
ChannelType,
Client,
GuildMember,
PermissionResolvable,
} from "discord.js";
import { Context, ContextOf, On } from "necord";

@Injectable()
export class VoiceStateUpdateEvents {
public constructor(private readonly client: Client) {}

@On("voiceChannelJoin")
public async onVoiceChannelJoin(
@Context() [member, channel]: ContextOf<"voiceChannelJoin">,
) {
if (
this.checkMember(member) &&
this.checkPermission(member, "DeafenMembers")
) {
await member.voice.setDeaf(true);
}

if (
channel.type === ChannelType.GuildStageVoice &&
this.checkPermission(member, "Speak")
) {
await member.voice.setSuppressed(false);
}
}

// TODO: Auto leave if everyone leaves or is deafened
@On("voiceChannelLeave")
public async onVoiceChannelLeave(
@Context() [member, channel]: ContextOf<"voiceChannelLeave">,
) {
if (this.checkMember(member) && !channel) {
}
}

// TODO: Anti undeafen
@On("voiceChannelUndeaf")
public async onVoiceChannelUndeaf(
@Context() [member, type]: ContextOf<"voiceChannelUndeaf">,
) {
console.log("undeaf");
if (this.checkMember(member)) {
console.log("undeaf bot");
member.voice.setDeaf(true);
}
}

private checkMember(member: GuildMember): boolean {
return member.id === this.client.user?.id;
}

private checkPermission(
member: GuildMember,
permission: PermissionResolvable,
): boolean {
return member.permissions.has(permission);
}
}
5 changes: 5 additions & 0 deletions src/modules/events/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./Gateway";
export * from "./Guild";
export * from "./NDC";
export * from "./Thread";
export * from "./VoiceStateUpdate";
1 change: 1 addition & 0 deletions src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { ReactionRolesModule } from "@/modules/reactionRoles/ReactionRoles.modul
export { ScheduleModule } from "@/modules/schedule/schedule.module";
export { CommandsModule } from "@/modules/commands/Commands.module";
export { ComponentsModule } from "@/modules/components/Components.module";
export { MusicModule } from "@/modules/music/Music.module";
10 changes: 10 additions & 0 deletions src/modules/music/Music.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { localizationMapByKey } from "@necord/localization";
import { createCommandGroupDecorator } from "necord";

export const MusicCommand = createCommandGroupDecorator({
name: "music",
description: "Category 🎵 Music",
nameLocalizations: localizationMapByKey("Music.category.name"),
descriptionLocalizations: localizationMapByKey("Music.category.description"),
dmPermission: false,
});
146 changes: 146 additions & 0 deletions src/modules/music/Music.embeds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import {
LOCALIZATION_ADAPTER,
NestedLocalizationAdapter,
} from "@necord/localization";
import { Inject, Injectable } from "@nestjs/common";
import {
Client,
EmbedBuilder,
Guild,
VoiceChannel,
channelMention,
} from "discord.js";
import { IMusicEmbeds } from "./interfaces";

@Injectable()
export class MusicEmbeds implements IMusicEmbeds {
public constructor(
@Inject(LOCALIZATION_ADAPTER)
private readonly translate: NestedLocalizationAdapter,
private readonly client: Client,
) {}

public async PlayerCreateEmbed(
guild: Guild,
textChannelId: string,
voiceChannelId: string,
): Promise<EmbedBuilder> {
return new EmbedBuilder()
.setAuthor({
name: guild.name,
iconURL: guild.iconURL(),
})
.setColor("#00c26f")
.setTitle(
this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Title",
guild.preferredLocale,
),
)
.setFields([
{
name: this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Fields.1",
guild.preferredLocale,
),
value: this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Fields.Content.1",
guild.preferredLocale,
{ TEXT: channelMention(textChannelId) },
),
inline: true,
},
{
name: this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Fields.3",
guild.preferredLocale,
),
value: this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Fields.Content.3",
guild.preferredLocale,
{ VOICE: channelMention(voiceChannelId) },
),
},
])
.setFooter({
text: this.translate.getTranslation(
"Events.PlayerEvents.playerCreate.Embed.Footer",
guild.preferredLocale,
),
iconURL: this.client.user.displayAvatarURL(),
})
.setTimestamp();
}

public async PlayerMoveKickEmbed(
guildLocale: string,
voiceChannelId: string,
): Promise<EmbedBuilder> {
const voiceChannel = (await this.client.channels.fetch(
voiceChannelId,
)) as VoiceChannel;
return new EmbedBuilder()
.setAuthor({
name: this.client.user.tag,
url: this.client.user.displayAvatarURL(),
})
.setColor("#00c26f")
.setTitle(
this.translate.getTranslation(
"Events/PlayerEvents:playerMove:KickEmbed:Title",
guildLocale,
),
)
.setDescription(
this.translate.getTranslation(
"Events/PlayerEvents:playerMove:KickEmbed:Description",
guildLocale,
{ CHANNEL: voiceChannel.name },
),
)
.setFooter({
text: this.translate.getTranslation(
"Events/PlayerEvents:playerMove:KickEmbed:Footer",
guildLocale,
),
})
.setTimestamp();
}

public async QueueEndAutoLeaveEmbed(
guildLocale: string,
voiceChannelId: string,
timer: string,
): Promise<EmbedBuilder> {
const voiceChannel = (await this.client.channels.fetch(
voiceChannelId,
)) as VoiceChannel;
return new EmbedBuilder()
.setAuthor({
name: this.client.user.tag,
url: this.client.user.displayAvatarURL(),
})
.setColor("#00c26f")
.setTitle(
this.translate.getTranslation(
"Events.PlayerEvents.playerMove.queueEnd.Title",
guildLocale,
),
)
.setDescription(
this.translate.getTranslation(
"Events.PlayerEvents.playerMove.queueEnd.Description",
guildLocale,
{ CHANNEL: voiceChannel.name, Timer: timer },
),
)
.setFooter({
text: this.translate.getTranslation(
"Events.PlayerEvents.playerMove.queueEnd.Footer",
guildLocale,
{ TIMER: timer },
),
})
.setTimestamp();
}
}
13 changes: 13 additions & 0 deletions src/modules/music/Music.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Global, Module } from "@nestjs/common";
import * as CommandsMap from "./commands";
import * as EventsMap from "./events";
import * as ProvidersMap from "./types/providers";
const Commands = Object.values(CommandsMap);
const Events = Object.values(EventsMap);
const Providers = Object.values(ProvidersMap);

@Global()
@Module({
providers: [...Commands, ...Events, ...Providers],
})
export class MusicModule {}
Loading

0 comments on commit 1aee1ea

Please sign in to comment.