From 20298eee1a26bbf97959b350ce7f05af80d910e3 Mon Sep 17 00:00:00 2001 From: space2lim Date: Mon, 12 Feb 2024 16:47:20 +0900 Subject: [PATCH] init --- cgvAjax.js | 525 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 cgvAjax.js diff --git a/cgvAjax.js b/cgvAjax.js new file mode 100644 index 0000000..41174cb --- /dev/null +++ b/cgvAjax.js @@ -0,0 +1,525 @@ +import * as fs from 'fs'; +import axios from 'axios'; +import dotenv from 'dotenv'; +import moment from 'moment'; +import puppeteer from 'puppeteer'; +import TelegramBot from 'node-telegram-bot-api'; +import _ from 'lodash'; +import * as util from './commonUtil.js'; +import * as crypto from './cryptoUtil.js'; + +dotenv.config(); +const COOKIE = process.env.COOKIE; +const BOT_TOKEN = process.env.BOT_TOKEN; +const BOT_CHATID = process.env.BOT_CHATID; +const bot = new TelegramBot(BOT_TOKEN, { polling: true }); + +const __INFO__ = async (str) => { + const prefix = '[INFO]' + await bot.sendMessage(BOT_CHATID, `${prefix} ${str}`); + console.log(`${prefix} ${str}`); +} +const __ERROR__ = async (str) => { + const prefix = '[ERROR]' + await bot.sendMessage(BOT_CHATID, `${prefix} ${str}`); + console.error(`${prefix} ${str}`); +} + +class cgvGetter { + constructor(cookie) { + this.seatAspx = 'https://m.cgv.co.kr/WebApp/Reservation/seat.aspx?'; + this.loginAspx = 'https://m.cgv.co.kr/Webapp/Member/Login.aspx?'; + } + + // 에러 발생 시 null 반환 + async _post(postUrl, payload, headers) { + return axios.post(postUrl, payload, { headers, timeout: 3000 }) + .then(response => { + return { cookies: response.headers['set-cookie'], data: response.data }; + }) + .catch(error => { + __ERROR__('\t> Post request failed:', error.message); + return null; + }); + } + + async _get(getUrl, headers) { + return axios.get(getUrl, { headers, timeout: 3000 }) + .then(response => { + return { cookies: response.headers['set-cookie'], data: response.data }; + }) + .catch(error => { + __ERROR__('\t> Get request failed:', error.message); + return null; + }); + } + + // 가중치 계산식 + calculateSeatWeight(seatPosition, centerPosition) { + // 중앙점 설정 + const centerX = centerPosition.x; + const centerY = centerPosition.y.charCodeAt(0) - 'A'.charCodeAt(0) + 1; + // 좌석 위치 설정 + const seatX = seatPosition.x; + const seatY = seatPosition.y.charCodeAt(0) - 'A'.charCodeAt(0) + 1; + // 중앙으로부터의 수평/수직 거리 계산 + const horizontalDistance = Math.abs(seatX - centerX); + const verticalDistance = Math.abs(seatY - centerY); + // 비선형 거리 가중치 + const distanceWeight = Math.pow(horizontalDistance + verticalDistance, 2); + // 앞쪽 행 선호도를 직접 반영 + const rowPreference = (centerY > seatY) ? (centerY - seatY) * 0.00001 : 0; + // 총 가중치 계산 - 거리 가중치와 앞쪽 행 선호도를 합산 + const totalWeight = distanceWeight - rowPreference; + return totalWeight; + } + // calculateSeatWeight(seat, centerSeat) { + // const horizontalDistance = Math.abs(seat.x - centerSeat.x); + // const verticalDistance = Math.abs(seat.y.charCodeAt(0) - centerSeat.y.charCodeAt(0)); + + // let positionWeight; + // if (horizontalDistance === 0 && verticalDistance === 0) { + // positionWeight = 1.0; + // } else if (horizontalDistance === 1 && verticalDistance === 0) { + // positionWeight = 0.9; + // } else if (horizontalDistance === 0 && verticalDistance === 1) { + // positionWeight = seat.y === centerSeat.y ? 0.85 : 0.8; + // } else { + // positionWeight = 1.0 - (horizontalDistance + verticalDistance) / 10.0; + // } + + // const seatPreference = horizontalDistance + verticalDistance - positionWeight; + + // return seatPreference + 0.1; + // } + + // 인접한 예매 가능 좌석 확인 - 넘겨받은 좌석 수 포함한 값 반환 + countAdjacentSeats(seats, seatname) { + const seatInfo = seats.find(seat => seat.seatname === seatname); + if (!seatInfo) return -1; + + const startX = Math.min(...seats.map(seat => seat.locxnm)); + const endX = Math.max(...seats.map(seat => seat.locxnm)); + + const row = seatInfo.locynm; + const col = seatInfo.locxnm; + let leftAdjacentCount = 0; + let rightAdjacentCount = 0; + + // 왼쪽 + for (let i = col - 1; i >= startX; i--) { + const adjacentSeat = seats.find(seat => seat.locynm === row && seat.locxnm === i); + if (adjacentSeat) leftAdjacentCount++; + else break; // 연속된 좌석이 아닌 경우 반복문을 종료합니다. + } + // 오른쪽 + for (let i = col + 1; i <= endX; i++) { + const adjacentSeat = seats.find(seat => seat.locynm === row && seat.locxnm === i); + if (adjacentSeat) rightAdjacentCount++; + else break; // 연속된 좌석이 아닌 경우 반복문을 종료합니다. + } + return leftAdjacentCount + rightAdjacentCount + 1; + } + + + async getResultData(mgCD = null, ymd = null, td = null, fmac = null, fsrc = null) { + const postUrl = "https://m.cgv.co.kr/WebAPP/Reservation/Common/ajaxTheaterScheduleList.aspx/GetTheaterScheduleList"; + const param = { + strRequestType: "COMPARE", // 영화별예매 : MOVIE, 극장별예매/상영시간표 : THEATER, 비교예매 : COMPARE + strUserID: '', + strPlayYMD: ymd ? ymd : '', // 비어있을 시 가장 빠른 날짜 + strMovieGroupCd: mgCD ? mgCD : '20035290', // 영화 그룹코드 + strTheaterCd: td ? td : '0013', + strMovieTypeCd: fmac ? fmac : '04', // 영화 속성 IMAX + strScreenTypeCd: '', + strRankType: 'MOVIE' + }; + const headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36', + 'Accept-Language': 'ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7,de;q=0.6', + 'Content-Type': 'application/json', + Origin: 'https://m.cgv.co.kr', + Cookie: COOKIE + }; + const result = await this._post(postUrl, param, headers); + return result + } + + async main_dev(mgCD = '20035290', ymd = null, dateAft = moment('2024-03-09', 'YYYY-MM-DD')) { + const generalSeatCnt = 2; + // const startTime = moment('2024-02-05'); + // const endTIme = moment('2024-02-12'); + const tmRange = { start: '1100', end: '1800' }; + let targetYMD = null; + let targetSchedule = null; + + let attemptCnt = 0; + const init = moment(); + + //================================================== + // requset - 날짜 탐색 + //================================================== + while (1) { + // 스케줄 데이터 요청 + const { cookies, data: result } = await this.getResultData(mgCD, targetYMD); // 20035290 듄, 20035256 웡카 + + // 예외 - 요청 성공 확인 + if (result === null) throw new Error(`\t> Request failed`); + // 예외(실험적) - 응답 데이터 구조 확인 + if (!result.hasOwnProperty('d')) throw new Error(`\t> Response lacks the expected "d" property`); + const gScheduleList = JSON.parse(result.d); + // 예외 - CGV 응답 코드 확인 + if (gScheduleList.ResultCode !== "00000" || gScheduleList.ResultMessage !== "성공") throw new Error(`\t> Invalid schedule response`); + // 예외 - 영화 스케줄 존재 여부 확인 + if (gScheduleList.ResultSchedule.ListPlayYmd.length === 0 && gScheduleList.ResultSchedule.ScheduleList.length === 0) { + throw new Error(`\t> Schedule not found`); + } + + // 날짜 추출 - YMD 문자열 moment로 변환 + const listPlayYmd = gScheduleList.ResultSchedule.ListPlayYmd.split('|').map(YMD => util.YMDConvert(YMD)); + // 예매 가능한 일정만 필터 + const filteredDates = util.dayFilter(listPlayYmd); + const targetDates = filteredDates.filter(date => date.isSameOrAfter(dateAft)); + + //================================================== + // requset - 날짜 찾았을 때 + //================================================== + if (targetYMD === null && targetDates.length !== 0) { + targetYMD = util.YMDConvert(targetDates[0]); + await __INFO__(`조건 부합 날짜 식별됨 - 시도: ${++attemptCnt} / 시간: ${moment().format('MM-DD HH:mm:ss')} / 경과: ${init.fromNow()}`); + continue + + //================================================== + // requset - 시간표 찾았을 때 + //================================================== + } else if (targetYMD !== null && targetSchedule === null) { + /* ++++++++++++ 남은 좌석 수 필터링 추가 'SeatRemainCnt' */ + // 상영시간표 추출 + const scheduleList = gScheduleList.ResultSchedule.ScheduleList; + const seatFilteredSchedule = scheduleList.filter(schedule => schedule.SeatRemainCnt >= 550); + const tmFilteredSchedule = seatFilteredSchedule.filter(schedule => { + const scheduleTm = moment(schedule.PlayStartTm, 'HHmm'); + const t_start = moment(tmRange.start, 'HHmm'); + const t_end = moment(tmRange.end, 'HHmm'); + return scheduleTm.isBetween(t_start, t_end, null, '[]'); + }) + if (tmFilteredSchedule.length !== 0) { + targetSchedule = tmFilteredSchedule[0]; + await __INFO__(`조건 부합 스케줄 식별됨 - 시도: ${++attemptCnt} / 시간: ${moment().format('MM-DD HH:mm:ss')} / 경과: ${init.fromNow()}`); + break + } + } + console.log(`탐색 중 - 시도: ${++attemptCnt} / 시간: ${moment().format('MM-DD HH:mm:ss')} / 경과: ${init.fromNow()}`); + console.log(`\t 조건 - 날짜: ${targetYMD ? targetYMD : '빠른순'}`); + await new Promise((resolve) => setTimeout(resolve, 60000)); + } + await __INFO__(`예매 시작 / 시간: ${moment().format('MM.DD HH:mm:ss')}`); + await __INFO__(`\t 조건 - 날짜: ${targetSchedule.PlayYmd} / 시간: ${targetSchedule.PlayStartTm}`); + + //================================================== + // requset - 예매 시작 + //================================================== + // 추출한 영화 정보 -> 페이로드로 재구성 + const tcktDate = targetSchedule; + const TicketTypeOrderInfo = [ + { "TicketTypeName": "일반", "TicketTypeCode": "01", "TicketTypeCss": "General", "TicketTypeCount": generalSeatCnt }, + { "TicketTypeName": "청소년", "TicketTypeCode": "02", "TicketTypeCss": "Student", "TicketTypeCount": 0 } + ] + const payload = { + "hfTheaterCd": tcktDate.TheaterCd, + "hfTheaterNm": tcktDate.TheaterNm, + "hfCGVCode": tcktDate.MovieCd, + "hfPlayYMD": tcktDate.PlayYmd, + "hfPlayNum": tcktDate.PlayNum, + "hfScreenCd": tcktDate.ScreenCd, + "hfScreenNM": tcktDate.ScreenNm, + "hfPlayTimeCd": tcktDate.PlayTimeCd, + "hfScreenRatingCd": tcktDate.ScreenRatingCd, + "hfRating": tcktDate.MovieRatingCd, + "hfScreenRatingNm": tcktDate.ScreenRatingNm, + "hfStartHHMM": tcktDate.PlayStartTm, + "hfEndHHMM": tcktDate.PlayEndTm, + "hfKidsScreenType": tcktDate.KidsScreenType, + "hfmovieIdx": tcktDate.MovieIdx, + "hfmovieName": tcktDate.MovieNmKor, + "hfPlayEndTM": tcktDate.PlayEndTm, + "hfPlatformNM": tcktDate.PlatformNm, + "hfMovieRatingNM": tcktDate.MovieRatingNm, + "hfPlayTimeNM": tcktDate.PlayTimeNm, + "hfMoviePkgYn": tcktDate.MoviePkgYn, + "hfMovieNoShowYn": tcktDate.MovieNoshowYn, + "hfPlatformCd": tcktDate.PlatformCd, + "hfGeneral_count": generalSeatCnt, + "hfSpecialSeat_count": 0, + "hfStudent_count": 0, + "hfKid_count": 0, + "hfGiveSpecial_count": 0, + "hfSenior_count": 0, + "hfArmy_count": 0, + "hfMovieGroupCd": tcktDate.MovieGroupCd, + "hfMovieAttrCd": tcktDate.MovieAttrCd, + "hfMovieAttrNm": tcktDate.MovieAttrNm, + "hfSeatRemainRate": tcktDate.SeatRate, + "hfTicketTypeOrderInfo": JSON.stringify(TicketTypeOrderInfo) + } + + // 페이로드 -> 요청용 쿼리 (암호화) + const encryptedPayload = crypto.encrypt_hfObject(payload); + const seatQuery = new URLSearchParams(encryptedPayload).toString(); + const url_seat = this.seatAspx + seatQuery; + + //================================================== + // requset - 로그인 쿠키 + //================================================== + + // 로그인 세션 쿠키 추출 + const loginParam = { + "hfUserId": crypto.cgv_encrypt_hf(process.env.CGV_ID), + "hfPasswordInter": encodeURIComponent(crypto.cgv_sha256_encrypt(process.env.CGV_PW)), + "hfPasswordLocal": encodeURIComponent(crypto.cgv_sha256_encrypt(crypto.cgv_md5_encrypt(process.env.CGV_PW))), + "hfReUrl": crypto.cgv_encrypt_hf('https%3a%2f%2fm.cgv.co.kr%2f'), + "hfAgree": crypto.cgv_encrypt_hf('0'), + "nonmemberStateCd": crypto.cgv_encrypt_hf('0') + }; + const loginHeaders = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36', + 'Accept-Language': 'ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7,de;q=0.6', + 'Content-Type': 'application/x-www-form-urlencoded', + Origin: 'https://m.cgv.co.kr', + Cookie: COOKIE + } + const { cookies: loginCookies } = await this._post(this.loginAspx, loginParam, loginHeaders); + /* ++++++++++++ 예외 추가 or 예외 자체를 요청과정에 합병 */ + + // cookie list -> cookie string + const joinedLoginCookie = util.joinCookie(loginCookies); + const parsedLoginCookie = util.parseCookie(joinedLoginCookie) + const PuppeteerCookie = Object.entries(parsedLoginCookie).map(([key, val]) => ({ 'name': key, 'value': val })); + + //================================================== + // puppeteer - 기본 설정 + //================================================== + // const browser = await puppeteer.launch({ headless: false }); + const browser = await puppeteer.launch({ headless: 'new' }); + const page = await browser.newPage(); + await page.setViewport({ width: 1920, height: 1080, }); + await page.setRequestInterception(true); + page.on('request', (req) => { + if (['font'].includes(req.resourceType())) req.abort(); + else req.continue(); + }); + page.on('popup', async popup => { + await __INFO__(await popup.title()); + // await page.screenshot({path: 'popup.png'}); + await popup.close(); + }); + page.on('dialog', async dialog => { + await __INFO__(dialog.message()); + // await page.screenshot({path: 'dialog.png'}); + await dialog.dismiss(); // 혹은 accept()로 확인 가능 + }); + + //================================================== + // puppeteer - seatAspx + //================================================== + await page.goto('https://m.cgv.co.kr/'); + await page.setCookie(...PuppeteerCookie); + + let failCnt = 0; + // 반복 시작: 좌석 추출 -> 좌석 선택 -> 결제 시도 + while (1) { + try { + await page.goto(url_seat); + await __INFO__(` ===== 좌석창 들어옴`); + + + // 연령 등급 팝업 제거 - jQeury함수 + await page.evaluate(() => { jQuery.fn.closePopup('popAge') }); + + //================================================== + // puppeteer - 좌석 추출 + //================================================== + // 예매 가능한 일반석 시트 정보 추출 + const availableSeats = await page.$$eval('#seat_table > tbody > tr > td[reservation="Yes"].pointer[rating_nm="일반석"]', elements => { + return elements.map(element => { + const seatname = element.getAttribute('seatname').trim(); + const locynm = element.getAttribute('locynm').trim(); + const locxnm = Number(element.getAttribute('locxnm').trim()); + return { seatname, locynm, locxnm }; + }); + }); + + //================================================== + // puppeteer - 최적 좌석 탐색 알고리즘 + //================================================== + // 중심 및 범위 설정 + const setCenterSeat = { x: 23, y: 'H' }; + const setSeatRange = { startX: 16, endX: 29, startY: 'H', endY: 'K' } + + // 탐색 범위 제한 + const filteredX = availableSeats.filter(seat => seat.locxnm >= setSeatRange.startX && seat.locxnm <= setSeatRange.endX); + const filteredY = filteredX.filter(seat => { + const seatChar = seat.seatname.charAt(0); + return seatChar >= setSeatRange.startY && seatChar <= setSeatRange.endY; + }); + + // 가중치 적용 + const seatWeights = filteredY.map(seat => ({ ...seat, weight: this.calculateSeatWeight({ x: seat.locxnm, y: seat.locynm }, setCenterSeat) })); + // 설정한 인원수 불충족 좌석 필터링 (= 좌석 연속성 확인) + const continuousSeats = seatWeights.filter(seat => this.countAdjacentSeats(seatWeights, seat.seatname) >= generalSeatCnt); + // 낮은 가중치순 정렬 + const sortedSeats = continuousSeats.sort((a, b) => a.weight - b.weight); + + if (sortedSeats.length === 0) { + await __INFO__(`자리가 없음!!!!`) + return -1 + } + + // 좌석 선택 + const finalSeat = sortedSeats[0].seatname + await page.evaluate((seatname) => { jQuery(`#seat_table > tbody > tr > td[seatname='${seatname}']`).click(); }, finalSeat); + // 스마트결제로 이동 + // await new Promise((resolve) => setTimeout(resolve, 1000000)); + await page.evaluate(() => { submitSeat('O') }); + + //================================================== + // puppeteer - 결제 카드 선택 + //================================================== + await page.waitForSelector('#ContainerView > article > div > ul > li:nth-child(1)', { visible: true }); + await __INFO__(` ===== 카드창 들어옴`); + // 첫번째 결제 카드 클릭 + await page.evaluate(() => { jQuery('#ContainerView > article > div > ul > li:nth-child(1)').click(); }); + // 결제 버튼 클릭 + await page.evaluate(() => { jQuery('#next').click(); }); + + //================================================== + // puppeteer - 결제 비번 입력 + //================================================== + await page.waitForSelector('#ownKeypad', { visible: true }); + await page.waitForFunction(() => window.nshc.finishedCallback !== null); + await __INFO__(` ===== 비번창 들어옴`); + // 간편결제 비밀번호 입력 + const nums = process.env.EZPAY_PW.split(''); + for (let num of nums) { + await page.evaluate((num) => { + jQuery(`#ownKeypad > div.kpdWrap.kpd-img.kpdNum.typeB > div.nfilter_keypad_div.kpdGrp.number > [aria-label="${num}"]`).click(); + }, num); + } + await page.evaluate(() => { jQuery(`#ownKeypad > div.kpdWrap.kpd-img.kpdNum.typeB > div.nfilter_keypad_div.kpdGrp.number > #nfilter_enter`).click(); }); + break + } catch (e) { + await __ERROR__(e); + failCnt++; + if (failCnt >= 10) return -1 + else continue + } + } + await new Promise(resolve => setTimeout(resolve, 5000)); + await page.screenshot({ path: 'result.png' }); + return 1 + } +} + + + +await (async () => { + console.time('timer'); + const CGV = new cgvGetter(); + let mode = null; + + if (process.argv[2] && process.argv[2] === '0') mode = 0; + else if (process.argv[2] && process.argv[2] === '1') mode = 1; + else if (process.argv[2] && process.argv[2] === '2') mode = 2; + + switch (mode) { + + /*====== infinite loop ======*/ + case 0: + let cnt = 0; + const startTime = moment(); + let movieData; + let prevMovieData = null; // 이전 movieData를 저장할 변수 추가 + while (1) { + try { + cnt++; + const { cookies, data: result } = await CGV.getResultData("20035479", '20240218'); // 20035290 듄, 20035256 웡카 + // 예외 - 요청 성공 확인 + if (result === null) throw new Error(`\t> Request failed`); + // 예외(실험적) - 응답 데이터 구조 확인 + if (!result.hasOwnProperty('d') || result.d.length === 0) throw new Error(`\t> Response lacks the expected "d" property`); + const gScheduleList = JSON.parse(result.d); + // 예외 - CGV 응답 코드 확인 + if (gScheduleList.ResultCode !== "00000" || gScheduleList.ResultMessage !== "성공") throw new Error(`\t> Invalid schedule response`); + // 예외 - 영화 스케줄 존재 여부 확인 + if (gScheduleList.ResultSchedule.ListPlayYmd.length === 0 && gScheduleList.ResultSchedule.ScheduleList.length === 0) { + throw new Error(`\t> Schedule not found`) + } + + // 데이터 가공 + const listPlayYmd = gScheduleList.ResultSchedule.ListPlayYmd; + movieData = listPlayYmd; // 현재 movieData 업데이트 + + // 이전 movieData와 현재 movieData 비교 + if (!_.isEqual(movieData, prevMovieData)) { + await __INFO__(`스케줄 변경됨\n\t> 시도: ${cnt} / 현재시간: ${moment().format('MM-DD HH:mm:ss')} / 경과: ${startTime.fromNow()}\n\t${movieData.replace(/\|/g, "\n\t")}`); + prevMovieData = movieData; // 현재 movieData를 이전 movieData로 업데이트 + } else { + await __INFO__(`스케줄 변경 없음\n\t> 시도: ${cnt} / 현재시간: ${moment().format('YYYY-MM-DD HH:mm:ss')} / 경과: ${startTime.fromNow()}`); + } + } catch (err) { + await __ERROR__(`사유: \n${err.message}\n\t> 시도: ${cnt} / 현재시간: ${moment().format('YYYY-MM-DD HH:mm:ss')} / 경과: ${startTime.fromNow()}`); + } + await new Promise(resolve => setTimeout(resolve, 60000)); + } + break; + + /*====== Dev Mode ======*/ + case 1: + const result = await CGV.main_dev('20035290', null, moment('2024-03-09', 'YYYY-MM-DD')); // '20035290', null, moment('2024-03-09', 'YYYY-MM-DD' //// '20035479', null, moment('2024-02-16', 'YYYY-MM-DD' + if (result === -1) await __ERROR__('예매에 실패했습니다!'); + else if (result === 1) await __INFO__(`예매에 성공했습니다!`); + break + + /*====== Test Mode ======*/ + case 2: + // let url = 'https://m.cgv.co.kr/Webapp/Member/Login.aspx'; + // let param = { + // "hfUserId": "yymyeAbXPYSBovwH6LsywQ%3D%3D", + // "hfPasswordInter": "a273558ce0cebd7bb31d76055eb9cf4886d72ccb997fadd908f1ff0e4e4dd089", + // "hfPasswordLocal": "cfe9a2ee0e2989179a480c1dcbe8f07a556cc831f869cb069e623aa3f9d9e231", + // "hfReUrl": "Nkzul22VO0IswIN0zK0vHDanF3%2By9OXs4cqrIhv8vP0%3D", + // "hfAgree": "9LcHk229qY1EvJegzd%2FTQg%3D%3D", + // "nonmemberStateCd": "QB1eobbBB2OMOrekypNmww%3D%3D" + // }; + // let headers = { + // 'Accept-Language': 'ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7,de;q=0.6', + // 'Content-Type': 'application/x-www-form-urlencoded', + // Origin: 'https://m.cgv.co.kr', + // Cookie: COOKIE + // } + // let { cookies, data } = await CGV._post(url, param, headers); + // const joinedCookie = util.joinCookie(cookies); + // await __INFO__(util.parseCookie(joinedCookie)) + + // headers.Cookie = joinedCookie; + // ({ cookies, data } = await CGV._get('https://m.cgv.co.kr/WebApp/MyCgvV5/myMain.aspx', headers)); + // await __INFO__(util.parseCookie(util.joinCookie(cookies))) + + + + // const foo = { + // "hfUserId": "yymyeAbXPYSBovwH6LsywQ%3D%3D", + // "hfPasswordInter": "자체검열", + // "hfPasswordLocal": "자체검열", + // "hfReUrl": "Nkzul22VO0IswIN0zK0vHDanF3%2By9OXs4cqrIhv8vP0%3D", + // "hfAgree": "9LcHk229qY1EvJegzd%2FTQg%3D%3D", + // "nonmemberStateCd": "QB1eobbBB2OMOrekypNmww%3D%3D" + // } + // await __INFO__(crypto.decrypt_hfObject(foo)) + + const { cookies, data } = await CGV.getResultData('20035290', '20240228'); + await __INFO__(data) + } + console.timeEnd('timer'); + process.exit() +})(); \ No newline at end of file