Initial commit: Telegram bot with Cloudflare Workers

- OpenAI GPT-4o-mini with Function Calling
- Cloudflare D1 for user profiles and message buffer
- Sliding window (3 summaries max) for infinite context
- Tools: weather, search, time, calculator
- Workers AI fallback support
- Webhook security with rate limiting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-14 13:00:44 +09:00
commit 1e71e035e7
15 changed files with 2272 additions and 0 deletions

124
src/commands.ts Normal file
View File

@@ -0,0 +1,124 @@
import { Env } from './types';
import { getConversationContext, getLatestSummary } from './summary-service';
export async function handleCommand(
env: Env,
userId: number,
chatId: string,
command: string,
_args: string
): Promise<string> {
const config = {
threshold: parseInt(env.SUMMARY_THRESHOLD || '20', 10),
maxSummaries: parseInt(env.MAX_SUMMARIES_PER_USER || '3', 10),
};
switch (command) {
case '/start':
return `👋 안녕하세요! AI 어시스턴트입니다.
<b>특징:</b>
• 대화를 통해 당신을 이해합니다
${config.threshold}개 메시지마다 프로필 업데이트
• 무한한 대화 기억 가능
<b>명령어:</b>
/help - 도움말
/context - 현재 컨텍스트 확인
/profile - 내 프로필 보기
/stats - 대화 통계
/reset - 대화 초기화`;
case '/help':
return `📖 <b>도움말</b>
<b>기본 명령어:</b>
/start - 봇 시작
/context - 현재 컨텍스트 상태
/profile - 내 프로필 보기
/stats - 대화 통계
/reset - 모든 대화 기록 삭제
<b>사용자 프로필 시스템:</b>
• 메시지 ${config.threshold}개마다 프로필 업데이트
• 사용자 발언 위주로 관심사/목표/맥락 분석
• 의미 없는 대화(인사, 확인 등)는 제외
일반 메시지를 보내면 AI가 응답합니다.`;
case '/context': {
const ctx = await getConversationContext(env.DB, userId, chatId);
const remaining = config.threshold - ctx.recentMessages.length;
return `📊 <b>현재 컨텍스트</b>
분석된 메시지: ${ctx.previousSummary?.message_count || 0}
버퍼 메시지: ${ctx.recentMessages.length}
프로필 버전: ${ctx.previousSummary?.generation || 0}
총 메시지: ${ctx.totalMessages}
💡 ${remaining > 0 ? `${remaining}개 메시지 후 프로필 업데이트` : '업데이트 대기 중'}`;
}
case '/profile':
case '/summary': {
const summary = await getLatestSummary(env.DB, userId, chatId);
if (!summary) {
return '📭 아직 프로필이 없습니다.\n대화를 더 나눠보세요!';
}
const createdAt = new Date(summary.created_at).toLocaleString('ko-KR', {
timeZone: 'Asia/Seoul',
});
return `👤 <b>내 프로필</b> (v${summary.generation})
${summary.summary}
<i>분석된 메시지: ${summary.message_count}개</i>
<i>업데이트: ${createdAt}</i>`;
}
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 }>();
return `📈 <b>대화 통계</b>
총 메시지: ${ctx.totalMessages}
프로필 버전: ${ctx.previousSummary?.generation || 0}
저장된 프로필: ${profileCount?.cnt || 0}
버퍼 대기: ${ctx.recentMessages.length}`;
}
case '/reset': {
await env.DB.batch([
env.DB.prepare('DELETE FROM message_buffer WHERE user_id = ?').bind(userId),
env.DB.prepare('DELETE FROM summaries WHERE user_id = ?').bind(userId),
]);
return '🗑️ 모든 대화 기록과 요약이 초기화되었습니다.';
}
case '/debug': {
// 디버그용 명령어 (개발 시 유용)
const ctx = await getConversationContext(env.DB, userId, chatId);
const recentMsgs = ctx.recentMessages.slice(-5).map((m, i) =>
`${i + 1}. [${m.role}] ${m.message.substring(0, 30)}...`
).join('\n');
return `🔧 <b>디버그 정보</b>
User ID: ${userId}
Chat ID: ${chatId}
Buffer Count: ${ctx.recentMessages.length}
Summary Gen: ${ctx.previousSummary?.generation || 0}
<b>최근 버퍼 (5개):</b>
${recentMsgs || '(비어있음)'}`;
}
default:
return '❓ 알 수 없는 명령어입니다.\n/help를 입력해보세요.';
}
}