Разработка20 марта 2026 14 мин

Telegram-бот с ИИ за 2 часа: пошаговый гайд

Создаём полноценного Telegram-бота на grammy + Claude API: от регистрации до деплоя и монетизации.

Что мы построим

Полноценный Telegram-бот с ИИ, который:

  • Отвечает на вопросы пользователей с помощью Claude
  • Помнит контекст диалога (последние 20 сообщений)
  • Поддерживает несколько режимов (ассистент, переводчик, копирайтер)
  • Имеет систему подписок (бесплатный/платный)
  • Работает 24/7 на сервере
  • Приносит деньги

Стек: TypeScript + grammy + Claude API + SQLite + Railway

Часть 1: Фундамент (30 минут)

Регистрация бота

  1. Откройте @BotFather в Telegram
  2. Отправьте /newbot
  3. Придумайте имя: "AI Помощник"
  4. Придумайте username: my_ai_helper_bot
  5. Сохраните токен: 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 и протестируйте:

  1. /start — приветствие
  2. Отправьте вопрос — получите ответ от Claude
  3. /mode → Переводчик → отправьте текст на английском
  4. /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"]

Деплой

  1. Создайте репозиторий на GitHub и запушьте код
  2. Зайдите на railway.app → New Project → Deploy from GitHub
  3. Добавьте Environment Variables:
    • TELEGRAM_TOKEN
    • ANTHROPIC_API_KEY
    • ADMIN_USER_ID
    • FREE_DAILY_LIMIT=10
  4. 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 в месяц.

Понравилась статья?

Переходи от чтения к практике. 7-дневный челлендж — бесплатно.

Блог NEURO BRATVA — статьи про AI, вайбкодинг и заработок на ИИ