feat: add /history and /search commands

- Add /history [N] command to view recent conversation history
  - Default 20 messages, max 100
  - Shows date/time, role (user/bot), and message preview
- Add /search <keyword> command to search conversations
  - Uses extractKeywords() for smart keyword extraction
  - Shows up to 15 results with date and content preview
- Update /stats command to use new conversation-storage service
  - Shows total message count, first/last message dates
  - Shows archived summary count
  - Links to /history and /search commands
- Update /start and /help commands to include new commands
- Import conversation-storage functions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-02-05 12:32:45 +09:00
parent 92ddc239ef
commit e764dd683e

View File

@@ -1,5 +1,11 @@
import { Env } from './types';
import { getConversationContext, getLatestSummary } from './summary-service';
import {
getConversationHistory,
searchConversations,
getConversationStats,
extractKeywords
} from './services/conversation-storage';
export async function handleCommand(
env: Env,
@@ -26,6 +32,8 @@ export async function handleCommand(
/server - 내 서버 목록
/security - DDoS 방어 현황
/phantomx - PhantomX VPN
/history - 대화 기록
/search - 대화 검색
무엇을 도와드릴까요?`;
@@ -38,6 +46,8 @@ export async function handleCommand(
/server - 내 서버 목록
/security - DDoS 방어 서비스
/phantomx - PhantomX VPN 서비스
/history [N] - 대화 기록 (기본 20개)
/search 키워드 - 대화 검색
<b>자연어로 요청:</b>
• "도메인 등록" - 도메인 검색/등록
@@ -90,19 +100,88 @@ ${summary.summary}
<i>업데이트: ${createdAt}</i>`;
}
case '/history': {
const limit = _args ? parseInt(_args, 10) : 20;
const validLimit = Math.min(Math.max(limit, 1), 100); // 1-100 제한
const messages = await getConversationHistory(env.DB, chatId, validLimit);
if (messages.length === 0) {
return '📜 저장된 대화가 없습니다.';
}
const formatted = messages.map(m => {
const time = m.created_at
? new Date(m.created_at).toLocaleString('ko-KR', {
timeZone: 'Asia/Seoul',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
: '';
const role = m.role === 'user' ? '나' : '봇';
const content = m.content.length > 50
? m.content.substring(0, 50) + '...'
: m.content;
return `[${time}] ${role}: ${content}`;
}).join('\n');
return `📜 <b>최근 대화</b> (${messages.length}개)\n\n${formatted}\n\n/history 50 으로 더 보기`;
}
case '/search': {
if (!_args || _args.trim().length < 2) {
return '🔍 검색어를 입력해주세요.\n예: /search 도메인';
}
const keywords = extractKeywords(_args);
if (keywords.length === 0) {
return '🔍 유효한 검색어가 없습니다.';
}
const results = await searchConversations(env.DB, chatId, keywords, 15);
if (results.length === 0) {
return `🔍 "${_args}" 검색 결과가 없습니다.`;
}
const formatted = results.map(m => {
const date = m.created_at
? new Date(m.created_at).toLocaleDateString('ko-KR')
: '';
const content = m.content.length > 60
? m.content.substring(0, 60) + '...'
: m.content;
return `[${date}] ${content}`;
}).join('\n');
return `🔍 <b>"${_args}"</b> 검색 결과 (${results.length}건)\n\n${formatted}`;
}
case '/stats': {
const ctx = await getConversationContext(env.DB, userId, chatId);
const profileCount = await env.DB
.prepare('SELECT COUNT(*) as cnt FROM summaries WHERE user_id = ?')
.bind(userId)
.first<{ cnt: number }>();
const stats = await getConversationStats(env.DB, chatId);
if (!stats) {
return '📈 아직 대화 기록이 없습니다.';
}
const firstDate = stats.first_message_at
? new Date(stats.first_message_at).toLocaleDateString('ko-KR')
: '없음';
const lastDate = stats.last_message_at
? new Date(stats.last_message_at).toLocaleDateString('ko-KR')
: '없음';
return `📈 <b>대화 통계</b>
총 메시지: ${ctx.totalMessages}
프로필 버전: ${ctx.previousSummary?.generation ?? 0}
저장된 프로필: ${profileCount?.cnt ?? 0}
버퍼 대기: ${ctx.recentMessages.length}`;
총 메시지: ${stats.message_count.toLocaleString()}
첫 대화: ${firstDate}
최근 대화: ${lastDate}
아카이브된 요약: ${stats.archived_summaries}
/history - 대화 기록 보기
/search 키워드 - 대화 검색`;
}
case '/domain': {