디스코드 봇 만들기#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에 넣으세요! 깃허브에 토큰이 올라가면 디스코드가 자동 감지해서 바로 폐기해버립니다.
그리고 인텐트 하나가 더 필요해요. 채팅 내용을 읽어야 하니까요.
intents.message_content = True # ← 오늘 주인공!디스코드 개발자 포털 → 내 앱 → Bot → Privileged Gateway Intents → MESSAGE CONTENT INTENT도 켜져 있어야 해요. #19편에서 이미 켜뒀다면 그대로 두시면 됩니다.

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