k-dhlottery-api 0.2.3
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/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +113 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +102 -0
- package/dist/domain/deposit.d.ts +4 -0
- package/dist/domain/deposit.js +22 -0
- package/dist/domain/lotto645-ticket.d.ts +13 -0
- package/dist/domain/lotto645-ticket.js +68 -0
- package/dist/domain/user.d.ts +5 -0
- package/dist/domain/user.js +10 -0
- package/dist/endpoint/lottery-api-endpoint.d.ts +55 -0
- package/dist/endpoint/lottery-api-endpoint.js +26 -0
- package/dist/endpoint/lottery-stdout-printer.d.ts +21 -0
- package/dist/endpoint/lottery-stdout-printer.js +103 -0
- package/dist/endpoint/version-stdout-printer.d.ts +1 -0
- package/dist/endpoint/version-stdout-printer.js +15 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +14 -0
- package/dist/port/credentials-provider.d.ts +13 -0
- package/dist/port/credentials-provider.js +135 -0
- package/dist/port/lottery-client.d.ts +19 -0
- package/dist/port/lottery-client.js +454 -0
- package/dist/port/lottery-endpoint.d.ts +20 -0
- package/dist/port/lottery-endpoint.js +2 -0
- package/dist/purchase/lotto645-buy-confirmer.d.ts +5 -0
- package/dist/purchase/lotto645-buy-confirmer.js +41 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +32 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fhwang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# 비공식 동행복권 API (Node.js)
|
|
2
|
+
|
|
3
|
+
[동행복권](https://dhlottery.co.kr/) 사이트를 터미널에서 이용할 수 있게 랩핑한 API입니다.
|
|
4
|
+
원본 Python 프로젝트 [roeniss/dhlottery-api](https://github.com/roeniss/dhlottery-api)를 Node.js로 마이그레이션한 버전입니다.
|
|
5
|
+
|
|
6
|
+
## 요구사항
|
|
7
|
+
|
|
8
|
+
- Node.js 18 이상
|
|
9
|
+
|
|
10
|
+
## 설치 및 사용법
|
|
11
|
+
|
|
12
|
+
### npm 패키지로 설치 (배포 후)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g k-dhlottery-api
|
|
16
|
+
# 또는
|
|
17
|
+
npx k-dhlottery-api --help
|
|
18
|
+
|
|
19
|
+
dhapi buy-lotto645 -y
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
설치 후 실행 명령어는 `dhapi`입니다 (패키지 이름은 `k-dhlottery-api`).
|
|
23
|
+
|
|
24
|
+
### 소스에서 설치
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone <this-repo>
|
|
28
|
+
cd k-dhlottery-api
|
|
29
|
+
npm install
|
|
30
|
+
npm run build
|
|
31
|
+
|
|
32
|
+
# 기본 도움말
|
|
33
|
+
dhapi --help
|
|
34
|
+
# 또는
|
|
35
|
+
node dist/cli.js --help
|
|
36
|
+
|
|
37
|
+
# 로또6/45 구매 도움말
|
|
38
|
+
dhapi buy-lotto645 --help
|
|
39
|
+
|
|
40
|
+
# 자동모드 5장 구매 & 확인절차 스킵
|
|
41
|
+
dhapi buy-lotto645 -y
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
전역 설치 후 사용:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm link
|
|
48
|
+
dhapi buy-lotto645 -y
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 구현된 기능들
|
|
52
|
+
|
|
53
|
+
- [로또6/45 구매](https://dhlottery.co.kr/gameInfo.do?method=gameMethod&wiselog=H_B_1_1) (`buy-lotto645`)
|
|
54
|
+
- 자동, 수동, 반자동 모드로 구매 가능합니다.
|
|
55
|
+
- 한 번에 최대 5장까지 구매 가능합니다.
|
|
56
|
+
- 매주 최대 5장까지 구매 가능합니다 (동행복권 측의 온라인 구매 관련 정책입니다).
|
|
57
|
+
- [예치금 현황 조회](https://dhlottery.co.kr/userSsl.do?method=myPage) (`show-balance`)
|
|
58
|
+
- 현재 보유한 예치금 정보를 조회합니다.
|
|
59
|
+
- [고정 가상계좌 입금을 위한 세팅](https://dhlottery.co.kr/userSsl.do?method=myPage) (`assign-virtual-account`)
|
|
60
|
+
- 개인에게 할당된 가상계좌에 입금하는 형태로 예치금을 충전할 수 있습니다. 이 때 얼마를 입금할건지 사이트에서 미리 선택해두어야 하는데, 이 작업을 대신 수행합니다.
|
|
61
|
+
- 입금은 직접 진행해야 합니다.
|
|
62
|
+
- 간편 충전 기능은 구현되지 않았습니다.
|
|
63
|
+
|
|
64
|
+
### 유틸성 기능들
|
|
65
|
+
|
|
66
|
+
- 복수 프로필 지정
|
|
67
|
+
- 두 개 이상의 프로필을 사용할 수 있습니다. 고급 설정 섹션을 참고해주세요.
|
|
68
|
+
- 프로필 목록 조회 (`show-profiles`)
|
|
69
|
+
- 설정된 프로필 이름들을 확인할 수 있습니다.
|
|
70
|
+
|
|
71
|
+
## 고급 설정
|
|
72
|
+
|
|
73
|
+
### 프로필 (계정) 설정
|
|
74
|
+
|
|
75
|
+
> [!NOTE] 최초 프로그램을 실행할 때 프로필 정보를 세팅하는 과정이 진행됩니다. 이 섹션에선 직접 프로필 정보 파일을 수정하는 법을 안내합니다.
|
|
76
|
+
|
|
77
|
+
`~/.dhapi/credentials` (Windows: `%USERPROFILE%\.dhapi\credentials`) 파일을 사용해 프로필 정보를 수정하거나 여러 계정을 설정할 수 있습니다. toml 포맷을 사용하고 있으며, 아래와 같은 형식으로 작성할 수 있습니다.
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
[default]
|
|
81
|
+
username = "dhlotter_id"
|
|
82
|
+
password = "****"
|
|
83
|
+
|
|
84
|
+
[another_profile]
|
|
85
|
+
username = "dhlotter_second_id"
|
|
86
|
+
password = "****"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
이후 `-p` 플래그로 프로필을 골라 사용합니다.
|
|
90
|
+
|
|
91
|
+
## 명령어 예시
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 자동 5장 (기본)
|
|
95
|
+
dhapi buy-lotto645
|
|
96
|
+
|
|
97
|
+
# 자동 1장
|
|
98
|
+
dhapi buy-lotto645 ""
|
|
99
|
+
|
|
100
|
+
# 수동 1장
|
|
101
|
+
dhapi buy-lotto645 "1,2,3,4,5,6"
|
|
102
|
+
|
|
103
|
+
# 반자동 1장 (1,2,3 고정)
|
|
104
|
+
dhapi buy-lotto645 "1,2,3"
|
|
105
|
+
|
|
106
|
+
# 수동 1장 + 반자동 1장
|
|
107
|
+
dhapi buy-lotto645 "1,2,3,4,5,6" "7,8,9"
|
|
108
|
+
|
|
109
|
+
# 확인 없이 자동 5장
|
|
110
|
+
dhapi buy-lotto645 -y
|
|
111
|
+
|
|
112
|
+
# 예치금 조회
|
|
113
|
+
dhapi show-balance
|
|
114
|
+
|
|
115
|
+
# 구매 내역 (최근 14일)
|
|
116
|
+
dhapi show-buy-list -f json
|
|
117
|
+
|
|
118
|
+
# 기간 지정
|
|
119
|
+
dhapi show-buy-list -s 20250101 -e 20250131
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 백엔드 API 서버 (REST API)
|
|
123
|
+
|
|
124
|
+
CLI 외에 HTTP API 서버로 같은 기능을 사용할 수 있습니다.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm run server
|
|
128
|
+
# 포트 지정 (Windows: set PORT=4000 && npm run server)
|
|
129
|
+
PORT=4000 npm run server
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
| 메서드 | 경로 | 설명 |
|
|
133
|
+
|--------|------|------|
|
|
134
|
+
| GET | `/api/profiles` | 프로필 목록 |
|
|
135
|
+
| GET | `/api/balance` | 예치금 현황 (query: `profile`) |
|
|
136
|
+
| POST | `/api/buy-lotto645` | 로또 구매 (body: `profile`, `tickets`, `skipConfirm`) |
|
|
137
|
+
| GET | `/api/buy-list` | 구매 내역 (query: `profile`, `format`, `startDate`, `endDate`) |
|
|
138
|
+
| POST | `/api/assign-virtual-account` | 가상계좌 세팅 (body: `profile`, `amount`) |
|
|
139
|
+
|
|
140
|
+
성공 시 `{ "success": true, "data": ... }`, 실패 시 `{ "success": false, "error": "메시지" }` 형식입니다.
|
|
141
|
+
|
|
142
|
+
## npm 패키지 배포
|
|
143
|
+
|
|
144
|
+
이 프로젝트를 npm에 배포하려면:
|
|
145
|
+
|
|
146
|
+
1. [npmjs.com](https://www.npmjs.com/) 회원가입 후 `npm login`
|
|
147
|
+
2. 패키지 이름: 이 프로젝트는 `k-dhlottery-api`로 npm에 배포됩니다. 스코프 패키지(예: `@내계정/dhapi`)로 쓰려면 `name`을 변경하면 됩니다.
|
|
148
|
+
3. 버전 업데이트 후 배포:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm run build
|
|
152
|
+
npm test
|
|
153
|
+
npm publish
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- `prepublishOnly` 스크립트로 publish 전에 자동으로 `npm run build`가 실행됩니다.
|
|
157
|
+
- 스코프 패키지(`@xxx/dhapi`)로 배포할 때는 `npm publish --access public`이 필요할 수 있습니다. (`publishConfig.access`가 이미 `public`으로 설정되어 있음)
|
|
158
|
+
|
|
159
|
+
배포 전에 포함되는 파일 확인: `npm pack --dry-run`
|
|
160
|
+
|
|
161
|
+
### GitHub Actions로 태그 푸시 시 자동 배포
|
|
162
|
+
|
|
163
|
+
`v*` 형태의 태그를 푸시하면 자동으로 npm에 배포됩니다.
|
|
164
|
+
|
|
165
|
+
1. **npm 인증 토큰 발급**
|
|
166
|
+
[npm → Access Tokens](https://www.npmjs.com/account/tokens)에서 "Generate New Token" → "Automation" 또는 "Classic" 선택 후 토큰 복사.
|
|
167
|
+
|
|
168
|
+
2. **GitHub 저장소에 시크릿 추가**
|
|
169
|
+
저장소 **Settings → Secrets and variables → Actions**에서 New repository secret 추가:
|
|
170
|
+
- Name: `NPM_TOKEN`
|
|
171
|
+
- Value: 위에서 복사한 npm 토큰
|
|
172
|
+
|
|
173
|
+
3. **버전 올리고 태그 푸시**
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# package.json의 version 수정 후 (예: 1.0.1)
|
|
177
|
+
git add package.json
|
|
178
|
+
git commit -m "chore: release 1.0.1"
|
|
179
|
+
git tag v1.0.1
|
|
180
|
+
git push origin main
|
|
181
|
+
git push origin v1.0.1
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
태그 `v1.0.1`을 푸시하면 `.github/workflows/publish-npm.yml`이 실행되어 빌드·테스트 후 npm에 publish됩니다.
|
|
185
|
+
|
|
186
|
+
## 기부하기
|
|
187
|
+
|
|
188
|
+
이 프로그램을 사용해서 1등에 당첨된다면, [원작자 roeniss](https://github.com/roeniss)에게 꼭 1000만원을 기부해주시길 바랍니다.
|
|
189
|
+
|
|
190
|
+
원본 Python 프로젝트: [Buy Me A Coffee](https://www.buymeacoffee.com/roeniss)
|
|
191
|
+
|
|
192
|
+
## 기여하기
|
|
193
|
+
|
|
194
|
+
기여는 대환영합니다! [CONTRIBUTING.md](docs/CONTRIBUTING.md) 파일을 참고해주세요.
|
|
195
|
+
|
|
196
|
+
## 라이선스
|
|
197
|
+
|
|
198
|
+
MIT. 원본 [dhlottery-api](https://github.com/roeniss/dhlottery-api)는 Apache-2.0 라이선스입니다.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createApiRouter = createApiRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const deposit_1 = require("../domain/deposit");
|
|
6
|
+
const lotto645_ticket_1 = require("../domain/lotto645-ticket");
|
|
7
|
+
const lottery_api_endpoint_1 = require("../endpoint/lottery-api-endpoint");
|
|
8
|
+
const credentials_provider_1 = require("../port/credentials-provider");
|
|
9
|
+
const lottery_client_1 = require("../port/lottery-client");
|
|
10
|
+
function getProfile(req) {
|
|
11
|
+
return req.query.profile || req.body?.profile || "default";
|
|
12
|
+
}
|
|
13
|
+
function jsonSuccess(res, data, status = 200) {
|
|
14
|
+
res.status(status).json({ success: true, data });
|
|
15
|
+
}
|
|
16
|
+
function jsonError(res, message, status = 500) {
|
|
17
|
+
res.status(status).json({ success: false, error: message });
|
|
18
|
+
}
|
|
19
|
+
function createApiRouter() {
|
|
20
|
+
const router = (0, express_1.Router)();
|
|
21
|
+
/** GET /api/profiles - 등록된 프로필 목록 */
|
|
22
|
+
router.get("/profiles", (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const profiles = credentials_provider_1.CredentialsProvider.listProfiles();
|
|
25
|
+
jsonSuccess(res, { profiles });
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
jsonError(res, e.message || "프로필 목록을 불러올 수 없습니다.", 404);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
/** GET /api/balance - 예치금 현황 (query: profile) */
|
|
32
|
+
router.get("/balance", async (req, res) => {
|
|
33
|
+
const profile = getProfile(req);
|
|
34
|
+
try {
|
|
35
|
+
const user = new credentials_provider_1.CredentialsProvider(profile).getUser();
|
|
36
|
+
const endpoint = new lottery_api_endpoint_1.LotteryApiEndpoint();
|
|
37
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
38
|
+
await client.showBalance();
|
|
39
|
+
if (!endpoint.result.showBalance) {
|
|
40
|
+
return jsonError(res, "예치금 정보를 가져오지 못했습니다.", 502);
|
|
41
|
+
}
|
|
42
|
+
jsonSuccess(res, endpoint.result.showBalance);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
jsonError(res, e.message || "예치금 조회에 실패했습니다.", 500);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
/** POST /api/buy-lotto645 - 로또 6/45 구매 (body: { profile?, tickets?: string[], skipConfirm?: boolean }) */
|
|
49
|
+
router.post("/buy-lotto645", async (req, res) => {
|
|
50
|
+
const profile = getProfile(req);
|
|
51
|
+
const ticketsInput = req.body?.tickets;
|
|
52
|
+
const skipConfirm = !!req.body?.skipConfirm;
|
|
53
|
+
try {
|
|
54
|
+
const user = new credentials_provider_1.CredentialsProvider(profile).getUser();
|
|
55
|
+
const tickets = ticketsInput && ticketsInput.length > 0
|
|
56
|
+
? lotto645_ticket_1.Lotto645Ticket.createTickets(ticketsInput)
|
|
57
|
+
: lotto645_ticket_1.Lotto645Ticket.createAutoTickets(5);
|
|
58
|
+
if (!skipConfirm && tickets.length > 0) {
|
|
59
|
+
// API에서는 기본적으로 확인 스킵 (skipConfirm=true 권장)
|
|
60
|
+
}
|
|
61
|
+
const endpoint = new lottery_api_endpoint_1.LotteryApiEndpoint();
|
|
62
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
63
|
+
await client.buyLotto645(tickets);
|
|
64
|
+
if (!endpoint.result.buyLotto645) {
|
|
65
|
+
return jsonError(res, "구매 결과를 가져오지 못했습니다.", 502);
|
|
66
|
+
}
|
|
67
|
+
jsonSuccess(res, endpoint.result.buyLotto645);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
jsonError(res, e.message || "로또 구매에 실패했습니다.", 500);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
/** GET /api/buy-list - 구매 내역 (query: profile, format?, startDate?, endDate?) */
|
|
74
|
+
router.get("/buy-list", async (req, res) => {
|
|
75
|
+
const profile = getProfile(req);
|
|
76
|
+
const format = req.query.format || "json";
|
|
77
|
+
const startDate = req.query.startDate;
|
|
78
|
+
const endDate = req.query.endDate;
|
|
79
|
+
try {
|
|
80
|
+
const user = new credentials_provider_1.CredentialsProvider(profile).getUser();
|
|
81
|
+
const endpoint = new lottery_api_endpoint_1.LotteryApiEndpoint();
|
|
82
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
83
|
+
await client.showBuyList(format, startDate, endDate);
|
|
84
|
+
if (!endpoint.result.showBuyList) {
|
|
85
|
+
return jsonError(res, "구매 내역을 가져오지 못했습니다.", 502);
|
|
86
|
+
}
|
|
87
|
+
jsonSuccess(res, endpoint.result.showBuyList);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
jsonError(res, e.message || "구매 내역 조회에 실패했습니다.", 500);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
/** POST /api/assign-virtual-account - 가상계좌 세팅 (body: { profile?, amount: number }) */
|
|
94
|
+
router.post("/assign-virtual-account", async (req, res) => {
|
|
95
|
+
const profile = getProfile(req);
|
|
96
|
+
const amount = req.body?.amount != null ? Number(req.body.amount) : 50000;
|
|
97
|
+
try {
|
|
98
|
+
const user = new credentials_provider_1.CredentialsProvider(profile).getUser();
|
|
99
|
+
const deposit = new deposit_1.Deposit(amount);
|
|
100
|
+
const endpoint = new lottery_api_endpoint_1.LotteryApiEndpoint();
|
|
101
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
102
|
+
await client.assignVirtualAccount(deposit);
|
|
103
|
+
if (!endpoint.result.assignVirtualAccount) {
|
|
104
|
+
return jsonError(res, "가상계좌 정보를 가져오지 못했습니다.", 502);
|
|
105
|
+
}
|
|
106
|
+
jsonSuccess(res, endpoint.result.assignVirtualAccount);
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
jsonError(res, e.message || "가상계좌 할당에 실패했습니다.", 500);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return router;
|
|
113
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
const deposit_1 = require("./domain/deposit");
|
|
9
|
+
const lotto645_ticket_1 = require("./domain/lotto645-ticket");
|
|
10
|
+
const lottery_stdout_printer_1 = require("./endpoint/lottery-stdout-printer");
|
|
11
|
+
const credentials_provider_1 = require("./port/credentials-provider");
|
|
12
|
+
const lottery_client_1 = require("./port/lottery-client");
|
|
13
|
+
const lotto645_buy_confirmer_1 = require("./purchase/lotto645-buy-confirmer");
|
|
14
|
+
const pkg = require("../package.json");
|
|
15
|
+
const program = new commander_1.Command();
|
|
16
|
+
program
|
|
17
|
+
.name("dhapi")
|
|
18
|
+
.description("동행복권 비공식 API\n\n각 명령어에 대한 자세한 도움말은 'dhapi [명령어] -h'를 입력하세요.")
|
|
19
|
+
.version(pkg.version, "-V, --version", "dhapi 버전을 출력합니다.");
|
|
20
|
+
program
|
|
21
|
+
.command("assign-virtual-account")
|
|
22
|
+
.description("예치금 충전용 가상계좌를 세팅합니다. dhapi에서는 본인 전용 계좌를 발급받는 것까지만 가능합니다. 출력되는 계좌로 직접 입금해주세요.")
|
|
23
|
+
.argument("[amount]", "입금할 금액 (5천원, 1만원, 2만원, 3만원, 5만원, 10만원, 20만원, 30만원, 50만원, 70만원, 100만원 중 하나)", "50000")
|
|
24
|
+
.option("-p, --profile <name>", "프로필을 지정합니다", "default")
|
|
25
|
+
.option("-d, --debug", "debug 로그를 활성화합니다")
|
|
26
|
+
.action(async (amountStr, opts) => {
|
|
27
|
+
const user = new credentials_provider_1.CredentialsProvider(opts.profile).getUser();
|
|
28
|
+
const amount = parseInt(amountStr, 10);
|
|
29
|
+
const deposit = new deposit_1.Deposit(amount);
|
|
30
|
+
const endpoint = new lottery_stdout_printer_1.LotteryStdoutPrinter();
|
|
31
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
32
|
+
await client.assignVirtualAccount(deposit);
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command("show-balance")
|
|
36
|
+
.description("예치금 현황을 조회합니다.")
|
|
37
|
+
.option("-p, --profile <name>", "프로필을 지정합니다", "default")
|
|
38
|
+
.option("-d, --debug", "debug 로그를 활성화합니다")
|
|
39
|
+
.action(async (opts) => {
|
|
40
|
+
const user = new credentials_provider_1.CredentialsProvider(opts.profile).getUser();
|
|
41
|
+
const endpoint = new lottery_stdout_printer_1.LotteryStdoutPrinter();
|
|
42
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
43
|
+
await client.showBalance();
|
|
44
|
+
});
|
|
45
|
+
program
|
|
46
|
+
.command("show-buy-list")
|
|
47
|
+
.description("구매 내역을 조회합니다. 기본적으로 최근 14일간의 내역을 조회하며, --start-date 및 --end-date 옵션을 통해 조회 기간을 지정할 수 있습니다.")
|
|
48
|
+
.option("-p, --profile <name>", "프로필을 지정합니다", "default")
|
|
49
|
+
.option("-f, --format <format>", "출력 형식 (table, json)", "table")
|
|
50
|
+
.option("-s, --start-date <YYYYMMDD>", "조회 시작 날짜")
|
|
51
|
+
.option("-e, --end-date <YYYYMMDD>", "조회 종료 날짜")
|
|
52
|
+
.option("-d, --debug", "debug 로그를 활성화합니다")
|
|
53
|
+
.action(async (opts) => {
|
|
54
|
+
const user = new credentials_provider_1.CredentialsProvider(opts.profile).getUser();
|
|
55
|
+
const endpoint = new lottery_stdout_printer_1.LotteryStdoutPrinter();
|
|
56
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
57
|
+
await client.showBuyList(opts.format, opts.startDate, opts.endDate);
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command("show-profiles")
|
|
61
|
+
.description("등록된 프로필 목록을 출력합니다.")
|
|
62
|
+
.action(() => {
|
|
63
|
+
try {
|
|
64
|
+
const profiles = credentials_provider_1.CredentialsProvider.listProfiles();
|
|
65
|
+
const table = new cli_table3_1.default({ head: ["profiles"], style: { head: [] } });
|
|
66
|
+
profiles.forEach((name) => table.push([name]));
|
|
67
|
+
console.log(table.toString());
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
console.error("❌", e.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
program
|
|
75
|
+
.command("buy-lotto645")
|
|
76
|
+
.description(`로또6/45 복권을 구매합니다. 매주 최대 다섯 장까지 구매할 수 있습니다.
|
|
77
|
+
|
|
78
|
+
예시:
|
|
79
|
+
dhapi buy-lotto645 자동모드 5장 (default)
|
|
80
|
+
dhapi buy-lotto645 '' 자동모드 1장
|
|
81
|
+
dhapi buy-lotto645 '1,2,3,4,5,6' 수동모드 1장
|
|
82
|
+
dhapi buy-lotto645 '1,2,3' 반자동모드 1장
|
|
83
|
+
dhapi buy-lotto645 '1,2,3,4,5,6' '7,8,9' 수동 1장 + 반자동 1장
|
|
84
|
+
dhapi buy-lotto645 -y 확인 스킵하고 자동 5장 구매`)
|
|
85
|
+
.argument("[tickets...]", "구매할 번호 (생략 시 자동 5장)")
|
|
86
|
+
.option("-y, --yes", "구매 전 확인 절차를 스킵합니다.")
|
|
87
|
+
.option("-p, --profile <name>", "프로필을 지정합니다", "default")
|
|
88
|
+
.option("-d, --debug", "debug 로그를 활성화합니다")
|
|
89
|
+
.action(async (ticketArgs, opts) => {
|
|
90
|
+
const user = new credentials_provider_1.CredentialsProvider(opts.profile).getUser();
|
|
91
|
+
const tickets = ticketArgs && ticketArgs.length > 0
|
|
92
|
+
? lotto645_ticket_1.Lotto645Ticket.createTickets(ticketArgs)
|
|
93
|
+
: lotto645_ticket_1.Lotto645Ticket.createAutoTickets(5);
|
|
94
|
+
const endpoint = new lottery_stdout_printer_1.LotteryStdoutPrinter();
|
|
95
|
+
const client = new lottery_client_1.LotteryClient(user, endpoint);
|
|
96
|
+
const confirmer = new lotto645_buy_confirmer_1.Lotto645BuyConfirmer();
|
|
97
|
+
const ok = confirmer.confirm(tickets, !!opts.yes);
|
|
98
|
+
if (!ok)
|
|
99
|
+
process.exit(0);
|
|
100
|
+
await client.buyLotto645(tickets);
|
|
101
|
+
});
|
|
102
|
+
program.parse();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Deposit = void 0;
|
|
4
|
+
const ALLOWED_AMOUNTS = [
|
|
5
|
+
5000, 10000, 20000, 30000, 50000, 100000, 200000, 300000, 500000, 700000,
|
|
6
|
+
1000000,
|
|
7
|
+
];
|
|
8
|
+
class Deposit {
|
|
9
|
+
constructor(amount) {
|
|
10
|
+
const n = typeof amount === "string" ? parseInt(amount, 10) : amount;
|
|
11
|
+
if (Number.isNaN(n)) {
|
|
12
|
+
throw new Error(`숫자를 입력하세요 (입력된 값: ${amount}).`);
|
|
13
|
+
}
|
|
14
|
+
if (!ALLOWED_AMOUNTS.includes(n)) {
|
|
15
|
+
throw new Error("입금 가능한 금액은 5천원, 1만원, 2만원, 3만원, 5만원, 10만원, 20만원, 30만원, 50만원, 70만원, 100만원입니다 (입력된 값: " +
|
|
16
|
+
amount +
|
|
17
|
+
").");
|
|
18
|
+
}
|
|
19
|
+
this.amount = n;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.Deposit = Deposit;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare enum Lotto645Mode {
|
|
2
|
+
AUTO = "auto",
|
|
3
|
+
SEMIAUTO = "semiauto",
|
|
4
|
+
MANUAL = "manual"
|
|
5
|
+
}
|
|
6
|
+
export declare class Lotto645Ticket {
|
|
7
|
+
readonly numbers: number[];
|
|
8
|
+
readonly mode: Lotto645Mode;
|
|
9
|
+
constructor(numbersStr?: string);
|
|
10
|
+
get modeKor(): string;
|
|
11
|
+
static createAutoTickets(count: number): Lotto645Ticket[];
|
|
12
|
+
static createTickets(numbersList: string[]): Lotto645Ticket[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Lotto645Ticket = exports.Lotto645Mode = void 0;
|
|
4
|
+
var Lotto645Mode;
|
|
5
|
+
(function (Lotto645Mode) {
|
|
6
|
+
Lotto645Mode["AUTO"] = "auto";
|
|
7
|
+
Lotto645Mode["SEMIAUTO"] = "semiauto";
|
|
8
|
+
Lotto645Mode["MANUAL"] = "manual";
|
|
9
|
+
})(Lotto645Mode || (exports.Lotto645Mode = Lotto645Mode = {}));
|
|
10
|
+
class Lotto645Ticket {
|
|
11
|
+
constructor(numbersStr) {
|
|
12
|
+
if (!numbersStr || numbersStr.trim() === "") {
|
|
13
|
+
this.numbers = [];
|
|
14
|
+
this.mode = Lotto645Mode.AUTO;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const parts = numbersStr.split(",").map((s) => s.trim());
|
|
18
|
+
const numbers = parts
|
|
19
|
+
.map((s) => {
|
|
20
|
+
const n = parseInt(s, 10);
|
|
21
|
+
if (Number.isNaN(n))
|
|
22
|
+
throw new Error(`숫자를 입력하세요 (입력된 값: ${numbersStr}).`);
|
|
23
|
+
return n;
|
|
24
|
+
})
|
|
25
|
+
.sort((a, b) => a - b);
|
|
26
|
+
const unique = [...new Set(numbers)];
|
|
27
|
+
if (numbers.length !== unique.length) {
|
|
28
|
+
throw new Error(`중복되지 않도록 숫자들을 입력하세요 (입력된 값: ${numbersStr}).`);
|
|
29
|
+
}
|
|
30
|
+
for (const n of numbers) {
|
|
31
|
+
if (n < 1 || n > 45) {
|
|
32
|
+
throw new Error(`각 번호는 1부터 45까지의 숫자만 사용할 수 있습니다 (입력된 값: ${n}).`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this.numbers = numbers;
|
|
36
|
+
if (numbers.length === 6) {
|
|
37
|
+
this.mode = Lotto645Mode.MANUAL;
|
|
38
|
+
}
|
|
39
|
+
else if (numbers.length >= 1 && numbers.length <= 5) {
|
|
40
|
+
this.mode = Lotto645Mode.SEMIAUTO;
|
|
41
|
+
}
|
|
42
|
+
else if (numbers.length === 0) {
|
|
43
|
+
this.mode = Lotto645Mode.AUTO;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
throw new Error(`숫자는 0개 이상 6개 이하의 숫자를 입력해야 합니다 (입력된 값: ${numbersStr}).`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
get modeKor() {
|
|
50
|
+
switch (this.mode) {
|
|
51
|
+
case Lotto645Mode.AUTO:
|
|
52
|
+
return "자동";
|
|
53
|
+
case Lotto645Mode.SEMIAUTO:
|
|
54
|
+
return "반자동";
|
|
55
|
+
case Lotto645Mode.MANUAL:
|
|
56
|
+
return "수동";
|
|
57
|
+
default:
|
|
58
|
+
throw new Error("지원하지 않는 게임 타입입니다.");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
static createAutoTickets(count) {
|
|
62
|
+
return Array.from({ length: count }, () => new Lotto645Ticket());
|
|
63
|
+
}
|
|
64
|
+
static createTickets(numbersList) {
|
|
65
|
+
return numbersList.map((n) => new Lotto645Ticket(n));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.Lotto645Ticket = Lotto645Ticket;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { LotteryEndpoint } from "../port/lottery-endpoint";
|
|
2
|
+
/**
|
|
3
|
+
* Endpoint adapter that captures results for API responses (no stdout).
|
|
4
|
+
*/
|
|
5
|
+
export interface ApiResult {
|
|
6
|
+
buyLotto645?: {
|
|
7
|
+
slots: Array<{
|
|
8
|
+
slot: string;
|
|
9
|
+
mode: string;
|
|
10
|
+
numbers: string[];
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
showBalance?: {
|
|
14
|
+
totalDeposit: number;
|
|
15
|
+
purchaseAvailable: number;
|
|
16
|
+
reservedAmount: number;
|
|
17
|
+
withdrawalPending: number;
|
|
18
|
+
purchaseUnavailable: number;
|
|
19
|
+
lastMonthTotalPurchase: number;
|
|
20
|
+
};
|
|
21
|
+
showBuyList?: {
|
|
22
|
+
startDate: string;
|
|
23
|
+
endDate: string;
|
|
24
|
+
format: string;
|
|
25
|
+
data: Array<{
|
|
26
|
+
headers: string[];
|
|
27
|
+
rows: string[][];
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
assignVirtualAccount?: {
|
|
31
|
+
accountNumber: string;
|
|
32
|
+
amount: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare class LotteryApiEndpoint implements LotteryEndpoint {
|
|
36
|
+
readonly result: ApiResult;
|
|
37
|
+
printResultOfBuyLotto645(slots: Array<{
|
|
38
|
+
slot: string;
|
|
39
|
+
mode: string;
|
|
40
|
+
numbers: string[];
|
|
41
|
+
}>): void;
|
|
42
|
+
printResultOfShowBalance(data: {
|
|
43
|
+
totalDeposit: number;
|
|
44
|
+
purchaseAvailable: number;
|
|
45
|
+
reservedAmount: number;
|
|
46
|
+
withdrawalPending: number;
|
|
47
|
+
purchaseUnavailable: number;
|
|
48
|
+
lastMonthTotalPurchase: number;
|
|
49
|
+
}): void;
|
|
50
|
+
printResultOfShowBuyList(data: Array<{
|
|
51
|
+
headers: string[];
|
|
52
|
+
rows: string[][];
|
|
53
|
+
}>, outputFormat: string, startDate: string, endDate: string): void;
|
|
54
|
+
printResultOfAssignVirtualAccount(accountNumber: string, amount: string): void;
|
|
55
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LotteryApiEndpoint = void 0;
|
|
4
|
+
class LotteryApiEndpoint {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.result = {};
|
|
7
|
+
}
|
|
8
|
+
printResultOfBuyLotto645(slots) {
|
|
9
|
+
this.result.buyLotto645 = { slots };
|
|
10
|
+
}
|
|
11
|
+
printResultOfShowBalance(data) {
|
|
12
|
+
this.result.showBalance = { ...data };
|
|
13
|
+
}
|
|
14
|
+
printResultOfShowBuyList(data, outputFormat, startDate, endDate) {
|
|
15
|
+
this.result.showBuyList = {
|
|
16
|
+
startDate,
|
|
17
|
+
endDate,
|
|
18
|
+
format: outputFormat,
|
|
19
|
+
data,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
printResultOfAssignVirtualAccount(accountNumber, amount) {
|
|
23
|
+
this.result.assignVirtualAccount = { accountNumber, amount };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.LotteryApiEndpoint = LotteryApiEndpoint;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LotteryEndpoint } from "../port/lottery-endpoint";
|
|
2
|
+
export declare class LotteryStdoutPrinter implements LotteryEndpoint {
|
|
3
|
+
printResultOfAssignVirtualAccount(accountNumber: string, amountStr: string): void;
|
|
4
|
+
printResultOfShowBalance(data: {
|
|
5
|
+
totalDeposit: number;
|
|
6
|
+
purchaseAvailable: number;
|
|
7
|
+
reservedAmount: number;
|
|
8
|
+
withdrawalPending: number;
|
|
9
|
+
purchaseUnavailable: number;
|
|
10
|
+
lastMonthTotalPurchase: number;
|
|
11
|
+
}): void;
|
|
12
|
+
printResultOfBuyLotto645(slots: Array<{
|
|
13
|
+
slot: string;
|
|
14
|
+
mode: string;
|
|
15
|
+
numbers: string[];
|
|
16
|
+
}>): void;
|
|
17
|
+
printResultOfShowBuyList(data: Array<{
|
|
18
|
+
headers: string[];
|
|
19
|
+
rows: string[][];
|
|
20
|
+
}>, outputFormat: string, startDate: string, endDate: string): void;
|
|
21
|
+
}
|