카테고리: 디스코드 봇 만들기

디스코드 봇 만들기#21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제)

목차12개 항목

안녕하세요! Mojangcam입니다 😎

지난 #20편에서는 새 멤버가 들어오면 환영 카드를 띄우고, 규칙 동의 버튼으로 자동 역할 부여까지 되게 만들었죠. 사람 손 없이 신규 유저를 받는 문이 생긴 거예요 🚪

그럼 문 안쪽은요? 오늘은 채팅방을 지키는 모듈을 만듭니다. 봇이 채팅을 읽고 있다가 특정 키워드에 자동으로 답해주고, 광고·욕설은 바로 지우고 경고를 누적해서 일정 횟수가 넘으면 타임아웃까지 먹이는 cogs/automod.py예요 🛡️

이번 편에서 배울 것

  • 모든 채팅을 읽는 on_message 이벤트와 처리 순서
  • 봇이 자기 말에 자기가 반응하는 무한루프 막는 법
  • 정규표현식(re)으로 키워드·금지어 매칭하기
  • 바 보, 바.보 같은 우회 표현까지 잡는 정규화(normalize)
  • 메시지 자동 삭제 + 경고 누적 저장 (JSON)
  • 경고 3회 → 타임아웃(Timeout) 자동 부여

서버 규모가 커지면 관리자가 24시간 채팅을 볼 수 없어요. 새벽 3시에 광고봇이 초대 링크를 뿌려도, 이 모듈이 있으면 3초 만에 지워집니다 🌙

🧠 왜 직접 만들까요? 디스코드 기본 자동조정도 있는데

맞아요. 디스코드에는 AutoMod(자동 조정)라는 기본 기능이 있어요. 단순 금지어 차단만 필요하면 사실 그걸 켜는 게 제일 편합니다.

그런데 직접 만들면 이런 게 가능해져요.

  • 조건부 대응 — "이 채널에서만", "역할 없는 사람만", "가입 3일 미만만" 같은 우리 서버 규칙
  • 경고 누적 관리 — 몇 번째 위반인지 세고, 횟수별로 다른 처벌을 주기
  • 자동응답과 한 몸 — 같은 on_message 안에서 FAQ 답변까지 처리
  • 로그 남기기 — 누가 뭘 썼는지 관리자 채널에 기록 (#24 로그 편으로 이어져요)

무엇보다 이 모듈은 #27 최종 프로젝트의 자동 모더레이션 부품이라서, 나중에 DB(#23)와 로그(#24)를 붙이면 훨씬 강해집니다 💪

📌 준비물

  • Python 3.8+
  • discord.py 2.x (2026년 8월 기준 최신은 2.7.1)
  • python-dotenv — ID·설정값을 코드에 박지 않기 위해 필수!
  • #19편에서 만든 server-bot/ 폴더 (오늘도 파일 하나만 추가해요)
  • 정규표현식이 처음이라면 파이썬 정규표현식(re) 입문 글을 먼저 보고 오시면 훨씬 쉬워요 📖
터미널
pip install -U discord.py python-dotenv

버전 확인은 이렇게 해요.

터미널
python -c "import discord; print(discord.__version__)"

.env 파일에 오늘 쓸 값을 추가합니다.

텍스트
DISCORD_TOKEN=여기에_봇_토큰_붙여넣기
AUTOMOD_LOG_CHANNEL_ID=제재_기록을_남길_채널_ID
AUTOMOD_WARN_LIMIT=3
AUTOMOD_MUTE_MINUTES=10

💡 .env는 꼭 .gitignore에 넣으세요! 깃허브에 토큰이 올라가면 디스코드가 자동 감지해서 바로 폐기해버립니다.

그리고 인텐트 하나가 더 필요해요. 채팅 내용을 읽어야 하니까요.

Python
intents.message_content = True    # ← 오늘 주인공!

디스코드 개발자 포털 → 내 앱 → Bot → Privileged Gateway Intents → MESSAGE CONTENT INTENT도 켜져 있어야 해요. #19편에서 이미 켜뒀다면 그대로 두시면 됩니다.

📌 1단계 - on_message와 무한루프 방지

on_message는 봇이 볼 수 있는 모든 채널의 모든 메시지마다 불립니다. 그래서 제일 먼저 할 일은 거를 것부터 거르기예요.

Python
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
    if self.is_exempt(message):
        return
    ...

def is_exempt(self, message: discord.Message) -> bool:
    if message.author.bot or message.webhook_id:
        return True
    if not isinstance(message.author, discord.Member):
        return True
    return message.author.guild_permissions.manage_messages

하나씩 뜯어볼게요 💡

  • message.author.bot : 오늘 글에서 제일 중요한 한 줄이에요. 봇이 자동응답을 보내면 그 메시지도 다시 on_message를 부르거든요. 이 체크가 없으면 봇이 자기 말에 자기가 답하는 무한루프에 빠집니다 🔁 (레이트리밋 먹고 봇이 끊겨요)
  • message.webhook_id : 웹훅으로 들어온 메시지예요. 자동화 #4편에서 만든 알림 웹훅이 금지어에 걸려 지워지면 곤란하죠 😅
  • isinstance(..., discord.Member) : DM에서 온 메시지는 작성자가 Member가 아니라 User라서 guild_permissions가 없어요. 여기서 안 걸러주면 AttributeError가 납니다
  • manage_messages : 관리자·모더레이터는 면제예요. 관리자가 "이런 말 쓰지 마세요"라고 예시를 들었다가 경고 먹으면 웃기잖아요 🤣

⚠️ Cog 리스너라서 안전해요! bot.py에 @bot.event로 on_message를 직접 만들면 기본 동작을 덮어써서 !명령어가 전부 죽어요. 그때는 마지막에 await bot.process_commands(message)를 직접 불러줘야 하죠. 반면 우리처럼 Cog 안에서 @commands.Cog.listener()로 쓰면 기본 처리와 같이 실행돼서 명령어가 멀쩡합니다 👍

📌 2단계 - 정규표현식으로 키워드 잡기

이제 무엇에 반응할지 정할 차례예요. 딕셔너리로 키워드 : 답변을 짝지어둡니다.

Python
import re

AUTO_REPLIES = {
    r"공지|공지사항": "📢 공지는 <#000000000000000000> 채널을 확인해주세요!",
    r"봇\s*명령어|명령어\s*목록": "🤖 `/도움말` 을 입력하면 명령어 목록이 나와요!",
    r"안녕|하이|ㅎㅇ": "👋 안녕하세요! 반가워요~",
}

COMPILED_REPLIES = [
    (re.compile(pattern, re.IGNORECASE), reply)
    for pattern, reply in AUTO_REPLIES.items()
]

하나씩 뜯어볼게요 💡

  • r"..." : 앞에 r을 붙인 raw 문자열이에요. 정규식에서 \s, \w 같은 역슬래시를 그대로 쓰려면 필수입니다
  • | : "또는"이에요. 공지|공지사항은 둘 중 아무거나 걸립니다
  • \s* : 공백이 0개 이상. 봇명령어, 봇 명령어, 봇 명령어를 전부 잡아요
  • re.compile : 패턴을 미리 컴파일해두는 거예요. on_message는 초당 수십 번 불릴 수 있어서, 매번 컴파일하면 낭비입니다. 모듈 최상단에서 한 번만 해두세요 ⚡
  • re.IGNORECASE : 영어 대소문자 구분을 없애요. Discord도 discord도 잡힙니다
  • <#채널ID> : 디스코드에서 채널 링크로 자동 변환되는 문법이에요. 유저를 멘션하려면 <@유저ID>를 쓰면 돼요

📌 3단계 - 우회 표현까지 잡는 정규화

금지어 필터를 만들면 바로 이런 게 옵니다.

텍스트
바 보
바.보
바_보
바ㅡ보

사람 눈에는 다 똑같은데 정규식에는 다른 문자열이에요. 그래서 비교하기 전에 텍스트를 정리합니다.

Python
import unicodedata

NOISE = re.compile(r"[\s._\-*~`|^]+")

def normalize(text: str) -> str:
    text = unicodedata.normalize("NFKC", text)
    return NOISE.sub("", text).lower()

하나씩 뜯어볼게요 💡

  • unicodedata.normalize("NFKC", ...) : 전각 문자(bad)나 특수 유니코드를 일반 문자로 통일해줘요. 유니코드 꼼수를 1차로 막습니다
  • NOISE.sub("", text) : 공백·마침표·언더바·별표 같은 끼워넣기용 문자를 전부 제거해요. 바 . 보 → 바보가 됩니다
  • .lower() : 영어 금지어 대응이에요
  • 두 번 검사하는 이유 : 아래 전체 코드에서는 normalize한 문자열과 원본을 둘 다 검사해요. discord.gg/abc처럼 마침표·슬래시가 패턴의 일부인 경우는 정규화하면 오히려 안 잡히거든요 🧐

💬 완벽한 필터는 세상에 없어요. 우회하려는 사람은 결국 우회합니다. 목표는 "작정한 사람 막기"가 아니라 "광고봇과 충동적인 욕설 거르기" 정도로 잡는 게 현실적이에요.

📌 4단계 - 삭제 · 경고 누적 · 타임아웃

걸렸을 때 뭘 할지가 남았어요. 삭제 → 경고 +1 → 한도 넘으면 타임아웃 순서예요.

Python
async def punish(self, message: discord.Message, matched: str) -> None:
    try:
        await message.delete()
    except discord.Forbidden:
        print("[AutoMod] 메시지 삭제 권한이 없어요!")
    except discord.NotFound:
        pass

    count = self.add_warn(message.guild.id, message.author.id)

    await message.channel.send(
        f"{message.author.mention} 🚫 사용할 수 없는 표현이에요. "
        f"경고 **{count}/{WARN_LIMIT}**회 누적됐어요.",
        delete_after=10,
    )

    if count >= WARN_LIMIT:
        await self.mute(message.author)

async def mute(self, member: discord.Member) -> None:
    try:
        await member.timeout(
            timedelta(minutes=MUTE_MINUTES), reason=f"금지어 경고 {WARN_LIMIT}회 누적"
        )
    except discord.Forbidden:
        print("[AutoMod] 타임아웃 권한(멤버 조절하기)이 없어요!")

하나씩 뜯어볼게요 💡

  • discord.NotFound : 다른 봇이나 관리자가 이미 지운 메시지를 또 지우려 할 때 나요. 흔한 일이라 조용히 넘깁니다
  • delete_after=10 : 경고 안내 메시지를 10초 뒤 자동 삭제해요. 안 그러면 채널이 경고문으로 도배됩니다 🧹
  • member.timeout(timedelta(minutes=10)) : 디스코드 타임아웃 기능이에요. 그 시간 동안 채팅·음성이 막힙니다. timedelta 대신 datetime을 넣으면 "언제까지"로도 지정할 수 있어요. 최대 28일까지만 가능합니다
  • 권한 필요 : 타임아웃은 봇에게 멤버 조절하기(Moderate Members) 권한이 있어야 해요. 그리고 봇 역할이 대상보다 위에 있어야 합니다 (#20편의 그 함정, 여기서도 똑같아요 😅)
  • 경고 저장은 JSON : #14편에서 배운 방식 그대로예요. 서버가 커지면 느려지는데, 그건 #23편 aiosqlite에서 갈아엎을 예정입니다

📌 전체 코드 (복붙 OK)

1) cogs/automod.py

Python
# discord.py 2.x (2.7.1 기준) / Python 3.8+
# 디스코드 봇 만들기 #21 - 키워드 자동응답 · 금지어 필터

import json
import os
import re
import unicodedata
from datetime import timedelta
from pathlib import Path

import discord
from discord import app_commands
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()

def env_id(key: str) -> int:
    """.env 값이 비어 있어도 안 터지게 정수로 바꿔주는 헬퍼"""
    raw = (os.getenv(key) or "").strip()
    return int(raw) if raw.isdigit() else 0

LOG_CHANNEL_ID = env_id("AUTOMOD_LOG_CHANNEL_ID")
WARN_LIMIT = env_id("AUTOMOD_WARN_LIMIT") or 3
MUTE_MINUTES = env_id("AUTOMOD_MUTE_MINUTES") or 10

BRAND_COLOR = discord.Color.from_str("#5865F2")
WARN_FILE = Path("data/warns.json")

# 키워드 : 자동응답 문구
AUTO_REPLIES = {
    r"공지|공지사항": "📢 공지는 <#000000000000000000> 채널을 확인해주세요!",
    r"봇\s*명령어|명령어\s*목록": "🤖 `/도움말` 을 입력하면 명령어 목록이 나와요!",
    r"안녕|하이|ㅎㅇ": "👋 안녕하세요! 반가워요~",
}

# 금지어 (정규표현식). 실제 운영에서는 서버 성격에 맞게 채우세요
BANNED_PATTERNS = [
    r"바보",
    r"멍청이",
    r"discord\.gg/\w+",       # 초대 링크 광고
    r"https?://\S*(도박|토토)",
]

COMPILED_REPLIES = [
    (re.compile(pattern, re.IGNORECASE), reply) for pattern, reply in AUTO_REPLIES.items()
]
COMPILED_BANNED = [re.compile(pattern, re.IGNORECASE) for pattern in BANNED_PATTERNS]

# 우회 문자 제거용 (바 보 / 바.보 / 바-보 같은 꼼수 차단)
NOISE = re.compile(r"[\s._\-*~`|^]+")

def normalize(text: str) -> str:
    text = unicodedata.normalize("NFKC", text)
    return NOISE.sub("", text).lower()

class AutoMod(commands.Cog):
    """키워드 자동응답 · 금지어 필터 · 경고 누적"""

    def __init__(self, bot: commands.Bot):
        self.bot = bot
        self.warns = self.load_warns()

    # ---------- 경고 저장 (#14 JSON 편 방식) ----------
    def load_warns(self) -> dict:
        if WARN_FILE.exists():
            with WARN_FILE.open(encoding="utf-8") as f:
                return json.load(f)
        return {}

    def save_warns(self) -> None:
        WARN_FILE.parent.mkdir(parents=True, exist_ok=True)
        with WARN_FILE.open("w", encoding="utf-8") as f:
            json.dump(self.warns, f, ensure_ascii=False, indent=2)

    def add_warn(self, guild_id: int, user_id: int) -> int:
        key = f"{guild_id}:{user_id}"
        self.warns[key] = self.warns.get(key, 0) + 1
        self.save_warns()
        return self.warns[key]

    # ---------- 면제 대상 판별 ----------
    def is_exempt(self, message: discord.Message) -> bool:
        if message.author.bot or message.webhook_id:
            return True
        if not isinstance(message.author, discord.Member):
            return True
        return message.author.guild_permissions.manage_messages

    # ---------- 메인 리스너 ----------
    @commands.Cog.listener()
    async def on_message(self, message: discord.Message):
        if self.is_exempt(message):
            return

        cleaned = normalize(message.content)

        # ① 금지어 먼저
        for pattern in COMPILED_BANNED:
            if pattern.search(cleaned) or pattern.search(message.content):
                await self.punish(message, pattern.pattern)
                return

        # ② 자동응답
        for pattern, reply in COMPILED_REPLIES:
            if pattern.search(message.content):
                await message.channel.send(reply, reference=message, mention_author=False)
                return

    async def punish(self, message: discord.Message, matched: str) -> None:
        try:
            await message.delete()
        except discord.Forbidden:
            print("[AutoMod] 메시지 삭제 권한이 없어요!")
        except discord.NotFound:
            pass

        count = self.add_warn(message.guild.id, message.author.id)

        await message.channel.send(
            f"{message.author.mention} 🚫 사용할 수 없는 표현이에요. "
            f"경고 **{count}/{WARN_LIMIT}**회 누적됐어요.",
            delete_after=10,
        )

        if count >= WARN_LIMIT:
            await self.mute(message.author)

        await self.write_log(message, matched, count)

    async def mute(self, member: discord.Member) -> None:
        try:
            await member.timeout(
                timedelta(minutes=MUTE_MINUTES), reason=f"금지어 경고 {WARN_LIMIT}회 누적"
            )
        except discord.Forbidden:
            print("[AutoMod] 타임아웃 권한(멤버 조절하기)이 없어요!")

    async def write_log(self, message: discord.Message, matched: str, count: int) -> None:
        channel = message.guild.get_channel(LOG_CHANNEL_ID)
        if channel is None:
            return

        embed = discord.Embed(
            title="🚫 금지어 감지",
            description=f"```{message.content[:500]}```",
            color=discord.Color.red(),
            timestamp=discord.utils.utcnow(),
        )
        embed.add_field(name="작성자", value=message.author.mention, inline=True)
        embed.add_field(name="채널", value=message.channel.mention, inline=True)
        embed.add_field(name="누적 경고", value=f"{count}회", inline=True)
        embed.set_footer(text=f"패턴: {matched}")
        await channel.send(embed=embed)

    # ---------- 관리 명령어 ----------
    @app_commands.command(name="경고조회", description="멤버의 누적 경고 횟수를 확인해요")
    @app_commands.describe(멤버="확인할 멤버")
    async def check_warn(self, interaction: discord.Interaction, 멤버: discord.Member):
        count = self.warns.get(f"{interaction.guild_id}:{멤버.id}", 0)
        await interaction.response.send_message(
            f"**{멤버.display_name}** 님의 누적 경고는 **{count}회** 예요.", ephemeral=True
        )

    @app_commands.command(name="경고초기화", description="멤버의 경고를 0으로 되돌려요")
    @app_commands.checks.has_permissions(manage_messages=True)
    async def reset_warn(self, interaction: discord.Interaction, 멤버: discord.Member):
        self.warns.pop(f"{interaction.guild_id}:{멤버.id}", None)
        self.save_warns()
        await interaction.response.send_message(
            f"**{멤버.display_name}** 님의 경고를 초기화했어요 ✅", ephemeral=True
        )

    @reset_warn.error
    async def reset_warn_error(self, interaction: discord.Interaction, error):
        if isinstance(error, app_commands.MissingPermissions):
            await interaction.response.send_message(
                "이 명령어는 **메시지 관리** 권한이 있어야 쓸 수 있어요 🔒", ephemeral=True
            )

async def setup(bot: commands.Bot):
    await bot.add_cog(AutoMod(bot))

2) 폴더 구조

#19편의 bot.py가 cogs/ 안의 파일을 자동으로 불러오니까, 오늘도 파일만 넣으면 끝이에요 😎

텍스트
server-bot/
├─ .env
├─ .gitignore
├─ bot.py
├─ data/
│  └─ warns.json          # ← 자동 생성돼요
└─ cogs/
   ├─ admin.py
   ├─ poll.py             # #18 투표
   ├─ onboarding.py       # #20 환영·자동역할
   └─ automod.py          # ← 오늘 추가!

💡 data/warns.json도 .gitignore에 넣어두세요. 유저 ID와 경고 기록이 들어가는 개인정보성 파일이에요.

📌 실행 결과

python bot.py로 실행하면 Cog가 하나 더 올라와요.

텍스트
[Onboarding] 규칙 동의 패널 등록 완료!
✅ cogs.admin 불러옴
✅ cogs.automod 불러옴
✅ cogs.onboarding 불러옴
✅ cogs.poll 불러옴
MojangBot#1234 로그인 성공! (Cog 4개)

관리 권한이 없는 계정으로 안녕이라고 쳐보세요. 봇이 바로 답장합니다.

이번엔 금지어를 쳐볼게요. 메시지가 사라지고 경고 안내가 뜬 뒤 10초 후 자동으로 사라져요.

텍스트
@테스터 🚫 사용할 수 없는 표현이에요. 경고 1/3회 누적됐어요.

3번째 위반에서는 타임아웃이 걸리고, 로그 채널에는 빨간 임베드가 쌓입니다.

/경고조회로 확인하고 /경고초기화로 리셋할 수 있어요. 슬래시 명령어가 안 보이면 #19편의 !sync를 한 번 실행해주세요!

📌 자주 나는 에러

1. on_message가 아예 안 불려요

  • 원인 : message_content 인텐트가 꺼져 있어요. 이게 없으면 message.content가 빈 문자열로 옵니다
  • 해결 : 코드의 intents.message_content = True 그리고 개발자 포털 → Bot → MESSAGE CONTENT INTENT를 둘 다 켜고 봇을 재시작하세요

2. 봇이 자기 메시지에 무한 반응해요 (레이트리밋)

  • 원인 : if message.author.bot: return을 빠뜨렸어요. 자동응답 → 그 메시지가 다시 on_message → 또 자동응답... 무한루프예요 🔁
  • 해결 : is_exempt()를 리스너 제일 첫 줄에서 부르세요. 이미 루프가 돌았다면 봇을 끄고 고친 뒤 몇 분 뒤 재시작하세요

3. !명령어가 전부 먹통이 됐어요

  • 원인 : bot.py에 @bot.event로 on_message를 직접 만들었어요. 기본 처리를 덮어써버립니다
  • 해결 : 오늘처럼 Cog 리스너(@commands.Cog.listener())로 옮기세요. 굳이 @bot.event를 써야 한다면 함수 마지막에 await bot.process_commands(message)를 넣어주세요

4. discord.errors.Forbidden: 403 Forbidden (error code: 50013)

  • 원인 : 봇에게 메시지 관리(Manage Messages) 또는 멤버 조절하기(Moderate Members) 권한이 없어요
  • 해결 : 서버 설정 → 역할에서 두 권한을 주고, 봇 역할을 대상 멤버 역할보다 위로 올리세요

5. AttributeError: 'User' object has no attribute 'guild_permissions'

  • 원인 : DM으로 온 메시지를 처리했어요. DM에는 서버가 없어서 권한 정보도 없습니다
  • 해결 : isinstance(message.author, discord.Member) 체크를 넣으세요. 전체 코드의 is_exempt()에 이미 들어 있어요

6. discord.errors.NotFound: 404 Not Found (error code: 10008): Unknown Message

  • 원인 : 이미 삭제된 메시지를 또 지우려 했어요. 다른 관리봇과 같이 쓰면 자주 납니다
  • 해결 : except discord.NotFound: pass로 조용히 넘기세요

7. 우회 표현(바 보)이 안 걸려요

  • 원인 : 원본 문자열만 검사하고 있어요
  • 해결 : normalize()를 거친 문자열도 함께 검사하세요. 다만 discord.gg/처럼 기호가 패턴에 포함된 금지어는 원본에서만 잡히니 둘 다 검사하는 게 정답이에요

8. int() argument must be a string... / ValueError: invalid literal for int()

  • 원인 : .env에 키만 써두고 값을 비워뒀어요. int("")는 에러가 납니다 (지난 #20편 코드에서 실제로 밟았던 함정이에요 😅)
  • 해결 : 전체 코드의 env_id() 헬퍼처럼 isdigit()로 확인한 뒤 변환하세요. 값이 비면 0을 돌려주고 조용히 넘어갑니다

📌 마무리

오늘은 서버를 스스로 지키는 cogs/automod.py를 만들었어요 🙌

오늘의 요약

  • on_message는 거를 것부터 거르기 — 봇·웹훅·DM·관리자
  • message.author.bot 한 줄이 무한루프를 막아줘요
  • 패턴은 re.compile로 미리 만들어두기 (성능!)
  • 우회 표현은 NFKC 정규화 + 노이즈 제거로 잡되, 원본도 같이 검사
  • 삭제 → 경고 누적 → member.timeout(timedelta(...)) 순으로 제재
  • Cog 리스너를 쓰면 기존 명령어가 안 죽어요

다음 #22편에서는 슬래시 명령어 자동완성(Autocomplete)을 만듭니다. 명령어를 입력하는 도중에 후보를 띄워주는 그 기능이에요. 오늘 만든 금지어 목록을 자동완성으로 골라서 추가·삭제하게 만들어볼게요. 기대해주세요! 🙌

💬 오늘 만든 automod.py는 #27 최종 프로젝트 "커뮤니티 통합 관리 봇"의 자동 모더레이션 모듈이 됩니다. 경고 데이터는 #23 aiosqlite 편에서 DB로 옮길 예정이니 폴더째 잘 보관해두세요!

📚 디스코드 봇 만들기 시리즈

연재 · 32편 중 21번째

디스코드 봇 만들기

  1. #1디스코드 봇 만들기 - Slash Command 완전 정복!
  2. #2Slash Command에 옵션(Arguments) 추가하기!
  3. #3Slash Command에 서브 커맨드(Subcommand) 넣기!
  4. #4Slash Command로 임베드 전송하기!
  5. #5버튼(Button) 상호작용 구현하기!
  6. #6드롭다운(Select Menu) 메뉴 구현하기!
  7. #7모달(Modal)로 입력 받기 구현하기 (파이썬 예제)
  8. #8임베드(Embed) 이미지·필드로 꾸미기 (파이썬 예제)
  9. #9AI(Claude·ChatGPT API) 연동하기! (파이썬 예제)
  10. #10다국어(i18n) 지원하기! (파이썬 예제)
  11. #11플라스크(Flask)로 웹 대시보드 만들기! (파이썬 예제)
  12. #12명령어 쿨다운(Cooldown)·권한(Permission) 관리하기! (파이썬 예제)
  13. #13역할·채널 자동화로 서버 관리하기! (파이썬 예제)
  14. #14데이터 저장하기! JSON vs SQLite (파이썬 예제)
  15. #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제)
  16. #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제)
  17. #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제)
  18. #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제)
  19. #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제)
  20. #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제)
  21. #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제)지금 읽는 글
  22. #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제)
  23. #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제)
  24. #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제)
  25. #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제)
  26. #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제)
  27. #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제)
  28. #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제)
  29. #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제)
  30. #30투표·설문(Poll) 봇 만들기! 네이티브 투표 + 버튼으로 실시간 집계까지 (파이썬 예제) 🗳️
  31. #32서버 로그·감사(Audit Log) 채널 만들기! 메시지 삭제·수정·입퇴장·추방까지 자동 기록 (파이썬 예제) 🕵️
  32. #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉
전체 32편 보기접기

글쓴이

Mojangcam

직접 코드를 짜고, 서버를 돌리고, 막힌 에러를 해결하며 배운 것들을 기록하는 개발·IT 실전 기록 블로그