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.
- package/README.md +249 -533
- package/dist/api/actionsProxy.js +82 -0
- package/dist/api/cgvHandlers.js +103 -16
- package/dist/api/configStatus.js +19 -0
- package/dist/api/daisoHandlers.js +127 -0
- package/dist/api/emart24Handlers.js +176 -0
- package/dist/api/gs25Handlers.js +267 -0
- package/dist/api/handlers.js +209 -104
- package/dist/api/healthChecks.js +504 -0
- package/dist/api/lottecinemaHandlers.js +155 -0
- package/dist/api/lottemartHandlers.js +144 -0
- package/dist/api/megaboxHandlers.js +81 -50
- package/dist/api/response.js +2 -0
- package/dist/api/routes/cuRoutes.js +17 -0
- package/dist/api/routes/daisoRoutes.js +6 -1
- package/dist/api/routes/emart24Routes.js +22 -0
- package/dist/api/routes/gs25Routes.js +22 -0
- package/dist/api/routes/healthRoutes.js +61 -0
- package/dist/api/routes/lottecinemaRoutes.js +22 -0
- package/dist/api/routes/lottemartRoutes.js +18 -0
- package/dist/api/routes/oliveyoungRoutes.js +6 -1
- package/dist/api/routes/sevenelevenRoutes.js +32 -0
- package/dist/api/sevenelevenHandlers.js +200 -0
- package/dist/bin.js +0 -0
- package/dist/cli/args.js +75 -0
- package/dist/cli/commandOptions.js +38 -0
- package/dist/cli/commands/convenience.js +293 -0
- package/dist/cli/commands/daiso.js +112 -0
- package/dist/cli/constants.js +5 -0
- package/dist/cli/deps.js +48 -0
- package/dist/cli/http.js +156 -0
- package/dist/cli/interactiveFetch.js +31 -0
- package/dist/cli/interactiveItemSearch.js +261 -0
- package/dist/cli/interactivePrompt.js +50 -0
- package/dist/cli/interactiveTypes.js +4 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli.js +87 -196
- package/dist/cliHelp.js +212 -1
- package/dist/cliInteractive.js +243 -0
- package/dist/cliPicker.js +57 -0
- package/dist/cliRenderer.js +90 -3
- package/dist/core/errors.js +66 -0
- package/dist/core/registry.js +227 -3
- package/dist/index.js +149 -9
- package/dist/pages/openapi.js +2 -1
- package/dist/pages/openapiFullSpec.js +58 -0
- package/dist/pages/openapiResponse.js +28 -0
- package/dist/pages/openapiSpec.js +2 -41
- package/dist/pages/openapiSpecActions.js +109 -0
- package/dist/pages/openapiSpecComponents.js +30 -391
- package/dist/pages/openapiSpecComponentsCgv.js +24 -0
- package/dist/pages/openapiSpecComponentsCu.js +112 -0
- package/dist/pages/openapiSpecComponentsDaisoOliveyoung.js +342 -0
- package/dist/pages/openapiSpecComponentsEmart24.js +141 -0
- package/dist/pages/openapiSpecComponentsGs25.js +109 -0
- package/dist/pages/openapiSpecComponentsLotteMart.js +85 -0
- package/dist/pages/openapiSpecComponentsLottecinema.js +163 -0
- package/dist/pages/openapiSpecComponentsMegabox.js +144 -0
- package/dist/pages/openapiSpecComponentsSeveneleven.js +82 -0
- package/dist/pages/openapiSpecPathsCgv.js +66 -3
- package/dist/pages/openapiSpecPathsCu.js +145 -0
- package/dist/pages/openapiSpecPathsDaiso.js +302 -0
- package/dist/pages/openapiSpecPathsDaisoOliveyoung.js +4 -376
- package/dist/pages/openapiSpecPathsEmart24.js +223 -0
- package/dist/pages/openapiSpecPathsGs25.js +166 -0
- package/dist/pages/openapiSpecPathsLotteMart.js +149 -0
- package/dist/pages/openapiSpecPathsLottecinema.js +212 -0
- package/dist/pages/openapiSpecPathsMegabox.js +52 -3
- package/dist/pages/openapiSpecPathsOliveyoung.js +266 -0
- package/dist/pages/openapiSpecPathsSeveneleven.js +234 -0
- package/dist/pages/prompt.js +8 -402
- package/dist/pages/promptCoreText.js +278 -0
- package/dist/pages/promptReferenceText.js +137 -0
- package/dist/pages/promptRetailCinemaText.js +274 -0
- package/dist/services/cgv/client.js +6 -1
- package/dist/services/cgv/index.js +3 -3
- package/dist/services/cgv/location.js +235 -0
- package/dist/services/cgv/tools/findTheaters.js +30 -5
- package/dist/services/cgv/tools/getTimetable.js +42 -14
- package/dist/services/cgv/tools/searchMovies.js +40 -12
- package/dist/services/cgv/transport.js +0 -3
- package/dist/services/cu/api.js +11 -0
- package/dist/services/cu/client.js +297 -0
- package/dist/services/cu/index.js +21 -0
- package/dist/services/cu/tools/checkInventory.js +123 -0
- package/dist/services/cu/tools/findNearbyStores.js +49 -0
- package/dist/services/cu/types.js +4 -0
- package/dist/services/daiso/api.js +22 -2
- package/dist/services/daiso/client.js +67 -0
- package/dist/services/daiso/index.js +4 -0
- package/dist/services/daiso/product.js +17 -0
- package/dist/services/daiso/tools/checkInventory.js +80 -43
- package/dist/services/daiso/tools/findInventoryByName.js +222 -0
- package/dist/services/daiso/tools/findStores.js +17 -7
- package/dist/services/daiso/tools/getDisplayLocation.js +75 -0
- package/dist/services/emart24/api.js +11 -0
- package/dist/services/emart24/client.js +182 -0
- package/dist/services/emart24/index.js +22 -0
- package/dist/services/emart24/inventoryLookup.js +134 -0
- package/dist/services/emart24/tools/checkInventory.js +54 -0
- package/dist/services/emart24/tools/findNearbyStores.js +72 -0
- package/dist/services/emart24/tools/searchProducts.js +73 -0
- package/dist/services/emart24/types.js +4 -0
- package/dist/services/gs25/api.js +9 -0
- package/dist/services/gs25/client.js +208 -0
- package/dist/services/gs25/index.js +22 -0
- package/dist/services/gs25/storeUtils.js +168 -0
- package/dist/services/gs25/tools/checkInventory.js +163 -0
- package/dist/services/gs25/tools/findNearbyStores.js +103 -0
- package/dist/services/gs25/tools/searchProducts.js +57 -0
- package/dist/services/gs25/types.js +4 -0
- package/dist/services/lottecinema/api.js +12 -0
- package/dist/services/lottecinema/client.js +161 -0
- package/dist/services/lottecinema/index.js +30 -0
- package/dist/services/lottecinema/location.js +191 -0
- package/dist/services/lottecinema/tools/findNearbyTheaters.js +47 -0
- package/dist/services/lottecinema/tools/getRemainingSeats.js +85 -0
- package/dist/services/lottecinema/tools/listNowShowing.js +79 -0
- package/dist/services/lottecinema/types.js +4 -0
- package/dist/services/lottemart/api.js +31 -0
- package/dist/services/lottemart/client.js +254 -0
- package/dist/services/lottemart/clientTypes.js +4 -0
- package/dist/services/lottemart/config.js +4 -0
- package/dist/services/lottemart/debug.js +92 -0
- package/dist/services/lottemart/geocode.js +49 -0
- package/dist/services/lottemart/index.js +30 -0
- package/dist/services/lottemart/keyword.js +29 -0
- package/dist/services/lottemart/parser.js +185 -0
- package/dist/services/lottemart/session.js +331 -0
- package/dist/services/lottemart/storeSearch.js +52 -0
- package/dist/services/lottemart/tools/findNearbyStores.js +67 -0
- package/dist/services/lottemart/tools/searchProducts.js +69 -0
- package/dist/services/lottemart/types.js +4 -0
- package/dist/services/lottemart/zetta.js +105 -0
- package/dist/services/megabox/location.js +219 -0
- package/dist/services/megabox/tools/findNearbyTheaters.js +13 -44
- package/dist/services/megabox/tools/getRemainingSeats.js +31 -3
- package/dist/services/megabox/tools/listNowShowing.js +31 -3
- package/dist/services/oliveyoung/api.js +2 -0
- package/dist/services/oliveyoung/client.js +225 -13
- package/dist/services/oliveyoung/index.js +3 -1
- package/dist/services/oliveyoung/tools/checkInventory.js +21 -5
- package/dist/services/oliveyoung/tools/searchProducts.js +57 -0
- package/dist/services/seveneleven/api.js +15 -0
- package/dist/services/seveneleven/client.js +286 -0
- package/dist/services/seveneleven/index.js +30 -0
- package/dist/services/seveneleven/inventory.js +302 -0
- package/dist/services/seveneleven/productKeyword.js +141 -0
- package/dist/services/seveneleven/tools/checkInventory.js +82 -0
- package/dist/services/seveneleven/tools/getCatalogSnapshot.js +55 -0
- package/dist/services/seveneleven/tools/getSearchPopwords.js +41 -0
- package/dist/services/seveneleven/tools/searchProducts.js +67 -0
- package/dist/services/seveneleven/tools/searchStores.js +45 -0
- package/dist/services/seveneleven/types.js +4 -0
- package/dist/utils/cliInteractiveHelpers.js +63 -0
- package/dist/utils/daisoKeyword.js +30 -0
- package/dist/utils/http.js +98 -12
- package/dist/utils/zyte.js +47 -26
- package/openapi.json +1979 -736
- package/openapi.yaml +1428 -541
- package/package.json +49 -18
- package/dist/tools/checkInventory.js +0 -98
- package/dist/tools/findStores.js +0 -125
- package/dist/tools/getPriceInfo.js +0 -77
- package/dist/tools/searchProducts.js +0 -61
- 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,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
|
+
];
|