Discord 서버 운영, AI 보조, 음악 재생, 게임/도박, 일정, 외부 알림을 한 번에 처리하는 Python 기반 Discord 봇입니다.
이 저장소는 봇 애플리케이션 코드, Docker 실행 자산, Jenkins 예시 파이프라인, DB 마이그레이션 스크립트를 함께 관리합니다. 공유 Jenkins 컨트롤러나 외부 런타임 인프라는 이 저장소 범위가 아닙니다.
discord.py기반 slash command/package cog 구조- OpenAI Responses API 기반 질문, 검색, 요약, 번역, 해석, 설명, 음식 추천, 공지 요약
- YouTube 영상 요약, 댓글 요약, 라이브/업로드/WebSub/커뮤니티 게시물 알림
- 음악 재생, 대기열, 반복, 셔플, 즐겨찾기, 패널 UI
- MySQL 기반 길드 설정, 도박 잔액, 예약 메시지, YouTube 구독, DDAY, 기능 상태 저장
aiohttp.web기반 상태/외부 콜백 API- Docker Compose 배포와 Jenkins 검증/마이그레이션/배포 단계
- Runtime: Python 3.11
- Discord:
discord.py,discord.py[voice],discord-ext-voice-recv - HTTP/DB:
aiohttp,aiomysql - AI: OpenAI Responses API, hosted prompt payload
- 음성/미디어:
faster-whisper,pyttsx3,yt-dlp,ffmpeg - 외부 API: YouTube Data API, Riot API, 한국은행 ECOS
- 테스트:
unittest,compileall - 배포: Docker, Docker Compose, Jenkins Pipeline
.
├── bot.py # Bot 생성, Cog 자동 로드, 메시지 캐시, startup 초기화
├── api/ # OpenAI, Riot, ECOS API adapter
├── cogs/ # Slash command와 background/API cog
├── common/ # 공통 OpenAI prompt payload helper
├── func/ # YouTube 요약/미디어 처리와 1557 감지 기능
├── util/ # DB, guild/message/music/youtube/maplestory/loop helper
├── scripts/migrate_db.py # MySQL schema migration entrypoint
├── test/ # unittest 기반 회귀 테스트
├── Dockerfile* # runtime/dependency image
├── docker-compose.yml # local/deploy compose service
└── Jenkinsfile # checkout, verify, migrate, deploy 예시 pipeline
bot.py가 commands.Bot을 생성하고 cogs/ 아래의 단일 파일 cog와 package cog를 자동으로 발견해 로드합니다. cogs.status_api는 필수 cog로 취급되어 로드 실패 시 startup이 중단됩니다.
bot.py는 다음 전역 상태도 관리합니다.
DISCORD_CLIENT.USER_MESSAGES: 길드별 최근 메시지 캐시. AI 질문과 요약 컨텍스트로 사용합니다. 대화 요약은 최근 이미지 최대 4장도 함께 처리하며, 해석과 설명은 선택 메시지 기준 채널 기록을 직접 조회합니다.DISCORD_CLIENT.PARTY_LIST:-파티로 끝나는 Discord 카테고리를 길드별로 추적합니다.
현재 주요 기능은 package cog 형태인 cogs/<name>/__init__.py에 위치합니다. 복잡한 기능은 해당 cog가 command surface만 담당하고 세부 로직은 util/, func/, 하위 모듈로 분리합니다.
대표 예시는 다음과 같습니다.
cogs/gambling/: 도박 명령 surface, 잔액/게임 로직은 하위 파일로 분리cogs/music/:__init__.py는 Cog 등록, 명령어, 이벤트를 담당합니다. 즐겨찾기는favorites.py, 패널은panel.py, 검색과 대기열은queue.py, URL 재생 요청은requests.py, 일시정지와 구간 이동은controls.py, 재생 수명주기와 곡 종료는playback.py에 있습니다. 공통 상수와 로거는runtime.py, 순수 로직과 source/view helper는util/music/에 있습니다.cogs/youtube_subscriptions/: YouTube 구독 명령 surface, 상태/알림/WebSub helper는util/youtube/cogs/maplestory/: MapleStory 명령 surface, fetch/parser/sender/state는util/maplestory/cogs/loop/: background task orchestration, 세부 runner는util/loop/,util/youtube/,util/maplestory/,util/codex_resets/,util/earthquake/
| 명령어 | 설명 |
|---|---|
/도움 |
카테고리별 도움말 UI |
/기가채드 |
기가채드 이미지 전송 |
/핑 |
봇 latency 확인 |
/clean |
관리자 전용 최근 메시지 정리 |
/채널설정 |
기념일, 도박, 음악, 유튜브, 메이플공지, 로아공지, 코덱스리셋, 지진알림 채널 지정/해제 |
/채널설정확인 |
현재 길드의 기능별 채널 설정 확인 |
| 명령어 | 설명 |
|---|---|
/기념일업데이트 |
오늘 기념일/사건 공지를 수동 갱신 |
/dday추가 |
서버 DDAY 추가 |
/dday삭제 |
DDAY 목록에서 선택 삭제 |
/dday목록 |
서버 DDAY 목록 조회 |
/예약 일반 |
지정 시각에 메시지 예약 |
/예약 반복 |
매시간/매일/매주/매달 반복 메시지 예약 |
/예약 리스트 |
예약 목록 조회 및 삭제 |
| 명령어/액션 | 설명 |
|---|---|
/질문 |
최근 대화 문맥을 반영하는 텍스트/이미지 질문 |
/신이시여 |
텍스트와 이미지 기반 질문 |
/검색 |
텍스트와 선택 이미지 단서를 이용한 웹 검색 |
/대화요약 |
최근 채팅과 최근 이미지 최대 4장 요약 |
/번역 |
입력 텍스트/이미지 또는 최근 메시지 번역 |
/해석 |
입력 텍스트/이미지 또는 최근 메시지와 가까운 주변 이미지 최대 4장의 수치, 계산, 밈 후보 검색 해석 |
/설명 |
입력 텍스트/이미지 또는 최근 메시지와 가까운 주변 이미지 최대 4장 설명 |
/뭐먹지 |
저장 프롬프트와 gpt-6-luna로 국내 배달 식사 메뉴 하나 추천, 17개 분류의 102개 메뉴로 제한하고 최근 20개 추천 반복 방지 (재시작 시 초기화) |
컨텍스트 메뉴 메시지 번역 |
선택 메시지 번역 |
컨텍스트 메뉴 메시지 해석 |
선택 메시지 해석 |
컨텍스트 메뉴 메시지 설명 |
선택 메시지 설명 |
OpenAI 호출은 api/chatGPT.py에서 감싸며, hosted prompt payload는 common/openai_prompt.py의 helper로 생성합니다. OpenAI SDK 예외는 OpenAIModelError로 매핑해 사용자에게 안전한 오류 메시지를 보냅니다.
| 명령어 | 설명 |
|---|---|
/대화 |
음성 채널에 참여해 실시간 음성 대화 시작 |
/대화종료 |
음성 대화 종료 및 퇴장 |
음성 처리는 discord-ext-voice-recv Opus sink에서 Discord DAVE payload를 먼저 해독한 뒤 사용자별 Opus decoder로 PCM을 만들고, silence detection, faster-whisper STT, pyttsx3 TTS 경로를 사용합니다. 손상된 음성 frame은 해당 사용자 decoder에서만 폐기하므로 전체 수신 loop를 중단하지 않습니다. Windows 로컬 실행에서는 bin/ 또는 PATH의 ffmpeg를 사용합니다.
STT는 기본적으로 tiny 모델을 CPU int8로 실행합니다. CUDA 모델은 GPU 런타임을 실제로 준비한 환경에서만 VOICE_CHAT_WHISPER_DEVICE=cuda와 모델 및 연산 형식을 명시해 활성화합니다.
| 명령어/기능 | 설명 |
|---|---|
/요약 |
최근 메시지의 YouTube 링크 10개를 선택해 영상/댓글/게시물 요약 |
컨텍스트 메뉴 유튜브 요약 |
선택 메시지의 YouTube 링크 요약 |
/유튜브구독 추가 |
채널 URL, ID, @handle, 검색어로 구독 추가 |
/유튜브구독 알림설정 |
구독별 라이브/영상/커뮤니티 알림 on/off |
/유튜브구독 삭제 |
구독 목록에서 선택 삭제 |
/유튜브구독 목록 |
서버의 YouTube 구독 목록 확인 |
YouTube 요약은 자막을 우선 사용하고, 필요 시 오디오를 MP3로 변환해 STT 후 요약합니다. 댓글은 최대 40개를 요약합니다. 커뮤니티 게시물은 direct post URL과 channel posts page를 파싱합니다.
YouTube 알림은 다음 경로를 조합합니다.
- WebSub Atom callback 수신
- YouTube Data API
videos.list로 live/upcoming/upload/shorts 분류 - Atom feed fallback polling
- 커뮤니티 게시물 10분 polling
- WebSub 구독 12시간 주기 갱신
| 명령어 | 설명 |
|---|---|
/마크서버 |
mc.ios.kr 서버 상태 표시. 설치 안내 버튼과 화이트리스트 등록 버튼 제공 |
온라인 여부, 접속 인원, 서버 버전을 mcsrvstat.us API로 조회해 상태 임베드에 표시합니다. API 결과는 5분간 캐시될 수 있으며 조회 실패 시에도 설치 안내 버튼은 표시합니다. 누구나 Fabric, Sodium, Iris, SEUS 버튼을 누르면 같은 채널에 설치 안내 임베드를 새 메시지로 보냅니다. 버튼은 봇 재시작 후에도 사용할 수 있습니다. 안내에는 FPS 향상과 렉 감소를 위한 Sodium, 셰이더 적용을 위한 Iris 다운로드 링크와 .minecraft/mods 설치 순서가 포함됩니다.
버튼으로 전송하는 안내 임베드에는 SEUS PTGI 다운로드 페이지와 버전별 특징도 표시합니다. E12는 E 시리즈 최종판으로 화질 중심이며 무겁고, HRR 2.1은 E12 기반 최적화로 FPS를 높여 비교적 가볍습니다. HRR 3.0은 광원, 반사, 구름 개선과 추적 범위 확대로 HRR 2.1보다 무겁습니다. 셰이더 ZIP은 압축 해제하지 않고 .minecraft/shaderpacks에 넣은 뒤 게임의 비디오 설정에서 선택합니다.
디스코드 서버 멤버 누구나 화이트리스트 등록 버튼을 눌러 Java Edition 닉네임을 입력할 수 있습니다. 봇은 RCON으로 whitelist add <닉네임>만 실행하며 서버 응답을 신청자에게만 표시합니다. 이미 등록된 닉네임이나 존재하지 않는 닉네임도 서버 응답으로 안내하며, 응답을 받지 못한 경우 성공으로 표시하지 않습니다. DM에서는 등록할 수 없습니다. 이 기능은 게임 계정 소유권을 인증하지 않습니다.
마크 서버의 server.properties를 다음과 같이 설정하고 서버를 재시작하세요. 비밀번호는 직접 생성한 긴 무작위 값으로 바꾸세요.
enable-rcon=true
rcon.port=25575
rcon.password=replace-with-long-random-password
white-list=true봇의 실행 환경에는 MINECRAFT_RCON_HOST(마크 서버 컴퓨터의 내부 IP), MINECRAFT_RCON_PORT(기본 25575), MINECRAFT_RCON_PASSWORD(위와 동일한 비밀번호)를 설정하세요. 로컬 봇은 .env, 배포 봇은 .env.deploy에 설정하고, Jenkins 배포에서는 discordbot-env 자격 증명에도 전체 .env.deploy 내용을 반영해야 합니다. 비밀번호는 Git에 올리지 마세요.
같은 공유기에서 마크 서버 컴퓨터의 내부 IP를 고정하거나 DHCP 예약을 사용하고, 해당 컴퓨터의 방화벽에서 배포 서버 IP만 TCP RCON 포트에 접근하도록 허용하세요. 공유기에서 RCON 포트를 인터넷으로 포트포워딩하지 마세요. RCON은 암호화되지 않은 관리자 연결입니다. 마크 서버 컴퓨터가 꺼져 있거나 절전 상태이면 등록할 수 없습니다. Docker 환경변수 등으로 화이트리스트를 매번 덮어쓰는 구성이라면 재시작 시 등록 내역이 사라지지 않도록 해당 설정도 조정하세요.
| 명령어/기능 | 설명 |
|---|---|
/썬데이메이플 |
진행 중인 썬데이 메이플과 스페셜 썬데이 메이플 이벤트 이미지 조회 |
/메이플공지구독 |
현재 채널에 메이플스토리 공지 알림 구독/해제 |
| 명령어 | 설명 |
|---|---|
/로아공지구독 [상태] |
관리자가 현재 채널에 로스트아크 공지 알림을 구독/해제 |
/채널설정 기능:로아공지에서도 같은 알림 채널을 지정하거나 해제합니다./채널설정확인에서 확인할 수 있습니다.- 공식 공지 목록을 3분마다 확인하며, 제목과 본문 변경도 감지합니다.
- 최초 구독과 채널 변경 시 기존 공지는 기준 상태로 저장하며 과거 글을 전송하지 않습니다.
- 메이플 공지와 같은 3~4줄 AI 요약 임베드를 사용하며, 요약 실패 시 원문 기반 요약을 제공합니다.
- 이미 전송한 글이 수정되면 기존 메시지를 갱신합니다. 동일 글의 점검 연장과 완료 처리는 메이플 공지 전송 로직을 따릅니다.
- 수집과 구독 상태는
util/lostark/notices.py에서 관리하며 DB의lostarkNoticeState에 저장합니다.
공지 알림은 3분마다 확인합니다. 새 점검 공지는 새 메시지로 보내고, 일반 수정은 기존 메시지를 수정합니다. 연장 공지는 별도 메시지로 알리고, 점검 완료 공지가 나오면 이전 점검 관련 메시지를 정리한 뒤 완료 공지를 전송합니다.
X 게시물 기반의 리셋 알림은 /번역과 분리된 전용 번역 코드와 프롬프트 에디터의 codex_reset_translation v2의 JSON Schema으로 본문을 한국어 번역해 임베드에 표시합니다. Codex 사용량 한도 리셋 맥락에서 관용구를 자연스러운 안내문으로 옮기며, 원문에 없는 적용 대상이나 혜택은 추가하지 않습니다. 원문 링크와 리셋 유형은 유지합니다. 번역 오류 또는 비어 있거나 한국어가 아닌 응답이면 전송 상태를 갱신하지 않고 다음 확인 때 재시도합니다. X 게시물이 없는 관측 리셋은 기존 본문으로 전송합니다.
| 명령어/기능 | 설명 |
|---|---|
/코덱스리셋알림 |
현재 채널에 Codex 리셋과 Tibo Daily log 알림 설정/해제 |
codex-resets.com이 제공하는 공개 API의 /api/v1/resets를 3분마다 확인합니다. 최신 100건 중 새 일반 리셋과 적립형 리셋을 알리며, X 게시물 없이 관측된 리셋도 포함합니다. 예정 리셋과 리셋 가능성 예측은 알림 대상에서 제외합니다. 이 사이트는 OpenAI 공식 서비스가 아닌 비공식 추적기입니다.
Tibo의 28일 챌린지 Daily log도 같은 주기로 확인해 같은 채널에 전송합니다. /코덱스리셋알림 또는 /채널설정 기능:코덱스리셋으로 함께 설정하거나 해제하며, 이미 코덱스 리셋 채널이 설정된 서버는 추가 설정이 필요 없습니다. 첫 확인과 채널 재설정에서는 기존 발표를 DB에 저장하고 알림을 생략합니다. 이후 새 발표와 제목, 본문, 유형, 출처 등의 변경이 있는 Day를 알리며, 여러 날짜에 변경이 있으면 오래된 날짜부터 전송합니다. 대기 상태와 투표 수 변화는 알리지 않습니다.
같은 Day의 모든 발표를 하나의 임베드에 묶고, 전용 tibo_daily_log_summary v1 프롬프트와 JSON Schema로 한국어 제목과 본문 요약을 만듭니다. 각 발표는 한국어 제목, 1~2문장의 요약, 원문 링크로 표시합니다. 한 발표만 추가되거나 수정되어도 해당 Day 전체를 다시 묶어 기존 임베드 메시지를 수정합니다. 같은 확인 주기의 서버들은 Day별 번역 결과를 공유합니다. 번역에 실패하면 해당 Day의 전송 상태를 저장하지 않고 다음 확인 때 재시도합니다. 개선 소식은 보라색, 리셋 소식은 주황색, 둘이 함께 있는 Day는 금색 임베드로 구분합니다. Day 번호, 발표 날짜 (PT), 새 글 또는 갱신 여부도 표시합니다. OpenAI API 호출이 필요하며 요약 문구는 호출마다 달라질 수 있습니다. 챌린지에서 다룬 리셋은 기존 사용량 리셋 알림과 각각 전송될 수 있습니다. Daily log 페이지의 HTML 구조가 바뀌면 파서를 수정해야 합니다.
기존 Day를 갱신할 때는 별도 안내나 추가 메시지 없이 저장된 메시지 ID로 기존 임베드를 수정합니다. 이전 버전에서 보낸 메시지는 채널 기록에서 같은 Day의 최신 봇 임베드를 찾아 연결하므로 메시지 기록 보기 권한이 필요합니다. 처음 발견한 새 Day 또는 기존 메시지가 없거나 삭제된 경우에만 임베드 하나를 전송합니다. 수정이나 기록 조회에 실패하면 상태를 저장하지 않고 다음 확인 때 재시도합니다.
| 명령어/기능 | 설명 |
|---|---|
/지진알림 |
현재 채널에 일본 M6.5 이상, 한국 M5.5 이상, 기타 국가 M7.5 이상 지진 알림 설정/해제 |
/채널설정 기능:지진알림 |
지정한 채널에 지진 알림 설정/해제 |
/지진알림 상태:true로 실행한 현재 채널이나 /채널설정 기능:지진알림 채널:#알림채널로 지정한 채널에 알림을 보냅니다. 두 명령어는 같은 earthquake_alert 설정을 사용합니다.
| 대상 | 정보 출처 | Discord 전송 기준 | @everyone 기준 |
|---|---|---|---|
| 일본 | Wolfx JMA 긴급지진속보 | M6.5 이상 | M7.5 이상 |
| 한국 및 한반도 인접 해역 | EMSC/CSEM | M5.5 이상 | M6.0 이상 |
| 일본과 한국을 제외한 국가 | EMSC/CSEM | M7.5 이상 | M7.5 이상 |
일본은 Wolfx의 JMA 긴급지진속보 WebSocket에 상시 연결해 일본 기상청이 발표한 M6.5 이상 예보와 경보를 실시간으로 전달합니다. 진원지와 예상 지역의 일본어는 별도 키가 필요 없는 Google 번역으로 변환해 일본어(한국어) 형식으로 표시하며, 번역 요청이 실패하면 원문으로 알림을 계속 보냅니다. 같은 지진의 후속보와 최종보는 기존 Discord 메시지와 지도 위치를 수정하며 취소보도 반영합니다. 훈련보와 2분 이상 지난 초기 스냅샷은 알리지 않습니다.
일본 외 전 세계는 EMSC/CSEM Standing Order WebSocket에 상시 연결합니다. 한국은 지역명과 한반도 좌표 범위로 판별해 M5.5 이상부터, 그 밖의 국가는 M7.5 이상부터 근실시간으로 전달합니다. EMSC의 unid와 갱신 시각으로 신규, 수정, 삭제 정보를 같은 Discord 메시지에 반영하며 일본 지역은 JMA 알림과의 중복을 막기 위해 제외합니다. 연결이 끊겼다가 복구되면 최근 10분 동안 갱신된 M5.5 이상 자료를 조회한 뒤 지역별 기준을 적용해 누락을 보완하지만 최초 실행 때 과거 지진은 재생하지 않습니다. EMSC는 조기경보가 아니라 관측 후 자동 분석되는 예비 정보이며 원자료 기관과 EMSC/CSEM을 임베드 하단에 표시합니다.
@everyone은 일본 지진 M7.5 이상과 한국 지진 M6.0 이상에 적용합니다. 같은 지진에서는 한 번만 멘션하며, 일반 알림을 보낸 뒤 후속 정보에서 처음 멘션 기준 이상으로 상향되면 멘션이 포함된 새 메시지로 교체합니다. 그 밖의 국가에서 발생한 EMSC 지진은 M7.5 이상부터 멘션합니다. 멘션을 보내려면 봇에 대상 채널의 @everyone, @here, 모든 역할 멘션 권한이 있어야 합니다.
Discord 임베드는 두 소스 모두 규모, 깊이, 최대 예상 진도와 발표 단계, 발표 시각, 발생 추정 시각을 각각 3열 행으로 표시합니다. EMSC는 예상 진도를 제공하지 않아 제공 안 함으로 표시합니다. 추정 진원은 OpenStreetMap 지도 이미지와 크게 보기 링크로 표시합니다. 왼쪽 테두리는 규모 M3.0 이상 초록, M4.0 이상 노랑, M5.0 이상 주황, M6.0 이상 빨강이며 취소 또는 삭제 정보와 규모 미상은 회색입니다.
Wolfx는 일본 기상청과 관계없는 비공식 중계 서비스이며 EMSC/CSEM은 여러 관측기관의 자료를 수집하는 비정부 비영리 기관입니다. EMSC 자료는 이용 조건에 따라 출처를 표시하며 상업적 사용 전 별도 허가 여부를 확인해야 합니다. 속보의 규모, 진원, 예상 진도는 후속 정보에서 바뀌거나 취소될 수 있으므로 현지 공식 재난 알림을 우선 확인해야 합니다.
| 명령어 | 설명 |
|---|---|
/내전 |
같은 음성방에서 버튼으로 10명을 모집한 뒤 LoL 내전 팀/포지션 자동 배정 |
/솔랭 |
Riot ID 기반 솔로 랭크 조회 |
/자랭 |
Riot ID 기반 자유 랭크 조회 |
/일일랭크 |
자정 솔랭 조회 대상 확인 |
/일일랭크변경 |
자정 솔랭 조회 대상 변경 |
/일일랭크루프 |
자정 랭크 루프 on/off |
/투표 |
콤마 구분 항목과 참여 인원으로 버튼 투표 진행 |
/내전은 명령을 실행한 사용자가 참가 중인 음성방을 기준으로 10분 동안 모집합니다. 같은 음성방의 사용자만 참가 버튼을 누를 수 있으며, 참가자가 음성방을 나가면 명단에서 자동으로 제외됩니다. 10명이 모이면 참가 순서와 관계없이 레드팀과 블루팀의 5개 포지션을 무작위로 배정합니다. 모집자와 서버 관리자는 완료 전에 모집을 취소할 수 있습니다.
도박 명령은 /채널설정 기능:도박으로 지정한 채널에서만 사용할 수 있습니다.
| 명령어 | 설명 |
|---|---|
/뿌리기 |
지정 금액을 지정 인원에게 선착순 랜덤 분배 |
/돈줘 |
하루 1회 10,000원 지급 |
/잔액 |
보유 금액 확인 |
/순위 |
길드 내 보유 금액 순위 |
/송금 |
다른 사용자에게 송금 |
/가위바위보 |
승리 2배, 무승부 절반, 패배 손실 |
/도박 |
30-70% 랜덤 확률, 성공 시 2배 |
/즉석복권 |
300원 복권 구매 |
/사다리 |
3개 사다리 중 1개 당첨 선택 |
/슬롯 |
3개 슬롯 일치 시 배당 |
/블랙잭 |
Hit/Stand/Double, 블랙잭 1.5배 추가 배당 |
음악 명령은 /채널설정 기능:음악으로 채널을 제한할 수 있습니다. 일반 채팅 메시지를 자동 삭제하지 않습니다.
재생 상태 처리와 재발 방지 테스트는 음악 재생 상태와 비동기 처리를 참고하세요.
| 명령어 | 설명 |
|---|---|
/음악 |
재생 상태와 버튼 패널 표시 |
/재생 |
YouTube URL 또는 검색어 재생 |
/일시정지 |
현재 곡 일시정지 |
/다시재생 |
일시정지된 곡 재개 |
/정지 |
재생 정지 및 음성 채널 퇴장 |
/스킵 |
현재 곡 건너뛰기 |
/대기열 |
현재 대기열 조회 |
/구간이동 |
재생 위치 이동 |
/반복 |
반복 모드 on/off |
/대기열삭제 |
지정 번호의 대기열 항목 삭제 |
/대기열비우기 |
대기열 전체 비우기 |
/대기열이동 |
대기열 순서 변경 |
/셔플 |
대기열 무작위 섞기 |
| 명령어 | 설명 |
|---|---|
/파티 |
현재 생성된 파티 목록 조회 |
/파티생성 |
비공개 카테고리/텍스트/음성 채널 생성 |
/파티초대 |
파티와 유저를 선택해 초대 |
/파티참가 |
파티 참가 |
/파티원 |
파티 멤버 목록 조회 |
/파티탈퇴 |
파티 권한 해제 |
/파티해제 |
파티 삭제 |
| 명령어 | 설명 |
|---|---|
/환율 |
한국은행 ECOS 기준 최신 환율과 기본 30일 그래프, 최대 365일 |
/외환보유액 |
한국은행 ECOS 기준 최신 외환보유액과 기본 12개월 그래프, 최대 60개월 |
- 단일 커스텀 이모지 입력 시 원본 크기 이미지 출력
- 일반 메시지를
USER_MESSAGES에 캐시해 AI 컨텍스트로 사용 - 메시지 내 YouTube 링크 감지 후 요약 UI 제공
- 특정 채널의
1557감지 및 주간 리포트 집계
| 주기 | 작업 |
|---|---|
| 60초 | Discord presence 갱신 |
| 매일 00:00 KST | 최근 메시지 재로딩, 기념일/DDAY/썬데이메이플 refresh |
| 매일 00:01 KST | 주간 1557 리포트 조건 확인 |
| 60초 | YouTube WebSub/Atom feed 후보 확인 |
| 10분 | YouTube 커뮤니티 게시물 polling |
| 3분 | MapleStory 공지 polling |
| 3분 | 로스트아크 공지 polling |
| 12시간 | YouTube WebSub 구독 갱신 |
| 30초 | 예약 메시지 발송 확인 |
cogs/status_api가 aiohttp.web 서버를 실행합니다. 기본 포트는 1557이며 API_PORT로 변경할 수 있습니다.
| Method | Path | 설명 |
|---|---|---|
GET |
/ |
간단한 상태 문자열 |
GET |
/health |
bot readiness, latency, uptime, guild/user/message count, schema version |
POST |
/celebration/update |
기념일 공지 수동 갱신 |
GET |
/youtube/websub |
YouTube WebSub challenge 검증 |
POST |
/youtube/websub |
YouTube Atom notification 수신 |
/celebration/update는 X-API-Key: <CELEBRATION_UPDATE_API_KEY> 헤더가 필요합니다. JSON body 또는 query string으로 guild_id를 넘기면 특정 길드만 갱신하고, 생략하면 기념일 채널이 설정된 모든 길드를 대상으로 합니다.
WebSub callback은 YOUTUBE_WEBSUB_VERIFY_TOKEN을 query의 token 또는 hub.verify_token으로 검증합니다. 배포 런타임에서는 이 값이 필수입니다.
MySQL을 사용합니다. 앱은 시작 시 schema version만 확인하고, 실제 DDL 적용은 scripts/migrate_db.py에서 수행합니다.
주요 테이블:
guild,discord_userchannel_settingssetting_datagambling_balancesscheduled_messagespanel_messagesmusic_favoritescounter_1557youtube_subscriptionsdday_eventsspecial_daysschema_migrations
마이그레이션 실행:
python -m scripts.migrate_db.env는 로컬 실행용, .env.deploy는 배포 실행용입니다. 둘 다 secret 파일이며 Git에 포함하지 않습니다.
처음 설정할 때는 .env.example을 복사해 실제 값을 채웁니다.
필수/주요 값:
DISCORD_TOKEN=...
OPENAI_KEY=...
GOOGLE_API_KEY=...
RIOT_KEY=...
ECOS_API_KEY=...
SONPANNO_GUILD_ID=...
SSAFY_GUILD_ID=...
DB_HOST=localhost:3306
DB_DATABASE=...
DB_USERNAME=...
DB_PASSWORD=...
API_PORT=1557
CELEBRATION_UPDATE_API_KEY=...
VOICE_CHAT_WHISPER_DEVICE=cpu
VOICE_CHAT_WHISPER_MODEL=tiny
VOICE_CHAT_WHISPER_COMPUTE_TYPE=int8
TZ=Asia/Seoul
YOUTUBE_WEBSUB_CALLBACK_URL=https://example.com/youtube/websub
YOUTUBE_WEBSUB_VERIFY_TOKEN=...Docker 배포에서 MySQL이 호스트 loopback에 바인딩되어 있으면 DB_HOST=host.docker.internal:3306처럼 Docker host gateway를 가리켜야 합니다.
Python 3.11 기준으로 실행합니다.
pip install -r requirements.txt
python -m scripts.migrate_db
python bot.pyWindows에서는 아래 스크립트도 사용할 수 있습니다.
.\_launchBot.ps1또는:
_launchBot.batpip_install.txt는 legacy/manual 설치 메모이며, 기본 dependency entrypoint는 requirements.txt입니다.
docker build -f Dockerfile.deps -t bot-discord-bot-deps:latest .
docker compose --env-file .env up -d --build
docker compose --env-file .env psCompose는 127.0.0.1:1557:1557로 Status API를 노출하고, 컨테이너에서 호스트 DB 접근을 위해 host.docker.internal host gateway를 추가합니다. Whisper 다운로드는 whisper-model-cache named volume에 보존되므로 애플리케이션 컨테이너 교체 때 같은 모델을 다시 받지 않습니다. Docker json-file 로그는 기본 10MB 파일 3개로 회전하며 BOT_LOG_MAX_SIZE와 BOT_LOG_MAX_FILES로 조정할 수 있습니다.
비동기 HTTP 테스트를 작성할 때는 모킹 기준과 Tibo 테스트 실패 분석을 참고합니다. 테스트 실행 여부는 사용자의 요청 범위에 따르며, 테스트 작성과 실제 통과를 구분합니다.
코드 변경 후 기본 검증:
python -m compileall -q bot.py api cogs common func util test
python -m unittest discover -s testtest/는 importable package가 아니므로 focused test도 discovery mode로 실행합니다.
python -m unittest discover -s test -p "test_scheduler.py"Docker/Jenkins와 맞춰 DB migration까지 확인할 때:
python -m scripts.migrate_db
python -m compileall -q bot.py api cogs common func util test
python -m unittest discover -s testJenkinsfile은 예시 pipeline으로 다음 단계를 수행합니다.
- Checkout
.env.deploy복원 및 Docker 환경 확인- dependency image 기반
compileall/unittest검증 - DB migration 실행
scripts/jenkins_deploy.sh로 Docker Compose 배포- 배포 로그와 상태 메타데이터 보관
Jenkins 배포는 Secret text credential discordbot-env에서 .env.deploy 내용을 복원하도록 구성되어 있습니다. 배포 환경 변수를 변경했다면 저장소 diff만으로는 배포 값이 바뀌지 않으므로 Jenkins credential의 Secret text payload도 같은 내용으로 갱신해야 합니다.
배포 스크립트 직접 실행 예:
ENV_FILE=.env.deploy ./scripts/jenkins_deploy.sh auto
ENV_FILE=.env.deploy ./scripts/jenkins_deploy.sh restart
ENV_FILE=.env.deploy ./scripts/jenkins_deploy.sh force- I/O, HTTP, DB 작업은 async 기반으로 작성합니다.
- HTTP client는
aiohttp를 우선 사용합니다. - 길드별 채널 설정은
util/guild/channel_settings.pyhelper를 사용합니다. - 기능 상태는
setting_data또는 전용 테이블에 저장하고 로컬 JSON 파일을 새로 만들지 않습니다. - 경로 처리는
pathlib또는bot.py의BASE_DIR기반으로 작성합니다. - 사용자에게 보이는 실패는 Discord 메시지로 안전하게 안내하고, 내부 예외는 traceback과 함께 로그에 남깁니다.
.env,.env.deploy, 내부 URL, 로컬 절대 경로, private 운영 세부사항은 커밋하지 않습니다.
OpenAI 출력 형식 적용 기준과 저장 프롬프트의 응답 계약은 구조화된 출력 안내를 참고하세요.
런타임 안전성 개선, 스키마 버전 2, 알림 재시도와 배포 복구의 계약은 안전성 개선과 재발 방지를 참고하세요.