Files
telegram-bot-workers/CLAUDE.md
kappa ab314b10c4 feat: recreate Server Agent for premium VM management
Session-based agent with OpenAI Function Calling (9 tools).
Follows ddos-agent pattern: execute tools inside loop, feed results back to AI.
Includes D1 migration, session routing in openai-service, and doc updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 19:40:58 +09:00

865 lines
32 KiB
Markdown

# CLAUDE.md
> 🔧 **개발자용 문서**: 기술 상세, 코드 패턴, 트러블슈팅
> 📖 **[README.md](./README.md)**: 기능 소개, 배포 가이드, 사용법
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
---
## 1. Quick Start
### Auto-Read on Start
**세션 시작 시 반드시 수행:**
1. `README.md`를 Read 도구로 읽어 프로젝트 전체 구조 파악
2. 작업 대상 파일의 기존 코드 먼저 확인
```
필수 읽기: README.md → 작업 대상 파일
```
### Agent Usage Policy
**🎯 목표: 컨텍스트 절약 - 대부분의 작업을 에이전트에 위임**
**프로젝트 특성:**
- 언어: TypeScript (strict mode)
- 런타임: Cloudflare Workers
- 프레임워크: Wrangler, Workers AI, D1
- 주요 디렉토리: `src/tools/`, `src/routes/`, `src/services/`
**사용 가능한 에이전트 타입:**
- `coder`: 코드 작성/수정 (TypeScript/Workers 전문, 프로덕션 품질)
- `explorer`: 코드베이스 탐색/분석 (thorough 레벨)
- `planner`: 설계/계획 수립 전문가
- `reviewer`: 코드 리뷰 (qa/security/performance 페르소나 조합)
- `Bash`: 빌드/배포/테스트 실행 (긴 로그 출력 분리)
**CRITICAL: 다음 작업은 반드시 Task tool (agent)를 사용하여 메인 세션 컨텍스트 절약:**
| 작업 유형 | 조건 | 에이전트 타입 | 이유 |
|-----------|------|---------------|------|
| **코드 작성/수정** | 모든 코드 변경 | `coder` | TS/Workers 전문, 타입 안정성, 프로덕션 품질 |
| **리팩토링** | 파일 수 무관 | `coder` (병렬) | 일관성, 컨텍스트 분리, TS 최적화 |
| **Function Calling 도구** | 추가/수정 | `coder` (병렬) | tools/ + openai-service.ts 동시 처리 |
| **스키마 작업** | D1 마이그레이션 | `coder` | 백업→마이그레이션→검증 전체 위임 |
| **프로젝트 분석** | 구조 파악 | `explorer` (thorough) | 대량 파일 읽기 분리 |
| **설계/계획** | 아키텍처 설계 | `planner` | 시스템 분석 및 개선 방향 제시 |
| **코드 리뷰** | 보안/성능 | `reviewer``coder` | 분석 후 개선 제안 (reviewer는 읽기 전용) |
| **빌드/배포** | npm run, wrangler | `Bash` | 긴 로그 출력 분리 |
| **테스트** | 로컬 테스트 실행 | `Bash` | 테스트 출력 분리 |
**에이전트 위임의 이점:**
- ✅ 각 에이전트가 독립 컨텍스트 사용 (메인 세션 부담 0)
- ✅ 요약만 메인 세션에 반환 (토큰 대폭 절약)
- ✅ 병렬 처리 가능 (시간 단축)
- ✅ 메인 세션은 조율/지시만 담당
**병렬 처리 필수:**
- 독립적인 파일 여러 개 → 병렬 `coder` 에이전트
- 다른 디렉토리 동시 작업 → 병렬 `coder` 에이전트
- Function Calling 도구 추가 → tools/{new}.ts + openai-service.ts 병렬
**직접 처리 (최소화):**
- 간단한 문서 읽기 (README.md 확인)
- 사용자와의 대화/질문
- 에이전트 작업 조율/검토
### Critical Rules
**절대 지켜야 할 규칙:**
| 규칙 | 이유 |
|------|------|
| 배포 전 `npm run dev` 로컬 테스트 | 프로덕션 장애 방지 |
| D1 스키마 변경 시 마이그레이션 SQL 별도 작성 | 기존 데이터 보존 |
| Secrets(BOT_TOKEN, API_KEY 등) 코드에 하드코딩 금지 | 보안 |
| `wrangler.toml`의 ID 값 변경 금지 | 리소스 연결 유지 |
| Function Calling 도구 추가 시 `tools` 배열 + `executeFunctionCall()` 동시 수정 | 불일치 방지 |
**위험한 작업:**
- `wrangler d1 execute` 직접 실행 (production) → 반드시 확인 요청
- `user_deposits`, `deposit_transactions` 테이블 직접 수정 → 금전 관련, 주의
### Documentation Rules
**작업 완료 후 문서 자동 업데이트:**
| 변경 유형 | 업데이트 대상 |
|-----------|---------------|
| `src/*.ts` 파일 수정 | CLAUDE.md (Architecture, Key Patterns) |
| 새 Function Calling 도구 추가 | 양쪽 (README: 지원 기능 테이블, CLAUDE: tools 목록) |
| `schema.sql` 변경 | 양쪽 (Data Layer 섹션) |
| `wrangler.toml` 환경변수 추가 | 양쪽 (Configuration 섹션) |
| 외부 API 연동 추가/변경 | 양쪽 (External Integrations) |
| 봇 명령어 추가 | 양쪽 (Commands 섹션) |
---
## 2. Commands & Setup
### NPM Scripts
```bash
npm run dev # 로컬 개발 (wrangler dev)
npm run deploy # Cloudflare Workers 배포
npm run db:init # D1 스키마 초기화 (production) ⚠️ 주의
npm run db:init:local # D1 스키마 초기화 (local)
npm run tail # Workers 로그 스트리밍
npm test # 단위 테스트 실행 (Vitest)
npm run test:watch # Watch 모드
npm run test:coverage # 커버리지 리포트
```
### KV Namespace 생성
```bash
# Rate Limiting용 (필수)
wrangler kv:namespace create RATE_LIMIT_KV
# 서버 상담 세션용 (서버 추천 기능 사용 시 필수)
wrangler kv:namespace create SESSION_KV
# 출력된 id를 wrangler.toml의 [[kv_namespaces]] 섹션에 입력
```
### Secrets 설정
```bash
wrangler secret put BOT_TOKEN # Telegram Bot Token
wrangler secret put WEBHOOK_SECRET # Webhook 검증용
wrangler secret put OPENAI_API_KEY # OpenAI API 키
wrangler secret put NAMECHEAP_API_KEY # namecheap-api 래퍼 인증 키
wrangler secret put NAMECHEAP_API_KEY_INTERNAL # Namecheap API 키 (내부용)
wrangler secret put BRAVE_API_KEY # Brave Search API 키
wrangler secret put DEPOSIT_API_SECRET # Deposit API 인증 키
```
### Webhook 설정
```bash
# Webhook 설정
curl "https://telegram-summary-bot.kappa-d8e.workers.dev/setup-webhook?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}"
# Webhook 정보 조회
curl "https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}"
```
### Database Migrations
**마이그레이션 목록:**
| 파일 | 설명 |
|------|------|
| `001_optimize_prefix_indexes.sql` | 입금자명 prefix 인덱스 최적화 (99% 성능 향상) |
| `002_add_version_columns.sql` | Optimistic Locking (user_deposits.version) |
| `003_add_server_tables.sql` | server_orders, server_specs 테이블 추가 |
| `004_add_terminated_at.sql` | server_orders.terminated_at 컬럼 추가 |
| `005_add_stopped_status.sql` | server_orders 테이블에 'stopped' 상태 추가 |
**실행:**
```bash
# 로컬 테스트
wrangler d1 execute telegram-conversations --local --file=migrations/001_optimize_prefix_indexes.sql
# 프로덕션 적용 ⚠️ 주의: 데이터 백업 권장
wrangler d1 execute telegram-conversations --file=migrations/001_optimize_prefix_indexes.sql
```
---
## 3. Architecture
### Message Flow
```
Telegram Webhook → Security Validation → Command/Message Router
┌──────────────────────────┴──────────────────────────┐
↓ ↓
Command Handler AI Response Generator
(commands.ts) (openai-service.ts)
Function Calling (9개)
(weather, search, time, calc, docs,
domain, suggest_domains, deposit, server)
Profile System
(summary-service.ts)
```
### Core Services
| 파일 | 역할 | 주요 함수 |
|------|------|----------|
| `index.ts` | Worker 진입점, Email Handler | `fetch()`, `email()` |
| `openai-service.ts` | AI 응답 + Function Calling + 세션 라우팅 | `generateResponse()`, `executeFunctionCall()` |
| `summary-service.ts` | 프로필 시스템 | `updateSummary()`, `getConversationContext()` |
| `security.ts` | Webhook 보안, Rate Limiting (KV) | `validateWebhook()`, `checkRateLimit()` |
| `services/notification.ts` | 관리자 알림 | `notifyAdmin()` |
| `commands.ts` | 봇 명령어 | `handleCommand()` |
| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` |
**Agent System (세션 기반 전문가 AI):**
| 파일 | 역할 | 상태 관리 |
|------|------|----------|
| `agents/server-agent.ts` | 서버 관리 전문가 (프리미엄 호스팅) | D1 (1시간) |
| `agents/troubleshoot-agent.ts` | 트러블슈팅 상담 | D1 (1시간) |
| `agents/domain-agent.ts` | 도메인 추천 상담 (10년 경력 컨설턴트) | D1 (1시간) |
| `agents/deposit-agent.ts` | 예치금 입금 신고 상담 (금융 상담사) | D1 (30분) |
| `agents/ddos-agent.ts` | DDoS 방어 보안 전문가 | D1 (1시간) |
**Logging & Monitoring:**
| 파일 | 역할 |
|------|------|
| `utils/logger.ts` | 구조화된 로깅 (JSON 기반, 환경별 전환) |
| `utils/metrics.ts` | 성능 메트릭 수집 (API 호출 시간, 에러율) |
| `utils/circuit-breaker.ts` | Circuit Breaker (OpenAI API 보호) |
| `utils/retry.ts` | 재시도 로직 (지수 백오프, 15개 API 지원) |
### Function Calling Tools (9개)
| 도구 | 함수명 | 처리 방식 | 트리거 키워드 |
|------|--------|----------|---------------|
| 날씨 | `get_weather` | wttr.in API | 날씨 |
| 검색 | `search_web` | Brave Search API (한글→영문 자동 번역) | ~란, ~뭐야 |
| 시간 | `get_current_time` | 내장 | 몇 시, 시간 |
| 계산 | `calculate` | 내장 | 계산, +, -, *, / |
| 문서 | `lookup_docs` | Context7 API | 문서, 사용법, API |
| 도메인 | `manage_domain` | **Domain Agent 위임** (세션 기반) | 도메인, 네임서버, WHOIS |
| 도메인 추천 | `suggest_domains` | GPT + Namecheap API | 도메인 추천, 도메인 제안 |
| 예치금 | `manage_deposit` | **Deposit Agent 위임** (세션 기반) | 입금, 충전, 잔액, 계좌 |
| 서버 | `manage_server` | **Server Agent 위임** (세션 기반) | 서버, VPS, 클라우드, 호스팅 |
### Data Layer (D1 SQLite)
| 테이블 | 용도 | 주요 컬럼 |
|--------|------|----------|
| `users` | 사용자 | telegram_id, username |
| `message_buffer` | 대화 기록 | user_id, role, content |
| `summaries` | 프로필 | user_id, generation, summary |
| `user_deposits` | 예치금 계정 | user_id, balance, version |
| `deposit_transactions` | 거래 내역 | user_id, amount, status |
| `bank_notifications` | SMS 파싱 | depositor_name, amount, bank |
| `user_domains` | 도메인 소유권 | user_id, domain, verified |
| `server_orders` | 서버 주문 | user_id, spec_id, status |
| `server_specs` | 서버 스펙 | name, cpu, ram, disk, price |
| `domain_sessions` | 도메인 상담 세션 | user_id, status, collected_info, messages |
| `deposit_sessions` | 예치금 상담 세션 | user_id, status, collected_info, messages |
| `server_sessions` | 서버 관리 세션 | user_id, status, collected_info, messages |
| `ddos_sessions` | DDoS 방어 세션 | user_id, status, collected_info, messages |
| `troubleshoot_sessions` | 트러블슈팅 세션 | user_id, status, collected_info, messages |
**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환
### 동적 도구 로딩
**목적:** 토큰 절약 + AI 선택 정확도 향상
```
사용자 메시지 → 키워드 패턴 매칭 → 관련 도구만 선택 → AI 호출
```
**카테고리 분류:**
| 카테고리 | 도구 | 감지 패턴 |
|----------|------|-----------|
| domain | manage_domain, suggest_domains | 도메인, 네임서버, whois, .com |
| deposit | manage_deposit | 입금, 충전, 잔액, 계좌 |
| server | manage_server | 서버, VPS, 클라우드, 호스팅 |
| weather | get_weather | 날씨, 기온, 비, 눈 |
| search | search_web, lookup_docs | 검색, 찾아, 뭐야, 가격 |
| utility | get_current_time, calculate | (항상 포함) |
**폴백:** 패턴 매칭 없으면 전체 도구 사용
---
## 4. Feature Systems
### 4.1 Deposit System
**아키텍처 변경 (2026-02-05):**
- **이전**: manage_deposit 도구 → 직접 코드 실행
- **현재**: manage_deposit 도구 → **Deposit Agent 위임** (세션 기반 상담)
**흐름:**
```
사용자: "잔액 확인해줘"
메인 AI → manage_deposit(action="balance") 호출
deposit-tool.ts: action → 자연어 변환 ("잔액 확인해줘")
Deposit Agent (금융 상담사 페르소나)
- 세션 생성/조회 (D1, TTL 30분)
- OpenAI Function Calling (get_balance, get_account_info, request_deposit)
- 도구 실행 → executeDepositFunction()
응답 반환 + 세션 저장
```
**Deposit Agent Function Calling:**
| 함수 | 설명 | 권한 |
|------|------|------|
| `get_balance` | 잔액 조회 | 모든 사용자 |
| `get_account_info` | 입금 계좌 안내 | 모든 사용자 |
| `request_deposit` | 입금 신고 (금액+입금자명 필수) | 모든 사용자 |
| `get_transactions` | 거래 내역 | 모든 사용자 |
| `cancel_transaction` | 입금 취소 | 모든 사용자 |
| `get_pending_list` | 대기 목록 | 관리자 |
| `confirm_deposit` | 입금 확인 | 관리자 |
| `reject_deposit` | 입금 거절 | 관리자 |
**스마트 파싱:**
- "홍길동 5만원 입금" → 금액(50000) + 입금자명(홍길동) 즉시 추출 → confirming 상태
- "3만원 입금" → 금액만 추출 → collecting_name 상태 (입금자명 요청)
- Agent가 세션 컨텍스트 유지, 대화형 정보 수집
**자동 매칭 흐름:**
```
[시나리오 1: 사용자 먼저]
"홍길동 50000원 입금" → bank_notifications 검색
매칭 O → confirmed + 잔액↑ | 매칭 X → pending
[시나리오 2: SMS 먼저 - Email Routing]
은행 SMS → Cloudflare Email Routing → Worker (email handler)
파싱 → bank_notifications 저장
deposit_transactions 검색 (pending)
매칭 O → confirmed + 잔액↑ + 알림
매칭 X → 저장만 + 관리자 알림
```
**매칭 로직 (7글자 제한):**
- 은행 SMS는 입금자명을 7글자까지만 표시
- 매칭 시 SUBSTR(depositor_name, 1, 7) 비교
- `deposit-agent.ts:72`, `index.ts:908` 구현
**Optimistic Locking:**
- `user_deposits.version` 컬럼으로 동시성 제어
- 잔액 변경 시 version 자동 증가
- 충돌 시 자동 재시도 (최대 3회, 지수 백오프)
- Double-spending 방지 (도메인 등록 결제 적용)
**Cron 자동 취소:**
- UTC 15:00 (KST 00:00) 매일 실행
- 24시간 이상 대기 중인 입금 요청 자동 취소
- 사용자에게 Telegram 알림 전송
**입금 계좌:** 하나은행 427-910018-27104 (주식회사 아이언클래드)
### 4.2 Domain System
**manage_domain 도구 파라미터:**
```typescript
{
action: 'register' | 'check' | 'whois' | 'list' | 'info' | 'get_ns' | 'set_ns' | 'price' | 'cheapest',
domain?: string,
nameservers?: string[],
tld?: string
}
```
**action별 처리:**
| action | 설명 | 권한 |
|--------|------|------|
| `list` | 내 도메인 목록 | 소유자 |
| `info` | 도메인 상세정보 | 소유자 |
| `get_ns` | 네임서버 조회 | 공개 |
| `set_ns` | 네임서버 변경 | 소유자 |
| `check` | 가용성 확인 + 가격 | 공개 |
| `whois` | WHOIS 조회 | 공개 |
| `price` | TLD 가격 | 공개 |
| `cheapest` | 가장 저렴한 TLD 목록 (TOP 15) | 공개 |
| `register` | 등록 확인 페이지 | 사용자 |
**등록 흐름 (인라인 버튼):**
```
사용자: "example.com 등록해줘"
executeDomainAction():
1. 가용성 확인 + 가격 조회
2. 잔액 확인
3. __KEYBOARD__ 마커 포함 응답 생성
telegram.ts: inline_keyboard 생성 → "✅ 등록 확인 / ❌ 취소" 버튼 표시
index.ts: callback_query 핸들러 → domain-register.ts 호출
domain-register.ts:
- 잔액 재확인
- Optimistic Locking으로 잔액 차감
- Namecheap API 호출
- user_domains 테이블에 등록
```
**도메인 추천 (`suggest_domains`):**
```
1. GPT-4o-mini: 키워드 기반 도메인 15개 생성
2. Namecheap API: 가용성 일괄 확인
3. 등록 가능 도메인 < 10개? → 재시도 (최대 3회)
4. TLD별 가격 조회 (병렬 처리)
5. 결과 포맷팅 (등록 가능한 것만 표시)
```
**Namecheap API:**
- 엔드포인트: `namecheap-api.anvil.it.com`
- 가격 정책: Namecheap 원가 + 13%, 매일 환율 업데이트
- WHOIS Guard 자동 적용 (개인정보 비공개)
### 4.3 Server System
**서비스 정책:**
- 프리미엄 VPS만 취급 (DDoS 방어 1Tbps+ 포함)
- 저가형 VM, VPN, 프록시 → 별도 서비스 준비 중
- 추천(recommend) 기능 없음 (성능 문제로 제거)
**manage_server 도구 파라미터:**
```typescript
{
action: 'order' | 'list' | 'info' | 'delete' | 'images' | 'start' | 'stop' | 'reboot' | 'rename',
order_id?: number, // info/start/stop/reboot/delete/rename용
pricing_id?: number, // order용
label?: string, // order용
new_label?: string, // rename용
image?: string // order용 (OS 이미지)
}
```
**action별 상태:**
| action | 설명 | 상태 |
|--------|------|------|
| `order` | 서버 주문 | ✅ 구현 완료 |
| `list` | 내 서버 목록 | ✅ 구현 완료 |
| `info` | 서버 상세 정보 | ✅ 구현 완료 |
| `start` | 서버 시작 | ✅ 구현 완료 |
| `stop` | 서버 중지 | ✅ 구현 완료 |
| `reboot` | 서버 재시작 | ✅ 구현 완료 |
| `delete` | 서버 해지 (환불 포함) | ✅ 구현 완료 |
| `rename` | 서버 이름 변경 | ✅ 구현 완료 |
| `images` | OS 이미지 목록 | ✅ 구현 완료 |
**Server Agent Flow (세션 기반 관리):**
```
사용자: "서버 목록 보여줘" / "1번 서버 시작"
① 메인 AI → manage_server(action="list") 호출
② server-tool.ts: action → 자연어 변환 ("내 서버 목록 보여줘")
③ Server Agent (프리미엄 호스팅 전문가 페르소나)
- 세션 생성/조회 (D1, TTL 1시간)
- OpenAI Function Calling (9개 도구)
- 도구 실행 → executeServerAction()
④ 응답 반환 + 세션 저장
[세션 라우팅]
기존 세션이 있는 경우 → openai-service.ts에서 직접 Server Agent로 라우팅
세션 없는 경우 → 메인 AI가 manage_server 도구 호출 → Server Agent 위임
```
**Server Agent Function Calling (9개 도구):**
| 함수 | 설명 | 필수 인자 |
|------|------|----------|
| `list_servers` | 서버 목록 | - |
| `get_server_info` | 서버 상세 | order_id |
| `order_server` | 서버 주문 | pricing_id, label |
| `start_server` | 서버 시작 | order_id |
| `stop_server` | 서버 중지 | order_id |
| `reboot_server` | 서버 재시작 | order_id |
| `delete_server` | 서버 삭제 | order_id |
| `rename_server` | 이름 변경 | order_id, new_label |
| `list_images` | OS 이미지 목록 | - |
**서버 주문 상태 전이:**
| 상태 | 설정 주체 | UI 표시 |
|------|----------|---------|
| `pending` | telegram-bot | ❌ 표시 안 함 |
| `provisioning` | cloud-orchestrator | ✅ 🔄 생성 중... |
| `active` | cloud-orchestrator | ✅ 🟢 가동 중 |
| `stopped` | cloud-orchestrator | ✅ ⛔ 중지됨 |
| `failed` | cloud-orchestrator | ❌ 표시 안 함 |
| `terminated` | telegram-bot | ❌ 표시 안 함 |
**Service Binding:** `CLOUD_ORCHESTRATOR``cloud-orchestrator` (wrangler.toml)
---
## 5. API & Security
### Endpoints
**공개 엔드포인트:**
| 엔드포인트 | 메서드 | 인증 | 용도 |
|-----------|--------|------|------|
| `/health` | GET | None | Health check (최소 정보만) |
| `/webhook-info` | GET | Query Param | Telegram Webhook 상태 조회 |
| `/setup-webhook` | GET | Query Param | Telegram Webhook 설정 |
**인증 필요 엔드포인트:**
| 엔드포인트 | 메서드 | 인증 방식 | 권한 |
|-----------|--------|----------|------|
| `/webhook` | POST | Telegram Secret Token | Telegram만 호출 가능 |
| `/api/deposit/balance` | GET | X-API-Key 헤더 | namecheap-api 전용 |
| `/api/deposit/deduct` | POST | X-API-Key 헤더 | namecheap-api 전용 |
| `/api/contact` | POST | CORS | hosting.anvil.it.com만 |
| `/api/metrics` | GET | Bearer Token | 관리자 전용 |
| `/api/test` | POST | WEBHOOK_SECRET | 테스트 전용 |
**인증 방식:**
- **API Key**: `X-API-Key: {DEPOSIT_API_SECRET}`
- **Bearer**: `Authorization: Bearer {WEBHOOK_SECRET}`
- **Query Param**: `?token={BOT_TOKEN}&secret={WEBHOOK_SECRET}`
- **CORS**: `hosting.anvil.it.com`만 허용
**External Consumers:**
- **namecheap-api**: `/api/deposit/*` 호출 (도메인 등록 시 잔액 조회/차감)
- **hosting.anvil.it.com**: `/api/contact` 호출 (웹사이트 문의 폼)
- **Monitoring Tools**: `/api/metrics` 조회 (Circuit Breaker 상태)
### Rate Limiting
**Cloudflare KV 기반:**
- 사용자별 메시지 제한: 30 requests / 60초
- KV Namespace: `RATE_LIMIT_KV`
- 인스턴스 간 공유, 재시작 후 유지
- 자동 만료 (TTL), 분산 환경 일관성 보장
- 과도한 요청 시 경고 메시지 + 차단
**관리자 알림 Rate Limiting:**
- 같은 유형의 알림은 1시간에 1회만 전송
- 키: `notification:{type}:{service}`
- TTL: 3600초
### Admin Notification
**알림 유형:**
| 유형 | 트리거 조건 | 심각도 |
|------|------------|--------|
| `circuit_breaker` | Circuit Breaker OPEN 상태 전환 | 🚨 HIGH |
| `retry_exhausted` | 모든 재시도 실패 (3회) | ⚠️ MEDIUM |
| `api_error` | 치명적 API 에러 (5xx, Rate Limit) | 🔴 CRITICAL |
**통합 지점:**
- `utils/circuit-breaker.ts`: Circuit 차단 시
- `utils/retry.ts`: 재시도 실패 시
- `openai-service.ts`: OpenAI API 에러 시
- `tools/*.ts`: 외부 API 에러 시
---
## 6. Development
### Code Style
**TypeScript:**
- Strict mode 활성화 (`tsconfig.json`)
- 타입 정의: `types.ts`에 인터페이스 집중 관리
- any 사용 금지 (불가피한 경우 주석으로 이유 명시)
**네이밍:**
- 파일: `kebab-case.ts` (예: `openai-service.ts`)
- 함수: `camelCase` (예: `executeFunctionCall`)
- 상수: `UPPER_SNAKE_CASE` (예: `SUMMARY_THRESHOLD`)
- 타입/인터페이스: `PascalCase` (예: `TelegramUpdate`)
**에러 핸들링:**
```typescript
import { createLogger } from './utils/logger';
const logger = createLogger('service-name');
try {
// 작업
} catch (error) {
logger.error('작업 실패', error as Error, { context: 'data' });
return '죄송합니다. 일시적인 오류가 발생했습니다.';
}
```
**로깅:**
```typescript
import { createLogger } from './utils/logger';
const logger = createLogger('service-name');
logger.info('동작 설명', { key: 'value' });
logger.error('에러 설명', error as Error);
logger.warn('경고 메시지', { context: 'data' });
const end = logger.startTimer('작업 완료');
await doWork();
end(); // duration 자동 기록
```
### Testing
**자동화된 단위 테스트 (Vitest):**
**실행 명령어:**
```bash
npm test # 모든 테스트 실행
npm run test:watch # Watch 모드
npm run test:coverage # 커버리지 리포트
```
**테스트 범위:**
| 기능 | 테스트 케이스 | 상태 |
|------|--------------|------|
| **음수 금액 거부** | 음수/0원 입금 시도 | ✅ |
| **동시성 처리** | 동일 사용자 동시 입금, Race condition | ✅ |
| **Batch 실패 처리** | db.batch() 부분 실패 시뮬레이션 | ✅ |
| **7글자 매칭** | "홍길동아버지님" → "홍길동아버지" 자동 매칭 | ✅ |
| **관리자 권한** | 비관리자 confirm/reject/pending 차단 | ✅ |
| **거래 상태** | confirmed 거래 취소 차단 | ✅ |
| **Edge Cases** | 999,999,999원, 특수문자, 1글자 이름 | ✅ |
**Mock 전략:**
- D1 Database: Miniflare in-memory SQLite
- Environment Variables: `vitest.config.ts`에서 바인딩
- KV Namespace: Rate Limiting 모킹
**헬퍼 함수 (`tests/setup.ts`):**
```typescript
createTestUser(telegramId, username)
createBankNotification(depositorName, amount)
createDepositTransaction(userId, amount, status)
getTestDB()
```
**수동 테스트 (Webhook):**
```bash
# 1. 로컬 D1 초기화 (최초 1회)
npm run db:init:local
# 2. 로컬 서버 실행
npm run dev
# 3. 테스트 요청
curl -X POST http://localhost:8787/webhook \
-H "Content-Type: application/json" \
-H "X-Telegram-Bot-Api-Secret-Token: test-secret" \
-d '{"message":{"chat":{"id":123},"text":"테스트"}}'
```
**Web Chat Testing:** [telegram-cli/README.md](../telegram-cli/README.md) - 봇 테스트용 별도 Worker
---
## 7. Configuration
### Environment Variables (wrangler.toml)
**기본 설정:**
| 변수 | 기본값 | 설명 |
|------|--------|------|
| `SUMMARY_THRESHOLD` | 20 | 프로필 업데이트 주기 (메시지 수) |
| `MAX_SUMMARIES_PER_USER` | 3 | 유지할 프로필 버전 수 |
| `DOMAIN_OWNER_ID` | - | 도메인 관리 권한 Telegram ID |
| `DEPOSIT_ADMIN_ID` | - | 예치금 관리 권한 Telegram ID |
**외부 API 엔드포인트 (커스터마이징 가능):**
| 변수 | 기본값 |
|------|--------|
| `OPENAI_API_BASE` | `https://gateway.ai.cloudflare.com/v1/.../openai` |
| `NAMECHEAP_API_URL` | `https://namecheap-api.anvil.it.com` |
| `WHOIS_API_URL` | `https://whois-api-...vercel.app` |
| `CONTEXT7_API_BASE` | `https://context7.com/api/v2` |
| `BRAVE_API_BASE` | `https://api.search.brave.com/res/v1` |
| `WTTR_IN_URL` | `https://wttr.in` |
| `HOSTING_SITE_URL` | `https://hosting.anvil.it.com` |
### Secrets
| 변수 | 설명 | 저장 위치 |
|------|------|----------|
| `BOT_TOKEN` | Telegram Bot Token | Vault: telegram-bot |
| `WEBHOOK_SECRET` | Telegram Webhook 인증 | Vault: telegram-bot |
| `OPENAI_API_KEY` | OpenAI API 키 | - |
| `NAMECHEAP_API_KEY` | namecheap-api 래퍼 인증 | - |
| `NAMECHEAP_API_KEY_INTERNAL` | Namecheap API 키 (내부) | - |
| `BRAVE_API_KEY` | Brave Search API 키 | - |
| `DEPOSIT_API_SECRET` | Deposit API 인증 | - |
### Bindings
**KV Namespaces:**
| Binding | 설명 |
|---------|------|
| `RATE_LIMIT_KV` | Rate Limiting 저장소 |
| `SESSION_KV` | 서버 상담 세션 저장소 |
**Other Bindings:**
| Binding | 타입 | 용도 |
|---------|------|------|
| `DB` | D1 Database | 사용자/메시지/예치금 데이터 |
| `AI` | Workers AI | OpenAI 폴백용 (Llama 3.1 8B) |
| `CLOUD_ORCHESTRATOR` | Service Binding | 서버 관리 Worker |
### External Integrations
| 서비스 | 용도 | 엔드포인트 | 주의사항 |
|--------|------|-----------|----------|
| **AI Gateway** | OpenAI 프록시 | gateway.ai.cloudflare.com | 지역 제한 우회, 로그/캐시 |
| Context7 | 문서 조회 | context7.com API | - |
| Namecheap API | 도메인 백엔드 | namecheap-api.anvil.it.com | 날짜: MM/DD/YYYY → ISO 변환 |
| WHOIS API | WHOIS 조회 | whois-api-eight.vercel.app | ccSLD 미지원 |
| wttr.in | 날씨 | wttr.in | - |
| Brave Search | 검색 | api.search.brave.com | Free AI 플랜 (2,000/월) |
| Email Routing | 입금 SMS 수신 | Cloudflare Email Routing | Worker email handler로 직접 처리 |
**Cloudflare AI Gateway:**
```
Gateway ID: telegram-bot
Account ID: d8e5997eb4040f8b489f09095c0f623c
URL: gateway.ai.cloudflare.com/v1/{account_id}/telegram-bot/openai/...
```
**적용 범위:**
- ✅ Chat Completions - AI Gateway 경유
- ✅ 예치금 관리 - 코드 직접 처리 (Assistants API 제거)
- ✅ 도메인 관리 - 코드 직접 처리
---
## 8. Troubleshooting
### 자주 발생하는 에러
| 증상 | 원인 | 해결 |
|------|------|------|
| `D1_ERROR: no such table` | 스키마 미적용 | `npm run db:init` 실행 |
| `401 Unauthorized` (OpenAI) | API 키 만료/잘못됨 | `wrangler secret put OPENAI_API_KEY` |
| Webhook 응답 없음 | Secret Token 불일치 | `WEBHOOK_SECRET` 재설정 후 `/setup-webhook` |
| Function Calling 무한 루프 | tool_choice 설정 오류 | `tool_choice: "auto"` 확인 |
| 프로필 업데이트 안됨 | 메시지 20개 미만 | `/context`로 버퍼 수 확인 |
| Email Worker 파싱 실패 | SMS 형식 변경 | `index.ts`의 정규식 패턴 확인 |
| AI가 도구 호출 안 함 | 키워드 미인식 | 시스템 프롬프트 + 도구 description에 키워드 추가 |
| CORS 오류 (웹사이트 문의) | 허용된 Origin 아님 | `hosting.anvil.it.com`만 허용됨 |
### 디버깅 명령어
```bash
# D1 데이터 직접 조회 (로컬)
wrangler d1 execute telegram-conversations --local --command "SELECT * FROM users LIMIT 5"
# D1 데이터 직접 조회 (production) ⚠️ 주의
wrangler d1 execute telegram-conversations --command "SELECT * FROM users LIMIT 5"
```
---
## 9. Key Patterns
### Function Calling 추가 방법
```typescript
// 1. openai-service.ts의 tools 배열에 추가
const tools = [
{
type: "function",
function: {
name: "new_tool",
description: "도구 설명 (트리거 키워드 포함)",
parameters: { /* JSON Schema */ }
}
}
];
// 2. executeFunctionCall()에 케이스 추가
case 'new_tool':
return await executeNewTool(args, env);
```
### 프로필 시스템 (3개 요약 통합)
```
메시지 수신 → message_buffer 저장 (최대 19개)
↓ 20개 도달
┌──────────────┴──────────────┐
↓ ↓
기존 요약 3개 조회 사용자 발언만 추출
↓ ↓
└──────────→ OpenAI 통합 분석 ←┘
summaries 테이블 저장 (generation++)
구버전 삭제 (최근 3개만 유지)
```
**Context Enrichment:**
```typescript
// getConversationContext() 반환값
{
previousSummary: Summary | null, // 최신 요약 (호환성)
summaries: Summary[], // 전체 요약 (최대 3개, 최신순)
recentMessages: BufferedMessage[],
totalMessages: number,
}
```
### AI 시스템 프롬프트
**핵심 규칙 (`summary-service.ts`):**
```
- 날씨, 시간, 계산 요청은 제공된 도구를 사용하세요.
- 최신 정보, 실시간 데이터, 현재 가격, 뉴스 등은 search_web 도구로 검색하세요.
- 예치금, 입금, 충전, 잔액, 계좌 관련 요청은 반드시 manage_deposit 도구를 사용하세요.
- 도메인 추천, 도메인 제안, 도메인 아이디어 요청은 반드시 suggest_domains 도구를 사용하세요.
- 기타 도메인 관련 요청(조회, 등록, 네임서버 등)은 manage_domain 도구를 사용하세요.
- 서버, VPS, 클라우드, 호스팅 추천 요청은 반드시 manage_server 도구를 사용하세요.
- manage_deposit, manage_domain, suggest_domains, manage_server 도구 결과는 그대로 전달하세요.
```
**중요:** 메인 AI가 도구를 호출하지 않고 직접 답변하는 경우:
1. 시스템 프롬프트에 해당 키워드 추가 (`summary-service.ts`)
2. 도구 description에 키워드 명시 (`openai-service.ts` tools 배열)
### 검색 한글→영문 자동 번역
```
"판골린 VPN" → GPT-4o-mini 번역 → "Pangolin VPN" → Brave Search
```
- 한글 포함 검색어 감지 (`/[가-힣]/`)
- GPT-4o-mini로 영문 번역 (외래어/기술용어 원어 복원)
- 번역된 쿼리로 검색
- 결과에 원본+번역 표시
### 에러 핸들링 구조 (index.ts:handleMessage)
```
Webhook 수신
보안 검증 실패 → 401 반환 (로그 기록)
Rate Limit 초과 → 경고 메시지 전송 + return
사용자 DB 조회/생성 (try-catch)
↓ 실패 시 → "일시적인 오류" 메시지 전송 + return
메시지 처리 (전체 try-catch)
├── 명령어 처리 (handleCommand)
└── AI 응답 생성 (generateAIResponse)
↓ 실패 시 → "메시지 처리 오류" 메시지 전송
응답 전송 (sendMessage)
```
**중요:** 모든 DB 작업과 AI 호출은 try-catch로 감싸서 오류 시에도 사용자에게 메시지 전송
---
## OpenAPI Documentation
**OpenAPI Specification**: `openapi.yaml`
**문서 보기:**
```bash
npx swagger-ui-watcher openapi.yaml # Swagger UI
npx redoc-cli bundle openapi.yaml -o docs/api.html # Redoc HTML
npx @apidevtools/swagger-cli validate openapi.yaml # 스펙 검증
```