블로깅 챌린지 운영기 : 디스코드 챗봇 v2.0 전체 코드

길하균(Lagun)·2023년 12월 17일

이전 : 디스코드 챗봇 v2.0 유저용 명령어 구현

지금까지 디스코드에서 블로깅 챌린지 운영을 위한 디스코드 챗봇을 만든 과정들을 기록하고 있습니다. v1.0에 대한 내용은 아래 포스팅을 참고해주시면 감사하겠습니다.

🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 구상하기
🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 v1.0 만들기

v2.0에 대한 포스팅은 아래를 참고해주시면 감사하겠습니다.

🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 v2.0 구상하기
🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 v2.0 함수 및 기타설정
🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 v2.0 유저용 명령어 구현
🔥 블로깅 챌린지 운영기 : 디스코드 챗봇 v2.0 관리자용 명령어 구현

직전 포스팅에서는 관리자가 챌린지 참가자들의 활동 log를 관리할 수 있는 명령어들 어떻게 구현했는지에 대해서 정리했습니다. 이번 포스팅에서는 디스코드 메시지에 반응을 남겼을 때 점수를 어떻게 기록하는지에 대해서 정리하고 전체 코드를 업로드하려 합니다. 포스팅과 관려내서 피드백 할 부분이 있으시다면 댓글이나 ghaguniv@gmail.com으로 메일 주시면 감사하겠습니다.

part X : 챗봇 v2.0 디스코드 반응 기록하기

저희 챌린지에서는 디스코드 메시지에 반응을 남기면 활동점수 1점을 획들할 수 있습니다. 이 기능은 on_raw_reaction_add를 이용해서 구현할 수 있습니다. 메시지에 반응이 달리는 이벤트를 트리거로 활설화되는 함수입니다.

# === 반응 점수 추가 ===
@client.event
async def on_raw_reaction_add(payload):
  id = str(payload.user_id)
  name = db['name_info'][id]

  now = datetime.datetime.now()
  week = str(int(now.strftime('%W')) - 36)
  today = now.strftime('%m.%d')

  act = '반응'
  url = payload.message_id

  data_log_pd = client.make_df()
  max_act = sum((data_log_pd['id'] == id) & (data_log_pd['week'] == week)
                & (data_log_pd['act'] == act))
  if max_act > 10:
    score = 0
    print(f'case 1 : {max_act}')
  else:
    score = client.send_score(act, week)
    print(f'case 2 : {max_act}')

  db['data_log'].append([week, today, id, name, act, url, score])

우선 이벤트가 발생하면 data_log DB에 들어갈 수 있도록 id, name, week, today, act, url 등을 추출합니다. 이때 url에는 그냥 참가자의 id를 넣도록 구상했습니다. 그 다음 메시지 반응은 1주일에 10개까지만 가능하기 때문에 max_act가 10을 초과하는 경우에는 score에 0을 넣도록 합니다. 그렇지 않은 경우에는 앞에서 만들었던 send_score함수를 통해서 점수를 받아옵니다. 이제 정리된 data를 data_log에 넣어주면 끝입니다.

Part XI : 챗봇 v2.0 전체코드

지금까지 챗봇 v2..0의 각 기능들에 대해서 정리하였습니다. 이번 파트에는 전체 코드를 업로드하였습니다. 비슷한 기능을 개발하시는 분들에게 조금이라도 도움이 된다면 좋겠네요. 지금까지 블로깅 챌린지 디스코드 챗봇 구현에 대해 읽어주셔서 감사합니다.

import discord
import numpy as np
import pandas as pd
from replit import db
import time
import datetime

from keep_alive import keep_alive

keep_alive()

TOKEN = 'your_token'
MAIN_CHANNEL_ID = 'yout_id'


class MyClient(discord.Client):
  # === pandas set ===
  # df 생략 없이 모두 출력
  pd.set_option('display.max_rows', None)
  pd.set_option('display.max_columns', None)

  # === 아래는 db 등록 과정 ===
  # 코드 최초 구동시에만 작동
  # db['name_info']['id']='name'
  # db['data_log']=[]

  # === set week, day ===
  now = datetime.datetime.now()
  week = now.strftime('%W')
  today = now.strftime('%m.%d')

  # === pandas data base ===
  # df를 미리 만들면 db 변경사항을 적용하지 못하므로 필요할 때 만들어서 사용 권장.
  def make_df(self):
    data_log_pd = pd.DataFrame(
        data=db['data_log'],
        columns=['week', 'date', 'id', 'name', 'act', 'url', 'score'])
    return data_log_pd

  # === score function ===
  def send_score(self, act, week):
    act_score = {
        '!등록': 10,
        '!게시물': 15,
        '!피드백': 15,
        '!회고록': 30,
        '!정보글': 5,
        '반응': 1
    }
    
    return act_score[act]

  # === message function ===
  def share_respond(self, message):
    icon_dict = {'!게시물': "📄", '!회고록': "📅", '!정보글': "📑"}

    id = str(message.author.id)
    name = db['name_info'][id]
    content = message.content

    if id not in db['name_info'].keys():
      return '📢 등록을 먼저 진행해주세요!'

    elif 'http' in content:
      now = datetime.datetime.now()
      week = str(int(now.strftime('%W')) - 36)
      today = now.strftime('%m.%d')

      act, url = content.split(" ")

      data_log_pd = self.make_df()
      max_act = sum((data_log_pd['id'] == id) & (data_log_pd['week'] == week)
                    & (data_log_pd['act'] == act))
      if max_act > 3:
        score = 0
      else:
        score = self.send_score(act, week)

      db['data_log'].append([week, today, id, name, act[1:-1], url, score])

      data_log_pd = self.make_df()  # 점수 반영된 db 불러오기
      total_score = data_log_pd[data_log_pd['id'] == id]['score'].sum()
      
      return f'{icon_dict[act]} {act[1:]} 공유! \n{name}님의 현재 점수는 {total_score}점 입니다.'

    else:
      return '✅ 명령어 뒤에 URL 주소를 같이 보내주세요.'

  # === chat-bot 상태 설정 ===
  async def on_ready(self):
    print('Logged on as {0}!'.format(self.user))
    await self.change_presence(status=discord.Status.online,
                               activity=discord.Game("🔥🔥🔥"))

  # === 채팅 설정 ===
  async def on_message(self, message):
    id = str(message.author.id)
    content = message.content

    # === 채팅창 응답 - 일반 회원용 ===
    if '!명령어' in content:
      await message.channel.send(
          '!뒤에 원하시는 명령어를 입력해주세요. \n 사용 가능한 명령어 : 등록, 게시물, 회고록, 정보글, 점수, 기록, 전체등수'
      )

    if '!등록' in content:
      if (id not in db["name_info"].keys()) & ('http' in content):
        name = message.author.global_name
        db["name_info"][id] = name

        now = datetime.datetime.now()
        week = str(int(now.strftime('%W')) - 36)
        today = now.strftime('%m.%d')

        act, url = content.split(" ")

        score = self.send_score(act, week)

        db['data_log'].append([week, today, id, name, act[1:], url, score])

        await message.channel.send(f'{name}님 환영합니다!🤗')
      elif 'http' not in content:
        await message.channel.send('✅ 명령어 뒤에 URL 주소를 같이 보내주세요.')

    if '!게시물' in content:
      await message.channel.send((self.share_respond(message)))

    if '!회고록' in content:
      await message.channel.send((self.share_respond(message)))

    if '!정보글' in content:
      await message.channel.send((self.share_respond(message)))

    if '!피드백' in content:
      act, name_list = content.split(' ')
      name_list = name_list.split(',')

      now = datetime.datetime.now()
      week = str(int(now.strftime('%W')) - 36)
      today = now.strftime('%m.%d')

      tmp_dict = {}
      for item in db["name_info"].items():
        tmp_dict[item[1]] = item[0]

      score = self.send_score(act, week)
      act = act[1:]
      url = 'None'
      for name in name_list:
        id = tmp_dict[name]
        db['data_log'].append([week, today, id, name, act, url, score])

      await message.channel.send(f'📢  {week}주차 블로깅 피드백 참가자 : {name_list}')

    if '!점수' in content:
      name = db['name_info'][id]

      data_log_pd = self.make_df()
      total_score = data_log_pd[data_log_pd['id'] == id]['score'].sum()
      rank_pd = data_log_pd.groupby('name')['score'].sum().sort_values(
          ascending=False)
      rank = list(rank_pd.index).index(name) + 1
      await message.channel.send(f'{name}님의 점수는 {total_score}점으로 {rank}등 입니다.')

    if '!기록' in content:
      name = db['name_info'][id]
      act_list = ['등록', '게시', '피드백', '회고', '정보', '반응']

      data_log_pd = self.make_df()
      history = data_log_pd[data_log_pd['id'] == id].groupby(['act']).count()

      output_str = f'{name}님의 현재까지 기록은\n\n'
      for act in act_list:
        try:
          cnt = history.loc[act]['score']
          output_str += f'{act} : {cnt}회\n'

        except Exception as ex:
          output_str += f'{act} : 0회\n'
      output_str += f'\n입니다! 화이팅  😀😀😀'
      await message.channel.send(output_str)

    if '!전체등수' in content:
      data_log_pd = self.make_df()
      rank_pd = data_log_pd.groupby('name')['score'].sum().sort_values(
          ascending=False)

      output_str = f'현재 전체등수는\n\n'
      for name in rank_pd.index:
        rank = list(rank_pd.index).index(name) + 1
        total_score = data_log_pd[data_log_pd['name'] == name]['score'].sum()

        output_str += f'{rank}등 : {total_score}점, {name}님\n'
      output_str += f'\n입니다! 다들 화이팅  😀😀😀'
      await message.channel.send(output_str)

    # === 이름 DB 관리 ===
    if '!이름' in content:
      for key in db["name_info"].keys():
        await message.channel.send(f'{key} / {db["name_info"][key]}')

    if '!이름수정' in content:
      com, name, id = content.split(" ")
      db["name_info"][id] = name

    if '!이름삭제' in content:
      com, name = content.split(" ")
      del db["name_info"][name]

    # === Log DB 관리 ===
    if '!data_log' in content:
      data_log_pd = self.make_df()
      for i in range(len(data_log_pd)):
        await message.channel.send(f'{i} : {list(data_log_pd.iloc[i])}')

    if '!add_data' in content:
      tmp_list = content.split(" ")
      tmp_list = tmp_list[1].split(',')
      tmp_list[-1] = int(tmp_list[-1])

      db['data_log'].append(tmp_list)

      data_log_pd = self.make_df()
      for i in range(len(data_log_pd) - 3, len(data_log_pd)):
        await message.channel.send(f'{i} : {list(data_log_pd.iloc[i])}')

    if '!del_data' in content:
      index_num = int(content.split(" ")[1])

      del db['data_log'][index_num]

      data_log_pd = self.make_df()
      for i in range(len(data_log_pd) - 3, len(data_log_pd)):
        await message.channel.send(f'{i} : {list(data_log_pd.iloc[i])}')

    if '!fix_data' in content:
      index_num = int(content.split(" ")[1])

      tmp_list = content.split(" ")
      tmp_list = tmp_list[2].split(',')
      tmp_list[-1] = int(tmp_list[-1])
      db['data_log'][index_num] = tmp_list

      data_log_pd = self.make_df()
      await message.channel.send(
          f'{index_num} : {list(data_log_pd.iloc[index_num])}')


intents = discord.Intents.default()
intents.message_content = True
client = MyClient(intents=intents)


# === 반응 점수 추가 ===
@client.event
async def on_raw_reaction_add(payload):
  id = str(payload.user_id)
  name = db['name_info'][id]

  now = datetime.datetime.now()
  week = str(int(now.strftime('%W')) - 36)
  today = now.strftime('%m.%d')

  act = '반응'
  url = payload.message_id

  data_log_pd = client.make_df()
  max_act = sum((data_log_pd['id'] == id) & (data_log_pd['week'] == week)
                & (data_log_pd['act'] == act))
                
  if max_act > 10:
    score = 0
    print(f'case 1 : {max_act}')
  else:
    score = client.send_score(act, week)
    print(f'case 2 : {max_act}')

  db['data_log'].append([week, today, id, name, act, url, score])

client.run(TOKEN)
profile
AI, 빅데이터를 배우기 위해 항해 중

0개의 댓글