slash conmand를 사용하는
디스코드 봇 제작하는 방법에 대해
처음부터 알려드리겠습니다.
interaction 오류 처리 & 안정적인 interaction 반응은
목차 6에 작성하였습니다!
1. 봇 개발 준비
1-1 이름을 영어로 작성한 폴더 생성
1-2 해당 폴더에 npm init을 하여 프로젝트 생성
1-3 프롬프트(or 파워쉘)에서 해당 폴더위치로 이동
1-4 아래의 명령어를 프롬프트에 적어서 설치 진행
npm install discord.js
2. 봇 개발 시작하기
*참고*
디스코드 봇을 제작하기 위해서는 먼저 디스코드 봇을
생성해야 합니다.
만약 생성하지 않았다면 아래의 블로그를 보고 생성한 다음
보는 것을 권장합니다.
디스코드 봇 생성하기(봇 생성하기) (tistory.com)
디스코드 봇 생성하기(봇 생성하기)
디스코드 봇을 생성하는 방법에 대해 알려드리겠습니다. 1. 디스코드 디벨로퍼 포털에 접속한 후 로그인을 진행합니다. https://discord.com/developers\ Discord Developer Portal — API Docs for Bots and Developers Int
devlany.tistory.com
2-1 폴더 안에 index.js 파일 생성
및 commands 폴더생성
// index.js
const fs = require('node:fs');
const path = require('node:path');
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
const { token } = require('./config.json');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, c => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
client.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
//index.js가 존재하는 폴더에서 command 폴더 안의 모든 파일,폴더 가져오기
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
//해당 폴더,파일중 확장자가 js인 파일만가져오기
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
//파일데이터 가져오기
if ('data' in command && 'execute' in command) {
// 해당 파일데이터에 'data'그리고 'execute' 프로퍼티가 있는지 확인
client.commands.set(command.data.name, command);
//있다면 클라이언트 커맨드 콜렉터에 저장
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const command = interaction.client.commands.get(interaction.commandName);
if(!command) return console.log("명령어가 없어요!");
try {
await command.execute(interaction);
} catch (error) {
console.log(error)
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: "[명령어 실행 과정] 에러가 발생하여 명령어를 사용할수없습니다.", ephemeral: true });
} else {
await interaction.reply({ content: '[명령어 실행 과정] 에러가 발생하여 명령어를 사용할수없습니다.', ephemeral: true });
}
}
})
client.login(token);
Client({ intents:[GatewayIntentBits.Guilds] });
intents에 들어가는 것은 일종의 권한입니다.
디렉트 메시지, 메시지 검열봇 등을 제작할 때
설령 초대는 관리자 권한으로 했어도
해당 봇에 맞는 권한을 추가해줘야 합니다!
대표적인 intents 예시
0. 필수 권한
GatewayIntentBits.Guilds
1. 디스코드 서버에서 메시지 정보 가져오기
GatewayIntentBits.MessageContent
GatewayIntentBits.GuildMessages
2. 디스코드 개인 메시지 채널 권한
GatewayIntentBits.DirectMessages
기타 intents는 아래 사이트를 참조해 주세요!
https://discord.com/developers/docs/topics/gateway#list-of-intents
Discord Developer Portal — API Docs for Bots and Developers
Integrate your service with Discord — whether it's a bot or a game or whatever your wildest imagination can come up with.
discord.com
3. 간단한 slash command 만들기
commands 폴더에 ping.js 파일 만들기
ping.js에 아래 코드 작성하기
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
// 명령어 이름(띄어쓰기 및 대부분의 특수문자 사용 불가)
.setDescription('Replies with Pong!'),
// 명령어 설명
async execute(interaction) {
//명령어가 실행될때 실행되는 함수
await interaction.reply('Pong!');
},
};
4. 명령어 등록하기
register.js 파일 생성하고 아래의 코드를
입력합니다.
(해당코드는 commands > [명령어 카테고리 폴더] > [command].js 형식일때 사용하는 방법 입니다.)
const { REST, Routes } = require('discord.js');
const { token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const commands = [];
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
const rest = new REST().setToken(token);
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationGuildCommands(client_id, guild_id),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();
*applicationGuildCommands 대신 applicationCommands(client_id)를 사용하여 특정 디스코드 서버가 아닌 봇이 초대된 모든 서버에 명령어 등록가능
*참고*
( commands 폴더 바로 아래에 .js 파일이 있을시에는 추가적으로 수정해야합니다. )
const foldersPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(foldersPath);
for (const file of commandFiles) {
const filePath = path.join(foldersPath, file);
console.log(filePath)
const command = require(filePath);
if ('data' in command && 'execute' in command) {
console.log(command.data)
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
5. 테스트해 보기
프롬프트에서 node ./register.js를
실행하여 명령어를 등록하시고
node index.js를 실행하여 봇을 실행시킵니다.
위의 과정을 거친다면 채팅에 /ping을 입력할 시
명령어가 자동으로 뜨게 됩니다.
6.interaction 주요 반응 함수들 (Tip)
interaction 주요 반응 함수들
메시지 보내기: interaction.reply({ content: "start!" });
메세지를 보내기 위해 대기하기: interaction.deferReply();
//.editReply(), .deleteReply(), .followUp()를 안정적으로 사용하실려면 꼭 앞에 먼저 위의 코드를 실행하세요
보낸 메시지 수정하기: interaction.editReply({ content: "end!" });
보낸 메시지 삭제하기: interaction.deleteReply();
메시지 반응 대기 끝내기: interaction.deferUpdate();
//메시지가 반응을 대기중일때 실행하면 대기중이 사라집니다.
interaction.reply({ embeds: [Embed], components: [Component], ephemeral: true/false });
deferRelpy를 제외하고 대부분의 반응은
Embed 그리고 Component를 보낼수있으며
사용자에게만 보이는가,서버 모든 인원에게 보이는가를 설정할수있습니다.
또한
해당 메시지가 삭제되어있으면 오류가 발생하는데요
이를 해결하기위해 아래와 같은 코드를 사용할수있습니다!
interaction.isRepliable() // 반응할수있는 함수인지 확인합니다 (삭제 유무 확인에 많이 사용됨)
interaction.replied // 반응이 되었는지 확인합니다
interaction.deferred // 대기중인지 확인합니다.
예시)
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: "메시지 반응 및 대기 중 일때" });
} else {
await interaction.reply({ content: '메시지 처음 전송준비일때' });
}
if (interaction.isRepliable()) {
interaction.deferReply();
interaction.editReply({content: "메시지에 반응할수있습니다!"});
}
특히 isRepliable은 메시지를 reply할수있는가 확인하는 함수입니다!
그러기에
메시지가 삭제되어있거나 deferReply()가 되어있지않아
메시지를 수정,반응 할시 오류가 발생할수있는 상황을 피할수있습니다!
문제가 발생한다면 댓글로 질문해 주세요!
읽어주셔서 감사합니다.
'discord developer' 카테고리의 다른 글
interaction & message 올바른 type(Interface) 지정하는 법 (discord.js / typescript) (0) | 2023.07.28 |
---|---|
how to fix 'node-pre-gyp error' in linux or ubuntu (@discordjs/opu installing issue) (0) | 2023.07.16 |
slash commands에 쿨타임 적용하기 [discord.js] (0) | 2023.06.30 |
디스코드 봇 생성하기(봇 생성하기) (0) | 2023.06.27 |
ytdl-core를 이용하여 디스코드 음악봇 만들기 discord.js (0) | 2023.06.20 |