[Discord-bot] Ping! Pong! 및 구조 분리

Ahnjh·2022년 10월 16일
0

Discord-bot

목록 보기
3/3

디스코드에서의 명령어 응답하기

이전 글에서 만든 discord.js 파일에 아래와 같이 추가해준다

// discord.js 

client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;

    const { commandName } = interaction;

    if (commandName === 'ping') {
        await interaction.reply('Pong!');
    } else if (commandName === 'server') {
        await interaction.reply('Server info.');
    } else if (commandName === 'user') {
        await interaction.reply('User info.');
    }
});

isChatInputCommand() = 채팅 입력된게 상호작용을 뜻하는것인지 확인하는 함수
commandName = 명령을 입력하면 해당 변수로 값이 들어오게된다
reply = 명령에 응답하는 함수

다시 재실행을 한 후 디스코드에서 /ping /server /user 명령어를 입력하면 아래와 같이 나오게 된다



구조 분리

위와같이 if문으로 수많은 명령에 대한 분기처리를 하게되면 점점 봇의 역할이 늘어갈수록 소스를 읽기 힘든 스파게티코드가 될 것이다. 때문에 명령어를 파일로 분리할 것이다. (디스코드에서도 적극 권장)

아래는 기본적으로 사용할 파일들

// deploy-commands.js

const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');

const commands = [];

const rest = new REST({ version: '10' }).setToken(token);

rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
    .then(data => console.log(`Successfully registered ${data.length} application commands.`))
    .catch(console.error);
// discord.js


// Require the necessary discord.js classes
const { Client, GatewayIntentBits } = require('discord.js');
const token = process.env.DISCORD_BOT_TOKEN;

// Create a new client instance
const client = new Client({ intents: [GatewayIntentBits.Guilds] });

// When the client is ready, run this code (only once)
client.once('ready', () => {
    console.log('Ready!');
});

client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;

    const { commandName } = interaction;

    if (commandName === 'ping') {
        await interaction.reply('Pong!');
    } else if (commandName === 'server') {
        await interaction.reply('Server info.');
    } else if (commandName === 'user') {
        await interaction.reply('User info.');
    }
});

client.login(token);

그럼 현재 필자의 디렉토리 구조는 다음과 같다

discord-bot
├──`commands`
│   └── `ping.js`
├── node_modules
├── .env (config.json 대신)
├── deploy-commands.js
├── serverSides
│   └──routes
│       └──api
│           ├──discord.js
│           └──index.js
├── index.js
├── package-lock.json
└── package.json

그후 위에서 언급한 if 문으로 명령어를 분기처리하는 것이 아닌 명령어를 파일별로 분기처리를 할 것이기때문에 /commands/ping.js 파일을 테스트용으로 만들어준다.

// ping.js

const { SlashCommandBuilder } = require('discord.js');

module.exports = {
	data: new SlashCommandBuilder()
		.setName('ping')
		.setDescription('Replies with Pong!'),
	async execute(interaction) {
		await interaction.reply('Pong!');
	},
};

명령 파일 읽기

그후 discord.js 파일에서 명령파일을 동적으로 검색 할 수 있게 다음과 같이 추가해준다

// discord.js

const { Client, GatewayIntentBits, Collection } = require('discord.js');
const fs = require('node:fs');
const path = require('node:path');

client.commands = new Collection();

const commandsPath = path.join(__dirname, '../../../commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); // 

commandFiles.map(file => {
    const filePath = path.join(commandsPath, file);
    const command = require(filePath);

    console.log("command list : ", command?.data?.name);

    client.commands.set(command.data.name, command);
});

deploy-commands.js 파일도 위와같이 수정이 필요하다

// deploy-commands.js

const token = process.env.DISCORD_BOT_TOKEN;
const clientId = process.env.CLIENT_ID;
const guildId = process.env.GUILD_ID;

const commands = [];
const commandsPath = path.join(__dirname, './commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));

commandFiles.map(file => {
    const filePath = path.join(commandsPath, file);
    const command = require(filePath);

    console.log("deploy-commands file command name : ", command?.data?.name);
    commands.push(command.data.toJSON());
});

const rest = new REST({ version: '10' }).setToken(token);

rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
    .then(data => console.log(`Successfully registered ${data.length} application commands.`))
    .catch(console.error);

동적으로 명령 실행

맨처음 if 문으로 분기처리해줬던 discord.js 파일에 interactionCreate 를 다음과같이 바꿔준다

// discord.js

client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;

    const command = interaction.client.commands.get(interaction.commandName);

    if (!command) return;

    try {
        await command.execute(interaction);
    } catch (error) {
        console.error(error);
        await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
    }
});

이제 분기처리 구조를 바꿨는데 실행을 해보자!

$ node deploy-commands.js
$ node server.js

command list :  ping

위와같이 command list 가 찍히면 정상적으로 해당 명령어를 불러오게 된것이다. (/server 명령어는 분기처리 해주면서 제거했기 때문에 현재는 ping 명령어만 먹힌다)
!!! 명령어 파일을 추가하게되면 node deploy-commands.js 를 꼭 실행시켜주자

출처 : https://discordjs.guide/

profile
Clean Code & Clean Architecture

0개의 댓글