Когда мне поставили задачу автоматизировать сообщения сообщества ВК для цветочного магазина, идея на старте выглядела довольно просто: бот должен показывать готовые букеты, цветы в наличии, помогать выбрать вариант по бюджету, отвечать на типовые вопросы и при необходимости переводить диалог на живого сотрудника.
На практике основная сложность оказалась не в кнопках и не в сценариях. Больше времени ушло на ограничения API, типы токенов, доступ к Market API, интеграцию с учётной системой и выбор архитектуры. Изначально я рассматривал обычный VDS/VPS как нормальный технический вариант. Но клиент не хотел отдельно покупать сервер и добавлять ещё одну регулярную оплату, поэтому архитектуру пришлось собрать без собственного VDS — на Cloudflare Workers, Google Apps Script и Google Sheets.
Ниже — не абстрактный туториал «напишем Hello World-бота», а разбор реального проекта: что требовалось магазину, с какими ошибками я столкнулся и почему некоторые изначально очевидные решения пришлось выбросить.
Что должен был уметь бот
Для цветочного магазина обычного автоответчика недостаточно. Клиент редко пишет точную фразу вроде «хочу товар №17». Чаще запрос выглядит так: «что подарить маме», «нужен букет коллеге до 5000», «есть что-то не слишком романтичное», «что сегодня в наличии».
Поэтому я закладывал несколько уровней логики:
- раздел «Готовые букеты»;
- раздел «Цветы в наличии»;
- поиск по названию и фильтрация по бюджету;
- карточка товара: название, цена, фото и ссылка;
- кнопки «Назад» и «Написать в поддержку»;
- акции, праздники и сезонные подборки;
- приглашение в бонусную программу;
- передача диалога оператору;
- сценарный режим для точных ответов;
- опциональный ИИ-режим как fallback, когда готового ответа нет.
Отдельное требование — каталог и остатки не должны поддерживаться вручную внутри ВК. Если цена или наличие изменились в источнике, бот должен брать актуальные данные автоматически.
Почему проект пришлось собрать без VDS/VPS
Первый очевидный вариант — обычный VDS/VPS: Ubuntu, Nginx, Node.js или PHP, база данных, cron, SSL и мониторинг. Технически это был нормальный и понятный путь. Но клиент не хотел покупать отдельный сервер и платить за него каждый месяц, поэтому мне нужно было решить ту же задачу без собственного VDS.
После этого я уже подбирал архитектуру под ограничение клиента. Боту нужен публичный HTTPS endpoint для Callback API, обработка событий, источник каталога и несколько запросов к API. Оказалось, что всё это можно разнести по serverless-сервисам и не заставлять клиента покупать отдельный VDS.
В итоге архитектура получилась такой:
| Компонент | Задача |
|---|---|
| Cloudflare Worker | Принимает Callback API ВК, подтверждает сервер, проверяет секрет и передаёт событие дальше |
| Google Apps Script | Содержит бизнес-логику бота, маршрутизацию команд, работу с каталогом и отправку сообщений |
| Google Sheets | Хранит каталог, остатки, цены, FAQ, состояния диалогов и служебные данные |
| VK API | Получает сообщения сообщества и отправляет ответы пользователю |
В итоге ограничение по бюджету на инфраструктуру даже помогло упростить схему: нет отдельного Linux-сервера, который нужно обновлять, защищать и мониторить. Для небольшого магазина такой serverless-вариант оказался практичным, потому что клиенту не пришлось отдельно покупать VDS.
Шаг 1. Cloudflare Worker как входная точка Callback API
ВК должен отправлять события на публичный HTTPS URL. Cloudflare Worker для этого подходит отлично: домен подключается без настройки Nginx, сертификат выдаётся автоматически, а код запускается только при запросе.
Упрощённая версия входного обработчика выглядит так:
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const event = await request.json();
if (event.type === 'confirmation') {
return new Response(env.VK_CONFIRMATION_CODE);
}
if (event.secret !== env.VK_CALLBACK_SECRET) {
return new Response('Forbidden', { status: 403 });
}
if (String(event.group_id) !== String(env.VK_GROUP_ID)) {
return new Response('Wrong group', { status: 403 });
}
await fetch(env.GAS_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event)
});
return new Response('ok');
}
};
Здесь есть три важных момента. Код подтверждения, секрет Callback API и идентификатор сообщества нельзя хранить прямо в исходнике. Они должны находиться в secrets/environment variables. Второе — ВК ожидает быстрый ответ от webhook, поэтому тяжёлую обработку лучше не выполнять до бесконечности внутри входного запроса. Третье — нужно проверять не только тип события, но и источник.
Шаг 2. Логика бота в Google Apps Script
Worker передаёт событие в Google Apps Script. Там уже можно разбирать message_new, искать товар, хранить состояние диалога и формировать ответ.
function doPost(e) {
const event = JSON.parse(e.postData.contents);
if (event.type === 'message_new') {
const message = event.object.message;
handleMessageNew_(message);
}
return ContentService.createTextOutput('ok');
}
function handleMessageNew_(message) {
const peerId = message.peer_id;
const text = String(message.text || '').trim().toLowerCase();
if (text === 'готовые букеты') {
sendReadyBouquets_(peerId);
return;
}
if (text === 'цветы в наличии') {
sendFlowersInStock_(peerId);
return;
}
routeUserMessage_(peerId, text);
}
Для небольшого магазина этого достаточно, чтобы отделить транспортный слой от бизнес-логики. Если позже понадобится заменить Google Apps Script на полноценный backend, Worker и Callback API можно оставить без изменений.
Ошибка №1: messages.send и «random_id not int64»
Одна из первых ошибок, которую я получил при отправке ответа через VK API, была связана с random_id. API ожидает целое число формата int64. Если передать UUID, строку со случайными символами или другое неподходящее значение, сообщение не уйдёт.
В рабочем варианте я формирую именно целое число:
function makeRandomId_() {
return Math.floor(Date.now() + Math.random() * 1000);
}
И передаю его в messages.send вместе с peer_id, текстом, токеном сообщества и актуальной версией API.
Мелочь, но именно такие мелочи часто съедают больше времени, чем сама логика меню.
Ошибка №2: токен оказался привязан к IP
На одном из этапов я получил ошибку вида access_token was given to another ip address. Это особенно неприятно в serverless-архитектуре: исходящий IP Cloudflare Workers или другой платформы не обязан быть постоянным.
Главный вывод — нельзя строить serverless-интеграцию вокруг токена, который жёстко зависит от одного исходящего IP. Нужно использовать подходящий для задачи тип авторизации и заранее проверять ограничения конкретного токена.
В моём случае это стало ещё одним аргументом не связывать всю работу бота с Market API.
Ошибка №3: Market API оказался недоступен для выбранного типа доступа
Изначально идея была красивой: бот сам читает товары непосредственно из магазина ВК, ищет позиции через методы Market API и возвращает карточки. На практике я столкнулся с ошибкой Method is not available for this profile type.
Я пробовал разбираться с VK ID, отдельной авторизацией и получением другого токена. Но в какой-то момент стало понятно: я начинаю усложнять проект только ради того, чтобы использовать ВК как базу данных.
Это был неправильный уровень абстракции.
Я сделал наоборот: источником данных стала Google Sheets, а ВК остался каналом общения. Такое разделение оказалось намного надёжнее.
Как устроил каталог через Google Sheets
В таблице достаточно хранить нормализованные поля:
| Поле | Пример |
|---|---|
| id | bouquet-102 |
| type | ready_bouquet |
| title | Нежность |
| price | 4380 |
| stock | 3 |
| image | URL изображения |
| url | Ссылка на товар или страницу заказа |
| active | 1 |
После этого бот может спокойно искать по названию, отбирать товары до определённого бюджета и разделять «готовые букеты» и «цветы в наличии» без вызовов Market API на каждый шаг.
Что произошло с Posiflora
Ещё одна часть проекта — остатки из учётной системы. Я тестировал подключение к Posiflora и столкнулся сразу с двумя типами проблем: 401 Authentication required: Token not found и ответы 500 на отдельных запросах.
Здесь я тоже отказался от идеи делать бота зависимым от внешнего API в реальном времени. Если учётная система временно не отвечает, бот не должен переставать показывать каталог.
Поэтому более устойчивый вариант выглядит так:
- отдельная синхронизация получает цены и остатки из учётной системы;
- данные нормализуются и записываются в Google Sheets;
- бот читает уже подготовленную таблицу;
- если внешнее API временно недоступно, остаются последние успешно синхронизированные данные.
Это обычный принцип развязки систем: чат-бот не должен знать, почему сегодня не отвечает складской API.
Как я сделал передачу диалога живому оператору
Для магазина важно, чтобы бот не мешал сотруднику. Если менеджер уже подключился к диалогу, автоматические ответы должны остановиться.
Самый простой вариант — хранить состояние по peer_id:
bot— отвечает автоматика;operator— бот молчит;operator_until— можно автоматически вернуть бота через заданное время.
Состояние можно хранить в отдельном листе Google Sheets или в более быстром кеше. Для локального магазина таблицы вполне достаточно, а логи остаются понятными владельцу без отдельной админки.
Почему я не стал сразу делать «полностью ИИ-бота»
Для продаж цветов генеративный ИИ полезен: он может понять запрос «букет начальнице до 5000, чтобы без романтики» гораздо лучше жёсткого меню. Но отдавать ему весь диалог без ограничений — плохая идея.
Я предпочитаю двухуровневую схему:
- сначала точные сценарии, каталог, цены, остатки и FAQ;
- если точного сценария нет — ИИ получает только разрешённый контекст и помогает сформировать ответ;
- цены и наличие ИИ не придумывает, а берёт из таблицы;
- сложный или конфликтный диалог переводится оператору.
Так бот остаётся полезным, но не начинает «творчески» продавать несуществующие букеты.
Нужно ли выгружать пять лет переписки для обучения?
На старте была идея собрать огромную историю сообщений и построить FAQ по всем диалогам за несколько лет. Технически это выглядит заманчиво, но на практике там много мусора: старые цены, устаревшие акции, личные сообщения, дубли и ответы, которые уже не соответствуют текущему ассортименту.
Для магазина полезнее собрать качественную базу:
- 20–50 частых вопросов;
- правила доставки;
- оплата;
- адрес и часы работы;
- условия заказа;
- актуальная бонусная программа;
- сценарии для праздников;
- типовые рекомендации по бюджету и получателю.
Качество контекста для бота важнее его объёма.
Что в итоге получилось без VDS
Финальная архитектура не требует отдельного виртуального сервера:
Пользователь ВК
↓
VK Callback API
↓
Cloudflare Worker
↓
Google Apps Script
↓
Google Sheets
↙ ↘
Каталог FAQ / состояния
↓
VK API → ответ пользователю
Отдельным процессом можно синхронизировать остатки из Posiflora или другой CRM/учётной системы. При необходимости рядом подключается OpenRouter или другой LLM API для ИИ-ответов.
Что обязательно защитить
- не хранить access token сообщества в коде или Google Sheets;
- использовать Cloudflare Secrets и Script Properties;
- проверять
secretиgroup_idкаждого события; - не логировать токены и персональные данные целиком;
- ограничивать команды, которые может выполнять ИИ;
- не доверять цене и наличию из текста модели — только данным каталога;
- вести отдельный технический лог ошибок API.
Когда всё-таки нужен VPS
Я не считаю VPS плохим решением. Он просто не всегда нужен. Переходить на свой сервер имеет смысл, если появляются:
- большой поток сообщений;
- собственная PostgreSQL или Redis;
- длинные очереди задач;
- тяжёлая обработка изображений;
- несколько ИИ-агентов;
- headless browser;
- сложная CRM-логика и десятки интеграций;
- жёсткие требования к контролю окружения и сетевым адресам.
Для одного локального магазина схема Worker + Apps Script + Sheets закрывает задачу заметно проще.
Главные выводы из проекта
Самый важный вывод — не нужно заставлять API делать то, для чего он неудобен. Я потратил время на Market API, типы токенов и авторизацию, а затем получил более устойчивую систему, просто вынеся каталог из ВК.
Второй вывод — чат-бот лучше строить из независимых слоёв. Webhook не должен быть каталогом, каталог не должен зависеть от чата, а чат не должен падать из-за временной ошибки учётной системы.
И третий — небольшой бизнес не обязан покупать VPS только потому, что в проекте есть слово «бот». Serverless-инструментов часто достаточно, если правильно разнести ответственность между компонентами.
Частые вопросы
Можно ли сделать VK-бота вообще без сервера?
Без backend-логики — нет, но без собственного VPS — да. Cloudflare Workers и Google Apps Script фактически выполняют серверную работу как управляемые serverless-сервисы.
Подойдёт ли Google Sheets как база товаров?
Для небольшого локального магазина — да. Если ассортимент и нагрузка вырастут, таблицу можно заменить на PostgreSQL, Supabase или другую базу, не меняя внешний Callback API.
Можно ли брать товары прямо из VK Market?
Можно, если нужные методы доступны вашему типу сообщества, токена и текущим правилам VK API. В моём проекте именно на этом этапе появились ограничения, поэтому каталог был вынесен в независимый источник.
Можно ли подключить ИИ?
Да. Я бы использовал ИИ как дополнительный слой для понимания свободного текста, но цены, наличие, акции и правила доставки всегда получал бы из проверяемых данных.
Сколько стоит такая инфраструктура?
Для небольшого проекта она может укладываться в бесплатные или минимальные тарифы используемых сервисов. Реальная стоимость начинает расти прежде всего из-за ИИ API, высокой нагрузки и внешних коммерческих интеграций.
Итог
VK-бот для цветочного магазина в итоге оказался не задачей «написать несколько кнопок», а маленькой интеграционной системой. Пришлось разобраться с Callback API, токенами, ограничениями Market API, ошибками отправки сообщений и нестабильным доступом к данным учётной системы.
Но именно эти ограничения помогли собрать более правильную архитектуру: Cloudflare Worker принимает события, Google Apps Script управляет логикой, Google Sheets хранит каталог, а внешние системы подключаются отдельными синхронизациями.
В результате бот работает без отдельного VDS, а каждый компонент можно заменить или масштабировать независимо от остальных.
Если нужна похожая автоматизация для ВК, Telegram, CRM, магазина или внутренней базы — в A.S Groups я могу собрать архитектуру под конкретный бизнес-процесс, а не просто установить очередной конструктор ботов.
Обсуждение
Вопросы и комментарии
Можно уточнить детали статьи или поделиться своим опытом. Первый комментарий проходит проверку.