@nomadamas/k-skill 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.
- package/bin/k-skill.js +60 -0
- package/package.json +37 -0
- package/skills/assembly-bill-vote-search/instruction.md +111 -0
- package/skills/assembly-bill-vote-search/skill.json +10 -0
- package/skills/biz-health-check/instruction.md +69 -0
- package/skills/biz-health-check/scripts/biz_health_check.py +163 -0
- package/skills/biz-health-check/skill.json +10 -0
- package/skills/bok-ecos-stats/instruction.md +95 -0
- package/skills/bok-ecos-stats/scripts/bok_ecos.py +302 -0
- package/skills/bok-ecos-stats/skill.json +10 -0
- package/skills/building-register-search/instruction.md +69 -0
- package/skills/building-register-search/scripts/building_register.py +309 -0
- package/skills/building-register-search/scripts/building_register_xml.py +32 -0
- package/skills/building-register-search/skill.json +9 -0
- package/skills/bunjang-search/instruction.md +154 -0
- package/skills/bunjang-search/skill.json +10 -0
- package/skills/catchtable-sniper/instruction.md +270 -0
- package/skills/catchtable-sniper/skill.json +9 -0
- package/skills/cheap-gas-nearby/instruction.md +107 -0
- package/skills/cheap-gas-nearby/skill.json +10 -0
- package/skills/corporate-registration-consulting/instruction.md +117 -0
- package/skills/corporate-registration-consulting/scripts/fill_official_hwp.py +106 -0
- package/skills/corporate-registration-consulting/skill.json +8 -0
- package/skills/coupang-product-search/instruction.md +219 -0
- package/skills/coupang-product-search/scripts/coupang_partners_mcp.py +146 -0
- package/skills/coupang-product-search/skill.json +10 -0
- package/skills/court-auction-notice-search/instruction.md +201 -0
- package/skills/court-auction-notice-search/skill.json +9 -0
- package/skills/court-payment-order-assistant/instruction.md +120 -0
- package/skills/court-payment-order-assistant/skill.json +10 -0
- package/skills/d2b-notice-search/instruction.md +117 -0
- package/skills/d2b-notice-search/skill.json +10 -0
- package/skills/daangn-cars-search/instruction.md +91 -0
- package/skills/daangn-cars-search/scripts/daangn_cars.py +72 -0
- package/skills/daangn-cars-search/skill.json +9 -0
- package/skills/daangn-jobs-search/instruction.md +90 -0
- package/skills/daangn-jobs-search/scripts/daangn_jobs.py +98 -0
- package/skills/daangn-jobs-search/skill.json +9 -0
- package/skills/daangn-realty-search/instruction.md +114 -0
- package/skills/daangn-realty-search/scripts/daangn_detail_ld.py +76 -0
- package/skills/daangn-realty-search/scripts/daangn_realty.py +216 -0
- package/skills/daangn-realty-search/scripts/daangn_relay_store.py +174 -0
- package/skills/daangn-realty-search/skill.json +8 -0
- package/skills/daangn-used-goods-search/instruction.md +90 -0
- package/skills/daangn-used-goods-search/scripts/daangn_used_goods.py +80 -0
- package/skills/daangn-used-goods-search/skill.json +9 -0
- package/skills/daishin-report-search/instruction.md +138 -0
- package/skills/daishin-report-search/skill.json +10 -0
- package/skills/daiso-product-search/instruction.md +172 -0
- package/skills/daiso-product-search/skill.json +8 -0
- package/skills/danawa-price-search/instruction.md +183 -0
- package/skills/danawa-price-search/scripts/danawa_search.py +354 -0
- package/skills/danawa-price-search/skill.json +8 -0
- package/skills/delivery-tracking/instruction.md +358 -0
- package/skills/delivery-tracking/skill.json +8 -0
- package/skills/donation-place-search/instruction.md +129 -0
- package/skills/donation-place-search/skill.json +9 -0
- package/skills/emergency-room-beds/instruction.md +82 -0
- package/skills/emergency-room-beds/skill.json +8 -0
- package/skills/ev-charger-nearby/instruction.md +80 -0
- package/skills/ev-charger-nearby/scripts/ev_charger.py +222 -0
- package/skills/ev-charger-nearby/skill.json +10 -0
- package/skills/ev-subsidy-status/instruction.md +165 -0
- package/skills/ev-subsidy-status/skill.json +10 -0
- package/skills/express-bus-booking/instruction.md +207 -0
- package/skills/express-bus-booking/references/kobus-http-flow.md +159 -0
- package/skills/express-bus-booking/scripts/kobus_express_booking.py +243 -0
- package/skills/express-bus-booking/skill.json +8 -0
- package/skills/fine-dust-location/instruction.md +89 -0
- package/skills/fine-dust-location/skill.json +9 -0
- package/skills/flight-ticket-search/instruction.md +237 -0
- package/skills/flight-ticket-search/scripts/flight_ticket_search.py +501 -0
- package/skills/flight-ticket-search/skill.json +9 -0
- package/skills/foresttrip-vacancy/instruction.md +167 -0
- package/skills/foresttrip-vacancy/scripts/run_foresttrip_vacancy.py +549 -0
- package/skills/foresttrip-vacancy/skill.json +10 -0
- package/skills/fsc-corporate-info/instruction.md +57 -0
- package/skills/fsc-corporate-info/scripts/fsc_corporate_info.py +113 -0
- package/skills/fsc-corporate-info/skill.json +10 -0
- package/skills/g2b-order-plan-search/instruction.md +131 -0
- package/skills/g2b-order-plan-search/scripts/g2b_order_plan.py +158 -0
- package/skills/g2b-order-plan-search/skill.json +10 -0
- package/skills/g2b-sanctioned-supplier/instruction.md +61 -0
- package/skills/g2b-sanctioned-supplier/scripts/g2b_sanctioned_supplier.py +114 -0
- package/skills/g2b-sanctioned-supplier/skill.json +10 -0
- package/skills/gangnamunni-clinic-search/instruction.md +113 -0
- package/skills/gangnamunni-clinic-search/skill.json +9 -0
- package/skills/geeknews-search/instruction.md +69 -0
- package/skills/geeknews-search/scripts/geeknews_search.py +296 -0
- package/skills/geeknews-search/skill.json +8 -0
- package/skills/gongsijiga-search/instruction.md +128 -0
- package/skills/gongsijiga-search/skill.json +9 -0
- package/skills/gov-overseas-trip-report/instruction.md +488 -0
- package/skills/gov-overseas-trip-report/scripts/gov_overseas_trip_report.py +1206 -0
- package/skills/gov-overseas-trip-report/skill.json +9 -0
- package/skills/han-river-water-level/instruction.md +83 -0
- package/skills/han-river-water-level/skill.json +10 -0
- package/skills/highway-traffic-status/instruction.md +88 -0
- package/skills/highway-traffic-status/scripts/highway_traffic.py +315 -0
- package/skills/highway-traffic-status/skill.json +11 -0
- package/skills/hipass-receipt/instruction.md +97 -0
- package/skills/hipass-receipt/skill.json +10 -0
- package/skills/hola-poke-yeoksam/instruction.md +247 -0
- package/skills/hola-poke-yeoksam/skill.json +8 -0
- package/skills/household-waste-info/instruction.md +117 -0
- package/skills/household-waste-info/skill.json +10 -0
- package/skills/housing-official-price/instruction.md +177 -0
- package/skills/housing-official-price/skill.json +10 -0
- package/skills/hwp/instruction.md +206 -0
- package/skills/hwp/skill.json +8 -0
- package/skills/intercity-bus-booking/instruction.md +189 -0
- package/skills/intercity-bus-booking/references/tmoney-intercity-http-flow.md +126 -0
- package/skills/intercity-bus-booking/scripts/intercity_bus_search.py +381 -0
- package/skills/intercity-bus-booking/skill.json +8 -0
- package/skills/iros-registry-automation/instruction.md +229 -0
- package/skills/iros-registry-automation/scripts/iros_pdf_summary.py +249 -0
- package/skills/iros-registry-automation/scripts/upstream.pin +1 -0
- package/skills/iros-registry-automation/skill.json +9 -0
- package/skills/job-posting-match/instruction.md +130 -0
- package/skills/job-posting-match/scripts/job_posting_match.py +396 -0
- package/skills/job-posting-match/scripts/test_job_posting_match.py +54 -0
- package/skills/job-posting-match/skill.json +8 -0
- package/skills/jobkorea-talent-search/instruction.md +118 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_models.py +27 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_parse.py +186 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search.py +94 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search_condition.py +136 -0
- package/skills/jobkorea-talent-search/scripts/test_jobkorea_talent_search.py +76 -0
- package/skills/jobkorea-talent-search/skill.json +9 -0
- package/skills/joseon-sillok-search/instruction.md +76 -0
- package/skills/joseon-sillok-search/scripts/sillok_search.py +552 -0
- package/skills/joseon-sillok-search/skill.json +8 -0
- package/skills/k-dart/instruction.md +406 -0
- package/skills/k-dart/skill.json +8 -0
- package/skills/k-schoollunch-menu/instruction.md +109 -0
- package/skills/k-schoollunch-menu/skill.json +9 -0
- package/skills/k-skill-cleaner/instruction.md +80 -0
- package/skills/k-skill-cleaner/scripts/k_skill_cleaner.py +410 -0
- package/skills/k-skill-cleaner/skill.json +8 -0
- package/skills/k-skill-setup/instruction.md +253 -0
- package/skills/k-skill-setup/skill.json +11 -0
- package/skills/kakao-bar-nearby/instruction.md +76 -0
- package/skills/kakao-bar-nearby/skill.json +8 -0
- package/skills/kakao-map/instruction.md +176 -0
- package/skills/kakao-map/skill.json +10 -0
- package/skills/kakaotalk-mac/instruction.md +189 -0
- package/skills/kakaotalk-mac/skill.json +8 -0
- package/skills/kbl-results/instruction.md +89 -0
- package/skills/kbl-results/skill.json +9 -0
- package/skills/kbo-results/instruction.md +82 -0
- package/skills/kbo-results/skill.json +8 -0
- package/skills/keris-academic-search/instruction.md +81 -0
- package/skills/keris-academic-search/scripts/keris_academic.py +210 -0
- package/skills/keris-academic-search/skill.json +10 -0
- package/skills/kleague-results/instruction.md +92 -0
- package/skills/kleague-results/skill.json +8 -0
- package/skills/kopis-performance-search/instruction.md +109 -0
- package/skills/kopis-performance-search/skill.json +10 -0
- package/skills/korea-weather/instruction.md +93 -0
- package/skills/korea-weather/skill.json +10 -0
- package/skills/korean-character-count/instruction.md +87 -0
- package/skills/korean-character-count/scripts/korean_character_count.js +268 -0
- package/skills/korean-character-count/skill.json +8 -0
- package/skills/korean-cinema-search/instruction.md +177 -0
- package/skills/korean-cinema-search/skill.json +8 -0
- package/skills/korean-heritage-search/instruction.md +106 -0
- package/skills/korean-heritage-search/scripts/korean_heritage_search.py +321 -0
- package/skills/korean-heritage-search/skill.json +8 -0
- package/skills/korean-holiday-calendar/instruction.md +97 -0
- package/skills/korean-holiday-calendar/skill.json +9 -0
- package/skills/korean-humanizer/instruction.md +389 -0
- package/skills/korean-humanizer/references/ai-tell-taxonomy.md +147 -0
- package/skills/korean-humanizer/skill.json +8 -0
- package/skills/korean-jangbu-for/instruction.md +133 -0
- package/skills/korean-jangbu-for/scripts/install.sh +237 -0
- package/skills/korean-jangbu-for/scripts/upstream.pin +1 -0
- package/skills/korean-jangbu-for/skill.json +8 -0
- package/skills/korean-law-search/instruction.md +126 -0
- package/skills/korean-law-search/skill.json +10 -0
- package/skills/korean-marathon-schedule/instruction.md +111 -0
- package/skills/korean-marathon-schedule/skill.json +9 -0
- package/skills/korean-middle-korean/instruction.md +79 -0
- package/skills/korean-middle-korean/scripts/korean_middle_korean.js +214 -0
- package/skills/korean-middle-korean/skill.json +8 -0
- package/skills/korean-patent-search/instruction.md +79 -0
- package/skills/korean-patent-search/scripts/patent_search.py +409 -0
- package/skills/korean-patent-search/skill.json +9 -0
- package/skills/korean-privacy-terms/instruction.md +128 -0
- package/skills/korean-privacy-terms/scripts/install.sh +108 -0
- package/skills/korean-privacy-terms/scripts/upstream.pin +1 -0
- package/skills/korean-privacy-terms/skill.json +8 -0
- package/skills/korean-scholarship-search/instruction.md +317 -0
- package/skills/korean-scholarship-search/references/report-format.md +40 -0
- package/skills/korean-scholarship-search/references/school-discovery.md +61 -0
- package/skills/korean-scholarship-search/references/search-clues.md +58 -0
- package/skills/korean-scholarship-search/references/source-patterns.md +67 -0
- package/skills/korean-scholarship-search/scripts/scholarship_filter.py +811 -0
- package/skills/korean-scholarship-search/scripts/test_scholarship_filter.py +224 -0
- package/skills/korean-scholarship-search/scripts/university_search_plan.py +148 -0
- package/skills/korean-scholarship-search/skill.json +8 -0
- package/skills/korean-slang-writing/instruction.md +181 -0
- package/skills/korean-slang-writing/scripts/_slang_http.py +91 -0
- package/skills/korean-slang-writing/scripts/slang_lookup.py +291 -0
- package/skills/korean-slang-writing/scripts/slang_search.py +284 -0
- package/skills/korean-slang-writing/skill.json +8 -0
- package/skills/korean-spell-check/instruction.md +105 -0
- package/skills/korean-spell-check/scripts/korean_spell_check.py +523 -0
- package/skills/korean-spell-check/skill.json +9 -0
- package/skills/korean-stock-search/instruction.md +194 -0
- package/skills/korean-stock-search/skill.json +10 -0
- package/skills/korean-transit-route/instruction.md +113 -0
- package/skills/korean-transit-route/skill.json +10 -0
- package/skills/kosis-stats/instruction.md +232 -0
- package/skills/kosis-stats/references/kosis-openapi-guide.md +171 -0
- package/skills/kosis-stats/scripts/run_kosis_stats.py +896 -0
- package/skills/kosis-stats/skill.json +9 -0
- package/skills/kr-whois-lookup/instruction.md +107 -0
- package/skills/kr-whois-lookup/skill.json +10 -0
- package/skills/kstartup-search/instruction.md +186 -0
- package/skills/kstartup-search/scripts/run_kstartup.py +424 -0
- package/skills/kstartup-search/skill.json +11 -0
- package/skills/ktx-booking/instruction.md +244 -0
- package/skills/ktx-booking/skill.json +10 -0
- package/skills/lck-analytics/instruction.md +192 -0
- package/skills/lck-analytics/scripts/_lib.js +103 -0
- package/skills/lck-analytics/scripts/analyze-live-game.js +52 -0
- package/skills/lck-analytics/scripts/build-match-report.js +44 -0
- package/skills/lck-analytics/scripts/sync-oracle.js +50 -0
- package/skills/lck-analytics/skill.json +9 -0
- package/skills/lh-notice-search/instruction.md +206 -0
- package/skills/lh-notice-search/skill.json +10 -0
- package/skills/library-book-search/instruction.md +139 -0
- package/skills/library-book-search/skill.json +9 -0
- package/skills/local-election-candidate-search/instruction.md +77 -0
- package/skills/local-election-candidate-search/skill.json +8 -0
- package/skills/localdata-business-status/instruction.md +64 -0
- package/skills/localdata-business-status/scripts/localdata_business_status.py +206 -0
- package/skills/localdata-business-status/skill.json +8 -0
- package/skills/lotto-results/instruction.md +80 -0
- package/skills/lotto-results/skill.json +8 -0
- package/skills/lovebug-report/instruction.md +185 -0
- package/skills/lovebug-report/skill.json +9 -0
- package/skills/market-kurly-search/instruction.md +125 -0
- package/skills/market-kurly-search/skill.json +8 -0
- package/skills/mfds-drug-safety/instruction.md +86 -0
- package/skills/mfds-drug-safety/scripts/mfds_drug_safety.py +184 -0
- package/skills/mfds-drug-safety/skill.json +10 -0
- package/skills/mfds-food-safety/instruction.md +126 -0
- package/skills/mfds-food-safety/scripts/mfds_food_safety.py +281 -0
- package/skills/mfds-food-safety/skill.json +10 -0
- package/skills/myrealtrip-search/instruction.md +239 -0
- package/skills/myrealtrip-search/scripts/myrealtrip_mcp.py +194 -0
- package/skills/myrealtrip-search/scripts/test_myrealtrip_mcp.py +99 -0
- package/skills/myrealtrip-search/skill.json +9 -0
- package/skills/naming-house/instruction.md +146 -0
- package/skills/naming-house/skill.json +8 -0
- package/skills/national-pension-workplace/instruction.md +64 -0
- package/skills/national-pension-workplace/scripts/national_pension_workplace.py +113 -0
- package/skills/national-pension-workplace/skill.json +10 -0
- package/skills/naver-ad-performance/instruction.md +108 -0
- package/skills/naver-ad-performance/scripts/naver_ad_performance.py +240 -0
- package/skills/naver-ad-performance/skill.json +9 -0
- package/skills/naver-blog-research/instruction.md +128 -0
- package/skills/naver-blog-research/scripts/_naver_http.py +58 -0
- package/skills/naver-blog-research/scripts/naver_download_images.py +233 -0
- package/skills/naver-blog-research/scripts/naver_read.py +256 -0
- package/skills/naver-blog-research/scripts/naver_search.py +192 -0
- package/skills/naver-blog-research/skill.json +8 -0
- package/skills/naver-news-search/instruction.md +103 -0
- package/skills/naver-news-search/skill.json +10 -0
- package/skills/naver-shopping-search/instruction.md +94 -0
- package/skills/naver-shopping-search/skill.json +11 -0
- package/skills/nhis-care-checkup-search/instruction.md +116 -0
- package/skills/nhis-care-checkup-search/skill.json +10 -0
- package/skills/nts-business-registration/instruction.md +115 -0
- package/skills/nts-business-registration/scripts/nts_business_registration.py +215 -0
- package/skills/nts-business-registration/skill.json +10 -0
- package/skills/nts-tax-delinquency/instruction.md +54 -0
- package/skills/nts-tax-delinquency/scripts/nts_tax_delinquency.py +150 -0
- package/skills/nts-tax-delinquency/skill.json +8 -0
- package/skills/ohou-today-deal/instruction.md +182 -0
- package/skills/ohou-today-deal/scripts/ohou_today_deal.py +369 -0
- package/skills/ohou-today-deal/skill.json +9 -0
- package/skills/olive-young-search/instruction.md +154 -0
- package/skills/olive-young-search/skill.json +8 -0
- package/skills/parking-lot-search/instruction.md +96 -0
- package/skills/parking-lot-search/skill.json +9 -0
- package/skills/popbill/instruction.md +146 -0
- package/skills/popbill/scripts/popbill_cli.py +284 -0
- package/skills/popbill/scripts/popbill_registry.py +54 -0
- package/skills/popbill/scripts/popbill_safety.py +37 -0
- package/skills/popbill/scripts/popbill_templates.py +71 -0
- package/skills/popbill/skill.json +9 -0
- package/skills/public-restroom-nearby/instruction.md +89 -0
- package/skills/public-restroom-nearby/skill.json +9 -0
- package/skills/real-estate-search/instruction.md +172 -0
- package/skills/real-estate-search/skill.json +10 -0
- package/skills/rhwp-advanced/instruction.md +145 -0
- package/skills/rhwp-advanced/skill.json +8 -0
- package/skills/rhwp-edit/instruction.md +153 -0
- package/skills/rhwp-edit/skill.json +8 -0
- package/skills/s2b-notice-search/instruction.md +66 -0
- package/skills/s2b-notice-search/skill.json +9 -0
- package/skills/saju-fortune/instruction.md +177 -0
- package/skills/saju-fortune/skill.json +8 -0
- package/skills/saramin-talent-search/instruction.md +119 -0
- package/skills/saramin-talent-search/skill.json +9 -0
- package/skills/seoul-bike/instruction.md +83 -0
- package/skills/seoul-bike/scripts/seoul_bike.py +247 -0
- package/skills/seoul-bike/skill.json +10 -0
- package/skills/seoul-density/instruction.md +109 -0
- package/skills/seoul-density/scripts/seoul_density.py +271 -0
- package/skills/seoul-density/skill.json +10 -0
- package/skills/seoul-subway-arrival/instruction.md +85 -0
- package/skills/seoul-subway-arrival/skill.json +9 -0
- package/skills/sh-notice-search/instruction.md +150 -0
- package/skills/sh-notice-search/skill.json +9 -0
- package/skills/srt-booking/instruction.md +181 -0
- package/skills/srt-booking/scripts/srt_booking.py +272 -0
- package/skills/srt-booking/scripts/srt_seats.py +156 -0
- package/skills/srt-booking/skill.json +20 -0
- package/skills/subway-lost-property/instruction.md +93 -0
- package/skills/subway-lost-property/scripts/subway_lost_property.py +244 -0
- package/skills/subway-lost-property/skill.json +8 -0
- package/skills/ticket-availability/instruction.md +175 -0
- package/skills/ticket-availability/scripts/ticket_availability.py +430 -0
- package/skills/ticket-availability/skill.json +9 -0
- package/skills/toss-securities/instruction.md +116 -0
- package/skills/toss-securities/skill.json +9 -0
- package/skills/used-car-price-search/instruction.md +109 -0
- package/skills/used-car-price-search/skill.json +8 -0
- package/skills/yebigun-training/instruction.md +177 -0
- package/skills/yebigun-training/skill.json +9 -0
- package/skills/zipcode-search/instruction.md +159 -0
- package/skills/zipcode-search/scripts/zipcode_search.py +150 -0
- package/skills/zipcode-search/skill.json +8 -0
- package/src/assemble.js +134 -0
- package/src/detect.js +18 -0
- package/templates/action-account.md +4 -0
- package/templates/action-booking.md +5 -0
- package/templates/action-commerce.md +5 -0
- package/templates/action-communication.md +5 -0
- package/templates/action-submission.md +5 -0
- package/templates/browser.md +4 -0
- package/templates/core.md +5 -0
- package/templates/hard-boundary.md +3 -0
- package/templates/local.md +2 -0
- package/templates/lookup.md +2 -0
- package/templates/proxy.md +2 -0
- package/templates/vault.md +4 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse, json, re, sys, urllib.parse, urllib.request
|
|
3
|
+
from html import unescape
|
|
4
|
+
|
|
5
|
+
HEADERS = {"User-Agent":"Mozilla/5.0", "Accept":"application/json,text/html;q=0.9,*/*;q=0.8"}
|
|
6
|
+
|
|
7
|
+
def fetch_json(url):
|
|
8
|
+
req = urllib.request.Request(url, headers=HEADERS)
|
|
9
|
+
with urllib.request.urlopen(req, timeout=25) as r:
|
|
10
|
+
return json.load(r)
|
|
11
|
+
|
|
12
|
+
def fetch_text(url):
|
|
13
|
+
req = urllib.request.Request(url, headers={"User-Agent":"Mozilla/5.0", "Accept":"text/html"})
|
|
14
|
+
with urllib.request.urlopen(req, timeout=25) as r:
|
|
15
|
+
return r.read().decode('utf-8', 'ignore')
|
|
16
|
+
|
|
17
|
+
def won(v):
|
|
18
|
+
if v in (None, ''): return '-'
|
|
19
|
+
try: return f"{int(float(v)):,}원"
|
|
20
|
+
except Exception: return str(v)
|
|
21
|
+
|
|
22
|
+
def resolve_region(region):
|
|
23
|
+
if not region: return None
|
|
24
|
+
url = 'https://www.daangn.com/kr/api/v1/regions/keyword?keyword=' + urllib.parse.quote(region)
|
|
25
|
+
data = fetch_json(url)
|
|
26
|
+
locs = data.get('locations') or []
|
|
27
|
+
if not locs: raise SystemExit(f'지역 후보 없음: {region}')
|
|
28
|
+
# Exact dong/name match first, then Seoul depth-3, then first candidate.
|
|
29
|
+
exact = [x for x in locs if region in (x.get('name'), x.get('name1'), x.get('name2'), x.get('name3'))]
|
|
30
|
+
seoul = [x for x in locs if x.get('name1') == '서울특별시' and x.get('depth') == 3]
|
|
31
|
+
sel = (exact or seoul or locs)[0]
|
|
32
|
+
return sel
|
|
33
|
+
|
|
34
|
+
def region_param(sel):
|
|
35
|
+
return urllib.parse.quote(f"{sel['name']}-{sel['id']}")
|
|
36
|
+
|
|
37
|
+
def absolute(href):
|
|
38
|
+
if not href: return ''
|
|
39
|
+
if href.startswith('http'): return href
|
|
40
|
+
return 'https://www.daangn.com' + href
|
|
41
|
+
|
|
42
|
+
def print_json(obj):
|
|
43
|
+
print(json.dumps(obj, ensure_ascii=False, indent=2))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cmd_search(args):
|
|
47
|
+
params = []
|
|
48
|
+
effective = None
|
|
49
|
+
path = '/kr/buy-sell/'
|
|
50
|
+
if args.region:
|
|
51
|
+
effective = resolve_region(args.region)
|
|
52
|
+
path = '/kr/buy-sell/all/'
|
|
53
|
+
params.append(('in', f"{effective['name']}-{effective['id']}"))
|
|
54
|
+
params.append(('search', args.keyword))
|
|
55
|
+
if args.only_on_sale: params.append(('only_on_sale','true'))
|
|
56
|
+
params.append(('_data','routes/kr.buy-sell._index'))
|
|
57
|
+
url = 'https://www.daangn.com' + path + '?' + urllib.parse.urlencode(params)
|
|
58
|
+
data = fetch_json(url)
|
|
59
|
+
arr = (((data.get('allPage') or {}).get('fleamarketArticles')) or [])[:args.limit]
|
|
60
|
+
print_json({
|
|
61
|
+
'source': url,
|
|
62
|
+
'effective_region': effective or data.get('region'),
|
|
63
|
+
'count': len(arr),
|
|
64
|
+
'items': [{
|
|
65
|
+
'title': a.get('title'), 'price': a.get('price'), 'price_text': won(a.get('price')),
|
|
66
|
+
'region': (a.get('region') or {}).get('name'), 'status': a.get('status'),
|
|
67
|
+
'url': absolute(a.get('href') or a.get('webUrl')),
|
|
68
|
+
} for a in arr]
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
def cmd_detail(args):
|
|
72
|
+
u = args.url.rstrip('/') + '/?_data=routes%2Fkr.buy-sell.%24buy_sell_id'
|
|
73
|
+
data = fetch_json(u); p = data.get('product') or data.get('article') or data
|
|
74
|
+
print_json({'source': u, 'product': p})
|
|
75
|
+
|
|
76
|
+
p=argparse.ArgumentParser(description='Daangn used-goods read-only search/detail')
|
|
77
|
+
sub=p.add_subparsers(dest='cmd', required=True)
|
|
78
|
+
s=sub.add_parser('search'); s.add_argument('keyword'); s.add_argument('--region'); s.add_argument('--limit',type=int,default=10); s.add_argument('--only-on-sale',action='store_true',default=True); s.set_defaults(func=cmd_search)
|
|
79
|
+
d=sub.add_parser('detail'); d.add_argument('url'); d.set_defaults(func=cmd_detail)
|
|
80
|
+
args=p.parse_args(); args.func(args)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daangn-used-goods-search",
|
|
3
|
+
"description": "당근 중고거래 공개 웹 데이터로 매물을 검색하고, 돌쇠에서는 공식 계정 표면에서 찜·채팅·거래 준비까지 진행한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"proxy",
|
|
6
|
+
"action:commerce"
|
|
7
|
+
],
|
|
8
|
+
"frontmatter": "name: daangn-used-goods-search\ndescription: 당근 중고거래 공개 웹 데이터로 매물을 검색하고, 돌쇠에서는 공식 계정 표면에서 찜·채팅·거래 준비까지 진행한다.\nlicense: MIT\nmetadata:\n category: marketplace\n locale: ko-KR\n phase: v1"
|
|
9
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Daishin Report Search
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
대신증권 리포트 HTML 미러(`jay-jo-0/github_pages_repo`)에서 최신 리포트 목록을 찾고, 특정 리포트의 원문 텍스트·제목·헤딩·Rating/Target 표·원문 링크를 에이전트가 재사용하기 쉬운 JSON으로 반환한다.
|
|
6
|
+
|
|
7
|
+
이 스킬은 투자 조언, 매매 자동화, 추천을 하지 않는다. 공개 HTML 리포트를 읽어 요약 가능한 자료로 정리하는 조회 전용 스킬이다.
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
- "대신증권 최신 리포트 보여줘"
|
|
12
|
+
- "대신증권 반도체 리포트 찾아줘"
|
|
13
|
+
- "20260511082352 리포트 원문과 설명 페이지를 읽어줘"
|
|
14
|
+
- "대신증권 리포트 목록을 에이전트가 쓰기 좋은 JSON으로 줘"
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- 인터넷 연결
|
|
19
|
+
- Node.js 18+
|
|
20
|
+
- 이 저장소의 `daishin-report-search` npm package 또는 동일 로직
|
|
21
|
+
|
|
22
|
+
## Public access path discovered
|
|
23
|
+
|
|
24
|
+
### Primary source: GitHub recursive tree API
|
|
25
|
+
|
|
26
|
+
- list endpoint: `https://api.github.com/repos/jay-jo-0/github_pages_repo/git/trees/main?recursive=1`
|
|
27
|
+
- selected paths: repository-root files matching `YYYYMMDDHHMMSS.html`
|
|
28
|
+
- optional companion paths: `YYYYMMDDHHMMSS_explain.html`
|
|
29
|
+
- detail raw HTML: `https://raw.githubusercontent.com/Jay-jo-0/github_pages_repo/main/<path>`
|
|
30
|
+
- browser detail URL: `https://jay-jo-0.github.io/github_pages_repo/<path>`
|
|
31
|
+
- reason selected: the sample GitHub Pages URL maps directly to a public GitHub repository. The recursive tree API exposes all timestamped HTML filenames without relying on a brittle directory listing screen scrape. Raw GitHub URLs provide stable unauthenticated detail fetches.
|
|
32
|
+
|
|
33
|
+
### Fallback source: GitHub contents API for an exact file
|
|
34
|
+
|
|
35
|
+
- exact-file endpoint: `https://api.github.com/repos/jay-jo-0/github_pages_repo/contents/<path>?ref=main`
|
|
36
|
+
- used automatically for a known timestamp when the raw detail URL is unavailable; it also provides GitHub content metadata for manual diagnostics.
|
|
37
|
+
|
|
38
|
+
No `k-skill-proxy` route is used because the upstream is public and does not require an API key.
|
|
39
|
+
|
|
40
|
+
## Workflow
|
|
41
|
+
|
|
42
|
+
### 1. List latest reports
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const { listReports } = require("daishin-report-search")
|
|
46
|
+
|
|
47
|
+
const result = await listReports({
|
|
48
|
+
limit: 10,
|
|
49
|
+
query: "반도체", // optional; matches title/headings/detail text
|
|
50
|
+
maxInspect: 100, // optional query crawl budget among newest pages
|
|
51
|
+
githubToken: process.env.GITHUB_TOKEN // optional; raises GitHub API limits when caller has one
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
console.log(result.items)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
CLI:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node packages/daishin-report-search/src/cli.js --limit 10
|
|
61
|
+
node packages/daishin-report-search/src/cli.js 반도체 --limit 5 --max-inspect 100
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Return each item with:
|
|
65
|
+
|
|
66
|
+
- `id` (`YYYYMMDDHHMMSS`)
|
|
67
|
+
- `date`, `time`, `timestamp` (filename-derived KST timestamp)
|
|
68
|
+
- `title`
|
|
69
|
+
- `headings`
|
|
70
|
+
- `excerpt`
|
|
71
|
+
- `ratingTargets` when a Rating/Target table is present
|
|
72
|
+
- `pageUrl`, `rawUrl`, `apiUrl`
|
|
73
|
+
- `hasExplain`, `explainUrl` when a companion explanation page exists
|
|
74
|
+
|
|
75
|
+
### 2. Fetch one report
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
const { fetchReport } = require("daishin-report-search")
|
|
79
|
+
|
|
80
|
+
const report = await fetchReport("20260511082352", {
|
|
81
|
+
includeExplain: true
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
console.log(report.title)
|
|
85
|
+
console.log(report.text)
|
|
86
|
+
console.log(report.explain?.text)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
CLI:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
node packages/daishin-report-search/src/cli.js --id 20260511082352 --include-explain
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. Summarize conservatively
|
|
96
|
+
|
|
97
|
+
When answering a user, show:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
- 제목: ...
|
|
101
|
+
게시 추정 시각: 2026-05-11 08:23:52 KST (파일명 기준)
|
|
102
|
+
주요 헤딩: ...
|
|
103
|
+
Rating/Target: ... (있는 경우)
|
|
104
|
+
원문: https://jay-jo-0.github.io/github_pages_repo/...
|
|
105
|
+
설명 페이지: ... (있는 경우)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Always state that the timestamp is filename-derived and that report contents can change in the public mirror.
|
|
109
|
+
|
|
110
|
+
## Fallback order
|
|
111
|
+
|
|
112
|
+
1. GitHub recursive tree API → filter timestamped root HTML files → sort newest filename first → fetch raw detail HTML for selected/latest candidates.
|
|
113
|
+
2. If a query is present, inspect newer candidates up to `maxInspect` until enough matches are found or the budget is exhausted; return a warning if the budget is exhausted.
|
|
114
|
+
3. For a known id, fetch raw detail directly. If explanation is requested, fetch `<id>_explain.html`; if absent, return the original report plus a warning.
|
|
115
|
+
4. If the tree endpoint is truncated, blocked, rate-limited, or changed, report that as a source warning/failure instead of guessing hidden pages.
|
|
116
|
+
5. For a known id, if the raw detail URL fails, fall back to the GitHub contents API for that exact file path. Explanation pages use the same exact-file fallback but remain optional and return a warning if unavailable.
|
|
117
|
+
6. If the caller has authenticated GitHub access, pass `githubToken` / `githubHeaders` in library calls or set `DAISHIN_GITHUB_TOKEN` / `GITHUB_TOKEN` for the CLI; these credentials are scoped to `api.github.com` requests and are not sent to raw detail URLs. Do not require or proxy a token by default.
|
|
118
|
+
|
|
119
|
+
## Done when
|
|
120
|
+
|
|
121
|
+
- Latest report rows or a specific report are returned with direct source URLs.
|
|
122
|
+
- Query and limit were applied or explicitly left broad.
|
|
123
|
+
- Explanation pages were included only when requested or when listing metadata shows they exist.
|
|
124
|
+
- Empty results and upstream warnings are disclosed.
|
|
125
|
+
|
|
126
|
+
## Failure modes
|
|
127
|
+
|
|
128
|
+
- GitHub unauthenticated API rate limits can return 403/429; latest/search returns empty `items` plus `source.error.kind = "rate_limit"` and rate-limit reset metadata when GitHub exposes it. Retry later or use caller-supplied authenticated GitHub access if appropriate.
|
|
129
|
+
- The repository path or branch can change; then tree/raw URLs will fail.
|
|
130
|
+
- The tree response could become truncated; in that case the latest-list completeness is not guaranteed.
|
|
131
|
+
- HTML structure can change; title/headings/table extraction may be partial, but URLs and raw text fallback should still be returned when available.
|
|
132
|
+
- Some pages may not be authored by Daishin even though they are in the issue-scoped public mirror. Do not infer provenance beyond page title/content.
|
|
133
|
+
|
|
134
|
+
## Notes
|
|
135
|
+
|
|
136
|
+
- Read-only lookup only; no login, trading, order placement, recommendation, or investment advice.
|
|
137
|
+
- Do not scrape private Daishin services or bypass CAPTCHA/login walls.
|
|
138
|
+
- No secrets or API keys are required. Optional GitHub tokens are caller-owned, used only when explicitly supplied via options or environment, and scoped to GitHub API hosts.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daishin-report-search",
|
|
3
|
+
"description": "대신증권 리포트 GitHub Pages 미러에서 최신 HTML 리포트 목록과 원문/설명 페이지를 조회한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"proxy",
|
|
6
|
+
"vault",
|
|
7
|
+
"lookup"
|
|
8
|
+
],
|
|
9
|
+
"frontmatter": "name: daishin-report-search\ndescription: 대신증권 리포트 GitHub Pages 미러에서 최신 HTML 리포트 목록과 원문/설명 페이지를 조회한다.\nlicense: MIT\nmetadata:\n category: finance\n locale: ko-KR\n phase: v1"
|
|
10
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Daiso Product Search
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
다이소몰 공식 검색/매장/재고 표면을 사용해 **특정 다이소 매장의 상품 재고**를 확인한다.
|
|
6
|
+
|
|
7
|
+
- 공식 매장 검색으로 매장 코드를 찾는다.
|
|
8
|
+
- 공식 상품 검색으로 상품 후보를 찾는다.
|
|
9
|
+
- 공식 매장 픽업 재고 표면으로 해당 매장의 재고를 확인한다.
|
|
10
|
+
- **공식 표면이 매장 내 진열 위치를 주지 않으면 재고 중심으로만 답한다.**
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
- "강남역2호점에 리들샷 있어?"
|
|
15
|
+
- "다이소 특정 매장 재고 확인해줘"
|
|
16
|
+
- "이 상품 어느 매장에 있는지 확인해줘"
|
|
17
|
+
- "다이소 매장명 주면 그 매장 재고 봐줘"
|
|
18
|
+
|
|
19
|
+
## When not to use
|
|
20
|
+
|
|
21
|
+
- 매장명도 상품명도 전혀 없는 상태에서 바로 재고를 단정해야 하는 경우
|
|
22
|
+
- 결제/주문/픽업 예약까지 자동화해야 하는 경우
|
|
23
|
+
- 비공식 크롤링 결과를 우선해야 하는 경우
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
- 인터넷 연결
|
|
28
|
+
- `node` 18+
|
|
29
|
+
- 이 저장소의 `daiso-product-search` package 또는 동일 로직
|
|
30
|
+
|
|
31
|
+
## Required inputs
|
|
32
|
+
|
|
33
|
+
### 1. Ask the store name first if it is missing
|
|
34
|
+
|
|
35
|
+
매장명이 없으면 바로 조회하지 말고 먼저 물어본다.
|
|
36
|
+
|
|
37
|
+
- 권장 질문: `어느 다이소 매장을 확인할까요? 매장명(예: 강남역2호점)을 알려주세요.`
|
|
38
|
+
- 비슷한 매장이 여러 개면: `후보 매장이 여러 개예요. 정확한 매장명을 하나만 골라주세요.`
|
|
39
|
+
|
|
40
|
+
### 2. Ask the product name or keyword if it is missing
|
|
41
|
+
|
|
42
|
+
상품명/검색어도 반드시 필요하다.
|
|
43
|
+
|
|
44
|
+
- 권장 질문: `찾을 상품명이나 검색어도 알려주세요. 예: VT 리들샷 100`
|
|
45
|
+
- 너무 넓으면: `검색어가 너무 넓어요. 브랜드나 용량까지 같이 알려주세요.`
|
|
46
|
+
|
|
47
|
+
## Official Daiso Mall surfaces
|
|
48
|
+
|
|
49
|
+
- store keyword catalog: `https://www.daisomall.co.kr/api/ms/msg/selStrSrchKeyword`
|
|
50
|
+
- store search: `https://www.daisomall.co.kr/api/ms/msg/selStr`
|
|
51
|
+
- store detail: `https://www.daisomall.co.kr/api/dl/dla-api/selStrInfo`
|
|
52
|
+
- product search summary: `https://www.daisomall.co.kr/ssn/search/Search`
|
|
53
|
+
- product search list: `https://www.daisomall.co.kr/ssn/search/SearchGoods`
|
|
54
|
+
- product summary list: `https://www.daisomall.co.kr/ssn/search/GoodsMummResult`
|
|
55
|
+
- auth (비로그인 JWT 발급): `https://www.daisomall.co.kr/api/auth/request`
|
|
56
|
+
- store pickup stock: `https://www.daisomall.co.kr/api/pd/pdh/selStrPkupStck` ← **인증 필요**
|
|
57
|
+
- pickup eligibility fallback: `https://www.daisomall.co.kr/api/ms/msg/selPkupStr`
|
|
58
|
+
- optional online stock cross-check: `https://www.daisomall.co.kr/api/pdo/selOnlStck`
|
|
59
|
+
|
|
60
|
+
## Workflow
|
|
61
|
+
|
|
62
|
+
### 1. Resolve the store
|
|
63
|
+
|
|
64
|
+
공식 매장 검색 API로 매장명을 먼저 해결한다.
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
const { searchStores } = require("daiso-product-search")
|
|
68
|
+
|
|
69
|
+
const storeResult = await searchStores("강남역2호점", {
|
|
70
|
+
limit: 5
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
console.log(storeResult.items)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
매장 후보가 여러 개면 상위 2~3개만 보여주고 다시 확인받는다.
|
|
77
|
+
|
|
78
|
+
### 2. Resolve the product
|
|
79
|
+
|
|
80
|
+
공식 `SearchGoods` 표면으로 상품 후보를 찾는다.
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const { searchProducts } = require("daiso-product-search")
|
|
84
|
+
|
|
85
|
+
const productResult = await searchProducts("VT 리들샷 100", {
|
|
86
|
+
limit: 10
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
console.log(productResult.items)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
상품 후보가 여러 개면 아래 우선순위로 짧게 정리한다.
|
|
93
|
+
|
|
94
|
+
- 정확히 일치하는 이름
|
|
95
|
+
- 브랜드 + 용량/호수까지 포함된 이름
|
|
96
|
+
- 리뷰 수/검색 점수가 높은 후보
|
|
97
|
+
- 온라인 재고 교차 확인이 필요하면 후보의 `onldPdNo` 를 함께 보존한다
|
|
98
|
+
|
|
99
|
+
### 3. Check the store pickup stock
|
|
100
|
+
|
|
101
|
+
`selStrPkupStck`는 `Authorization` 헤더 없이 호출하면 **403**을 반환한다.
|
|
102
|
+
로그인 없이 `/api/auth/request`로 비로그인 JWT를 발급받아 AES-CBC로 암호화한 뒤 Bearer 헤더로 전달한다.
|
|
103
|
+
|
|
104
|
+
**Bearer 토큰 생성 방법:**
|
|
105
|
+
|
|
106
|
+
1. `GET /api/auth/request` → 응답 바디: JWT 평문, 응답 헤더 `x-dm-uid` 보존 (유효 30초)
|
|
107
|
+
2. 랜덤 16바이트 IV 생성 후 JWT를 AES-128-CBC / PKCS7 / 키 `"PRE_AUTH_ENC_KEY"`로 암호화
|
|
108
|
+
3. `bearer = base64(IV) + base64(암호문)` 으로 조합 후 `Authorization: Bearer <bearer>`, `X-DM-UID: <uid>` 헤더로 전달
|
|
109
|
+
|
|
110
|
+
바디는 `{pdNo, strCd}` 쌍 배열로 여러 매장을 한 번에 조회할 수 있다.
|
|
111
|
+
응답의 `stck` 필드가 `"0"` 또는 빈 값이면 재고 없음.
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const { getStorePickupStock } = require("daiso-product-search")
|
|
115
|
+
|
|
116
|
+
const stock = await getStorePickupStock({
|
|
117
|
+
pdNo: "1049275",
|
|
118
|
+
strCd: "10224"
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
console.log(stock)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 4. Use the end-to-end helper when both names are already known
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
const { lookupStoreProductAvailability } = require("daiso-product-search")
|
|
128
|
+
|
|
129
|
+
const result = await lookupStoreProductAvailability({
|
|
130
|
+
storeQuery: "강남역2호점",
|
|
131
|
+
productQuery: "VT 리들샷 100"
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
console.log(result.selectedStore)
|
|
135
|
+
console.log(result.selectedProduct)
|
|
136
|
+
console.log(result.pickupStock)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 5. Respond conservatively
|
|
140
|
+
|
|
141
|
+
응답은 짧고 명확하게 정리한다.
|
|
142
|
+
|
|
143
|
+
- 매장명
|
|
144
|
+
- 상품명
|
|
145
|
+
- 매장 재고 수량 또는 재고 없음
|
|
146
|
+
- 필요하면 온라인 재고 참고값
|
|
147
|
+
- **공식 표면이 매장 내 진열 위치를 주지 않으면 `공식 표면에서는 매장 재고까지만 확인된다`고 분명히 말한다.**
|
|
148
|
+
|
|
149
|
+
## Done when
|
|
150
|
+
|
|
151
|
+
- 매장명과 상품명이 모두 확인되었다.
|
|
152
|
+
- 공식 표면으로 매장 후보와 상품 후보를 찾았다.
|
|
153
|
+
- 공식 매장 재고 결과를 최소 1회 반환했다.
|
|
154
|
+
- 위치 정보가 없으면 없다고 분명히 고지했다.
|
|
155
|
+
|
|
156
|
+
## Failure modes
|
|
157
|
+
|
|
158
|
+
- 매장명이 너무 넓으면 같은 상권의 여러 지점이 동시에 잡힐 수 있다.
|
|
159
|
+
- 상품명이 너무 넓으면 다른 용량/호수 후보가 많이 섞일 수 있다.
|
|
160
|
+
- 공식 재고는 시점 차이로 실제 방문 시 수량이 달라질 수 있다.
|
|
161
|
+
- 현재 확인된 공식 표면은 **매장 내 aisle/진열 위치**를 직접 주지 않을 수 있다.
|
|
162
|
+
- `selStrPkupStck` 403 → `/api/auth/request` 재호출 후 Bearer를 새로 빌드해 재시도한다.
|
|
163
|
+
- Bearer 재시도 후에도 401/403이면 재고 수량은 `retrievalStatus: "blocked"` 로 표시하고, `selPkupStr` 기반 `pickupEligibility`(픽업 가능 여부)만 보조 정보로 제공한다.
|
|
164
|
+
|
|
165
|
+
## Notes
|
|
166
|
+
|
|
167
|
+
- 조회형 스킬이다.
|
|
168
|
+
- 공식 표면 우선 원칙을 유지한다.
|
|
169
|
+
- 공식 표면이 위치를 주지 않으면 억지 추정을 하지 않는다.
|
|
170
|
+
- 인증 키(`PRE_AUTH_ENC_KEY`)는 JS 번들에 하드코딩되어 있으며 변경될 수 있다.
|
|
171
|
+
- `selStrPkupStck` 호출 시: `/api/auth/request` 호출 후 Bearer를 만들어 시도한다.
|
|
172
|
+
- fallback order: Bearer 재고 조회 → 401/403 시 토큰 재발급 후 1회 재시도 → 구조화된 blocked 재고 → 선택적 `selPkupStr` 픽업 가능 여부.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daiso-product-search",
|
|
3
|
+
"description": "Look up Daiso products by store name and product keyword using official Daiso Mall store/search/stock surfaces. Use when the user wants to know whether a product is available at a specific Daiso store. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"action:commerce"
|
|
6
|
+
],
|
|
7
|
+
"frontmatter": "name: daiso-product-search\ndescription: Look up Daiso products by store name and product keyword using official Daiso Mall store/search/stock surfaces. Use when the user wants to know whether a product is available at a specific Daiso store. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: retail\n locale: ko-KR\n phase: v1"
|
|
8
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Danawa Price Search
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
다나와의 로그인 없는 공개 검색/가격비교 표면을 읽기 전용으로 호출해 한국 쇼핑몰 가격을 비교한다.
|
|
6
|
+
|
|
7
|
+
- 상품명/검색어로 다나와 상품 후보와 `pcode`를 찾는다.
|
|
8
|
+
- 선택한 상품의 쇼핑몰별 오퍼를 조회한다.
|
|
9
|
+
- 상품가만이 아니라 배송비 포함 실구매가, 무료배송 여부, 카드 할인가, 무이자 할부 문구를 함께 정리한다.
|
|
10
|
+
- 구매, 로그인, 장바구니, 찜, 주문 액션은 하지 않는다.
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
- "다나와에서 에어팟 최저가 찾아줘"
|
|
15
|
+
- "다나와 가격비교로 쇼핑몰별 가격 비교해줘"
|
|
16
|
+
- "무료배송인지, 카드 할인까지 보면 어디가 제일 싸?"
|
|
17
|
+
- "무이자 할부 붙은 최저가도 같이 봐줘"
|
|
18
|
+
|
|
19
|
+
## When not to use
|
|
20
|
+
|
|
21
|
+
- 실제 구매/주문/결제/로그인이 필요한 경우
|
|
22
|
+
- 회원 전용 쿠폰, 개인화 포인트, 앱 전용 혜택을 확정해야 하는 경우
|
|
23
|
+
- 대량 모니터링이나 고빈도 크롤링을 해야 하는 경우
|
|
24
|
+
- CAPTCHA, 접근 차단, fingerprint 우회를 해야 하는 경우
|
|
25
|
+
|
|
26
|
+
## Required inputs
|
|
27
|
+
|
|
28
|
+
상품명 또는 검색어가 필요하다. 검색어가 넓으면 브랜드, 모델명, 용량, 색상, 자급제/통신사 여부 등을 추가로 물어본다.
|
|
29
|
+
|
|
30
|
+
권장 질문:
|
|
31
|
+
|
|
32
|
+
> 찾을 다나와 상품명이나 모델명을 알려주세요. 예: 갤럭시 S25 울트라 256GB 자급제, 에어팟 프로 2세대 USB-C
|
|
33
|
+
|
|
34
|
+
## Public surfaces
|
|
35
|
+
|
|
36
|
+
현재 구현은 인증 없는 공개 표면만 사용한다.
|
|
37
|
+
|
|
38
|
+
- 검색 페이지: `https://search.danawa.com/dsearch.php?query=...`
|
|
39
|
+
- 상품 상세 페이지: `https://prod.danawa.com/info/?pcode=...`
|
|
40
|
+
- 가격비교 AJAX: `https://prod.danawa.com/info/ajax/getAllPriceCompareMallList.ajax.php`
|
|
41
|
+
|
|
42
|
+
AJAX endpoint는 HTML fragment를 반환한다. helper는 `.diff_item`, 쇼핑몰 로고 `alt`, `em.prc_c`/`em.prc_t`, 배송 문구, 결제조건 배지(`.ico.cash`/`.ico.point`/`.ico.coupon`/`.ico.discount`/`.ico.card`/`.ico.membership` 등), 카드 할인 라인, 무이자 할부 레이어, 다나와 bridge link를 파싱한다.
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
스킬 디렉터리에서 실행한다.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python scripts/danawa_search.py search "에어팟 프로 2세대" --limit 8
|
|
50
|
+
python scripts/danawa_search.py offers 28208783 --limit 10
|
|
51
|
+
python scripts/danawa_search.py compare "에어팟 프로 2세대" --limit 5 --offers 5
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
helper는 JSON만 출력한다. 결과를 확인한 뒤 사용자에게는 한국어 표와 짧은 결론으로 정리한다.
|
|
55
|
+
|
|
56
|
+
## Output shape
|
|
57
|
+
|
|
58
|
+
### `search`
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"query": "...",
|
|
63
|
+
"source_url": "...",
|
|
64
|
+
"count": 0,
|
|
65
|
+
"items": []
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
각 `items[]` 주요 필드:
|
|
70
|
+
|
|
71
|
+
- `pcode`
|
|
72
|
+
- `title`
|
|
73
|
+
- `price`, `price_text`
|
|
74
|
+
- `mall_text`
|
|
75
|
+
- `url`
|
|
76
|
+
- `image_url`
|
|
77
|
+
- `spec`
|
|
78
|
+
|
|
79
|
+
### `offers`
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"pcode": "...",
|
|
84
|
+
"title": "...",
|
|
85
|
+
"source_url": "...",
|
|
86
|
+
"count": 0,
|
|
87
|
+
"normal_count": 0,
|
|
88
|
+
"conditional_count": 0,
|
|
89
|
+
"offers": [],
|
|
90
|
+
"meta": { "sort": "total_price" }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`offers[]`는 **배송비 포함 실구매가(`total_price`) 오름차순**으로 정렬된다. `count` / `normal_count` / `conditional_count`는 `limit` 적용 후 실제 반환된 `offers[]` window 기준이다. 결제조건(현금/쿠폰/포인트/할인/특정카드/멤버십 한정)이 붙은 row도 같은 정렬에 그대로 참여한다 — 가장 싸면 1위로 올라온다. 결제조건은 분리 그룹이나 추가 필터링 없이 row 단위 `payment_badges` / `payment_condition_types` / `payment_condition_label` / `cash_only` / `point_only` / `coupon_only` / `card_only_badge` / `discount_badge` / `membership_badge` / `is_conditional_price` 필드로 노출한다. 호출자는 사용자의 결제 수단에 따라 직접 판단한다.
|
|
95
|
+
|
|
96
|
+
각 `offers[]` 주요 필드:
|
|
97
|
+
|
|
98
|
+
- `mall`
|
|
99
|
+
- `price`, `price_text`
|
|
100
|
+
- `shipping`
|
|
101
|
+
- `is_free_shipping`
|
|
102
|
+
- `shipping_fee`
|
|
103
|
+
- `total_price`, `total_price_text`
|
|
104
|
+
- `card_price`, `card_price_text`
|
|
105
|
+
- `card_name`
|
|
106
|
+
- `card_discount`, `card_discount_text`
|
|
107
|
+
- `installment`
|
|
108
|
+
- `installment_detail`
|
|
109
|
+
- `payment_badges` — Danawa가 가격 옆에 노출한 결제조건 배지의 표시 라벨 목록. 배지 텍스트가 비어 있고 `.ico.cash`처럼 클래스만 있는 경우도 정규화 라벨을 합성한다 (예: `["현금"]`, `["포인트"]`, `["쿠폰"]`, `["카드"]`, `["할인"]`, `["멤버십"]`)
|
|
110
|
+
- `payment_condition_types` — 화이트리스트 배지를 정규화한 조건 타입 목록 (`cash`/`point`/`coupon`/`card`/`discount`/`membership`)
|
|
111
|
+
- `payment_condition_label` — 사용자 응답용 결제조건 라벨 (예: `현금`, `할인`, `멤버십`, 복수 조건이면 `현금, 할인`)
|
|
112
|
+
- `cash_only` — 현금 결제 전용가
|
|
113
|
+
- `point_only` — 포인트 차감 적용가
|
|
114
|
+
- `coupon_only` — 쿠폰 적용가
|
|
115
|
+
- `card_only_badge` — 특정 카드 한정 노출가
|
|
116
|
+
- `discount_badge` — 할인 조건 배지 노출가
|
|
117
|
+
- `membership_badge` — 멤버십 조건 배지 노출가
|
|
118
|
+
- `is_conditional_price` — `payment_condition_types`가 하나 이상 있으면 True. **일반 결제가가 아니므로 카드 일반 결제 시 가격이 다르거나 불가능할 수 있음**
|
|
119
|
+
- `url`
|
|
120
|
+
|
|
121
|
+
항상 무료배송 여부, 배송비 포함 실구매가, 카드별 할인 가격, 무이자 할부 문구, **그리고 `payment_badges`/`payment_condition_label`/`is_conditional_price`를 함께 확인한다.** 조건부 가격을 일반가처럼 1위로 노출하면 비교 결과가 거짓이 된다.
|
|
122
|
+
|
|
123
|
+
### `compare`
|
|
124
|
+
|
|
125
|
+
`compare`는 검색 결과를 먼저 가져온 뒤 각 후보 상품에 대해 `offers[]`를 best-effort로 붙인다. 검색 결과가 애매하면 상위 후보의 제목과 `pcode`를 먼저 보여주고 선택을 요청한다.
|
|
126
|
+
|
|
127
|
+
## Response style
|
|
128
|
+
|
|
129
|
+
Discord/Telegram/chat 응답에서는 표 형식을 우선한다.
|
|
130
|
+
|
|
131
|
+
```md
|
|
132
|
+
| 순위 | 판매처 | 상품가 | 결제조건 | 배송 | 실구매가 | 카드할인가 | 무이자 | 링크 |
|
|
133
|
+
|---:|---|---:|---|---|---:|---:|---|---|
|
|
134
|
+
| 1 | 킴스클럽 | 979,000원 | **현금 전용** | 유/무료 | 979,000원 | - | - | 보기 |
|
|
135
|
+
| 2 | 롯데ON | 1,073,890원 | 일반 | 무료배송 | 1,073,890원 | - | - | 보기 |
|
|
136
|
+
| 3 | G마켓 | 1,089,590원 | 일반 | 무료배송 | 1,089,590원 | - | 최대 24개월 | 보기 |
|
|
137
|
+
| 4 | 옥션 | 1,121,780원 | **쿠폰 적용가** | 무료배송 | 1,121,780원 | 우리카드 303,720원 | 최대 24개월 | 보기 |
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
정렬 기준:
|
|
141
|
+
|
|
142
|
+
1. **`total_price` 오름차순 단일 기준.** 결제조건(현금/쿠폰/포인트/할인/특정카드/멤버십 한정)이 붙은 row도 같은 정렬에 그대로 참여한다 — 가장 싸면 1위로 올라온다. 결제조건은 분리 그룹화하지 않고 표의 "결제조건" 컬럼에 행별로 표시한다 (`payment_condition_label`이 있으면 그 값을 우선 표시, 없으면 "일반"; 세부 매핑은 `cash` → "현금 전용", `coupon` → "쿠폰 적용가", `point` → "포인트 적용가", `card` → 카드명/카드 조건, `discount` → "할인 조건", `membership` → "멤버십 조건"). 사용자는 자기 결제 수단에 따라 직접 판단한다.
|
|
143
|
+
2. `card_price`가 있고 카드 적용 시 승자가 바뀌면 표 아래에 "카드 기준 최저가"를 별도로 적는다.
|
|
144
|
+
3. 무이자 할부는 결제 조건이 달라질 수 있으므로 Danawa 노출 문구 기준이라고 밝힌다.
|
|
145
|
+
4. 1위가 조건부 가격이면 요약 문장에 결제수단 단서를 짧게 덧붙인다. 예: "**최저 실구매가: 킴스클럽 979,000원 / 현금 결제 한정**, 카드 결제 기준 최저가는 롯데ON 1,073,890원". 카드 결제 가능한 최저가도 같이 알려 사용자가 결제수단별 결과를 한 번에 비교할 수 있게 한다.
|
|
146
|
+
|
|
147
|
+
요약 예시:
|
|
148
|
+
|
|
149
|
+
```md
|
|
150
|
+
최저 실구매가: G마켓 217,950원 / 무료배송
|
|
151
|
+
카드 기준 최저가: 옥션 우리카드 303,720원
|
|
152
|
+
무이자: G마켓·옥션 최대 24개월 표기
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
카드 할인 markup이 없으면 "카드 할인가 표기 없음"이라고 쓰고, 체크아웃 할인 자체가 없다고 단정하지 않는다.
|
|
156
|
+
|
|
157
|
+
## Workflow
|
|
158
|
+
|
|
159
|
+
1. 검색어를 확인한다.
|
|
160
|
+
2. `python scripts/danawa_search.py search "<검색어>" --limit 5`로 후보를 확인한다.
|
|
161
|
+
3. 후보가 명확하면 해당 `pcode`로 `offers`를 실행한다.
|
|
162
|
+
4. 후보가 애매하면 상위 3~5개 상품명/가격/`pcode`를 보여주고 선택을 요청한다.
|
|
163
|
+
5. 오퍼는 **`total_price` 오름차순 단일 기준으로 정렬한다 (결제조건 분리 그룹화하지 않음).** 결제조건은 표의 "결제조건" 컬럼과 row 단위 플래그로만 표기하고, 1위가 현금/쿠폰가여도 그대로 1위로 노출한다.
|
|
164
|
+
6. 카드 할인가가 있으면 카드 기준 최저가도 별도 요약한다. 1위가 조건부 가격이면 "카드 결제 기준 최저가"도 요약 문장에 함께 적어 결제수단별 최저가를 한 번에 알게 한다.
|
|
165
|
+
7. 조회 시점 기준이며 가격/배송/카드 혜택은 변동될 수 있음을 짧게 덧붙인다.
|
|
166
|
+
|
|
167
|
+
## Failure modes
|
|
168
|
+
|
|
169
|
+
- 검색 결과가 0개면 검색어를 더 구체화한다.
|
|
170
|
+
- Danawa HTML/AJAX 구조가 바뀌면 selector가 깨져 `offers`가 비거나 필드가 누락될 수 있다.
|
|
171
|
+
- 다나와가 새로운 결제조건 배지 클래스나 문구를 도입하면 결제조건 배지 화이트리스트(`cash`/`point`/`coupon`/`discount`/`card`/`membership` 클래스, `현금`/`포인트`/`쿠폰`/`할인`/`카드`/`멤버십` 텍스트 키워드)와 `payment_condition_types`/`payment_condition_label` 매핑을 함께 갱신해야 한다.
|
|
172
|
+
- 검색 결과 가격과 오퍼 AJAX 가격은 갱신 시점·카드가·제휴 링크 기준 차이로 다를 수 있다.
|
|
173
|
+
- 카드 할인과 무이자 문구는 Danawa가 노출한 경우에만 확정적으로 보여준다.
|
|
174
|
+
- 공개 표면 기반이므로 고빈도 요청에는 throttling/backoff를 추가해야 한다.
|
|
175
|
+
- 접근 차단이나 CAPTCHA가 나오면 우회를 시도하지 말고 실패 모드로 보고한다.
|
|
176
|
+
|
|
177
|
+
## Done when
|
|
178
|
+
|
|
179
|
+
- 검색어 또는 모델명을 확인했다.
|
|
180
|
+
- 상품 후보를 최소 1개 이상 반환하거나, 반환 실패 이유를 설명했다.
|
|
181
|
+
- 쇼핑몰별 상품가, 배송비, 실구매가, 카드 할인가, 무이자 문구를 조회 시점 기준으로 정리했다.
|
|
182
|
+
- 사용자 응답은 표 형식으로 제공했다.
|
|
183
|
+
- 로그인/구매/차단 우회 범위를 벗어나지 않았다.
|