Add nestjs discord package

This commit is contained in:
Manuel Ruwe 2022-12-16 13:48:39 +01:00
parent 0e70791284
commit beb34b7d7c
13 changed files with 230 additions and 190 deletions

View File

@ -21,6 +21,8 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@discord-nestjs/common": "^4.0.8",
"@discord-nestjs/core": "^4.3.1",
"@discordjs/opus": "^0.9.0", "@discordjs/opus": "^0.9.0",
"@jellyfin/sdk": "^0.7.0", "@jellyfin/sdk": "^0.7.0",
"@nestjs/common": "^9.0.0", "@nestjs/common": "^9.0.0",

View File

@ -1,14 +1,16 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import * as Joi from 'joi'; import * as Joi from 'joi';
import { EventEmitterModule } from '@nestjs/event-emitter'; import { DiscordModule } from '@discord-nestjs/core';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { DiscordClientModule } from './clients/discord/discord.module'; import { DiscordClientModule } from './clients/discord/discord.module';
import { CommandHandlerModule } from './commands/handler/command-handler.module';
import { JellyfinClientModule } from './clients/jellyfin/jellyfin.module'; import { JellyfinClientModule } from './clients/jellyfin/jellyfin.module';
import { CommandModule } from './commands/command.module';
import { DiscordConfigService } from './clients/discord/jellyfin.config.service';
@Module({ @Module({
imports: [ imports: [
@ -20,10 +22,14 @@ import { JellyfinClientModule } from './clients/jellyfin/jellyfin.module';
JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(), JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(),
}), }),
}), }),
DiscordModule.forRootAsync({
useClass: DiscordConfigService,
}),
DiscordModule,
EventEmitterModule.forRoot(), EventEmitterModule.forRoot(),
CommandModule,
DiscordClientModule, DiscordClientModule,
JellyfinClientModule, JellyfinClientModule,
CommandHandlerModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],

View File

@ -1,21 +1,10 @@
import { Module, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { DiscordService } from './discord.service'; import { DiscordConfigService } from './jellyfin.config.service';
@Module({ @Module({
imports: [], imports: [],
controllers: [], controllers: [],
providers: [DiscordService], providers: [DiscordConfigService],
exports: [DiscordService], exports: [DiscordConfigService],
}) })
export class DiscordClientModule implements OnModuleInit, OnModuleDestroy { export class DiscordClientModule {}
constructor(private discordService: DiscordService) {}
onModuleDestroy() {
this.discordService.destroyClient();
}
onModuleInit() {
this.discordService.initializeClient();
this.discordService.registerEventHandlers();
this.discordService.connectAndLogin();
}
}

View File

@ -1,46 +0,0 @@
import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Client } from 'discord.js';
@Injectable()
export class DiscordService {
private readonly logger = new Logger(DiscordService.name);
private client: Client;
constructor(private eventEmitter: EventEmitter2) {}
initializeClient() {
this.client = new Client({
intents: ['Guilds', 'GuildMessages', 'MessageContent'],
});
this.logger.debug('Initialized Discord client');
}
connectAndLogin() {
this.client.login(process.env.DISCORD_CLIENT_TOKEN);
}
registerEventHandlers() {
this.client.on('ready', () => {
this.logger.debug(`Connected as '${this.client.user.tag}' and ready!`);
this.eventEmitter.emit('client.discord.ready');
});
this.client.on('messageCreate', async (message) => {
if (message.author.bot) {
return;
}
await message.channel.send('nice');
});
}
destroyClient() {
this.client.destroy();
}
getClient() {
return this.client;
}
}

View File

@ -0,0 +1,18 @@
import {
DiscordModuleOption,
DiscordOptionsFactory,
} from '@discord-nestjs/core';
import { Injectable } from '@nestjs/common';
import { GatewayIntentBits } from 'discord.js';
@Injectable()
export class DiscordConfigService implements DiscordOptionsFactory {
createDiscordOptions(): DiscordModuleOption {
return {
token: process.env.DISCORD_CLIENT_TOKEN,
discordClientOptions: {
intents: [GatewayIntentBits.Guilds],
},
};
}
}

View File

@ -35,10 +35,18 @@ export class JellyfinService {
process.env.JELLYFIN_AUTHENTICATION_PASSWORD, process.env.JELLYFIN_AUTHENTICATION_PASSWORD,
) )
.then((response) => { .then((response) => {
if (response.data.SessionInfo === undefined) {
this.logger.error(
`Failed to authenticate with response code ${response.status}: '${response.data}'`,
);
return;
}
this.logger.debug( this.logger.debug(
`Connected using user '${response.data.SessionInfo.UserId}'`, `Connected using user '${response.data.SessionInfo.UserId}'`,
); );
}).catch((test) => { })
.catch((test) => {
this.logger.error(test); this.logger.error(test);
}); });
} }

View File

@ -1,6 +0,0 @@
import { SlashCommandBuilder } from "discord.js";
export abstract class Command {
abstract builder(): SlashCommandBuilder;
abstract execute(): void;
}

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { DiscordModule } from '@discord-nestjs/core';
import { HelpCommand } from './help.command';
@Module({
imports: [DiscordModule.forFeature()],
controllers: [],
providers: [HelpCommand],
exports: [],
})
export class CommandModule {}

View File

@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { DiscordClientModule } from '../../clients/discord/discord.module';
import { CommandHandlerService } from './command-handler.service';
@Module({
imports: [DiscordClientModule],
controllers: [],
providers: [CommandHandlerService],
})
export class CommandHandlerModule {}

View File

@ -1,107 +0,0 @@
import { EmbedBuilder } from '@discordjs/builders';
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import {
ApplicationCommand,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
} from 'discord.js';
import { DiscordService } from 'src/clients/discord/discord.service';
import { Command } from '../abstractCommand';
@Injectable()
export class CommandHandlerService {
private logger: Logger = new Logger(CommandHandlerService.name);
constructor(private discordService: DiscordService) {}
@OnEvent('client.discord.ready')
async handleOnDiscordClientReady() {
var commands = [
new SlashCommandBuilder()
.setName('play')
.setDescription('Immideatly play a track')
.addStringOption((option) =>
option
.setName('track')
.setDescription('the track name')
.setRequired(true),
),
new SlashCommandBuilder()
.setName('summon')
.setDescription('Join your current voice channel'),
new SlashCommandBuilder()
.setName('disconnect')
.setDescription('Disconnect from the current voice channel'),
new SlashCommandBuilder()
.setName('enqueue')
.setDescription('Enqueue a track to the current playlist')
.addStringOption((option) =>
option
.setName('track')
.setDescription('the track name')
.setRequired(true),
),
new SlashCommandBuilder()
.setName('current')
.setDescription('Print the current track information'),
new SlashCommandBuilder()
.setName('pause')
.setDescription('Pause or resume the playback of the current track'),
new SlashCommandBuilder()
.setName('skip')
.setDescription('Skip the current track'),
new SlashCommandBuilder()
.setName('stop')
.setDescription(
'Stop playback entirely and clear the current playlist',
),
new SlashCommandBuilder()
.setName('help')
.setDescription('Get help for this Discord Bot'),
];
await this.discordService
.getClient()
.application.commands.set(commands.map((x) => x.toJSON()));
this.discordService
.getClient()
.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) {
return;
}
await interaction.reply({
embeds: [
new EmbedBuilder()
.setAuthor({
name: 'Jellyfin Discord Bot',
iconURL:
'https://github.com/walkxcode/dashboard-icons/blob/main/png/jellyfin.png?raw=true',
url: 'https://github.com/manuel-rw/jellyfin-discord-music-bot',
})
.setTitle('Help Information')
.setDescription(
'Jellyfin Discord Music bot is an easy way to broadcast your music collection to a Discord voicechannel.',
)
.addFields([
{
name: 'Report an issue',
value:
'https://github.com/manuel-rw/jellyfin-discord-music-bot/issues/new/choose',
inline: true,
},
{
name: 'Source code',
value:
'https://github.com/manuel-rw/jellyfin-discord-music-bot',
inline: true,
},
])
.toJSON(),
],
});
});
}
}

View File

@ -0,0 +1,90 @@
import { TransformPipe } from '@discord-nestjs/common';
import {
Command,
DiscordTransformedCommand,
TransformedCommandExecutionContext,
UsePipes,
} from '@discord-nestjs/core';
import { EmbedBuilder } from '@discordjs/builders';
import { InteractionReplyOptions, MessagePayload } from 'discord.js';
@Command({
name: 'help',
description: 'ejifejf',
})
@UsePipes(TransformPipe)
export class HelpCommand implements DiscordTransformedCommand<unknown> {
handler(
dto: unknown,
executionContext: TransformedCommandExecutionContext<any>,
):
| string
| void
| MessagePayload
| InteractionReplyOptions
| Promise<string | void | MessagePayload | InteractionReplyOptions> {
return {
embeds: [
new EmbedBuilder()
.setAuthor({
name: 'Jellyfin Discord Bot',
iconURL:
'https://github.com/walkxcode/dashboard-icons/blob/main/png/jellyfin.png?raw=true',
url: 'https://github.com/manuel-rw/jellyfin-discord-music-bot',
})
.setTitle('Help Information')
.setDescription(
'Jellyfin Discord Music bot is an easy way to broadcast your music collection to a Discord voicechannel.',
)
.addFields([
{
name: 'Report an issue',
value:
'https://github.com/manuel-rw/jellyfin-discord-music-bot/issues/new/choose',
inline: true,
},
{
name: 'Source code',
value: 'https://github.com/manuel-rw/jellyfin-discord-music-bot',
inline: true,
},
])
.toJSON(),
],
};
}
/*
handler(
dto: unknown,
executionContext: TransformedCommandExecutionContext<any>,
) {
return new EmbedBuilder()
.setAuthor({
name: 'Jellyfin Discord Bot',
iconURL:
'https://github.com/walkxcode/dashboard-icons/blob/main/png/jellyfin.png?raw=true',
url: 'https://github.com/manuel-rw/jellyfin-discord-music-bot',
})
.setTitle('Help Information')
.setDescription(
'Jellyfin Discord Music bot is an easy way to broadcast your music collection to a Discord voicechannel.',
)
.addFields([
{
name: 'Report an issue',
value:
'https://github.com/manuel-rw/jellyfin-discord-music-bot/issues/new/choose',
inline: true,
},
{
name: 'Source code',
value: 'https://github.com/manuel-rw/jellyfin-discord-music-bot',
inline: true,
},
])
.toJSON();
}
*/
}

View File

@ -16,6 +16,7 @@
"noImplicitAny": false, "noImplicitAny": false,
"strictBindCallApply": false, "strictBindCallApply": false,
"forceConsistentCasingInFileNames": false, "forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false "noFallthroughCasesInSwitch": false,
"useDefineForClassFields": true
} }
} }

View File

@ -491,6 +491,39 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@discord-nestjs/common@npm:^4.0.8":
version: 4.0.8
resolution: "@discord-nestjs/common@npm:4.0.8"
dependencies:
"@nestjs/mapped-types": 1.2.0
class-transformer: 0.5.1
class-validator: 0.13.2
peerDependencies:
"@discord-nestjs/core": "*"
"@nestjs/common": 8.* || 9.*
"@nestjs/core": 8.* || 9.*
discord.js: 14.*
reflect-metadata: ^0.1.13
rxjs: ^7.*
checksum: 254f740d6762f3cfe827500b7c0b0a1357c631032a95fe82a1425dbbebe97e442add85a5d85dfaf333a53cb16caea293a8345bb8e17832f8f67c605676bca69f
languageName: node
linkType: hard
"@discord-nestjs/core@npm:^4.3.1":
version: 4.3.1
resolution: "@discord-nestjs/core@npm:4.3.1"
dependencies:
class-transformer: 0.5.1
peerDependencies:
"@nestjs/common": 8.* || 9.*
"@nestjs/core": 8.* || 9.*
discord.js: 14.*
reflect-metadata: ^0.1.13
rxjs: ^7.*
checksum: 2a9013126359520d9133fa547f4a08f46eb9f3d7329aa516ef22c9c779794815ba4b1e1cb5b7e899a9e16c11568931d8a14f9cb52e3944d80bf8e292e6a6daba
languageName: node
linkType: hard
"@discordjs/builders@npm:^1.4.0": "@discordjs/builders@npm:^1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "@discordjs/builders@npm:1.4.0" resolution: "@discordjs/builders@npm:1.4.0"
@ -1076,6 +1109,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@nestjs/mapped-types@npm:1.2.0":
version: 1.2.0
resolution: "@nestjs/mapped-types@npm:1.2.0"
peerDependencies:
"@nestjs/common": ^7.0.8 || ^8.0.0 || ^9.0.0
class-transformer: ^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0
class-validator: ^0.11.1 || ^0.12.0 || ^0.13.0
reflect-metadata: ^0.1.12
peerDependenciesMeta:
class-transformer:
optional: true
class-validator:
optional: true
checksum: cc625310873c5caf69521bc03c75a6780467f662fd4c8b2402f6e65228268eec1244c131dbf65c81c985680feacee50dae7d3c1f2b7bced7bad79922389aa38f
languageName: node
linkType: hard
"@nestjs/platform-express@npm:^9.0.0": "@nestjs/platform-express@npm:^9.0.0":
version: 9.2.1 version: 9.2.1
resolution: "@nestjs/platform-express@npm:9.2.1" resolution: "@nestjs/platform-express@npm:9.2.1"
@ -2547,6 +2597,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"class-transformer@npm:0.5.1":
version: 0.5.1
resolution: "class-transformer@npm:0.5.1"
checksum: f191c8b4cc4239990f5efdd790cabdd852c243ed66248e39f6616a349c910c6eed2d9fd1fbf7ee6ea89f69b4f1d0b493b27347fe0cd0ae26b47c3745a805b6d3
languageName: node
linkType: hard
"class-validator@npm:0.13.2":
version: 0.13.2
resolution: "class-validator@npm:0.13.2"
dependencies:
libphonenumber-js: ^1.9.43
validator: ^13.7.0
checksum: 0deb4c29faa18345f6989fd7eaaaa07b05caae5298603fcd6485531c6daad503e5d2b24cc1342e4fc88ae5ba0acffdc24d0fc333110ef3f21a667cd8a79e1258
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0": "clean-stack@npm:^2.0.0":
version: 2.2.0 version: 2.2.0
resolution: "clean-stack@npm:2.2.0" resolution: "clean-stack@npm:2.2.0"
@ -4361,6 +4428,8 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "jellyfin-discord-music-bot@workspace:." resolution: "jellyfin-discord-music-bot@workspace:."
dependencies: dependencies:
"@discord-nestjs/common": ^4.0.8
"@discord-nestjs/core": ^4.3.1
"@discordjs/opus": ^0.9.0 "@discordjs/opus": ^0.9.0
"@jellyfin/sdk": ^0.7.0 "@jellyfin/sdk": ^0.7.0
"@nestjs/cli": ^9.0.0 "@nestjs/cli": ^9.0.0
@ -4995,6 +5064,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"libphonenumber-js@npm:^1.9.43":
version: 1.10.15
resolution: "libphonenumber-js@npm:1.10.15"
checksum: 94283206159b9eaf07e4af9c002dda2b0b171099a8116baa0b6871f6312475112ec92121cca85916968fa9fb1220641205f6ee2349a1cc33197d811a69572198
languageName: node
linkType: hard
"lines-and-columns@npm:^1.1.6": "lines-and-columns@npm:^1.1.6":
version: 1.2.4 version: 1.2.4
resolution: "lines-and-columns@npm:1.2.4" resolution: "lines-and-columns@npm:1.2.4"
@ -7137,6 +7213,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"validator@npm:^13.7.0":
version: 13.7.0
resolution: "validator@npm:13.7.0"
checksum: 2b83283de1222ca549a7ef57f46e8d49c6669213348db78b7045bce36a3b5843ff1e9f709ebf74574e06223461ee1f264f8cc9a26a0060a79a27de079d8286ef
languageName: node
linkType: hard
"vary@npm:^1, vary@npm:~1.1.2": "vary@npm:^1, vary@npm:~1.1.2":
version: 1.1.2 version: 1.1.2
resolution: "vary@npm:1.1.2" resolution: "vary@npm:1.1.2"