동행복권 자동구매 시스템 - 로또 6/45 및 연금복권 720 자동화
Important
동행복권 구매지원 도구이며, 자동구매 설정시 신중한 확인 필요 모든 구매 결과 및 예치금 사용 책임은 사용자 본인에게 있음
복권 과몰입 및 중독 예방 제도 준수 동행복권 제공 제도(구매 한도, 충전 제한, 이용 시간 등) 범위 내에서만 동작하도록 설계됨. 해당 제도를 우회하거나 무력화하는 기능 미포함.
- 로그인 - 세션 저장 후 이후 단계에서 재사용
- 잔액 확인 - 구매가능 금액 조회 (화면 값이 채워질 때까지 대기)
- 조건부 충전 - 구매가능 금액이 10,000원 미만일 때만 간편충전 10,000원
- 연금복권 720 구매 - 자동 구매 (5,000원)
- 로또 6/45 구매 - 자동/수동 번호 선택 구매 (최대 5게임)
- 구매 한도 감지 - 주간 구매 한도 도달 시 자동 중단 및 알림
- ✅ 완전 자동화 - Playwright 기반 브라우저 자동화
- ✅ 두 가지 실행 방식 - 내 서버(systemd 타이머) 또는 GitHub Actions 중 선택
- ✅ OCR 키패드 인식 - 랜덤 키패드 자동 입력 (Tesseract)
- ✅ 결제 금액 및 영수증 검증 - 구매 전 금액 확인 및 구매 후 최종 영수증 확인
- ✅ 원격 환경 대응 - 로그인 제출 재시도, 이동(goto) 재시도, 타임아웃 배율 조정
- ✅ 알림 - Discord 웹훅 (선택)
같은 코드(scripts/run.sh)를 어디에서 실행할지만 다릅니다. 상황에 맞는 방식 하나만 선택하세요.
| Option A: 서버 (systemd) | Option B: GitHub Actions | |
|---|---|---|
| 실행 위치 | 내 Linux 서버/홈서버 (국내 IP) | GitHub 러너 (해외 데이터센터 IP) |
| 스케줄 | systemd 타이머 (매주 월요일 09:00 KST) | GitHub 스케줄 (매주 월요일 11:45 KST) + 수동 실행 |
| 계정 정보 | 서버의 .env 파일 |
GitHub Secrets / Variables |
| 필요한 것 | 상시 켜진 Linux 서버 | GitHub 계정 (서버 불필요) |
| 장점 | 국내 IP라 사이트 접속이 안정적 | 서버 관리 불필요 |
| 단점 | 서버 운영/관리 필요 | 해외 IP라 로그인 제출 무시, 응답 지연, 연결 끊김이 간헐적으로 발생할 수 있음 (재시도 로직으로 대부분 흡수) |
Warning
두 방식을 동시에 켜지 마세요. 같은 시각에 두 곳에서 실행되어 연금복권이 중복 구매되고 충전도 중복될 수 있습니다. 방식을 바꿀 때는 이전 방식을 먼저 끄세요 (아래 각 옵션의 "끄기" 참고).
내 Linux 서버(EC2, 홈서버 등)에서 systemd 사용자 타이머로 매주 자동 실행합니다.
git clone https://github.com/yoonbae81/lotto.git
cd lotto
cd scripts && ./setup-env.sh && cd ..Python 가상환경 생성, 의존성 설치, 브라우저 설치, .env 파일 자동 생성. Tesseract OCR은 별도 설치가 필요합니다.
sudo apt-get install tesseract-ocr # Ubuntu/Debian
brew install tesseract # macOS.env 파일을 편집해 계정 정보와 구매 설정을 입력합니다 (환경 변수 참고).
nano .env
chmod 600 .envsource .venv/bin/activate
./src/balance.py # 잔액만 조회 (충전/구매 없음)
./scripts/run.sh # 전체 워크플로우 (실제 충전/구매 수행)
./scripts/run.sh --645 # 로또 6/45만 구매 (연금복권 스킵)
./scripts/run.sh --720 # 연금복권 720만 구매 (로또 스킵)cd scripts
./install-systemd.sh서비스/타이머 파일 설치, 경로 자동 치환({{PROJECT_ROOT}}), loginctl enable-linger, 타이머 활성화까지 수행합니다. 타이머는 OnCalendar=Mon *-*-* 09:00:00 Asia/Seoul입니다.
systemctl --user status lotto.timer # 상태 확인
systemctl --user list-timers lotto.timer # 다음 실행 시간 확인
journalctl --user -u lotto.service -f # 로그 확인
systemctl --user start lotto.service # 지금 바로 실행 (실제 구매 수행)systemctl --user disable --now lotto.timer # 타이머 중지 (Option B로 옮길 때)
rm ~/.config/systemd/user/lotto.{service,timer}
systemctl --user daemon-reload서버 없이 GitHub 러너에서 직접 실행합니다.
| 항목 | 내용 |
|---|---|
| 워크플로 | .github/workflows/purchase.yml (Lotto Purchase (Run on GitHub)) |
| 실행 시점 | 매주 월요일 11:45 KST (schedule, 45 2 * * 1) + 수동 실행 (workflow_dispatch) |
| 실행 환경 | ubuntu-24.04, Python 3.14, Playwright Chromium, Tesseract OCR |
| 사용 액션 | actions/checkout@v7, actions/setup-python@v7, actions/upload-artifact@v7 (Node 24) |
| 커밋 시 동작 | 없음 (push 트리거 없음, schedule + dispatch만) |
Note
- GitHub
schedule는 UTC 기준 (월요일 11:45 KST =45 2 * * 1). 수분 지연이 발생할 수 있고, 60일간 활동 없으면 자동 비활성화 (gh workflow enable purchase.yml).
이 저장소를 본인의 GitHub 계정으로 Fork 하고, Actions 탭에서 워크플로를 활성화합니다.
워크플로는 계정 정보는 Secrets, 구매 설정과 알림 주소는 Variables에서 읽어 환경변수로 주입합니다. 러너에서는 .env를 사용하지 않습니다.
| 종류 | 이름 | 필수 | 설명 | 예시 |
|---|---|---|---|---|
| Secret | USER_ID |
✅ | 동행복권 아이디 | your_id |
| Secret | PASSWD |
✅ | 동행복권 비밀번호 | your_password |
| Secret | CHARGE_PIN |
✅ | 간편충전 PIN 6자리 | 123456 |
| Variable | AUTO_GAMES |
선택 | 로또 6/45 자동 게임 수 (0~5) | 2 |
| Variable | MANUAL_NUMBERS |
선택 | 로또 6/45 수동 번호 (JSON, 게임당 6개) | [[1,2,3,4,5,6],[7,8,9,10,11,12]] |
| Variable | REPORTER_DISCORD_WEBHOOK |
선택 | Discord 웹훅 주소 (알림용) | https://discord.com/api/webhooks/... |
AUTO_GAMES와MANUAL_NUMBERS를 합쳐 최대 5게임까지 구매됩니다. 미설정 시 기본값(0,[])이 적용됩니다.- 웹훅 주소는 노출되면 누구나 해당 채널에 글을 올릴 수 있습니다. 저장소가 공개이거나 협업자가 많다면 Variable 대신 Secret으로 등록하고
purchase.yml의vars.REPORTER_DISCORD_WEBHOOK를secrets.REPORTER_DISCORD_WEBHOOK로 바꾸세요.
저장소 Settings → Secrets and variables → Actions
- Secrets 탭 →
New repository secret→USER_ID,PASSWD,CHARGE_PIN등록 - Variables 탭 →
New repository variable→AUTO_GAMES,MANUAL_NUMBERS,REPORTER_DISCORD_WEBHOOK등록
Secret은
secrets.*, Variable은vars.*로만 읽힙니다. 탭을 잘못 고르면 값이 비어 있는 채로 실행됩니다.
REPO=OWNER/lotto # 본인 저장소
# Secrets (값은 표준입력으로 전달하면 셸 기록에 남지 않음)
printf '%s' 'your_id' | gh secret set USER_ID -R "$REPO"
printf '%s' 'your_password' | gh secret set PASSWD -R "$REPO"
printf '%s' '123456' | gh secret set CHARGE_PIN -R "$REPO"
# Variables
gh variable set AUTO_GAMES -R "$REPO" --body '2'
gh variable set MANUAL_NUMBERS -R "$REPO" --body '[[1,2,3,4,5,6],[7,8,9,10,11,12]]'
gh variable set REPORTER_DISCORD_WEBHOOK -R "$REPO" --body 'https://discord.com/api/webhooks/...'
# 확인 (Secret 값은 조회되지 않고 이름만 표시)
gh secret list -R "$REPO"
gh variable list -R "$REPO"이미 .env가 있다면 Secret 3개는 파일에서 바로 읽어 등록할 수 있습니다.
grep -E '^(USER_ID|PASSWD|CHARGE_PIN)=' .env > .secrets.env
gh secret set -f .secrets.env -R "$REPO"
rm .secrets.env- 자동 실행: 매주 월요일 11:45 KST (
schedule). 결과는 저장소 Actions 탭에서 확인 - 수동 실행: Actions 탭 →
Lotto Purchase (Run on GitHub)→Run workflow
gh workflow run purchase.yml -R "$REPO" --ref main
gh run list -R "$REPO" -w purchase.yml -L 5
gh run watch -R "$REPO" # 진행 상황
gh run view <run-id> -R "$REPO" --log-failed # 실패 로그Warning
수동 실행도 실제로 충전과 구매를 수행합니다. 테스트 목적이라도 구매가능 금액이 10,000원 미만이면 충전이 일어납니다. 로또 6/45는 주간 구매 한도가 차면 구매하지 않고 종료합니다.
- 실패 시:
failure-screenshots아티팩트에 로그인/이동 실패 화면이 3일간 보관됩니다.
purchase.yml의 env:에서 조정합니다.
| 변수 | 워크플로 값 | 기본값 | 설명 |
|---|---|---|---|
GLOBAL_TIMEOUT_MS |
60000 |
10000 |
페이지 이동/대기 타임아웃 (ms) |
TIMEOUT_SCALE |
3 |
1 |
소스의 모든 고정 타임아웃에 곱하는 배율 |
TZ |
Asia/Seoul |
- | 러너 시간대 |
HEADLESS |
미설정 (true) |
true |
브라우저 헤드리스 여부 |
gh workflow disable purchase.yml -R "$REPO" # 일시 중지 (재개는 enable)lotto/
├── .github/workflows/
│ └── purchase.yml # (Option B) 스케줄/수동 구매 워크플로
├── src/ # Python 스크립트
│ ├── balance.py # 잔액 조회
│ ├── charge.py # 예치금 충전 (간편 충전)
│ ├── login.py # 로그인 모듈 (성공 판별/재시도/goto 재시도)
│ ├── lotto645.py # 로또 6/45 구매
│ ├── pension720.py # 연금복권 720 구매
│ └── tscale.py # 타임아웃 배율 (TIMEOUT_SCALE)
├── scripts/
│ ├── run.sh # 메인 워크플로우 스크립트 (A/B 공통)
│ ├── setup-env.sh # (Option A) 환경 설정 (venv, pip, 브라우저)
│ ├── install-systemd.sh # (Option A) systemd 타이머 설치
│ ├── deploy.sh # (Option A) 서버 배포
│ └── systemd/ # (Option A) 서비스/타이머 정의
│ ├── lotto.service
│ └── lotto.timer # 매주 월요일 09:00 KST
├── .env.example # (Option A) 환경 변수 예시
├── requirements.txt # Python 의존성 (버전 미고정: 실행 시 최신 설치)
└── README.md
스크립트는 프로세스 환경변수를 우선 사용하고, 없으면 .env를 읽습니다 (python-dotenv는 기존 환경변수를 덮어쓰지 않음). Option A는 .env, Option B는 Secrets/Variables가 환경변수로 주입됩니다.
| 변수 | 설명 |
|---|---|
USER_ID |
동행복권 아이디 |
PASSWD |
동행복권 비밀번호 |
CHARGE_PIN |
간편충전 PIN 6자리 |
| 변수 | 설명 | 기본값 |
|---|---|---|
AUTO_GAMES |
로또 6/45 자동 게임 수 | 0 |
MANUAL_NUMBERS |
로또 6/45 수동 번호 (JSON) | [] |
REPORTER_DISCORD_WEBHOOK |
Discord 알림 웹훅 | (없음) |
GLOBAL_TIMEOUT_MS |
페이지 이동/대기 타임아웃 (ms) | 10000 |
TIMEOUT_SCALE |
고정 타임아웃 배율 | 1 |
HEADLESS |
헤드리스 실행 여부 | true |
TESSERACT_PATH |
Tesseract 실행 파일 경로 | 자동 탐색 |
USER_ID=myid
PASSWD=mypassword
CHARGE_PIN=123456
AUTO_GAMES=2
MANUAL_NUMBERS=[[1,2,3,4,5,6],[7,8,9,10,11,12]]
# 알림 (선택)
# REPORTER_DISCORD_WEBHOOK=https://discord.com/api/webhooks/your_webhook_url- 예치금 잔액 및 구매가능 금액 조회
- 화면 금액은 페이지 로딩 후 JS로 채워지므로, 0이 아닌 값이 나타날 때까지 최대 20초 대기하고 끝까지 0이면 새로고침 후 한 번 더 확인
- 반환값:
{'deposit_balance': int, 'available_amount': int}
- 간편충전 기능 (가상계좌 입금 아님)
- OCR 활용 랜덤 키패드 자동 인식, 지연 클릭을 통한 PIN 입력 신뢰성 확보
- 공통 로그인 모듈 (타 스크립트 import 사용)
- 로그인 성공 판별: 로그아웃 요소 또는
/login이탈(30초 대기), 제출이 무시되면 최대 2회 재시도 - 이미 로그인된 세션이면
/login이 메인으로 리다이렉트되므로 로그인 생략 page.goto네트워크 오류(ERR_*, Timeout) 시 최대 3회 재시도
- 로또 6/45 구매
- 자동/수동 번호 선택 및 장바구니 관리 (최대 5게임)
- 주간 구매 한도 및 예치금 부족 감지, 최종 구매 결과 검증
- 연금복권 720 구매
- 임의 번호 모든 조(組) 자동 선택, 고정 금액 5,000원, 결제 금액 검증
TIMEOUT_SCALE환경변수로 소스의 모든 고정 타임아웃(ms)을 배율 조정
메인 워크플로우 스크립트 (Option A/B 공통):
- 로그인 및 세션 저장
- 잔액 확인
- 조건부 충전 (구매가능 10,000원 미만 시)
- 연금복권 720 구매
- 로또 6/45 구매
--645, --720 옵션으로 한쪽만 구매할 수 있습니다.
Option A 환경 설정: Python 가상환경, 의존성, Playwright 브라우저, .env 생성
Option A systemd 타이머 설치: 서비스/타이머 파일 복사, 경로 치환, linger 활성화, 타이머 시작
- Python - Option B는 3.14, Option A는 3.10+ 권장
- Playwright - 브라우저 자동화
- Tesseract OCR - 키패드 숫자 인식
- Pillow, python-dotenv, script-reporter(알림)
- systemd (Option A) / GitHub Actions (Option B) - 스케줄링
- 한 가지 방식만 사용: systemd 타이머와 GitHub Actions 스케줄을 동시에 켜면 중복 구매/충전 발생
- 간편 충전 사용: [간편충전] 기능 사용, [가상계좌 입금] 미지원
- 충전 조건: 구매가능 금액이 10,000원 미만일 때만 충전. 잔액 판독이 실패하면 불필요한 충전이 반복될 수 있으므로, 처음 몇 번은 로그의
Balance Summary를 실제 잔액과 대조 권장 - 주간 구매 한도: 로또 6/45는 동행복권 모바일 사이트 정책에 따른 주간 구매 한도를 초과하면 구매를 시도하지 않고 종료
- OCR 정확도: 키패드 숫자 인식률 약 90-95%, 실패 시 재시도 또는 수동 확인 요망
- Option A는 Linux 전용: systemd 타이머는 Linux 전용, macOS는 launchd 필요
- 보안:
.env커밋 금지 (.gitignore 포함). Option B는 계정 정보를 Secrets에만 저장 - 테스트: 실제 사용 전
./src/balance.py로 조회 테스트 권장
- (Option B) Actions 로그의
Still on login page; retrying submit여부 확인 - (Option B) 아티팩트
failure-screenshots의 화면 확인 (아이디/비밀번호 오류 문구, 보안 절차 여부) TIMEOUT_SCALE,GLOBAL_TIMEOUT_MS를 늘려 재시도. 러너에서 계속 불안정하면 Option A(국내 IP 서버) 고려
- 로그의
Balance Summary가 실제 잔액과 다른지 확인 (deposit이 0이고available이 실제 값일 수 있음) - 구매가능 금액이
0원으로 반복되면balance.py의 선택자/대기 시간 점검
# Tesseract 재설치 (macOS)
brew reinstall tesseract
# Ubuntu/Debian
sudo apt-get install --reinstall tesseract-ocr.venv/bin/playwright install chromiumls -la .env # 위치 확인
chmod 600 .env # 권한 확인loginctl enable-linger $USER # 로그아웃 후에도 실행
systemctl --user daemon-reload
systemctl --user restart lotto.timer- cron은 UTC 기준 (월요일 11:45 KST =
45 2 * * 1). 수분 지연 가능. 60일간 활동 없으면 자동 비활성화 (gh workflow enable purchase.yml).