В этой статье
Если нужен небольшой Telegram-бот с ответами OpenAI, отдельный фреймворк для Telegram не обязателен. Достаточно Node.js, двух секретов и нескольких HTTPS-запросов: бот получает обновление через getUpdates, передаёт текст в Responses API, а затем отправляет результат обратно методом sendMessage.
Ниже — локальный стартовый вариант на JavaScript. Он не хранит долговременную историю диалога и использует long polling. Это удобно, когда бот запускается на компьютере или на простом сервере без публичного HTTPS-адреса.
Что понадобится
Установите Node.js 20 или новее. В терминале создайте каталог проекта и установите официальный пакет OpenAI:
mkdir telegram-openai-bot
cd telegram-openai-bot
npm init -y
npm install openai
Понадобятся два секрета:
- токен Telegram-бота, который выдаёт
@BotFather; - ключ
OPENAI_API_KEYиз платформы OpenAI.
Telegram передаёт токен в запросах к адресу вида https://api.telegram.org/bot<token>/METHOD_NAME, а OpenAI SDK читает ключ из переменной окружения. Не вставляйте реальные значения в bot.mjs, коммиты и публичные примеры. Описание формата запросов есть в Telegram Bot API, а способ настройки SDK — в официальном quickstart OpenAI.
В Linux или macOS переменные можно задать так:
export TELEGRAM_BOT_TOKEN='123456789:замените-на-токен'
export OPENAI_API_KEY='sk-замените-на-ключ'
export OPENAI_MODEL='gpt-4.1-mini'
В PowerShell:
$env:TELEGRAM_BOT_TOKEN = "123456789:замените-на-токен"
$env:OPENAI_API_KEY = "sk-замените-на-ключ"
$env:OPENAI_MODEL = "gpt-4.1-mini"
В примере ниже модель берётся из OPENAI_MODEL, а если переменная не задана, используется значение по умолчанию. Если в вашем проекте доступна другая модель, укажите её явно.
Как устроен обмен сообщениями
Цикл выглядит так:
getUpdatesждёт новые сообщения Telegram.- Бот находит в обновлении
message.textиchat.id. - Текст передаётся в
client.responses.create. - Свойство
response.output_textстановится ответом бота. sendMessageотправляет этот текст в тот же чат.
После обработки обновления программа увеличивает offset. Благодаря этому Telegram не отдаёт одно и то же сообщение при следующем запросе — это правило также описано в FAQ Telegram о ботах. Вызовы getUpdates и webhook нельзя использовать одновременно; если у бота уже установлен webhook, перед запуском polling его нужно удалить через deleteWebhook.
Рабочий файл bot.mjs
Создайте файл bot.mjs и вставьте код:
import OpenAI from "openai";
const telegramToken = process.env.TELEGRAM_BOT_TOKEN;
const openaiApiKey = process.env.OPENAI_API_KEY;
const model = process.env.OPENAI_MODEL || "gpt-4.1-mini";
if (!telegramToken) {
throw new Error("Не задана переменная TELEGRAM_BOT_TOKEN");
}
if (!openaiApiKey) {
throw new Error("Не задана переменная OPENAI_API_KEY");
}
const openai = new OpenAI({ apiKey: openaiApiKey });
const telegramApi = `https://api.telegram.org/bot${telegramToken}`;
async function telegram(method, body) {
const response = await fetch(`${telegramApi}/${method}`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
});
const data = await response.json();
if (!response.ok || !data.ok) {
throw new Error(`Telegram ${method}: ${data.description || response.statusText}`);
}
return data.result;
}
function splitMessage(text, maxLength = 4096) {
const chunks = [];
let rest = text.trim();
while (rest.length > maxLength) {
let cut = rest.lastIndexOf("\n", maxLength);
if (cut < Math.floor(maxLength * 0.6)) {
cut = rest.lastIndexOf(" ", maxLength);
}
if (cut < 1) {
cut = maxLength;
}
chunks.push(rest.slice(0, cut).trim());
rest = rest.slice(cut).trim();
}
if (rest) {
chunks.push(rest);
}
return chunks;
}
async function answer(text) {
const response = await openai.responses.create({
model,
input: text,
});
return response.output_text?.trim() || "OpenAI не вернул текстовый ответ.";
}
async function run() {
let offset = 0;
console.log("Бот запущен. Остановить: Ctrl+C");
while (true) {
try {
const updates = await telegram("getUpdates", {
offset,
timeout: 30,
allowed_updates: ["message"],
});
for (const update of updates) {
offset = update.update_id + 1;
const message = update.message;
const text = message?.text;
const chatId = message?.chat?.id;
if (!text || chatId === undefined) {
continue;
}
try {
await telegram("sendChatAction", {
chat_id: chatId,
action: "typing",
});
const reply = await answer(text);
for (const chunk of splitMessage(reply)) {
await telegram("sendMessage", {
chat_id: chatId,
text: chunk,
});
}
} catch (error) {
console.error("Ошибка обработки сообщения:", error);
await telegram("sendMessage", {
chat_id: chatId,
text: "Не удалось получить ответ. Проверьте ключ OpenAI и логи бота.",
}).catch((sendError) => console.error("Ошибка отправки уведомления:", sendError));
}
}
} catch (error) {
console.error("Ошибка long polling:", error);
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}
}
run().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Вызов responses.create соответствует серверному JavaScript-примеру OpenAI, а текст берётся через response.output_text. Для Telegram важна другая деталь: sendMessage принимает не более 4096 символов после разбора сущностей. Поэтому функция splitMessage режет длинный ответ по переносу строки или пробелу до отправки.
Запуск и первая проверка
Запустите программу из того же терминала, где заданы переменные окружения:
node bot.mjs
Откройте чат с ботом в Telegram и отправьте, например:
Объясни в двух предложениях, что такое long polling
В логе появится сообщение о запуске, а в чате — ответ модели. Если реакции нет, сначала проверьте три вещи: бот действительно получил сообщение, переменная TELEGRAM_BOT_TOKEN не содержит лишних кавычек или пробелов, а OPENAI_API_KEY действителен и для проекта настроен биллинг. Telegram-бот и OpenAI API — разные учётные данные и разные сервисы.
Для отдельной проверки Telegram можно запросить информацию о боте:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getMe"
Если раньше у бота был webhook, удалите его один раз перед polling:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/deleteWebhook" \
-H "content-type: application/json" \
-d '{}'
После этого перезапустите node bot.mjs. Два экземпляра программы одновременно запускать не стоит: они будут конкурировать за одни и те же обновления.
Что учесть перед публикацией
Long polling подходит для локального старта и простого развёртывания. Для production-сценария с публичным HTTPS обычно переходят на webhook: Telegram сам отправляет обновления на ваш URL, а сервер отвечает быстро и контролирует повторную обработку. Это отдельная схема — не добавляйте webhook поверх работающего getUpdates.
Пример намеренно не добавляет долговременную историю. Каждый запрос отправляется в OpenAI отдельно, поэтому бот не помнит предыдущие реплики. Если позже вы решите сохранять историю, заранее определите срок хранения, доступ к базе и состав передаваемых данных. В документации OpenAI о настройках хранения указано стандартное хранение состояния приложения в течение 30 дней; параметр store и собственное хранение следует выбирать осознанно, а сообщения пользователей не стоит передавать без необходимости.
Наконец, не путайте API-ключ с подпиской ChatGPT. Для бота нужен доступ к платформе OpenAI API и отдельная настройка биллинга. Если после технической части вам нужен именно доступ к ChatGPT для аккаунта, а не пополнение OpenAI API, варианты можно посмотреть в каталоге Amber Market; условия и наличие перед оформлением нужно перепроверить на странице.
Источники
- Developer quickstart: Make your first API requestOpenAI Platform
- Telegram Bot APITelegram
- Bots FAQTelegram
- Data controls in the OpenAI platformOpenAI Platform