@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,146 @@
|
|
|
1
|
+
# Popbill all-service k-skill
|
|
2
|
+
|
|
3
|
+
## 제공 범위
|
|
4
|
+
|
|
5
|
+
`scripts/popbill_cli.py`는 아래 SDK service class를 모두 노출한다.
|
|
6
|
+
|
|
7
|
+
- `taxinvoice`: 전자세금계산서 발행/임시저장/수정/취소/국세청 전송/조회/인쇄 URL/인증서 확인/첨부/메일·문자·팩스 재전송
|
|
8
|
+
- `statement`: 전자명세서 등록/발행/수정/취소/조회/인쇄 URL/메일·문자·팩스 전송
|
|
9
|
+
- `cashbill`: 현금영수증 등록/발행/취소/역발행/조회/메일·문자·팩스 전송
|
|
10
|
+
- `message`: SMS/LMS/MMS 단건·대량 발송, 예약취소, 결과조회, 발신번호 관리 조회
|
|
11
|
+
- `kakao`: 알림톡/친구톡/브랜드메시지 템플릿·채널·발신번호 조회와 발송/예약취소/결과조회
|
|
12
|
+
- `fax`: 팩스 발송/예약취소/결과조회/발신번호 관리
|
|
13
|
+
- `closedown`: 휴폐업 조회
|
|
14
|
+
- `bizinfo`: 기업정보조회
|
|
15
|
+
- `easyfin-bank`: 계좌 등록/수집작업/내역조회 등 팝빌 계좌조회 서비스
|
|
16
|
+
- `account-check`: 예금주·계좌성명 조회
|
|
17
|
+
- `ht-taxinvoice`: 홈택스 전자세금계산서 수집 작업/검색/상세/XML
|
|
18
|
+
- `ht-cashbill`: 홈택스 현금영수증 수집 작업/검색/요약
|
|
19
|
+
|
|
20
|
+
모든 SDK method는 `call` 서브커맨드로 호출 가능하다. 안전한 조회는 바로 실행하고, 발행·전송·취소·삭제·계좌검증 등 상태 변경/과금 가능 작업은 `--yes-i-understand`가 있어야 실행된다. 운영환경 mutation은 추가로 `--no-test --allow-production`이 필요하다.
|
|
21
|
+
|
|
22
|
+
## 인증/시크릿
|
|
23
|
+
|
|
24
|
+
시크릿은 저장소에 넣지 않는다. k-skill 표준 resolution order를 따른다.
|
|
25
|
+
|
|
26
|
+
1. 이미 환경변수에 있으면 그대로 사용
|
|
27
|
+
2. 에이전트 vault(1Password/Bitwarden/macOS Keychain 등)를 쓰면 거기서 주입
|
|
28
|
+
3. `~/.config/k-skill/secrets.env` fallback, 권한 `0600`
|
|
29
|
+
4. 없으면 사용자에게 물어 위 경로에 저장
|
|
30
|
+
|
|
31
|
+
지원 환경변수:
|
|
32
|
+
|
|
33
|
+
```dotenv
|
|
34
|
+
KSKILL_POPBILL_LINK_ID=...
|
|
35
|
+
KSKILL_POPBILL_SECRET_KEY=...
|
|
36
|
+
KSKILL_POPBILL_CORP_NUM=...
|
|
37
|
+
KSKILL_POPBILL_USER_ID=
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
호환 alias도 읽는다: `POPBILL_LINKID`, `POPBILL_LINK_ID`, `POPBILL_SECRET_KEY`, `POPBILL_TEST_CORP_NUM`, `POPBILL_CORP_NUM`, `POPBILL_USER_ID`.
|
|
41
|
+
|
|
42
|
+
기본은 팝빌 테스트 환경이다. 운영환경은 명시적으로 `--no-test`를 붙여야 한다.
|
|
43
|
+
|
|
44
|
+
## 필수 사전조건
|
|
45
|
+
|
|
46
|
+
서비스별로 팝빌 계정 준비가 다르다.
|
|
47
|
+
|
|
48
|
+
- 전자세금계산서: 테스트/운영 환경 각각 공동인증서 등록 필요
|
|
49
|
+
- 홈택스 수집: 홈택스 수집용 인증서/정액제 상태 필요
|
|
50
|
+
- 문자: 발신번호 사전 등록 필요
|
|
51
|
+
- 카카오톡/알림톡: 비즈니스 채널, 발신번호, 승인 템플릿 필요
|
|
52
|
+
- 팩스: 발신번호 등록 권장 또는 필요할 수 있음
|
|
53
|
+
- 계좌/예금주조회: 팝빌 서비스 신청·과금·조회 대상 입력값 필요
|
|
54
|
+
- 테스트와 운영 환경은 분리되어 있으며 테스트 데이터가 운영으로 이관되지 않는다
|
|
55
|
+
|
|
56
|
+
## 기본 명령
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 시크릿 파일 권한과 필수 env 존재 확인. 실제 secret 값은 출력하지 않는다.
|
|
60
|
+
uv run popbill/scripts/popbill_cli.py config-check
|
|
61
|
+
|
|
62
|
+
# 특정 SDK service method 목록과 approval 필요 여부 확인
|
|
63
|
+
uv run popbill/scripts/popbill_cli.py methods taxinvoice
|
|
64
|
+
uv run popbill/scripts/popbill_cli.py methods kakao
|
|
65
|
+
|
|
66
|
+
# 안전한 SDK 객체 템플릿 출력
|
|
67
|
+
uv run popbill/scripts/popbill_cli.py object-template taxinvoice
|
|
68
|
+
uv run popbill/scripts/popbill_cli.py object-template cashbill
|
|
69
|
+
|
|
70
|
+
# 테스트 환경에서 안전한 회원/파트너 잔액 smoke test
|
|
71
|
+
uv run popbill/scripts/popbill_cli.py health taxinvoice
|
|
72
|
+
|
|
73
|
+
# 휴폐업 단건 조회
|
|
74
|
+
uv run popbill/scripts/popbill_cli.py closedown-check --target-corp-num 123-45-67890
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Generic SDK call
|
|
78
|
+
|
|
79
|
+
전체 Popbill SDK 기능은 아래 형태로 호출한다.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv run popbill/scripts/popbill_cli.py call <service> <method> \
|
|
83
|
+
--args-json '["@corp", "arg2"]' \
|
|
84
|
+
--kwargs-json '{"UserID":"optional-user"}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`--args-json` 첫 번째 원소가 `"@corp"`이면 `--corp-num` 또는 `KSKILL_POPBILL_CORP_NUM` 값으로 대체된다. JSON 파일을 쓰려면 `@path/to/file.json` 형식을 사용한다. 중첩 SDK 객체는 `__kind__` 필드로 지정한다.
|
|
88
|
+
|
|
89
|
+
예: 전자세금계산서 임시저장 payload skeleton
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"__kind__": "taxinvoice",
|
|
94
|
+
"issueType": "정발행",
|
|
95
|
+
"chargeDirection": "정과금",
|
|
96
|
+
"purposeType": "청구",
|
|
97
|
+
"taxType": "과세",
|
|
98
|
+
"writeDate": "20260701",
|
|
99
|
+
"invoicerMgtKey": "TEST-20260701-001",
|
|
100
|
+
"invoicerCorpNum": "1234567890",
|
|
101
|
+
"invoicerCorpName": "공급자 상호",
|
|
102
|
+
"invoicerCEOName": "대표자",
|
|
103
|
+
"invoiceeType": "사업자",
|
|
104
|
+
"invoiceeCorpNum": "0987654321",
|
|
105
|
+
"invoiceeCorpName": "공급받는자 상호",
|
|
106
|
+
"invoiceeCEOName": "대표자",
|
|
107
|
+
"supplyCostTotal": "91",
|
|
108
|
+
"taxTotal": "9",
|
|
109
|
+
"totalAmount": "100",
|
|
110
|
+
"detailList": [
|
|
111
|
+
{"__kind__":"taxinvoice-detail", "serialNum":1, "itemName":"테스트 품목", "supplyCost":"91", "tax":"9"}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
발행·전송·삭제·취소처럼 되돌리기 어려운 동작은 사용자의 현재 턴 승인을 받은 뒤에만 실행한다.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv run popbill/scripts/popbill_cli.py call taxinvoice registIssue \
|
|
120
|
+
--args-json '["@corp", {"__kind__":"taxinvoice", "...":"..."}]' \
|
|
121
|
+
--yes-i-understand
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 안전 경계
|
|
125
|
+
|
|
126
|
+
- 실제 `LinkID`, `SecretKey`, 사업자번호, 인증서, 수신자 전화번호/이메일, 계좌번호를 저장소·PR·스크린샷에 넣지 않는다.
|
|
127
|
+
- 테스트 환경에서도 발행/전송/취소/삭제는 사용자 승인 없이 실행하지 않는다.
|
|
128
|
+
- 운영환경에서 발행/전송/취소/삭제는 `--no-test --allow-production --yes-i-understand`와 현재 턴 승인 둘 다 필요하다.
|
|
129
|
+
- 메시지·알림톡·팩스 발송은 테스트라도 수신자에게 도달하거나 과금될 수 있으므로 기본적으로 dry-run/템플릿/조회부터 한다.
|
|
130
|
+
- 계좌조회/예금주조회는 개인정보·금융정보이므로 목적·보관·파기 기준이 명확할 때만 실행한다.
|
|
131
|
+
|
|
132
|
+
## 실패 모드
|
|
133
|
+
|
|
134
|
+
- `missing link_id/secret_key/corp_num`: k-skill secrets 파일 또는 환경변수 설정 필요
|
|
135
|
+
- `permissions ... expected 0600`: `chmod 600 ~/.config/k-skill/secrets.env`
|
|
136
|
+
- `-99003008`: 해당 테스트/운영 환경에 연동회원 사업자번호가 없음
|
|
137
|
+
- `-10004000`: 전자세금계산서 공동인증서 미등록
|
|
138
|
+
- 문자/카카오/팩스 발신번호·채널·템플릿 미등록: 서비스별 관리 URL에서 등록 후 재시도
|
|
139
|
+
- 테스트 환경 성공이 운영환경 성공을 보장하지 않음: 운영 전환 신청과 운영 계정 세팅이 별도로 필요
|
|
140
|
+
|
|
141
|
+
## 완료 기준
|
|
142
|
+
|
|
143
|
+
- `config-check`가 필수 시크릿 존재를 확인한다.
|
|
144
|
+
- `methods <service>`로 필요한 SDK method가 확인된다.
|
|
145
|
+
- 가능한 경우 `health <service>` 또는 조회형 API가 테스트 환경에서 성공한다.
|
|
146
|
+
- mutation 작업은 payload를 파일로 저장해 사용자 확인을 받은 뒤 `--yes-i-understand`로 실행하고 응답 code/message를 보관한다.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = [
|
|
5
|
+
# "popbill==1.64.2",
|
|
6
|
+
# ]
|
|
7
|
+
# ///
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import datetime as dt
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import stat
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Callable
|
|
19
|
+
|
|
20
|
+
from popbill import PopbillException
|
|
21
|
+
from popbill_registry import OBJECT_CLASSES, SERVICE_CLASSES
|
|
22
|
+
from popbill_safety import is_dangerous_method
|
|
23
|
+
from popbill_templates import object_templates
|
|
24
|
+
|
|
25
|
+
SECRET_FILE = Path.home() / ".config" / "k-skill" / "secrets.env"
|
|
26
|
+
ENV_ALIASES = {
|
|
27
|
+
"link_id": ("KSKILL_POPBILL_LINK_ID", "POPBILL_LINKID", "POPBILL_LINK_ID"),
|
|
28
|
+
"secret_key": ("KSKILL_POPBILL_SECRET_KEY", "POPBILL_SECRET_KEY"),
|
|
29
|
+
"corp_num": ("KSKILL_POPBILL_CORP_NUM", "POPBILL_TEST_CORP_NUM", "POPBILL_CORP_NUM"),
|
|
30
|
+
"user_id": ("KSKILL_POPBILL_USER_ID", "POPBILL_USER_ID"),
|
|
31
|
+
}
|
|
32
|
+
DEFAULT_TEST_CORP_NUMS = ("1231212312", "1248100998", "1208800767")
|
|
33
|
+
|
|
34
|
+
def load_dotenv(path: Path = SECRET_FILE) -> None:
|
|
35
|
+
if not path.exists():
|
|
36
|
+
return
|
|
37
|
+
mode = stat.S_IMODE(path.stat().st_mode)
|
|
38
|
+
if mode & 0o077:
|
|
39
|
+
raise RuntimeError(f"{path} permissions are {oct(mode)}; expected 0600")
|
|
40
|
+
for raw in path.read_text(encoding="utf-8").splitlines():
|
|
41
|
+
line = raw.strip()
|
|
42
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
43
|
+
continue
|
|
44
|
+
key, value = line.split("=", 1)
|
|
45
|
+
key = key.strip()
|
|
46
|
+
value = value.strip().strip('"').strip("'")
|
|
47
|
+
os.environ.setdefault(key, value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def env_value(name: str, explicit: str | None = None, *, required: bool = False) -> str | None:
|
|
51
|
+
if explicit:
|
|
52
|
+
return explicit.strip()
|
|
53
|
+
for key in ENV_ALIASES[name]:
|
|
54
|
+
value = os.environ.get(key, "").strip()
|
|
55
|
+
if value and value != "replace-me":
|
|
56
|
+
return value
|
|
57
|
+
if required:
|
|
58
|
+
names = ", ".join(ENV_ALIASES[name])
|
|
59
|
+
raise ValueError(f"missing {name}; set one of: {names}")
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def normalize_corp_num(value: str | None) -> str:
|
|
64
|
+
if not value:
|
|
65
|
+
raise ValueError("missing corp_num; set KSKILL_POPBILL_CORP_NUM or pass --corp-num")
|
|
66
|
+
normalized = re.sub(r"\D", "", value)
|
|
67
|
+
if not re.fullmatch(r"\d{10}", normalized):
|
|
68
|
+
raise ValueError("corp_num must be a 10 digit Korean business registration number")
|
|
69
|
+
return normalized
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def create_service(service_name: str, *, link_id: str | None, secret_key: str | None, is_test: bool) -> Any:
|
|
73
|
+
cls = SERVICE_CLASSES[service_name]
|
|
74
|
+
service = cls(env_value("link_id", link_id, required=True), env_value("secret_key", secret_key, required=True))
|
|
75
|
+
service.IsTest = bool(is_test)
|
|
76
|
+
service.IPRestrictOnOff = True
|
|
77
|
+
return service
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def to_jsonable(value: Any) -> Any:
|
|
81
|
+
if value is None or isinstance(value, (str, int, float, bool)):
|
|
82
|
+
return value
|
|
83
|
+
if isinstance(value, (dt.date, dt.datetime)):
|
|
84
|
+
return value.isoformat()
|
|
85
|
+
if isinstance(value, (list, tuple, set)):
|
|
86
|
+
return [to_jsonable(v) for v in value]
|
|
87
|
+
if isinstance(value, dict):
|
|
88
|
+
return {str(k): to_jsonable(v) for k, v in value.items()}
|
|
89
|
+
if hasattr(value, "__dict__"):
|
|
90
|
+
return {k: to_jsonable(v) for k, v in vars(value).items() if not k.startswith("_")}
|
|
91
|
+
return str(value)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def print_json(payload: Any) -> None:
|
|
95
|
+
print(json.dumps(to_jsonable(payload), ensure_ascii=False, indent=2, sort_keys=True))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def parse_json_arg(value: str | None, default: Any) -> Any:
|
|
99
|
+
if not value:
|
|
100
|
+
return default
|
|
101
|
+
if value.startswith("@"):
|
|
102
|
+
value = Path(value[1:]).read_text(encoding="utf-8")
|
|
103
|
+
return json.loads(value)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def object_from_payload(kind: str, payload: dict[str, Any]) -> Any:
|
|
107
|
+
if kind not in OBJECT_CLASSES:
|
|
108
|
+
raise ValueError(f"unsupported object kind: {kind}")
|
|
109
|
+
return OBJECT_CLASSES[kind](**payload)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def nested_object(value: Any) -> Any:
|
|
113
|
+
if isinstance(value, dict):
|
|
114
|
+
kind = value.pop("__kind__", None)
|
|
115
|
+
converted = {k: nested_object(v) for k, v in value.items()}
|
|
116
|
+
if kind:
|
|
117
|
+
return object_from_payload(kind, converted)
|
|
118
|
+
return converted
|
|
119
|
+
if isinstance(value, list):
|
|
120
|
+
return [nested_object(v) for v in value]
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def guard_dangerous(method_name: str, args: argparse.Namespace) -> None:
|
|
125
|
+
if not is_dangerous_method(method_name):
|
|
126
|
+
return
|
|
127
|
+
if not args.yes_i_understand:
|
|
128
|
+
raise ValueError(f"{method_name} can mutate Popbill/test data or send messages; pass --yes-i-understand after current-turn approval")
|
|
129
|
+
if not args.test and not args.allow_production:
|
|
130
|
+
raise ValueError("production mutation requires --allow-production and current-turn approval")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def command_config_check(args: argparse.Namespace) -> int:
|
|
134
|
+
load_dotenv()
|
|
135
|
+
missing = []
|
|
136
|
+
for logical, aliases in ENV_ALIASES.items():
|
|
137
|
+
if logical == "user_id":
|
|
138
|
+
continue
|
|
139
|
+
if logical == "corp_num" and os.environ.get("KSKILL_POPBILL_TRY_DEFAULT_TEST_CORP_NUMS") == "1":
|
|
140
|
+
continue
|
|
141
|
+
if not env_value(logical, getattr(args, logical, None)):
|
|
142
|
+
missing.append({"field": logical, "accepted_env": aliases})
|
|
143
|
+
result = {
|
|
144
|
+
"ok": not missing,
|
|
145
|
+
"secrets_file": str(SECRET_FILE),
|
|
146
|
+
"secrets_file_exists": SECRET_FILE.exists(),
|
|
147
|
+
"missing": missing,
|
|
148
|
+
"mode": "test" if args.test else "production",
|
|
149
|
+
}
|
|
150
|
+
print_json(result)
|
|
151
|
+
return 0 if not missing else 1
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def command_methods(args: argparse.Namespace) -> int:
|
|
155
|
+
cls = SERVICE_CLASSES[args.service]
|
|
156
|
+
import inspect
|
|
157
|
+
|
|
158
|
+
methods = []
|
|
159
|
+
for name, func in inspect.getmembers(cls, inspect.isfunction):
|
|
160
|
+
if name.startswith("_") or name == "__init__":
|
|
161
|
+
continue
|
|
162
|
+
methods.append({"name": name, "signature": str(inspect.signature(func)), "requires_approval": is_dangerous_method(name)})
|
|
163
|
+
print_json({"service": args.service, "methods": methods})
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def command_object_template(args: argparse.Namespace) -> int:
|
|
168
|
+
templates = object_templates()
|
|
169
|
+
print_json({"kind": args.kind, "template": templates.get(args.kind, {})})
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def command_call(args: argparse.Namespace) -> int:
|
|
174
|
+
load_dotenv()
|
|
175
|
+
guard_dangerous(args.method, args)
|
|
176
|
+
service = create_service(args.service, link_id=args.link_id, secret_key=args.secret_key, is_test=args.test)
|
|
177
|
+
if not hasattr(service, args.method):
|
|
178
|
+
raise ValueError(f"{args.service} has no method {args.method}")
|
|
179
|
+
method: Callable[..., Any] = getattr(service, args.method)
|
|
180
|
+
positional = nested_object(parse_json_arg(args.args_json, []))
|
|
181
|
+
keywords = nested_object(parse_json_arg(args.kwargs_json, {}))
|
|
182
|
+
if not isinstance(positional, list):
|
|
183
|
+
raise ValueError("--args-json must be a JSON array")
|
|
184
|
+
if not isinstance(keywords, dict):
|
|
185
|
+
raise ValueError("--kwargs-json must be a JSON object")
|
|
186
|
+
if positional and positional[0] == "@corp":
|
|
187
|
+
positional[0] = resolve_corp_candidates(args.corp_num)[0]
|
|
188
|
+
result = method(*positional, **keywords)
|
|
189
|
+
print_json({"ok": True, "service": args.service, "method": args.method, "result": result})
|
|
190
|
+
return 0
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def resolve_corp_candidates(explicit: str | None = None) -> list[str]:
|
|
194
|
+
configured = env_value("corp_num", explicit)
|
|
195
|
+
if configured:
|
|
196
|
+
return [normalize_corp_num(configured)]
|
|
197
|
+
if os.environ.get("KSKILL_POPBILL_TRY_DEFAULT_TEST_CORP_NUMS") == "1":
|
|
198
|
+
return list(DEFAULT_TEST_CORP_NUMS)
|
|
199
|
+
raise ValueError("missing corp_num; set KSKILL_POPBILL_CORP_NUM or pass --corp-num")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def command_health(args: argparse.Namespace) -> int:
|
|
203
|
+
load_dotenv()
|
|
204
|
+
service = create_service(args.service, link_id=args.link_id, secret_key=args.secret_key, is_test=args.test)
|
|
205
|
+
all_checks = []
|
|
206
|
+
for corp_num in resolve_corp_candidates(args.corp_num):
|
|
207
|
+
checks: dict[str, Any] = {"service": args.service, "corp_num": corp_num, "mode": "test" if args.test else "production"}
|
|
208
|
+
success = False
|
|
209
|
+
for method_name in ("checkIsMember", "getPartnerBalance"):
|
|
210
|
+
if hasattr(service, method_name):
|
|
211
|
+
try:
|
|
212
|
+
checks[method_name] = getattr(service, method_name)(corp_num)
|
|
213
|
+
success = True
|
|
214
|
+
except PopbillException as exc:
|
|
215
|
+
checks[method_name] = {"popbill_error": int(exc.code), "message": str(exc.message)}
|
|
216
|
+
all_checks.append(checks)
|
|
217
|
+
if success:
|
|
218
|
+
print_json(checks)
|
|
219
|
+
return 0
|
|
220
|
+
print_json({"ok": False, "checks": all_checks})
|
|
221
|
+
return 2
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def command_closedown_check(args: argparse.Namespace) -> int:
|
|
225
|
+
load_dotenv()
|
|
226
|
+
corp_num = normalize_corp_num(env_value("corp_num", args.corp_num, required=True))
|
|
227
|
+
target = normalize_corp_num(args.target_corp_num)
|
|
228
|
+
service = create_service("closedown", link_id=args.link_id, secret_key=args.secret_key, is_test=args.test)
|
|
229
|
+
print_json({"ok": True, "result": service.checkCorpNum(corp_num, target)})
|
|
230
|
+
return 0
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
234
|
+
parser = argparse.ArgumentParser(description="Popbill all-service k-skill CLI")
|
|
235
|
+
parser.add_argument("--link-id")
|
|
236
|
+
parser.add_argument("--secret-key")
|
|
237
|
+
parser.add_argument("--corp-num")
|
|
238
|
+
parser.add_argument("--test", action=argparse.BooleanOptionalAction, default=True, help="use Popbill test environment (default: true)")
|
|
239
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
240
|
+
|
|
241
|
+
p = sub.add_parser("config-check", help="check local k-skill Popbill secret wiring without printing secrets")
|
|
242
|
+
p.set_defaults(func=command_config_check)
|
|
243
|
+
|
|
244
|
+
p = sub.add_parser("methods", help="list SDK methods for a Popbill service")
|
|
245
|
+
p.add_argument("service", choices=sorted(SERVICE_CLASSES))
|
|
246
|
+
p.set_defaults(func=command_methods)
|
|
247
|
+
|
|
248
|
+
p = sub.add_parser("object-template", help="print safe starter JSON for SDK input objects")
|
|
249
|
+
p.add_argument("kind", choices=sorted(set(OBJECT_CLASSES) | {"message-receiver", "fax-receiver", "kakao-receiver"}))
|
|
250
|
+
p.set_defaults(func=command_object_template)
|
|
251
|
+
|
|
252
|
+
p = sub.add_parser("health", help="safe membership/balance smoke test")
|
|
253
|
+
p.add_argument("service", choices=sorted(SERVICE_CLASSES), nargs="?", default="taxinvoice")
|
|
254
|
+
p.set_defaults(func=command_health)
|
|
255
|
+
|
|
256
|
+
p = sub.add_parser("closedown-check", help="휴폐업 단건 조회")
|
|
257
|
+
p.add_argument("--target-corp-num", required=True)
|
|
258
|
+
p.set_defaults(func=command_closedown_check)
|
|
259
|
+
|
|
260
|
+
p = sub.add_parser("call", help="generic SDK method call; covers all Popbill SDK service features")
|
|
261
|
+
p.add_argument("service", choices=sorted(SERVICE_CLASSES))
|
|
262
|
+
p.add_argument("method")
|
|
263
|
+
p.add_argument("--args-json", default="[]", help="JSON array; use @path to read from file. Use @corp as first item to inject --corp-num/env corp num.")
|
|
264
|
+
p.add_argument("--kwargs-json", default="{}", help="JSON object; use @path to read from file")
|
|
265
|
+
p.add_argument("--yes-i-understand", action="store_true", help="required for mutation/send/issue operations")
|
|
266
|
+
p.add_argument("--allow-production", action="store_true", help="required together with --no-test for production mutation")
|
|
267
|
+
p.set_defaults(func=command_call)
|
|
268
|
+
return parser
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def main(argv: list[str] | None = None) -> int:
|
|
272
|
+
args = build_parser().parse_args(argv)
|
|
273
|
+
try:
|
|
274
|
+
return args.func(args)
|
|
275
|
+
except PopbillException as exc:
|
|
276
|
+
print_json({"ok": False, "popbill_error": int(exc.code), "message": str(exc.message)})
|
|
277
|
+
return 2
|
|
278
|
+
except Exception as exc:
|
|
279
|
+
print_json({"ok": False, "error": str(exc)})
|
|
280
|
+
return 1
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if __name__ == "__main__":
|
|
284
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from popbill import (
|
|
4
|
+
AccountCheckService,
|
|
5
|
+
BizInfoCheckService,
|
|
6
|
+
Cashbill,
|
|
7
|
+
CashbillService,
|
|
8
|
+
ClosedownService,
|
|
9
|
+
EasyFinBankService,
|
|
10
|
+
FaxReceiver,
|
|
11
|
+
FaxService,
|
|
12
|
+
FileData,
|
|
13
|
+
HTCashbillService,
|
|
14
|
+
HTTaxinvoiceService,
|
|
15
|
+
KakaoButton,
|
|
16
|
+
KakaoReceiver,
|
|
17
|
+
KakaoService,
|
|
18
|
+
MessageReceiver,
|
|
19
|
+
MessageService,
|
|
20
|
+
Statement,
|
|
21
|
+
StatementDetail,
|
|
22
|
+
StatementService,
|
|
23
|
+
Taxinvoice,
|
|
24
|
+
TaxinvoiceDetail,
|
|
25
|
+
TaxinvoiceService,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
SERVICE_CLASSES: dict[str, type] = {
|
|
29
|
+
"taxinvoice": TaxinvoiceService,
|
|
30
|
+
"statement": StatementService,
|
|
31
|
+
"cashbill": CashbillService,
|
|
32
|
+
"message": MessageService,
|
|
33
|
+
"kakao": KakaoService,
|
|
34
|
+
"fax": FaxService,
|
|
35
|
+
"closedown": ClosedownService,
|
|
36
|
+
"bizinfo": BizInfoCheckService,
|
|
37
|
+
"easyfin-bank": EasyFinBankService,
|
|
38
|
+
"account-check": AccountCheckService,
|
|
39
|
+
"ht-taxinvoice": HTTaxinvoiceService,
|
|
40
|
+
"ht-cashbill": HTCashbillService,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
OBJECT_CLASSES: dict[str, type] = {
|
|
44
|
+
"taxinvoice": Taxinvoice,
|
|
45
|
+
"taxinvoice-detail": TaxinvoiceDetail,
|
|
46
|
+
"statement": Statement,
|
|
47
|
+
"statement-detail": StatementDetail,
|
|
48
|
+
"cashbill": Cashbill,
|
|
49
|
+
"message-receiver": MessageReceiver,
|
|
50
|
+
"fax-receiver": FaxReceiver,
|
|
51
|
+
"file-data": FileData,
|
|
52
|
+
"kakao-receiver": KakaoReceiver,
|
|
53
|
+
"kakao-button": KakaoButton,
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
DANGEROUS_METHOD_PREFIXES = (
|
|
4
|
+
"accept",
|
|
5
|
+
"assign",
|
|
6
|
+
"attach",
|
|
7
|
+
"bulk",
|
|
8
|
+
"cancel",
|
|
9
|
+
"checkaccountinfo",
|
|
10
|
+
"checkdepositorinfo",
|
|
11
|
+
"close",
|
|
12
|
+
"delete",
|
|
13
|
+
"deny",
|
|
14
|
+
"detach",
|
|
15
|
+
"faxsend",
|
|
16
|
+
"issue",
|
|
17
|
+
"join",
|
|
18
|
+
"payment",
|
|
19
|
+
"quit",
|
|
20
|
+
"refund",
|
|
21
|
+
"refuse",
|
|
22
|
+
"regist",
|
|
23
|
+
"register",
|
|
24
|
+
"request",
|
|
25
|
+
"resend",
|
|
26
|
+
"revoke",
|
|
27
|
+
"send",
|
|
28
|
+
"update",
|
|
29
|
+
)
|
|
30
|
+
SAFE_URL_METHODS = frozenset(
|
|
31
|
+
{"getaccessurl", "getchargeurl", "getpaymenturl", "getpopbillurl", "geturl", "getusehistoryurl"}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_dangerous_method(method_name: str) -> bool:
|
|
36
|
+
lowered = method_name.lower()
|
|
37
|
+
return lowered.startswith(DANGEROUS_METHOD_PREFIXES) and lowered not in SAFE_URL_METHODS
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def object_templates(today: dt.date | None = None) -> dict[str, dict[str, Any]]:
|
|
8
|
+
write_date = (today or dt.date.today()).strftime("%Y%m%d")
|
|
9
|
+
return {
|
|
10
|
+
"taxinvoice": {
|
|
11
|
+
"issueType": "정발행",
|
|
12
|
+
"chargeDirection": "정과금",
|
|
13
|
+
"purposeType": "청구",
|
|
14
|
+
"taxType": "과세",
|
|
15
|
+
"writeDate": write_date,
|
|
16
|
+
"invoicerMgtKey": "TEST-YYYYMMDD-001",
|
|
17
|
+
"invoicerCorpNum": "1234567890",
|
|
18
|
+
"invoicerCorpName": "공급자 상호",
|
|
19
|
+
"invoicerCEOName": "대표자",
|
|
20
|
+
"invoicerAddr": "공급자 주소",
|
|
21
|
+
"invoicerEmail": "tax@example.com",
|
|
22
|
+
"invoiceeType": "사업자",
|
|
23
|
+
"invoiceeCorpNum": "0987654321",
|
|
24
|
+
"invoiceeCorpName": "공급받는자 상호",
|
|
25
|
+
"invoiceeCEOName": "대표자",
|
|
26
|
+
"invoiceeAddr": "공급받는자 주소",
|
|
27
|
+
"invoiceeEmail1": "receiver@example.com",
|
|
28
|
+
"supplyCostTotal": "91",
|
|
29
|
+
"taxTotal": "9",
|
|
30
|
+
"totalAmount": "100",
|
|
31
|
+
"detailList": [
|
|
32
|
+
{
|
|
33
|
+
"__kind__": "taxinvoice-detail",
|
|
34
|
+
"serialNum": 1,
|
|
35
|
+
"itemName": "테스트 품목",
|
|
36
|
+
"supplyCost": "91",
|
|
37
|
+
"tax": "9",
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
"statement": {
|
|
42
|
+
"itemCode": 121,
|
|
43
|
+
"mgtKey": "STMT-YYYYMMDD-001",
|
|
44
|
+
"senderCorpNum": "1234567890",
|
|
45
|
+
"receiverCorpNum": "0987654321",
|
|
46
|
+
"detailList": [
|
|
47
|
+
{
|
|
48
|
+
"__kind__": "statement-detail",
|
|
49
|
+
"serialNum": 1,
|
|
50
|
+
"itemName": "테스트 품목",
|
|
51
|
+
"supplyCost": "91",
|
|
52
|
+
"tax": "9",
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
"cashbill": {
|
|
57
|
+
"mgtKey": "CASH-YYYYMMDD-001",
|
|
58
|
+
"tradeType": "승인거래",
|
|
59
|
+
"tradeUsage": "소득공제용",
|
|
60
|
+
"taxationType": "과세",
|
|
61
|
+
"totalAmount": "100",
|
|
62
|
+
"supplyCost": "91",
|
|
63
|
+
"tax": "9",
|
|
64
|
+
"serviceFee": "0",
|
|
65
|
+
"identityNum": "01000000000",
|
|
66
|
+
"customerName": "테스트",
|
|
67
|
+
},
|
|
68
|
+
"message-receiver": {"receiveNum": "01000000000", "receiveName": "테스트"},
|
|
69
|
+
"fax-receiver": {"receiveNum": "0200000000", "receiveName": "테스트"},
|
|
70
|
+
"kakao-receiver": {"rcv": "01000000000", "rcvnm": "테스트", "msg": "알림톡 테스트"},
|
|
71
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "popbill",
|
|
3
|
+
"description": "팝빌 전자세금계산서·전자명세서·현금영수증·문자·카카오톡·팩스·휴폐업/기업정보·계좌조회·홈택스 수집을 테스트/운영 환경에서 안전하게 호출한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"vault",
|
|
6
|
+
"action:submission"
|
|
7
|
+
],
|
|
8
|
+
"frontmatter": "name: popbill\ndescription: 팝빌 전자세금계산서·전자명세서·현금영수증·문자·카카오톡·팩스·휴폐업/기업정보·계좌조회·홈택스 수집을 테스트/운영 환경에서 안전하게 호출한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: business\n locale: ko-KR\n phase: v1"
|
|
9
|
+
}
|