# 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` | ์„œ๋ฒ„ ์ถ”์ฒœ ์ƒ๋‹ด (30๋…„ ๊ฒฝ๋ ฅ ์•„ํ‚คํ…ํŠธ) | KV (1์‹œ๊ฐ„) | | `agents/troubleshoot-agent.ts` | ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์ƒ๋‹ด | KV (1์‹œ๊ฐ„) | | `agents/domain-agent.ts` | ๋„๋ฉ”์ธ ์ถ”์ฒœ ์ƒ๋‹ด (10๋…„ ๊ฒฝ๋ ฅ ์ปจ์„คํ„ดํŠธ) | D1 (1์‹œ๊ฐ„) | | `agents/deposit-agent.ts` | ์˜ˆ์น˜๊ธˆ ์ž…๊ธˆ ์‹ ๊ณ  ์ƒ๋‹ด (๊ธˆ์œต ์ƒ๋‹ด์‚ฌ) | D1 (30๋ถ„) | **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 | **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 **manage_server ๋„๊ตฌ ํŒŒ๋ผ๋ฏธํ„ฐ:** ```typescript { action: 'recommend' | 'order' | 'start' | 'stop' | 'delete' | 'list', tech_stack?: string[], // recommend์šฉ expected_users?: number, // recommend์šฉ use_case?: string, // recommend์šฉ traffic_pattern?: 'steady' | 'spiky' | 'growing', region_preference?: string[], budget_limit?: number, lang?: 'ko' | 'ja' | 'zh' | 'en', // ์ž๋™ ๊ฐ์ง€ server_id?: string, // order/start/stop/delete์šฉ region_code?: string, // order์šฉ label?: string // order์šฉ } ``` **action๋ณ„ ์ƒํƒœ:** | action | ์„ค๋ช… | ์ƒํƒœ | |--------|------|------| | `recommend` | ์„œ๋ฒ„ ์ถ”์ฒœ | โœ… ๊ตฌํ˜„ ์™„๋ฃŒ | | `order` | ์„œ๋ฒ„ ์‹ ์ฒญ | ๐Ÿšง ์ค€๋น„ ์ค‘ | | `start` | ์„œ๋ฒ„ ์‹œ์ž‘ | ๐Ÿšง ์ค€๋น„ ์ค‘ | | `stop` | ์„œ๋ฒ„ ์ค‘์ง€ | ๐Ÿšง ์ค€๋น„ ์ค‘ | | `delete` | ์„œ๋ฒ„ ํ•ด์ง€ | ๐Ÿšง ์ค€๋น„ ์ค‘ | | `list` | ๋‚ด ์„œ๋ฒ„ ๋ชฉ๋ก | ๐Ÿšง ์ค€๋น„ ์ค‘ | **Server Expert AI Flow (์ƒ๋‹ด ๊ธฐ๋ฐ˜ ์ถ”์ฒœ):** ``` ์‚ฌ์šฉ์ž: "์„œ๋ฒ„ ์ถ”์ฒœํ•ด์ค˜" โ†“ ๋ฉ”์ธ AI โ†’ manage_server(action="start_consultation") โ†“ KV ์„ธ์…˜ ์ƒ์„ฑ (server_session:{userId}, TTL 1h) โ†“ Server Expert AI (gpt-4o-mini + Function Calling) - ํŽ˜๋ฅด์†Œ๋‚˜: 30๋…„ ๊ฒฝ๋ ฅ ํด๋ผ์šฐ๋“œ ์•„ํ‚คํ…ํŠธ - ์šฉ๋„/๊ทœ๋ชจ ํŒŒ์•… (์ตœ๋Œ€ 2๋ฒˆ ์งˆ๋ฌธ) - [์„ ํƒ] search_trends (Brave Search) - [์„ ํƒ] lookup_framework_docs (Context7) โ†“ action="question" โ†’ ์ถ”๊ฐ€ ์ •๋ณด ์ˆ˜์ง‘ (์„ธ์…˜ ์œ ์ง€) action="recommend" โ†’ ์ž๋™ ์ŠคํŽ™ ์ถ”๋ก  โ†’ Cloud Orchestrator API ํ˜ธ์ถœ โ†’ ์„ธ์…˜ ์‚ญ์ œ โ†“ ์„œ๋ฒ„ ์ถ”์ฒœ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ ``` **์ž๋™ ์ถ”๋ก  (30๋…„ ๊ฒฝํ—˜ ๊ธฐ๋ฐ˜):** | ์šฉ๋„ | ์ถ”๋ก ๋œ tech_stack | ์ถ”๋ก ๋œ expected_users | |------|-------------------|---------------------| | ๋ธ”๋กœ๊ทธ / WordPress | `['wordpress']` | 100๋ช… | | ์‡ผํ•‘๋ชฐ / ์ด์ปค๋จธ์Šค | `['ecommerce']` | 500๋ช… | | ์ปค๋ฎค๋‹ˆํ‹ฐ / ๊ฒŒ์‹œํŒ | `['php', 'mysql']` | - | | API / ๋ฐฑ์—”๋“œ | `['nodejs', 'express']` | - | | ๊ธฐ๋ณธ๊ฐ’ | `['web']` | 100๋ช… | **์ถ”์ฒœ ๊ฒฐ๊ณผ ํฌ๋งท:** ``` ๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ ์ถ”์ฒœ ๊ฒฐ๊ณผ 1๏ธโƒฃ Standard 8GB (Anvil) โ€ข ์ŠคํŽ™: 4vCPU / 8GB / 160GB SSD โ€ข ๋ฆฌ์ „: Tokyo 3 (JP) โ€ข ๊ฐ€๊ฒฉ: โ‚ฉ69,719/์›” (๋Œ€์—ญํญ 5TB) โ€ข ์˜ˆ์ƒ ํŠธ๋ž˜ํ”ฝ: 1.7TB (ํฌํ•จ ๋ฒ”์œ„ ๋‚ด) โ€ข ์ ์ˆ˜: 95์  / ์ตœ๋Œ€ 7,500๋ช… ``` **์„œ๋ฒ„ ์ฃผ๋ฌธ ์ƒํƒœ ์ „์ด:** | ์ƒํƒœ | ์„ค์ • ์ฃผ์ฒด | 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 # ์ŠคํŽ™ ๊ฒ€์ฆ ```