Files
baekjoon-bot/README.md
sm4640 bd045b43c2
Some checks failed
baekjoon-bot-cicd / build_push_deploy (push) Failing after 1m34s
Fix: [main] 리드미 수정 및 cicd md 파일 제외
2026-03-26 00:26:44 +09:00

5.5 KiB

baekjoon-bot

매일 백준(BOJ) 문제를 추천하고 Discord embed payload로 반환하는 FastAPI 서비스. solved.ac 검색 기반 추천과 PostgreSQL 문제집(workbook) 기반 추천 두 가지 모드를 지원하며, n8n 등 외부 스케줄러와 연동하여 Discord 자동 알림을 구성할 수 있다.

주요 기능

  • Search 모드: solved.ac API로 난이도/태그/언어 조건에 맞는 문제를 랜덤 추천
  • Workbook 모드: DB에 저장된 문제집에서 아직 보내지 않은 문제를 순서대로 또는 랜덤으로 추천
  • Discord embed: 모든 응답에 Discord webhook용 embed payload 포함
  • Admin API: 문제집 메타데이터 보강(enrich), 진행상황 초기화(reset)

기술 스택

  • Python 3.12+ / FastAPI / Uvicorn
  • SQLAlchemy 2.0 (async) + asyncpg (PostgreSQL)
  • httpx (비동기 HTTP) / requests (동기 HTTP)
  • Docker / Gitea Actions CI/CD

빠른 시작

git clone https://nkeystudy.site/gitea/nkey/baekjoon-bot.git
cd baekjoon-bot

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

# .env 파일 생성
cat > .env <<'EOF'
DATABASE_URL=postgresql+asyncpg://USER:PASSWORD@HOST:5432/DBNAME
ADMIN_PASSWORD=change-me
EOF

uvicorn app:app --host 0.0.0.0 --port 8000

Docker로 실행:

docker build -f dockerfile -t baekjoon-bot:local .
docker run --rm -p 8000:8000 --env-file .env baekjoon-bot:local

API 엔드포인트

Method Endpoint 설명 인증
GET / 헬스체크 -
GET /today 오늘의 추천 문제 -
POST /admin/workbooks/{id}/enrich 문제집 메타데이터 보강 O
DELETE /admin/workbooks/{id}/reset 문제집 발송 기록 초기화 O

사용 예시

# 기본 추천 (search 모드)
curl -s "http://localhost:8000/today"

# 난이도/태그/언어 지정
curl -s "http://localhost:8000/today?difficulty=6..10&tags=dp,graphs&lang=ko"

# 문제집 모드
curl -s "http://localhost:8000/today?source_mode=workbook&workbook_id=12345"

# 문제집 메타 보강
curl -s -X POST -H "X-Admin-Password: change-me" \
  "http://localhost:8000/admin/workbooks/12345/enrich"

# 문제집 진행상황 초기화
curl -s -X DELETE -H "X-Admin-Password: change-me" \
  "http://localhost:8000/admin/workbooks/12345/reset"

Workbook 모드 셋업

Workbook 모드를 사용하려면 DB에 문제집 데이터를 직접 넣어야 한다. (BOJ 자동 크롤링은 스크래핑 차단으로 현재 미지원)

1. 문제집 등록

-- id는 BOJ 문제집 URL의 번호 (acmicpc.net/workbook/view/12345)
INSERT INTO workbooks (id, title)
VALUES (12345, '내 문제집 이름');

2. 문제 목록 등록

-- 문제 번호만 넣으면 됨 (제목/난이도/태그는 3단계에서 자동으로 채워짐)
INSERT INTO workbook_problems (workbook_id, problem_id) VALUES
(12345, 1000),
(12345, 1001),
(12345, 1002);

3. 메타데이터 자동 보강

# solved.ac API로 제목, 난이도, 태그를 자동으로 가져와서 DB에 채움
curl -s -X POST \
  -H "X-Admin-Password: change-me" \
  "http://localhost:8000/admin/workbooks/12345/enrich"

이후 /today?source_mode=workbook&workbook_id=12345로 문제를 추천받을 수 있다. 모든 문제를 다 뽑은 뒤 다시 처음부터 시작하려면 reset API를 호출하면 된다.

환경변수

변수 설명 기본값 필수
DATABASE_URL PostgreSQL 비동기 연결 URL - O
ADMIN_PASSWORD Admin API 비밀번호 "" Admin API 사용 시
SOURCE_MODE_DEFAULT 기본 소스 모드 (search/workbook) search
WORKBOOK_ID_DEFAULT workbook 모드 기본 ID -
DIFFICULTY_MODE_DEFAULT 기본 난이도 모드 (easy/hard/all) easy
TAG_MODE_DEFAULT 기본 태그 모드 easy
LANG_DEFAULT 기본 언어 필터 all
DIFFICULTY_EASY easy 난이도 범위 6..10
DIFFICULTY_HARD hard 난이도 범위 11..15
DIFFICULTY_ALL all 난이도 범위 1..30
TAGS_EASY / TAGS_HARD / TAGS_ALL 태그 프리셋 (CSV) ""
TAG_PICK / TAG_PICK_EASY 태그 선택 정책 (random/none/전체) random
TAGS_JOIN 태그 결합 방식 (or/and) or

프로젝트 구조

app.py                 # FastAPI 엔트리포인트, 라우팅
utils.py               # 환경변수 헬퍼, solved.ac 검색 쿼리 빌드, HTTP 호출
db.py                  # SQLAlchemy async 엔진/세션
workbook_picker.py     # 문제집에서 미발송 문제 1개 선택 + 발송 기록
workbook_enricher.py   # solved.ac로 문제 메타데이터 채우기
workbook_importer.py   # BOJ 문제집 크롤링 (현재 미사용)
dockerfile             # Docker 이미지 정의
requirements.txt       # Python 의존성

CI/CD

  • Workflow: .gitea/workflows/cicd.yml
  • Trigger: main 브랜치 push (단, 문서 파일만 변경된 경우 스킵)
  • Flow: 수동 checkout → Docker Hub 빌드/푸시 → 서버 배포(compose.apps.yml) → Discord 알림

필요한 Secrets

Secret 용도
NKEY_PAT Gitea repo fetch 인증
DOCKERHUB_USERNAME Docker Hub 네임스페이스
DOCKERHUB_TOKEN Docker Hub 로그인
DISCORD_WEBHOOK CI/CD 결과 알림

상세 문서