@rich-automation/lotto 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -72
- package/lib/cjs/apis/dhlottery/getWinningNumbers.js +8 -6
- package/lib/cjs/constants/index.js +3 -1
- package/lib/cjs/constants/selectors.js +4 -3
- package/lib/cjs/constants/urls.js +2 -2
- package/lib/cjs/controllers/api/index.js +28 -0
- package/lib/cjs/controllers/factory.js +20 -6
- package/lib/cjs/controllers/playwright/index.js +1 -2
- package/lib/cjs/controllers/playwright/playwright.page.js +10 -0
- package/lib/cjs/controllers/puppeteer/index.js +2 -6
- package/lib/cjs/controllers/puppeteer/puppeteer.page.js +15 -0
- package/lib/cjs/lottoError.js +6 -1
- package/lib/cjs/lottoService.js +45 -42
- package/lib/esm/apis/dhlottery/getWinningNumbers.js +8 -6
- package/lib/esm/constants/index.js +3 -1
- package/lib/esm/constants/selectors.js +4 -3
- package/lib/esm/constants/urls.js +2 -2
- package/lib/esm/controllers/api/index.js +25 -0
- package/lib/esm/controllers/factory.js +20 -6
- package/lib/esm/controllers/playwright/index.js +1 -2
- package/lib/esm/controllers/playwright/playwright.page.js +10 -0
- package/lib/esm/controllers/puppeteer/index.js +2 -3
- package/lib/esm/controllers/puppeteer/puppeteer.page.js +15 -1
- package/lib/esm/lottoError.js +6 -1
- package/lib/esm/lottoService.js +45 -42
- package/lib/typescript/apis/dhlottery/getWinningNumbers.d.ts +1 -1
- package/lib/typescript/constants/index.d.ts +2 -0
- package/lib/typescript/constants/selectors.d.ts +1 -0
- package/lib/typescript/controllers/api/index.d.ts +13 -0
- package/lib/typescript/controllers/factory.d.ts +2 -2
- package/lib/typescript/controllers/playwright/index.d.ts +4 -3
- package/lib/typescript/controllers/playwright/playwright.page.d.ts +1 -0
- package/lib/typescript/controllers/puppeteer/index.d.ts +4 -4
- package/lib/typescript/controllers/puppeteer/puppeteer.page.d.ts +2 -1
- package/lib/typescript/lottoError.d.ts +4 -1
- package/lib/typescript/lottoService.d.ts +1 -1
- package/lib/typescript/types.d.ts +22 -12
- package/package.json +16 -4
package/README.md
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# @rich-automation/lotto
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@rich-automation/lotto)
|
|
4
|
-
[](https://github.com/rich-automation/lotto-module/actions/workflows/ci.yml)
|
|
3
|
+
[](https://www.npmjs.com/package/@rich-automation/lotto)
|
|
4
|
+
[](https://github.com/rich-automation/lotto-module/actions/workflows/ci.yml)
|
|
5
5
|
[](https://codecov.io/gh/rich-automation/lotto-module)
|
|
6
6
|
|
|
7
|
-
`@rich-automation/lotto
|
|
7
|
+
`@rich-automation/lotto`는 headless browser를 활용해 JS 환경에서 로또를 자동으로 구매할 수 있는 인터페이스를 제공합니다.
|
|
8
8
|
|
|
9
|
+
## 설치
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
```shell
|
|
11
|
+
```bash
|
|
13
12
|
# npm
|
|
14
13
|
npm install @rich-automation/lotto
|
|
15
14
|
|
|
@@ -17,105 +16,119 @@ npm install @rich-automation/lotto
|
|
|
17
16
|
yarn add @rich-automation/lotto
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
## 준비 사항
|
|
20
|
+
|
|
21
|
+
1. 내부적으로 Playwright 또는 Puppeteer를 사용하므로, Chromium이 사전에 설치되어 있어야 합니다. ([링크](https://github.com/rich-automation/lotto-module/blob/main/package.json#L38-L39))
|
|
22
|
+
2. 구매는 [동행복권](https://dhlottery.co.kr/common.do?method=main) 사이트에서 진행되며, 예치금 충전 기능은 없으므로 미리 계정에 예치금을 충전해두어야 합니다.
|
|
23
|
+
|
|
24
|
+
## 사용법
|
|
25
|
+
|
|
26
|
+
### 기본 설정
|
|
23
27
|
|
|
24
|
-
## Usage
|
|
25
|
-
### 공통
|
|
26
|
-
lottoService 객체 인스턴스를 생성합니다.
|
|
27
28
|
```js
|
|
28
29
|
import { LottoService } from '@rich-automation/lotto';
|
|
30
|
+
import { chromium } from 'playwright';
|
|
31
|
+
import puppeteer from 'puppeteer';
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
// playwright 로 시작, 모든 기능 사용 가능
|
|
34
|
+
const lottoService = new LottoService({
|
|
35
|
+
controller: chromium // puppeteer
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// puppeteer 로 시작, 모든 기능 사용 가능
|
|
39
|
+
const lottoService = new LottoService({
|
|
40
|
+
controller: puppeteer
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// API 모드로 시작, check / getCheckWinningLink 기능만 사용 가능
|
|
44
|
+
const lottoService = new LottoService({
|
|
45
|
+
controller: 'api'
|
|
46
|
+
});
|
|
32
47
|
```
|
|
33
|
-
LottoService 클래스의 생성자는 BrowserConfigs를 인수로 받을 수 있습니다. 인수로 전달된 configs는 다음과 같은 속성을 가질 수 있습니다:
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- `
|
|
38
|
-
- `
|
|
49
|
+
옵션 설명:
|
|
50
|
+
|
|
51
|
+
- `controller`: (필수) `puppeteer`, `playwright` 모듈 또는 `"api"`
|
|
52
|
+
- `headless`: 기본값 `false`
|
|
53
|
+
- `defaultViewport`: 기본값 `{ width: 1080, height: 1024 }`
|
|
54
|
+
- `logLevel`: 기본값 `2` (NONE = -1, ERROR = 0, WARN = 1, INFO = 2, DEBUG = 3)
|
|
55
|
+
|
|
39
56
|
```js
|
|
40
|
-
|
|
41
|
-
import {LottoService, LogLevel} from '@rich-automation/lotto'
|
|
57
|
+
import { LottoService, LogLevel } from '@rich-automation/lotto';
|
|
42
58
|
|
|
43
59
|
const lottoService = new LottoService({
|
|
60
|
+
controller: chromium,
|
|
44
61
|
headless: true,
|
|
45
|
-
defaultViewport: { width: 1280, height: 720 },
|
|
62
|
+
defaultViewport: { width: 1280, height: 720 },
|
|
46
63
|
logLevel: LogLevel.DEBUG
|
|
47
64
|
});
|
|
48
65
|
```
|
|
49
|
-
### 구매
|
|
50
|
-
```js
|
|
51
|
-
import {LottoService} from "@rich-automation/lotto";
|
|
52
66
|
|
|
53
|
-
|
|
54
|
-
const PWD = "<YOUR_PASSWORD>";
|
|
67
|
+
### 로또 구매
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
```js
|
|
70
|
+
const ID = '<YOUR_ID>';
|
|
71
|
+
const PWD = '<YOUR_PASSWORD>';
|
|
59
72
|
|
|
60
|
-
await lottoService.signIn(ID,PWD);
|
|
73
|
+
await lottoService.signIn(ID, PWD);
|
|
61
74
|
|
|
62
75
|
const numbers = await lottoService.purchase(5);
|
|
63
|
-
console.log(numbers); //[[ 1, 14, 21, 27, 30, 44 ],[ 4, 5, 27, 29, 40, 44 ],[ 9, 18, 19, 24, 38, 42 ],[ 4, 6, 13, 20, 38, 39 ],[ 8, 9, 10, 19, 32, 40 ]]
|
|
64
|
-
|
|
65
|
-
|
|
66
76
|
|
|
77
|
+
console.log(numbers); // [[ 1, 14, 21, 27, 30, 44 ],[ 4, 5, 27, 29, 40, 44 ],[ 9, 18, 19, 24, 38, 42 ],[ 4, 6, 13, 20, 38, 39 ],[ 8, 9, 10, 19, 32, 40 ]]
|
|
67
78
|
```
|
|
68
79
|
|
|
69
80
|
### 당첨 확인
|
|
70
|
-
이번 회차 당첨 확인
|
|
71
|
-
```js
|
|
72
|
-
import {getLastLottoRound,LottoService} from "@rich-automation/lotto";
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
이번 회차 확인:
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
```js
|
|
85
|
+
import { getLastLottoRound } from '@rich-automation/lotto';
|
|
86
|
+
|
|
87
|
+
const numbers = [
|
|
88
|
+
[1, 2, 3, 4, 5, 6],
|
|
89
|
+
[5, 6, 7, 8, 9, 10]
|
|
90
|
+
];
|
|
79
91
|
|
|
80
92
|
const currentRound = getLastLottoRound();
|
|
81
93
|
|
|
82
|
-
const result = await lottoService.check(numbers,currentRound)
|
|
83
|
-
console.log(result) //[{rank:1,matchedNumbers:[1,2,3,4,5,6]},{rank:5,matchedNumbers:[5,6]]
|
|
84
|
-
```
|
|
85
|
-
다음 회차 당첨 링크 생성
|
|
86
|
-
```js
|
|
87
|
-
import {getNextLottoRound,LottoService} from "@rich-automation/lotto";
|
|
94
|
+
const result = await lottoService.check(numbers, currentRound);
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
console.log(result); // [{rank:1,matchedNumbers:[1,2,3,4,5,6]},{rank:5,matchedNumbers:[5,6]]
|
|
97
|
+
```
|
|
90
98
|
|
|
99
|
+
다음 회차 링크 생성:
|
|
91
100
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
101
|
+
```js
|
|
102
|
+
import { getNextLottoRound } from '@rich-automation/lotto';
|
|
95
103
|
|
|
96
104
|
const nextRound = getNextLottoRound();
|
|
97
|
-
const link = lottoService.getCheckWinningLink(numbers,nextRound);
|
|
98
|
-
console.log(link) //"https://dhlottery.co.kr/qr.do?method=winQr&v=1071q010203040506q050607080910";
|
|
105
|
+
const link = lottoService.getCheckWinningLink(numbers, nextRound);
|
|
99
106
|
|
|
107
|
+
console.log(link); // "https://dhlottery.co.kr/qr.do?method=winQr&v=1071q010203040506q050607080910";
|
|
100
108
|
```
|
|
101
109
|
|
|
110
|
+
## API
|
|
111
|
+
|
|
112
|
+
### `signIn(id: string, password: string): Promise<string>`
|
|
113
|
+
|
|
114
|
+
동행복권 로그인, 성공 시 로그인 쿠키 반환
|
|
115
|
+
|
|
116
|
+
### `signInWithCookie(cookies: string): Promise<string>`
|
|
117
|
+
|
|
118
|
+
쿠키 기반 로그인
|
|
119
|
+
|
|
120
|
+
### `purchase(amount?: number): Promise<number[][]>`
|
|
121
|
+
|
|
122
|
+
로또 번호 자동 구매 (1~5 게임)
|
|
123
|
+
|
|
124
|
+
### `check(numbers: number[][], round?: number): Promise<{ rank:number; matchedNumbers:number[] }[]>`
|
|
125
|
+
|
|
126
|
+
(API) 당첨 결과 확인
|
|
127
|
+
|
|
128
|
+
### `getCheckWinningLink(numbers: number[][], round?: number): string`
|
|
129
|
+
|
|
130
|
+
(API) 당첨 확인 링크 생성
|
|
131
|
+
|
|
132
|
+
### `destroy(): Promise<void>`
|
|
102
133
|
|
|
103
|
-
|
|
104
|
-
### signIn
|
|
105
|
-
- `(id: string, password: string) => Promise<string>`
|
|
106
|
-
- description: id와 pwd를 입력받아 동행복권에 로그인합니다. 성공할경우 로그인 쿠키를 반환합니다.
|
|
107
|
-
### signInWithCookie
|
|
108
|
-
- `(cookies: string) => Promise<string>`
|
|
109
|
-
- description: 로그인 쿠키를 입력받아 동행복권에 로그인합니다. 성공할경우 로그인 쿠키를 반환합니다.
|
|
110
|
-
### purchase
|
|
111
|
-
- `(amount?: number) => Promise<number[][]>`
|
|
112
|
-
- description: 구매할 게임 횟수를 입력받아 로또를 구매하고, 구매한 번호를 이차원 배열 형태로 반환합니다. amount는 1~5사이 값을 가집니다.
|
|
113
|
-
### check
|
|
114
|
-
- `(numbers: number[][], round?: number) => Promise<{ rank:number; matchedNumbers:number[] }[]>`
|
|
115
|
-
- description: 회차와 해당 회차에 구매한 로또번호를 입력받아 당첨 등수(rank)와 맞춘 번호(matchedNumbers)목록을 반환합니다. 회차를 지정하지 않으면 최신 회차를 기준으로 확인합니다.
|
|
116
|
-
### getCheckWinningLink
|
|
117
|
-
- `(numbers: number[][], round?: number) => string`
|
|
118
|
-
- description: 회차와 구매한 로또 번호를 입력받아 당첨 확인 링크를 생성합니다. 로또 번호는 해당 회차에 구매한 모든 게임을 이차원 배열 형태로 입력받습니다. 회차를 지정하지 않으면 다음 회차를 기준으로 링크를 생성합니다.
|
|
119
|
-
### destroy
|
|
120
|
-
- `() => Promise<void>`
|
|
121
|
-
- description: LottoService 인스턴스에서 사용한 브라우저 컨트롤러를 종료합니다.
|
|
134
|
+
브라우저 인스턴스 종료
|
|
@@ -15,16 +15,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.getWinningNumbers = void 0;
|
|
16
16
|
const axios_1 = __importDefault(require("axios"));
|
|
17
17
|
const lottoError_1 = __importDefault(require("../../lottoError"));
|
|
18
|
-
const getWinningNumbers = (
|
|
18
|
+
const getWinningNumbers = (round) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
var _a, _b;
|
|
19
20
|
let res;
|
|
20
21
|
try {
|
|
21
|
-
res = yield axios_1.default.get(`https://
|
|
22
|
+
res = yield axios_1.default.get(`https://dhlottery.co.kr/lt645/selectPstLt645Info.do?srchStrLtEpsd=${round}&srchEndLtEpsd=${round}`);
|
|
22
23
|
}
|
|
23
|
-
catch (
|
|
24
|
+
catch (_c) {
|
|
24
25
|
throw lottoError_1.default.NetworkError();
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const item = (_b = (_a = res.data.data) === null || _a === void 0 ? void 0 : _a.list) === null || _b === void 0 ? void 0 : _b[0];
|
|
28
|
+
if (item) {
|
|
29
|
+
return toOrderedWinningNumbers(item);
|
|
28
30
|
}
|
|
29
31
|
else {
|
|
30
32
|
throw lottoError_1.default.InvalidRound();
|
|
@@ -32,5 +34,5 @@ const getWinningNumbers = (volume) => __awaiter(void 0, void 0, void 0, function
|
|
|
32
34
|
});
|
|
33
35
|
exports.getWinningNumbers = getWinningNumbers;
|
|
34
36
|
function toOrderedWinningNumbers(data) {
|
|
35
|
-
return [data.
|
|
37
|
+
return [data.tm1WnNo, data.tm2WnNo, data.tm3WnNo, data.tm4WnNo, data.tm5WnNo, data.tm6WnNo, data.bnsWnNo];
|
|
36
38
|
}
|
|
@@ -8,6 +8,8 @@ exports.CONST = {
|
|
|
8
8
|
BROWSER_DESTROY_SAFE_TIMEOUT: 1000,
|
|
9
9
|
BROWSER_PAGE_POPUP_WAIT: 1500,
|
|
10
10
|
BROWSER_PAGE_DIALOG_WAIT: 10000,
|
|
11
|
+
BROWSER_LOGIN_WAIT: 5000,
|
|
11
12
|
WEEK_TO_MILLISECOND: 604800000,
|
|
12
|
-
THOUSAND_ROUND_DATE: '2022-01-29T11:50:00Z'
|
|
13
|
+
THOUSAND_ROUND_DATE: '2022-01-29T11:50:00Z',
|
|
14
|
+
LOGIN_ERROR_MESSAGE: '아이디 또는 비밀번호가 일치하지 않습니다'
|
|
13
15
|
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SELECTORS = void 0;
|
|
4
4
|
exports.SELECTORS = {
|
|
5
|
-
ID_INPUT: '#
|
|
6
|
-
PWD_INPUT: '#
|
|
7
|
-
LOGIN_BUTTON: '#
|
|
5
|
+
ID_INPUT: '#inpUserId',
|
|
6
|
+
PWD_INPUT: '#inpUserPswdEncn',
|
|
7
|
+
LOGIN_BUTTON: '#btnLogin',
|
|
8
|
+
LOGIN_ERROR_POPUP: '.msgPop[role="alertdialog"]',
|
|
8
9
|
ENVIRONMENT_ALERT_CONFIRM: 'input[value="확인"][onclick="javascript:closepopupLayerAlert();"]',
|
|
9
10
|
PURCHASE_TYPE_RANDOM_BTN: 'a[href="#divWay2Buy1"]#num2',
|
|
10
11
|
PURCHASE_AMOUNT_SELECT: 'select#amoundApply',
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.URLS = void 0;
|
|
4
4
|
exports.URLS = {
|
|
5
|
-
MAIN: 'https://dhlottery.co.kr/
|
|
6
|
-
LOGIN: 'https://dhlottery.co.kr/
|
|
5
|
+
MAIN: 'https://dhlottery.co.kr/main',
|
|
6
|
+
LOGIN: 'https://dhlottery.co.kr/login',
|
|
7
7
|
LOTTO_645: 'https://ol.dhlottery.co.kr/olotto/game/game645.do',
|
|
8
8
|
CHECK_WINNING: 'https://dhlottery.co.kr/qr.do'
|
|
9
9
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.APIModeController = void 0;
|
|
13
|
+
class APIModeController {
|
|
14
|
+
constructor(configs, logger) {
|
|
15
|
+
this.focus = () => __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
throw new Error('APIModeController does not support focus method');
|
|
17
|
+
});
|
|
18
|
+
this.close = () => __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
this.logger.warn('APIModeController does not support close method');
|
|
20
|
+
});
|
|
21
|
+
this.cleanPages = () => __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
this.logger.warn('APIModeController does not support cleanPages method');
|
|
23
|
+
});
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
this.configs = configs;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.APIModeController = APIModeController;
|
|
@@ -3,12 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createBrowserController = void 0;
|
|
4
4
|
const puppeteer_1 = require("./puppeteer");
|
|
5
5
|
const playwright_1 = require("./playwright");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
case 'playwright':
|
|
11
|
-
return new playwright_1.PlaywrightController(configs, logger);
|
|
6
|
+
const api_1 = require("./api");
|
|
7
|
+
function createBrowserController(configs, logger) {
|
|
8
|
+
if (isAPIMode(configs)) {
|
|
9
|
+
return new api_1.APIModeController(configs, logger);
|
|
12
10
|
}
|
|
11
|
+
if (isPlaywright(configs)) {
|
|
12
|
+
return new playwright_1.PlaywrightController(configs, logger);
|
|
13
|
+
}
|
|
14
|
+
if (isPuppeteer(configs)) {
|
|
15
|
+
return new puppeteer_1.PuppeteerController(configs, logger);
|
|
16
|
+
}
|
|
17
|
+
throw new Error('Invalid browser controller');
|
|
13
18
|
}
|
|
14
19
|
exports.createBrowserController = createBrowserController;
|
|
20
|
+
function isAPIMode(configs) {
|
|
21
|
+
return configs.controller === 'api';
|
|
22
|
+
}
|
|
23
|
+
function isPlaywright(configs) {
|
|
24
|
+
return typeof configs.controller !== 'string' && 'connectOverCDP' in configs.controller;
|
|
25
|
+
}
|
|
26
|
+
function isPuppeteer(configs) {
|
|
27
|
+
return typeof configs.controller !== 'string' && 'executablePath' in configs.controller;
|
|
28
|
+
}
|
|
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.PlaywrightController = void 0;
|
|
13
|
-
const playwright_1 = require("playwright");
|
|
14
13
|
const deferred_1 = require("../../utils/deferred");
|
|
15
14
|
const playwright_page_1 = require("./playwright.page");
|
|
16
15
|
const constants_1 = require("../../constants");
|
|
@@ -69,7 +68,7 @@ class PlaywrightController {
|
|
|
69
68
|
});
|
|
70
69
|
this.configs = configs;
|
|
71
70
|
this.logger = logger;
|
|
72
|
-
|
|
71
|
+
this.configs.controller.launch(this.configs).then((browser) => __awaiter(this, void 0, void 0, function* () {
|
|
73
72
|
this.browser = browser;
|
|
74
73
|
this.context = yield browser.newContext();
|
|
75
74
|
}));
|
|
@@ -54,6 +54,16 @@ class PlaywrightPage {
|
|
|
54
54
|
querySelectorAll(selector, callback) {
|
|
55
55
|
return this.page.$$eval(selector, callback);
|
|
56
56
|
}
|
|
57
|
+
exists(selector, containsText) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
if (containsText) {
|
|
60
|
+
const count = yield this.page.locator(selector).filter({ hasText: containsText }).count();
|
|
61
|
+
return count > 0;
|
|
62
|
+
}
|
|
63
|
+
const count = yield this.page.locator(selector).count();
|
|
64
|
+
return count > 0;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
57
67
|
getCookies() {
|
|
58
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
69
|
const cookies = yield this.context.cookies();
|
|
@@ -8,12 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
12
|
exports.PuppeteerController = void 0;
|
|
16
|
-
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
17
13
|
const deferred_1 = require("../../utils/deferred");
|
|
18
14
|
const puppeteer_page_1 = require("./puppeteer.page");
|
|
19
15
|
const constants_1 = require("../../constants");
|
|
@@ -71,8 +67,8 @@ class PuppeteerController {
|
|
|
71
67
|
});
|
|
72
68
|
this.configs = configs;
|
|
73
69
|
this.logger = logger;
|
|
74
|
-
|
|
75
|
-
.launch(Object.assign(Object.assign({}, this.configs), { headless: this.configs.headless === true
|
|
70
|
+
configs.controller
|
|
71
|
+
.launch(Object.assign(Object.assign({}, this.configs), { headless: this.configs.headless === true }))
|
|
76
72
|
.then((browser) => __awaiter(this, void 0, void 0, function* () {
|
|
77
73
|
this.browser = browser;
|
|
78
74
|
}));
|
|
@@ -53,6 +53,21 @@ class PuppeteerPage {
|
|
|
53
53
|
querySelectorAll(selector, callback) {
|
|
54
54
|
return this.page.$$eval(selector, callback);
|
|
55
55
|
}
|
|
56
|
+
exists(selector, containsText) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
if (containsText) {
|
|
59
|
+
const elements = yield this.page.$$(selector);
|
|
60
|
+
for (const el of elements) {
|
|
61
|
+
const text = yield el.evaluate(node => node.textContent);
|
|
62
|
+
if (text === null || text === void 0 ? void 0 : text.includes(containsText))
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const element = yield this.page.$(selector);
|
|
68
|
+
return element !== null;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
56
71
|
getCookies() {
|
|
57
72
|
return __awaiter(this, void 0, void 0, function* () {
|
|
58
73
|
const cookies = yield this.page.cookies();
|
package/lib/cjs/lottoError.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const invertObject_1 = require("./utils/invertObject");
|
|
4
4
|
const BaseErrorCode = {
|
|
5
5
|
NETWORK_ERROR: 100001,
|
|
6
|
+
NOT_SUPPORTED: 110000,
|
|
6
7
|
UNKNOWN_ERROR: 199999
|
|
7
8
|
};
|
|
8
9
|
const LoginErrorCode = {
|
|
@@ -25,7 +26,8 @@ const ErrorMessage = {
|
|
|
25
26
|
[ErrorCode.INVALID_ROUND]: '로또 회차가 올바르지 않습니다.',
|
|
26
27
|
[ErrorCode.INVALID_LOTTO_NUMBER]: '로또 번호가 올바르지 않습니다.',
|
|
27
28
|
[ErrorCode.NOT_AUTHENTICATED]: '인증되지 않았습니다.',
|
|
28
|
-
[ErrorCode.PURCHASE_UNAVAILABLE]: '현재는 로또 구매가 불가능합니다.'
|
|
29
|
+
[ErrorCode.PURCHASE_UNAVAILABLE]: '현재는 로또 구매가 불가능합니다.',
|
|
30
|
+
[ErrorCode.NOT_SUPPORTED]: '지원되지 않는 기능입니다.'
|
|
29
31
|
};
|
|
30
32
|
class LottoError extends Error {
|
|
31
33
|
static NetworkError() {
|
|
@@ -52,6 +54,9 @@ class LottoError extends Error {
|
|
|
52
54
|
static PurchaseUnavailable() {
|
|
53
55
|
return new LottoError(ErrorCode.PURCHASE_UNAVAILABLE);
|
|
54
56
|
}
|
|
57
|
+
static NotSupported(message) {
|
|
58
|
+
return new LottoError(ErrorCode.NOT_SUPPORTED, message);
|
|
59
|
+
}
|
|
55
60
|
static get code() {
|
|
56
61
|
return ErrorCode;
|
|
57
62
|
}
|
package/lib/cjs/lottoService.js
CHANGED
|
@@ -17,7 +17,6 @@ const lottoError_1 = __importDefault(require("./lottoError"));
|
|
|
17
17
|
const selectors_1 = require("./constants/selectors");
|
|
18
18
|
const factory_1 = require("./controllers/factory");
|
|
19
19
|
const urls_1 = require("./constants/urls");
|
|
20
|
-
const deferred_1 = require("./utils/deferred");
|
|
21
20
|
const constants_1 = require("./constants");
|
|
22
21
|
const lazyRun_1 = require("./utils/lazyRun");
|
|
23
22
|
const logger_1 = __importDefault(require("./logger"));
|
|
@@ -30,14 +29,19 @@ const getCheckWinningLink_1 = require("./utils/getCheckWinningLink");
|
|
|
30
29
|
const getNextLottoRound_1 = require("./utils/getNextLottoRound");
|
|
31
30
|
class LottoService {
|
|
32
31
|
constructor(configs) {
|
|
33
|
-
var _a;
|
|
34
32
|
this.context = {
|
|
35
33
|
authenticated: false
|
|
36
34
|
};
|
|
37
35
|
this.destroy = () => __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
if (this.browserController.configs.controller === 'api') {
|
|
37
|
+
throw lottoError_1.default.NotSupported('API mode does not support destroy.');
|
|
38
|
+
}
|
|
38
39
|
return (0, lazyRun_1.lazyRun)(this.browserController.close, constants_1.CONST.BROWSER_DESTROY_SAFE_TIMEOUT);
|
|
39
40
|
});
|
|
40
41
|
this.signInWithCookie = (cookies) => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
if (this.browserController.configs.controller === 'api') {
|
|
43
|
+
throw lottoError_1.default.NotSupported('API mode does not support signInWithCookie.');
|
|
44
|
+
}
|
|
41
45
|
// 쿠키 설정 & 페이지 이동
|
|
42
46
|
const page = yield this.browserController.focus(0);
|
|
43
47
|
this.logger.debug('[signInWithCookie]', 'setCookies');
|
|
@@ -59,46 +63,45 @@ class LottoService {
|
|
|
59
63
|
return page.getCookies();
|
|
60
64
|
});
|
|
61
65
|
this.signIn = (id, password) => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
yield page.fill(selectors_1.SELECTORS.PWD_INPUT, password);
|
|
97
|
-
yield page.click(selectors_1.SELECTORS.LOGIN_BUTTON);
|
|
98
|
-
}));
|
|
99
|
-
return p.promise;
|
|
66
|
+
if (this.browserController.configs.controller === 'api') {
|
|
67
|
+
throw lottoError_1.default.NotSupported('API mode does not support signIn.');
|
|
68
|
+
}
|
|
69
|
+
// 페이지 이동
|
|
70
|
+
const page = yield this.browserController.focus(0);
|
|
71
|
+
this.logger.debug('[signIn]', 'goto', 'login page');
|
|
72
|
+
yield page.goto(urls_1.URLS.LOGIN);
|
|
73
|
+
this.logger.debug('[signIn]', 'page url', yield page.url());
|
|
74
|
+
// 로그인 시도
|
|
75
|
+
this.logger.debug('[signIn]', 'try login');
|
|
76
|
+
yield page.fill(selectors_1.SELECTORS.ID_INPUT, id);
|
|
77
|
+
yield page.fill(selectors_1.SELECTORS.PWD_INPUT, password);
|
|
78
|
+
yield page.click(selectors_1.SELECTORS.LOGIN_BUTTON);
|
|
79
|
+
// 결과 대기
|
|
80
|
+
yield page.wait(constants_1.CONST.BROWSER_LOGIN_WAIT);
|
|
81
|
+
// 결과 확인
|
|
82
|
+
const currentUrl = yield page.url();
|
|
83
|
+
// 성공: MAIN URL로 이동됨
|
|
84
|
+
if (currentUrl.includes(urls_1.URLS.MAIN)) {
|
|
85
|
+
this.logger.info('[signIn]', 'success');
|
|
86
|
+
this.context.authenticated = true;
|
|
87
|
+
this.logger.debug('[signIn]', 'clear popups');
|
|
88
|
+
yield page.wait(constants_1.CONST.BROWSER_PAGE_POPUP_WAIT);
|
|
89
|
+
yield this.browserController.cleanPages([0]);
|
|
90
|
+
return page.getCookies();
|
|
91
|
+
}
|
|
92
|
+
// 실패: 로그인 에러 팝업 확인
|
|
93
|
+
if (yield page.exists(selectors_1.SELECTORS.LOGIN_ERROR_POPUP, constants_1.CONST.LOGIN_ERROR_MESSAGE)) {
|
|
94
|
+
this.logger.info('[signIn]', 'failed', 'credentials incorrect');
|
|
95
|
+
throw lottoError_1.default.CredentialsIncorrect();
|
|
96
|
+
}
|
|
97
|
+
// 기타 실패
|
|
98
|
+
this.logger.info('[signIn]', 'failed', 'unknown');
|
|
99
|
+
throw lottoError_1.default.CredentialsIncorrect();
|
|
100
100
|
});
|
|
101
101
|
this.purchase = (amount = 5) => __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
if (this.browserController.configs.controller === 'api') {
|
|
103
|
+
throw lottoError_1.default.NotSupported('API mode does not support purchase.');
|
|
104
|
+
}
|
|
102
105
|
if (!this.context.authenticated)
|
|
103
106
|
throw lottoError_1.default.NotAuthenticated();
|
|
104
107
|
this.logger.debug('[purchase]', 'validatePurchaseAvailability');
|
|
@@ -142,8 +145,8 @@ class LottoService {
|
|
|
142
145
|
this.logger.debug('[getCheckWinningLink]', 'getCheckWinningLink');
|
|
143
146
|
return (0, getCheckWinningLink_1.getCheckWinningLink)(numbers, round);
|
|
144
147
|
};
|
|
145
|
-
this.logger = new logger_1.default(configs
|
|
146
|
-
this.browserController = (0, factory_1.createBrowserController)(
|
|
148
|
+
this.logger = new logger_1.default(configs.logLevel, '[LottoService]');
|
|
149
|
+
this.browserController = (0, factory_1.createBrowserController)(Object.assign({ defaultViewport: { width: 1080, height: 1024 } }, configs), this.logger);
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
exports.LottoService = LottoService;
|
|
@@ -9,21 +9,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import axios from 'axios';
|
|
11
11
|
import LottoError from '../../lottoError';
|
|
12
|
-
export const getWinningNumbers = (
|
|
12
|
+
export const getWinningNumbers = (round) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
var _a, _b;
|
|
13
14
|
let res;
|
|
14
15
|
try {
|
|
15
|
-
res = yield axios.get(`https://
|
|
16
|
+
res = yield axios.get(`https://dhlottery.co.kr/lt645/selectPstLt645Info.do?srchStrLtEpsd=${round}&srchEndLtEpsd=${round}`);
|
|
16
17
|
}
|
|
17
|
-
catch (
|
|
18
|
+
catch (_c) {
|
|
18
19
|
throw LottoError.NetworkError();
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const item = (_b = (_a = res.data.data) === null || _a === void 0 ? void 0 : _a.list) === null || _b === void 0 ? void 0 : _b[0];
|
|
22
|
+
if (item) {
|
|
23
|
+
return toOrderedWinningNumbers(item);
|
|
22
24
|
}
|
|
23
25
|
else {
|
|
24
26
|
throw LottoError.InvalidRound();
|
|
25
27
|
}
|
|
26
28
|
});
|
|
27
29
|
function toOrderedWinningNumbers(data) {
|
|
28
|
-
return [data.
|
|
30
|
+
return [data.tm1WnNo, data.tm2WnNo, data.tm3WnNo, data.tm4WnNo, data.tm5WnNo, data.tm6WnNo, data.bnsWnNo];
|
|
29
31
|
}
|
|
@@ -5,6 +5,8 @@ export const CONST = {
|
|
|
5
5
|
BROWSER_DESTROY_SAFE_TIMEOUT: 1000,
|
|
6
6
|
BROWSER_PAGE_POPUP_WAIT: 1500,
|
|
7
7
|
BROWSER_PAGE_DIALOG_WAIT: 10000,
|
|
8
|
+
BROWSER_LOGIN_WAIT: 5000,
|
|
8
9
|
WEEK_TO_MILLISECOND: 604800000,
|
|
9
|
-
THOUSAND_ROUND_DATE: '2022-01-29T11:50:00Z'
|
|
10
|
+
THOUSAND_ROUND_DATE: '2022-01-29T11:50:00Z',
|
|
11
|
+
LOGIN_ERROR_MESSAGE: '아이디 또는 비밀번호가 일치하지 않습니다'
|
|
10
12
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export const SELECTORS = {
|
|
2
|
-
ID_INPUT: '#
|
|
3
|
-
PWD_INPUT: '#
|
|
4
|
-
LOGIN_BUTTON: '#
|
|
2
|
+
ID_INPUT: '#inpUserId',
|
|
3
|
+
PWD_INPUT: '#inpUserPswdEncn',
|
|
4
|
+
LOGIN_BUTTON: '#btnLogin',
|
|
5
|
+
LOGIN_ERROR_POPUP: '.msgPop[role="alertdialog"]',
|
|
5
6
|
ENVIRONMENT_ALERT_CONFIRM: 'input[value="확인"][onclick="javascript:closepopupLayerAlert();"]',
|
|
6
7
|
PURCHASE_TYPE_RANDOM_BTN: 'a[href="#divWay2Buy1"]#num2',
|
|
7
8
|
PURCHASE_AMOUNT_SELECT: 'select#amoundApply',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const URLS = {
|
|
2
|
-
MAIN: 'https://dhlottery.co.kr/
|
|
3
|
-
LOGIN: 'https://dhlottery.co.kr/
|
|
2
|
+
MAIN: 'https://dhlottery.co.kr/main',
|
|
3
|
+
LOGIN: 'https://dhlottery.co.kr/login',
|
|
4
4
|
LOTTO_645: 'https://ol.dhlottery.co.kr/olotto/game/game645.do',
|
|
5
5
|
CHECK_WINNING: 'https://dhlottery.co.kr/qr.do'
|
|
6
6
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import {} from '../../logger';
|
|
11
|
+
export class APIModeController {
|
|
12
|
+
constructor(configs, logger) {
|
|
13
|
+
this.focus = () => __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
throw new Error('APIModeController does not support focus method');
|
|
15
|
+
});
|
|
16
|
+
this.close = () => __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
this.logger.warn('APIModeController does not support close method');
|
|
18
|
+
});
|
|
19
|
+
this.cleanPages = () => __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
this.logger.warn('APIModeController does not support cleanPages method');
|
|
21
|
+
});
|
|
22
|
+
this.logger = logger;
|
|
23
|
+
this.configs = configs;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { PuppeteerController } from './puppeteer';
|
|
2
2
|
import { PlaywrightController } from './playwright';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
case 'playwright':
|
|
8
|
-
return new PlaywrightController(configs, logger);
|
|
3
|
+
import { APIModeController } from './api';
|
|
4
|
+
export function createBrowserController(configs, logger) {
|
|
5
|
+
if (isAPIMode(configs)) {
|
|
6
|
+
return new APIModeController(configs, logger);
|
|
9
7
|
}
|
|
8
|
+
if (isPlaywright(configs)) {
|
|
9
|
+
return new PlaywrightController(configs, logger);
|
|
10
|
+
}
|
|
11
|
+
if (isPuppeteer(configs)) {
|
|
12
|
+
return new PuppeteerController(configs, logger);
|
|
13
|
+
}
|
|
14
|
+
throw new Error('Invalid browser controller');
|
|
15
|
+
}
|
|
16
|
+
function isAPIMode(configs) {
|
|
17
|
+
return configs.controller === 'api';
|
|
18
|
+
}
|
|
19
|
+
function isPlaywright(configs) {
|
|
20
|
+
return typeof configs.controller !== 'string' && 'connectOverCDP' in configs.controller;
|
|
21
|
+
}
|
|
22
|
+
function isPuppeteer(configs) {
|
|
23
|
+
return typeof configs.controller !== 'string' && 'executablePath' in configs.controller;
|
|
10
24
|
}
|
|
@@ -7,7 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { chromium } from 'playwright';
|
|
11
10
|
import { deferred } from '../../utils/deferred';
|
|
12
11
|
import { PlaywrightPage } from './playwright.page';
|
|
13
12
|
import { CONST } from '../../constants';
|
|
@@ -67,7 +66,7 @@ export class PlaywrightController {
|
|
|
67
66
|
});
|
|
68
67
|
this.configs = configs;
|
|
69
68
|
this.logger = logger;
|
|
70
|
-
|
|
69
|
+
this.configs.controller.launch(this.configs).then((browser) => __awaiter(this, void 0, void 0, function* () {
|
|
71
70
|
this.browser = browser;
|
|
72
71
|
this.context = yield browser.newContext();
|
|
73
72
|
}));
|
|
@@ -51,6 +51,16 @@ export class PlaywrightPage {
|
|
|
51
51
|
querySelectorAll(selector, callback) {
|
|
52
52
|
return this.page.$$eval(selector, callback);
|
|
53
53
|
}
|
|
54
|
+
exists(selector, containsText) {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
if (containsText) {
|
|
57
|
+
const count = yield this.page.locator(selector).filter({ hasText: containsText }).count();
|
|
58
|
+
return count > 0;
|
|
59
|
+
}
|
|
60
|
+
const count = yield this.page.locator(selector).count();
|
|
61
|
+
return count > 0;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
54
64
|
getCookies() {
|
|
55
65
|
return __awaiter(this, void 0, void 0, function* () {
|
|
56
66
|
const cookies = yield this.context.cookies();
|
|
@@ -7,7 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import puppeteer, { Browser } from 'puppeteer';
|
|
11
10
|
import { deferred } from '../../utils/deferred';
|
|
12
11
|
import { PuppeteerPage } from './puppeteer.page';
|
|
13
12
|
import { CONST } from '../../constants';
|
|
@@ -66,8 +65,8 @@ export class PuppeteerController {
|
|
|
66
65
|
});
|
|
67
66
|
this.configs = configs;
|
|
68
67
|
this.logger = logger;
|
|
69
|
-
|
|
70
|
-
.launch(Object.assign(Object.assign({}, this.configs), { headless: this.configs.headless === true
|
|
68
|
+
configs.controller
|
|
69
|
+
.launch(Object.assign(Object.assign({}, this.configs), { headless: this.configs.headless === true }))
|
|
71
70
|
.then((browser) => __awaiter(this, void 0, void 0, function* () {
|
|
72
71
|
this.browser = browser;
|
|
73
72
|
}));
|
|
@@ -7,7 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { Page } from 'puppeteer';
|
|
11
10
|
import { deferred } from '../../utils/deferred';
|
|
12
11
|
import { lazyRun } from '../../utils/lazyRun';
|
|
13
12
|
export class PuppeteerPage {
|
|
@@ -51,6 +50,21 @@ export class PuppeteerPage {
|
|
|
51
50
|
querySelectorAll(selector, callback) {
|
|
52
51
|
return this.page.$$eval(selector, callback);
|
|
53
52
|
}
|
|
53
|
+
exists(selector, containsText) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
if (containsText) {
|
|
56
|
+
const elements = yield this.page.$$(selector);
|
|
57
|
+
for (const el of elements) {
|
|
58
|
+
const text = yield el.evaluate(node => node.textContent);
|
|
59
|
+
if (text === null || text === void 0 ? void 0 : text.includes(containsText))
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const element = yield this.page.$(selector);
|
|
65
|
+
return element !== null;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
54
68
|
getCookies() {
|
|
55
69
|
return __awaiter(this, void 0, void 0, function* () {
|
|
56
70
|
const cookies = yield this.page.cookies();
|
package/lib/esm/lottoError.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { invertObject } from './utils/invertObject';
|
|
2
2
|
const BaseErrorCode = {
|
|
3
3
|
NETWORK_ERROR: 100001,
|
|
4
|
+
NOT_SUPPORTED: 110000,
|
|
4
5
|
UNKNOWN_ERROR: 199999
|
|
5
6
|
};
|
|
6
7
|
const LoginErrorCode = {
|
|
@@ -23,7 +24,8 @@ const ErrorMessage = {
|
|
|
23
24
|
[ErrorCode.INVALID_ROUND]: '로또 회차가 올바르지 않습니다.',
|
|
24
25
|
[ErrorCode.INVALID_LOTTO_NUMBER]: '로또 번호가 올바르지 않습니다.',
|
|
25
26
|
[ErrorCode.NOT_AUTHENTICATED]: '인증되지 않았습니다.',
|
|
26
|
-
[ErrorCode.PURCHASE_UNAVAILABLE]: '현재는 로또 구매가 불가능합니다.'
|
|
27
|
+
[ErrorCode.PURCHASE_UNAVAILABLE]: '현재는 로또 구매가 불가능합니다.',
|
|
28
|
+
[ErrorCode.NOT_SUPPORTED]: '지원되지 않는 기능입니다.'
|
|
27
29
|
};
|
|
28
30
|
export default class LottoError extends Error {
|
|
29
31
|
static NetworkError() {
|
|
@@ -50,6 +52,9 @@ export default class LottoError extends Error {
|
|
|
50
52
|
static PurchaseUnavailable() {
|
|
51
53
|
return new LottoError(ErrorCode.PURCHASE_UNAVAILABLE);
|
|
52
54
|
}
|
|
55
|
+
static NotSupported(message) {
|
|
56
|
+
return new LottoError(ErrorCode.NOT_SUPPORTED, message);
|
|
57
|
+
}
|
|
53
58
|
static get code() {
|
|
54
59
|
return ErrorCode;
|
|
55
60
|
}
|
package/lib/esm/lottoService.js
CHANGED
|
@@ -11,7 +11,6 @@ import LottoError from './lottoError';
|
|
|
11
11
|
import { SELECTORS } from './constants/selectors';
|
|
12
12
|
import { createBrowserController } from './controllers/factory';
|
|
13
13
|
import { URLS } from './constants/urls';
|
|
14
|
-
import { deferred } from './utils/deferred';
|
|
15
14
|
import { CONST } from './constants';
|
|
16
15
|
import { lazyRun } from './utils/lazyRun';
|
|
17
16
|
import Logger, {} from './logger';
|
|
@@ -24,14 +23,19 @@ import { getCheckWinningLink } from './utils/getCheckWinningLink';
|
|
|
24
23
|
import { getNextLottoRound } from './utils/getNextLottoRound';
|
|
25
24
|
export class LottoService {
|
|
26
25
|
constructor(configs) {
|
|
27
|
-
var _a;
|
|
28
26
|
this.context = {
|
|
29
27
|
authenticated: false
|
|
30
28
|
};
|
|
31
29
|
this.destroy = () => __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
if (this.browserController.configs.controller === 'api') {
|
|
31
|
+
throw LottoError.NotSupported('API mode does not support destroy.');
|
|
32
|
+
}
|
|
32
33
|
return lazyRun(this.browserController.close, CONST.BROWSER_DESTROY_SAFE_TIMEOUT);
|
|
33
34
|
});
|
|
34
35
|
this.signInWithCookie = (cookies) => __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
if (this.browserController.configs.controller === 'api') {
|
|
37
|
+
throw LottoError.NotSupported('API mode does not support signInWithCookie.');
|
|
38
|
+
}
|
|
35
39
|
// 쿠키 설정 & 페이지 이동
|
|
36
40
|
const page = yield this.browserController.focus(0);
|
|
37
41
|
this.logger.debug('[signInWithCookie]', 'setCookies');
|
|
@@ -53,46 +57,45 @@ export class LottoService {
|
|
|
53
57
|
return page.getCookies();
|
|
54
58
|
});
|
|
55
59
|
this.signIn = (id, password) => __awaiter(this, void 0, void 0, function* () {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
yield page.fill(SELECTORS.PWD_INPUT, password);
|
|
91
|
-
yield page.click(SELECTORS.LOGIN_BUTTON);
|
|
92
|
-
}));
|
|
93
|
-
return p.promise;
|
|
60
|
+
if (this.browserController.configs.controller === 'api') {
|
|
61
|
+
throw LottoError.NotSupported('API mode does not support signIn.');
|
|
62
|
+
}
|
|
63
|
+
// 페이지 이동
|
|
64
|
+
const page = yield this.browserController.focus(0);
|
|
65
|
+
this.logger.debug('[signIn]', 'goto', 'login page');
|
|
66
|
+
yield page.goto(URLS.LOGIN);
|
|
67
|
+
this.logger.debug('[signIn]', 'page url', yield page.url());
|
|
68
|
+
// 로그인 시도
|
|
69
|
+
this.logger.debug('[signIn]', 'try login');
|
|
70
|
+
yield page.fill(SELECTORS.ID_INPUT, id);
|
|
71
|
+
yield page.fill(SELECTORS.PWD_INPUT, password);
|
|
72
|
+
yield page.click(SELECTORS.LOGIN_BUTTON);
|
|
73
|
+
// 결과 대기
|
|
74
|
+
yield page.wait(CONST.BROWSER_LOGIN_WAIT);
|
|
75
|
+
// 결과 확인
|
|
76
|
+
const currentUrl = yield page.url();
|
|
77
|
+
// 성공: MAIN URL로 이동됨
|
|
78
|
+
if (currentUrl.includes(URLS.MAIN)) {
|
|
79
|
+
this.logger.info('[signIn]', 'success');
|
|
80
|
+
this.context.authenticated = true;
|
|
81
|
+
this.logger.debug('[signIn]', 'clear popups');
|
|
82
|
+
yield page.wait(CONST.BROWSER_PAGE_POPUP_WAIT);
|
|
83
|
+
yield this.browserController.cleanPages([0]);
|
|
84
|
+
return page.getCookies();
|
|
85
|
+
}
|
|
86
|
+
// 실패: 로그인 에러 팝업 확인
|
|
87
|
+
if (yield page.exists(SELECTORS.LOGIN_ERROR_POPUP, CONST.LOGIN_ERROR_MESSAGE)) {
|
|
88
|
+
this.logger.info('[signIn]', 'failed', 'credentials incorrect');
|
|
89
|
+
throw LottoError.CredentialsIncorrect();
|
|
90
|
+
}
|
|
91
|
+
// 기타 실패
|
|
92
|
+
this.logger.info('[signIn]', 'failed', 'unknown');
|
|
93
|
+
throw LottoError.CredentialsIncorrect();
|
|
94
94
|
});
|
|
95
95
|
this.purchase = (amount = 5) => __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
if (this.browserController.configs.controller === 'api') {
|
|
97
|
+
throw LottoError.NotSupported('API mode does not support purchase.');
|
|
98
|
+
}
|
|
96
99
|
if (!this.context.authenticated)
|
|
97
100
|
throw LottoError.NotAuthenticated();
|
|
98
101
|
this.logger.debug('[purchase]', 'validatePurchaseAvailability');
|
|
@@ -136,7 +139,7 @@ export class LottoService {
|
|
|
136
139
|
this.logger.debug('[getCheckWinningLink]', 'getCheckWinningLink');
|
|
137
140
|
return getCheckWinningLink(numbers, round);
|
|
138
141
|
};
|
|
139
|
-
this.logger = new Logger(configs
|
|
140
|
-
this.browserController = createBrowserController(
|
|
142
|
+
this.logger = new Logger(configs.logLevel, '[LottoService]');
|
|
143
|
+
this.browserController = createBrowserController(Object.assign({ defaultViewport: { width: 1080, height: 1024 } }, configs), this.logger);
|
|
141
144
|
}
|
|
142
145
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const getWinningNumbers: (
|
|
1
|
+
export declare const getWinningNumbers: (round: number) => Promise<number[]>;
|
|
@@ -5,6 +5,8 @@ export declare const CONST: {
|
|
|
5
5
|
BROWSER_DESTROY_SAFE_TIMEOUT: number;
|
|
6
6
|
BROWSER_PAGE_POPUP_WAIT: number;
|
|
7
7
|
BROWSER_PAGE_DIALOG_WAIT: number;
|
|
8
|
+
BROWSER_LOGIN_WAIT: number;
|
|
8
9
|
WEEK_TO_MILLISECOND: number;
|
|
9
10
|
THOUSAND_ROUND_DATE: string;
|
|
11
|
+
LOGIN_ERROR_MESSAGE: string;
|
|
10
12
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BrowserConfigs, BrowserControllerInterface } from '../../types';
|
|
2
|
+
import type { Browser, BrowserContext } from 'playwright';
|
|
3
|
+
import { type LoggerInterface } from '../../logger';
|
|
4
|
+
export declare class APIModeController implements BrowserControllerInterface {
|
|
5
|
+
configs: BrowserConfigs;
|
|
6
|
+
logger: LoggerInterface;
|
|
7
|
+
browser: Browser;
|
|
8
|
+
context: BrowserContext;
|
|
9
|
+
constructor(configs: BrowserConfigs, logger: LoggerInterface);
|
|
10
|
+
focus: () => Promise<never>;
|
|
11
|
+
close: () => Promise<void>;
|
|
12
|
+
cleanPages: () => Promise<void>;
|
|
13
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { BrowserConfigs, BrowserControllerInterface } from '../types';
|
|
1
|
+
import type { BrowserConfigs, BrowserController, BrowserControllerInterface } from '../types';
|
|
2
2
|
import type { LoggerInterface } from '../logger';
|
|
3
|
-
export declare function createBrowserController
|
|
3
|
+
export declare function createBrowserController<T extends BrowserController>(configs: BrowserConfigs<T>, logger: LoggerInterface): BrowserControllerInterface;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { BrowserConfigs, BrowserControllerInterface } from '../../types';
|
|
2
2
|
import type { Browser, BrowserContext } from 'playwright';
|
|
3
|
+
import type { BrowserType } from 'playwright-core';
|
|
3
4
|
import { PlaywrightPage } from './playwright.page';
|
|
4
5
|
import { type LoggerInterface } from '../../logger';
|
|
5
|
-
export declare class PlaywrightController implements BrowserControllerInterface {
|
|
6
|
-
configs: BrowserConfigs
|
|
6
|
+
export declare class PlaywrightController implements BrowserControllerInterface<BrowserType> {
|
|
7
|
+
configs: BrowserConfigs<BrowserType>;
|
|
7
8
|
logger: LoggerInterface;
|
|
8
9
|
browser: Browser;
|
|
9
10
|
context: BrowserContext;
|
|
10
|
-
constructor(configs: BrowserConfigs
|
|
11
|
+
constructor(configs: BrowserConfigs<BrowserType>, logger: LoggerInterface);
|
|
11
12
|
private getBrowserContext;
|
|
12
13
|
focus: (pageIndex?: number) => Promise<PlaywrightPage>;
|
|
13
14
|
close: () => Promise<void>;
|
|
@@ -13,6 +13,7 @@ export declare class PlaywrightPage implements BrowserPageInterface {
|
|
|
13
13
|
click(selector: string, domDirect?: boolean): Promise<void>;
|
|
14
14
|
select(selector: string, value: string): Promise<void>;
|
|
15
15
|
querySelectorAll<T>(selector: string, callback: (elems: FakeDOMElement[]) => T): Promise<T>;
|
|
16
|
+
exists(selector: string, containsText?: string): Promise<boolean>;
|
|
16
17
|
getCookies(): Promise<string>;
|
|
17
18
|
setCookies(cookies: StringifiedCookies): Promise<void>;
|
|
18
19
|
wait(param: 'idle' | 'load' | number): Promise<void>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { BrowserConfigs, BrowserControllerInterface } from '../../types';
|
|
2
|
-
import { Browser } from 'puppeteer';
|
|
3
2
|
import { PuppeteerPage } from './puppeteer.page';
|
|
4
3
|
import { type LoggerInterface } from '../../logger';
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import type { PuppeteerNode, Browser } from 'puppeteer';
|
|
5
|
+
export declare class PuppeteerController implements BrowserControllerInterface<PuppeteerNode> {
|
|
6
|
+
configs: BrowserConfigs<PuppeteerNode>;
|
|
7
7
|
logger: LoggerInterface;
|
|
8
8
|
browser: Browser;
|
|
9
|
-
constructor(configs: BrowserConfigs
|
|
9
|
+
constructor(configs: BrowserConfigs<PuppeteerNode>, logger: LoggerInterface);
|
|
10
10
|
private getBrowser;
|
|
11
11
|
focus: (pageIndex?: number) => Promise<PuppeteerPage>;
|
|
12
12
|
close: () => Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BrowserPageEvents, BrowserPageInterface, FakeDOMElement, StringifiedCookies } from '../../types';
|
|
2
|
-
import { Page } from 'puppeteer';
|
|
2
|
+
import type { Page } from 'puppeteer';
|
|
3
3
|
import type { LoggerInterface } from '../../logger';
|
|
4
4
|
export declare class PuppeteerPage implements BrowserPageInterface {
|
|
5
5
|
page: Page;
|
|
@@ -11,6 +11,7 @@ export declare class PuppeteerPage implements BrowserPageInterface {
|
|
|
11
11
|
click(selector: string, domDirect?: boolean): Promise<void>;
|
|
12
12
|
select(selector: string, value: string): Promise<void>;
|
|
13
13
|
querySelectorAll<T>(selector: string, callback: (elems: FakeDOMElement[]) => T): Promise<T>;
|
|
14
|
+
exists(selector: string, containsText?: string): Promise<boolean>;
|
|
14
15
|
getCookies(): Promise<string>;
|
|
15
16
|
setCookies(cookies: StringifiedCookies): Promise<void>;
|
|
16
17
|
wait(param: 'idle' | 'load' | number): Promise<void>;
|
|
@@ -6,6 +6,7 @@ declare const ErrorCode: {
|
|
|
6
6
|
CREDENTIALS_INCORRECT: 200001;
|
|
7
7
|
INVALID_COOKIE: 200002;
|
|
8
8
|
NETWORK_ERROR: 100001;
|
|
9
|
+
NOT_SUPPORTED: 110000;
|
|
9
10
|
UNKNOWN_ERROR: 199999;
|
|
10
11
|
};
|
|
11
12
|
type ErrorCodeNumber = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
@@ -18,6 +19,7 @@ export default class LottoError extends Error {
|
|
|
18
19
|
static InvalidLottoNumber(): LottoError;
|
|
19
20
|
static NotAuthenticated(): LottoError;
|
|
20
21
|
static PurchaseUnavailable(): LottoError;
|
|
22
|
+
static NotSupported(message?: string): LottoError;
|
|
21
23
|
static get code(): {
|
|
22
24
|
INVALID_ROUND: 300001;
|
|
23
25
|
INVALID_LOTTO_NUMBER: 300002;
|
|
@@ -26,10 +28,11 @@ export default class LottoError extends Error {
|
|
|
26
28
|
CREDENTIALS_INCORRECT: 200001;
|
|
27
29
|
INVALID_COOKIE: 200002;
|
|
28
30
|
NETWORK_ERROR: 100001;
|
|
31
|
+
NOT_SUPPORTED: 110000;
|
|
29
32
|
UNKNOWN_ERROR: 199999;
|
|
30
33
|
};
|
|
31
34
|
static getMessage(code: ErrorCodeNumber): string;
|
|
32
|
-
static getName(code: ErrorCodeNumber): "INVALID_ROUND" | "INVALID_LOTTO_NUMBER" | "NOT_AUTHENTICATED" | "PURCHASE_UNAVAILABLE" | "CREDENTIALS_INCORRECT" | "INVALID_COOKIE" | "NETWORK_ERROR" | "UNKNOWN_ERROR";
|
|
35
|
+
static getName(code: ErrorCodeNumber): "INVALID_ROUND" | "INVALID_LOTTO_NUMBER" | "NOT_AUTHENTICATED" | "PURCHASE_UNAVAILABLE" | "CREDENTIALS_INCORRECT" | "INVALID_COOKIE" | "NETWORK_ERROR" | "NOT_SUPPORTED" | "UNKNOWN_ERROR";
|
|
33
36
|
code: number;
|
|
34
37
|
constructor(code: ErrorCodeNumber, message?: string);
|
|
35
38
|
}
|
|
@@ -6,7 +6,7 @@ export declare class LottoService implements LottoServiceInterface {
|
|
|
6
6
|
};
|
|
7
7
|
browserController: BrowserControllerInterface;
|
|
8
8
|
logger: LoggerInterface;
|
|
9
|
-
constructor(configs
|
|
9
|
+
constructor(configs: BrowserConfigs);
|
|
10
10
|
destroy: () => Promise<void>;
|
|
11
11
|
signInWithCookie: (cookies: string) => Promise<string>;
|
|
12
12
|
signIn: (id: string, password: string) => Promise<string>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { LogLevel } from './logger';
|
|
2
|
+
import type { PuppeteerNode } from 'puppeteer';
|
|
3
|
+
import type { BrowserType } from 'playwright-core';
|
|
2
4
|
export interface LottoServiceInterface {
|
|
3
5
|
destroy(): Promise<void>;
|
|
4
6
|
signIn(id: string, password: string): Promise<string>;
|
|
@@ -10,8 +12,9 @@ export interface LottoServiceInterface {
|
|
|
10
12
|
purchase(amount: number): Promise<number[][]>;
|
|
11
13
|
getCheckWinningLink(numbers: number[][], round: number): string;
|
|
12
14
|
}
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
+
export type BrowserController = PuppeteerNode | BrowserType | 'api';
|
|
16
|
+
export interface BrowserConfigs<T extends BrowserController = BrowserController> {
|
|
17
|
+
controller: T;
|
|
15
18
|
logLevel?: LogLevel;
|
|
16
19
|
headless?: boolean;
|
|
17
20
|
defaultViewport?: {
|
|
@@ -21,8 +24,8 @@ export interface BrowserConfigs {
|
|
|
21
24
|
[key: string]: unknown;
|
|
22
25
|
args?: string[];
|
|
23
26
|
}
|
|
24
|
-
export interface BrowserControllerInterface {
|
|
25
|
-
configs: BrowserConfigs
|
|
27
|
+
export interface BrowserControllerInterface<T extends BrowserController = BrowserController> {
|
|
28
|
+
configs: BrowserConfigs<T>;
|
|
26
29
|
focus(pageIndex?: number): Promise<BrowserPageInterface>;
|
|
27
30
|
cleanPages(remainingPageIndex: number[]): Promise<void>;
|
|
28
31
|
close(): Promise<void>;
|
|
@@ -34,6 +37,7 @@ export interface BrowserPageInterface {
|
|
|
34
37
|
click(selector: string, domDirect?: boolean): Promise<void>;
|
|
35
38
|
select(selector: string, value: string): Promise<void>;
|
|
36
39
|
querySelectorAll<T>(selector: string, callback: (elems: FakeDOMElement[]) => T): Promise<T>;
|
|
40
|
+
exists(selector: string, containsText?: string): Promise<boolean>;
|
|
37
41
|
wait(time: number): Promise<void>;
|
|
38
42
|
wait(type: 'load'): Promise<void>;
|
|
39
43
|
wait(type: 'idle'): Promise<void>;
|
|
@@ -50,14 +54,20 @@ export type BrowserPageEvents = 'response';
|
|
|
50
54
|
export type Unsubscribe = () => void;
|
|
51
55
|
export type StringifiedCookies = string;
|
|
52
56
|
export type GetWinningNumbersResponse = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
resultCode: string | null;
|
|
58
|
+
resultMessage: string | null;
|
|
59
|
+
data: {
|
|
60
|
+
list: Array<{
|
|
61
|
+
ltEpsd: number;
|
|
62
|
+
tm1WnNo: number;
|
|
63
|
+
tm2WnNo: number;
|
|
64
|
+
tm3WnNo: number;
|
|
65
|
+
tm4WnNo: number;
|
|
66
|
+
tm5WnNo: number;
|
|
67
|
+
tm6WnNo: number;
|
|
68
|
+
bnsWnNo: number;
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
61
71
|
};
|
|
62
72
|
declare global {
|
|
63
73
|
namespace NodeJS {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rich-automation/lotto",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Lotto module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"build:esm": "tsc --project tsconfig.esm.json && echo '{\"type\": \"module\"}' > lib/esm/package.json",
|
|
36
36
|
"build:dts": "tsc --project tsconfig.json --emitDeclarationOnly --declaration --declarationDir lib/typescript",
|
|
37
37
|
"test": "jest --forceExit --detectOpenHandles",
|
|
38
|
-
"install:puppeteer": "node ./node_modules/puppeteer/install.
|
|
38
|
+
"install:puppeteer": "node ./node_modules/puppeteer/install.mjs",
|
|
39
39
|
"install:playwright": "npx playwright install chromium --with-deps",
|
|
40
40
|
"fix": "yarn fix:eslint && yarn fix:prettier",
|
|
41
41
|
"fix:eslint": "eslint --fix src --ext js,jsx,ts,tsx ",
|
|
@@ -46,11 +46,21 @@
|
|
|
46
46
|
"lint:prettier": "prettier --check \"src/**/*.{ts,tsx,js}\""
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"axios": "^1.
|
|
50
|
-
"dayjs": "^1.11.
|
|
49
|
+
"axios": "^1.8.4",
|
|
50
|
+
"dayjs": "^1.11.13"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
51
53
|
"playwright": "^1.35.0",
|
|
52
54
|
"puppeteer": "^20.5.0"
|
|
53
55
|
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"playwright": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"puppeteer": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
54
64
|
"devDependencies": {
|
|
55
65
|
"@babel/core": "^7.21.4",
|
|
56
66
|
"@babel/preset-env": "^7.21.4",
|
|
@@ -64,7 +74,9 @@
|
|
|
64
74
|
"eslint-config-prettier": "^8.8.0",
|
|
65
75
|
"eslint-plugin-prettier": "^4.2.1",
|
|
66
76
|
"jest": "^29.5.0",
|
|
77
|
+
"playwright": "^1.51.1",
|
|
67
78
|
"prettier": "^2.8.7",
|
|
79
|
+
"puppeteer": "^24.6.1",
|
|
68
80
|
"release-it": "^15.10.3",
|
|
69
81
|
"ts-node": "^10.9.1",
|
|
70
82
|
"typescript": "^5.0.4"
|