-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [feat] puppeteer 환경 설정 * [feat] 예약 동작 구현 * [feat] 확인 동작 구현 * [fix] 테스트용 데이터 삭제 * [feat] 요청과 응답 구현
- Loading branch information
1 parent
67e3e1b
commit d30de8f
Showing
5 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,38 @@ | ||
import express from "express"; | ||
import http from "http"; | ||
import bodyParser from "body-parser"; | ||
import reserveMasterkey from "./src/masterkey/run.js"; | ||
|
||
const app = express(); | ||
app.use(express.json({ extended: true })); | ||
|
||
app.use(bodyParser.urlencoded({ extended: false })); | ||
|
||
const server = http.createServer(app); | ||
const PORT = 3000; | ||
|
||
app.post("/reserve/masterkey", async (req, res) => { | ||
const info = req.body; | ||
const response = await reserveMasterkey(info); | ||
|
||
res.json({ | ||
isSucceed: response, | ||
}); | ||
}); | ||
|
||
server.listen(PORT, () => { | ||
console.log(`Server running on http://localhost:${PORT}`); | ||
}); | ||
|
||
/** | ||
info 객체 예시 | ||
{ | ||
"targetUrl": "https://www.master-key.co.kr/booking/bk_detail?bid=11", | ||
"themeTitle": "연애조작단", | ||
"targetDate": "2023-11-25", | ||
"targetTime": "99:05", | ||
"bookerName": "이남곤", | ||
"bookerPhoneNumber": "01012345678" | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { createBrowser, createPage } from "../tools/browser.js"; | ||
|
||
const check = async ({ | ||
targetUrl, | ||
themeTitle, | ||
targetDate, | ||
targetTime, | ||
bookerName, | ||
bookerPhoneNumber, | ||
}) => { | ||
const browser = await createBrowser(); | ||
const page = await createPage(browser); | ||
|
||
try { | ||
await Promise.all([page.waitForNavigation(), page.goto(targetUrl)]); | ||
|
||
await Promise.all([ | ||
page.waitForSelector(".modal-content", { visible: true }), | ||
page.waitForSelector("input[name='chk_user_name']"), | ||
page.waitForSelector("input[name='chk_user_phone']"), | ||
page.click("a#booking_check_btn"), | ||
]); | ||
|
||
await page.type("input[name='chk_user_name']", bookerName); | ||
await page.type("input[name='chk_user_phone']", bookerPhoneNumber); | ||
await page.click("button#booking_check_ajax"); | ||
|
||
await page.waitForSelector("#booking_check_result > table > tbody"); | ||
const [tbody] = await page.$x("//*[@id='booking_check_result']/table/tbody"); | ||
|
||
const trs = await tbody.$x("//tr"); | ||
|
||
for (let index = 0; index < trs.length; index += 2) { | ||
const fromSite = {}; | ||
fromSite["date"] = await trs[index].$eval("td:nth-child(2)", (el) => el.textContent.trim()); | ||
fromSite["themeTitle"] = await trs[index + 1].$eval("td:nth-child(1)", (el) => | ||
el.textContent.trim() | ||
); | ||
const status = await trs[index + 1].$eval("td:nth-child(2)", (el) => el.textContent.trim()); | ||
|
||
const fromUser = { themeTitle, targetDate, targetTime }; | ||
|
||
if (isReserved(fromUser, fromSite, status)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} catch (error) { | ||
console.log(error); | ||
return false; | ||
} finally { | ||
await page.close(); | ||
await browser.close(); | ||
} | ||
}; | ||
|
||
const isReserved = (fromUser, fromSite, status) => { | ||
if ( | ||
fromUser.themeTitle == fromSite.themeTitle && | ||
`${fromUser.targetDate} ${fromUser.targetTime}` == fromSite.date && | ||
status == "예약됨" | ||
) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
export default check; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { createBrowser, createPage } from "../tools/browser.js"; | ||
|
||
const reserve = async ({ | ||
targetUrl, | ||
themeTitle, | ||
targetDate, | ||
targetTime, | ||
bookerName, | ||
bookerPhoneNumber, | ||
}) => { | ||
const browser = await createBrowser(); | ||
const page = await createPage(browser); | ||
|
||
try { | ||
await Promise.all([page.waitForNavigation(), page.goto(targetUrl)]); | ||
|
||
// 날짜 클릭, 해당 날짜 없는 경우 에러 발생 | ||
await Promise.all([ | ||
page.waitForSelector("#booking_list", { visible: true }), | ||
page.click(`#tab1 > div.box1 > div > div.date-click > div > p[data-dd='${targetDate}']`), | ||
]); | ||
|
||
// 테마 선택, 해당 테마 없는 경우 에러 발생 | ||
const [box2Inner] = await page.$x( | ||
`//div[@class='box2-inner' and descendant::div[@class='title' and contains(., '${themeTitle}')]]` | ||
); | ||
|
||
// 시간과 예약 상태 선택 | ||
const [aTag] = await box2Inner.$x(`//div[@class='right']/p/a[contains(., '${targetTime}')]`); | ||
|
||
// 해당 시간 없는 경우, undefined 반환 | ||
if (!aTag) { | ||
return false; | ||
} | ||
|
||
const isAvailable = await aTag.$eval("span", (el) => el.textContent.trim()); | ||
|
||
// 이미 예약완료된 테마인 경우 | ||
if (isAvailable == "예약완료") { | ||
return false; | ||
} | ||
|
||
await Promise.all([ | ||
page.waitForSelector(".modal-content", { visible: true }), | ||
page.waitForSelector("button#booking_go"), | ||
page.waitForSelector("input[name='user_name']"), | ||
page.waitForSelector("input[name='user_phone']"), | ||
aTag.click(), | ||
]); | ||
|
||
await page.type("input[name='user_name']", bookerName); | ||
await page.type("input[name='user_phone']", bookerPhoneNumber); | ||
|
||
// 예약 버튼 클릭 | ||
await page.click("button#booking_go"); | ||
|
||
return true; | ||
} catch (error) { | ||
console.log(error); | ||
return false; | ||
} finally { | ||
await page.close(); | ||
await browser.close(); | ||
} | ||
}; | ||
|
||
export default reserve; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import check from "./check.js"; | ||
import reserve from "./reserve.js"; | ||
|
||
const run = async (info) => { | ||
const reservationResult = await reserve(info); | ||
|
||
if (!reservationResult) { | ||
return false; | ||
} | ||
|
||
return await check(info); | ||
}; | ||
|
||
export default run; |