디스코드 봇 만들기 (1)

GwangSoo·2024년 9월 9일
0

개인공부

목록 보기
8/34
post-thumbnail

방학이 끝나고 게을러지지 않기 위해 친구들과 주 1회 기술 블로그 작성하기 스터디를 결성했다. 벌칙은 차차 정하기로 한 후 시작한 지 일주일이 지났다.

이 과정에서 “기간 내에 기술 블로그를 제출했는지 체크하고 페널티를 관리해 주는 것을 자동화하면 어떨까?” 하는 생각이 들었다.

그리하여 블로그 제출을 기록하고 페널티를 관리하는 봇을 디스코드에 추가해 보기로 했다.

이 글은 discord.js를 이용하여 개발하였고 공식 문서를 참고하며 만들었다.

사전 준비

Application 생성

Discord Developer 페이지에 들어가서 로그인 후 우측 상단에 있는 New Application을 누른다.

Application 생성

사진과 같이 Name 입력 및 체크박스 체크Create를 눌러 생성을 완료한다.

Client ID, Guild ID

밑에서 DISCORD_CLIENT_IDDISCORD_GUILD_ID라는 변수를 쓰게 되는데, 이는 각각 봇의 Application ID봇을 추가한 서버의 ID이다.

Application ID는 General Information 탭에 있고 서버의 ID는 디스코드에서 사용자 설정 → 앱 설정 → 고급 → 개발자 모드를 활성화한 후 서버 프로필 우클릭 후 맨 하단에 서버 ID 복사하기를 하면 알 수 있다.

봇 Token 발급

왼쪽에 Bot에 들어가면 아래 사진과 같이 TOKEN 부분이 Reset Token으로 되어있는데, 이를 눌러서 Token을 생성해준다.

Token 생성

‼️당연한 얘기지만 Token은 외부로 노출되면 안 된다‼️

OAuth2 설정

그 다음 OAuth2 탭에 들어가서 SCOPES에 있는 botapplications.commands를 선택한다.

OAuth2 설정

맨 밑에 내리면 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);

node index.js 실행

봇 활성화

명령어(커맨더) 추가하기

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️⃣
  },
};
  1. 새로운 커맨더를 생성하는 데 이름을 submit이라고 한다.
  2. 해당 커맨드는 작성한 블로그를 제출합니다.라는 설명이 덧붙여있다.
  3. url이라는 stirng의 추가 인자를 받으며 필수 입력이다. 또한 url에는 작성한 블로그의 URL을 입력하세요.라는 설명이 덧붙여있다.
  4. 해당 명령어가 정상적으로 호출되었을 때 유저가 입력한 url의 값과 유저의 이름을 받아온 후 제출자: ${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이 정상적으로 답장으로 돌아오는 것을 볼 수 있다.

마무리하며

이번 글에서는 디스코드에 봇 추가 및 명령어를 추가하여 상호작용하는 법을 해보았다.

다음 글에는 제출한 결과들을 구글 스프레드시트에 저장하는 방법에 대해 작성하겠다.

맨땅에 헤딩하며 작성한 글이라 부족한 점이 많을 수 있으니 피드백 주시면 아주아주 감사합니다!!

레포지토리: https://github.com/gs0428/VIG-Discord

0개의 댓글