daiso 1.0.4 → 1.0.6

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 (166) hide show
  1. package/README.md +249 -533
  2. package/dist/api/actionsProxy.js +82 -0
  3. package/dist/api/cgvHandlers.js +103 -16
  4. package/dist/api/configStatus.js +19 -0
  5. package/dist/api/daisoHandlers.js +127 -0
  6. package/dist/api/emart24Handlers.js +176 -0
  7. package/dist/api/gs25Handlers.js +267 -0
  8. package/dist/api/handlers.js +209 -104
  9. package/dist/api/healthChecks.js +504 -0
  10. package/dist/api/lottecinemaHandlers.js +155 -0
  11. package/dist/api/lottemartHandlers.js +144 -0
  12. package/dist/api/megaboxHandlers.js +81 -50
  13. package/dist/api/response.js +2 -0
  14. package/dist/api/routes/cuRoutes.js +17 -0
  15. package/dist/api/routes/daisoRoutes.js +6 -1
  16. package/dist/api/routes/emart24Routes.js +22 -0
  17. package/dist/api/routes/gs25Routes.js +22 -0
  18. package/dist/api/routes/healthRoutes.js +61 -0
  19. package/dist/api/routes/lottecinemaRoutes.js +22 -0
  20. package/dist/api/routes/lottemartRoutes.js +18 -0
  21. package/dist/api/routes/oliveyoungRoutes.js +6 -1
  22. package/dist/api/routes/sevenelevenRoutes.js +32 -0
  23. package/dist/api/sevenelevenHandlers.js +200 -0
  24. package/dist/bin.js +0 -0
  25. package/dist/cli/args.js +75 -0
  26. package/dist/cli/commandOptions.js +38 -0
  27. package/dist/cli/commands/convenience.js +293 -0
  28. package/dist/cli/commands/daiso.js +112 -0
  29. package/dist/cli/constants.js +5 -0
  30. package/dist/cli/deps.js +48 -0
  31. package/dist/cli/http.js +156 -0
  32. package/dist/cli/interactiveFetch.js +31 -0
  33. package/dist/cli/interactiveItemSearch.js +261 -0
  34. package/dist/cli/interactivePrompt.js +50 -0
  35. package/dist/cli/interactiveTypes.js +4 -0
  36. package/dist/cli/types.js +4 -0
  37. package/dist/cli.js +87 -196
  38. package/dist/cliHelp.js +212 -1
  39. package/dist/cliInteractive.js +243 -0
  40. package/dist/cliPicker.js +57 -0
  41. package/dist/cliRenderer.js +90 -3
  42. package/dist/core/errors.js +66 -0
  43. package/dist/core/registry.js +227 -3
  44. package/dist/index.js +149 -9
  45. package/dist/pages/openapi.js +2 -1
  46. package/dist/pages/openapiFullSpec.js +58 -0
  47. package/dist/pages/openapiResponse.js +28 -0
  48. package/dist/pages/openapiSpec.js +2 -41
  49. package/dist/pages/openapiSpecActions.js +109 -0
  50. package/dist/pages/openapiSpecComponents.js +30 -391
  51. package/dist/pages/openapiSpecComponentsCgv.js +24 -0
  52. package/dist/pages/openapiSpecComponentsCu.js +112 -0
  53. package/dist/pages/openapiSpecComponentsDaisoOliveyoung.js +342 -0
  54. package/dist/pages/openapiSpecComponentsEmart24.js +141 -0
  55. package/dist/pages/openapiSpecComponentsGs25.js +109 -0
  56. package/dist/pages/openapiSpecComponentsLotteMart.js +85 -0
  57. package/dist/pages/openapiSpecComponentsLottecinema.js +163 -0
  58. package/dist/pages/openapiSpecComponentsMegabox.js +144 -0
  59. package/dist/pages/openapiSpecComponentsSeveneleven.js +82 -0
  60. package/dist/pages/openapiSpecPathsCgv.js +66 -3
  61. package/dist/pages/openapiSpecPathsCu.js +145 -0
  62. package/dist/pages/openapiSpecPathsDaiso.js +302 -0
  63. package/dist/pages/openapiSpecPathsDaisoOliveyoung.js +4 -376
  64. package/dist/pages/openapiSpecPathsEmart24.js +223 -0
  65. package/dist/pages/openapiSpecPathsGs25.js +166 -0
  66. package/dist/pages/openapiSpecPathsLotteMart.js +149 -0
  67. package/dist/pages/openapiSpecPathsLottecinema.js +212 -0
  68. package/dist/pages/openapiSpecPathsMegabox.js +52 -3
  69. package/dist/pages/openapiSpecPathsOliveyoung.js +266 -0
  70. package/dist/pages/openapiSpecPathsSeveneleven.js +234 -0
  71. package/dist/pages/prompt.js +8 -402
  72. package/dist/pages/promptCoreText.js +278 -0
  73. package/dist/pages/promptReferenceText.js +137 -0
  74. package/dist/pages/promptRetailCinemaText.js +274 -0
  75. package/dist/services/cgv/client.js +6 -1
  76. package/dist/services/cgv/index.js +3 -3
  77. package/dist/services/cgv/location.js +235 -0
  78. package/dist/services/cgv/tools/findTheaters.js +30 -5
  79. package/dist/services/cgv/tools/getTimetable.js +42 -14
  80. package/dist/services/cgv/tools/searchMovies.js +40 -12
  81. package/dist/services/cgv/transport.js +0 -3
  82. package/dist/services/cu/api.js +11 -0
  83. package/dist/services/cu/client.js +297 -0
  84. package/dist/services/cu/index.js +21 -0
  85. package/dist/services/cu/tools/checkInventory.js +123 -0
  86. package/dist/services/cu/tools/findNearbyStores.js +49 -0
  87. package/dist/services/cu/types.js +4 -0
  88. package/dist/services/daiso/api.js +22 -2
  89. package/dist/services/daiso/client.js +67 -0
  90. package/dist/services/daiso/index.js +4 -0
  91. package/dist/services/daiso/product.js +17 -0
  92. package/dist/services/daiso/tools/checkInventory.js +80 -43
  93. package/dist/services/daiso/tools/findInventoryByName.js +222 -0
  94. package/dist/services/daiso/tools/findStores.js +17 -7
  95. package/dist/services/daiso/tools/getDisplayLocation.js +75 -0
  96. package/dist/services/emart24/api.js +11 -0
  97. package/dist/services/emart24/client.js +182 -0
  98. package/dist/services/emart24/index.js +22 -0
  99. package/dist/services/emart24/inventoryLookup.js +134 -0
  100. package/dist/services/emart24/tools/checkInventory.js +54 -0
  101. package/dist/services/emart24/tools/findNearbyStores.js +72 -0
  102. package/dist/services/emart24/tools/searchProducts.js +73 -0
  103. package/dist/services/emart24/types.js +4 -0
  104. package/dist/services/gs25/api.js +9 -0
  105. package/dist/services/gs25/client.js +208 -0
  106. package/dist/services/gs25/index.js +22 -0
  107. package/dist/services/gs25/storeUtils.js +168 -0
  108. package/dist/services/gs25/tools/checkInventory.js +163 -0
  109. package/dist/services/gs25/tools/findNearbyStores.js +103 -0
  110. package/dist/services/gs25/tools/searchProducts.js +57 -0
  111. package/dist/services/gs25/types.js +4 -0
  112. package/dist/services/lottecinema/api.js +12 -0
  113. package/dist/services/lottecinema/client.js +161 -0
  114. package/dist/services/lottecinema/index.js +30 -0
  115. package/dist/services/lottecinema/location.js +191 -0
  116. package/dist/services/lottecinema/tools/findNearbyTheaters.js +47 -0
  117. package/dist/services/lottecinema/tools/getRemainingSeats.js +85 -0
  118. package/dist/services/lottecinema/tools/listNowShowing.js +79 -0
  119. package/dist/services/lottecinema/types.js +4 -0
  120. package/dist/services/lottemart/api.js +31 -0
  121. package/dist/services/lottemart/client.js +254 -0
  122. package/dist/services/lottemart/clientTypes.js +4 -0
  123. package/dist/services/lottemart/config.js +4 -0
  124. package/dist/services/lottemart/debug.js +92 -0
  125. package/dist/services/lottemart/geocode.js +49 -0
  126. package/dist/services/lottemart/index.js +30 -0
  127. package/dist/services/lottemart/keyword.js +29 -0
  128. package/dist/services/lottemart/parser.js +185 -0
  129. package/dist/services/lottemart/session.js +331 -0
  130. package/dist/services/lottemart/storeSearch.js +52 -0
  131. package/dist/services/lottemart/tools/findNearbyStores.js +67 -0
  132. package/dist/services/lottemart/tools/searchProducts.js +69 -0
  133. package/dist/services/lottemart/types.js +4 -0
  134. package/dist/services/lottemart/zetta.js +105 -0
  135. package/dist/services/megabox/location.js +219 -0
  136. package/dist/services/megabox/tools/findNearbyTheaters.js +13 -44
  137. package/dist/services/megabox/tools/getRemainingSeats.js +31 -3
  138. package/dist/services/megabox/tools/listNowShowing.js +31 -3
  139. package/dist/services/oliveyoung/api.js +2 -0
  140. package/dist/services/oliveyoung/client.js +225 -13
  141. package/dist/services/oliveyoung/index.js +3 -1
  142. package/dist/services/oliveyoung/tools/checkInventory.js +21 -5
  143. package/dist/services/oliveyoung/tools/searchProducts.js +57 -0
  144. package/dist/services/seveneleven/api.js +15 -0
  145. package/dist/services/seveneleven/client.js +286 -0
  146. package/dist/services/seveneleven/index.js +30 -0
  147. package/dist/services/seveneleven/inventory.js +302 -0
  148. package/dist/services/seveneleven/productKeyword.js +141 -0
  149. package/dist/services/seveneleven/tools/checkInventory.js +82 -0
  150. package/dist/services/seveneleven/tools/getCatalogSnapshot.js +55 -0
  151. package/dist/services/seveneleven/tools/getSearchPopwords.js +41 -0
  152. package/dist/services/seveneleven/tools/searchProducts.js +67 -0
  153. package/dist/services/seveneleven/tools/searchStores.js +45 -0
  154. package/dist/services/seveneleven/types.js +4 -0
  155. package/dist/utils/cliInteractiveHelpers.js +63 -0
  156. package/dist/utils/daisoKeyword.js +30 -0
  157. package/dist/utils/http.js +98 -12
  158. package/dist/utils/zyte.js +47 -26
  159. package/openapi.json +1979 -736
  160. package/openapi.yaml +1428 -541
  161. package/package.json +49 -18
  162. package/dist/tools/checkInventory.js +0 -98
  163. package/dist/tools/findStores.js +0 -125
  164. package/dist/tools/getPriceInfo.js +0 -77
  165. package/dist/tools/searchProducts.js +0 -61
  166. package/dist/types/index.js +0 -4
@@ -0,0 +1,191 @@
1
+ /**
2
+ * 롯데시네마 위치 해석 및 근처 극장 조회 보조 모듈
3
+ */
4
+ import { fetchJson } from '../../utils/http.js';
5
+ import { fetchLotteCinemaTicketingPage } from './client.js';
6
+ const DEFAULT_TIMEOUT_MS = 15000;
7
+ const GEOCODE_CACHE_TTL_MS = 60 * 60 * 1000;
8
+ const LOCATION_STOPWORDS = new Set([
9
+ '롯데시네마',
10
+ '영화',
11
+ '극장',
12
+ '주변',
13
+ '근처',
14
+ '오늘',
15
+ '상영',
16
+ '영화목록',
17
+ '시간표',
18
+ '좌석',
19
+ '잔여',
20
+ '찾아',
21
+ '주세요',
22
+ ]);
23
+ const geocodeCache = new Map();
24
+ function calculateDistanceKm(lat1, lng1, lat2, lng2) {
25
+ const toRad = (deg) => (deg * Math.PI) / 180;
26
+ const dLat = toRad(lat2 - lat1);
27
+ const dLng = toRad(lng2 - lng1);
28
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
29
+ Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
30
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
31
+ return 6371 * c;
32
+ }
33
+ function isValidCoordinate(value) {
34
+ return typeof value === 'number' && Number.isFinite(value);
35
+ }
36
+ function normalizeText(text) {
37
+ return text.replace(/\s+/g, '').toLowerCase();
38
+ }
39
+ function extractLocationTokens(...values) {
40
+ const tokens = new Set();
41
+ for (const value of values) {
42
+ const matches = (value || '').match(/[0-9A-Za-z가-힣]+/g) || [];
43
+ for (const match of matches) {
44
+ const normalized = normalizeText(match);
45
+ if (normalized.length < 2 || LOCATION_STOPWORDS.has(normalized)) {
46
+ continue;
47
+ }
48
+ tokens.add(normalized);
49
+ if (normalized.endsWith('역') && normalized.length > 2) {
50
+ tokens.add(normalized.slice(0, -1));
51
+ }
52
+ }
53
+ }
54
+ return [...tokens];
55
+ }
56
+ function scoreTheater(theater, tokens) {
57
+ const normalizedName = normalizeText(theater.theaterName);
58
+ const normalizedAddress = normalizeText(theater.address || '');
59
+ return tokens.reduce((score, token) => {
60
+ if (normalizedName.includes(token)) {
61
+ return score + 2;
62
+ }
63
+ if (normalizedAddress.includes(token)) {
64
+ return score + 1;
65
+ }
66
+ return score;
67
+ }, 0);
68
+ }
69
+ async function geocodeAddress(query, options) {
70
+ const apiKey = (options.googleMapsApiKey || '').trim();
71
+ const trimmedQuery = query.trim();
72
+ if (apiKey.length === 0 || trimmedQuery.length === 0) {
73
+ return null;
74
+ }
75
+ const cacheKey = `keyword:${trimmedQuery}`;
76
+ const cached = geocodeCache.get(cacheKey);
77
+ if (cached && cached.expiresAt > Date.now()) {
78
+ return cached.value;
79
+ }
80
+ const endpoint = new URL('https://maps.googleapis.com/maps/api/geocode/json');
81
+ endpoint.searchParams.set('address', trimmedQuery);
82
+ endpoint.searchParams.set('key', apiKey);
83
+ const body = await fetchJson(endpoint.toString(), {
84
+ method: 'GET',
85
+ timeout: options.timeout || DEFAULT_TIMEOUT_MS,
86
+ headers: { Accept: 'application/json' },
87
+ });
88
+ const firstResult = body.status === 'OK' ? body.results?.[0] : null;
89
+ const latitude = firstResult?.geometry?.location?.lat;
90
+ const longitude = firstResult?.geometry?.location?.lng;
91
+ const value = typeof latitude === 'number' && typeof longitude === 'number'
92
+ ? {
93
+ latitude,
94
+ longitude,
95
+ formattedAddress: firstResult?.formatted_address || null,
96
+ }
97
+ : null;
98
+ geocodeCache.set(cacheKey, {
99
+ expiresAt: Date.now() + GEOCODE_CACHE_TTL_MS,
100
+ value,
101
+ });
102
+ return value;
103
+ }
104
+ export async function resolveLotteCinemaLocation(params, options = {}) {
105
+ const keyword = (params.keyword || '').trim();
106
+ const hasCoordinates = isValidCoordinate(params.latitude) && isValidCoordinate(params.longitude);
107
+ if (hasCoordinates) {
108
+ return {
109
+ keyword: keyword.length > 0 ? keyword : null,
110
+ latitude: params.latitude,
111
+ longitude: params.longitude,
112
+ geocodeUsed: false,
113
+ formattedAddress: null,
114
+ };
115
+ }
116
+ if (keyword.length === 0) {
117
+ return {
118
+ keyword: null,
119
+ latitude: null,
120
+ longitude: null,
121
+ geocodeUsed: false,
122
+ formattedAddress: null,
123
+ };
124
+ }
125
+ const geocoded = await geocodeAddress(keyword, options);
126
+ return {
127
+ keyword,
128
+ latitude: geocoded?.latitude ?? null,
129
+ longitude: geocoded?.longitude ?? null,
130
+ geocodeUsed: Boolean(geocoded),
131
+ formattedAddress: geocoded?.formattedAddress ?? null,
132
+ };
133
+ }
134
+ export async function fetchLotteCinemaNearbyTheaters(params, options = {}) {
135
+ const timeout = typeof params.timeout === 'number' ? params.timeout : DEFAULT_TIMEOUT_MS;
136
+ const limit = typeof params.limit === 'number' ? Math.max(1, params.limit) : 10;
137
+ const location = await resolveLotteCinemaLocation(params, options);
138
+ const { theaters } = await fetchLotteCinemaTicketingPage(timeout);
139
+ let nearby;
140
+ if (location.latitude !== null && location.longitude !== null) {
141
+ nearby = theaters
142
+ .filter((theater) => theater.latitude !== null && theater.longitude !== null)
143
+ .map((theater) => ({
144
+ ...theater,
145
+ distanceKm: Number(calculateDistanceKm(location.latitude, location.longitude, theater.latitude, theater.longitude).toFixed(2)),
146
+ }))
147
+ .sort((left, right) => left.distanceKm - right.distanceKm || left.theaterName.localeCompare(right.theaterName))
148
+ .slice(0, limit);
149
+ }
150
+ else {
151
+ const tokens = extractLocationTokens(location.keyword, location.formattedAddress);
152
+ nearby = theaters
153
+ .map((theater) => ({
154
+ theater,
155
+ score: scoreTheater(theater, tokens),
156
+ }))
157
+ .filter((item) => item.score > 0)
158
+ .sort((left, right) => right.score - left.score || left.theater.theaterName.localeCompare(right.theater.theaterName))
159
+ .slice(0, limit)
160
+ .map((item) => ({
161
+ ...item.theater,
162
+ distanceKm: null,
163
+ }));
164
+ }
165
+ return {
166
+ ...location,
167
+ playDate: params.playDate,
168
+ count: nearby.length,
169
+ theaters: nearby,
170
+ };
171
+ }
172
+ export async function resolveLotteCinemaNearestTheater(params, options = {}) {
173
+ const nearby = await fetchLotteCinemaNearbyTheaters({
174
+ ...params,
175
+ limit: 1,
176
+ timeout: params.timeout,
177
+ }, options);
178
+ return {
179
+ location: {
180
+ keyword: nearby.keyword,
181
+ latitude: nearby.latitude,
182
+ longitude: nearby.longitude,
183
+ geocodeUsed: nearby.geocodeUsed,
184
+ formattedAddress: nearby.formattedAddress,
185
+ },
186
+ theater: nearby.theaters[0] || null,
187
+ };
188
+ }
189
+ export function __testOnlyClearLotteCinemaLocationCaches() {
190
+ geocodeCache.clear();
191
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 롯데시네마 주변 지점 탐색 도구
3
+ */
4
+ import * as z from 'zod';
5
+ import { toYyyymmdd } from '../client.js';
6
+ import { fetchLotteCinemaNearbyTheaters } from '../location.js';
7
+ async function findNearbyTheaters(args, googleMapsApiKey) {
8
+ const { keyword, latitude, longitude, playDate = toYyyymmdd(), limit = 10, timeoutMs = 15000 } = args;
9
+ const hasCoordinates = typeof latitude === 'number' && typeof longitude === 'number';
10
+ const nearby = await fetchLotteCinemaNearbyTheaters({
11
+ keyword,
12
+ latitude: hasCoordinates ? latitude : keyword ? undefined : 37.5665,
13
+ longitude: hasCoordinates ? longitude : keyword ? undefined : 126.978,
14
+ playDate,
15
+ limit,
16
+ timeout: timeoutMs,
17
+ }, {
18
+ timeout: timeoutMs,
19
+ googleMapsApiKey: googleMapsApiKey || process.env.GOOGLE_MAPS_API_KEY,
20
+ });
21
+ return {
22
+ content: [
23
+ {
24
+ type: 'text',
25
+ text: JSON.stringify(nearby, null, 2),
26
+ },
27
+ ],
28
+ };
29
+ }
30
+ export function createFindNearbyTheatersTool(googleMapsApiKey) {
31
+ return {
32
+ name: 'lottecinema_find_nearby_theaters',
33
+ metadata: {
34
+ title: '롯데시네마 주변 지점 탐색',
35
+ description: '위치 키워드 또는 좌표 기준으로 롯데시네마 지점을 거리순으로 조회합니다.',
36
+ inputSchema: {
37
+ keyword: z.string().optional().describe('위치 키워드 (예: 안산 중앙역, 잠실역)'),
38
+ latitude: z.number().optional().default(37.5665).describe('위도 (기본값: 서울 시청 37.5665)'),
39
+ longitude: z.number().optional().default(126.978).describe('경도 (기본값: 서울 시청 126.978)'),
40
+ playDate: z.string().optional().describe('조회 날짜(YYYYMMDD, 기본값: 오늘)'),
41
+ limit: z.number().optional().default(10).describe('반환할 최대 지점 수 (기본값: 10)'),
42
+ timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
43
+ },
44
+ },
45
+ handler: ((args) => findNearbyTheaters(args, googleMapsApiKey)),
46
+ };
47
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * 롯데시네마 잔여 좌석 조회 도구
3
+ */
4
+ import * as z from 'zod';
5
+ import { fetchLotteCinemaNowShowing, toYyyymmdd } from '../client.js';
6
+ import { resolveLotteCinemaNearestTheater } from '../location.js';
7
+ async function getRemainingSeats(args, googleMapsApiKey) {
8
+ const { playDate = toYyyymmdd(), theaterId, movieId, keyword, latitude, longitude, limit = 50, timeoutMs = 15000, } = args;
9
+ let resolvedTheater = null;
10
+ let targetTheaterId = theaterId;
11
+ if (!targetTheaterId && (keyword || typeof latitude === 'number' || typeof longitude === 'number')) {
12
+ const resolved = await resolveLotteCinemaNearestTheater({
13
+ playDate,
14
+ keyword,
15
+ latitude,
16
+ longitude,
17
+ timeout: timeoutMs,
18
+ }, {
19
+ timeout: timeoutMs,
20
+ googleMapsApiKey: googleMapsApiKey || process.env.GOOGLE_MAPS_API_KEY,
21
+ });
22
+ resolvedTheater = resolved.theater;
23
+ targetTheaterId = resolved.theater?.theaterId;
24
+ }
25
+ const shouldReturnEmpty = !targetTheaterId && (keyword || typeof latitude === 'number' || typeof longitude === 'number');
26
+ const { showtimes } = shouldReturnEmpty
27
+ ? { showtimes: [] }
28
+ : await fetchLotteCinemaNowShowing({
29
+ playDate,
30
+ theaterId: targetTheaterId,
31
+ movieId,
32
+ timeout: timeoutMs,
33
+ });
34
+ const seats = showtimes
35
+ .filter((item) => (targetTheaterId ? item.theaterId === targetTheaterId : true))
36
+ .filter((item) => (movieId ? item.movieId === movieId : true))
37
+ .sort((a, b) => {
38
+ if (a.startTime === b.startTime) {
39
+ return a.theaterName.localeCompare(b.theaterName);
40
+ }
41
+ return a.startTime.localeCompare(b.startTime);
42
+ })
43
+ .slice(0, limit);
44
+ return {
45
+ content: [
46
+ {
47
+ type: 'text',
48
+ text: JSON.stringify({
49
+ playDate,
50
+ filters: {
51
+ theaterId: targetTheaterId || null,
52
+ movieId: movieId || null,
53
+ keyword: keyword || null,
54
+ latitude: latitude ?? null,
55
+ longitude: longitude ?? null,
56
+ limit,
57
+ },
58
+ resolvedTheater,
59
+ count: seats.length,
60
+ seats,
61
+ }, null, 2),
62
+ },
63
+ ],
64
+ };
65
+ }
66
+ export function createGetRemainingSeatsTool(googleMapsApiKey) {
67
+ return {
68
+ name: 'lottecinema_get_remaining_seats',
69
+ metadata: {
70
+ title: '롯데시네마 잔여 좌석 조회',
71
+ description: '영화/지점/날짜 조건으로 롯데시네마 상영 회차별 남은 좌석 수를 조회합니다. 지점 ID가 없으면 위치 키워드 기준 최근접 극장을 선택할 수 있습니다.',
72
+ inputSchema: {
73
+ playDate: z.string().optional().describe('조회 날짜(YYYYMMDD, 기본값: 오늘)'),
74
+ theaterId: z.string().optional().describe('롯데시네마 지점 ID (예: 1016)'),
75
+ movieId: z.string().optional().describe('롯데시네마 대표 영화 코드 (예: 23816)'),
76
+ keyword: z.string().optional().describe('위치 키워드 (예: 안산 중앙역, 잠실역)'),
77
+ latitude: z.number().optional().describe('위도'),
78
+ longitude: z.number().optional().describe('경도'),
79
+ limit: z.number().optional().default(50).describe('반환할 최대 회차 수 (기본값: 50)'),
80
+ timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
81
+ },
82
+ },
83
+ handler: ((args) => getRemainingSeats(args, googleMapsApiKey)),
84
+ };
85
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 롯데시네마 영화 목록 조회 도구
3
+ */
4
+ import * as z from 'zod';
5
+ import { fetchLotteCinemaNowShowing, toYyyymmdd } from '../client.js';
6
+ import { resolveLotteCinemaNearestTheater } from '../location.js';
7
+ async function listNowShowing(args, googleMapsApiKey) {
8
+ const { playDate = toYyyymmdd(), theaterId, movieId, keyword, latitude, longitude, timeoutMs = 15000 } = args;
9
+ let resolvedTheater = null;
10
+ let targetTheaterId = theaterId;
11
+ if (!targetTheaterId && (keyword || typeof latitude === 'number' || typeof longitude === 'number')) {
12
+ const resolved = await resolveLotteCinemaNearestTheater({
13
+ playDate,
14
+ keyword,
15
+ latitude,
16
+ longitude,
17
+ timeout: timeoutMs,
18
+ }, {
19
+ timeout: timeoutMs,
20
+ googleMapsApiKey: googleMapsApiKey || process.env.GOOGLE_MAPS_API_KEY,
21
+ });
22
+ resolvedTheater = resolved.theater;
23
+ targetTheaterId = resolved.theater?.theaterId;
24
+ }
25
+ const shouldReturnEmpty = !targetTheaterId && (keyword || typeof latitude === 'number' || typeof longitude === 'number');
26
+ const { theaters, movies, showtimes } = shouldReturnEmpty
27
+ ? { theaters: [], movies: [], showtimes: [] }
28
+ : await fetchLotteCinemaNowShowing({
29
+ playDate,
30
+ theaterId: targetTheaterId,
31
+ movieId,
32
+ timeout: timeoutMs,
33
+ });
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: JSON.stringify({
39
+ playDate,
40
+ filters: {
41
+ theaterId: targetTheaterId || null,
42
+ movieId: movieId || null,
43
+ keyword: keyword || null,
44
+ latitude: latitude ?? null,
45
+ longitude: longitude ?? null,
46
+ },
47
+ resolvedTheater,
48
+ counts: {
49
+ theaters: theaters.length,
50
+ movies: movies.length,
51
+ showtimes: showtimes.length,
52
+ },
53
+ theaters,
54
+ movies,
55
+ showtimes,
56
+ }, null, 2),
57
+ },
58
+ ],
59
+ };
60
+ }
61
+ export function createListNowShowingTool(googleMapsApiKey) {
62
+ return {
63
+ name: 'lottecinema_list_now_showing',
64
+ metadata: {
65
+ title: '롯데시네마 영화 목록 조회',
66
+ description: '날짜/지점/영화 조건으로 롯데시네마 영화 및 상영 회차 목록을 조회합니다. 지점 ID가 없으면 위치 키워드 기준 최근접 극장을 선택할 수 있습니다.',
67
+ inputSchema: {
68
+ playDate: z.string().optional().describe('조회 날짜(YYYYMMDD, 기본값: 오늘)'),
69
+ theaterId: z.string().optional().describe('롯데시네마 지점 ID (예: 1016)'),
70
+ movieId: z.string().optional().describe('롯데시네마 대표 영화 코드 (예: 23816)'),
71
+ keyword: z.string().optional().describe('위치 키워드 (예: 안산 중앙역, 잠실역)'),
72
+ latitude: z.number().optional().describe('위도'),
73
+ longitude: z.number().optional().describe('경도'),
74
+ timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
75
+ },
76
+ },
77
+ handler: ((args) => listNowShowing(args, googleMapsApiKey)),
78
+ };
79
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 롯데시네마 서비스 전용 타입 정의
3
+ */
4
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 롯데마트 모바일 도와센터 엔드포인트
3
+ */
4
+ export const LOTTEMART_API = {
5
+ BASE_URL: 'https://company.lottemart.com',
6
+ ORIGIN_HOST: 'company.lottemart.com',
7
+ SOCKET_HOST: '210.93.146.57',
8
+ ORIGIN_REFERER: 'https://company.lottemart.com/mobiledowa/',
9
+ MARKET_OPTIONS_PATH: '/mobiledowa/inc/asp/search_market_list.asp',
10
+ STORE_SEARCH_PATH: '/mobiledowa/market/search_shop.asp',
11
+ PRODUCT_SEARCH_PATH: '/mobiledowa/product/search_product.asp',
12
+ PRODUCT_PAGE_PATH: '/mobiledowa/inc/asp/search_product_list.asp',
13
+ };
14
+ export const LOTTEMART_AREAS = [
15
+ '서울',
16
+ '경기',
17
+ '인천',
18
+ '강원',
19
+ '충북',
20
+ '충남',
21
+ '대전',
22
+ '경북',
23
+ '경남',
24
+ '대구',
25
+ '부산',
26
+ '울산',
27
+ '전북',
28
+ '전남',
29
+ '광주',
30
+ '기타',
31
+ ];