GPT-3 чат-бот в Telegram: как создать и подключить к API

В этой статье

Если под «GPT-3 чат-ботом» вы имеете в виду своего Telegram-бота, который отвечает через OpenAI API, схема выглядит так: Telegram принимает сообщение, ваш Python-код передаёт его модели, а затем отправляет полученный текст обратно пользователю. Это не готовый бот из каталога и не подписка ChatGPT, а небольшая собственная интеграция.

Что понадобится

Нужны три вещи:

  • аккаунт в Telegram;
  • токен бота от @BotFather;
  • отдельный ключ OpenAI API с доступом к выбранной модели.

Токен Telegram — это пароль от управления ботом. Не вставляйте его в исходный код, Git-репозиторий и сообщения об ошибках. То же правило относится к OPENAI_API_KEY: ключ хранится в переменных окружения и не выводится в лог.

Если вам нужно только личное общение с ChatGPT, а не собственный Telegram-бот, можно посмотреть каталог услуг Amber Market. В нём есть карточка ChatGPT с месячными вариантами; условия и цены проверьте перед оформлением. Такая подписка не является пополнением OpenAI API и сама по себе не выдаёт ключ для созданного вами бота: для программы всё равно нужен отдельный доступ к API.

Для примера нужен Python 3.10 или новее и официальная библиотека OpenAI. Установите её в виртуальном окружении:

python -m venv .venv
source .venv/bin/activate       # Windows: .venv\\Scripts\\activate
python -m pip install openai

Создайте бота через @BotFather командой /newbot и сохраните выданный токен. Затем задайте переменные окружения. В PowerShell это можно сделать так:

$env:TELEGRAM_BOT_TOKEN = "токен-от-BotFather"
$env:OPENAI_API_KEY = "ключ-OpenAI"
$env:OPENAI_MODEL = "gpt-3.5-turbo-0125"

В Linux или macOS используются команды export. Значение OPENAI_MODEL здесь только пример: gpt-3.5-turbo-0125 относится к legacy-линейке GPT-3.5, поэтому сначала проверьте, доступен ли этот идентификатор в вашем проекте по актуальной документации и каталогу моделей OpenAI. Если API вернёт ошибку о недоступной модели, замените только это значение на идентификатор из текущего каталога OpenAI.

Как работает первый запуск

Для учебного бота удобнее начать с long polling. Программа периодически вызывает Telegram Bot API методом getUpdates, получает новые сообщения и отправляет ответы методом sendMessage. Для sendMessage нужны chat_id и текст ответа. Webhook решает ту же задачу другим способом, но для первого запуска потребовал бы публичного HTTPS-адреса. Long polling и webhook одновременно для одного бота не используются — это два взаимоисключающих способа получать обновления, описанных в Telegram Bot API.

Сохраните следующий код в bot.py:

import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request

from openai import OpenAI


TELEGRAM_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
MODEL = os.environ.get("OPENAI_MODEL", "gpt-3.5-turbo-0125")
TELEGRAM_API = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}"

client = OpenAI()  # библиотека сама читает OPENAI_API_KEY


def telegram_call(method: str, data: dict) -> dict:
    """Вызвать метод Telegram Bot API и вернуть JSON-ответ."""
    body = urllib.parse.urlencode(
        {key: value for key, value in data.items() if value is not None}
    ).encode("utf-8")
    request = urllib.request.Request(
        f"{TELEGRAM_API}/{method}",
        data=body,
        method="POST",
    )
    with urllib.request.urlopen(request, timeout=40) as response:
        result = json.load(response)

    if not result.get("ok"):
        raise RuntimeError(f"Telegram API error: {result}")
    return result


def make_answer(user_text: str) -> str:
    completion = client.chat.completions.create(
        model=MODEL,
        messages=[
            {
                "role": "system",
                "content": "Отвечай по-русски, кратко и по существу.",
            },
            {"role": "user", "content": user_text},
        ],
    )

    answer = completion.choices[0].message.content
    return answer or "Модель не вернула текстовый ответ."


def main() -> None:
    offset = None
    print(f"Бот запущен, модель: {MODEL}")

    while True:
        try:
            updates = telegram_call(
                "getUpdates",
                {
                    "timeout": 30,
                    "offset": offset,
                    "allowed_updates": json.dumps(["message"]),
                },
            ).get("result", [])

            for update in updates:
                offset = update["update_id"] + 1
                message = update.get("message", {})
                text = message.get("text")
                chat = message.get("chat", {})

                if not text or "id" not in chat:
                    continue

                if text == "/start":
                    answer = "Напишите вопрос, и я передам его модели."
                else:
                    answer = make_answer(text)

                for start in range(0, len(answer), 2000):
                    telegram_call(
                        "sendMessage",
                        {"chat_id": chat["id"], "text": answer[start:start + 2000]},
                    )

        except (urllib.error.URLError, TimeoutError, OSError) as error:
            print(f"Сетевая ошибка: {type(error).__name__}. Повтор через 5 секунд.")
            time.sleep(5)
        except Exception as error:
            # Не выводим текст исключения: он может содержать секреты.
            print(f"Ошибка обработки: {type(error).__name__}. Повтор через 5 секунд.")
            time.sleep(5)


if __name__ == "__main__":
    main()

В коде offset не даёт обрабатывать один и тот же update снова после следующего запроса. Поле message.text отбрасывает сообщения без текста, а chat.id определяет диалог, куда нужно вернуть результат. Вызов client.chat.completions.create передаёт модели список сообщений с ролями system и user; такой формат и возвращаемое сообщение assistant описаны в справочнике Chat Completions OpenAI. Структура доступных параметров зависит от конкретной модели.

Как проверить бота по шагам

Сначала убедитесь, что Telegram-токен действителен. В отдельном окне терминала выполните запрос к getMe:

curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getMe"

В Windows PowerShell можно выполнить:

Invoke-RestMethod "https://api.telegram.org/bot$env:TELEGRAM_BOT_TOKEN/getMe"

В успешном ответе будет ok: true и данные бота. После этого запустите программу:

python bot.py

Откройте бота в Telegram, отправьте /start, затем обычную фразу. В первом случае сработает локальное приветствие, во втором появится запрос к OpenAI API и ответ модели.

Если Telegram отвечает, но модель недоступна, проверьте текст ошибки и значение OPENAI_MODEL. Для старого идентификатора GPT-3.5 доступ может отличаться от проекта к проекту; это не означает, что нужно менять весь код. Замените модель на доступный идентификатор и повторите запуск.

Проверьте также журналы терминала: в них не должны появиться значения TELEGRAM_BOT_TOKEN или OPENAI_API_KEY. Если ключ случайно попал в публичный репозиторий или лог, отзовите его и создайте новый.

Что добавить перед публикацией

Long polling подходит для небольшого собственного бота и проверки идеи. Для постоянной работы понадобятся отдельный процесс, ограничение частоты запросов, хранение истории диалога и более аккуратная обработка ошибок OpenAI. Когда появится публичный HTTPS-сервис, можно перейти на webhook. Масштабирование и очередь запросов лучше проектировать отдельно: учебный цикл выше намеренно оставляет один запрос к модели последовательным.

Если нужен именно готовый бесплатный бот в Telegram, а не разработка своей интеграции, это уже другая задача — для неё пригодится отдельная инструкция о бесплатном боте.

Источники

Есть следующая задача?Ещё по теме «Telegram-боты» →