korbus-mcp 0.1.0

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/bus/gateway.d.ts +11 -0
  4. package/dist/bus/gateway.d.ts.map +1 -0
  5. package/dist/bus/gateway.js +36 -0
  6. package/dist/bus/gateway.js.map +1 -0
  7. package/dist/bus/gyeonggi.d.ts +9 -0
  8. package/dist/bus/gyeonggi.d.ts.map +1 -0
  9. package/dist/bus/gyeonggi.js +69 -0
  10. package/dist/bus/gyeonggi.js.map +1 -0
  11. package/dist/bus/seoul.d.ts +9 -0
  12. package/dist/bus/seoul.d.ts.map +1 -0
  13. package/dist/bus/seoul.js +72 -0
  14. package/dist/bus/seoul.js.map +1 -0
  15. package/dist/db.d.ts +55 -0
  16. package/dist/db.d.ts.map +1 -0
  17. package/dist/db.js +409 -0
  18. package/dist/db.js.map +1 -0
  19. package/dist/errors.d.ts +21 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +71 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +55 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/notifier/console.d.ts +3 -0
  28. package/dist/notifier/console.d.ts.map +1 -0
  29. package/dist/notifier/console.js +4 -0
  30. package/dist/notifier/console.js.map +1 -0
  31. package/dist/notifier/dispatcher.d.ts +11 -0
  32. package/dist/notifier/dispatcher.d.ts.map +1 -0
  33. package/dist/notifier/dispatcher.js +28 -0
  34. package/dist/notifier/dispatcher.js.map +1 -0
  35. package/dist/notifier/telegram.d.ts +3 -0
  36. package/dist/notifier/telegram.d.ts.map +1 -0
  37. package/dist/notifier/telegram.js +14 -0
  38. package/dist/notifier/telegram.js.map +1 -0
  39. package/dist/notifier/webhook.d.ts +3 -0
  40. package/dist/notifier/webhook.d.ts.map +1 -0
  41. package/dist/notifier/webhook.js +6 -0
  42. package/dist/notifier/webhook.js.map +1 -0
  43. package/dist/openclaw/dispatcher.d.ts +10 -0
  44. package/dist/openclaw/dispatcher.d.ts.map +1 -0
  45. package/dist/openclaw/dispatcher.js +50 -0
  46. package/dist/openclaw/dispatcher.js.map +1 -0
  47. package/dist/openclaw/index.d.ts +21 -0
  48. package/dist/openclaw/index.d.ts.map +1 -0
  49. package/dist/openclaw/index.js +39 -0
  50. package/dist/openclaw/index.js.map +1 -0
  51. package/dist/openclaw/service.d.ts +11 -0
  52. package/dist/openclaw/service.d.ts.map +1 -0
  53. package/dist/openclaw/service.js +28 -0
  54. package/dist/openclaw/service.js.map +1 -0
  55. package/dist/openclaw/tools.d.ts +27 -0
  56. package/dist/openclaw/tools.d.ts.map +1 -0
  57. package/dist/openclaw/tools.js +318 -0
  58. package/dist/openclaw/tools.js.map +1 -0
  59. package/dist/scheduler.d.ts +15 -0
  60. package/dist/scheduler.d.ts.map +1 -0
  61. package/dist/scheduler.js +106 -0
  62. package/dist/scheduler.js.map +1 -0
  63. package/dist/tools/alarm.d.ts +3 -0
  64. package/dist/tools/alarm.d.ts.map +1 -0
  65. package/dist/tools/alarm.js +118 -0
  66. package/dist/tools/alarm.js.map +1 -0
  67. package/dist/tools/arrival.d.ts +4 -0
  68. package/dist/tools/arrival.d.ts.map +1 -0
  69. package/dist/tools/arrival.js +37 -0
  70. package/dist/tools/arrival.js.map +1 -0
  71. package/dist/tools/helpers.d.ts +14 -0
  72. package/dist/tools/helpers.d.ts.map +1 -0
  73. package/dist/tools/helpers.js +14 -0
  74. package/dist/tools/helpers.js.map +1 -0
  75. package/dist/tools/poll.d.ts +4 -0
  76. package/dist/tools/poll.d.ts.map +1 -0
  77. package/dist/tools/poll.js +17 -0
  78. package/dist/tools/poll.js.map +1 -0
  79. package/dist/tools/search.d.ts +4 -0
  80. package/dist/tools/search.d.ts.map +1 -0
  81. package/dist/tools/search.js +26 -0
  82. package/dist/tools/search.js.map +1 -0
  83. package/dist/types.d.ts +117 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +16 -0
  86. package/dist/types.js.map +1 -0
  87. package/package.json +73 -0
  88. package/prisma/migrations/20260303085107_init/migration.sql +62 -0
  89. package/prisma/migrations/20260303120821_add_once_alarm_fields/migration.sql +22 -0
  90. package/prisma/migrations/migration_lock.toml +3 -0
  91. package/prisma/schema.prisma +72 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BearMett
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,251 @@
1
+ # korbus-mcp
2
+
3
+ 서울/경기 실시간 버스 도착 정보를 제공하는 [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 서버입니다.
4
+
5
+ AI 어시스턴트가 버스 정류장 검색, 노선 조회, 실시간 도착 정보 확인, 알림 설정 등을 수행할 수 있습니다.
6
+
7
+ ## Features
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+ | `search_stations` | 정류장 이름으로 검색 (서울 + 경기) |
12
+ | `search_routes` | 버스 노선 번호/이름으로 검색 (서울 + 경기) |
13
+ | `get_arrivals` | 정류장의 실시간 버스 도착 정보 조회 |
14
+ | `create_alarm` | 버스 도착 알림 생성 |
15
+ | `list_alarms` | 등록된 알림 목록 조회 |
16
+ | `update_alarm` | 기존 알림 수정 |
17
+ | `delete_alarm` | 알림 삭제 |
18
+ | `poll_now` | 활성 알림에 대해 즉시 도착 정보 확인 및 알림 발송 |
19
+
20
+ ## API 키 발급
21
+
22
+ 이 서버를 사용하려면 [공공데이터포털](https://www.data.go.kr/index.do)에서 API 키를 발급받고, 필요한 서비스를 신청해야 합니다.
23
+
24
+ ### 1단계: 회원가입
25
+
26
+ [data.go.kr](https://www.data.go.kr/index.do)에 접속하여 회원가입 후 로그인합니다.
27
+
28
+ ### 2단계: API 활용 신청
29
+
30
+ 아래 **6개 서비스**에 각각 활용 신청을 해야 합니다. 링크를 클릭한 뒤 **"활용신청"** 버튼을 누르세요.
31
+
32
+ **서울 버스** (3개):
33
+
34
+ | 서비스 | 용도 |
35
+ |--------|------|
36
+ | [서울특별시_정류소정보조회 서비스](https://www.data.go.kr/data/15000303/openapi.do) | 정류장 검색 |
37
+ | [서울특별시_노선정보조회 서비스](https://www.data.go.kr/data/15000193/openapi.do) | 버스 노선 검색 |
38
+ | [서울특별시_버스도착정보조회 서비스](https://www.data.go.kr/data/15000314/openapi.do) | 실시간 도착 정보 |
39
+
40
+ **경기 버스** (3개):
41
+
42
+ | 서비스 | 용도 |
43
+ |--------|------|
44
+ | [경기도_정류소 조회](https://www.data.go.kr/data/15080666/openapi.do) | 정류장 검색 |
45
+ | [경기도_버스노선 조회](https://www.data.go.kr/data/15080662/openapi.do) | 버스 노선 검색 |
46
+ | [경기도_버스도착정보 조회](https://www.data.go.kr/data/15080346/openapi.do) | 실시간 도착 정보 |
47
+
48
+ > **참고**: 서울 또는 경기 중 한 지역만 사용한다면 해당 지역의 3개만 신청해도 됩니다.
49
+
50
+ ### 3단계: 인증키 확인
51
+
52
+ 1. 활용 신청 승인 후 (즉시 ~ 최대 1~2시간), [마이페이지 > 오픈API > 인증키 발급현황](https://www.data.go.kr/iim/api/selectAPIAc498View.do)에서 **일반 인증키 (Decoding)** 을 복사합니다.
53
+ 2. 동일 계정으로 신청한 서비스들은 같은 인증키를 공유합니다.
54
+
55
+ ### 4단계: 환경변수 설정
56
+
57
+ 발급받은 키를 `KORBUS_DATA_API_KEY`에 설정합니다:
58
+
59
+ ```bash
60
+ KORBUS_DATA_API_KEY=your_decoding_key npx korbus-mcp
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ 두 가지 방식으로 사용할 수 있습니다:
66
+
67
+ ### A. OpenClaw 플러그인 (권장)
68
+
69
+ [OpenClaw](https://openclaw.ai) 게이트웨이에 플러그인으로 설치합니다. 텔레그램, 디스코드, 슬랙 등 OpenClaw이 지원하는 모든 채널로 알림을 받을 수 있습니다.
70
+
71
+ ```bash
72
+ openclaw plugins install korbus-mcp
73
+ ```
74
+
75
+ 플러그인 설정에 API 키를 추가합니다. `~/.openclaw/openclaw.json`을 직접 편집하거나:
76
+
77
+ ```json5
78
+ {
79
+ "plugins": {
80
+ "entries": {
81
+ "korbus-mcp": {
82
+ "enabled": true,
83
+ "config": {
84
+ "apiKey": "your_decoding_key"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ 또는 환경변수로 설정합니다:
93
+
94
+ ```bash
95
+ export KORBUS_DATA_API_KEY=your_decoding_key
96
+ ```
97
+
98
+ 설정 후 게이트웨이를 재시작합니다:
99
+
100
+ ```bash
101
+ openclaw gateway restart
102
+ ```
103
+
104
+ 설치 확인:
105
+
106
+ ```bash
107
+ openclaw plugins list # korbus-mcp 표시 확인
108
+ openclaw doctor # 전체 상태 점검
109
+ ```
110
+
111
+ ### B. Standalone MCP 서버
112
+
113
+ MCP 클라이언트(Claude Desktop, Cursor 등)에서 직접 실행합니다:
114
+
115
+ ```bash
116
+ KORBUS_DATA_API_KEY=your_key npx korbus-mcp
117
+ ```
118
+
119
+ ## 환경변수
120
+
121
+ | Variable | Required | Description |
122
+ |----------|----------|-------------|
123
+ | `KORBUS_DATA_API_KEY` | Yes | 공공데이터포털 버스 API 인증키 (서울/경기 공용) |
124
+ | `TELEGRAM_BOT_TOKEN` | Standalone만 | 텔레그램 알림 봇 토큰 (OpenClaw 모드에서는 불필요) |
125
+ | `DATABASE_URL` | No | SQLite DB 경로 (기본: `~/.korbus-mcp/korbus.db`) |
126
+
127
+ > **참고**: API 키는 인코딩된 값과 디코딩된 값 모두 사용 가능합니다.
128
+ >
129
+ > OpenClaw 플러그인 모드에서는 `plugins.entries.korbus-mcp.config.apiKey`로도 설정할 수 있습니다.
130
+
131
+ ## MCP 클라이언트 설정 (Standalone)
132
+
133
+ Standalone MCP 서버로 사용할 때의 클라이언트별 설정입니다.
134
+
135
+ ### Claude Desktop
136
+
137
+ `claude_desktop_config.json`에 추가:
138
+
139
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
140
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
141
+
142
+ ```json
143
+ {
144
+ "mcpServers": {
145
+ "korbus": {
146
+ "command": "npx",
147
+ "args": ["-y", "korbus-mcp"],
148
+ "env": {
149
+ "KORBUS_DATA_API_KEY": "your_api_key",
150
+ "TELEGRAM_BOT_TOKEN": "your_bot_token"
151
+ }
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### Claude Code (CLI)
158
+
159
+ ```bash
160
+ claude mcp add \
161
+ -e KORBUS_DATA_API_KEY=your_api_key \
162
+ korbus -- npx -y korbus-mcp
163
+ ```
164
+
165
+ ### Cursor
166
+
167
+ `.cursor/mcp.json`에 추가:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "korbus": {
173
+ "command": "npx",
174
+ "args": ["-y", "korbus-mcp"],
175
+ "env": {
176
+ "KORBUS_DATA_API_KEY": "your_api_key"
177
+ }
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### 기타 MCP 클라이언트
184
+
185
+ stdio 전송 방식을 지원하는 모든 MCP 클라이언트에서 사용할 수 있습니다:
186
+
187
+ ```json
188
+ {
189
+ "command": "npx",
190
+ "args": ["-y", "korbus-mcp"],
191
+ "env": {
192
+ "KORBUS_DATA_API_KEY": "your_api_key"
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## 알림 채널
198
+
199
+ ### OpenClaw 플러그인 모드
200
+
201
+ OpenClaw이 지원하는 모든 채널(Telegram, Discord, Slack, Signal 등)로 알림을 전송합니다. 알람 생성 시 `channel`과 `to` 파라미터로 수신 대상을 지정합니다. 대화 중 알람을 만들면 현재 대화 채널로 자동 설정됩니다.
202
+
203
+ ### Standalone MCP 모드
204
+
205
+ | 채널 | 설명 | 가이드 |
206
+ |------|------|--------|
207
+ | Telegram | 텔레그램 메시지로 알림 수신 | [설정 가이드](docs/telegram-setup.md) |
208
+ | Webhook | HTTP POST로 외부 서비스에 알림 전송 | [설정 가이드](docs/webhook-setup.md) |
209
+ | Console | 서버 로그에 출력 (디버깅용) | — |
210
+
211
+ ## 사용 예시
212
+
213
+ AI 어시스턴트에게 자연어로 요청하세요:
214
+
215
+ - "강남역 정류장 검색해줘"
216
+ - "341번 버스 노선 찾아줘"
217
+ - "강남역 정류장에 341번 버스 언제 오는지 알려줘"
218
+ - "매일 아침 8시에 강남역 341번 버스 도착 알림 만들어줘"
219
+ - "등록된 알림 목록 보여줘"
220
+
221
+ ## 데이터 영속성
222
+
223
+ 검색된 정류장, 노선, 알림 데이터는 `~/.korbus-mcp/korbus.db` SQLite 파일에 자동 저장됩니다. `npx`로 재실행해도 데이터가 유지됩니다.
224
+
225
+ `DATABASE_URL` 환경변수로 다른 경로를 지정할 수 있습니다:
226
+
227
+ ```bash
228
+ DATABASE_URL="file:/path/to/custom.db" npx korbus-mcp
229
+ ```
230
+
231
+ ## Development
232
+
233
+ ```bash
234
+ git clone https://github.com/BearMett/korbus-mcp.git
235
+ cd korbus-mcp
236
+ npm install
237
+ cp .env.example .env # API 키 설정
238
+ npm run dev
239
+ ```
240
+
241
+ ```bash
242
+ # 빌드
243
+ npm run build
244
+
245
+ # 마이그레이션
246
+ npm run db:migrate
247
+ ```
248
+
249
+ ## License
250
+
251
+ [MIT](LICENSE)
@@ -0,0 +1,11 @@
1
+ import type { Arrival, Route, Station, StationRef } from '../types.js';
2
+ export interface BusGateway {
3
+ searchStations(query: string): Promise<Station[]>;
4
+ searchRoutes(query: string): Promise<Route[]>;
5
+ getArrivals(stationRef: StationRef, routeId?: string): Promise<Arrival[]>;
6
+ }
7
+ export declare function createBusGateway(config: {
8
+ seoulApiKey: string;
9
+ gyeonggiApiKey: string;
10
+ }): BusGateway;
11
+ //# sourceMappingURL=gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/bus/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAU,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM/E,MAAM,WAAW,UAAU;IACzB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAClD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9C,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CAC3E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAgCpG"}
@@ -0,0 +1,36 @@
1
+ import { createSeoulAdapter } from './seoul.js';
2
+ import { createGyeonggiAdapter } from './gyeonggi.js';
3
+ export function createBusGateway(config) {
4
+ const adapters = [
5
+ createSeoulAdapter(config.seoulApiKey),
6
+ createGyeonggiAdapter(config.gyeonggiApiKey),
7
+ ];
8
+ function getAdapter(region) {
9
+ const adapter = adapters.find((a) => a.region === region);
10
+ if (!adapter)
11
+ throw new Error(`No adapter for region: ${region}`);
12
+ return adapter;
13
+ }
14
+ return {
15
+ async searchStations(query) {
16
+ const results = await Promise.allSettled(adapters.map((a) => a.searchStations(query)));
17
+ return results
18
+ .filter((r) => r.status === 'fulfilled')
19
+ .flatMap((r) => r.value);
20
+ },
21
+ async searchRoutes(query) {
22
+ const results = await Promise.allSettled(adapters.map((a) => a.searchRoutes(query)));
23
+ return results
24
+ .filter((r) => r.status === 'fulfilled')
25
+ .flatMap((r) => r.value);
26
+ },
27
+ async getArrivals(stationRef, routeId) {
28
+ const adapter = getAdapter(stationRef.region);
29
+ const arrivals = await adapter.getArrivalsByStation(stationRef);
30
+ if (routeId)
31
+ return arrivals.filter((a) => a.routeId === routeId);
32
+ return arrivals;
33
+ },
34
+ };
35
+ }
36
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/bus/gateway.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAqB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAwB,MAAM,eAAe,CAAC;AAU5E,MAAM,UAAU,gBAAgB,CAAC,MAAuD;IACtF,MAAM,QAAQ,GAAc;QAC1B,kBAAkB,CAAC,MAAM,CAAC,WAAW,CAAC;QACtC,qBAAqB,CAAC,MAAM,CAAC,cAAc,CAAC;KAC7C,CAAC;IAEF,SAAS,UAAU,CAAC,MAAc;QAChC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QAClE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,cAAc,CAAC,KAAK;YACxB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACvF,OAAO,OAAO;iBACX,MAAM,CAAC,CAAC,CAAC,EAA0C,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;iBAC/E,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,KAAK;YACtB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrF,OAAO,OAAO;iBACX,MAAM,CAAC,CAAC,CAAC,EAAwC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;iBAC7E,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,OAAQ;YACpC,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;YAChE,IAAI,OAAO;gBAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;YAClE,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Arrival, Route, Station, StationRef } from '../types.js';
2
+ export declare function createGyeonggiAdapter(apiKey: string): {
3
+ region: "GYEONGGI";
4
+ searchStations(name: string): Promise<Station[]>;
5
+ searchRoutes(name: string): Promise<Route[]>;
6
+ getArrivalsByStation(stationRef: StationRef): Promise<Arrival[]>;
7
+ };
8
+ export type GyeonggiAdapter = ReturnType<typeof createGyeonggiAdapter>;
9
+ //# sourceMappingURL=gyeonggi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gyeonggi.d.ts","sourceRoot":"","sources":["../../src/bus/gyeonggi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKvE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM;;yBAuBrB,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;uBAa7B,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;qCAWX,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;EAiBzE;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -0,0 +1,69 @@
1
+ import axios from 'axios';
2
+ import { apiKeyError, apiKeyExpiredError, rateLimitError } from '../errors.js';
3
+ const BASE_URL = 'https://apis.data.go.kr/6410000';
4
+ export function createGyeonggiAdapter(apiKey) {
5
+ const client = axios.create({ timeout: 10000 });
6
+ const key = decodeURIComponent(apiKey);
7
+ function extractBody(data, bodyKey) {
8
+ const header = data?.response?.msgHeader;
9
+ if (header) {
10
+ const code = String(header.resultCode ?? '');
11
+ const msg = String(header.resultMessage ?? '');
12
+ if (code === '20' || code === '30')
13
+ throw apiKeyError('경기', msg);
14
+ if (code === '21' || code === '31')
15
+ throw apiKeyExpiredError('경기');
16
+ if (code === '22')
17
+ throw rateLimitError('경기');
18
+ }
19
+ const body = data?.response?.msgBody;
20
+ if (!body)
21
+ return [];
22
+ const items = body[bodyKey];
23
+ if (!items)
24
+ return [];
25
+ return Array.isArray(items) ? items : [items];
26
+ }
27
+ return {
28
+ region: 'GYEONGGI',
29
+ async searchStations(name) {
30
+ const res = await client.get(`${BASE_URL}/busstationservice/v2/getBusStationListv2`, {
31
+ params: { keyword: name, serviceKey: key, format: 'json' },
32
+ });
33
+ return extractBody(res.data, 'busStationList').map((item) => ({
34
+ id: String(item.stationId),
35
+ name: String(item.stationName),
36
+ region: 'GYEONGGI',
37
+ posX: item.x != null ? Number(item.x) : undefined,
38
+ posY: item.y != null ? Number(item.y) : undefined,
39
+ }));
40
+ },
41
+ async searchRoutes(name) {
42
+ const res = await client.get(`${BASE_URL}/busrouteservice/v2/getBusRouteListv2`, {
43
+ params: { keyword: name, serviceKey: key, format: 'json' },
44
+ });
45
+ return extractBody(res.data, 'busRouteList').map((item) => ({
46
+ id: String(item.routeId),
47
+ name: String(item.routeName),
48
+ region: 'GYEONGGI',
49
+ }));
50
+ },
51
+ async getArrivalsByStation(stationRef) {
52
+ const res = await client.get(`${BASE_URL}/busarrivalservice/v2/getBusArrivalListv2`, {
53
+ params: { stationId: stationRef.id, serviceKey: key, format: 'json' },
54
+ });
55
+ return extractBody(res.data, 'busArrivalList').map((item) => {
56
+ const predictMinutes = Number(item.predictTime1);
57
+ return {
58
+ routeId: String(item.routeId),
59
+ routeName: String(item.routeName),
60
+ stationId: String(item.stationId),
61
+ vehicleId: item.plateNo1 ? String(item.plateNo1) : undefined,
62
+ arrivalSec: predictMinutes * 60,
63
+ arrivalMsg: `${predictMinutes}분 후 도착`,
64
+ };
65
+ });
66
+ },
67
+ };
68
+ }
69
+ //# sourceMappingURL=gyeonggi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gyeonggi.js","sourceRoot":"","sources":["../../src/bus/gyeonggi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Arrival, Route, Station, StationRef } from '../types.js';
2
+ export declare function createSeoulAdapter(apiKey: string): {
3
+ region: "SEOUL";
4
+ searchStations(name: string): Promise<Station[]>;
5
+ searchRoutes(name: string): Promise<Route[]>;
6
+ getArrivalsByStation(stationRef: StationRef): Promise<Arrival[]>;
7
+ };
8
+ export type SeoulAdapter = ReturnType<typeof createSeoulAdapter>;
9
+ //# sourceMappingURL=seoul.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seoul.d.ts","sourceRoot":"","sources":["../../src/bus/seoul.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKvE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;yBAuBlB,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;uBAc7B,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;qCAWX,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;EAmBzE;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,72 @@
1
+ import axios from 'axios';
2
+ import { XMLParser } from 'fast-xml-parser';
3
+ import { apiKeyError, apiKeyExpiredError, rateLimitError } from '../errors.js';
4
+ const BASE_URL = 'http://ws.bus.go.kr/api/rest';
5
+ export function createSeoulAdapter(apiKey) {
6
+ const client = axios.create({ timeout: 10000 });
7
+ const parser = new XMLParser({ parseTagValue: false });
8
+ const key = decodeURIComponent(apiKey);
9
+ function parseItemList(xml) {
10
+ const parsed = parser.parse(xml);
11
+ const header = parsed?.ServiceResult?.msgHeader;
12
+ if (header) {
13
+ const code = String(header.headerCd);
14
+ const msg = String(header.headerMsg ?? '');
15
+ if (code === '4' || code === '3')
16
+ throw apiKeyError('서울', msg);
17
+ if (code === '5')
18
+ throw apiKeyExpiredError('서울');
19
+ if (code === '8')
20
+ throw rateLimitError('서울');
21
+ }
22
+ const itemList = parsed?.ServiceResult?.msgBody?.itemList;
23
+ if (!itemList)
24
+ return [];
25
+ return Array.isArray(itemList) ? itemList : [itemList];
26
+ }
27
+ return {
28
+ region: 'SEOUL',
29
+ async searchStations(name) {
30
+ const res = await client.get(`${BASE_URL}/stationinfo/getStationByName`, {
31
+ params: { stSrch: name, serviceKey: key },
32
+ });
33
+ return parseItemList(res.data).map((item) => ({
34
+ id: String(item.stId),
35
+ arsId: item.arsId ? String(item.arsId) : undefined,
36
+ name: String(item.stNm),
37
+ region: 'SEOUL',
38
+ posX: item.tmX ? Number(item.tmX) : undefined,
39
+ posY: item.tmY ? Number(item.tmY) : undefined,
40
+ }));
41
+ },
42
+ async searchRoutes(name) {
43
+ const res = await client.get(`${BASE_URL}/busRouteInfo/getBusRouteList`, {
44
+ params: { strSrch: name, serviceKey: key },
45
+ });
46
+ return parseItemList(res.data).map((item) => ({
47
+ id: String(item.busRouteId),
48
+ name: String(item.busRouteNm),
49
+ region: 'SEOUL',
50
+ }));
51
+ },
52
+ async getArrivalsByStation(stationRef) {
53
+ const res = await client.get(`${BASE_URL}/arrive/getLowArrInfoByStId`, {
54
+ params: { stId: stationRef.id, serviceKey: key },
55
+ });
56
+ return parseItemList(res.data)
57
+ .filter((item) => {
58
+ const msg = String(item.arrmsg1 || '');
59
+ return msg !== '운행종료' && msg !== '출발대기';
60
+ })
61
+ .map((item) => ({
62
+ routeId: String(item.busRouteId),
63
+ routeName: String(item.rtNm || item.busRouteAbrv),
64
+ stationId: String(item.stId),
65
+ vehicleId: item.vehId1 ? String(item.vehId1) : undefined,
66
+ arrivalSec: Number(item.exps1 || 0),
67
+ arrivalMsg: String(item.arrmsg1),
68
+ }));
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=seoul.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seoul.js","sourceRoot":"","sources":["../../src/bus/seoul.ts"],"names":[],"mappings":"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"}
package/dist/db.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import type { Station, Route, StationRef, AlarmWithRelations, CreateAlarmInput, CreateOnceAlarmInput, UpdateAlarmInput } from './types.js';
3
+ /**
4
+ * Initialise PrismaClient singleton.
5
+ * Automatically runs `prisma migrate deploy` so the DB schema is up-to-date.
6
+ */
7
+ export declare function initDb(): Promise<PrismaClient>;
8
+ /** Return the initialised PrismaClient. Throws if `initDb()` has not been called. */
9
+ export declare function getDb(): PrismaClient;
10
+ /** Disconnect PrismaClient and reset the singleton. */
11
+ export declare function closeDb(): Promise<void>;
12
+ /** Upsert an array of stations, returning the mapped results. */
13
+ export declare function upsertStations(stations: Station[]): Promise<Station[]>;
14
+ /** Upsert an array of routes, returning the mapped results. */
15
+ export declare function upsertRoutes(routes: Route[]): Promise<Route[]>;
16
+ /** Find a station by id and return a lightweight StationRef. */
17
+ export declare function findStationById(stationId: string): Promise<StationRef | null>;
18
+ /** Create an alarm with nested schedules and channels. */
19
+ export declare function createAlarm(input: CreateAlarmInput): Promise<AlarmWithRelations>;
20
+ /** List all alarms with their station, route, schedules, and channels. */
21
+ export declare function listAlarms(): Promise<AlarmWithRelations[]>;
22
+ /** Find a single alarm by id, or return null. */
23
+ export declare function findAlarm(alarmId: string): Promise<AlarmWithRelations | null>;
24
+ /**
25
+ * Update an alarm. If `schedules` or `channels` are provided in the input
26
+ * the existing child rows are deleted and recreated.
27
+ */
28
+ export declare function updateAlarm(alarmId: string, input: UpdateAlarmInput): Promise<AlarmWithRelations>;
29
+ /** Delete an alarm by id. Cascading deletes handle children. */
30
+ export declare function deleteAlarm(alarmId: string): Promise<void>;
31
+ /** Create a one-time alarm (no schedules). */
32
+ export declare function createOnceAlarm(input: CreateOnceAlarmInput): Promise<AlarmWithRelations>;
33
+ /** Mark a one-time alarm as fired. */
34
+ export declare function markAlarmFired(alarmId: string): Promise<void>;
35
+ /**
36
+ * List all *active* alarms whose schedule matches the given `now` timestamp.
37
+ *
38
+ * "Active" means:
39
+ * - RECURRING: `enabled` is true and at least one schedule row matches now.
40
+ * - ONCE: `enabled` is true, `firedAt` is null, and `activeUntil` is null or >= now.
41
+ */
42
+ export declare function listActiveAlarms(now: Date): Promise<AlarmWithRelations[]>;
43
+ /**
44
+ * Check whether a notification log exists for the given alarm + vehicle
45
+ * combination since the provided timestamp.
46
+ */
47
+ export declare function hasRecentNotification(alarmId: string, vehicleId: string, since: Date): Promise<boolean>;
48
+ /** Create a notification log entry. */
49
+ export declare function createNotificationLog(input: {
50
+ alarmId: string;
51
+ vehicleId: string;
52
+ message: string;
53
+ channel: string;
54
+ }): Promise<void>;
55
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EACV,OAAO,EACP,KAAK,EACL,UAAU,EAIV,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAQpB;;;GAGG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CA+BpD;AAED,qFAAqF;AACrF,wBAAgB,KAAK,IAAI,YAAY,CAKpC;AAED,uDAAuD;AACvD,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAM7C;AAgID,iEAAiE;AACjE,wBAAsB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA2B5E;AAED,+DAA+D;AAC/D,wBAAsB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAqBpE;AAED,gEAAgE;AAChE,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAS5B;AAMD,0DAA0D;AAC1D,wBAAsB,WAAW,CAC/B,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CA2B7B;AAED,0EAA0E;AAC1E,wBAAsB,UAAU,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAIhE;AAED,iDAAiD;AACjD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQpC;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAiD7B;AAED,gEAAgE;AAChE,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE;AAED,8CAA8C;AAC9C,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CAkC7B;AAED,sCAAsC;AACtC,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnE;AAMD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAiD/B;AAMD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,IAAI,GACV,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,uCAAuC;AACvC,wBAAsB,qBAAqB,CAAC,KAAK,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB"}