diff --git a/.vscode/settings.json b/.vscode/settings.json index 78664b2..56ef292 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "editor.tabSize": 2 + "editor.tabSize": 2, + "cSpell.words": ["captchaimg", "catcha", "Interactor", "networkidle"] } diff --git a/README.md b/README.md index f59940a..154bb70 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,8 @@ import { NaverApp } from "trackpurchase"; const browser = await puppeteer.launch(); const page = await browser.newPage(); -const module = NaverApp.ModuleFactory.create(page); -const crawlService = new NaverApp.Service(module); +const module = NaverApp.NaverModuleFactory.create(page); +const crawlService = new NaverApp.NaverService(module); await crawlService.normalLogin(id, password, 100); const history = await crawlService.getHistory(); diff --git a/example/naver/printPaymentHistory.ts b/example/naver/printPaymentHistory.ts index 7e56ef4..f81841c 100644 --- a/example/naver/printPaymentHistory.ts +++ b/example/naver/printPaymentHistory.ts @@ -15,8 +15,8 @@ const printNaverPayHistory = async (id: string, password: string) => { await page.setViewport({ height: 800, width: 1200 }); await page.setUserAgent(MOBILE_UA); - const module = NaverApp.ModuleFactory.create(page); - const crawlService = new NaverApp.Service(module); + const module = NaverApp.NaverModuleFactory.create(page); + const crawlService = new NaverApp.NaverService(module); await crawlService.normalLogin(id, password, 100); diff --git a/example/naver/reactivelyPrintPaymentHistory.ts b/example/naver/reactivelyPrintPaymentHistory.ts deleted file mode 100644 index 6031ed5..0000000 --- a/example/naver/reactivelyPrintPaymentHistory.ts +++ /dev/null @@ -1,73 +0,0 @@ -import puppeteer from "puppeteer"; -import { NaverApp } from "trackpurchase"; - -import readline from "readline"; -import { concat, defer, filter, from, tap } from "rxjs"; -import { CaptchaStatus } from "trackpurchase/app/naver"; - -const printNaverPayHistory = async (id: string, password: string) => { - const MOBILE_UA = - "Mozilla/5.0 (iPhone; CPU iPhone OS 15_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Mobile/15E148 Safari/604.1"; - - const browser = await puppeteer.launch({ - headless: false, - args: ["--start-maximized"], - }); - - const page = await browser.newPage(); - await page.setViewport({ height: 800, width: 1200 }); - await page.setUserAgent(MOBILE_UA); - - const module = NaverApp.ModuleFactory.create(page); - const crawlService = new NaverApp.Service(module); - - const loginEvent$ = crawlService.interactiveLogin(id, password, 100); - const history$ = defer(() => from(crawlService.getHistory())); - const closePage$ = defer(() => from(page.close())); - const closeBrowser$ = defer(() => from(browser.close())); - - const final$ = concat(loginEvent$, history$, closePage$, closeBrowser$).pipe( - tap((event) => { - if (event === "otp-required") { - console.log("스마트폰 앱에서 OTP 인증을 완료해주세요."); - } - }), - tap((event) => { - if (event === "manual-otp-required") { - rl.question("otp code: ", async (code) => { - module.pageInteractor.fillManualOTPInput(code); - }); - } - }), - tap((event) => { - function instanceOfCaptchaStatus(object: any): object is CaptchaStatus { - if (object) { - return "imageData" in object && "question" in object; - } - return false; - } - - if (instanceOfCaptchaStatus(event)) { - console.log(`encodedImage: ${event.imageData}`); - console.log(`question: ${event.question}`); - rl.question("captcha code: ", (code) => { - module.pageInteractor.fillCaptchaInput(code, password); - }); - } - }), - filter((event) => event instanceof Array) - ); - final$.subscribe((event) => { - console.log(event); - }); -}; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); -rl.question("Naver ID: ", (id) => { - rl.question("Naver Password: ", (password) => { - printNaverPayHistory(id, password); - }); -}); diff --git a/package.json b/package.json index 9a09722..a50fead 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "trackpurchase", - "version": "1.2.0", + "version": "2.0.0", "main": "dist/index.js", "license": "MIT", "repository": { @@ -35,8 +35,7 @@ }, "dependencies": { "axios": "^0.25.0", - "puppeteer": "^12.0.1", - "rxjs": "^7.5.2" + "puppeteer": "^12.0.1" }, "devDependencies": { "@babel/core": "^7.16.5", diff --git a/src/app/naver/index.ts b/src/app/naver/index.ts index 98bde9b..ab4dde9 100644 --- a/src/app/naver/index.ts +++ b/src/app/naver/index.ts @@ -1,17 +1,21 @@ import { NaverModule } from "./module"; -import ModuleFactory from "./moduleFactory"; -import URLChanger from "./urlChanger"; -import PageInteractor, { LoginEvent, CaptchaStatus } from "./pageInteractor"; -import Service from "./service"; +import { NaverModuleFactory } from "./moduleFactory"; +import { NaverURLChanger } from "./urlChanger"; +import { + LoginEvent, + CaptchaStatus, + NaverPageInteractor, +} from "./pageInteractor"; +import { NaverService } from "./service"; import { NaverScraper } from "./scraper"; import { NaverParser } from "./parser"; export { NaverModule, - ModuleFactory, - URLChanger, - PageInteractor, - Service, + NaverModuleFactory, + NaverURLChanger, + NaverPageInteractor, + NaverService, LoginEvent, CaptchaStatus, NaverScraper, diff --git a/src/app/naver/module.ts b/src/app/naver/module.ts index cf8594c..d917a10 100644 --- a/src/app/naver/module.ts +++ b/src/app/naver/module.ts @@ -1,8 +1,13 @@ -import { URLChanger, PageInteractor, NaverScraper, NaverParser } from "."; +import { + NaverURLChanger, + NaverPageInteractor, + NaverScraper, + NaverParser, +} from "."; -export default interface Module { - readonly urlChanger: URLChanger; - readonly pageInteractor: PageInteractor; +export interface NaverModule { + readonly urlChanger: NaverURLChanger; + readonly pageInteractor: NaverPageInteractor; readonly scraper: NaverScraper; readonly parser: NaverParser; } diff --git a/src/app/naver/moduleFactory.ts b/src/app/naver/moduleFactory.ts index 1b32a3a..033409f 100644 --- a/src/app/naver/moduleFactory.ts +++ b/src/app/naver/moduleFactory.ts @@ -1,16 +1,16 @@ import puppeteer from "puppeteer"; import { - Module, - URLChanger, - PageInteractor, + NaverModule, + NaverURLChanger, + NaverPageInteractor, NaverScraper, NaverParser, } from "."; -export default class ModuleFactory { - static create(page: puppeteer.Page): Module { - const urlChanger = new URLChanger(page); - const pageInteractor = new PageInteractor(page); +export class NaverModuleFactory { + static create(page: puppeteer.Page): NaverModule { + const urlChanger = new NaverURLChanger(page); + const pageInteractor = new NaverPageInteractor(page); const scraper = new NaverScraper(); const parser = new NaverParser(); diff --git a/src/app/naver/pageInteractor.ts b/src/app/naver/pageInteractor.ts index d60b611..655f922 100644 --- a/src/app/naver/pageInteractor.ts +++ b/src/app/naver/pageInteractor.ts @@ -1,5 +1,4 @@ import puppeteer from "puppeteer"; -import { ElementParser } from "."; export type LoginEvent = | "success" @@ -12,7 +11,7 @@ export interface CaptchaStatus { readonly question: string; } -export default class PageInteractor { +export class NaverPageInteractor { constructor(private readonly page: puppeteer.Page) { this.page = page; } diff --git a/src/app/naver/service.ts b/src/app/naver/service.ts index 7aec84b..11bc94a 100644 --- a/src/app/naver/service.ts +++ b/src/app/naver/service.ts @@ -1,19 +1,10 @@ import { PaymentHistory } from "app/common"; import { CommonResponse } from "app/common/types/response"; -import { - concat, - defer, - distinctUntilChanged, - from, - interval, - mergeMap, - takeWhile, -} from "rxjs"; -import { Module } from "."; +import { NaverModule } from "."; -export default class Service { +export class NaverService { cookies?: string; - constructor(private readonly module: Module) { + constructor(private readonly module: NaverModule) { this.module = module; } @@ -23,28 +14,10 @@ export default class Service { this.cookies = await this.module.pageInteractor.getCookies(); } - interactiveLogin(id: string, password: string, delay?: number) { - const login$ = defer(() => from(this.normalLogin(id, password, delay))); - const loginStatus$ = interval(500) - .pipe(mergeMap(() => this.module.pageInteractor.getLoginStatus())) - .pipe( - distinctUntilChanged(), - takeWhile((loginStatus) => loginStatus !== "success") - ); - const captchaStatus$ = interval(500) - .pipe(mergeMap(() => this.module.pageInteractor.getCaptchaStatus())) - .pipe( - distinctUntilChanged((a, b) => a?.question === b?.question), - takeWhile((captchaStatus) => captchaStatus !== null) - ); - - const result$ = concat(login$, captchaStatus$, loginStatus$); - return result$; - } - private async isResponseValid(response: CommonResponse) { return response.status === 200; } + private async getHistoryResult(response: CommonResponse) { if (!this.isResponseValid(response)) { throw new Error(`Invalid response: ${response.status} ${response.data}`); diff --git a/src/app/naver/urlChanger.test.ts b/src/app/naver/urlChanger.test.ts index ec064ea..3fe8290 100644 --- a/src/app/naver/urlChanger.test.ts +++ b/src/app/naver/urlChanger.test.ts @@ -1,10 +1,10 @@ -import URLChanger from "./urlChanger"; +import { NaverURLChanger } from "./urlChanger"; describe("URLChanger", () => { describe("moveToLoginURL", () => { it("Should move page to login", async () => { // given - const urlChanger = new URLChanger(page); + const urlChanger = new NaverURLChanger(page); const pageSpy = jest.spyOn(page, "goto"); // when diff --git a/src/app/naver/urlChanger.ts b/src/app/naver/urlChanger.ts index 35b2cbe..a540f68 100644 --- a/src/app/naver/urlChanger.ts +++ b/src/app/naver/urlChanger.ts @@ -1,6 +1,6 @@ import puppeteer from "puppeteer"; -export default class URLChanger { +export class NaverURLChanger { constructor(private readonly page: puppeteer.Page) { this.page = page; } diff --git a/yarn.lock b/yarn.lock index 8c952eb..d4a9a32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4215,13 +4215,6 @@ rxjs@^7.1.0: dependencies: tslib "~2.1.0" -rxjs@^7.5.2: - version "7.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" - integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== - dependencies: - tslib "^2.1.0" - safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -4570,11 +4563,6 @@ tsc@^2.0.3: resolved "https://registry.yarnpkg.com/tsc/-/tsc-2.0.3.tgz#037fe579e3bd67a5cbdaa604b43c6c1991b04bef" integrity sha512-SN+9zBUtrpUcOpaUO7GjkEHgWtf22c7FKbKCA4e858eEM7Qz86rRDpgOU2lBIDf0fLCsEg65ms899UMUIB2+Ow== -tslib@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"