방학이 끝나고 게을러지지 않기 위해 친구들과 주 1회 기술 블로그 작성하기 스터디를 결성했다. 벌칙은 차차 정하기로 한 후 시작한 지 일주일이 지났다.
이 과정에서 “기간 내에 기술 블로그를 제출했는지 체크하고 페널티를 관리해 주는 것을 자동화하면 어떨까?” 하는 생각이 들었다.
그리하여 블로그 제출을 기록하고 페널티를 관리하는 봇을 디스코드에 추가해 보기로 했다.
이 글은 discord.js를 이용하여 개발하였고 공식 문서를 참고하며 만들었다.
Discord Developer 페이지에 들어가서 로그인 후 우측 상단에 있는 New Application을 누른다.
사진과 같이 Name 입력 및 체크박스 체크 후 Create를 눌러 생성을 완료한다.
밑에서 DISCORD_CLIENT_ID
와 DISCORD_GUILD_ID
라는 변수를 쓰게 되는데, 이는 각각 봇의 Application ID와 봇을 추가한 서버의 ID이다.
Application ID는 General Information 탭에 있고 서버의 ID는 디스코드에서 사용자 설정 → 앱 설정 → 고급 → 개발자 모드를 활성화한 후 서버 프로필 우클릭 후 맨 하단에 서버 ID 복사하기를 하면 알 수 있다.
왼쪽에 Bot에 들어가면 아래 사진과 같이 TOKEN 부분이 Reset Token으로 되어있는데, 이를 눌러서 Token을 생성해준다.
‼️당연한 얘기지만 Token은 외부로 노출되면 안 된다‼️
그 다음 OAuth2 탭에 들어가서 SCOPES에 있는 bot과 applications.commands를 선택한다.
맨 밑에 내리면 Generated URL이 있는데 이를 이용하여 내 디스코드 채널에 봇을 초대할 수 있다.
성공적으로 추가하면 아래와 같이 들어온 것을 확인할 수 있다.
여담으로 디스코드 채널 이름이 VIG인데 이거는 Very Important Geulsseugi, 즉 매우 중요한 글쓰기이다.
ㅎㅎ…ㅎㅎ…………………………….ㅎ
그리고 채널 사진이 베이글인 이유는 처음에는 베리 이임폴턴트 글쓰기로 하려고 했다.
ㅎ…ㅎㅎ….ㅎㅎㅎ……………..
넘어가보자.
현재는 봇을 초대만 한 상태이고 온라인인 상태는 아니다. 봇을 조작하기 위해서는 discord.js
라이브러리를 설치해야 한다. 또한 아까 발급받았던 discord token을 .env 파일에서 관리하기 위해 dotenv
도 같이 설치해준다.
pnpm add discord.js dotenv
나는 pnpm을 사용하기 때문에 위의 명령어로 설치했다.
‼️.gitignore에 .env와 node_modules 등 올라가면 안 되는 것들을 꼭 추가해 주자‼️
그리고 index.js
에 아래와 같이 코드를 작성한 후 node index.js
를 실행하면 아래의 console이 출력되며 봇이 활성화되는 것을 볼 수 있다.
// index.js
import { Client, Events, GatewayIntentBits } from "discord.js";
import { config } from "dotenv";
config();
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const token = process.env.DISCORD_TOKEN;
client.once(Events.ClientReady, (readyClient) => {
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});
client.login(token);
SlashCommandBuilder를 이용하여 명령어를 추가할 수 있는데, 아래 코드를 하나씩 살펴보자.
// commands/submit.js
import { SlashCommandBuilder } from "discord.js";
export default {
data: new SlashCommandBuilder()
.setName("submit") // 1️⃣
.setDescription("작성한 블로그를 제출합니다.") // 2️⃣
.addStringOption((option) => {
option.setName("url").setDescription("작성한 블로그의 URL을 입력하세요.").setRequired(true); // 3️⃣
return option;
}),
async execute(interaction) {
const url = interaction.options.getString("url");
const user = interaction.user.globalName;
await interaction.reply(`제출자: ${user}\n블로그 URL: ${url}`); // 4️⃣
},
};
제출자: ${user}\n블로그 URL: ${url}
양식으로 봇이 답장을 한다.만든 명령어를 봇에게 인식시키기 위해서는 배포 과정이 필요하다.
// commands/deploy-commands.js
import { REST, Routes } from "discord.js";
import { config } from "dotenv";
import submit from "./submit.js";
config();
const clientId = process.env.DISCORD_CLIENT_ID;
const guildId = process.env.DISCORD_GUILD_ID;
const token = process.env.DISCORD_TOKEN;
const commands = [];
commands.push(submit.data.toJSON());
const rest = new REST().setToken(token);
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), {
body: commands,
});
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();
커맨드에 작성한 객체에서 data에 해당하는 값을 JSON으로 변환시킨 후 body에 넣어 봇에게 요청하는 로직이다. 이제 node commands/deploy-commands.js
를 이용하여 위 코드를 실행시켜주면 봇이 커맨드를 인식한다.
명령어를 인식까지 시킨 후 해당 명령어가 들어오면 실행시켜줘야 한다.
index.js
파일에서 token 변수 밑에 아래 코드를 추가해 주자.
client.commands = new Collection();
client.commands.set("submit", submit);
‼️당연히 submit은 import 해야 한다.‼️
아래 코드는 login 위에 작성해 준다.
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) {
interaction.reply({
content: `${interaction.commandName} 명령어가 없어요.`,
ephemeral: true,
});
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: "명령어 실행 중에 오류가 발생했어요. 관리자에게 문의해주세요.",
ephemeral: true,
});
} else {
await interaction.reply({
content: "명령어 실행 중에 오류가 발생했어요. 관리자에게 문의해주세요.",
ephemeral: true,
});
}
}
});
위 코드는 유저가 입력한 커맨드가 우리가 추가한 커맨드에 있는지 확인 후 없다면 “명령어가 없어요.”라는 답변을 한다. 여기서 ephemeral
옵션은 서버에 남지 않고 사용자에게만 보이는 에러 메시지 개념이다.
만약 커맨드가 존재한다면 정의해둔 execute
를 이용하여 실행시켜준다. 그 외의 에러에 대해서는 이전처럼 에러 메시지를 답장으로 준다.
아래는 /submit https://velog.io/gs0428/posts를 실행시킨 결과이다.
내 이름과 제출한 블로그 url이 정상적으로 답장으로 돌아오는 것을 볼 수 있다.
이번 글에서는 디스코드에 봇 추가 및 명령어를 추가하여 상호작용하는 법을 해보았다.
다음 글에는 제출한 결과들을 구글 스프레드시트에 저장하는 방법에 대해 작성하겠다.
맨땅에 헤딩하며 작성한 글이라 부족한 점이 많을 수 있으니 피드백 주시면 아주아주 감사합니다!!