Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
49479ec
fix: switch AI chat from YandexGPT to DeepSeek via OpenRouter
Mar 30, 2026
30a33bb
fix: logo and guide download form
Mar 30, 2026
21a0fbd
fix: update guide form text - guide in development
Mar 30, 2026
1756977
Add detailed case studies page (/cases) with 5 SMB examples
Mar 31, 2026
905f403
Add detailed case studies page (/cases) with 5 SMB examples
Mar 31, 2026
c3bbafa
Add pricing calculator to /calculator
Apr 1, 2026
0bb2e4f
Fix: Filter out internal lead scoring tags (COLD/WARM/HOT) from AI re…
Apr 1, 2026
c679fda
Calculator v9.1: Site integration with header, Bitrix24 webhook, JS f…
Apr 1, 2026
5360161
Calculator v9.2: Analytics + Mobile CRO + UTM prefill
Apr 1, 2026
db6707d
Calculator v9.3: Fix embedded.html for /tariffs integration
Apr 1, 2026
7988883
Fix: ArticleCTA button visibility, hide checklist, fix all article da…
Apr 1, 2026
495a155
Fix: iframe auto-height with setTimeout after step transitions
Apr 1, 2026
5efcc7e
Add blog article: AI automation for 20-year-olds
Apr 2, 2026
c0fdc04
Fix: Remove BackButton from blog layout (was showing on main blog page)
Apr 2, 2026
d9543bd
Fix: Add 'Back to Home' button on blog main page
Apr 2, 2026
4100096
Add navigation to calculator from blog
Apr 2, 2026
72362e8
Transform blog into conversion funnel
Apr 2, 2026
eb65063
Transform 5-oshibok article into high-conversion funnel
Apr 2, 2026
8792ccd
Fix: Move inline scripts to client components for 5-oshibok
Apr 2, 2026
4939dbc
Fix: TypeScript error in Header.tsx
Apr 2, 2026
6e15806
Update pricing to Lite/Base/AI/Enterprise + clickable contacts
Apr 2, 2026
f768d17
Update pricing, chatbot, social proof, checklist, dark theme
Apr 2, 2026
c4abaf3
Fix prices, ROI calculator, WhyUs text
Apr 2, 2026
05c6464
v9.3: Restructure tariffs - AI-Assist 129k, remove Standard, add upse…
Apr 2, 2026
c24e92f
v9.4: Sync pricing with main page - AI-Assist 129k, Enterprise 249k
Apr 2, 2026
63fd5cd
Fix ROICalculator comment: Sales -> Base
Apr 2, 2026
8fbceaa
Force rebuild to fix cache
Apr 2, 2026
577cb14
Update pricing texts: remove 'от' from fixed prices, add timelines, b…
Apr 2, 2026
fbe1e2b
Fix TypeScript: add highlight property to PricingPlan
Apr 2, 2026
2e4793e
Force rebuild ROICalculator to fix cache
Apr 2, 2026
c3baaa0
Clean rebuild - remove .next cache
Apr 2, 2026
9e9c4f9
ROICalculator: force cache invalidation
Apr 2, 2026
ccc22e1
ROICalculator: add comment to force rebuild
Apr 2, 2026
5853e6d
Empty commit to trigger redeploy
Apr 2, 2026
aab2adb
Clear all caches and force rebuild
Apr 2, 2026
897f58f
Fix TypeScript error - add comment properly
Apr 2, 2026
28e1b6f
ROICalculator: rename BASE_TARIFF_PRICE to BASE_PRICE to force cache …
Apr 2, 2026
e088740
Fix Calculator.tsx: change Sales to Base, 129k to 39k
Apr 2, 2026
d2fbbeb
Update calculator: dark theme, Lucide icons, fix colors
timoshinoleg-eng Apr 3, 2026
780f0a3
Update calculator: dark theme, Lucide icons, fix colors
timoshinoleg-eng Apr 3, 2026
0391ecd
Add GitHub Actions auto-deploy workflow
timoshinoleg-eng Apr 3, 2026
dd229cf
Add GitHub Actions auto-deploy workflow
timoshinoleg-eng Apr 3, 2026
23718ff
Rollback: revert to pre-Swarm state
Apr 4, 2026
a7e6784
Add llms.txt for LLM crawlers
Apr 4, 2026
67aaec1
feat: Stage 3 улучшения из Swarm + доработки
Apr 4, 2026
c83712e
Merge Stage 3: Blog content, cases, SEO, VK integration
Apr 4, 2026
7835596
Add hero images to cases and blog articles
Apr 4, 2026
9ec8fa1
Fix duplicate Image import in sekundy-reshayut article
Apr 4, 2026
9073d37
Fix case images visibility: add z-index and overflow-hidden
Apr 4, 2026
a465b0d
Fix case images: remove negative margins, proper card padding
Apr 4, 2026
ae119d6
Crop case images to 16:9 horizontal format (1200x675)
Apr 4, 2026
f3502c2
Add detailed Midjourney/Slides prompt for case illustrations (Option B)
Apr 4, 2026
565f643
Update agent API route with enhanced prompt filtering and lead scoring
qwen-intl Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Deploy to Production

on:
push:
branches: [main, master]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Deploy to Yandex Cloud
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/chatbot24
git pull origin main
pm2 restart chatbot24
pm2 save
23 changes: 1 addition & 22 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1 @@
# Secrets
scripts/backup-postgres.sh
update_token.sh
.env
.env.local
*.pem
*.key

# Backups
*.sql
*.backup
*.bak

# Docker
Dockerfile
docker-compose.yml

# Temp
ENDOFFILE
PATCHEOF
SCHEMA
.vercel
Nothing should be ignored based on the provided file changes. The modified file 'app/api/agent/route.ts' is a source code file that should be tracked in version control. No build artifacts, dependencies, or temporary files are present in the changes.
28 changes: 28 additions & 0 deletions ENV_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Environment Variables Setup

## Для Vercel (Production)

Добавь переменную окружения в Vercel Dashboard:

1. Иди на https://vercel.com/dashboard
2. Выбери проект `march`
3. Settings → Environment Variables
4. Добавь:
- Name: `OPENROUTER_API_KEY`
- Value: `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`
- Environment: Production (и Preview, если нужно)

5. Save → Redeploy

## Для локальной разработки

Файл `.env.local` уже создан с ключом.

## Проверка

После деплоя проверь, что AI отвечает в чате на сайте.

## Fallback

Если OpenRouter недоступен, чат покажет fallback сообщение:
"Извините, возникла техническая проблема..."
159 changes: 73 additions & 86 deletions app/api/agent/route.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,56 @@
import { NextRequest, NextResponse } from 'next/server';
import { generateYandexGPTResponse } from '@/lib/yandexgpt';
import { generateOpenRouterResponse } from '@/lib/openrouter';

export const runtime = 'edge';
// DeepSeek через OpenRouter - системный промпт
const SYSTEM_PROMPT = `Ты — Алексей, старший консультант по автоматизации бизнеса в компании ChatBot24.

// Разрешённые origin для CORS
const ALLOWED_ORIGINS = [
'https://www.chatbot24.su',
'https://chatbot24.su',
'http://localhost:3000',
];

// YandexGPT Pro - полностью российское решение
const SYSTEM_PROMPT = `Ты — Алексей, старший консультант по автоматизации бизнеса в компании ChatBot24. Ты помогаешь предпринимателям внедрить чат-ботов для обработки заявок.
=== ВАЖНОЕ ПРАВИЛО ===
Отвечай клиенту ТОЛЬКО текстом для диалога. Никаких внутренних оценок, меток, категорий (HOT/WARM/COLD), баллов или служебных заметок в ответе быть НЕ должно.

=== ТВОЯ ЗАДАЧА ===
Провести диалог так, чтобы:
1. Быстро понять бизнес клиента и его проблему
2. Показать, как чат-бот решит эту проблему
3. Направить клиента заполнить бриф (это 6 коротких вопросов с кнопками) или получить контакты
4. Оценить готовность клиента к покупке (внутренняя оценка, клиенту НЕ показывать)
1. Понять бизнес клиента и его проблему
2. Показать, как чат-бот решит эту проблему
3. Направить заполнить бриф (6 вопросов с кнопками на сайте) или получить контакты
4. internal_note: оценить готовность клиента (в систему, НЕ клиенту)

=== БРИФ НА САЙТЕ ===
На сайте есть функция "Заполнить бриф" — это 6 вопросов с готовыми вариантами ответа:
1. Сфера бизнеса (магазин, услуги, образование, медицина, недвижимость)
2. Каналы связи (Telegram, WhatsApp, Instagram, сайт)
3. Сколько заявок в день
4. Задачи бота (FAQ, запись, расчет стоимости, CRM)
5. Есть примеры ботов
6. Бюджет

После брифа клиент оставляет телефон и получает предложение.

Когда предлагать бриф:
- Клиент спросил про цену
- Клиент сказал "интересно", "хочу", "давайте"
- Прошло 3-4 сообщения и клиент активен
- Не предлагай бриф сразу в первом сообщении

=== ВНУТРЕННЯЯ ОЦЕНКА ЛИДА (только для системы, клиенту НЕ говорить) ===
Оценивай silently по критериям:
- HOT: клиент говорит "готов", "когда начнем", "пришлите договор", называет конкретный бюджет 100к+, срочность "нужно сейчас"
- WARM: клиент спрашивает цену, сроки, говорит "интересно", "давайте обсудим", бюджет 50-100к
- COLD: клиент только собирает информацию, "посмотрю", "подумаю", бюджет до 50к или не называет

Оценку отправляй только в систему. КЛИЕНТУ НЕ ПОКАЗЫВАЙ.

=== ЧТО ПРЕДЛАГАЕМ ===
ТОЛЬКО чат-боты и автоматизация заявок.

Тарифы:
- Простой бот: от 49 000₽ — один канал, базовые ответы, передача заявок
- Стандарт: от 129 000₽ — несколько каналов, умные ответы, подключение к вашей системе
- Премиум: от 240 000₽ — AI-помощник, работает 24/7, любые подключения

Точную цену даст менеджер после брифа.

=== СФЕРЫ И ПРИМЕРЫ ===
РИТЕЙЛ: "Салон красоты — +35% конверсии", "Магазин одежды — 200 заказов/день без менеджеров"
УСЛУГИ: "Клининг — с 2 часов до 5 минут на заявку", "Автосервис — +40% записей"
ОБРАЗОВАНИЕ: "Онлайн-школа — 120 часов экономии/месяц", "Детский центр — автозапись на пробные"
МЕДИЦИНА: "Стоматология — -60% неявок", "Медцентр — в 3 раза быстрее записи"
НЕДВИЖИМОСТЬ: "Агентство — +25% сделок", "Застройщик — ответы покупателям ночью"

=== СТИЛЬ ОБЩЕНИЯ ===
- Обращайся на "вы"
- Отвечай коротко: 2-4 предложения
- Никакого жаргона: вместо "CRM" — "ваша система", вместо "интеграция" — "подключим"
- Говори конкретно: "экономия 10 часов в неделю" вместо "экономия времени"

=== ЧТО НЕЛЬЗЯ ===
❌ Говорить про оценку (HOT/WARM/COLD, баллы)
❌ Использовать: лид, квалификация, сценарий, воронка, интеграция, RAG, нейросеть
❌ Обещать сроки без уточнений
- Клиент спросил цену → "Чтобы назвать точную цену, нужно понять задачи. Заполните короткий бриф — 6 вопросов с кнопками, займёт 2 минуты"
- Клиент сказал "интересно", "хочу" → "Отлично! Лучше заполнить бриф — там 6 вопросов с вариантами ответов. После него менеджер подготовит расчёт"
- Не предлагай бриф в первом сообщении

=== ТАРИФЫ (называй при запросе цены) ===
Lite — от 19 900₽: 1 мессенджер, до 50 заявок/мес, базовый бот
Base — от 39 000₽ ⭐: до 3 каналов, до 200 заявок, умный бот с CRM
AI — от 69 000₽: AI-ассистент, безлимитные каналы, до 500 заявок
Enterprise — от 129 000₽: для медицины и крупных проектов, on-premise

Точную цену называй только после понимания задачи или после брифа.

=== ВНУТРЕННЯЯ ОЦЕНКА (НИКОГДА НЕ ПОКАЗЫВАЙ КЛИЕНТУ) ===
internal_only: HOT = "готов", "договор", бюджет 100к+, срочно
internal_only: WARM = спрашивает цену, "интересно", бюджет 50-100к
internal_only: COLD = "подумаю", "посмотрю", бюджет до 50к

Эти метки пишутся ТОЛЬКО в internal_note для системы. В ответе клиенту их быть НЕ может.

=== СТИЛЬ ===
- Коротко: 2-4 предложения
- Без жаргона: вместо "CRM" → "ваша система", вместо "интеграция" → "подключим"
- Конкретно: "экономия 10 часов в неделю"

=== ПРИМЕРЫ ПО СФЕРАМ ===
Ритейл: "Салон красоты +35% конверсии", "Магазин одежды 200 заказов/день без менеджеров"
Услуги: "Клининг с 2 часов до 5 минут на заявку", "Автосервис +40% записей"
Образование: "Онлайн-школа 120 часов экономии/месяц"
Медицина: "Стоматология -60% неявок"
Недвижимость: "Агентство +25% сделок"

=== НЕЛЬЗЯ ===
❌ Показывать оценку (HOT/WARM/COLD), баллы, internal_note
❌ Слова: лид, квалификация, сценарий, воронка, интеграция, RAG, нейросеть
❌ Называть цену до понимания задачи
❌ Предлагать сайты, дизайн, рекламу

=== ЧТО МОЖНО ===
✅ "Экономит 10 часов в неделю"
✅ "Отвечает клиентам ночью"
✅ "Не пропускает заявки"
✅ "У нас был похожий проект — результат +35%"`;
❌ Предлагать сайты, дизайн, рекламу`;

// CORS headers helper
function getCorsHeaders(origin: string | null): Record<string, string> {
Expand All @@ -104,7 +75,7 @@ export async function OPTIONS(req: NextRequest) {
});
}

// POST /api/agent - обработка сообщения через YandexGPT Pro
// POST /api/agent - обработка сообщения через DeepSeek (OpenRouter)
export async function POST(req: NextRequest) {
const startTime = Date.now();
const origin = req.headers.get('origin');
Expand All @@ -128,26 +99,42 @@ export async function POST(req: NextRequest) {
);
}

// Форматируем сообщения для YandexGPT
const yandexMessages = messages.map((msg: { role: string; content?: string; text?: string }) => ({
role: msg.role === 'assistant' ? 'assistant' : 'user',
text: msg.content || msg.text,
}));

// Генерируем ответ через YandexGPT Pro
const response = await generateYandexGPTResponse(yandexMessages as any, {
// Форматируем сообщения для OpenRouter
const openRouterMessages = [
{
role: 'system' as const,
content: SYSTEM_PROMPT,
},
...messages.map((msg: { role: string; content?: string; text?: string }) => ({
role: msg.role === 'assistant' ? 'assistant' as const : 'user' as const,
content: msg.content || msg.text || '',
})),
];

// Генерируем ответ через DeepSeek (OpenRouter)
let response = await generateOpenRouterResponse(openRouterMessages, {
temperature: 0.6,
maxTokens: 2000,
model: 'pro', // YandexGPT Pro
model: 'deepseek-chat',
});

// Фильтруем внутренние системные теги (COLD/WARM/HOT, internal_note, **Система:**) — клиент не должен видеть
response = response
.split('\n')
.filter(line => !line.match(/\*\*Система:\*\*/i))
.filter(line => !line.match(/\(.*(COLD|WARM|HOT).*\)/i))
.filter(line => !line.match(/internal_note|internal_only/i))
.filter(line => !line.match(/^\s*(COLD|WARM|HOT|Оценка|Категория|Баллы|Score|Rating):/i))
.join('\n')
.trim();

// Оцениваем лид (упрощённая версия)
const leadScore = evaluateLead(response);

const result = {
response,
provider: 'yandexgpt-pro',
model: 'yandexgpt-pro',
provider: 'openrouter',
model: 'deepseek-chat',
leadScore,
latency: Date.now() - startTime,
};
Expand All @@ -157,7 +144,7 @@ export async function POST(req: NextRequest) {
});

} catch (error) {
console.error('YandexGPT API Error:', error);
console.error('OpenRouter API Error:', error);

// Возвращаем fallback ответ
return NextResponse.json(
Expand Down
9 changes: 9 additions & 0 deletions app/api/lead/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from 'next/server';
import { sendLeadToTelegram } from '@/lib/telegram-chat';

export async function POST(req: NextRequest) {
try {
Expand Down Expand Up @@ -35,6 +36,14 @@ export async function POST(req: NextRequest) {
hasBriefData: !!businessType
});
console.log("businessType value:", businessType, "hasBriefData:", !!businessType);

// Отправляем в Telegram
await sendLeadToTelegram({
type: businessType ? 'consultation' : 'callback',
name,
email,
phone,
});
const briefInfo = businessType ? `
📋 ДАННЫЕ БРИФА:
Сфера: ${businessType || 'не указана'}
Expand Down
Loading