feat: 도메인 인라인 버튼 등록 + cheapest TLD + Cron 자동취소

- 도메인 등록 인라인 버튼 확인 플로우 (domain-register.ts)
- manage_domain에 cheapest action 추가 (가장 저렴한 TLD TOP 15)
- 24시간 경과 입금 대기 자동 취소 Cron (UTC 15:00)
- 거래 내역 한글 라벨 + description 표시
- CLAUDE.md 문서 업데이트

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-18 15:24:03 +09:00
parent 89f8ea19f1
commit db859efc56
8 changed files with 567 additions and 23 deletions

View File

@@ -128,8 +128,8 @@ const tools = [
properties: {
action: {
type: 'string',
enum: ['register', 'check', 'whois', 'list', 'info', 'get_ns', 'set_ns', 'price'],
description: 'price: TLD 가격 조회 (.com 가격, .io 가격), register: 도메인 등록, check: 가용성 확인, whois: WHOIS 조회, list: 내 도메인 목록, info: 도메인 상세정보, get_ns/set_ns: 네임서버 조회/변경',
enum: ['register', 'check', 'whois', 'list', 'info', 'get_ns', 'set_ns', 'price', 'cheapest'],
description: 'price: TLD 가격 조회 (.com 가격, .io 가격), cheapest: 가장 저렴한 TLD 목록 조회, register: 도메인 등록, check: 가용성 확인, whois: WHOIS 조회, list: 내 도메인 목록, info: 도메인 상세정보, get_ns/set_ns: 네임서버 조회/변경',
},
domain: {
type: 'string',
@@ -523,6 +523,11 @@ async function callNamecheapApi(
headers: { 'X-API-Key': apiKey },
}).then(r => r.json());
}
case 'get_all_prices': {
return fetch(`${apiUrl}/prices`, {
headers: { 'X-API-Key': apiKey },
}).then(r => r.json());
}
case 'check_domains': {
return fetch(`${apiUrl}/domains/check`, {
method: 'POST',
@@ -761,6 +766,27 @@ async function executeDomainAction(
return `💰 .${targetTld} 도메인 가격\n\n• 등록/갱신: ${price?.toLocaleString()}원/년`;
}
case 'cheapest': {
const result = await callNamecheapApi('get_all_prices', {}, allowedDomains, telegramUserId, db, userId);
if (result.error) return `🚫 ${result.error}`;
// 가격 > 0인 TLD만 필터링, krw 기준 정렬
const sorted = (result as any[])
.filter((p: any) => p.krw > 0)
.sort((a: any, b: any) => a.krw - b.krw)
.slice(0, 15);
if (sorted.length === 0) {
return '🚫 TLD 가격 정보를 가져올 수 없습니다.';
}
const list = sorted.map((p: any, i: number) =>
`${i + 1}. .${p.tld} - ${p.krw.toLocaleString()}원/년`
).join('\n');
return `💰 가장 저렴한 TLD TOP 15\n\n${list}\n\n💡 특정 TLD 가격은 ".com 가격" 형식으로 조회`;
}
case 'register': {
if (!domain) return '🚫 등록할 도메인을 지정해주세요.';
if (!telegramUserId) return '🚫 도메인 등록에는 로그인이 필요합니다.';
@@ -783,33 +809,38 @@ async function executeDomainAction(
balance = balanceRow?.balance || 0;
}
// 4. 확인 페이지 생성 (코드에서 고정 형식)
// 4. 확인 페이지 생성 (인라인 버튼 포함)
if (balance >= price) {
return `📋 도메인 등록 확인
// 버튼 데이터를 특수 마커로 포함
const keyboardData = JSON.stringify({
type: 'domain_register',
domain: domain,
price: price
});
return `__KEYBOARD__${keyboardData}__END__
📋 <b>도메인 등록 확인</b>
• 도메인: ${domain}
• 도메인: <code>${domain}</code>
• 가격: ${price.toLocaleString()}원 (예치금에서 차감)
• 현재 잔액: ${balance.toLocaleString()}
• 현재 잔액: ${balance.toLocaleString()}
• 등록 기간: 1년
📌 등록자 정보
📌 <b>등록자 정보</b>
서비스 기본 정보로 등록됩니다.
(WHOIS Guard가 적용되어 개인정보는 비공개)
⚠️ 주의사항
도메인 등록 후에는 취소 및 환불이 불가능합니다.
등록을 진행하시려면 '확인'이라고 입력해주세요.`;
⚠️ <b>주의사항</b>
도메인 등록 후에는 취소 및 환불이 불가능합니다.`;
} else {
const shortage = price - balance;
return `📋 도메인 등록 확인
return `📋 <b>도메인 등록 확인</b>
• 도메인: ${domain}
• 도메인: <code>${domain}</code>
• 가격: ${price.toLocaleString()}
• 현재 잔액: ${balance.toLocaleString()}원 ⚠️ 부족
• 부족 금액: ${shortage.toLocaleString()}
💳 입금 계좌
💳 <b>입금 계좌</b>
하나은행 427-910018-27104 (주식회사 아이언클래드)
입금 후 '홍길동 ${shortage}원 입금' 형식으로 알려주세요.`;
}
@@ -866,10 +897,12 @@ ${result.account_info.bank} ${result.account_info.account}
return `📋 ${result.message}`;
}
const statusIcon = (s: string) => s === 'confirmed' ? '✓' : s === 'pending' ? '⏳' : '✗';
const typeLabel = (t: string) => t === 'deposit' ? '입금' : t === 'withdrawal' ? '출금' : t === 'refund' ? '환불' : t;
const txList = result.transactions.map((tx: any) => {
const date = tx.confirmed_at || tx.created_at;
const dateStr = date ? new Date(date).toLocaleDateString('ko-KR', { month: '2-digit', day: '2-digit' }) : '';
return `#${tx.id}: ${tx.type === 'deposit' ? '입금' : tx.type} ${tx.amount.toLocaleString()}${statusIcon(tx.status)} (${dateStr})`;
const desc = tx.description ? ` - ${tx.description}` : '';
return `#${tx.id}: ${typeLabel(tx.type)} ${tx.amount.toLocaleString()}${statusIcon(tx.status)} (${dateStr})${desc}`;
}).join('\n');
return `📋 거래 내역\n\n${txList}`;
}
@@ -1266,6 +1299,12 @@ export async function generateOpenAIResponse(
for (const toolCall of assistantMessage.tool_calls) {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeTool(toolCall.function.name, args, env, telegramUserId, db);
// __KEYBOARD__ 마커가 있으면 AI 재해석 없이 바로 반환 (버튼 보존)
if (result.includes('__KEYBOARD__')) {
return result;
}
toolResults.push({
role: 'tool',
tool_call_id: toolCall.id,