디스코드 대본봇 개발기

junah201·2022년 7월 19일
11
post-thumbnail

디스코드 대본봇 개발을 시작하게 되었어요

지인분께서 성우 지망생(?)이신데 대본을 리딩할 때 대본을 고르고 할 때 필요한 디코 봇 개발을 요청하셔서 만들기 시작하게 되었어요.

원래 같은 기능을 하는 디코 봇이 있었는데 이 봇의 소스를 보니까

다음과 같이 DB도 없이 단순히 하드코딩된 부분이 너무 답답하고 새로 대본을 추가할 때 제 지인분을 통해서 직접 소스 코드를 수정해서 넣어야 하는 단점 등이 있어서 원래 봇을 단순 개조만 하려고 하였으나 아예 새로 만들려고 마음먹었다.


슬래시 명령어요...?

버튼 사용을 위해서는 슬래시 명령어를 사용해야 한다는 것을 알고 슬래시 명령어를 사용하기 위한 뻘짓이 시작되었다.

1. discord_slash 모듈

전에 슬래시 명령어가 처음 나왔을 때는 discord_slash 모듈을 사용하는 코드를 몇 번 봤기 때문에 discord_slash 모듈을 사용하려고 찾아보았으나 discord_slash 모듈을 설치하고 따로 import 하고 slash 오브젝트를 새로 만들고 하는 일련의 과정이 매우 비효율적으로 느껴져 discord.py가 지원 중단된 후에 포그본들을 찾아보게 되었다.

2. pycord 모듈

discord.py의 포크본 중에서 pycord, nextcord 등의 모듈 중 주변 지인들이 pycord를 추천해서 pycord로 시작해보려고 하였다.
또한 전에 pycord가 처음 나왔을 때 슬래시 명령어 관련해서 잠깐 끄적거렸던 소스 코드를 찾으려고 하였으나 C, D, E 드라이브를 모두 찾아보았지만 찾지 못했다 ㅠㅠ (아마 바탕화면에 저장해두어서 예전에 윈도우 포맷할 때 날아간 것 같아요 ㅠㅠ).

이후 pycord에서 슬래시 명령어를 사용하는 방법을 구글링 하다가 discord.py, pycord, nextcord 등의 모듈 검색 결과가 같이 나오는 것이 구글링에서 매우 스트레스받았고, discord.py 개발자분께서 개발중단을 번복하고서 다시 개발을 시작하였다는 이야기를 듣고 discord.py 2.0.0a 버전을 이용해서 개발하기로 마음먹었다.

3. discord.py 2.0.0a

discord.py가 슬래시 명령어 지원한다는 소리를 듣고 관련 docs를 찾아보았지만 아무리 docs를 찾아보아도 관련 자료가 없었고, 결국 구글링을 해보니 docs의 stable 탭이 아니라 latest 탭에 interaction API가 있는 것을 찾아서 이때부터 본격적인 개발이 시작되었다.

이것을 찾기 전까지 수많은 구글링과 뻘짓이 있었다... nextcord의 방식을 가져다 써놓고서는 왜 안 되지 하기도 하고.. discord.py 2.0.0a 버전이 아닌 과거 버전을 설치하고서 왜 안 되지 하기도 하고...


DB가 필요할까...?

내 생각에는 대본을 모두 합쳐도 기존 봇에는 500개 정도밖에 없어서 전문적인 SQL 등을 사용하면 제 지인이 직접 서버에 업로드하고 관리하게 될 때 DB를 직접 수정할 일이 생기면 수정할 수 없다는 판단하에 직관적으로 값을 수정할 수 있고 메모장으로도 열 수 있는 JSON 파일을 DB로 사용하게 되었다.

사실 가장 큰 이유는 다른 언어에서는 사용해본 적 있지만 Python에는 한 번도 SQL을 사용해본 적 없기 때문에 새로 모듈을 설치하고, 모듈 사용법을 익히는 일련의 과정이 매우 귀찮았다.

그리고 이 결정은 아직도 후회하고 있다.
SQL 쿼리에서 where 구문... 너무 쓰고 싶다...


개발기

개발 중간에 구현이 어려웠거나 버그로 고생했던 부분을 기능별로 정리해보았다.

대본목록

1. 성비가 공통인 대본이 따로 검색해서 나오지 않고 같이 나오도록 하는 기능

2남 2녀의 대본 목록을 찾고 싶을 때 2남 1녀 1공통인 대본도 같이 출력되는 것

기존에는 2남1녀1공통인 대본은 따로 찾아야 해서 공통인 대본은 손이 덜 가서 항상 하는 대본만 하게 되는 문제점이 있었다.

X남Y녀 의 대본 결과를 찾을려고 할 때 A남B녀C공통 인 대본에서

A <= X and B <= Y and A + B + C == X + Y

위 조건문을 이용해서 해결하였다.

2. 대본종류 (드라마, 영화 등)을 기준으로 나누어서 출력되도록 하는 기능

2남2녀의 대본을 찾을려고 할 때 드라마 영화 애니 등의 타입을 선택해서 결과가 따로 볼 수 있도록 구현

이 간단한 부분이 많은 고민은 유발했는데 가장 큰 이유는 SQL에서는 where을 통해 따로 불러올 수 있지만 JSON에서는 최대한 효율적으로 DB 구성을 하고 싶었다.

# Script.json

{
	"대본ID" : {
    	"name" : "대본명",
        "type" : "대본종류 (영화, 드라마, 애니 등)",
        "gender" : "대본성비 (1남2녀1공 등)"
    	"link" : "대본링크"
    }
}

이에 따라 추가한 순서대로 각 대본에 1부터의 숫자를 가지는 ID값을 부여하고, DB/Script 폴더 안에 위와 같이 저장하였다.

# A남B녀C공.json

{
	"대본종류 (영화, 드라마, 애니 등)" : {
    	"대본ID" : {
        	"name": "대본명",
            "link": "대본링크",
            "type": {
                "name": "대본성비 (1남2녀1공 등)",
                "남": A,
                "여": B,
                "공": C
            },
            "rating": 대본 평점의 합,
            "rating_users": 대본을 평가한 사람 수,
            "adder": "추가자",
            "adder_id": 추가자ID,
            "time": "추가 시점"
        }
    }
}

또한 이 대본의 구체적인 정보를 A남B녀C공.json의 파일에 대본종류를 KEY값으로 삼아 저장하여 대본 목록을 출력할 때 가장 빠르게 출력할 수 있도록 노력하였다.

3. 대본의 idx가 같이 움직이는 버그

1남1녀와 2남2녀의 대본을 출력하고 1남2녀의 idx를 3까지 바꾼 후, 2남2녀의 대본의 다음칸 버튼을 누르면 idx가 2가 아니라 4로 바뀌는 버그가 있었다.

Scripts[id] = {
    "idx": idx,
    "max_idx": max_idx,
    "man": man,
    "woman": woman,
	"add_time": datetime.datetime.now()
}

이것은 idx를 전역으로 관리해서 생기는 버그 였는데, 이것을 전역에 dict 타입의 변수를 만들고 이곳에 시간과 채널ID를 KEY 값으로 해서 idx와 관련 정보를 저장하여 구현하였다.

대본평가

1. 한 사람이 한 대본에 한번만 평가할 수 있는 기능

한 대본에 한사람이 1부터 5점까지의 정수 값으로 평가할 수 있고, 다시 평가하면 기존 평가에 추가로 되는 것이 아니라 기존 평가를 덮어씌워지는 것을 구현하려고 하였다.

단순해보이는 이것을 구현하기 위해서는 각 유저마다 대본에 대한 평가를 기록해두어야 했는데 이 부분이 유저에 대한 데이터 파일을 만들게 되는 계기가 되었다.

이후 DB/User 폴더에 user.json 파일을 만들고

# user.json

{
	"유저ID" : {
    	"name" : "유저명",
        "grade" : 좋아요 수,
        ...
        "review" : {
        	"대본ID" : 점수
        }
    }
}

위와 같은 형식으로 값을 저장함으로써 구현하였다.

2. 대본의 점수를 저장하는 방식

처음에는 단순히 대본의 점수를 하나의 변수로만 저장하면 된다고 판단하였었는데
여러사람의 평가의 평균을 출력하기 위해서
모든 사람의 평가의 합계를 저장하고 인원수를 저장함으로써 합계를 인원수로 나눈 값을 출력하는 방식을 사용하였다.

3. 대본의 점수를 하트로 표현

{''.join(['❤️' for i in range(round(datas[key]['rating'] // datas[key]['rating_users']))])}
{''.join(['🤍' for i in range(round(5 - (datas[key]['rating'] // datas[key]['rating_users'])))])}

이것은 어려운 부분은 아니였지만 최대한 한줄로 적어보려했던 노력이 기억나서 적어보았다. 괄호에 색깔을 넣는 확장을 포맷하고 안깔아두어서 괄호에서 계속 빨간줄이 떠서 어려웠다.

단문

1. 단문 예약이 채널별로 따로 작동하도록 하는 기능

대본의 idx가 같이 움직이는 버그 와 같은 방식으로 구현하였는데

short_script[채널ID] = {
	"channel": 채널 오브젝트,
	"last_member": 유저 오브젝트,
	"members": [],
	last_time": 현재 시간
}

전역 dict 타입의 변수를 이용해서 관련 정보를 저장함으로써 채널ID를 KEY 값으로 해서 idx와 관련 정보를 저장하여 구현하였다.

유저

1. 모바일에서 맨션시 string이 다르게 입력되는 문제

모바일에서 유저를 맨션하면 자체적으로 <@유저ID> 의 형태를 띄면 자동으로 맨션이 되지만
PC에서는 유저를 맨션하면 자체적으로 <@!유저ID>의 형태로 자동으로 맨션이 되기 때문에
두 플랫폼에서의 맨션이 달라서 생기는 문제가 있었다.

기존에는 단순히 문자열을 자름으로써 ID값만 잘라냈는데

re.sub(r'[^0-9]', '', 입력값)

위와 같은 방식으로 파싱하는 방법을 변경함으로써 수정하였다.

기존 슬래시 명령어가 아닌 discord.py 명령어는 타입 힌트로 유저 맨션 타입을 주면 자동으로 유저 타입으로 받아져서 이러한 문제가 있는 줄 몰랐는데 이번에 새로 알게 되었다.


후기

개발 후기

이렇게 대본 봇 개발이 무사히 완료되었다. 처음 시작할 때는 슬래시 명령어가 어려울 것이라고 걱정했었지만, 예전에 command 처럼 부드럽게 구현되어서 좋았다. 물론 작은 사이즈의 디코 봇이라서 문제가 생길 소지가 적었지만...

과거에 디스코드 봇을 처음 만들었을때 우매함의 봉우리에 있었던 기억이 나서 디스코드 봇을 만드는 것을 멀리 했던 것 같은데 오랜만에 다시 디스코드 봇을 만드니 다른 개발과 다르게 UI 편집 요소가 적어서 쉽게 만들 수 있고, 간단한 코드로 많은 기능을 만드는 것 같다고 느낌을 주는 것이 좋았다.

git 사용 후기

과거에 개발을 할 때 git을 사용을 안해서 버전 관리도 안되고 새로운 기능을 만들려다가 전체 코드가 작동안하는 대참사가 많이 생겼었기 때문에 이번에는 git을 사용하려고 노력하였다.

git GUI를 이용해서 git 관리를 하였는데 github desktop, 소스 트리, GitKraken 중에서 GitKraken이 가장 직관적이여서 이것을 선택하게 되었다.

중간에 대본 기능을 개발할 때 몇번이나 이전 커밋으로 초기화 하면서 git의 효율성을 체감하게 되었다.

-끝-


전체 소스 보기

Github : https://github.com/junah201/discord_script_bot

profile
개발자가 되고 싶은 고딩

1개의 댓글

comment-user-thumbnail
2022년 7월 19일

아, 이해하기 쉽고 글을 재밌게 쓰시네요. 완전히 이해했습니다! ( <- 전혀 이해하지 못했다)

답글 달기