version 0.0.1

This commit is contained in:
hojin.jeong
2025-08-25 16:10:19 +09:00
commit e07c771438
7 changed files with 286 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
class Constants {
static BASE_URL = 'https://dhlottery.co.kr'
static API_URL = 'https://ol.dhlottery.co.kr'
static DEFAULT_SESSION_URL = `${Constants.BASE_URL}/gameResult.do?method=byWin&wiselog=H_C_1_1`
static SYSTEM_CHECK_URL = `${Constants.BASE_URL}/index_check.html`
static MAIN_PAGE_URL = `${Constants.BASE_URL}/common.do?method=main`
static LOGIN_REQUEST_URL = `${Constants.BASE_URL}/userSsl.do?method=login`
static BUY_PAGE_URL = `${Constants.API_URL}/olotto/game/game645.do`
static BUY_LOTTO_645_URL = `${Constants.API_URL}/olotto/game/execBuy.do`
static BUY_READY_SOCKET_URL = `${Constants.API_URL}/olotto/game/egovUserReadySocket.json`
static ROUND_INFO_URL = `${Constants.BASE_URL}/common.do?method=main`
static DEFAULT_TIMEOUT = 10 * 1000
static DEFAULT_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
'sec-ch-ua-mobile': '?0"',
'Upgrade-Insecure-Requests': '1',
'Origin': Constants.BASE_URL,
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Referer': Constants.BASE_URL,
'Sec-Fetch-Site': 'same-site',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Accept-Language': 'ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7',
}
static LOTTO_BUY_TYPES = {
'1': '수동',
'2': '반자동',
'3': '자동'
}
static CONFIG_FILENAME = process.env.LOTTO_AUTOMATION_CONFIG_FILENAME ?? 'Config.json'
static CONFIG_PATH = `${process.env.LOTTO_AUTOMATION_CONFIG_DIR ?? '.'}/${Constants.CONFIG_FILENAME}`
}
module.exports = Constants
+122
View File
@@ -0,0 +1,122 @@
const Axios = require('axios')
const Cheerio = require('cheerio')
const Constants = require('./constants')
class Lotto {
static #SessionID = undefined
static #LottoDirect = undefined
static #_request(method, url, data) {
const requestOpts = {
method,
url,
data,
headers: Constants.DEFAULT_HEADERS,
timeout: Constants.DEFAULT_TIMEOUT,
}
if(this.#SessionID)
requestOpts.headers.Cookie = this.#SessionID
return Axios(requestOpts).catch(err => err)
}
static async #_syncSession() {
const sessionResponse = await this.#_request('GET', Constants.DEFAULT_SESSION_URL)
if(sessionResponse instanceof Error)
return sessionResponse
if(sessionResponse.request.res.responseUrl === Constants.SYSTEM_CHECK_URL)
return new Error('동행복권 사이트가 현재 시스템 점검중입니다.')
const sessionId = sessionResponse.headers['set-cookie']
.find(cookieStr => cookieStr.indexOf('JSESSIONID') !== -1)
if(!sessionId)
return new Error('쿠키가 정상적으로 세팅되지 않았습니다.')
this.#SessionID = sessionId
}
static async #_getRound() {
const roundResponse = await this.#_request('GET', Constants.ROUND_INFO_URL)
if(roundResponse instanceof Error)
return roundResponse
const $ = Cheerio.load(roundResponse.data)
const lastDrawnRound = parseInt($("strong#lottoDrwNo").text(), 10)
const round = lastDrawnRound + 1
return round
}
static async Login(userId, userPw) {
const sessionSyncError = await this.#_syncSession()
if(sessionSyncError instanceof Error)
return sessionSyncError
const loginData = {
returnUrl: Constants.MAIN_PAGE_URL,
userId,
password: userPw,
checkSave: 'off',
newsEventYn: '',
}
const loginResult = await this.#_request('POST', Constants.LOGIN_REQUEST_URL, loginData)
if(loginResult instanceof Error)
return loginResult
const $ = Cheerio.load(loginResult.data)
const btnCommonElements = $('a.btn_common')
if(btnCommonElements.length > 0)
return new Error('로그인에 실패했습니다. 아이디 또는 비밀번호를 확인해주세요.')
return true
}
static async Buy(lotteryInputs = []) {
if(lotteryInputs.length === 0)
return new Error('구매할 로또 정보를 입력해주세요.')
if(lotteryInputs.length > 5)
lotteryInputs = lotteryInputs.slice(0, 5)
const round = await this.#_getRound()
const readySocketResponse = await this.#_request('POST', Constants.BUY_READY_SOCKET_URL)
if(readySocketResponse instanceof Error)
return readySocketResponse
this.#LottoDirect = readySocketResponse.data.ready_ip
const requestParams = lotteryInputs.map((lotto, idx) => {
return {
genType: lotto === 'auto' ? '0' : '1',
arrGameChoiceNum: lotto === 'auto' ? null : lotto.map(num => String(num).padStart(2, 0)).join(','),
alpabet: String.fromCharCode(65 + idx)
}
})
const requestData = {
round: String(round),
direct: this.#LottoDirect,
nBuyAmount: `${requestParams.length * 1000}`,
param: JSON.stringify(requestParams),
gameCnt: `${requestParams.length}`,
}
const buyResponse = await this.#_request('POST', Constants.BUY_LOTTO_645_URL, requestData)
if(buyResponse instanceof Error)
return buyResponse
const { result } = buyResponse.data
if ((result.resultMsg || "FAILURE").toUpperCase() !== "SUCCESS")
return new Error(result.resultMsg)
return {
round: result.buyRound,
barcode: Object.keys(result)
.filter(k => k.startsWith('barCode'))
.map(k => result[k])
.join(' '),
cost: result.nBuyAmount,
numbers: result.arrGameChoiceNum,
message: result.resultMsg,
}
}
}
module.exports = Lotto
+29
View File
@@ -0,0 +1,29 @@
const Axios = require('axios')
class NTFY {
static #url
static #token
static Init(url, token) {
this.#url = url
this.#token = token
}
static Send(message, tags) {
if(!this.#url || !this.#token)
return
const requestOpts = {
method: 'POST',
url: this.#url,
headers: {
Authorization: `Bearer ${this.#token}`,
Tags: tags,
},
data: message
}
return Axios(requestOpts).catch(err => err)
}
}
module.exports = NTFY