Telegram и 1С: боты и Telegram Web App для интеграции с базой

Telegram-боты и Telegram Web App для 1С: как связать бот с базой
Видео: Telegram-боты и Telegram Web App для 1С: как связать бот с базой

TELEGRAM 1С. БОТЫ И TELEGRAM WEB APP ДЛЯ ИНТЕГРАЦИИ С 1С

Привет, на связи Илья Низамов. Регулярно приходит один и тот же вопрос: «Можно ли прикрутить Telegram-бота к 1С?» Можно, и задача типовая — но вокруг неё накопилось несколько заблуждений: то бота путают с обходом блокировок (было актуально в 2018–2020, сейчас нет), то пытаются писать интеграцию «в лоб» без веб-хука, то не различают классического бота и Telegram Web App (Mini Apps), хотя это разные инструменты под разные задачи. Разберём оба варианта на реальных примерах и сошлёмся на официальную документацию Bot API, а не на пересказ пересказа.

Что вы узнаете

  • Когда боту с 1С действительно нужна интеграция, а когда хватит обычной рассылки
  • Чем webhook отличается от long polling и какой вариант выбрать для 1С
  • Как принять апдейт от Telegram в микросервисе на FastAPI и передать его в 1С по HTTP
  • Как принять тот же апдейт напрямую в HTTP-сервисе 1С без промежуточного Python-слоя
  • Что такое Telegram Web App (Mini Apps) и чем он отличается от обычного бота
  • Как проверить подлинность initData от Mini App перед тем, как доверять данным
  • Какие ошибки Bot API встречаются чаще всего и что за ними стоит

Зачем боту вообще ходить в 1С

Если бот не читает и не пишет в 1С, это просто рассылка с кнопками. Интеграция нужна там, где данные по обе стороны должны совпадать:

  • Уведомления по событиям 1С — сменился статус заказа, поступила оплата, товар появился на складе — 1С сама инициирует сообщение в Telegram.
  • Статус заказа по запросу — клиент пишет номер заказа, бот идёт в 1С и возвращает актуальный статус, а не копию из своей базы.
  • Согласования — руководитель получает заявку с кнопками «Согласовать/Отклонить», нажатие меняет статус документа в 1С.
  • Простой сценарий заказа — бот проводит клиента по каталогу и создаёт заказ в 1С через диалог с кнопками.
  • Бот поддержки — отвечает на вопросы по остаткам и ценам, читая данные напрямую из 1С, а не из выгрузки недельной давности.

Общий признак: как только в сценарии появляется «а что там сейчас в базе» — нужна интеграция, а не бот-визитка.

Webhook или long polling: как бот получает апдейты

Bot API предлагает два способа получать входящие сообщения, и путать их не стоит — одновременно оба не работают: если для бота настроен webhook, вызов getUpdates вернёт ошибку 409 Conflict, и наоборот.

КритерийLong polling (getUpdates)Webhook (setWebhook)
Нужен публичный HTTPS-адресНет — бот сам стучится к TelegramДа, и по одному из портов 443, 80, 88 или 8443 — другие Telegram не примет
Задержка получения сообщенийСекунды, зависит от таймаута опросаСчитаные миллисекунды — Telegram сам присылает апдейт
Где уместно держать ботаЛокальная разработка, тестовый стенд без внешнего IPПродакшен — сервер с 1С или отдельный микросервис перед ним
СертификатНе нуженНужен валидный SSL или self-signed с CN, совпадающим с доменом/IP сервера

Для продакшен-интеграции с 1С почти всегда webhook — 1С не должна в фоновом задании постоянно ходить в Telegram «а нет ли новых сообщений», это лишняя нагрузка и лишняя точка отказа. Регистрация webhook — обычный вызов метода Bot API:

curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/telegram/webhook",
    "secret_token": "случайная-строка-для-проверки-источника",
    "max_connections": 40
  }'

Параметр secret_token — не опция для галочки. Telegram продублирует его в заголовке X-Telegram-Bot-Api-Secret-Token каждого запроса, и на принимающей стороне обязательно нужно сверять это значение — иначе вызвать ваш webhook сможет кто угодно, кто узнает URL.

Вариант 1: FastAPI перед 1С

Тонкий Python-микросервис на FastAPI принимает вебхук, проверяет secret_token, разбирает апдейт и обращается к HTTP-сервису или OData 1С. Такой слой стоит держать, если апдейты нужно обрабатывать асинхронно (состояние диалога), либо если у бота уже есть логика, не завязанная на 1С.

from fastapi import FastAPI, Header, HTTPException, Request
import httpx

app = FastAPI()

SECRET_TOKEN = "случайная-строка-для-проверки-источника"
ONES_URL = "http://1c-server/base/hs/tgbot/order-status"


@app.post("/telegram/webhook")
async def telegram_webhook(
    request: Request,
    x_telegram_bot_api_secret_token: str = Header(None),
):
    # без этой проверки вебхук может дёрнуть кто угодно, кто узнал URL
    if x_telegram_bot_api_secret_token != SECRET_TOKEN:
        raise HTTPException(status_code=401, detail="bad secret token")

    update = await request.json()
    message = update.get("message", {})
    chat_id = message.get("chat", {}).get("id")
    text = message.get("text", "")

    if chat_id and text.startswith("/status"):
        order_number = text.split(maxsplit=1)[1] if " " in text else ""
        async with httpx.AsyncClient(timeout=10) as client:
            # HTTP-сервис 1С, который отдаёт статус заказа по номеру
            response = await client.get(ONES_URL, params={"order": order_number})
        return {"chat_id": chat_id, "reply": response.text}

    return {"ok": True}

Вариант 2: 1С принимает webhook напрямую

Без Python вообще: HTTP-сервис 1С сам является точкой, на которую Telegram присылает апдейты. Требуется, чтобы у сервера 1С был публичный HTTPS-адрес и сертификат — самоподписанный тоже подходит, если он загружен параметром certificate при вызове setWebhook, а CN совпадает с адресом сервера. Уместно, когда логика простая (уведомление / статус / согласование):

// Модуль HTTP-сервиса, обработчик POST /telegram/webhook
Функция ОбработатьВебхук(Запрос)

	// секретный токен должен совпадать с тем, что передан в setWebhook
	Если Запрос.Заголовки.Получить("X-Telegram-Bot-Api-Secret-Token") <> ОжидаемыйТокен() Тогда
		Ответ = Новый HTTPСервисОтвет(401);
		Возврат Ответ;
	КонецЕсли;

	ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(ТелоЗапроса);
	Апдейт = ПрочитатьJSON(ЧтениеJSON);

	// дальше — обычная работа с документами 1С: найти заказ,
	// сменить статус, отправить ответ через sendMessage Bot API

	Ответ = Новый HTTPСервисОтвет(200);
	Возврат Ответ;

КонецФункции

Практическое правило: если бот — витрина поверх 1С с диалоговой логикой и состояниями, берите FastAPI-прослойку. Если бот — просто триггер уведомлений и коротких запросов к 1С без сложного диалога, HTTP-сервис 1С справится сам, без лишнего сервиса на балансе инфраструктуры.

Telegram Web App (Mini Apps): когда классического бота недостаточно

Telegram Web App, он же Mini App — это веб-страница (обычная HTML/JS-страница на вашем сервере), которая открывается внутри Telegram по нажатию кнопки в чате или через прямую ссылку вида t.me/ваш_бот/app. В отличие от классического бота с командами и кнопками, Mini App даёт полноценный интерфейс: каталог с фильтрами, форму заказа, таблицу, а не переписку сообщение-за-сообщением. Для 1С это уместно там, где обычной кнопкой не обойтись — витрина товаров с фото и остатками, форма многошагового заказа, личный кабинет с историей покупок.

Ключевое отличие с точки зрения интеграции — авторизация. Mini App передаёт на сервер поле Telegram.WebApp.initData, и ему нельзя доверять без проверки: это данные, отправленные клиентским JS, а не подписанный Telegram запрос напрямую к вашему серверу. Проверка — HMAC-SHA256 по алгоритму из документации Bot API: секретный ключ считается как HMAC-SHA256 от строки WebAppData с токеном бота, а дальше этим ключом подписывается отсортированная по алфавиту строка key=value-пар initData:

import hashlib
import hmac
from urllib.parse import parse_qsl


def check_webapp_init_data(init_data: str, bot_token: str) -> dict | None:
    pairs = dict(parse_qsl(init_data, strict_parsing=True))
    received_hash = pairs.pop("hash", None)
    if not received_hash:
        return None

    data_check_string = "\n".join(
        f"{key}={value}" for key, value in sorted(pairs.items())
    )
    secret_key = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
    computed_hash = hmac.new(
        secret_key, data_check_string.encode(), hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(computed_hash, received_hash):
        return None  # подделка либо неверный токен

    return pairs  # содержит user, auth_date и прочие поля Mini App

Отдельно стоит проверять поле auth_date — это unix-время, когда Telegram сформировал initData, и запросы старше нескольких минут разумно отклонять как повторное воспроизведение (replay), даже если хеш формально совпал. Дальше сервер уже доверенно знает, какой пользователь Telegram сделал запрос, и может от его имени обратиться в 1С — например, отдать актуальные остатки в витрину или создать заказ через HTTP-сервис 1С.

Отправка файлов ботом: multipart и типичный баг с бинарными данными

Методы sendPhoto, sendDocument, sendAudio отправляют файл в Telegram телом multipart/form-data. Здесь новички регулярно наступают на одни и те же грабли: собирают тело запроса как обычную строку. Вот как это выглядит в «сыром» варианте — файл читается текстом в кодировке ANSI и приклеивается к строке:

// ТАК ДЕЛАТЬ НЕЛЬЗЯ — файл читается как ANSI-текст
Функция ПолучитьДвоичныеДанныеВСтрокуБезКодирования(ПутьКФайлу)
    ТекстовыйДокумент = Новый ТекстовыйДокумент;
    ТекстовыйДокумент.Прочитать(ПутьКФайлу, КодировкаТекста.ANSI, Символы.ПС);
    Возврат ТекстовыйДокумент.ПолучитьТекст();
КонецФункции

// ... склейка тела строкой и отправка:
ТекстЗапроса = ТекстЗапроса + filedataPOST + Символы.ВК + Символы.ПС;
HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.ANSI, ИспользованиеByteOrderMark.НеИспользовать);

Проблема в том, что бинарный файл (JPEG, MP3, PDF) — это не текст: как только его прогоняют через ТекстовыйДокумент и строковую кодировку, все байты старше 127 «плывут». Telegram получает повреждённый файл и отвечает ошибкой или присылает битую картинку. Строкой можно собирать только текстовые поля формы, но не сам файл.

Правильный способ — собирать тело сразу в двоичном виде через ПотокВПамяти и ЗаписьДанных, а файл добавлять как ДвоичныеДанные, не превращая в строку:

// ТАК ПРАВИЛЬНО — тело собирается двоичным потоком
Функция СформироватьТелоЗапроса(Данные)
    Тело = Новый ПотокВПамяти();
    ЗаписьДанных = Новый ЗаписьДанных(Тело, КодировкаТекста.UTF8,
        ПорядокБайтов.LittleEndian, Символы.ВК + Символы.ПС, Символы.ПС, Истина);

    ЗаписьДанных.ЗаписатьСтроку("--" + Данные["Boundary"]);
    ЗаписьДанных.ЗаписатьСтроку(СтрШаблон(
        "Content-Disposition: form-data; name=""%1""; filename=""%2""", Данные["name"], Данные["ИмяФайла"]));
    ЗаписьДанных.ЗаписатьСтроку("Content-Type:" + Данные["Content-Type"]);
    ЗаписьДанных.ЗаписатьСтроку("");

    ДвоичныеДанные = Новый ДвоичныеДанные(Данные["ИмяФайлаПолное"]); // файл — как есть, байтами
    ЗаписьДанных.Записать(ДвоичныеДанные);

    ЗаписьДанных.ЗаписатьСтроку("");
    ЗаписьДанных.ЗаписатьСтроку("--" + Данные["Boundary"] + "--");
    ЗаписьДанных.Закрыть();

    Возврат Тело.ЗакрытьИПолучитьДвоичныеДанные();
КонецФункции

// и отправляем именно двоичное тело:
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(СформироватьТелоЗапроса(Данные));

Правило простое: файл в multipart — всегда двоичным телом (УстановитьТелоИзДвоичныхДанных), никогда через строку и тем более не через ANSI. Один и тот же приём работает для фото, аудио, документов и видео.

Голосовые заказы: приём voice и распознавание речи (STT)

Отдельный сильный сценарий — голосовой бот: пользователь надиктовывает заказ, бот скачивает аудио, прогоняет через распознавание речи (STT) и получает текст, который дальше можно разобрать (в том числе через LLM) и превратить в документ 1С. 1С здесь работает прослойкой между Telegram и STT-сервисом.

Голосовое приходит в апдейте как объект voice с file_id. Сам файл Telegram по апдейту не отдаёт — сначала методом getFile получаем file_path, затем скачиваем файл бинарно с файлового CDN (file/bot<token>/<file_path>) и сохраняем OGG/Opus в регистр.

Дальше двоичные данные аудио уходят на внешний STT-сервис обычным multipart-запросом (тем же приёмом с двоичным телом, что и выше) — а обратно приходит распознанный текст:

Функция ОтправитьHTTPЗапрос(ДвоичныеДанныеФайла) Экспорт
    HTTPСоединение = Новый HTTPСоединение("10.10.1.10", 8000, , , , 30);
    Разделитель = СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");

    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", СтрШаблон("multipart/form-data; boundary=%1", Разделитель));
    HTTPЗапрос = Новый HTTPЗапрос("get_text_from_audio/", Заголовки);

    ПотокВПамяти = Новый ПотокВПамяти();
    ЗаписьДанных = Новый ЗаписьДанных(ПотокВПамяти, , , Символы.ВК + Символы.ПС, "");
    ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель);
    ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""uploaded_file""; filename=""test.oga""");
    ЗаписьДанных.ЗаписатьСтроку("Content-Type: audio/ogg");
    ЗаписьДанных.ЗаписатьСтроку("");
    ЗаписьДанных.Записать(ДвоичныеДанныеФайла);
    ЗаписьДанных.ЗаписатьСтроку("");
    ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--");
    ЗаписьДанных.Закрыть();

    HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные());
    Результат = HTTPСоединение.ВызватьHTTPМетод("POST", HTTPЗапрос);
    Возврат Результат.ПолучитьТелоКакСтроку();
КонецФункции

Дальше распознанный текст можно скормить LLM вместе с выгруженным каталогом номенклатуры — и получить готовый разбор заказа по позициям. Голосовые боты, приём файлов и связку с LLM подробно собираем на курсе «Telegram-боты в 1С».

А что с блокировкой Telegram в России

Отдельно — если вы искали, как обойти блокировку Telegram: с 2018 по 2020 год мессенджер был заблокирован Роскомнадзором, тогда это была реальная и частая проблема. Сейчас блокировка снята, доступ к Telegram и к api.telegram.org в большинстве регионов работает без обходных путей, и техническая часть этой статьи актуальна и без VPN. Если у вас нестабильный доступ именно к API из конкретной инфраструктуры — это, как правило, вопрос сетевой настройки конкретного хостинга или прокси, а не блокировки на уровне страны.

Что дальше

Если нужен именно классический бот — уведомления, статусы, согласования, диалоговый заказ — практическая реализация webhook, обработки апдейтов и связки с HTTP-сервисом 1С разобрана на курсе «Telegram-бот и 1С»: от регистрации бота до рабочей интеграции с реальной базой.

Если нужен полноценный интерфейс внутри Telegram — витрина, форма заказа, личный кабинет — это уже про Mini Apps, и этому посвящён курс «Telegram Web App и 1С»: проверка initData, авторизация пользователя и обмен данными с 1С в реальном сценарии.

Частые вопросы