본문 바로가기
개발

디스코드 봇 만들기 #3

by wafla 2024. 8. 31.

멘션 기능 추가하기: 디스코드 봇을 활용한 알림 시스템 구현

이번 프로젝트에서는 정시가 될 때 해당 시간에 맞는 역할이 부여된 사람들에게 자동으로 알림 메시지를 보내는 디스코드 봇 기능을 구현해 보겠습니다. 이러한 기능은 커뮤니티에서 특정 시간에 활동을 독려하거나, 중요한 공지를 효과적으로 전달하는 데 유용하게 사용할 수 있습니다.

 

1. 시간 관련 모듈과 비동기 처리를 위한 모듈 추가

디스코드 봇을 만들 때, 정시에 맞춰 특정 작업을 수행해야 하므로 시간에 대한 정보를 정확하게 처리할 수 있는 모듈이 필요합니다. 이를 위해 datetime과 timedelta 모듈을 추가하여 현재 시간과 미래의 특정 시간을 계산할 수 있게 합니다.

 

또한, 프로그램이 시간을 기다리는 동안 다른 작업이 멈추지 않고 계속 수행될 수 있도록 하기 위해 비동기 프로그래밍이 필요합니다. 이를 위해 asyncio 모듈을 함께 임포트하여 비동기 처리를 구현합니다.

 

2. 봇의 멤버 정보를 가져오기 위한 설정

 

봇이 서버의 멤버들에 대한 정보를 가져오려면 적절한 권한이 필요합니다. 이를 위해 디스코드의 intents 기능을 사용하여 봇이 서버에서 멤버 정보를 접근할 수 있도록 설정합니다. 특히 intents.members를 True로 설정하여 봇이 서버의 모든 멤버에 대한 정보를 수집할 수 있도록 합니다.

import discord
import asyncio
from datetime import datetime, timedelta

intents = discord.Intents.default()
intents.members = True

bot = discord.Bot(intents=intents)

 

3. 정시 알림 기능 구현

 

정시마다 특정 역할이 부여된 멤버들에게 알림 메시지를 보내기 위해, 시간 확인 함수를 작성하여 백그라운드에서 실행되도록 합니다. 이를 위해 on_ready 이벤트 함수에 새로운 작업을 추가하는 코드를 작성합니다. on_ready 함수는 봇이 정상적으로 실행되었을 때 호출되며, 이 시점에 시간 확인 작업을 시작하도록 합니다.

@bot.event
async def on_ready():
    print(f"We have logged in as {bot.user}")
    bot.loop.create_task(check_hourly_roles())

 

4. 시간 확인 함수 작성

 

이제 정시에 알림을 보내는 기능을 구현하기 위해 check_hourly_roles() 함수를 작성합니다. 이 함수는 무한 루프를 돌면서 현재 시간과 다음 정시를 계산하고, 해당 시간이 되면 서버의 특정 역할을 가진 멤버들에게 메시지를 보냅니다.

async def check_hourly_roles():
    while True:
        now = datetime.now()
        next_hour = (now + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0) # 현재 시간에 1시간을 더하고 뒷 단위를 0으로 변경
        wait_time = (next_hour - now).total_seconds()
        await asyncio.sleep(wait_time) # wait_time 초만큼 기다림
        
        current_hour = datetime.now().strftime('%H') # 지금이 몇 시인지에 대한 정보를 str 형태로 가져옴
        role_name = current_hour.zfill(2) # 현재 시간을 역할 이름에 맞게 맞추기

        for guild in bot.guilds:
            role = discord.utils.get(guild.roles, name=role_name)
            if role:
                members_with_role = [member for member in guild.members if role in member.roles]
                if members_with_role:
                    mentions = ", ".join([member.mention for member in members_with_role])
                    channel = guild.system_channel
                    if channel:
                         await channel.send(f"10초만 책을 읽어봐요! {mentions}")

 

5. 비동기 함수와 역할 부여 기능의 결합

비동기 함수를 사용함으로써 시간을 기다리는 동안 프로그램이 멈춰있지 않고 다른 작업도 정상적으로 수행할 수 있게 됩니다. 이를 통해 정시에 맞춰 자동으로 알림을 보내는 기능을 안정적으로 구현할 수 있습니다.

 

6. 서버 설정 및 권한 관리

봇이 서버에서 멤버들에게 메시지를 보내거나 역할을 관리하기 위해서는 추가적인 권한 설정이 필요합니다. 이를 위해 봇이 서버에서 Manage Roles와 Mention Everyone 권한을 가지고 있어야 합니다. 이 권한이 없다면, 봇이 특정 역할을 부여하거나 멘션을 통해 알림을 보내는 작업이 제한될 수 있습니다.

(혹시 몰라 이전 포스트에 이어서 Manage Server를 추가했습니다.)

 

또한 intents 기능을 사용하기 위해 Bot에서 Privileged Gateway Intents 기능을 활성화해야 합니다.

Privileged Gateway Intents 활성화

 

7. 역할 부여 및 알림 메시지의 테스트

이제 봇이 정상적으로 작동하는지 테스트해 봅시다. 서버에서 특정 역할을 가진 멤버들에게 정시에 알림 메시지가 보내지는지 확인합니다. 역할을 추가하거나 제거하는 과정에서 봇이 적절하게 반응하는지도 확인해야 합니다.

역할도 제대로 추가해야합니다

 

8. 코드 작성 및 유지보수

코드 작성 과정에서 가장 중요한 부분은 check_hourly_roles() 함수였습니다. 이 함수는 정시에 맞춰 자동으로 알림을 보내는 핵심 기능을 담당합니다. 그러나 이 과정에서 intents.members = True 설정을 추가하지 않아 봇이 서버의 멤버 정보를 불러오지 못하는 문제가 발생할 수 있습니다. 이 설정이 빠져있으면, 봇이 특정 역할을 가진 멤버를 식별하지 못하고 알림을 보내지 못할 것입니다.

 

이러한 문제를 해결하기 위해 intents.members 설정을 추가하고, 봇의 권한을 올바르게 설정하여 문제를 해결할 수 있습니다. 또한, 초기에는 데이터베이스를 사용하여 서버나 멤버에 대한 정보를 저장해야 하나 싶었지만, 디스코드가 이러한 정보를 이미 관리하고 있기 때문에 추가적인 데이터베이스 구현은 필요하지 않았습니다.

 

9. 디스코드 봇 개발을 경험하며

이번 프로젝트를 통해 하루에 1~2시간씩 투자하여 간단한 디스코드 봇을 만들어보는 경험을 쌓을 수 있었습니다. 디스코드 봇을 만드는 데 필요한 도구와 라이브러리들이 잘 갖춰져 있어, 생각보다 쉽게 기능을 구현할 수 있었습니다. 많은 부분에서 GPT 모델의 도움을 받아 코드를 작성하고, 발생한 오류를 적절히 수정하여 원하는 기능을 성공적으로 구현할 수 있었습니다.

 

현재 GPT 모델은 주로 질문에 대한 답변을 제공하는 역할을 하지만, 앞으로는 직접 행동까지 수행할 수 있는 인공지능이 된다면 세상이 크게 변할 것입니다. 이러한 변화가 가져올 가능성은 무궁무진하지만, 동시에 새로운 도전에 직면하게 될 것입니다. 인공지능이 점점 더 많은 역할을 수행하게 되면서, 인간과 인공지능의 상호작용이 어떻게 변화할지 생각해보게 됩니다.

 

10. 디스코드 봇의 배포

봇을 완성했지만, 계속해서 컴퓨터를 켜놓고 봇을 실행시키는 것은 현실적으로 어려울 수 있습니다. 따라서 다음 단계로는 서버에 디스코드 봇 코드를 올려봅시다. 이를 통해 봇이 항상 온라인 상태를 유지하며, 정시에 알림 메시지를 보내는 기능을 안정적으로 수행할 수 있게 할 수 있습니다.

 

디스코드 봇을 서버에 배포하는 방법에는 여러 가지가 있지만, 가장 일반적으로 사용되는 방법 중 하나는 NAVER CLOUD나 AWS 같은 클라우드 플랫폼을 활용하는 것입니다. 이러한 플랫폼은 봇을 24시간 온라인 상태로 유지할 수 있도록 도와주며, 코드 업데이트도 쉽게 관리할 수 있습니다. 배포 과정에서 서버 설정과 관련된 추가적인 설정 작업이 필요할 수 있지만, 이 역시 잘 문서화된 가이드를 따라 진행하면 어렵지 않게 해결할 수 있습니다.

 

이렇게 해서 디스코드 봇을 개발하고 배포하는 과정을 마무리할 수 있습니다. 앞으로도 새로운 기능을 추가하거나 기존 기능을 개선하면서 봇을 점점 더 발전시켜 나갈 수 있습니다.

 

 

 

전체 코드

import discord
import asyncio
from datetime import datetime, timedelta

intents = discord.Intents.default()
intents.members = True

bot = discord.Bot(intents=intents)

@bot.event
async def on_ready():
    print(f"We have logged in as {bot.user}")
    bot.loop.create_task(check_hourly_roles())

@bot.slash_command()
async def hello(ctx):
    await ctx.respond("Hello!")

@bot.slash_command(name='시간', description="자신의 역할에 여러 시간을 추가합니다. 00부터 23까지 띄어쓰기로 구분해서 작성해주세요.")
async def 시간(ctx, 시간들 : str):
    await ctx.defer()  # 응답을 지연시킴

    시간들_set = set(시간들.split(" "))
    
    guild = ctx.guild
    member = ctx.author
    roles_to_add = []

    for 시간 in 시간들_set:
        if 시간.isdigit() and 0 <= int(시간) <= 23:
            role_name = 시간.zfill(2)  # '00', '01', ..., '23' 형식으로 맞춤
            role = discord.utils.get(guild.roles, name=role_name)
            if role is not None:
                roles_to_add.append(role)
            else:
                await ctx.respond(f"역할 '{role_name}'이(가) 존재하지 않습니다.")
        else:
            await ctx.respond(f"잘못된 입력: '{시간}'. 올바른 범위는 00부터 23까지입니다.")

    if roles_to_add:
        roles_to_add.sort(key=lambda role: role.name) # 역할.이름을 기준으로 정렬
        added_roles = ", ".join([role.name for role in roles_to_add]) # 정렬된 역할 이름 삽입
        await member.add_roles(*roles_to_add) # 사용자에게 역할 추가
        await ctx.followup.send(f"{member.name}님에게 '{added_roles}' 역할이 추가되었습니다.")
    else:
        await ctx.followup.send("추가할 역할이 없습니다.")

async def check_hourly_roles():
    while True:
        now = datetime.now()
        next_hour = (now + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0) # 현재 시간에 1시간을 더하고 뒷 단위를 0으로 변경
        wait_time = (next_hour - now).total_seconds()
        await asyncio.sleep(wait_time) # wait_time 초만큼 기다림
        
        current_hour = datetime.now().strftime('%H') # 지금이 몇 시인지에 대한 정보를 str 형태로 가져옴
        role_name = current_hour.zfill(2) # 현재 시간을 역할 이름에 맞게 맞추기

        for guild in bot.guilds:
            role = discord.utils.get(guild.roles, name=role_name)
            if role:
                members_with_role = [member for member in guild.members if role in member.roles]
                if members_with_role:
                    mentions = ", ".join([member.mention for member in members_with_role])
                    channel = guild.system_channel
                    if channel:
                         await channel.send(f"10초만 책을 읽어봐요! {mentions}")
        

bot.run("PUT_YOUR_BOT_TOKEN")

 

 

목차 -


디스코드 봇 만들기 #0
https://wafla.tistory.com/12
디스코드 봇 만들기 #1
https://wafla.tistory.com/13
디스코드 봇 만들기 #2
https://wafla.tistory.com/15
디스코드 봇 만들기 #3
https://wafla.tistory.com/16

디스코드 봇 만들기 #4

https://wafla.tistory.com/17

'개발' 카테고리의 다른 글

GPT로 디스코드 노래 봇 만들기  (1) 2024.09.26
디스코드 봇 만들기 #4  (0) 2024.09.05
디스코드 봇 만들기 #2  (0) 2024.08.29
디스코드 봇 만들기 #1  (0) 2024.08.27
디스코드 봇 만들기 #0  (0) 2024.08.27