Skip to content

Commit 4759617

Browse files
committed
refactor: use modules for listeners and explorer
1 parent e0177e3 commit 4759617

24 files changed

+342
-205
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
# [6.1.8](https://github.com/necordjs/necord/compare/v6.1.7...v6.1.8) - (2023-10-09)
5+
6+
## Bug Fixes
7+
8+
- Remove type of member permissions from subcommand and groups ([df4683e](https://github.com/necordjs/necord/commit/df4683ef1dc70747e754d711283d2fcd17b05a29))
9+
410
# [6.1.5](https://github.com/necordjs/necord/compare/v6.1.4...v6.1.5) - (2023-09-26)
511

612
## Bug Fixes

src/commands/commands.module.ts

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Global, Inject, Module, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2+
import { ContextMenusModule, ContextMenusService } from './context-menus';
3+
import { SlashCommandsModule, SlashCommandsService } from './slash-commands';
4+
import { CommandsService } from './commands.service';
5+
import { Client } from 'discord.js';
6+
import { NECORD_MODULE_OPTIONS } from '../necord.module-definition';
7+
import { NecordModuleOptions } from '../necord-options.interface';
8+
import { CommandDiscovery } from './command.discovery';
9+
10+
@Global()
11+
@Module({
12+
imports: [ContextMenusModule, SlashCommandsModule],
13+
providers: [CommandsService],
14+
exports: [ContextMenusModule, SlashCommandsModule, CommandsService]
15+
})
16+
export class CommandsModule implements OnModuleInit, OnApplicationBootstrap {
17+
public constructor(
18+
private readonly client: Client,
19+
@Inject(NECORD_MODULE_OPTIONS)
20+
private readonly options: NecordModuleOptions,
21+
private readonly commandsService: CommandsService,
22+
private readonly contextMenusService: ContextMenusService,
23+
private readonly slashCommandsService: SlashCommandsService
24+
) {}
25+
26+
public onModuleInit() {
27+
if (this.options.skipRegistration) {
28+
return;
29+
}
30+
31+
return this.client.once('ready', async () => this.commandsService.register());
32+
}
33+
34+
public onApplicationBootstrap() {
35+
const commands: CommandDiscovery[] = [
36+
...this.contextMenusService.cache.values(),
37+
...this.slashCommandsService.cache.values()
38+
];
39+
40+
for (const command of commands) {
41+
if (Array.isArray(this.options.development)) {
42+
command.setGuilds(this.options.development);
43+
} else {
44+
command.setGuilds(command.getGuilds() ?? [undefined]);
45+
}
46+
}
47+
}
48+
}

src/commands/commands.service.ts

+27-40
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,36 @@
1-
import { Inject, Injectable, Logger } from '@nestjs/common';
2-
import { NECORD_MODULE_OPTIONS } from '../necord.module-definition';
1+
import { Injectable, Logger } from '@nestjs/common';
32
import { Client, Collection } from 'discord.js';
4-
import { NecordModuleOptions } from '../necord-options.interface';
5-
import { ContextMenusService } from './context-menus';
63
import { CommandDiscovery } from './command.discovery';
4+
import { ContextMenusService } from './context-menus';
75
import { SlashCommandsService } from './slash-commands';
86

97
@Injectable()
108
export class CommandsService {
119
private readonly logger = new Logger(CommandsService.name);
1210

13-
public readonly cache = new Collection<string, CommandDiscovery[]>([[undefined, []]]);
14-
1511
public constructor(
1612
private readonly client: Client,
17-
@Inject(NECORD_MODULE_OPTIONS)
18-
private readonly options: NecordModuleOptions,
1913
private readonly contextMenusService: ContextMenusService,
2014
private readonly slashCommandsService: SlashCommandsService
2115
) {}
2216

23-
private onModuleInit() {
24-
if (this.options.skipRegistration) {
25-
return;
26-
}
27-
28-
return this.client.once('ready', async () => this.register());
29-
}
30-
31-
private onApplicationBootstrap() {
32-
const commands: CommandDiscovery[] = [
33-
...this.contextMenusService.cache.values(),
34-
...this.slashCommandsService.cache.values()
35-
];
36-
37-
for (const command of commands) {
38-
const guilds = Array.isArray(this.options.development)
39-
? this.options.development
40-
: command.getGuilds() ?? [undefined];
41-
42-
for (const guildId of guilds) {
43-
const visitedCommands = this.cache.get(guildId) ?? [];
44-
this.cache.set(guildId, visitedCommands.concat(command));
45-
}
46-
}
47-
}
48-
4917
public async register() {
5018
if (this.client.application.partial) {
5119
await this.client.application.fetch();
5220
}
5321

5422
this.logger.log(`Started refreshing application commands.`);
55-
for (const guild of this.cache.keys()) {
56-
if (this.getGuildCommands(guild).length === 0) {
23+
for (const [guild, commands] of this.getCommandsByGuilds().entries()) {
24+
if (commands.length === 0) {
5725
this.logger.log(
5826
`Skipping ${guild ? `guild ${guild}` : 'global'} as it has no commands.`
5927
);
6028
continue;
6129
}
6230

63-
await this.registerInGuild(guild).catch(error => {
31+
const rawCommands = commands.flatMap(command => command.toJSON());
32+
33+
await this.client.application.commands.set(rawCommands, guild).catch(error => {
6434
this.logger.error(
6535
`Failed to register application commands (${
6636
guild ? `in guild ${guild}` : 'global'
@@ -80,23 +50,40 @@ export class CommandsService {
8050
}
8151

8252
public getCommands(): CommandDiscovery[] {
83-
return [...this.cache.values()].flat();
53+
return [
54+
...this.contextMenusService.cache.values(),
55+
...this.slashCommandsService.cache.values()
56+
].flat();
57+
}
58+
59+
public getCommandsByGuilds(): Collection<string, CommandDiscovery[]> {
60+
const collection = new Collection<string, CommandDiscovery[]>();
61+
const commands = this.getCommands();
62+
63+
for (const command of commands) {
64+
for (const guildId of command.getGuilds()) {
65+
const visitedCommands = collection.get(guildId) ?? [];
66+
collection.set(guildId, visitedCommands.concat(command));
67+
}
68+
}
69+
70+
return collection;
8471
}
8572

8673
public getCommandByName(name: string): CommandDiscovery {
8774
return this.getCommands().find(command => command.getName() === name);
8875
}
8976

9077
public getGlobalCommands(): CommandDiscovery[] {
91-
return this.cache.get(undefined) ?? [];
78+
return this.getCommandsByGuilds().get(undefined) ?? [];
9279
}
9380

9481
public getGlobalCommandByName(name: string): CommandDiscovery {
9582
return this.getGlobalCommands().find(command => command.getName() === name);
9683
}
9784

9885
public getGuildCommands(guildId: string): CommandDiscovery[] {
99-
return this.cache.get(guildId) ?? [];
86+
return this.getCommandsByGuilds().get(guildId) ?? [];
10087
}
10188

10289
public getGuildCommandByName(guildId: string, name: string): CommandDiscovery {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Global, Module, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2+
import { ContextMenusService } from './context-menus.service';
3+
import { ExplorerService } from '../../necord-explorer.service';
4+
import { ContextMenuDiscovery } from './context-menu.discovery';
5+
import { Client } from 'discord.js';
6+
import { ContextMenu } from './decorators';
7+
8+
@Global()
9+
@Module({
10+
providers: [ContextMenusService],
11+
exports: [ContextMenusService]
12+
})
13+
export class ContextMenusModule implements OnModuleInit, OnApplicationBootstrap {
14+
public constructor(
15+
private readonly client: Client,
16+
private readonly explorerService: ExplorerService<ContextMenuDiscovery>,
17+
private readonly contextMenusService: ContextMenusService
18+
) {}
19+
20+
public onModuleInit() {
21+
return this.explorerService
22+
.explore(ContextMenu.KEY)
23+
.forEach(contextMenu => this.contextMenusService.add(contextMenu));
24+
}
25+
26+
public onApplicationBootstrap() {
27+
return this.client.on('interactionCreate', interaction => {
28+
if (!interaction.isContextMenuCommand()) return;
29+
30+
return this.contextMenusService
31+
.get(interaction.commandType, interaction.commandName)
32+
?.execute(interaction);
33+
});
34+
}
35+
}

src/commands/context-menus/context-menus.service.ts

+1-22
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,13 @@
1-
import { Client, Collection } from 'discord.js';
1+
import { Collection } from 'discord.js';
22
import { Injectable, Logger } from '@nestjs/common';
33
import { ContextMenuDiscovery, ContextMenuMeta } from './context-menu.discovery';
4-
import { ExplorerService } from '../../necord-explorer.service';
5-
import { ContextMenu } from './decorators';
64

75
@Injectable()
86
export class ContextMenusService {
97
private readonly logger = new Logger(ContextMenusService.name);
108

119
public readonly cache = new Collection<string, ContextMenuDiscovery>();
1210

13-
public constructor(
14-
private readonly client: Client,
15-
private readonly explorerService: ExplorerService<ContextMenuDiscovery>
16-
) {}
17-
18-
private onModuleInit() {
19-
return this.explorerService
20-
.explore(ContextMenu.KEY)
21-
.forEach(contextMenu => this.add(contextMenu));
22-
}
23-
24-
private onApplicationBootstrap() {
25-
return this.client.on('interactionCreate', interaction => {
26-
if (!interaction.isContextMenuCommand()) return;
27-
28-
return this.get(interaction.commandType, interaction.commandName)?.execute(interaction);
29-
});
30-
}
31-
3211
public add(contextMenu: ContextMenuDiscovery): void {
3312
const id = this.getId(contextMenu.getType(), contextMenu.getName());
3413

src/commands/context-menus/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export * from './decorators';
2+
23
export * from './context-menu.discovery';
4+
export * from './context-menus.module';
35
export * from './context-menus.service';

src/commands/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './context-menus';
22
export * from './slash-commands';
33

44
export * from './command.discovery';
5+
export * from './commands.module';
56
export * from './commands.service';

src/commands/slash-commands/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './decorators';
33
export * from './options';
44

55
export * from './slash-command.discovery';
6+
export * from './slash-commands.module';
67
export * from './slash-commands.service';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Global, Module, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2+
import { SlashCommandsService } from './slash-commands.service';
3+
import { Client } from 'discord.js';
4+
import { ExplorerService } from '../../necord-explorer.service';
5+
import { SlashCommandDiscovery } from './slash-command.discovery';
6+
import { SlashCommand, Subcommand } from './decorators';
7+
8+
@Global()
9+
@Module({
10+
providers: [SlashCommandsService],
11+
exports: [SlashCommandsService]
12+
})
13+
export class SlashCommandsModule implements OnModuleInit, OnApplicationBootstrap {
14+
public constructor(
15+
private readonly client: Client,
16+
private readonly explorerService: ExplorerService<SlashCommandDiscovery>,
17+
private readonly slashCommandsService: SlashCommandsService
18+
) {}
19+
20+
public onModuleInit() {
21+
this.explorerService
22+
.explore(SlashCommand.KEY)
23+
.forEach(command => this.slashCommandsService.add(command));
24+
25+
return this.explorerService
26+
.explore(Subcommand.KEY)
27+
.forEach(subcommand => this.slashCommandsService.addSubCommand(subcommand));
28+
}
29+
30+
public onApplicationBootstrap() {
31+
return this.client.on('interactionCreate', i => {
32+
if (!i.isChatInputCommand() && !i.isAutocomplete()) return;
33+
34+
return this.slashCommandsService.get(i.commandName)?.execute(i);
35+
});
36+
}
37+
}

src/commands/slash-commands/slash-commands.service.ts

+6-27
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,16 @@
1-
import { Injectable, Logger } from '@nestjs/common';
1+
import { Injectable, Logger, Scope } from '@nestjs/common';
22
import { SlashCommandDiscovery } from './slash-command.discovery';
3-
import { Client, Collection } from 'discord.js';
4-
import { ExplorerService } from '../../necord-explorer.service';
3+
import { Collection } from 'discord.js';
54
import { Reflector } from '@nestjs/core';
6-
import { SlashCommand, Subcommand, SubcommandGroup } from './decorators';
5+
import { SlashCommand, SubcommandGroup } from './decorators';
76

8-
@Injectable()
7+
@Injectable({ scope: Scope.DEFAULT, durable: true })
98
export class SlashCommandsService {
109
private readonly logger = new Logger(SlashCommandsService.name);
1110

1211
public readonly cache = new Collection<string, SlashCommandDiscovery>();
1312

14-
public constructor(
15-
private readonly client: Client,
16-
private readonly explorerService: ExplorerService<SlashCommandDiscovery>,
17-
private readonly reflector: Reflector
18-
) {}
19-
20-
private onModuleInit() {
21-
this.explorerService.explore(SlashCommand.KEY).forEach(command => this.add(command));
22-
23-
return this.explorerService
24-
.explore(Subcommand.KEY)
25-
.forEach(subcommand => this.addSubCommand(subcommand));
26-
}
27-
28-
private onApplicationBootstrap() {
29-
return this.client.on('interactionCreate', i => {
30-
if (!i.isChatInputCommand() && !i.isAutocomplete()) return;
31-
32-
return this.get(i.commandName)?.execute(i);
33-
});
34-
}
13+
public constructor(private readonly reflector: Reflector) {}
3514

3615
public add(command: SlashCommandDiscovery): void {
3716
if (this.cache.has(command.getName())) {
@@ -49,7 +28,7 @@ export class SlashCommandsService {
4928
return this.cache.delete(commandName);
5029
}
5130

52-
private addSubCommand(subCommand: SlashCommandDiscovery): void {
31+
public addSubCommand(subCommand: SlashCommandDiscovery): void {
5332
let rootCommand = this.reflector.get<SlashCommandDiscovery>(
5433
SlashCommand.KEY,
5534
subCommand.getClass()

src/listeners/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './decorators';
22
export * from './listener.discovery';
33
export * from './listener.interface';
4+
export * from './listeners.module';
45
export * from './listeners.service';

src/listeners/listeners.module.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Global, Module, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2+
import { ListenersService } from './listeners.service';
3+
import { Listener } from './decorators';
4+
import { Client } from 'discord.js';
5+
import { ExplorerService } from '../necord-explorer.service';
6+
import { ListenerDiscovery } from './listener.discovery';
7+
8+
@Global()
9+
@Module({
10+
providers: [ListenersService],
11+
exports: [ListenersService]
12+
})
13+
export class ListenersModule implements OnModuleInit, OnApplicationBootstrap {
14+
public constructor(
15+
private readonly client: Client,
16+
private readonly explorerService: ExplorerService<ListenerDiscovery>
17+
) {}
18+
19+
public onModuleInit() {
20+
return this.explorerService
21+
.explore(Listener.KEY)
22+
.forEach(listener =>
23+
this.client[listener.getType()](listener.getEvent(), (...args) =>
24+
listener.execute(args)
25+
)
26+
);
27+
}
28+
29+
public onApplicationBootstrap(): any {
30+
// TODO: Move here custom listeners
31+
}
32+
}

0 commit comments

Comments
 (0)