Skip to content

Commit

Permalink
Edited posts
Browse files Browse the repository at this point in the history
  • Loading branch information
sunwoo-j committed Jun 27, 2024
1 parent b72e9dd commit a4f779b
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 137 deletions.
10 changes: 5 additions & 5 deletions _posts/2024-05-22-discord-bot-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ description: 디스코드 봇을 생성하고 채팅에 대답하게 하기

![](/assets/img/discord%20bot/1_0.png)

완료되면 아래처럼 애플리케이션(Application)을 생성할 수 있는 개발자 포털 홈페이지가 나온다. 애플리케이션을 생성함으로써 인증 토큰을 제공받고 권한을 설정하는 등 디스코드 API와 상호작용을 할 수 있게 된다.
완료되면 아래처럼 애플리케이션(Application)을 생성할 수 있는 개발자 포털 홈페이지가 나온다. 애플리케이션을 생성함으로써 인증 토큰을 제공받고 권한을 설정하는 등 디스코드 API와 상호 작용을 할 수 있게 된다.

![](/assets/img/discord bot/1_1.png)

Expand All @@ -37,7 +37,7 @@ description: 디스코드 봇을 생성하고 채팅에 대답하게 하기

이제 생성된 애플리케이션에 대한 정보가 화면에 뜬다. 여기서 아이콘, 이름과 설명 등을 수정할 수 있지만 애플리케이션 정보는 봇을 이용하는 사용자에게 직접적으로 보이지는 않는다.

> 봇 외에도 디스코드 API와 상호작용을 하는 모든 프로그램은 [디스코드 애플리케이션](https://discord.com/developers/docs/game-sdk/applications)이 필요하다.
> 봇 외에도 디스코드 API와 상호 작용을 하는 모든 프로그램은 [디스코드 애플리케이션](https://discord.com/developers/docs/game-sdk/applications)이 필요하다.
{: .prompt-info }

우리는 봇을 만들 것이기 때문에 좌측 메뉴에서 <kbd>Bot</kbd> 탭을 눌러 이동해 주자.
Expand Down Expand Up @@ -121,9 +121,9 @@ $ pip install discord.py

### 2. 디스코드와 봇 연결시키기

명령어를 받고 답을 하려면 먼저 디스코드와 봇이 서로 통신이 되어야 한다. `discord.py`에는 통신을 위한 몇 가지 방법이 존재하는데 그중에서 우선 [**Client**](https://discordpy.readthedocs.io/en/stable/api.html#clients)에 대해 다뤄볼 것이다.
명령어를 받고 답을 하려면 먼저 디스코드와 봇이 서로 통신이 되어야 한다. `discord.py`에는 통신을 위한 몇 가지 방법이 존재하는데 그중에서 우선 [`Client`](https://discordpy.readthedocs.io/en/stable/api.html#clients)에 대해 다뤄볼 것이다.

`discord.Client`는 디스코드와의 연결을 나타내는 기본적인 객체라고 보면 되는데, 이를 활용해 디스코드 웹소켓과 API와의 상호작용이 가능하다. 아래는 `discord.py` documentation에서 제공하는 [간단한 시작 코드](https://discordpy.readthedocs.io/en/latest/quickstart.html#a-minimal-bot)다. 이번 글에서는 이 코드를 활용해 볼 것이다.
`discord.Client`는 디스코드와의 연결을 나타내는 기본적인 객체라고 보면 되는데, 이를 활용해 디스코드 웹소켓과 API와의 상호 작용이 가능하다. 아래는 `discord.py` documentation에서 제공하는 [간단한 시작 코드](https://discordpy.readthedocs.io/en/latest/quickstart.html#a-minimal-bot)다. 이번 글에서는 이 코드를 활용해 볼 것이다.

```python
# This example requires the 'message_content' intent.
Expand All @@ -150,7 +150,7 @@ async def on_message(message):
client.run('your token here')
```

코드를 보면 `client`를 먼저 설정하고 `on_ready()``on_message()`라는 **이벤트(Event)**들에 대한 Event Handler를 지정한 것을 볼 수 있다. 여기서 말하는 이벤트는 메시지 전송, 사용자 밴과 채널 생성처럼 상태에 변화가 생겼을 때 디스코드가 호출하는 일종의 함수라고 이해하면 된다. `on_ready()`는 그중에서 `Client`가 디스코드와의 연결을 성공했을 때 보내지는 이벤트로, 봇이 사용자와 상호작용을 할 준비가 되었을 때 호출된다고 보면 된다.
코드를 보면 `client`를 먼저 설정하고 `on_ready()``on_message()`라는 **이벤트(Event)**들에 대한 Event Handler를 지정한 것을 볼 수 있다. 여기서 말하는 이벤트는 메시지 전송, 사용자 밴과 채널 생성처럼 상태에 변화가 생겼을 때 디스코드가 호출하는 일종의 함수라고 이해하면 된다. `on_ready()`는 그중에서 `Client`가 디스코드와의 연결을 성공했을 때 보내지는 이벤트로, 봇이 사용자와 상호 작용을 할 준비가 되었을 때 호출된다고 보면 된다.

`on_message()`는 봇이 속한 길드에서 메시지가 전송됐을 때 발동되는 이벤트이다.

Expand Down
28 changes: 14 additions & 14 deletions _posts/2024-05-24-discord-bot-2.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: 디스코드 봇 DIY - 2. 기본적인 명령어 설정
date: 2024-05-24 11:07:32 +/-TTTT
last_modified_at: 2024-05-24 22:57:40 +/-TTTT
last_modified_at: 2024-06-27 13:34:40 +/-TTTT
categories: [Python, discord.py]
tags: [python, discord, bot]
description: discord.py 확장 라이브러리로 명령어 설정하기
Expand All @@ -14,7 +14,7 @@ description: discord.py 확장 라이브러리로 명령어 설정하기
## Bot과 확장 라이브러리

`Bot`은 앞서 본 `Client`의 기능들을 상속받는 일종의 subclass이다. `Client`만 활용해서 봇을 개발할 수도 있겠지만, `Client`는 이벤트를 일일이 지정해 줘야 하는 번거로움이 있다. 이와 같은 단점들을 보완하기 위해 `discord.py`는 확장 라이브러리인 `discord.ext`(extension) 포함하고 있다. 이 라이브러리에 `Bot`이 포함되어 있는데, 편리성을 위해 만들어진 만큼 비교적 간편하게 명령어를 추가할 수 있다.
[`Bot`](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html?#bot)은 앞서 본 `Client`의 기능들을 상속받는 일종의 subclass이다. `Client`만 활용해서 봇을 개발할 수도 있겠지만, `Client`는 이벤트를 일일이 지정해 줘야 하는 번거로움이 있다. 이와 같은 단점들을 보완하기 위해 `discord.py`는 확장 라이브러리인 `discord.ext`(extension) 포함하고 있다. 이 라이브러리에 `Bot`이 포함되어 있는데, 편리성을 위해 만들어진 만큼 비교적 간편하게 명령어를 추가할 수 있다.

### 1. Bot으로 명령어 추가하기

Expand All @@ -24,12 +24,12 @@ from discord.ext import commands
bot = commands.Bot(command_prefix='!') # 명령어 인식 기호
```

`Bot``discord.ext.commands`들어있기 때문에 `import discord`와 별개로 라이브러리를 추가해 주어야 한다. 그리고 `Bot`initialize할 때는 `command_prefix`를 지정해야 하는데, 여기서 지정된 string으로 input이 시작해야 명령어로 인식된다.
`Bot`확장 라이브러리에 들어있기 때문에 `import discord`와 별개로 라이브러리를 추가해 주어야 한다. 그리고 `Bot`초기화할 때는 `command_prefix`를 지정해야 하는데, 메시지가 여기서 **지정된 string으로 시작**해야 명령어로 인식된다. 이 경우에는 `'!'`로 설정되었으므로 느낌표로 시작하는 모든 메시지가 명령어로 인식이 된다.

> `command_prefix`를 여러 개 지정하고 싶다면 `('!', '?')`처럼 지정할 수 있다. 빈 string을 지정할 수도 있는데, 이 경우에는 모든 입력이 명령어로 인식된다.
{: .prompt-info}

`Command`는 명령어로 발동시키는 함수를 나타내는 객체다. 일반 함수 앞에 `@discord.ext.commands.command` decorator를 붙이면 `Command`되는데, 명령어가 decorator에서 정한 `name`과 일치한다면 `Command`가 발동한다. 예시로 전에 쓴 코드를 이를 활용해 바꿔 보겠다.
`Command`는 명령어로 발동시키는 함수를 나타내는 class다. 일반 함수 앞에 `@bot.command` decorator를 붙이면 `Command` 객체가 되는데, `command_prefix` 뒤에 붙은 명령어가 decorator에서 정한 `name`과 일치한다면 `Command`가 발동한다. 예시로 이전에 쓴 코드를 `@bot.command` 활용해 바꿔 보겠다.

```python
# bot.py
Expand Down Expand Up @@ -84,14 +84,14 @@ async def hello(ctx):
bot.run(TOKEN)
```

두 코드 모두 동일하게 작동한다. 자세히 들여다보면 `on_message()` 대신에 `Command`를 활용한 것을 볼 수 있다. `Client`를 사용할 때는 텍스트로 함수를 호출하기 위해서 `on_message()`라는 이벤트를 특정해 줘야 했지만, `Command`를 활용하면 이벤트를 따로 설정할 필요 없이 바로 명령어가 인식되는 모습이다. Decorator에서 `name='hello'`로 argument를 지정했기 때문에 `command_prefix``name`이 합쳐진 `$hello`를 입력하면 설정된 함수가 호출된다.
두 코드 모두 동일하게 작동한다. 자세히 들여다보면 `on_message()` 대신에 `Command`를 활용한 것을 볼 수 있다. `Client`를 사용할 때는 텍스트로 함수를 호출하기 위해서 `on_message()`라는 이벤트를 특정해 줘야 했지만, `Command`를 활용하면 이벤트를 따로 설정할 필요 없이 메시지에서 바로 명령어가 인식되는 모습이다. Decorator에서 `name='hello'`로 argument를 지정했기 때문에 `command_prefix``name`이 합쳐진 `$hello`를 입력하면 설정된 함수가 호출된다.

> **`ctx`**는 명령어를 실행한 사람이 누군지와 어떤 채널에서 입력됐는지 등의 [context 정보](https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#invocation-context)를 담고 있다.
> **`ctx`**는 명령어를 실행한 사람이 누군지와 어떤 채널에서 입력됐는지 등의 [**Context 정보**](https://discordpy.readthedocs.io/en/latest/ext/commands/commands.html#invocation-context)를 담고 있다.
{: .prompt-info}

![](/assets/img/discord%20bot/2_1.png)

`Command`를 쓰면 또 좋은 점이 함수들 중 명령어에 해당하는 것이 무엇인지 인지하고 있으며, 따로 추가하지 않아도 `help` 명령어로 봇에 설정된 명령어들의 목록을 보는 것이 가능하다는 점이다.
`Command`를 쓰면 또 좋은 점이 내가 쓴 함수 중 명령어에 해당하는 것이 무엇인지 인지하고 있으며, 따로 추가하지 않아도 `help` 명령어로 봇에 설정된 명령어들의 목록을 보는 것이 가능하다는 점이다.

```python
@bot.command(name='hello', help="인사를 합니다")
Expand All @@ -103,7 +103,7 @@ bot.run(TOKEN)

### 2. 명령어에 Parameter 추가하기

`Client`로 명령을 받을 때는 전부 string으로 받기 때문에 함수 안에서 type 변환을 해야 했지만, `Command`를 활용하면 parameter에서 원하는 type을 미리 설정할 수 있다. 아래는 간단한 예시다.
`Client`로 명령을 받을 때는 전부 string으로 받기 때문에 함수 안에서 type 변환을 해야 했지만, `Command`를 활용하면 parameter에서 **원하는 type** 미리 설정할 수 있다. 아래는 간단한 예시다.

```python
@bot.command(name='곱하기', help="숫자 두 개를 곱합니다")
Expand All @@ -112,7 +112,7 @@ async def multiply(ctx, first_int: int, second_int: int):
await ctx.send("결과는 ", product, "입니다.")
```

`first_int``second_int``: int`를 붙여서 이 parameter들은 정숫값을 받는다고 설정해둔 것이다. 다만 정수가 아닌 값을 넣지 못하게 막아둘 수는 없어서 error handling은 따로 해야 한다. 더 나아가 parameter에는 단순히 string이나 int 같은 기본적인 type 말고도 길드나 멤버 같은 디스코드 API만의 특수한 type도 들어갈 수 있다.
`first_int``second_int``: int`를 붙여서 이 parameter들은 정숫값을 받는다고 설정해 두었다. 다만 정수가 아닌 값을 넣지 못하게 막아둘 수는 없어서 error handling은 따로 해야 한다. 더 나아가 parameter에는 단순히 string이나 int 같은 기본적인 type 말고도 길드나 멤버 같은 디스코드 API만의 특수한 type도 들어갈 수 있다.

```python
@bot.command(name='참가일', help="멤버의 서버 참가 날짜를 알려줍니다")
Expand All @@ -121,11 +121,11 @@ async def joined(ctx, member: discord.Member):
await ctx.send(f"{member.display_name}님은 {join_date}에 서버에 참가했습니다.")
```

멤버가 길드에 언제 참여했는지를 보여주는 명령어를 만들었다. `discord.Member`를 type으로 설정함으로써 길드에 참여하고 있는 사용자 객체를 받아들이는 변수로 지정했다. 그렇기에 `Member`의 attribute 중 하나인 `joined_at`을 받아와 출력할 수 있는 것이다.
멤버가 길드에 언제 참여했는지를 보여주는 명령어를 만들었다. `discord.Member`를 type으로 설정함으로써 길드에 참여하고 있는 사용자 객체를 받아들이는 변수로 지정했다. 그렇기에 `Member`의 attribute 중 하나인 `joined_at`을 받아와 출력할 수 있다.

<img src="/assets/img/discord bot/2_3.png" alt="2_3" style="display: block; margin-left: auto; margin-right: auto; width: 60%;">

이런 식으로 `Member`가 현재 길드에 언제 참가했는지 보여줄 수 있다. 참고로 `Member.display_name`은 채팅 등 디스코드 UI에 보이는 별명, `Member.name`은 사용자명, `Member.id`는 사용자 고유 ID니 적재적소에 활용할 수 있도록 하자.
이런 식으로 `Member`가 현재 길드에 언제 참가했는지 보여준다. 참고로 `Member.display_name`은 채팅 등 디스코드 인터페이스에 보이는 길드 내 별명, `Member.name`은 사용자명, `Member.id`변하지 않는 사용자 고유 ID니, 적재적소에 활용할 수 있도록 하자.

### 3. 명령어 Exception 관리하기

Expand All @@ -136,16 +136,16 @@ async def multiply_error(ctx, error):
await ctx.send("오류: 정수 두 개를 입력해 주세요.")
```

확장 라이브러리의 장점이 여기 또 나온다. `Command`**exception** 넘겨받아 따로 error handling을 할 수 있는 건데, 위의 경우 argument가 정해진 type과 다르게 주어질 때 발생하는 `BadArgument`를 처리하고 있다.
확장 라이브러리의 장점이 여기 또 있다. `Command`**exception을 넘겨받아** 따로 error handling을 할 수 있는 건데, 위의 경우 argument가 정해진 type과 다르게 주어질 때 발생하는 `BadArgument`를 처리하고 있다.

> `Commands`에서 발생할 수 있는 exception 목록은 [여기](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html?error#exceptions)서 확인할 수 있다.
{: .prompt-info}

![](/assets/img/discord%20bot/2_4.png) | ![](/assets/img/discord%20bot/2_5.png)

정상적으로 int type이 주어졌을 때와 그렇지 않을 때 각자 다른 응답을 하는 것을 볼 수 있다.
정상적으로 정수가 주어졌을 때와 그렇지 않아 exception이 발생했을 때 각각 다른 응답을 하는 것을 볼 수 있다.

이외에도 parameter마다 설명을 추가하거나 생성된 리스트에서 변수를 정하는 식으로 오류를 원천 차단 하는 방법이 있지만 나중에 다른 명령어 시스템을 다룰 때 정리해 보겠다.
이외에도 parameter마다 설명을 추가하거나 생성된 리스트에서 변수를 정하는 식으로 오류를 원천 차단 하는 방법이 있지만 다음에 다른 명령어 시스템을 다룰 때 정리해 보겠다.

## 부록

Expand Down
Loading

0 comments on commit a4f779b

Please sign in to comment.