k-lottery
동행복권 로또 6/45를 대신 사고, 잔액도 보고, 당첨됐는지도 확인해주는 도구예요.터미널에서 명령어로 써도 되고, Claude Code나 Claude Desktop에서 말로 시켜도 돼요.CLI 4개랑 MCP 서버가 같은 코어를 써요.
직접 쓰려고 만든 개인용 도구예요. 진짜 돈이 나가니까 쓰기 전에면책을 꼭 읽어주세요.
$ uv run scripts/balance.py
구매가능금액 12,000 원
총 예치금 12,000 원
마일리지 500 원
오늘 구매액 0 / 150,000 원
이번주 구매액 5,000 원
최근 1달 21,000 원
고정 가상계좌 7010XXXXXXXXXX
목차
- 무엇을 할 수 있나
- 요구사항
- 빠른 시작
- MCP 서버 설정
- 안전장치
- CLI 사용법
- 환경변수
- 종료 코드
- 자동화 예시 (cron)
- 알아둘 점
- 개발
- 기여 · 면책 · 라이선스
무엇을 할 수 있나
| CLI | 하는 일 |
|---|---|
scripts/balance.py |
예치금·구매 한도·서약 유효성 |
scripts/check.py |
구매 내역과 당첨 결과 (등수는 서버가 계산) |
scripts/deposit.py |
충전용 가상계좌 발급 (이체는 직접) |
scripts/buy.py |
자동번호 구매 (기본 dry-run) |
| MCP 도구 | 파라미터 | 노출 | 하는 일 |
|---|---|---|---|
lotto_status |
— | 항상 | 잔액·한도·서약 유효성·가상계좌(뒤 4자리만) |
lotto_results |
days=14, round_no? |
항상 | 구매 내역 + 당첨 결과 |
lotto_draw |
round_no |
항상 | 공개 추첨 결과 (로그인 불필요) |
lotto_buy_precheck |
games=5 |
항상 | 구매 전 점검만. 절대 사지 않음 |
lotto_buy |
games=5 |
게이트 | 실제 구매 (승인 필수) |
lotto_deposit |
amount |
게이트 | 가상계좌 발급 (승인 필수) |
요구사항
| uv | 필수예요. Python 3.13은 uv가 .python-version을 보고 알아서 받아와요 |
| 동행복권 계정 | 아이디/비밀번호로 로그인하는 계정이요 |
| 건전구매 서약 | 사이트에서 직접 하셔야 해요. 만료됐으면 구매가 막혀요 (종료코드 3) |
| 예치금 | 충전하려면 은행 이체가 필요해서 완전 무인화는 안 돼요 |
| OS | macOS에서 확인했어요. Linux도 될 것 같고, Windows는 안 해봤어요 |
빠른 시작
git clone https://github.com/rajephon/k-lottery.git
cd k-lottery
uv sync
cp .env.example .env # 아이디·비밀번호 채우기 (.env는 커밋되지 않아요)
uv run scripts/balance.py
MCP 서버 설정
CLI와 같은 코어 위에 FastMCP(stdio) 서버를 올려뒀어요. 잔액이나 당첨 결과를 대화로 물어볼 수있고, 원하면 구매·충전까지 열 수 있어요. 다만 실제로 사는 건 매번 직접 승인하셔야 해요.
Claude Code에 등록
# 조회 전용 (기본). 이 저장소 디렉터리에서만 보여요
claude mcp add klottery -- uv run --directory /절대경로/k-lottery scripts/mcp_server.py
# 어느 디렉터리에서든 쓰려면 user 스코프로
claude mcp add klottery -s user -- uv run --directory /절대경로/k-lottery scripts/mcp_server.py
# 구매·충전까지 열려면 게이트를 환경변수로 넘겨요
claude mcp add klottery -s user \
-e KLOTTERY_MCP_ALLOW_BUY=1 -e KLOTTERY_MCP_ALLOW_DEPOSIT=1 -- \
uv run --directory /절대경로/k-lottery scripts/mcp_server.py
게이트는
.env에 적어도 안 열려요.-e옵션이나 Desktop 설정의env블록처럼서버를 띄우는 프로세스 환경에 넣어야 해요..env는 모델이 고칠 수 있는 파일이거든요.거기서 스위치를 읽으면 "모델이 못 건드리는 곳에 스위치를 둔다"는 전제가 무너져요.
게이트는 서버가 뜰 때 한 번 읽고 끝이에요. 나중에 켜거나 끄려면 등록을 지우고 다시 만든 뒤클라이언트를 재시작하세요 (claude mcp remove klottery).
Claude Desktop에 등록
설정 파일을 열어요. 없으면 새로 만들면 돼요.
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"klottery": {
"command": "/opt/homebrew/bin/uv",
"args": ["run", "--directory", "/절대경로/k-lottery", "scripts/mcp_server.py"],
"env": { "KLOTTERY_MCP_ALLOW_BUY": "1" }
}
}
}
command에는which uv로 나온 절대경로를 적어요. GUI 앱은 셸 PATH를 물려받지 않거든요.--directory뒤도 절대경로여야 해요.env를 빼면 조회 도구 4개만 떠요.- 저장했으면 Claude Desktop을 완전히 껐다가 다시 켜주세요.
Claude Desktop이 승인 프롬프트(elicitation)를 지원하는지는 2026년 7월 현재 확인하지못했어요. 지원하지 않으면 조회 도구만 동작하고 구매·충전은
[APPROVAL_UNSUPPORTED]로실패해요. 그럴 땐 CLI를 쓰시면 돼요.
잘 붙었는지 확인
claude mcp list # klottery가 ✔ Connected 인지 봐요
Claude Code에서 /mcp를 치면 어떤 도구가 올라왔는지 보여요. 조회 4개만 있으면 조회 전용이고,lotto_buy까지 보이면 게이트가 열린 거예요. 서버 로그(stderr)에도 남아요.
돈 도구 노출: buy=True deposit=False
구매 승인 화면
모델이 lotto_buy를 부르면 클라이언트가 사람에게 이 화면을 띄워요. 금액을 직접 쳐야넘어가요. 모델은 이 답변 경로에 끼어들 수 없어요.
로또 6/45 구매 승인 요청
대상 회차 1236회 (추첨 2026/08/08)
게임 수 5게임 (자동)
결제 금액 5,000원
구매가능금액 12,000원 → 구매 후 7,000원
오늘 누적 0원 / 한도 150,000원
승인하려면 결제 금액(원)을 숫자로 입력하세요.
※ 이 요청은 AI 모델이 호출한 것입니다. 방금 직접 지시하지 않았다면 거절하세요.
문제 해결
| 증상 | 원인과 해결 |
|---|---|
| Claude Code에 klottery가 안 보여요 | 기본 스코프가 local이라 그래요. 다른 디렉터리에서도 쓰려면 -s user로 다시 등록하세요 |
lotto_buy가 목록에 없어요 |
게이트를 .env에 넣으셨을 거예요. -e로 옮기고 다시 등록한 뒤 재시작하세요 |
| Desktop에서 서버가 안 떠요 | command를 which uv 절대경로로 바꿔보세요. GUI 앱은 셸 PATH를 못 봐요 |
[APPROVAL_UNSUPPORTED] |
클라이언트가 승인 프롬프트를 지원하지 않아요. CLI로 처리하세요 |
[RECENTLY_DECLINED] |
직전 승인을 거절했거나 취소했어요. 10분 뒤에 다시 하거나 CLI로 처리하세요 |
[CLOSED] |
판매시간(06:00~24:00, 토요일은 20:00 마감)이 아니에요. 시간 지나면 풀려요 |
[CONFIG] 건전구매 서약… |
사이트에서 직접 재서약하셔야 해요 |
[BALANCE] |
예치금이 모자라요. 가상계좌를 받아서 직접 이체해야 충전돼요 |
안전장치 (돈이 움직이는 경로)
CLI는 구매·충전이 기본 dry-run이고, --execute를 붙여야 실제로 실행돼요. 확인 프롬프트는stdin이 tty가 아니면 그냥 거절해요. cron 같은 데서 얼떨결에 결제되는 걸 막으려고요.
MCP엔 tty도 --execute도 없어서 다른 방법이 필요했어요. execute: bool 같은 파라미터를두면 모델이 알아서 true를 넣어버릴 수 있거든요. 그건 승인이 아니죠. 그래서 층을 나눴어요(자세한 근거는 설계 문서에 있어요).
- 노출 — 구매·충전은 서버를 띄우는 프로세스 환경에
KLOTTERY_MCP_ALLOW_*가 있어야만목록에 떠요. 모델은 실행 중에도,.env를 고쳐서도 이걸 못 바꿔요. - 승인 — 실행 직전에 사람이 결제 금액을 직접 입력해야 해요. 프롬프트에는 이 요청을모델이 했다는 사실도 같이 띄워요. 프롬프트 주입으로 모델이 멋대로 부른 경우를 구분하시라고요.
- 막히면 멈춰요 — 승인 프롬프트를 지원하지 않는 클라이언트에서는 그냥 실패해요.네트워크 요청은 한 번도 나가지 않아요.
- 조르기 차단 — 승인 요청은 한 번에 하나만 떠요. 거절·취소·중단하거나 금액을 틀리게입력하면 10분 동안 다시 묻지 않아요. 구매와 충전이 이 쿨다운을 같이 써요.
- 상한 — 한 번에 최대 5게임(5,000원)이고, 중복 구매를 뚫는
--force는 MCP에 없어요.승인을 받은 뒤에도 서버 데이터를 새로 받아 점검을 다시 돌려요. - 감사 — 모든 시도가
data/mcp-audit.jsonl에 남아요. 상관 ID, 결제 요청을 실제로보냈는지, 무슨 오류였는지까지 기록해요. 구매 응답 원문은 성공하든 실패하든 저장하고,기록에 실패해도 결제 결과를 뒤집지 않아요. - 정보 차단 — 도구 설명이나 반환값에 우회 방법(명령줄, 플래그)을 적지 않아요. 그걸 읽는 건사람이 아니라 모델이니까요. 승인에 걸린 시간, 계좌번호 전체, 계좌주 실명, 계정 아이디도모델에게 넘기지 않아요.
구매 요청을 보내기 전에 다섯 가지를 확인해요. 하나라도 걸리면 요청 자체를 안 보내요.
- 게임 수가 1~5인지
- 건전구매 서약이 살아 있는지
- 같은 회차를 이미 샀는지 (서버 구매내역 기준이에요. CLI의
--force로만 넘길 수 있어요) - 일 구매한도를 넘지 않는지
- 잔액이 충분한지
CLI 사용법
네 스크립트 모두 --json(결과 JSON을 stdout으로), -v(DEBUG 로그, stderr),--env-file <경로>를 받아요. 사람이 읽는 로그는 stderr로, 파이프로 넘길 데이터는 stdout으로나가요.
잔액 확인
uv run scripts/balance.py
uv run scripts/balance.py --json
당첨 확인
uv run scripts/check.py # 최근 14일
uv run scripts/check.py --days 30
uv run scripts/check.py --round 1234
등수는 서버가 계산해서 줘요. 내가 산 번호를 따로 저장해둘 필요가 없어요.사지 않은 회차를 물어보면 공개 추첨 결과를 대신 보여줘요.
충전
uv run scripts/deposit.py --amount 5000 # dry-run
uv run scripts/deposit.py --amount 5000 --execute
넣을 수 있는 금액은 5000 10000 20000 30000 50000 100000 150000이고,하루 15만원까지예요.
이 명령만으로는 돈이 움직이지 않아요. 고정 가상계좌에 "얼마 넣을 거다"라고 등록하는게 전부예요. 출력된 계좌로 직접 이체해야 예치금이 들어와요.
구매
uv run scripts/buy.py # dry-run (기본 5게임)
uv run scripts/buy.py --games 5 --execute
uv run scripts/buy.py --execute --yes # cron용
자동번호로 사요. 기본은 dry-run이라 --execute를 붙여야 실제로 결제돼요.
환경변수
계정 정보는 저장소 루트의 .env에 넣어요(cp .env.example .env). 이 파일은 커밋되지 않아요.
| 환경변수 | 설명 | 기본값 |
|---|---|---|
LOTTO_USER_ID |
동행복권 아이디 | (필수) |
LOTTO_PASSWORD |
동행복권 비밀번호 | (필수) |
LOTTO_DATA_DIR |
쿠키·이력 저장 위치 | ./data |
LOTTO_TIMEOUT |
HTTP 타임아웃(초) | 10 |
LOTTO_REQUEST_INTERVAL |
연속 요청 최소 간격(초) | 0.3 |
LOTTO_USER_AGENT |
User-Agent | 크롬 |
MCP 결제 도구를 여는 스위치는 .env가 아니라 서버를 띄우는 프로세스 환경에서 읽어요.
| 환경변수 | 설명 | 기본값 |
|---|---|---|
KLOTTERY_MCP_ALLOW_BUY |
lotto_buy 노출 (1/true/yes/on) |
(안 뜸) |
KLOTTERY_MCP_ALLOW_DEPOSIT |
lotto_deposit 노출 |
(안 뜸) |
접두사를 다르게 둔 건 일부러예요.
LOTTO_*는.env로 들어오는 설정이고,KLOTTERY_MCP_*는.env로 들어오면 안 되는 스위치거든요.
종료 코드
cron에서 실패 원인을 구분하시라고 나눠뒀어요.
| 코드 | 의미 | 코드 | 의미 |
|---|---|---|---|
| 0 | 성공 | 7 | 자동화 차단 (isAllowed=N) |
| 1 | 일반 오류 | 8 | 이미 구매한 회차 |
| 2 | 잘못된 인자·값 | 9 | 판매시간 아님·점검중 |
| 3 | 설정/자격증명 | 10 | 접속 대기열 |
| 4 | 인증 실패 | 11 | 사용자가 확인을 거부 |
| 5 | 서버 응답 이상 | 130 | 중단 |
| 6 | 잔액 부족 |
재시도해서 의미 있는 건 9번과 10번뿐이에요. 6번이나 8번은 몇 번을 돌려도 결과가 같아요.
자동화 예시 (cron)
# uv는 절대경로로 적어요. cron의 PATH는 셸과 다르거든요 (`which uv` 결과를 쓰면 돼요)
# 매주 월요일 19:00 구매, 일요일 09:00 당첨 확인
0 19 * * 1 cd /절대경로/k-lottery && /opt/homebrew/bin/uv run scripts/buy.py --execute --yes >> data/cron.log 2>&1
0 9 * * 0 cd /절대경로/k-lottery && /opt/homebrew/bin/uv run scripts/check.py --days 7 >> data/cron.log 2>&1
buy.py는 같은 회차를 이미 샀는지 서버 내역으로 확인하니까 두 번 돌아도 중복으로 사지 않아요.판매시간(06:00~24:00, 토요일은 20:00 마감)이 아니면 종료코드 9로 끝나요.
예치금이 떨어지면 종료코드 6으로 실패해요. 충전은 은행 이체가 필요해서 여기까지가 한계예요.
알아둘 점
- 사이트가 2026년에 개편됐어요. 인터넷에 돌아다니는 기존 봇 코드(
common.do?method=...,userSsl.do?method=login)는 전부 죽은 엔드포인트를 써요. - 구매 요청에 자동화 탐지(
isAllowed=="N")가 있어요. 아직 걸려본 적은 없지만, 걸리면종료코드 7로 확실하게 실패해요. - 모바일 구매는 토·일에 막혀요. 이 도구는 PC 경로를 써요.
- 판매시간이 아니면 사이트가 HTTP 200으로 멀쩡한 페이지를 주면서 구매 폼만 빼놔요.이 경우를
[CLOSED]로 따로 구분해요.
개발
uv run pre-commit install # 훅 설치 (최초 1회)
uv run ruff check --fix . # 린트
uv run ruff format . # 포맷
uv run ty check # 타입 검사
uv run pytest # 테스트 (네트워크 안 탐)
테스트는 실제 네트워크로 나가지 않아요. httpx 호출은 respx로 목킹하고, 소켓 가드가 이 규약을강제해요. 목킹을 빠뜨린 테스트는 통과가 아니라 실패해요.
src/klottery/
├── endpoints.py URL·필드명·상수 — 사이트가 바뀌면 여기만 고친다
├── dhlottery.py 사이트 클라이언트
├── models.py 응답 파싱
├── flows.py 구매·충전·조회 조립 (CLI·MCP 공유)
├── payloads.py JSON 빌더 (CLI --json과 MCP 출력의 단일 계약)
├── purchase.py 구매 전 점검 (순수 함수)
├── session.py httpx 세션 + 쿠키 영속화
├── crypto.py 로그인 RSA
├── clock.py KST 시각·날짜 계산
├── cli.py 공통 인자·종료코드 (CLI 전용)
├── config.py 설정
├── errors.py 도메인 예외
├── log.py 로깅·비밀값 마스킹
├── storage.py data/ 원자적 쓰기
└── mcp/ MCP 서버 배선 — 도메인 로직은 안 둔다
├── server.py 조립 + 노출 게이트
├── tools.py 도구 정의
├── confirm.py 승인·쿨다운 (유일 접점)
├── runtime.py 락·에러 변환·감사 로그
└── errors.py 도메인 예외 → ToolError 코드 표
scripts/ 진입점 5개 (CLI 4개 + mcp_server.py)
tests/ pytest (respx 목킹)
data/ 쿠키·이력 (gitignore)
- 작업 규약: CLAUDE.md
- 사이트 API 실측 스펙: docs/dhlottery-api-spec.md
- 설계: docs/2026-07-26_dhlottery-cli.md ·docs/2026-07-26_klottery-mcp-server.md
기여
개인용 도구라 기능 요청은 받지 않아요. 사이트가 바뀌어서 깨진 부분이라면 이슈나 PR을 환영해요.다만 그럴 땐 docs/dhlottery-api-spec.md에 실제로 캡처한 요청·응답을 같이 올려주세요.추측으로 쓴 엔드포인트는 머지하지 않아요.
면책
- 본인 계정에 쓰라고 만든 개인용 도구예요. 남의 계정에 쓰라고 만든 게 아니에요.
- 동행복권 이용약관이나 자동화 정책에 걸리는지는 쓰는 분이 판단하셔야 해요. 구매 응답에자동화 탐지(
isAllowed=="N")가 있고, 계정이 정지될 수 있어요. - 진짜 돈이 나가요. 잘못 사거나 잘못 충전해서 생긴 손해는 쓰는 분 몫이에요.
- 복권은 사행성 상품이에요. 자동화하면 나도 모르게 사는 횟수가 늘어요. 동행복권 구매한도랑이 도구 상한을 같이 걸어두시길 권해요. 혹시 도박 문제로 힘드시다면 국번 없이 1336으로전화해 보세요.
- 사이트가 개편되면 언제든 안 돌아갈 수 있어요. 아무것도 보증하지 않아요(AS-IS).
라이선스
MIT