From 7d7df59b4347b9129248241d590c8b667da02ddf Mon Sep 17 00:00:00 2001 From: kappa Date: Fri, 16 Jan 2026 14:09:20 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Namecheap=20API=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=B3=80=ED=99=98=20(MM/DD/YYYY=20?= =?UTF-8?q?=E2=86=92=20ISO)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Namecheap API가 미국 형식(08/01/2026)을 반환하여 AI가 1월 8일로 오해석 - ISO 형식(2026-08-01)으로 변환하여 명확한 날짜 표시 - list_domains, get_domain_info에 날짜 변환 적용 - 문서에 WHOIS API 서버 정보 및 ccSLD 미지원 안내 추가 Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 5 +++- README.md | 8 ++++--- src/openai-service.ts | 54 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3bd99aa..506177a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,7 +76,10 @@ Telegram Webhook → Security Validation → Command/Message Router - **Context7 API**: `lookup_docs` 함수로 라이브러리 문서 조회 - **Domain Agent**: `manage_domain` 함수 → OpenAI Assistants API (`asst_MzPFKoqt7V4w6bc0UwcXU4ob`) - **Namecheap API**: `https://namecheap-api.anvil.it.com` (도메인 목록, 가격, 네임서버) -- **RDAP API**: 공개 WHOIS 조회 (com/net/org/io/me/info/biz TLD 지원) + - 날짜 형식: MM/DD/YYYY (미국 형식) → ISO (YYYY-MM-DD) 변환 필요 +- **WHOIS API**: `https://whois-api-eight.vercel.app` (공개 WHOIS 조회, TCP 43 직접 쿼리) + - 지원 TLD: com, net, org, io, co, me, kr, jp 등 40+ TLD + - ccSLD 미지원: it.com, uk.com, us.com 등 (사설 레지스트리) - **wttr.in**: 날씨 API - **DuckDuckGo**: 웹 검색 API - **Vault**: `vault.anvil.it.com`에서 API 키 중앙 관리 diff --git a/README.md b/README.md index a65ee55..17cd7d8 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ OpenAI Function Calling을 통해 AI가 자동으로 필요한 도구를 호출 | **시간** | "지금 몇 시야", "뉴욕 시간" | 내장 | | **계산** | "123 * 456", "100의 20%" | 내장 | | **문서** | "React hooks 사용법", "OpenAI API 예제" | Context7 | -| **도메인** | "도메인 목록", "anvil.it.com 네임서버", ".com 가격", "google.com whois" | Domain Agent + RDAP | +| **도메인** | "도메인 목록", "anvil.it.com 네임서버", ".com 가격", "google.com whois" | Domain Agent + WHOIS API | | **예치금** | "잔액 확인", "충전하고 싶어", "10000원 입금했어" | D1 + Email Worker | ### 동작 방식 @@ -263,14 +263,16 @@ Namecheap 가격 + 13% 마진, 매일 환율 업데이트 ### WHOIS 조회 -공개 RDAP API를 통해 아무 도메인이나 조회 가능 +자체 WHOIS API 서버(Vercel)를 통해 TCP 43 포트로 직접 쿼리 ``` 사용자: "google.com whois" 봇: 등록일, 만료일, 네임서버, 등록기관 정보 표시 ``` -지원 TLD: com, net, org, io, me, info, biz (RDAP 지원 TLD) +- **WHOIS API**: `https://whois-api-eight.vercel.app` +- **지원 TLD**: com, net, org, io, co, me, kr, jp, cn, uk, de, fr 등 40+ TLD +- **ccSLD 미지원**: it.com, uk.com, us.com 등 사설 레지스트리는 WHOIS 비공개 --- diff --git a/src/openai-service.ts b/src/openai-service.ts index 2c7eb69..d7395f9 100644 --- a/src/openai-service.ts +++ b/src/openai-service.ts @@ -181,23 +181,44 @@ async function callNamecheapApi(funcName: string, funcArgs: Record, const result = await fetch(`${apiUrl}/domains?page=${funcArgs.page || 1}&page_size=${funcArgs.page_size || 100}`, { headers: { 'X-API-Key': apiKey }, }).then(r => r.json()) as any[]; - // 허용된 도메인만 필터링 - return result.filter((d: any) => allowedDomains.includes(d.name)); + // MM/DD/YYYY → YYYY-MM-DD 변환 (Namecheap은 미국 형식 사용) + const convertDate = (date: string) => { + const [month, day, year] = date.split('/'); + return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; + }; + // 허용된 도메인만 필터링, 날짜는 ISO 형식으로 변환 + return result + .filter((d: any) => allowedDomains.includes(d.name)) + .map((d: any) => ({ + ...d, + created: convertDate(d.created), + expires: convertDate(d.expires), + user: undefined, // 민감 정보 제거 + })); } case 'get_domain_info': { - const domainInfo = await fetch(`${apiUrl}/domains/${funcArgs.domain}`, { + // 목록 API에서 더 많은 정보 조회 (단일 API는 정보 부족) + const domains = await fetch(`${apiUrl}/domains?page=1&page_size=100`, { headers: { 'X-API-Key': apiKey }, - }).then(r => r.json()) as any; - // 민감 정보 필터링 (계정 ID 등) + }).then(r => r.json()) as any[]; + const domainInfo = domains.find((d: any) => d.name === funcArgs.domain); + if (!domainInfo) { + return { error: `도메인을 찾을 수 없습니다: ${funcArgs.domain}` }; + } + // MM/DD/YYYY → YYYY-MM-DD 변환 (Namecheap은 미국 형식 사용) + const convertDate = (date: string) => { + const [month, day, year] = date.split('/'); + return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; + }; + // 민감 정보 필터링 (user/owner 제거), 날짜는 ISO 형식으로 변환 return { - domain: domainInfo.name || funcArgs.domain, - status: domainInfo.status, - created: domainInfo.created, - expires: domainInfo.expires, + domain: domainInfo.name, + created: convertDate(domainInfo.created), + expires: convertDate(domainInfo.expires), + is_expired: domainInfo.is_expired, auto_renew: domainInfo.auto_renew, is_locked: domainInfo.is_locked, - is_premium: domainInfo.is_premium, - nameservers: domainInfo.nameservers, + whois_guard: domainInfo.whois_guard, }; } case 'get_nameservers': @@ -290,6 +311,17 @@ async function callNamecheapApi(funcName: string, funcArgs: Record, return { error: `WHOIS 조회 오류: ${whois.error}` }; } + // ccSLD WHOIS 미지원 처리 + if (whois.whois_supported === false) { + return { + domain: whois.domain, + whois_supported: false, + ccSLD: whois.ccSLD, + message: whois.message_ko, + suggestion: whois.suggestion_ko, + }; + } + // raw WHOIS 응답을 그대로 반환 (AI가 파싱) return { domain: whois.domain,