Skip to content

Commit dbcc820

Browse files
Merge branch 'SocketSomeone-feautre/recipes/necord'
2 parents 6105c15 + 3a3f109 commit dbcc820

File tree

4 files changed

+315
-0
lines changed

4 files changed

+315
-0
lines changed

content/recipes/necord.md

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
### Necord
2+
3+
Necord is a powerful module that simplifies the creation of [Discord](https://discord.com) bots, allowing for seamless integration with your NestJS application.
4+
5+
> info **Note** Necord is a third-party package and is not officially maintained by the NestJS core team. If you encounter any issues, please report them in the [official repository](https://github.com/necordjs/necord).
6+
7+
#### Installation
8+
9+
To get started, you need to install Necord alongside its dependency, [`Discord.js`](https://discord.js.org).
10+
11+
```bash
12+
$ npm install necord discord.js
13+
```
14+
15+
#### Usage
16+
17+
To utilize Necord in your project, import the `NecordModule` and configure it with the necessary options.
18+
19+
```typescript
20+
@Module({
21+
imports: [
22+
NecordModule.forRoot({
23+
token: process.env.DISCORD_TOKEN,
24+
intents: [IntentsBitField.Guilds],
25+
development: [process.env.DISCORD_DEVELOPMENT_GUILD_ID],
26+
}),
27+
],
28+
providers: [AppService],
29+
})
30+
export class AppModule {}
31+
```
32+
33+
> info **Hint** You can find a comprehensive list of available intents [here](https://discord.com/developers/docs/topics/gateway#gateway-intents).
34+
35+
With this setup, you can inject the `AppService` into your providers to easily register commands, events, and more.
36+
37+
```typescript
38+
@Injectable()
39+
export class AppService {
40+
private readonly logger = new Logger(AppService.name);
41+
42+
@Once('ready')
43+
public onReady(@Context() [client]: ContextOf<'ready'>) {
44+
this.logger.log(`Bot logged in as ${client.user.username}`);
45+
}
46+
47+
@On('warn')
48+
public onWarn(@Context() [message]: ContextOf<'warn'>) {
49+
this.logger.warn(message);
50+
}
51+
}
52+
```
53+
54+
##### Understanding context
55+
56+
You may have noticed the `@Context` decorator in the examples above. This decorator injects the event context into your method, allowing you to access various event-specific data. Since there are multiple types of events, the context type is inferred using the `ContextOf<type: string>` type. You can easily access context variables by using the `@Context()` decorator, which fills the variable with an array of arguments relevant to the event.
57+
58+
#### Text commands
59+
60+
> warning **Caution** Text commands rely on message content, which is set to be deprecated for verified bots and applications with over 100 servers. This means that if your bot is unable to access message content, text commands will not function. Read more about this change [here](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Access-Deprecation-for-Verified-Bots).
61+
62+
Here's how to create a simple command handler for messages using the `@TextCommand` decorator.
63+
64+
```typescript
65+
@Injectable()
66+
export class AppCommands {
67+
@TextCommand({
68+
name: 'ping',
69+
description: 'Responds with pong!',
70+
})
71+
public onPing(
72+
@Context() [message]: TextCommandContext,
73+
@Arguments() args: string[],
74+
) {
75+
return message.reply('pong!');
76+
}
77+
}
78+
```
79+
80+
#### Application commands
81+
82+
Application commands provide a native way for users to interact with your app within the Discord client. There are three types of application commands that can be accessed through different interfaces: chat input, message context menu (accessed by right-clicking a message), and user context menu (accessed by right-clicking a user).
83+
84+
<figure><img src="https://i.imgur.com/4EmG8G8.png" /></figure>
85+
86+
#### Slash commands
87+
88+
Slash commands are an excellent way to engage with users in a structured manner. They allow you to create commands with precise arguments and options, enhancing the user experience significantly.
89+
90+
To define a slash command using Necord, you can use the `SlashCommand` decorator.
91+
92+
```typescript
93+
@Injectable()
94+
export class AppCommands {
95+
@SlashCommand({
96+
name: 'ping',
97+
description: 'Responds with pong!',
98+
})
99+
public async onPing(@Context() [interaction]: SlashCommandContext) {
100+
return interaction.reply({ content: 'Pong!' });
101+
}
102+
}
103+
```
104+
105+
> info **Hint** When your bot client logs in, it will automatically register all defined commands. Note that global commands are cached for up to an hour. To avoid issues with the global cache, utilize the `development` argument in the Necord module, which restricts command visibility to a single guild.
106+
107+
##### Options
108+
109+
You can define parameters for your slash commands using option decorators. Let's create a `TextDto` class for this purpose:
110+
111+
```typescript
112+
export class TextDto {
113+
@StringOption({
114+
name: 'text',
115+
description: 'Input your text here',
116+
required: true,
117+
})
118+
text: string;
119+
}
120+
```
121+
122+
You can then use this DTO in the `AppCommands` class:
123+
124+
```typescript
125+
@Injectable()
126+
export class AppCommands {
127+
@SlashCommand({
128+
name: 'length',
129+
description: 'Calculate the length of your text',
130+
})
131+
public async onLength(
132+
@Context() [interaction]: SlashCommandContext,
133+
@Options() { text }: TextDto,
134+
) {
135+
return interaction.reply({
136+
content: `The length of your text is: ${text.length}`,
137+
});
138+
}
139+
}
140+
```
141+
142+
For a complete list of built-in option decorators, check out [this documentation](https://necord.org/interactions/slash-commands#options).
143+
144+
##### Autocomplete
145+
146+
To implement autocomplete functionality for your slash commands, you'll need to create an interceptor. This interceptor will handle requests as users type in the autocomplete field.
147+
148+
```typescript
149+
@Injectable()
150+
class CatsAutocompleteInterceptor extends AutocompleteInterceptor {
151+
public transformOptions(interaction: AutocompleteInteraction) {
152+
const focused = interaction.options.getFocused(true);
153+
let choices: string[];
154+
155+
if (focused.name === 'cat') {
156+
choices = ['Siamese', 'Persian', 'Maine Coon'];
157+
}
158+
159+
return interaction.respond(
160+
choices
161+
.filter((choice) => choice.startsWith(focused.value.toString()))
162+
.map((choice) => ({ name: choice, value: choice })),
163+
);
164+
}
165+
}
166+
```
167+
168+
You will also need to mark your options class with `autocomplete: true`:
169+
170+
```typescript
171+
export class CatDto {
172+
@StringOption({
173+
name: 'cat',
174+
description: 'Choose a cat breed',
175+
autocomplete: true,
176+
required: true,
177+
})
178+
cat: string;
179+
}
180+
```
181+
182+
Finally, apply the interceptor to your slash command:
183+
184+
```typescript
185+
@Injectable()
186+
export class CatsCommands {
187+
@UseInterceptors(CatsAutocompleteInterceptor)
188+
@SlashCommand({
189+
name: 'cat',
190+
description: 'Retrieve information about a specific cat breed',
191+
})
192+
public async onSearch(
193+
@Context() [interaction]: SlashCommandContext,
194+
@Options() { cat }: CatDto,
195+
) {
196+
return interaction.reply({
197+
content: `I found information on the breed of ${cat} cat!`,
198+
});
199+
}
200+
}
201+
```
202+
203+
#### User context menu
204+
205+
User commands appear on the context menu that appears when right-clicking (or tapping) on users. These commands provide quick actions that target users directly.
206+
207+
```typescript
208+
@Injectable()
209+
export class AppCommands {
210+
@UserCommand({ name: 'Get avatar' })
211+
public async getUserAvatar(
212+
@Context() [interaction]: UserCommandContext,
213+
@TargetUser() user: User,
214+
) {
215+
return interaction.reply({
216+
embeds: [
217+
new MessageEmbed()
218+
.setTitle(`Avatar of ${user.username}`)
219+
.setImage(user.displayAvatarURL({ size: 4096, dynamic: true })),
220+
],
221+
});
222+
}
223+
}
224+
```
225+
226+
#### Message context menu
227+
228+
Message commands show up in the context menu when right-clicking on messages, allowing for quick actions relevant to those messages.
229+
230+
```typescript
231+
@Injectable()
232+
export class AppCommands {
233+
@MessageCommand({ name: 'Copy Message' })
234+
public async copyMessage(
235+
@Context() [interaction]: MessageCommandContext,
236+
@TargetMessage() message: Message,
237+
) {
238+
return interaction.reply({ content: message.content });
239+
}
240+
}
241+
```
242+
243+
#### Buttons
244+
245+
[Buttons](https://discord.com/developers/docs/interactions/message-components#buttons) are interactive elements that can be included in messages. When clicked, they send an [interaction](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object) to your application.
246+
247+
```typescript
248+
@Injectable()
249+
export class AppComponents {
250+
@Button('BUTTON')
251+
public onButtonClick(@Context() [interaction]: ButtonContext) {
252+
return interaction.reply({ content: 'Button clicked!' });
253+
}
254+
}
255+
```
256+
257+
#### Select menus
258+
259+
[Select menus](https://discord.com/developers/docs/interactions/message-components#select-menus) are another type of interactive component that appears on messages. They provide a dropdown-like UI for users to select options.
260+
261+
```typescript
262+
@Injectable()
263+
export class AppComponents {
264+
@StringSelect('SELECT_MENU')
265+
public onSelectMenu(
266+
@Context() [interaction]: StringSelectContext,
267+
@Values() values: string[],
268+
) {
269+
return interaction.reply({ content: `You selected: ${values.join(', ')}` });
270+
}
271+
}
272+
```
273+
274+
For a full list of built-in select menu components, visit [this link](https://necord.org/interactions/message-components#select-menu).
275+
276+
#### Modals
277+
278+
Modals are pop-up forms that allow users to submit formatted input. Here's how to create and handle modals using Necord:
279+
280+
```typescript
281+
@@filename(app.modals)
282+
import { Injectable } from '@nestjs/common';
283+
import { Context, Modal, ModalContext } from 'necord';
284+
285+
@Injectable()
286+
export class AppModals {
287+
@Modal('pizza')
288+
public onModal(@Context() [interaction]: ModalContext) {
289+
return interaction.reply({
290+
content: `Your fav pizza : ${interaction.fields.getTextInputValue('pizza')}`
291+
});
292+
}
293+
}
294+
```
295+
296+
#### More information
297+
298+
Visit the [Necord](https://necord.org) website for more information.

src/app/homepage/menu/menu.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export class MenuComponent implements OnInit {
249249
{ title: 'Serve static', path: '/recipes/serve-static' },
250250
{ title: 'Commander', path: '/recipes/nest-commander' },
251251
{ title: 'Async local storage', path: '/recipes/async-local-storage' },
252+
{ title: 'Necord', path: '/recipes/necord' },
252253
{ title: 'Suites (Automock)', path: '/recipes/suites' },
253254
],
254255
},
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { BasePageComponent } from '../../page/page.component';
3+
4+
@Component({
5+
selector: 'app-necord',
6+
templateUrl: './necord.component.html',
7+
changeDetection: ChangeDetectionStrategy.OnPush,
8+
})
9+
export class NecordComponent extends BasePageComponent {}

src/app/homepage/pages/recipes/recipes.module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { NestCommanderComponent } from './nest-commander/nest-commander.componen
1919
import { AsyncLocalStorageComponent } from './async-local-storage/async-local-storage.component';
2020
import { SuitesComponent } from './suites/suites.component';
2121
import { SwcComponent } from './swc/swc.component';
22+
import { NecordComponent } from './necord/necord.component';
2223
import { PassportComponent } from './passport/passport.component';
2324

2425
const routes: Routes = [
@@ -123,6 +124,11 @@ const routes: Routes = [
123124
component: SuitesComponent,
124125
data: { title: 'Suites (Automock)' },
125126
},
127+
{
128+
path: 'necord',
129+
component: NecordComponent,
130+
data: { title: 'Necord' },
131+
},
126132
{
127133
path: 'passport',
128134
component: PassportComponent,
@@ -150,6 +156,7 @@ const routes: Routes = [
150156
SuitesComponent,
151157
ReplComponent,
152158
SwcComponent,
159+
NecordComponent,
153160
PassportComponent,
154161
],
155162
})

0 commit comments

Comments
 (0)