CodeDog : 동아리 디코 봇 프로젝트 시작 !

Jiwon Lee·2023년 4월 5일
0

CodeDog 제작기

목록 보기
1/2

CodeDog 프로젝트 동기

우리 동아리는 멋쟁이사자처럼 고대세종 지부이자 ,,, 교내 IT 창업 동아리인데 고질적인 소통, 네트워킹 문제를 해결하고자 올해부터 모든 온라인 소통은 디스코드로 통일하기로 했다. 그 외에도 여러가지 개선이 생기기도 했는데 디스코드를 사용하다보니 워낙 다른 서버들에 좋은 봇들이 많아서 뭔가... 우리도 뭔가... 더 편하게 쓸 수 있지 않을까 싶은 부분이 너무 많았ㄷㅏ.

그래서 백엔드 리드 분이랑 같이 개발을 하기로 했는데 학기 중이라 너무 바빠서 진척이 느리게 되고 있따 ... 그래도 올해 내로는 완성 되겟지 뭐 ^^ ; 이름은 우리 동아리 이름인 언더독레볼루션을 따서 코드독으로 하기로 했다! 호호


첫 번째 기능, 익명 질문 기능 만들기

기능을 제작하게 된 이유

스터디, 동아리 멘토링 등등을 하면서 항상 느끼는 건데 사람들이 정말 질문하는 걸 너무너무너무너무 무서워한다 ... 나는 대학교 수업 들으면서도 맨 앞자리 앉아서 궁금한 거 생기면 손들고 질문하는 스타일이라 정말 이해가 안 되지만 더 좋은, 더 도움되는 동아리로 만들고자 많은 고민을 해왔다. 우선 사람들은 아래 경우일 때 더 질문을 많이 한다.

  1. 1:1 과외/멘토링 중일 때 -> 멘토에 비해서 멘티 인원이 소수일수록 질문 많이 함
  2. 익명 질문을 할 수 있을 때 -> 비슷한 느낌으로 대면 < 비대면 질문이 쉬운 것 같다.
  3. 질문을 하는 사람들이 많이 있을 때
  4. 질문에 대한 답변이 정말 간절할 때 ;;

그래서 우선 멘토링은 인당 4~5명 단위로 끊어냈지만 나는 개인적으로 오픈되고, 질문을 쉽게 할 수 있는 분위기 자체가 생겼으면 좋겠다는 생각이 많았다. 다른 동아리에서도 질문방 항상 파두는데 아무도 질문을 안 함 ㅇㅅㅇ ... ㅠㅠ

그래서 어떻게 할까 하다가 카톡 오픈채팅을 파야되나~ 했는데 디스코드로 통일하기로 한 상황에서 카카오톡을 사용하고 싶지 않았고, 질문 답변해주는 입장에서도 귀찮고 ... 무엇보다 가장 큰 문제점은 늦게 들어온 사람은 이전의 질문이 보이지 않는다는 점이다 ... 리크루팅 기간에 그거 때문에 열받아서 죽는 줄 알았다.

그러다가 생각난 게 로스트아크 통합 디스코드방의 떠상 제보 기능인데 ... 이런 걸로 하면 익명 질문으로 대신 포럼 글도 올려줄 수 있지 않을까? 하는 생각이 나서 바로 착수 !!

discord.py 개발 꿀팁

백엔드 리드 분이 파이썬이 익숙해서 discord.py로 시작했는데 자료가 생각보다 없어서 좀 당황스러웠다. 그래서 ChatGPT의 도움을 많이 받았다 ... 고마워요 Chat GPT ~~

근데 챗지피티가 사실 바보같은 짓을 많이해서 챗지피티로 대충 어케하는지 물어보고서 API reference에서 하나하나 속성과 메서드를 둘러보며 만들었다. 많은 도움이 됐음! 내가 본 API 레퍼런스는 예시 같은 건 안 나와서 ChatGPT가 많이 도움이 됐고, 이 깃허브도 봤다.

https://guide.pycord.dev/introduction 에서는 예시 느낌의 document를 볼 수 있는 듯 ?? 근데 나는 안 봐써 ... 다음 기능 만들면서는 봐야겠다 ...

익명 질문 기능 만들기

먼저 이 아래는 기본적으로 봇 생성을 위한 코드라서 따로 빼둿다... 사실 그냥 생략하고 주요한 부분만 올릴까 했는데 솔직히 저는 개발하면서 discord.py 자료 찾기 너무 힘들었기 때문에 ㅠㅠ;; 그냥 올려두겟슴니다

import asyncio
import discord
from discord.ext import commands
from discord.ui import Select, View
from discord import SelectOption
 

bot = commands.Bot(command_prefix='/', intents=discord.Intents.all())

token = "본인 봇 토큰 넣으시면 됩니다"

@bot.event
async def on_ready(): # 이 함수가 끝나기 전에 다른 함수를 호출할 수 있도록 비동기 실행 
    print(f'{bot.user.name} 연결 성공')
    await bot.change_presence(status=discord.Status.online, activity=None)

먼저 우리 디스코드 서버의 경우엔 질문 게시판이 포럼 형태로 되어 있고, 태그를 무조건! 달아줘야 한다. 그래서 discord.ui의 Select를 사용해서 태그 선택 기능을 만들어줬다.

# tag 선택을 위한 view
class TagView(View):
    def __init__(self, tags, ctx):
        super().__init__()

        options = []
        for i in tags :
            options.append(SelectOption(label=(i.name), value=i.name))

        self.select = Select(
            placeholder="질문 카테고리를 선택해주세요.",
            options=options
        )
        self.add_item(self.select)

        def check(msg): 
            return msg.author == ctx.author and msg.channel == ctx.

대충 요로코롬 생겼는데, 문제는 이제 태그 선택 후에 이것저것 물어볼 게 많다 ... 그래서 일단 callback 메소드에 다 때려박았는데 이 부분이 사실 좀 아쉽다. 코드가 너무 더럽자녀!!! .... 나중에 시험 끝나고나면 수정하든가 해야할 것 같다 ㄱ- 그래서 아래처럼 TagView 클래스 안에 콜백 메서드를 만들어서 질문의 제목과 내용을 마저 입력받아따. 내용을 계속 유지해야 되기 때문에 ctx를 계속 공유하면서 저장해뒀었는데... 어라... 리액트의 데자뷰가...

그리고 내용 확인용으로 임베드를 생성해서 이거 맞냐고 물어보고 버튼 ui로 확인까지 눌러야 업로드 되도록 했당... 근데 버튼 만들면서 이상하게 뻑이 많이 나서 아직도 글 잘올리고서 상호작용 실패가 뜬다... 왠지 아시는분?................... ㅠㅠㅠㅠㅠ

async def callback(interaction):
            ctx.bot.selected_tag = interaction.data['values'][0]
            await interaction.response.send_message(content=f"현재 선택된 질문 카테고리 `{ ctx.bot.selected_tag }`로 익명 질문을 진행하시려면 3분 내로 `질문제목`을 입력해주세요. 질문 작성을 원하지 않으실 경우 `!취소`를 입력해주세요.")
            
            # 질문 제목 입력받기 
            try: 
                title = await bot.wait_for("message", check=check, timeout=180.0)
                title = title.content

                if title == "!취소" :
                    await ctx.send("프로세스가 종료됩니다. 익명 질문을 남기시려면 다시 `/익명질문` 명령어를 사용해주세요.")
                    return
                
                ctx.bot.qTitle = title
                
            except asyncio.TimeoutError:
                await ctx.send("3분이 경과되어 익명 질문 프로세스를 종료합니다.")
                return
            
            # 질문 내용 입력받기 
            await ctx.send(f"익명 질문을 계속해서 작성하시려면 5분 내로 `/내용 질문내용` 명령어를 사용해서 질문의 내용을 입력해주세요. 질문 작성을 원하지 않으실 경우 `!취소`를 입력해주세요.")
            try: 
                content = await bot.wait_for("message", check=check, timeout=300.0)
                content = content.content

                if content == "!취소" :
                    await ctx.send("프로세스가 종료됩니다. 익명 질문을 남기시려면 다시 `/익명질문` 명령어를 사용해주세요.")
                    return
                
                ctx.bot.qContent = content
                
            except asyncio.TimeoutError:
                await ctx.send("3분이 경과되어 익명 질문 프로세스를 종료합니다.")
                return
            

            # 내용 확인용 임베드 
            embed = discord.Embed(title = f"{ctx.bot.qTitle}",
            description = f"{ctx.bot.qContent}", color = 0x62c1cc)
            embed.add_field(name = "카테고리", value = f"{ctx.bot.selected_tag}")
            embed.set_footer(text = "익명 질문")

            # 확인 / 취소 버튼 
            class ButtonView(View):
                def __init__(self, *, timeout=180):
                    super().__init__(timeout=timeout)
                    ctx.bot.isPublished = False;

                @discord.ui.button(label="확인", style=discord.ButtonStyle.green)
                async def submit_button(self, button:discord.ui.Button, interaction:discord.Interaction):
                    if ctx.bot.isPublished : # 이미 작성되었을 경우 버튼 작동 X
                        return
                    
                    qChannel = discord.utils.get(bot.guilds[0].channels, name="포럼-테스트용")

                    await ctx.send('익명 질문이 업로드 됩니다.')
                    applied_tags = list(filter(lambda tag : tag.name == ctx.bot.selected_tag, tags))
                    await qChannel.create_thread(name=ctx.bot.qTitle, content=ctx.bot.qContent, applied_tags=applied_tags) 
                    ctx.bot.isPublished = True
                    del self
                    return

                @discord.ui.button(label="취소",style=discord.ButtonStyle.gray)
                async def cancel_button(self, button:discord.ui.Button, interaction:discord.Interaction):
                    await ctx.send('프로세스가 종료됩니다. 익명 질문을 남기시려면 다시 `/익명질문` 명령어를 사용해주세요.')
                    return
                
            buttons = ButtonView()

            await ctx.send("아래 내용으로 질문을 올리시려면 확인 버튼을 눌러주시고, 원하지 않으실 경우 취소 버튼을 눌러주세요.", embed=embed, view=buttons)

        self.select.callback = callback # 초기화 후 등록해줘야 함 

그리고 마지막으로 질문 명령어를 받는 방식 ... 샤이한 분들을 위해 DM으로만 질문 남길 수 있도록 했다 ^^ ;

# 익명 질문 기능 
@bot.command(aliases=['익명질문', '질문'])
async def anony_question(ctx):
    # DM을 받은 경우가 아니면 return 
    if not isinstance(ctx.channel, discord.DMChannel):
        return
    
    # 질문 채널 정보 가져오기 
    qChannel = discord.utils.get(bot.guilds[0].channels, name="포럼-테스트용")
    tags = qChannel.available_tags

    # 작성자 정보 가져오기 
    author_id = ctx.author.id
    user = await bot.fetch_user(author_id) 

    # 익명 질문을 위한 태그 input 메세지 내보내기
    await user.send(view=TagView(tags, ctx))


bot.run(token)

프로필 기능도 마무리되고 나면 프로젝트 새로 파서 파일 분리도 하고 다시 배포해서 동아리 디코방에서 본격적으로 운영해봐야 할 것 같다! 와쿠오ㅏ쿠.. 그러나 우리를 기다리는 중간고사 🤯

profile
노는 게 제일 좋은데 공부는 하고 싶어요 😗

0개의 댓글