
Не так давно мне пришла мысль попробовать создать собственного Телеграм-бота (просто из любопытства). Никаких знаний в программировании у меня нет, поэтому первого бота я создал с помощью специального конструктора для Телеграм-ботов. Довольно удобная штука, но зачастую обладает ограниченным функционалом или требуют оформления подписки. И тут мне в голову пришла идея воспользоваться популярным ChatGPT и попробовать создать бота с нуля, во всём следуя инструкциям нейронки. Устанавливать дополнительный софт на своём основном ПК мне не очень хотелось, поэтому разместить бота я решил на VDS-сервере.
▍ Подготовка
Нет принципиальной разницы, какую операционную систему выбирать для размещения бота на сервере, но из-за личных предпочтений и удобства настройки я остановился на Windows Server. Благодаря тому, что в этой ОС есть графическая оболочка, а расположение сервера я выбрал за рубежом (в моём случае в Казахстане), я смог прямо с сервера, без какого-либо дополнительного софта пользоваться ChatGPT и напрямую копировать полученный код в файл бота. Конфигурации VDS сервера для небольшого бота хватило бы самой минимальной, но для возможности использовать версию Windows Server поновее, я взял конфиг: CPU 2, RAM 2, SSD 20.

В первую очередь необходимо было подготовить сервер к работе Телеграм-бота. Как изначально и планировалось, в этом вопросе я полностью положился на нейронку и отправил запрос:
Хочу написать Телеграм бота и разместить его на своем VDS сервере (ОС Windows). Я полный 0 в этом и не знаю, с чего начать. Опиши пошагово.
В ответ получил довольно подробную инструкцию, включающую себя следующие шаги:
- Регистрация бота в Telegram и получение токена с помощью BotFather.
- Настройка VDS сервера (установка Python и нужных библиотек).
- Создание папки и файла bot.py, в котором будет храниться код бота.

▍ Написание кода
Теперь начинается самое интересное — написание кода. Недолго думая, начать решил с кода для показа курса популярных валют (доллар, евро и юань). Отправил запрос:
Напиши мне код бота для Телеграм, который будет по запросу присылать курс валют: доллара, евро и юаня
Скопировал полученный код, вставил в файл bot.py и при запуске бота командой python3 bot.py ожидаемо получил ошибку:
«SyntaxError: unterminated f-string literal (detected at line 26) (, line 26)»
Так как написанием кода занимается ChatGPT, то и исправлением ошибок пусть занимается он. Копируем текст ошибки, отправляем его нейронке и получаем описание ошибки и исправление в коде.

Вновь пробуем запустить код и снова получаю ошибку. На этот раз текст ошибки следующий:
«Traceback (most recent call last): File „C:\telegram_bot\bot.py“, line 5, in from aiogram.utils import executor ImportError: cannot import name 'executor' from 'aiogram.utils' (C:\telegram_bot\venv\Lib\site-packages\aiogram\utils\__init__.py)»
Снова получаем описание ошибки и исправленный код. Сделав ещё несколько попыток (во время которых GPT указал на необходимость добавления токена из Telegram в код бота) наконец получилось запустить бота без ошибок. Теперь отправляемся в Телеграм, находим бота, запускаем его командой /start и, следуя инструкциям, пробуем получить курс валют командой /rate:

Отлично, всё работает. Однако постоянно пользоваться командой /rate не очень удобно, а в случае когда команда не одна, можно просто в них запутаться. Намного удобнее, если бы вместо ввода команд можно было бы просто кликнуть на кнопку. Отправляем новый запрос:
Бот работает, но постоянно отправлять команды не очень удобно. Хочу, чтобы у бота была кнопка, при нажатии на которую он присылал курс валют.
Как и в прошлый раз, полученный код не удалось запустить с первого раза и возникли новые ошибки, но после нескольких исправлений бот корректно запустился. Теперь при его запуске в Telegram появилось дополнительное меню, в котором отображается кнопка, при нажатии на которую получается тот же курс валют, что выводился при вводе команды /rate.

Другой разговор, с кнопкой намного удобнее. В целом, не без ошибок, но вполне ожидаемо, что ChatGPT справился с такой простой задачей. Но мне стало интересно, как он себя покажет при добавлении нового функционала (как схожего, так и отличающегося) и не сломает ли уже работающий код. Начать решил аккуратно и отправил следующий запрос:
Хочу, чтобы была ещё одна кнопка для вывода последних новостей. Пусть новости будут браться с ria.ru
Бот успешно запустился с обновлённым кодом, и в Telegram появилась новая кнопка «Последние новости», вот только при нажатии по ней бот выдаёт ошибку: «Ошибка при получении новостей. Попробуйте позже».

В консоли на сервере также появилась ошибка:
«ERROR:root: Ошибка получения новостей: No module named 'bs4' INFO:aiogram.event:Update id=644057290 is handled. Duration 316 ms by bot id=7290802386»
Как в случае с курсом валют, потребовалось несколько раз вносить изменения в код бота, а также установить дополнительные библиотеки на сервер, но в итоге удалось получить рабочий результат.

По этому же примеру, без особого труда удалось добавить ещё одну кнопку с выводом прогноза погоды в нужном городе. В отличии от новостей и курса валют, для вывода погоды боту потребовался API-ключ сервиса OpenWeatherMap, и GPT любезно предоставил инструкцию, как этот ключ получить:
- Перейди на сайт: home.openweathermap.org/users/sign_up
- Зарегистрируйся или войди в аккаунт.
- Перейди в раздел API keys (или API ключи).
- Там будет твой стандартный API-ключ (или создай новый, если нужно).
- Скопируй этот ключ и замени your_openweathermap_api_key в коде на свой API-ключ.
При нажатии на кнопку «Погода» бот запрашивает город. При вводе города выводится погода

Весь вышеописанный функционал бота работает по схожему принципу и при запросе без проблем выводит нужную информацию из определённых источников. Проблемы начались при добавлении нового функционала. В голову мне пришло добавить возможность создавать напоминания. Идея заключалась в том, чтобы добавить в уже существующее меню новую кнопку, при нажатии на которую будет открываться новое меню с новыми кнопками, отвечающими за управление напоминаниями. Для начала я отправил следующий запрос:
Хочу добавить ещё одну кнопку, при нажатии на которую будет открываться новое меню. В этом меню нужно сделать 2 кнопки. Одна — для создания напоминания: Сначала отправляем текст, а затем дату. Вторая — для показа всех напоминаний.
К моему удивлению, бот запустился с первого раза. Как я и хотел, в меню появилась новая кнопка «Напоминания».

При нажатии на кнопку успешно открылось новое меню с двумя новыми кнопками. При нажатии на кнопку «добавить напоминание» бот сначала запрашивает текст для напоминания, затем дату. И если текст напоминания бот принял без проблем, то как бы я ни пытался указать дату, он постоянно запрашивал её повторно:

Эту ошибку нейронке удалось исправить с первого раза, слегка изменив логику обработки сообщения с датой, и при повторном запуске напоминание успешно сохранилось.

При нажатии на кнопку «Показать напоминания» также успешно выводятся сохранённые напоминания.

В таком виде напоминания хранятся в памяти бота и автоматически удаляются при его выключении / перезагрузке. Чтобы этого не происходило, я попросил переписать код, чтобы все созданные напоминания хранились в отдельном файле.
Помимо создания напоминаний, необходима была возможность и удалять их. Также не очень удобно постоянно указывать дату напоминания вручную (особенно когда необходимо создать напоминание на сегодня / завтра). Поэтому я отправил следующий запрос:
Нужно добавить возможность удалять напоминания. Также нужно добавить возможность быстро оставить напоминание на сегодня / завтра с помощью специальных кнопок, появляющихся после ввода текста напоминания.
Потребовалось несколько раз исправлять код, но в итоге удалось получить тот результат, на который я рассчитывал. При вводе текста напоминания теперь появляется 4 кнопки, для добавления напоминания на сегодня / завтра, напоминания «без даты» и напоминания на любую другую дату.

▍ Возникшие проблемы
Казалось бы, всё, бот работает, напоминания создаются, что может пойти не так? А то, что пока я увлечённо занимался напоминаниями, исправляя возникающие с ними ошибки, я совсем забыл про тестирование уже ранее добавленного функционала (Курс валют, новости и прогноз погоды). Вернувшись к главному меню бота, я выяснил, что кроме кнопки «Напоминания», остальные кнопки просто не работают. Кроме того, в консоли на сервере также не было никаких ошибок. Я написал про это нейронке, и она начала вносить изменения в код, не сильно помогающие в исправлении проблем с нерабочими кнопками, но при этом ломающие оставшийся рабочий функционал. Сначала бот перестал показывать созданные напоминания, затем перестал создавать новые. В итоге перестали работать все кнопки, и бот превратился в тыкву. Устав биться с нейронкой, я решил вернуться к последней версии кода, где работали напоминания, и посмотреть, в чём же дело. Как выяснилось, ChatGPT, пока писал код для напоминаний, беспощадно удалил весь код с остальных кнопок, оставив только сами кнопки.
В итоге я решил немного изменить подход. Я запустил новый чат, чтобы сломанный код не влиял на ответ нейронки, взял две рабочие версии кода (версию до создания кнопки “напоминания” и версию с “напоминаниями”) и отправил следующий запрос:
У меня есть два кода. Первый код с рабочими кнопками «Курс валют», «Последние новости» и «Погода» (сам код целиком) и код с рабочими «Напоминаниями» (сам код целиком). Нужно объединить их в один рабочий код.
К моему приятному удивлению, после объединения бот запустился с первого раза и без ошибок, а кнопки работали как и планировалось. В итоге я получил следующий код:
import asyncio import requests import logging import json from aiogram import Bot, Dispatcher, types from aiogram.types import Message, ReplyKeyboardMarkup, KeyboardButton from bs4 import BeautifulSoup from datetime import datetime, timedelta # Токен бота TOKEN = "Ваш Telegram токен" WEATHER_API_KEY = "Ваш API ключ" REMINDERS_FILE = "reminders.json" logging.basicConfig(level=logging.INFO) bot = Bot(token=TOKEN) dp = Dispatcher() reminders = {} pending_reminders = {} waiting_for_city = {} def load_reminders(): global reminders try: with open(REMINDERS_FILE, "r", encoding="utf-8") as file: reminders = json.load(file) except (FileNotFoundError, json.JSONDecodeError): reminders = {} def save_reminders(): with open(REMINDERS_FILE, "w", encoding="utf-8") as file: json.dump(reminders, file, ensure_ascii=False, indent=4) async def set_reminder(user_id, text, date=None): if user_id not in reminders: reminders[user_id] = [] reminders[user_id].append((text, date)) save_reminders() return "Напоминание сохранено!" async def get_reminders(user_id): if user_id in reminders and reminders[user_id]: return "\n".join([f"{idx + 1}. 📅 {r[1] if r[1] else 'Без даты'} - {r[0]}" for idx, r in enumerate(reminders[user_id])]).encode("utf-8", "ignore").decode("utf-8") return "У вас нет напоминаний." async def delete_reminder(user_id, index): if user_id in reminders and 0 <= index < len(reminders[user_id]): del reminders[user_id][index] save_reminders() return "✅ Напоминание удалено!" return "❌ Неверный номер напоминания." def get_currency_rates(): url = "https://www.cbr-xml-daily.ru/daily_json.js" try: response = requests.get(url) data = response.json() usd, eur, cny = data["Valute"]["USD"]["Value"], data["Valute"]["EUR"]["Value"], data["Valute"]["CNY"]["Value"] return f"Курс валют:\n💵 Доллар: {usd:.2f} ₽\n💶 Евро: {eur:.2f} ₽\n🇨🇳 Юань: {cny:.2f} ₽" except Exception as e: logging.error(f"Ошибка получения курса валют: {e}") return "Ошибка при получении курса валют. Попробуйте позже." def get_latest_news(): url = "https://ria.ru/" try: response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}) soup = BeautifulSoup(response.text, "html.parser") headlines = soup.find_all("a", class_="list-item__title") or soup.find_all("a", class_="cell-list__item-link") news = "\n".join([f"🔹 {h.text.strip()}" for h in headlines[:5]]) return f"Последние новости:\n{news}" if news else "Не удалось получить новости." except Exception as e: logging.error(f"Ошибка получения новостей: {e}") return "Ошибка при получении новостей. Попробуйте позже." def get_weather(city): url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric&lang=ru" try: response = requests.get(url) data = response.json() if "main" in data: temp, description = data["main"]["temp"], data["weather"][0]["description"].capitalize() return f"🌤 Погода в {city}: {temp}°C, {description}" return "Не удалось получить погоду. Проверьте название города." except Exception as e: logging.error(f"Ошибка получения погоды: {e}") return "Ошибка при получении погоды. Попробуйте позже." keyboard = ReplyKeyboardMarkup( keyboard=[ [KeyboardButton(text="💰 Курс валют"), KeyboardButton(text="📰 Последние новости")], [KeyboardButton(text="🌤 Погода"), KeyboardButton(text="⏰ Напоминания")] ], resize_keyboard=True ) reminder_keyboard = ReplyKeyboardMarkup( keyboard=[ [KeyboardButton(text="➕ Добавить напоминание"), KeyboardButton(text="📋 Показать напоминания")], [KeyboardButton(text="🗑 Удалить напоминание"), KeyboardButton(text="🔙 Назад")] ], resize_keyboard=True ) quick_date_keyboard = ReplyKeyboardMarkup( keyboard=[ [KeyboardButton(text="📅 Сегодня"), KeyboardButton(text="📅 Завтра")], [KeyboardButton(text="📌 Без даты"), KeyboardButton(text="✏ Ввести дату вручную")] ], resize_keyboard=True ) @dp.message() async def handle_message(message: Message): user_id = str(message.from_user.id) text = message.text if user_id in waiting_for_city: weather_info = get_weather(text) del waiting_for_city[user_id] await message.answer(weather_info) return if text == "/start": await message.answer("Привет! Выбери действие ниже:", reply_markup=keyboard) elif text == "💰 Курс валют": await message.answer(get_currency_rates()) elif text == "📰 Последние новости": await message.answer(get_latest_news()) elif text == "🌤 Погода": waiting_for_city[user_id] = True await message.answer("Введите название города:") elif text == "⏰ Напоминания": await message.answer("Выберите действие:", reply_markup=reminder_keyboard) elif text == "➕ Добавить напоминание": pending_reminders[user_id] = {"stage": "text"} await message.answer("Введите текст напоминания:") elif text == "📋 Показать напоминания": reminders_text = await get_reminders(user_id) await message.answer(reminders_text) elif text == "🗑 Удалить напоминание": reminders_text = await get_reminders(user_id) if "нет напоминаний" in reminders_text.lower(): await message.answer(reminders_text) else: await message.answer(f"Ваши напоминания:\n{reminders_text}\n\nВведите номер напоминания для удаления:") pending_reminders[user_id] = {"stage": "delete"} elif text == "🔙 Назад": await message.answer("Главное меню:", reply_markup=keyboard) elif user_id in pending_reminders: stage = pending_reminders[user_id].get("stage") if stage == "text": pending_reminders[user_id]["text"] = text pending_reminders[user_id]["stage"] = "date_choice" await message.answer("Выберите дату:", reply_markup=quick_date_keyboard) elif stage == "date_choice": date = None if text == "📅 Сегодня": date = datetime.now().strftime("%d.%m.%Y") elif text == "📅 Завтра": date = (datetime.now() + timedelta(days=1)).strftime("%d.%m.%Y") elif text == "✏ Ввести дату вручную": pending_reminders[user_id]["stage"] = "date" await message.answer("Введите дату в формате ДД.ММ.ГГГГ ЧЧ:ММ:") return await set_reminder(user_id, pending_reminders[user_id]["text"], date) del pending_reminders[user_id] await message.answer("✅ Напоминание сохранено!", reply_markup=reminder_keyboard) async def main(): load_reminders() await dp.start_polling(bot) if __name__ == "__main__": asyncio.run(main())
▍ Вывод
В целом, я остался доволен получившимся результатом. Да, при написании кода не один раз возникали ошибки, но ChatGPT по большей части с ними справлялся и давал коррек��ный код. Увы, он не осилил добавление функционала напоминаний к уже имеющемуся коду и заставил изрядно попотеть, чтобы разобраться в причине, но при написании кода с использованием нескольких чатов (один чат для одного функционала, второй чат для другого) подобных проблем возникать не должно.
© 2025 ООО «МТ ФИНАНС»
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻

