diff --git a/CLAUDE.md b/CLAUDE.md index 506177a..955228b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,26 +2,175 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +## Auto-Read on Start + +**세션 시작 시 반드시 수행:** +1. `README.md`를 Read 도구로 읽어 프로젝트 전체 구조 파악 +2. 작업 대상 파일의 기존 코드 먼저 확인 + +``` +필수 읽기: 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 (Core Services, Key Patterns) | +| 새 Function Calling 도구 추가 | 양쪽 (README: 지원 기능 테이블, CLAUDE: tools 목록) | +| `schema.sql` 변경 | 양쪽 (Data Layer 섹션) | +| `wrangler.toml` 환경변수 추가 | 양쪽 (Configuration 섹션) | +| 외부 API 연동 추가/변경 | 양쪽 (External Integrations) | +| 봇 명령어 추가 | 양쪽 (Commands 섹션) | + +### 업데이트 체크리스트 +``` +[ ] CLAUDE.md - 기술 상세 (개발자용) +[ ] README.md - 사용자 가이드 (배포/운영자용) +[ ] 다이어그램/플로우 수정 필요 여부 +[ ] wrangler.toml 주석 업데이트 +``` + +--- + ## Commands ```bash npm run dev # 로컬 개발 (wrangler dev) npm run deploy # Cloudflare Workers 배포 -npm run db:init # D1 스키마 초기화 (production) +npm run db:init # D1 스키마 초기화 (production) ⚠️ 주의 npm run db:init:local # D1 스키마 초기화 (local) npm run tail # Workers 로그 스트리밍 ``` -**Secrets 설정**: +**Secrets 설정:** ```bash wrangler secret put BOT_TOKEN # Telegram Bot Token wrangler secret put WEBHOOK_SECRET # Webhook 검증용 wrangler secret put OPENAI_API_KEY # OpenAI API 키 ``` +**Webhook 설정:** +```bash +curl https://telegram-summary-bot.kappa-d8e.workers.dev/setup-webhook +curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info +``` + +--- + +## Code Style & Conventions + +### TypeScript +- **Strict mode**: `tsconfig.json`에서 strict 활성화 +- **타입 정의**: `types.ts`에 인터페이스 집중 관리 +- **any 사용 금지**: 불가피한 경우 주석으로 이유 명시 + +### 에러 핸들링 +```typescript +// 패턴: try-catch + 사용자 친화적 메시지 +try { + // 작업 +} catch (error) { + console.error('[ServiceName] 작업 실패:', error); + return '죄송합니다. 일시적인 오류가 발생했습니다.'; +} +``` + +### 로깅 규칙 +```typescript +console.log('[ServiceName] 동작 설명'); // 정상 동작 +console.error('[ServiceName] 에러 설명:', error); // 에러 +// wrangler tail로 확인 가능 +``` + +### 네이밍 +- 파일: `kebab-case.ts` (예: `openai-service.ts`) +- 함수: `camelCase` (예: `executeFunctionCall`) +- 상수: `UPPER_SNAKE_CASE` (예: `SUMMARY_THRESHOLD`) +- 타입/인터페이스: `PascalCase` (예: `TelegramUpdate`) + +--- + +## Testing + +**현재 테스트 스크립트 없음** - 수동 테스트 필수 + +### 로컬 테스트 절차 +```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":"테스트"}}' +``` + +### 배포 후 확인 +```bash +# 로그 스트리밍 +npm run tail + +# Webhook 상태 확인 +curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info +``` + +--- + +## 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`의 정규식 패턴 확인 | + +### 디버깅 명령어 +```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" +``` + +--- + ## Architecture -**Message Flow**: +**Message Flow:** ``` Telegram Webhook → Security Validation → Command/Message Router ↓ @@ -37,87 +186,148 @@ Telegram Webhook → Security Validation → Command/Message Router (summary-service.ts) ``` -**Core Services**: -- `openai-service.ts` - GPT-4o-mini + Function Calling (7개 도구: weather, search, time, calculate, lookup_docs, manage_domain, manage_deposit) -- `summary-service.ts` - 메시지 버퍼링 + 20개마다 프로필 추출 (슬라이딩 윈도우 3개) -- `security.ts` - Webhook 검증 (timing-safe comparison, timestamp validation, rate limiting 30req/min) -- `commands.ts` - 봇 명령어 핸들러 (/start, /help, /profile, /reset, /context, /stats, /debug) -- `index.ts` - Email Worker 핸들러 (SMS → 메일 파싱, 은행 알림 자동 매칭) +**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 보안 | `validateWebhook()`, `checkRateLimit()` | +| `commands.ts` | 봇 명령어 | `handleCommand()` | +| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` | -**Data Layer** (D1 SQLite): -- `users` - telegram_id 기반 사용자 -- `message_buffer` - 롤링 대화 기록 -- `summaries` - 프로필 버전 관리 (generation 추적) -- `user_deposits` - 예치금 계정 (잔액 관리) -- `deposit_transactions` - 예치금 거래 내역 (pending/confirmed/rejected) -- `bank_notifications` - 은행 SMS 파싱 결과 (자동 매칭용) +**Function Calling Tools (7개):** +| 도구 | 함수명 | 외부 API | +|------|--------|----------| +| 날씨 | `get_weather` | wttr.in | +| 검색 | `web_search` | DuckDuckGo | +| 시간 | `get_current_time` | 내장 | +| 계산 | `calculate` | 내장 | +| 문서 | `lookup_docs` | Context7 | +| 도메인 | `manage_domain` | Domain Agent → Namecheap | +| 예치금 | `manage_deposit` | D1 | -**AI Fallback**: OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환 +**Data Layer (D1 SQLite):** +| 테이블 | 용도 | 주요 컬럼 | +|--------|------|----------| +| `users` | 사용자 | telegram_id, username | +| `message_buffer` | 대화 기록 | user_id, role, content | +| `summaries` | 프로필 | user_id, generation, summary | +| `user_deposits` | 예치금 계정 | user_id, balance | +| `deposit_transactions` | 거래 내역 | user_id, amount, status | +| `bank_notifications` | SMS 파싱 | depositor_name, amount, bank | + +**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환 + +--- ## Key Patterns -**Function Calling**: `openai-service.ts`의 `tools` 배열에 JSON Schema 정의, `executeFunctionCall()`에서 실행 +### Function Calling 추가 방법 +```typescript +// 1. openai-service.ts의 tools 배열에 추가 +const tools = [ + // ... 기존 도구들 + { + type: "function", + function: { + name: "new_tool", + description: "도구 설명", + parameters: { /* JSON Schema */ } + } + } +]; -**Profile System**: 매 20개 메시지마다 사용자 발언 분석 → 관심사/목표/맥락 추출 → summaries 테이블에 generation 증가하며 저장 +// 2. executeFunctionCall()에 케이스 추가 +case 'new_tool': + return await executeNewTool(args); +``` -**Context Enrichment**: `getConversationContext()`로 이전 프로필 + 최근 10개 메시지 조합하여 AI 프롬프트에 포함 +### 프로필 시스템 흐름 +``` +메시지 수신 → message_buffer 저장 (최대 19개) + ↓ 20개 도달 + 사용자 발언만 추출 → OpenAI 분석 + ↓ + summaries 테이블 저장 (generation++) + ↓ + 구버전 삭제 (최근 3개만 유지) +``` + +### Context Enrichment +```typescript +// getConversationContext() 반환값 구조 +{ + profile: "이전 프로필 요약", + recentMessages: [ /* 최근 10개 */ ] +} +// → AI 프롬프트의 system 메시지에 포함 +``` + +--- ## Configuration `wrangler.toml` 환경변수: -- `SUMMARY_THRESHOLD`: 프로필 업데이트 주기 (기본 20) -- `MAX_SUMMARIES_PER_USER`: 유지할 프로필 버전 수 (기본 3) -- `DOMAIN_AGENT_ID`: OpenAI Assistant ID (도메인 관리 에이전트) -- `DOMAIN_OWNER_ID`: 도메인 관리 권한 Telegram ID (소유권 검증용) -- `DEPOSIT_ADMIN_ID`: 예치금 관리 권한 Telegram ID (입금 확인/거절) +| 변수 | 기본값 | 설명 | +|------|--------|------| +| `SUMMARY_THRESHOLD` | 20 | 프로필 업데이트 주기 (메시지 수) | +| `MAX_SUMMARIES_PER_USER` | 3 | 유지할 프로필 버전 수 | +| `DOMAIN_AGENT_ID` | - | OpenAI Assistant ID | +| `DOMAIN_OWNER_ID` | - | 도메인 관리 권한 Telegram ID | +| `DEPOSIT_ADMIN_ID` | - | 예치금 관리 권한 Telegram ID | + +--- ## External Integrations -- **Context7 API**: `lookup_docs` 함수로 라이브러리 문서 조회 -- **Domain Agent**: `manage_domain` 함수 → OpenAI Assistants API (`asst_MzPFKoqt7V4w6bc0UwcXU4ob`) -- **Namecheap API**: `https://namecheap-api.anvil.it.com` (도메인 목록, 가격, 네임서버) - - 날짜 형식: 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 키 중앙 관리 -- **Email Workers**: SMS → 메일 수신 → 은행 알림 파싱 (하나/KB/신한 지원) +| 서비스 | 용도 | 엔드포인트 | 주의사항 | +|--------|------|-----------|----------| +| Context7 | 문서 조회 | context7.com API | - | +| Domain Agent | 도메인 관리 | OpenAI Assistants | `asst_MzPFKoqt7V4w6bc0UwcXU4ob` | +| Namecheap API | 도메인 백엔드 | namecheap-api.anvil.it.com | 날짜: MM/DD/YYYY → ISO 변환 | +| WHOIS API | WHOIS 조회 | whois-api-eight.vercel.app | ccSLD 미지원 | +| wttr.in | 날씨 | wttr.in | - | +| DuckDuckGo | 검색 | api.duckduckgo.com | - | +| Vault | API 키 관리 | vault.anvil.it.com | - | + +--- ## Deposit System -**예치금 자동 매칭 흐름**: +**자동 매칭 흐름:** ``` -[사용자 신고] "홍길동 50000원 입금" - ↓ -bank_notifications 테이블 검색 (입금자명 + 금액 매칭) - ↓ -매칭 성공 → confirmed | 매칭 실패 → pending 대기 +[시나리오 1: 사용자 먼저] +"홍길동 50000원 입금" → bank_notifications 검색 + ↓ + 매칭 O → confirmed | 매칭 X → pending -[은행 SMS 수신] Email Worker - ↓ -SMS 파싱 → bank_notifications 저장 - ↓ -deposit_transactions 검색 (pending 상태 + 입금자명 + 금액) - ↓ -매칭 성공 → confirmed + 잔액 증가 | 매칭 실패 → 알림만 저장 +[시나리오 2: SMS 먼저] +은행 SMS → Email Worker → 파싱 → bank_notifications 저장 + ↓ + deposit_transactions 검색 (pending) + ↓ + 매칭 O → confirmed + 잔액↑ | 매칭 X → 저장만 ``` -**입금 계좌**: 하나은행 427-910018-27104 (주식회사 아이언클래드) +**입금 계좌:** 하나은행 427-910018-27104 (주식회사 아이언클래드) - Vault 경로: `secret/companies/ironclad-corp` +--- + ## Domain System -**Domain Agent 도구 (OpenAI Assistant)**: -- `list_domains` - 소유 도메인 목록 (권한 필요) -- `get_domain_info` - 도메인 상세 정보 (권한 필요) -- `get_nameservers` - 네임서버 조회 (누구나) -- `set_nameservers` - 네임서버 변경 (권한 필요) -- `get_price` - TLD/ccSLD 가격 조회 (누구나, 원화 표시) -- `check_domains` - 도메인 가용성 확인 (누구나) -- `whois_lookup` - 공개 WHOIS/RDAP 조회 (누구나) +**도구 목록:** +| 도구 | 권한 | 설명 | +|------|------|------| +| `list_domains` | 소유자 | 도메인 목록 | +| `get_domain_info` | 소유자 | 상세 정보 (만료일 등) | +| `get_nameservers` | 공개 | 네임서버 조회 | +| `set_nameservers` | 소유자 | 네임서버 변경 | +| `get_price` | 공개 | TLD 가격 (원화) | +| `check_domains` | 공개 | 가용성 확인 | +| `whois_lookup` | 공개 | WHOIS 조회 | -**가격 정책**: Namecheap 원가 + 13%, 매일 환율 업데이트 (USD→KRW) +**가격 정책:** Namecheap 원가 + 13%, 매일 환율 업데이트 -**권한 체크**: `user_domains` 테이블에서 `verified=1`인 도메인만 관리 가능 +**권한 체크:** `user_domains` 테이블 `verified=1`