daiso 1.0.3 → 1.0.5
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 +218 -537
- package/dist/api/actionsProxy.js +82 -0
- package/dist/api/cgvHandlers.js +103 -16
- package/dist/api/daisoHandlers.js +127 -0
- package/dist/api/emart24Handlers.js +176 -0
- package/dist/api/gs25Handlers.js +256 -0
- package/dist/api/handlers.js +184 -108
- package/dist/api/healthChecks.js +332 -0
- package/dist/api/lottecinemaHandlers.js +155 -0
- package/dist/api/lottemartHandlers.js +128 -0
- package/dist/api/megaboxHandlers.js +81 -50
- 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 +44 -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/cli/args.js +54 -0
- package/dist/cli/commands/convenience.js +241 -0
- package/dist/cli/commands/daiso.js +96 -0
- package/dist/cli/constants.js +5 -0
- package/dist/cli/deps.js +48 -0
- package/dist/cli/http.js +98 -0
- package/dist/cli/interactiveFetch.js +31 -0
- package/dist/cli/interactiveItemSearch.js +259 -0
- package/dist/cli/interactivePrompt.js +49 -0
- package/dist/cli/interactiveTypes.js +4 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli.js +66 -181
- package/dist/cliHelp.js +216 -9
- package/dist/cliInteractive.js +174 -0
- package/dist/cliPicker.js +56 -0
- package/dist/cliRenderer.js +139 -0
- package/dist/index.js +147 -8
- 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 +138 -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 +135 -0
- package/dist/pages/openapiSpecPathsLottecinema.js +212 -0
- package/dist/pages/openapiSpecPathsMegabox.js +52 -3
- package/dist/pages/openapiSpecPathsOliveyoung.js +231 -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 +131 -0
- package/dist/pages/promptRetailCinemaText.js +273 -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 +59 -0
- package/dist/services/daiso/index.js +2 -0
- package/dist/services/daiso/product.js +17 -0
- package/dist/services/daiso/tools/checkInventory.js +76 -41
- 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 +173 -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 +63 -0
- package/dist/services/emart24/types.js +4 -0
- package/dist/services/gs25/api.js +9 -0
- package/dist/services/gs25/client.js +198 -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 +50 -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 +245 -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 +62 -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 +150 -9
- package/dist/services/oliveyoung/index.js +3 -1
- package/dist/services/oliveyoung/tools/checkInventory.js +15 -4
- package/dist/services/oliveyoung/tools/searchProducts.js +57 -0
- package/dist/services/seveneleven/api.js +15 -0
- package/dist/services/seveneleven/client.js +279 -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 +56 -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 +16 -2
- package/openapi.json +1977 -734
- package/openapi.yaml +1430 -543
- package/package.json +28 -15
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daiso",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Daiso MCP Server for mobile access via Cloudflare Workers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,19 +18,22 @@
|
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"scripts": {
|
|
21
|
+
"hooks:install": "git config core.hooksPath .githooks && chmod +x .githooks/pre-push",
|
|
21
22
|
"dev": "wrangler dev",
|
|
22
23
|
"deploy": "wrangler deploy",
|
|
23
|
-
"build": "tsc &&
|
|
24
|
-
"build:openapi": "
|
|
24
|
+
"build": "tsc && npx tsx scripts/generate-openapi.ts",
|
|
25
|
+
"build:openapi": "npx tsx scripts/generate-openapi.ts",
|
|
26
|
+
"cli:smoke": "npm run build && npx tsx scripts/cli-smoke.ts",
|
|
27
|
+
"update:workers-chart": "npx tsx scripts/update-workers-invocations-chart.ts",
|
|
25
28
|
"prepack": "npm run build",
|
|
26
29
|
"prepublishOnly": "npm run check && npm run build",
|
|
27
30
|
"release:dry-run": "npm pack --dry-run",
|
|
28
31
|
"publish:public": "npm publish --access public",
|
|
29
32
|
"typecheck": "tsc --noEmit",
|
|
30
|
-
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.
|
|
31
|
-
"lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.
|
|
32
|
-
"format": "prettier .github/**/*.md .github/**/*.yml README.md SECURITY.md AGENTS.md CONTRIBUTING.md package.json tsconfig.json .prettierrc.json .mcp.json eslint.config.
|
|
33
|
-
"format:check": "prettier .github/**/*.md .github/**/*.yml README.md SECURITY.md AGENTS.md CONTRIBUTING.md package.json tsconfig.json .prettierrc.json .mcp.json eslint.config.
|
|
33
|
+
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"vitest.config.ts\"",
|
|
34
|
+
"lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"vitest.config.ts\" --fix",
|
|
35
|
+
"format": "prettier .github/**/*.md .github/**/*.yml README.md SECURITY.md AGENTS.md CONTRIBUTING.md package.json tsconfig.json .prettierrc.json .mcp.json eslint.config.ts --write",
|
|
36
|
+
"format:check": "prettier .github/**/*.md .github/**/*.yml README.md SECURITY.md AGENTS.md CONTRIBUTING.md package.json tsconfig.json .prettierrc.json .mcp.json eslint.config.ts --check",
|
|
34
37
|
"cf-typegen": "wrangler types",
|
|
35
38
|
"test": "vitest run",
|
|
36
39
|
"test:watch": "vitest",
|
|
@@ -57,23 +60,33 @@
|
|
|
57
60
|
"node": ">=20 <21"
|
|
58
61
|
},
|
|
59
62
|
"devDependencies": {
|
|
60
|
-
"@cloudflare/workers-types": "^4.
|
|
63
|
+
"@cloudflare/workers-types": "^4.20260316.1",
|
|
61
64
|
"@eslint/js": "^10.0.1",
|
|
62
|
-
"@types/node": "^25.
|
|
63
|
-
"@vitest/coverage-v8": "^4.0
|
|
65
|
+
"@types/node": "^25.5.0",
|
|
66
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
67
|
+
"chart.js": "^4.5.1",
|
|
68
|
+
"chartjs-node-canvas": "^5.0.0",
|
|
69
|
+
"chartjs-plugin-datalabels": "^2.2.0",
|
|
64
70
|
"eslint": "^10.0.2",
|
|
65
71
|
"eslint-config-prettier": "^10.1.8",
|
|
66
72
|
"globals": "^17.4.0",
|
|
73
|
+
"jiti": "^2.6.1",
|
|
67
74
|
"playwright": "^1.58.2",
|
|
68
75
|
"prettier": "^3.8.1",
|
|
69
|
-
"
|
|
70
|
-
"typescript
|
|
71
|
-
"
|
|
72
|
-
"
|
|
76
|
+
"tsx": "^4.20.6",
|
|
77
|
+
"typescript": "^6.0.3",
|
|
78
|
+
"typescript-eslint": "^8.59.3",
|
|
79
|
+
"vitest": "^4.1.0",
|
|
80
|
+
"wrangler": "^4.73.0"
|
|
81
|
+
},
|
|
82
|
+
"overrides": {
|
|
83
|
+
"flatted": "3.4.2",
|
|
84
|
+
"undici": "7.24.0",
|
|
85
|
+
"brace-expansion": "5.0.6"
|
|
73
86
|
},
|
|
74
87
|
"dependencies": {
|
|
75
88
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
76
|
-
"hono": "^4.12.
|
|
89
|
+
"hono": "^4.12.8",
|
|
77
90
|
"workers-mcp": "^0.0.13"
|
|
78
91
|
}
|
|
79
92
|
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 재고 확인 도구
|
|
3
|
-
*
|
|
4
|
-
* 다이소몰 API를 사용하여 매장별 재고를 확인합니다.
|
|
5
|
-
* API: https://mapi.daisomall.co.kr/ms/msg/newIntSelStr
|
|
6
|
-
*/
|
|
7
|
-
import { fetchJson } from '../utils/fetch.js';
|
|
8
|
-
// 온라인 재고 조회
|
|
9
|
-
async function fetchOnlineStock(productNo) {
|
|
10
|
-
const data = await fetchJson('https://mapi.daisomall.co.kr/ms/msg/selOnlStck', {
|
|
11
|
-
method: 'POST',
|
|
12
|
-
headers: { 'Content-Type': 'application/json' },
|
|
13
|
-
body: JSON.stringify({ pdNo: productNo }),
|
|
14
|
-
});
|
|
15
|
-
if (!data.success) {
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
return data.data?.stck || 0;
|
|
19
|
-
}
|
|
20
|
-
// 매장별 재고 조회
|
|
21
|
-
async function fetchStoreInventory(productNo, lat, lng, page = 1, pageSize = 30, keyword = '') {
|
|
22
|
-
const data = await fetchJson('https://mapi.daisomall.co.kr/ms/msg/newIntSelStr', {
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: { 'Content-Type': 'application/json' },
|
|
25
|
-
body: JSON.stringify({
|
|
26
|
-
keyword,
|
|
27
|
-
pdNo: productNo,
|
|
28
|
-
curLttd: lat,
|
|
29
|
-
curLitd: lng,
|
|
30
|
-
geolocationAgrYn: 'Y',
|
|
31
|
-
pkupYn: '',
|
|
32
|
-
intCd: '',
|
|
33
|
-
pageSize,
|
|
34
|
-
currentPage: page,
|
|
35
|
-
}),
|
|
36
|
-
});
|
|
37
|
-
if (!data.success || !data.data?.msStrVOList) {
|
|
38
|
-
return { stores: [], totalCount: 0 };
|
|
39
|
-
}
|
|
40
|
-
const stores = data.data.msStrVOList.map((store) => ({
|
|
41
|
-
storeCode: store.strCd,
|
|
42
|
-
storeName: store.strNm,
|
|
43
|
-
address: store.strAddr,
|
|
44
|
-
phone: store.strTno,
|
|
45
|
-
openTime: store.opngTime,
|
|
46
|
-
closeTime: store.clsngTime,
|
|
47
|
-
lat: store.strLttd,
|
|
48
|
-
lng: store.strLitd,
|
|
49
|
-
distance: store.km,
|
|
50
|
-
quantity: parseInt(store.qty) || 0,
|
|
51
|
-
options: {
|
|
52
|
-
parking: store.parkYn === 'Y',
|
|
53
|
-
simCard: store.usimYn === 'Y',
|
|
54
|
-
pickup: store.pkupYn === 'Y',
|
|
55
|
-
taxFree: store.taxfYn === 'Y',
|
|
56
|
-
elevator: store.elvtYn === 'Y',
|
|
57
|
-
ramp: store.entrRampYn === 'Y',
|
|
58
|
-
cashless: store.nocashYn === 'Y',
|
|
59
|
-
},
|
|
60
|
-
}));
|
|
61
|
-
return { stores, totalCount: data.data.intStrCont || stores.length };
|
|
62
|
-
}
|
|
63
|
-
export async function checkInventory(args) {
|
|
64
|
-
const { productId, storeQuery = '', latitude = 37.5665, // 기본값: 서울 시청
|
|
65
|
-
longitude = 126.978, page = 1, pageSize = 30, } = args;
|
|
66
|
-
if (!productId || productId.trim().length === 0) {
|
|
67
|
-
throw new Error('상품 ID(productId)를 입력해주세요.');
|
|
68
|
-
}
|
|
69
|
-
// 온라인 재고와 매장 재고 동시 조회
|
|
70
|
-
const [onlineStock, storeResult] = await Promise.all([
|
|
71
|
-
fetchOnlineStock(productId),
|
|
72
|
-
fetchStoreInventory(productId, latitude, longitude, page, pageSize, storeQuery),
|
|
73
|
-
]);
|
|
74
|
-
// 재고 있는 매장과 없는 매장 분류
|
|
75
|
-
const inStockStores = storeResult.stores.filter((s) => s.quantity > 0);
|
|
76
|
-
const outOfStockStores = storeResult.stores.filter((s) => s.quantity === 0);
|
|
77
|
-
const result = {
|
|
78
|
-
productId,
|
|
79
|
-
location: { latitude, longitude },
|
|
80
|
-
onlineStock,
|
|
81
|
-
storeInventory: {
|
|
82
|
-
totalStores: storeResult.totalCount,
|
|
83
|
-
inStockCount: inStockStores.length,
|
|
84
|
-
outOfStockCount: outOfStockStores.length,
|
|
85
|
-
page,
|
|
86
|
-
pageSize,
|
|
87
|
-
stores: storeResult.stores,
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
return {
|
|
91
|
-
content: [
|
|
92
|
-
{
|
|
93
|
-
type: 'text',
|
|
94
|
-
text: JSON.stringify(result, null, 2),
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
};
|
|
98
|
-
}
|
package/dist/tools/findStores.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 매장 찾기 도구
|
|
3
|
-
*
|
|
4
|
-
* 다이소 API를 사용하여 매장을 검색합니다.
|
|
5
|
-
* API: https://www.daiso.co.kr/cs/ajax/shop_search
|
|
6
|
-
*/
|
|
7
|
-
import { fetchHtml, fetchJson } from '../utils/fetch.js';
|
|
8
|
-
// HTML에서 매장 정보 파싱
|
|
9
|
-
function parseStoresFromHtml(html) {
|
|
10
|
-
const stores = [];
|
|
11
|
-
// bx-store div 태그의 시작 위치 찾기
|
|
12
|
-
const divStartRegex = /<div[^>]*class="bx-store"[^>]*>/gi;
|
|
13
|
-
let divMatch;
|
|
14
|
-
while ((divMatch = divStartRegex.exec(html)) !== null) {
|
|
15
|
-
const startIdx = divMatch.index;
|
|
16
|
-
const divTag = divMatch[0];
|
|
17
|
-
// 해당 div의 끝 찾기 (다음 bx-store div나 특정 마커까지)
|
|
18
|
-
const endIdx = html.indexOf('<div class="bx-store"', startIdx + 1);
|
|
19
|
-
const blockEnd = endIdx > 0 ? endIdx : startIdx + 2000;
|
|
20
|
-
const block = html.slice(startIdx, blockEnd);
|
|
21
|
-
// data 속성 추출
|
|
22
|
-
const startMatch = /data-start="(\d+)"/.exec(divTag);
|
|
23
|
-
const endMatch = /data-end="(\d+)"/.exec(divTag);
|
|
24
|
-
const latMatch = /data-lat="([^"]+)"/.exec(divTag);
|
|
25
|
-
const lngMatch = /data-lng="([^"]+)"/.exec(divTag);
|
|
26
|
-
const infoMatch = /data-info='([^']*)'/.exec(divTag);
|
|
27
|
-
if (!startMatch || !endMatch || !latMatch || !lngMatch)
|
|
28
|
-
continue;
|
|
29
|
-
// 매장명, 전화번호, 주소 추출
|
|
30
|
-
const nameMatch = /<h4[^>]*class="place"[^>]*>([^<]+)<\/h4>/i.exec(block);
|
|
31
|
-
const phoneMatch = /<em[^>]*class="phone"[^>]*>([^<]*)<\/em>/i.exec(block);
|
|
32
|
-
const addrMatch = /<p[^>]*class="addr"[^>]*>([^<]+)<\/p>/i.exec(block);
|
|
33
|
-
if (!nameMatch)
|
|
34
|
-
continue;
|
|
35
|
-
// data-info JSON 파싱
|
|
36
|
-
let info = {};
|
|
37
|
-
if (infoMatch) {
|
|
38
|
-
try {
|
|
39
|
-
const infoStr = infoMatch[1].replace(/"/g, '"').replace(/&/g, '&');
|
|
40
|
-
info = JSON.parse(infoStr);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// JSON 파싱 실패 시 빈 객체 사용
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const options = {
|
|
47
|
-
parking: info.shp_pak === 'Y',
|
|
48
|
-
ramp: info.entrramp === 'Y',
|
|
49
|
-
elevator: info.elvtor === 'Y',
|
|
50
|
-
cashless: info.ptcard === 'Y',
|
|
51
|
-
photoSticker: info.ptstk === 'Y',
|
|
52
|
-
nameSticker: info.nmstk === 'Y',
|
|
53
|
-
simCard: info.usim_yn === 'Y',
|
|
54
|
-
taxFree: info.tax_free === 'Y',
|
|
55
|
-
groupOrder: info['ext.group_yn'] === 'Y' || info.group_yn === 'Y',
|
|
56
|
-
pickup: info.online_yn === 'Y',
|
|
57
|
-
};
|
|
58
|
-
// 영업 시간 포맷팅
|
|
59
|
-
const formatTime = (time) => {
|
|
60
|
-
if (time.length === 4) {
|
|
61
|
-
return `${time.slice(0, 2)}:${time.slice(2)}`;
|
|
62
|
-
}
|
|
63
|
-
return time;
|
|
64
|
-
};
|
|
65
|
-
stores.push({
|
|
66
|
-
name: nameMatch[1].trim(),
|
|
67
|
-
phone: phoneMatch ? phoneMatch[1].replace('T.', '').trim() : '',
|
|
68
|
-
address: addrMatch ? addrMatch[1].trim() : '',
|
|
69
|
-
lat: parseFloat(latMatch[1]),
|
|
70
|
-
lng: parseFloat(lngMatch[1]),
|
|
71
|
-
openTime: formatTime(startMatch[1]),
|
|
72
|
-
closeTime: formatTime(endMatch[1]),
|
|
73
|
-
options,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return stores;
|
|
77
|
-
}
|
|
78
|
-
// 다이소 매장 검색 API 호출
|
|
79
|
-
async function fetchStores(keyword, sido, gugun, dong) {
|
|
80
|
-
const url = new URL('https://www.daiso.co.kr/cs/ajax/shop_search');
|
|
81
|
-
url.searchParams.set('name_address', keyword || '');
|
|
82
|
-
url.searchParams.set('sido', sido || '');
|
|
83
|
-
url.searchParams.set('gugun', gugun || '');
|
|
84
|
-
url.searchParams.set('dong', dong || '');
|
|
85
|
-
const html = await fetchHtml(url.toString());
|
|
86
|
-
return parseStoresFromHtml(html);
|
|
87
|
-
}
|
|
88
|
-
// 시/도별 구/군 목록 조회
|
|
89
|
-
export async function getDistricts(sido) {
|
|
90
|
-
const url = new URL('https://www.daiso.co.kr/cs/ajax/sido_search');
|
|
91
|
-
url.searchParams.set('sido', sido);
|
|
92
|
-
const data = await fetchJson(url.toString());
|
|
93
|
-
return data.map((item) => item.value);
|
|
94
|
-
}
|
|
95
|
-
// 구/군별 동 목록 조회
|
|
96
|
-
export async function getNeighborhoods(sido, gugun) {
|
|
97
|
-
const url = new URL('https://www.daiso.co.kr/cs/ajax/gugun_search');
|
|
98
|
-
url.searchParams.set('sido', sido);
|
|
99
|
-
url.searchParams.set('gugun', gugun);
|
|
100
|
-
const data = await fetchJson(url.toString());
|
|
101
|
-
return data.map((item) => item.value);
|
|
102
|
-
}
|
|
103
|
-
export async function findStores(args) {
|
|
104
|
-
const { keyword, sido, gugun, dong, limit = 50 } = args;
|
|
105
|
-
// 최소한 하나의 검색 조건이 필요
|
|
106
|
-
if (!keyword && !sido) {
|
|
107
|
-
throw new Error('검색어(keyword) 또는 지역(sido)을 입력해주세요.');
|
|
108
|
-
}
|
|
109
|
-
const stores = await fetchStores(keyword, sido, gugun, dong);
|
|
110
|
-
const limitedStores = stores.slice(0, limit);
|
|
111
|
-
const result = {
|
|
112
|
-
searchParams: { keyword, sido, gugun, dong },
|
|
113
|
-
totalCount: stores.length,
|
|
114
|
-
count: limitedStores.length,
|
|
115
|
-
stores: limitedStores,
|
|
116
|
-
};
|
|
117
|
-
return {
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: 'text',
|
|
121
|
-
text: JSON.stringify(result, null, 2),
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
};
|
|
125
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 가격 정보 조회 도구
|
|
3
|
-
*
|
|
4
|
-
* 다이소몰 API를 사용하여 상품의 가격 정보를 조회합니다.
|
|
5
|
-
* 상품 검색 API를 통해 가격 정보를 가져옵니다.
|
|
6
|
-
*/
|
|
7
|
-
import { fetchJson } from '../utils/fetch.js';
|
|
8
|
-
// 이미지 URL 생성
|
|
9
|
-
function getImageUrl(path) {
|
|
10
|
-
if (!path)
|
|
11
|
-
return undefined;
|
|
12
|
-
return `https://img.daisomall.co.kr${path}`;
|
|
13
|
-
}
|
|
14
|
-
// 상품 ID로 검색
|
|
15
|
-
async function fetchProductById(productId) {
|
|
16
|
-
const url = new URL('https://prdm.daisomall.co.kr/ssn/search/FindStoreGoods');
|
|
17
|
-
url.searchParams.set('searchTerm', productId);
|
|
18
|
-
url.searchParams.set('cntPerPage', '10');
|
|
19
|
-
url.searchParams.set('pageNum', '1');
|
|
20
|
-
const data = await fetchJson(url.toString());
|
|
21
|
-
if (!data.resultSet?.result?.[0]?.resultDocuments) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const docs = data.resultSet.result[0].resultDocuments;
|
|
25
|
-
// 정확한 ID 매칭 시도
|
|
26
|
-
const exactMatch = docs.find((doc) => doc.PD_NO === productId);
|
|
27
|
-
if (exactMatch) {
|
|
28
|
-
return exactMatch;
|
|
29
|
-
}
|
|
30
|
-
return docs.length > 0 ? docs[0] : null;
|
|
31
|
-
}
|
|
32
|
-
// 상품명으로 검색
|
|
33
|
-
async function fetchProductByName(productName) {
|
|
34
|
-
const url = new URL('https://prdm.daisomall.co.kr/ssn/search/FindStoreGoods');
|
|
35
|
-
url.searchParams.set('searchTerm', productName);
|
|
36
|
-
url.searchParams.set('cntPerPage', '1');
|
|
37
|
-
url.searchParams.set('pageNum', '1');
|
|
38
|
-
const data = await fetchJson(url.toString());
|
|
39
|
-
if (!data.resultSet?.result?.[0]?.resultDocuments?.length) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return data.resultSet.result[0].resultDocuments[0];
|
|
43
|
-
}
|
|
44
|
-
export async function getPriceInfo(args) {
|
|
45
|
-
const { productId, productName } = args;
|
|
46
|
-
if (!productId && !productName) {
|
|
47
|
-
throw new Error('상품 ID(productId) 또는 상품명(productName)을 입력해주세요.');
|
|
48
|
-
}
|
|
49
|
-
let product = null;
|
|
50
|
-
if (productId) {
|
|
51
|
-
product = await fetchProductById(productId);
|
|
52
|
-
}
|
|
53
|
-
else if (productName) {
|
|
54
|
-
product = await fetchProductByName(productName);
|
|
55
|
-
}
|
|
56
|
-
if (!product) {
|
|
57
|
-
throw new Error(`상품을 찾을 수 없습니다: ${productId || productName}`);
|
|
58
|
-
}
|
|
59
|
-
const price = parseInt(product.PD_PRC) || 0;
|
|
60
|
-
const result = {
|
|
61
|
-
productId: product.PD_NO,
|
|
62
|
-
productName: product.PDNM || product.EXH_PD_NM,
|
|
63
|
-
currentPrice: price,
|
|
64
|
-
currency: 'KRW',
|
|
65
|
-
imageUrl: getImageUrl(product.ATCH_FILE_URL),
|
|
66
|
-
brand: product.BRND_NM || undefined,
|
|
67
|
-
soldOut: product.SOLD_OUT_YN === 'Y',
|
|
68
|
-
};
|
|
69
|
-
return {
|
|
70
|
-
content: [
|
|
71
|
-
{
|
|
72
|
-
type: 'text',
|
|
73
|
-
text: JSON.stringify(result, null, 2),
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
};
|
|
77
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 제품 검색 도구
|
|
3
|
-
*
|
|
4
|
-
* 다이소몰 API를 사용하여 제품을 검색합니다.
|
|
5
|
-
* API: https://prdm.daisomall.co.kr/ssn/search/FindStoreGoods
|
|
6
|
-
*/
|
|
7
|
-
import { fetchJson } from '../utils/fetch.js';
|
|
8
|
-
// 이미지 URL 생성
|
|
9
|
-
function getImageUrl(path) {
|
|
10
|
-
if (!path)
|
|
11
|
-
return undefined;
|
|
12
|
-
return `https://img.daisomall.co.kr${path}`;
|
|
13
|
-
}
|
|
14
|
-
// 다이소몰 API에서 상품 검색
|
|
15
|
-
async function fetchProducts(query, page = 1, pageSize = 30) {
|
|
16
|
-
const url = new URL('https://prdm.daisomall.co.kr/ssn/search/FindStoreGoods');
|
|
17
|
-
url.searchParams.set('searchTerm', query);
|
|
18
|
-
url.searchParams.set('cntPerPage', pageSize.toString());
|
|
19
|
-
url.searchParams.set('pageNum', page.toString());
|
|
20
|
-
const data = await fetchJson(url.toString());
|
|
21
|
-
// 결과가 없는 경우
|
|
22
|
-
if (!data.resultSet?.result?.[0]?.resultDocuments) {
|
|
23
|
-
return { products: [], totalCount: 0 };
|
|
24
|
-
}
|
|
25
|
-
const result = data.resultSet.result[0];
|
|
26
|
-
const totalCount = result.totalSize || 0;
|
|
27
|
-
const products = result.resultDocuments.map((doc) => ({
|
|
28
|
-
id: doc.PD_NO,
|
|
29
|
-
name: doc.PDNM || doc.EXH_PD_NM || '',
|
|
30
|
-
price: parseInt(doc.PD_PRC) || 0,
|
|
31
|
-
imageUrl: getImageUrl(doc.ATCH_FILE_URL),
|
|
32
|
-
brand: doc.BRND_NM || undefined,
|
|
33
|
-
soldOut: doc.SOLD_OUT_YN === 'Y',
|
|
34
|
-
isNew: doc.NEW_PD_YN === 'Y',
|
|
35
|
-
pickupAvailable: doc.PKUP_OR_PSBL_YN === 'Y',
|
|
36
|
-
}));
|
|
37
|
-
return { products, totalCount };
|
|
38
|
-
}
|
|
39
|
-
export async function searchProducts(args) {
|
|
40
|
-
const { query, page = 1, pageSize = 30 } = args;
|
|
41
|
-
if (!query || query.trim().length === 0) {
|
|
42
|
-
throw new Error('검색어를 입력해주세요.');
|
|
43
|
-
}
|
|
44
|
-
const { products, totalCount } = await fetchProducts(query, page, pageSize);
|
|
45
|
-
const result = {
|
|
46
|
-
query,
|
|
47
|
-
page,
|
|
48
|
-
pageSize,
|
|
49
|
-
totalCount,
|
|
50
|
-
count: products.length,
|
|
51
|
-
products,
|
|
52
|
-
};
|
|
53
|
-
return {
|
|
54
|
-
content: [
|
|
55
|
-
{
|
|
56
|
-
type: 'text',
|
|
57
|
-
text: JSON.stringify(result, null, 2),
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
}
|
package/dist/types/index.js
DELETED