Telegram-бот с ИИ за 2 часа: пошаговый гайд
Создаём полноценного Telegram-бота на grammy + Claude API: от регистрации до деплоя и монетизации.
Что мы построим
Полноценный Telegram-бот с ИИ, который:
- Отвечает на вопросы пользователей с помощью Claude
- Помнит контекст диалога (последние 20 сообщений)
- Поддерживает несколько режимов (ассистент, переводчик, копирайтер)
- Имеет систему подписок (бесплатный/платный)
- Работает 24/7 на сервере
- Приносит деньги
Стек: TypeScript + grammy + Claude API + SQLite + Railway
Часть 1: Фундамент (30 минут)
Регистрация бота
- Откройте @BotFather в Telegram
- Отправьте
/newbot - Придумайте имя: "AI Помощник"
- Придумайте username:
my_ai_helper_bot - Сохраните токен:
7123456789:AAF...
Настройка бота в BotFather
/setdescription — "AI-ассистент на базе Claude. Ответы на вопросы, переводы, тексты."
/setabouttext — "Умный бот с ИИ. Бесплатно: 10 сообщений/день."
/setcommands:
start - Начать
mode - Выбрать режим
stats - Моя статистика
subscribe - Подписка
help - Помощь
Инициализация проекта
mkdir ai-telegram-bot && cd ai-telegram-bot
npm init -y
npm install grammy @anthropic-ai/sdk better-sqlite3 dotenv
npm install -D typescript @types/node @types/better-sqlite3 tsx
npx tsc --init
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": ["src/**/*"]
}
.env
TELEGRAM_TOKEN=7123456789:AAF...
ANTHROPIC_API_KEY=sk-ant-...
ADMIN_USER_ID=123456789
FREE_DAILY_LIMIT=10
Часть 2: Архитектура (20 минут)
Структура проекта
src/
├── index.ts # Точка входа
├── bot.ts # Инициализация бота
├── handlers/
│ ├── commands.ts # Обработчики команд
│ └── messages.ts # Обработчики сообщений
├── services/
│ ├── ai.ts # Сервис Claude API
│ └── db.ts # Работа с БД
├── middleware/
│ └── rateLimit.ts # Лимиты
└── config.ts # Конфигурация
База данных (SQLite)
// src/services/db.ts
import Database from 'better-sqlite3'
const db = new Database('bot.db')
// Инициализация таблиц
db.exec(\`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
telegram_id INTEGER UNIQUE,
username TEXT,
first_name TEXT,
mode TEXT DEFAULT 'assistant',
is_premium INTEGER DEFAULT 0,
messages_today INTEGER DEFAULT 0,
last_message_date TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_telegram_id INTEGER,
role TEXT,
content TEXT,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_telegram_id) REFERENCES users(telegram_id)
);
\`)
export function getOrCreateUser(telegramId: number, username?: string, firstName?: string) {
const user = db.prepare('SELECT * FROM users WHERE telegram_id = ?').get(telegramId)
if (user) return user as any
db.prepare(
'INSERT INTO users (telegram_id, username, first_name) VALUES (?, ?, ?)'
).run(telegramId, username || '', firstName || '')
return db.prepare('SELECT * FROM users WHERE telegram_id = ?').get(telegramId) as any
}
export function getMessageHistory(telegramId: number, limit: number = 20) {
return db.prepare(
'SELECT role, content FROM messages WHERE user_telegram_id = ? ORDER BY id DESC LIMIT ?'
).all(telegramId, limit).reverse() as Array<{role: string, content: string}>
}
export function saveMessage(telegramId: number, role: string, content: string) {
db.prepare(
'INSERT INTO messages (user_telegram_id, role, content) VALUES (?, ?, ?)'
).run(telegramId, role, content)
}
export function incrementMessageCount(telegramId: number) {
const today = new Date().toISOString().split('T')[0]
const user = db.prepare('SELECT * FROM users WHERE telegram_id = ?').get(telegramId) as any
if (user.last_message_date !== today) {
db.prepare(
'UPDATE users SET messages_today = 1, last_message_date = ? WHERE telegram_id = ?'
).run(today, telegramId)
return 1
}
db.prepare(
'UPDATE users SET messages_today = messages_today + 1 WHERE telegram_id = ?'
).run(telegramId)
return user.messages_today + 1
}
export function setUserMode(telegramId: number, mode: string) {
db.prepare('UPDATE users SET mode = ? WHERE telegram_id = ?').run(mode, telegramId)
}
export default db
Сервис Claude API
// src/services/ai.ts
import Anthropic from '@anthropic-ai/sdk'
const anthropic = new Anthropic()
const SYSTEM_PROMPTS: Record<string, string> = {
assistant: \`Ты — умный и дружелюбный ИИ-ассистент в Telegram.
Отвечай кратко (до 300 слов), но информативно.
Используй Markdown-форматирование для Telegram.
Если не знаешь ответ — честно скажи об этом.\`,
translator: \`Ты — профессиональный переводчик.
Определяй язык входного текста автоматически.
Если текст на русском — переводи на английский.
Если на любом другом языке — переводи на русский.
Давай только перевод, без пояснений.\`,
copywriter: \`Ты — опытный копирайтер.
Помогай с текстами: рекламные посты, описания, заголовки, email.
Предлагай несколько вариантов.
Спрашивай про целевую аудиторию и тон, если не указано.\`,
}
export async function generateResponse(
messages: Array<{role: string, content: string}>,
mode: string = 'assistant'
): Promise<string> {
const systemPrompt = SYSTEM_PROMPTS[mode] || SYSTEM_PROMPTS.assistant
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
system: systemPrompt,
messages: messages.map(m => ({
role: m.role as 'user' | 'assistant',
content: m.content,
})),
})
if (response.content[0].type === 'text') {
return response.content[0].text
}
return 'Не удалось сгенерировать ответ.'
}
Часть 3: Логика бота (40 минут)
Обработчики команд
// src/handlers/commands.ts
import { Bot, InlineKeyboard } from 'grammy'
import { getOrCreateUser, setUserMode } from '../services/db'
export function registerCommands(bot: Bot) {
bot.command('start', async (ctx) => {
const user = getOrCreateUser(
ctx.from!.id,
ctx.from?.username,
ctx.from?.first_name
)
await ctx.reply(
\`Привет, ${ctx.from?.first_name}! 👋
Я — AI-ассистент на базе Claude. Умею:
🤖 *Ассистент* — отвечу на любые вопросы
🌍 *Переводчик* — переведу текст
✍️ *Копирайтер* — помогу с текстами
Бесплатно: 10 сообщений в день.
Безлимит: /subscribe
Выбери режим: /mode\`,
{ parse_mode: 'Markdown' }
)
})
bot.command('mode', async (ctx) => {
const keyboard = new InlineKeyboard()
.text('🤖 Ассистент', 'mode_assistant')
.row()
.text('🌍 Переводчик', 'mode_translator')
.row()
.text('✍️ Копирайтер', 'mode_copywriter')
await ctx.reply('Выбери режим:', { reply_markup: keyboard })
})
bot.callbackQuery(/^mode_/, async (ctx) => {
const mode = ctx.callbackQuery.data.replace('mode_', '')
setUserMode(ctx.from.id, mode)
const modeNames: Record<string, string> = {
assistant: '🤖 Ассистент',
translator: '🌍 Переводчик',
copywriter: '✍️ Копирайтер',
}
await ctx.answerCallbackQuery(\`Режим: ${modeNames[mode]}\`)
await ctx.editMessageText(\`Режим изменён на: ${modeNames[mode]}\`)
})
bot.command('help', async (ctx) => {
await ctx.reply(
\`*Как пользоваться:*
1. Просто напишите сообщение — я отвечу
2. /mode — сменить режим работы
3. /stats — ваша статистика
4. /subscribe — безлимитный доступ
*Режимы:*
🤖 Ассистент — ответы на вопросы
🌍 Переводчик — перевод текстов
✍️ Копирайтер — помощь с текстами\`,
{ parse_mode: 'Markdown' }
)
})
}
Обработчик сообщений
// src/handlers/messages.ts
import { Bot } from 'grammy'
import {
getOrCreateUser,
getMessageHistory,
saveMessage,
incrementMessageCount
} from '../services/db'
import { generateResponse } from '../services/ai'
const FREE_LIMIT = parseInt(process.env.FREE_DAILY_LIMIT || '10')
export function registerMessageHandler(bot: Bot) {
bot.on('message:text', async (ctx) => {
const user = getOrCreateUser(
ctx.from.id,
ctx.from.username,
ctx.from.first_name
)
// Проверка лимитов
if (!user.is_premium) {
const count = incrementMessageCount(ctx.from.id)
if (count > FREE_LIMIT) {
await ctx.reply(
\`Достигнут дневной лимит (${FREE_LIMIT} сообщений).\n\nДля безлимита: /subscribe\`
)
return
}
}
// Показываем "печатает..."
await ctx.replyWithChatAction('typing')
// Сохраняем сообщение пользователя
saveMessage(ctx.from.id, 'user', ctx.message.text)
// Получаем историю
const history = getMessageHistory(ctx.from.id, 20)
try {
// Генерируем ответ
const response = await generateResponse(history, user.mode)
// Сохраняем ответ
saveMessage(ctx.from.id, 'assistant', response)
// Отправляем
await ctx.reply(response, { parse_mode: 'Markdown' })
} catch (error) {
console.error('AI Error:', error)
await ctx.reply('Произошла ошибка. Попробуйте ещё раз.')
}
})
}
Точка входа
// src/index.ts
import 'dotenv/config'
import { Bot } from 'grammy'
import { registerCommands } from './handlers/commands'
import { registerMessageHandler } from './handlers/messages'
const bot = new Bot(process.env.TELEGRAM_TOKEN!)
// Обработка ошибок
bot.catch((err) => {
console.error('Bot error:', err)
})
// Регистрация обработчиков
registerCommands(bot)
registerMessageHandler(bot)
// Запуск
bot.start()
console.log('Bot started!')
Часть 4: Запуск и тестирование (10 минут)
# Запуск в dev-режиме
npx tsx src/index.ts
Откройте бота в Telegram и протестируйте:
/start— приветствие- Отправьте вопрос — получите ответ от Claude
/mode→ Переводчик → отправьте текст на английском/mode→ Копирайтер → "Напиши пост про AI"
Часть 5: Деплой на Railway (20 минут)
Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npx tsc
CMD ["node", "dist/index.js"]
Деплой
- Создайте репозиторий на GitHub и запушьте код
- Зайдите на railway.app → New Project → Deploy from GitHub
- Добавьте Environment Variables:
TELEGRAM_TOKENANTHROPIC_API_KEYADMIN_USER_IDFREE_DAILY_LIMIT=10
- Deploy → через 2 минуты бот работает 24/7
Стоимость: ~$5/мес на Railway (Hobby plan).
Часть 6: Монетизация
Вариант 1: Подписка через бота
Подключите платёжную систему (ЮKassa, Stripe, криптоплатежи):
bot.command('subscribe', async (ctx) => {
const keyboard = new InlineKeyboard()
.text('1 месяц — 299₽', 'sub_1month')
.row()
.text('3 месяца — 699₽', 'sub_3month')
.row()
.text('Навсегда — 1499₽', 'sub_lifetime')
await ctx.reply(
\`*Безлимитная подписка:*
✅ Без ограничений по сообщениям
✅ Все режимы (ассистент, переводчик, копирайтер)
✅ Приоритетная скорость ответа
✅ История на 100 сообщений\`,
{ parse_mode: 'Markdown', reply_markup: keyboard }
)
})
Вариант 2: White-label для бизнеса
Продавайте кастомизированных ботов:
- Стоматология: бот для записи и FAQ — $2,000
- Ресторан: бот для бронирования и меню — $1,500
- Онлайн-школа: бот поддержки учеников — $3,000
Вариант 3: Реферальная программа
Приведи друга → оба получают +5 бесплатных сообщений/день
Экономика бота
| Метрика | Значение | |---------|---------| | Стоимость Claude API за сообщение | ~$0.003 | | Стоимость инфраструктуры | $5/мес | | Подписка пользователя | 299₽/мес | | Break-even | 5 платных пользователей | | 100 платных пользователей | 29,900₽/мес чистыми |
Улучшения
Голосовые сообщения:
bot.on('message:voice', async (ctx) => {
// Скачать голосовое → Whisper API → текст → Claude → ответ
})
Работа с изображениями:
bot.on('message:photo', async (ctx) => {
// Скачать фото → Claude Vision → анализ → ответ
})
Инлайн-режим:
bot.inlineQuery(/.*/, async (ctx) => {
// Ответы прямо в любом чате через @your_bot запрос
})
Хочешь изучить это глубже? Смотри наш курс — научим создавать AI-ботов, которые приносят $3,000-10,000 в месяц.