fix: Namecheap API 날짜 형식 변환 (MM/DD/YYYY → ISO)

- 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 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-16 14:09:20 +09:00
parent 20155e53a4
commit 7d7df59b43
3 changed files with 52 additions and 15 deletions

View File

@@ -76,7 +76,10 @@ Telegram Webhook → Security Validation → Command/Message Router
- **Context7 API**: `lookup_docs` 함수로 라이브러리 문서 조회 - **Context7 API**: `lookup_docs` 함수로 라이브러리 문서 조회
- **Domain Agent**: `manage_domain` 함수 → OpenAI Assistants API (`asst_MzPFKoqt7V4w6bc0UwcXU4ob`) - **Domain Agent**: `manage_domain` 함수 → OpenAI Assistants API (`asst_MzPFKoqt7V4w6bc0UwcXU4ob`)
- **Namecheap API**: `https://namecheap-api.anvil.it.com` (도메인 목록, 가격, 네임서버) - **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 - **wttr.in**: 날씨 API
- **DuckDuckGo**: 웹 검색 API - **DuckDuckGo**: 웹 검색 API
- **Vault**: `vault.anvil.it.com`에서 API 키 중앙 관리 - **Vault**: `vault.anvil.it.com`에서 API 키 중앙 관리

View File

@@ -131,7 +131,7 @@ OpenAI Function Calling을 통해 AI가 자동으로 필요한 도구를 호출
| **시간** | "지금 몇 시야", "뉴욕 시간" | 내장 | | **시간** | "지금 몇 시야", "뉴욕 시간" | 내장 |
| **계산** | "123 * 456", "100의 20%" | 내장 | | **계산** | "123 * 456", "100의 20%" | 내장 |
| **문서** | "React hooks 사용법", "OpenAI API 예제" | Context7 | | **문서** | "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 | | **예치금** | "잔액 확인", "충전하고 싶어", "10000원 입금했어" | D1 + Email Worker |
### 동작 방식 ### 동작 방식
@@ -263,14 +263,16 @@ Namecheap 가격 + 13% 마진, 매일 환율 업데이트
### WHOIS 조회 ### WHOIS 조회
공개 RDAP API를 통해 아무 도메인이나 조회 가능 자체 WHOIS API 서버(Vercel)를 통해 TCP 43 포트로 직접 쿼리
``` ```
사용자: "google.com whois" 사용자: "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 비공개
--- ---

View File

@@ -181,23 +181,44 @@ async function callNamecheapApi(funcName: string, funcArgs: Record<string, any>,
const result = await fetch(`${apiUrl}/domains?page=${funcArgs.page || 1}&page_size=${funcArgs.page_size || 100}`, { const result = await fetch(`${apiUrl}/domains?page=${funcArgs.page || 1}&page_size=${funcArgs.page_size || 100}`, {
headers: { 'X-API-Key': apiKey }, headers: { 'X-API-Key': apiKey },
}).then(r => r.json()) as any[]; }).then(r => r.json()) as any[];
// 허용된 도메인만 필터링 // MM/DD/YYYY → YYYY-MM-DD 변환 (Namecheap은 미국 형식 사용)
return result.filter((d: any) => allowedDomains.includes(d.name)); 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': { 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 }, headers: { 'X-API-Key': apiKey },
}).then(r => r.json()) as any; }).then(r => r.json()) as any[];
// 민감 정보 필터링 (계정 ID 등) 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 { return {
domain: domainInfo.name || funcArgs.domain, domain: domainInfo.name,
status: domainInfo.status, created: convertDate(domainInfo.created),
created: domainInfo.created, expires: convertDate(domainInfo.expires),
expires: domainInfo.expires, is_expired: domainInfo.is_expired,
auto_renew: domainInfo.auto_renew, auto_renew: domainInfo.auto_renew,
is_locked: domainInfo.is_locked, is_locked: domainInfo.is_locked,
is_premium: domainInfo.is_premium, whois_guard: domainInfo.whois_guard,
nameservers: domainInfo.nameservers,
}; };
} }
case 'get_nameservers': case 'get_nameservers':
@@ -290,6 +311,17 @@ async function callNamecheapApi(funcName: string, funcArgs: Record<string, any>,
return { error: `WHOIS 조회 오류: ${whois.error}` }; 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가 파싱) // raw WHOIS 응답을 그대로 반환 (AI가 파싱)
return { return {
domain: whois.domain, domain: whois.domain,