Skip to content

Commit ad127ec

Browse files
authored
Merge pull request #8 from code-yeongyu/naver-captcha
사용자는 네이버 서비스의 로그인 과정에 캡챠가 필요할 경우 캡챠 로그인을 할 수 있습니다.
2 parents 49d5d2a + 493e1a7 commit ad127ec

File tree

7 files changed

+327
-10
lines changed

7 files changed

+327
-10
lines changed

example/naver/reactivelyPrintPaymentHistory.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import puppeteer from "puppeteer";
2-
import { NaverApp } from "trackpurchase";
2+
import { NaverApp } from ".";
33

44
import readline from "readline";
55
import { concat, defer, filter, from, tap } from "rxjs";
6+
import { CaptchaStatus } from "app/naver";
67

78
const printNaverPayHistory = async (id: string, password: string) => {
89
const MOBILE_UA =
@@ -38,6 +39,22 @@ const printNaverPayHistory = async (id: string, password: string) => {
3839
});
3940
}
4041
}),
42+
tap((event) => {
43+
function instanceOfCaptchaStatus(object: any): object is CaptchaStatus {
44+
if (object) {
45+
return "imageData" in object && "question" in object;
46+
}
47+
return false;
48+
}
49+
50+
if (instanceOfCaptchaStatus(event)) {
51+
console.log(`encodedImage: ${event.imageData}`);
52+
console.log(`question: ${event.question}`);
53+
rl.question("captcha code: ", (code) => {
54+
module.pageInteractor.fillCaptchaInput(code, password);
55+
});
56+
}
57+
}),
4158
filter((event) => event instanceof Array)
4259
);
4360
final$.subscribe((event) => {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "trackpurchase",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"main": "dist/index.js",
55
"license": "MIT",
66
"repository": {

src/app/naver/elementParser.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,8 @@ export default class ElementParser {
117117
async parseManualOTPInputElement() {
118118
return await this.page.$("#otp");
119119
}
120+
121+
async parseCaptchaInputElement() {
122+
return await this.page.$("#captcha");
123+
}
120124
}

src/app/naver/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Module from "./module";
22
import ModuleFactory from "./moduleFactory";
33
import URLChanger from "./urlChanger";
4-
import PageInteractor, { LoginEvent } from "./pageInteractor";
4+
import PageInteractor, { LoginEvent, CaptchaStatus } from "./pageInteractor";
55
import ElementParser from "./elementParser";
66
import Service from "./service";
77

@@ -13,4 +13,5 @@ export {
1313
ElementParser,
1414
Service,
1515
LoginEvent,
16+
CaptchaStatus,
1617
};

src/app/naver/pageInteractor.test.ts

Lines changed: 252 additions & 0 deletions
Large diffs are not rendered by default.

src/app/naver/pageInteractor.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export type LoginEvent =
77
| "manual-otp-required"
88
| "unexpected";
99

10+
export interface CaptchaStatus {
11+
readonly imageData: string;
12+
readonly question: string;
13+
}
14+
1015
export default class PageInteractor {
1116
private _fullyLoaded = false;
1217

@@ -27,9 +32,9 @@ export default class PageInteractor {
2732

2833
private async typeLoginInfo(id: string, password: string, delay: number) {
2934
await this.page.focus("#id");
30-
await this.page.keyboard.type(id, { delay: delay || 200 });
35+
await this.page.keyboard.type(id, { delay: delay });
3136
await this.page.focus("#pw");
32-
await this.page.keyboard.type(password, { delay: delay || 200 });
37+
await this.page.keyboard.type(password, { delay: delay });
3338
await this.clickLoginButton();
3439
}
3540

@@ -83,6 +88,38 @@ export default class PageInteractor {
8388
await manualOTPElement.press("Enter");
8489
}
8590

91+
async getCaptchaStatus(): Promise<CaptchaStatus | null> {
92+
const data = await this.page.evaluate(() => {
93+
const captchaImage = document.querySelector(
94+
"#captchaimg"
95+
) as HTMLElement | null;
96+
const captchaText = document.querySelector(
97+
"#captcha_info"
98+
) as HTMLElement | null;
99+
100+
if (!captchaImage || !captchaText) {
101+
return;
102+
}
103+
104+
const imageData = captchaImage.getAttribute("src") as string;
105+
const question = captchaText.innerText;
106+
107+
return { imageData, question };
108+
});
109+
110+
return data || null;
111+
}
112+
113+
async fillCaptchaInput(answer: string, password: string) {
114+
const captchaElement = await this.elementParser.parseCaptchaInputElement();
115+
if (!captchaElement) {
116+
throw new Error("captcha input element not found");
117+
}
118+
await captchaElement.type(answer);
119+
120+
await this.typeLoginInfo("", password, 200);
121+
}
122+
86123
async loadMoreHistory() {
87124
if (this._fullyLoaded) {
88125
return;

src/app/naver/service.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,27 @@ export default class Service {
1414
this.module = module;
1515
}
1616

17-
async normalLogin(id: string, password: string) {
17+
async normalLogin(id: string, password: string, delay?: number) {
1818
await this.module.urlChanger.moveToLoginURL();
19-
await this.module.pageInteractor.login(id, password);
19+
await this.module.pageInteractor.login(id, password, delay);
2020
}
2121

22-
interactiveLogin(id: string, password: string) {
23-
const login$ = defer(() => from(this.normalLogin(id, password)));
22+
interactiveLogin(id: string, password: string, delay?: number) {
23+
const login$ = defer(() => from(this.normalLogin(id, password, delay)));
2424
const loginStatus$ = interval(500)
2525
.pipe(mergeMap(() => this.module.pageInteractor.getLoginStatus()))
2626
.pipe(
2727
distinctUntilChanged(),
2828
takeWhile((loginStatus) => loginStatus !== "success")
2929
);
30+
const captchaStatus$ = interval(500)
31+
.pipe(mergeMap(() => this.module.pageInteractor.getCaptchaStatus()))
32+
.pipe(
33+
distinctUntilChanged((a, b) => a?.question === b?.question),
34+
takeWhile((captchaStatus) => captchaStatus !== null)
35+
);
3036

31-
const result$ = concat(login$, loginStatus$);
37+
const result$ = concat(login$, captchaStatus$, loginStatus$);
3238
return result$;
3339
}
3440

0 commit comments

Comments
 (0)