Как написать телеграм-бот на python с использованием облачных сервисов яндекс?
Я не раньше не занимался программированием. От слова совсем. Да и сейчас не особо занимаюсь - всё-таки я учусь на гуманитарном факультете. Но я всегда мечтал сделать какое-нибудь приложение, которое облегчало бы жизнь, и желательно не только мне. И когда в этом семестре появился такой предмет, как кафедральный практикум, я почему-то первым делом подумал о телеграм-боте для студентов. Конечно, я подумал о нём по большей части потому, что я могу использовать gpt для перевода моих идей на язык кода. В общем, от слов - к делу.
Функционал моего бота пока что ограничивается выводом расписания по запросу <номер группы ><день недели>. Я смог написать (вернее сказать смог заставить gpt написать мне) достаотчно простой код, который бы осуществлял это, присылая запрашиваемые ячейки из таблице на яндекс диске. Но этот код, очевидно, работал только локально с запущенным пайчармом. Но мысль о том, чтобы развернуть его на сервере, упиралась в моё нежелание работать с чем-то чересчур для меня незнакомым, тем более ещё потенциально платным, и меня занесло на яндекс.функции (по совету gpt, насколько я помню, хотя может и сам где-то нашёл). Я адаптировал свой код, настроил API-шлюз (тоже от яндекса), и вроде всё должно работать, но бот не реагирует на команды, хотя ошибки не выдаёт.
(код не знаю как прикрепить чтобы уложиться в символы, если разберусь, как это сделать, отредактирую, если нет, то попробую в разместить комментариях )
gpt-4 не больше не находит ошибок и не даёт никаких полезных советов. В связи с чем, хотелось бы обратиться к тем, кто разбирается лучше меня-гуманитария, где искать ошибки и/или что мне ещё нужно сделать/задействовать, чтобы реализовать этот небольшой проект? Если всё заработает, я буду добавлять функционал, но это по мере поступления задач. Буду рад отклику.
Это так не работает. Данный ресурс предназначен для решения конкретных проблем, возникших в процессе ваших попыток решить задачу. Выглядеть это должно примерно так:
Мне надо: [описание задачи].
Я делал вот-так: [код или описание ваших действий].
(код от нейронки не в счёт.)
Появилась вот такая ошибка:
[текст ошибки или описание того, как итоговый результат отличается от ожидаемого]
В таком случае будет что обсуждать.
Хотите получить готовый код - заказывайте на фрилансе.
Хотите получить примеры/гайды - ищите в интернете. Есть документации к библиотекам, есть сторонние гайды.
Значит забываете о боте и читаете учебник по python (какой выбрать - здесь обсуждается регулярно).
Без знаний программирования сами его вы не напишите, а отвечающие не будут писать его за вас.
import telebot
import yadisk
import requests
import pandas as pd
#from datetime import datetime
# создаем объект бота и авторизуемся в Яндекс.Диске
bot = telebot.TeleBot('токен')
y = yadisk.YaDisk(token='токен')
# задаем путь к файлу на Яндекс Диске
path = '/Бот/Расписание для бота.xlsx'
# получаем ссылку на скачивание файла
url = y.get_download_link(path)
# скачиваем файл и читаем его в датафрейм
response = requests.get(url)
with open('file.xlsx', 'wb') as f:
f.write(response.content)
df = pd.read_excel('file.xlsx', engine='openpyxl')
# Обработчик команды /start, отправляет приветственное сообщение и кнопку с вызовом /menu
@bot.message_handler(commands=['start'])
def send_welcome(message):
markup = telebot.types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
itembtn1 = telebot.types.KeyboardButton('/menu')
markup.add(itembtn1)
bot.reply_to(message, "Привет, я бот для студентов.", reply_markup=markup)
# Обработчик команды /menu, отправляет кнопки с выбором действия
@bot.message_handler(commands=['menu'])
def send_menu(message):
chat_id = message.chat.id
markup = telebot.types.InlineKeyboardMarkup()
itembtn1 = telebot.types.InlineKeyboardButton('Контакты преподавателей', callback_data='contacts')
itembtn2 = telebot.types.InlineKeyboardButton('Расписание занятий', callback_data='schedule_button')
markup.add(itembtn1, itembtn2)
bot.send_message(chat_id, 'Выберите команду:', reply_markup=markup)
# Обработчик нажатия на кнопку в Inline-режиме
@bot.callback_query_handler(func=lambda call: True)
def callback_query(call):
if call.data == 'contacts':
bot.send_message(call.message.chat.id, 'Вот контакты преподавателей...')
elif call.data == 'schedule_button':
# Запоминаем chat_id пользователя, который нажал на кнопку
chat_id = call.message.chat.id
# Отправляем сообщение с просьбой ввести номер группы и день недели
bot.send_message(chat_id, 'Введите номер группы и день недели (например, 301 понедельник)')
# Запоминаем chat_id пользователя для последующей обработки ввода
bot.register_next_step_handler(call.message, process_schedule_input)
# Обработчик ввода данных для команды /schedule
def process_schedule_input(message):
try:
# Загружаем данные из файла в объект Pandas DataFrame
direct_link = y.get_download_link('/Бот/Расписание для бота.xlsx')
response = requests.get(direct_link)
response.raise_for_status() # проверяем успешность загрузки файла
df = pd.read_excel(response.content, engine='openpyxl', dtype=str, header=0)
# Выводим все данные таблицы для отладки
print(df)
print(df.columns) # выводим на экран названия столбцов
print(df.index) # выводим на экран названия строк
# Получаем от пользователя номер группы и день недели
group, weekday = message.text.split()
group = int(group) # Преобразуем значение group в строковое значение
# Выводим номер группы и день недели для отладки
print(f"Group: {group}, Weekday: {weekday}")
# Проверяем, есть ли строка с названием дня недели
weekday_row_index = df.index[df['День недели'] == weekday].tolist()
if not weekday_row_index:
raise ValueError(f"Название дня недели {weekday} не найдено в таблице")
# Получаем данные для номера группы и дня недели
group_col_name = int(group)
filtered = df.loc[weekday_row_index, group_col_name]
pd.set_option('display.max_colwidth', None)
# Если данные найдены, отправляем их пользователю
if not filtered.empty:
# Формируем строку вывода
output = "\n\n".join(["\n".join(cell.split("\n")) for cell in filtered])
bot.send_message(message.chat.id, output)
else:
bot.send_message(message.chat.id, "Данные для этой группы и дня недели не найдены.")
except requests.exceptions.HTTPError as e:
bot.send_message(message.chat.id, f"Ошибка загрузки файла: {e}")
except ValueError as e:
bot.send_message(message.chat.id, f"Ошибка ввода: {e}")
except Exception as e:
bot.send_message(message.chat.id, f"Неизвестная ошибка: {e}")
# Запускаем бота
bot.polling()
import base64
import json
import logging
import aiohttp
import pandas as pd
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiohttp import web
import yadisk
API_TOKEN = 'токен'
YANDEX_DISK_TOKEN = 'токен'
logging.basicConfig(level=logging.INFO)
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
dp.middleware.setup(LoggingMiddleware())
y = yadisk.YaDisk(token=YANDEX_DISK_TOKEN)
path = '/Бот/Расписание для бота.xlsx'
async def on_startup(dp):
webhook_url = "https://d5d803gi8o8tuc18idq7.apigw.yandexcloud.net/webhook"
await bot.set_webhook(url=webhook_url)
async def on_shutdown(dp):
await bot.close()
@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
markup.add(types.KeyboardButton('/menu'))
await bot.send_message(message.chat.id, "Привет, я бот для студентов.", reply_markup=markup)
@dp.message_handler(commands=['menu'])
async def send_menu(message: types.Message):
chat_id = message.chat.id
markup = types.InlineKeyboardMarkup()
markup.add(
types.InlineKeyboardButton('Контакты преподавателей', callback_data='contacts'),
types.InlineKeyboardButton('Расписание занятий', callback_data='schedule_button')
)
await bot.send_message(chat_id, 'Выберите команду:', reply_markup=markup)
@dp.callback_query_handler(lambda c: True)
async def callback_query(call: types.CallbackQuery):
if call.data == 'contacts':
await bot.send_message(call.message.chat.id, 'Вот контакты преподавателей...')
elif call.data == 'schedule_button':
chat_id = call.message.chat.id
await bot.send_message(chat_id, 'Введите номер группы и день недели (например, 301 понедельник)')
await dp.register_next_step_handler(call.message, process_schedule_input)
async def process_schedule_input(message: types.Message):
pd.set_option('display.max_colwidth', None)
try:
direct_link = y.get_download_link(path)
async with aiohttp.ClientSession() as session:
async with session.get(direct_link) as response:
df = pd.read_excel(await response.read(), engine='openpyxl', dtype=str, header=0)
group, weekday = message.text.split()
group = int(group)
weekday_row_index = df.index[df['День недели'] == weekday].tolist()
if not weekday_row_index:
raise ValueError(f"Название дня недели {weekday} не найдено в таблице")
group_col_name = int(group)
filtered = df.loc[weekday_row_index, group_col_name]
except ValueError as e:
await bot.send_message(message.chat.id, f"Ошибка ввода: {e}")
except Exception as e:
await bot.send_message(message.chat.id, f"Неизвестная ошибка: {e}")
else:
if not filtered.empty:
output = "\n\n".join(["\n".join(cell.split("\n")) for cell in filtered])
await bot.send_message(message.chat.id, output)
else:
await bot.send_message(message.chat.id, "Данные для этой группы и дня недели не найдены.")
async def handle_webhook(request):
logging.info(f"Received data: {request.content_type}")
if request.content_type == 'application/json':
data = await request.json()
else:
logging.warning(f"Unexpected content type: {request.content_type}")
return web.Response(text=json.dumps({"status": "error"}))
logging.info(f"Received data: {data}")
if data is None:
logging.warning("Received empty data")
return web.Response(text=json.dumps({"status": "ok"}))
try:
update = types.Update(**data)
await dp.process_updates([update])
except Exception as e:
logging.error(f"Error processing update: {e}")
return web.Response(text=json.dumps({"status": "ok"}))
async def handler(event, context):
if event is not None and 'headers' in event and 'content-type' in event['headers']:
content_type = event['headers']['content-type']
else:
content_type = 'application/json'
if content_type == 'application/json':
if 'body' in event and event['body'] is not None:
data = json.loads(event['body'])
else:
logging.warning("Received empty body")
return {"statusCode": 200, "body": json.dumps({"status": "error"})}
elif content_type == 'application/x-www-form-urlencoded':
raw_data = event['body']
decoded_data = base64.b64decode(raw_data).decode('utf-8')
data = json.loads(decoded_data)
else:
logging.warning(f"Unexpected content type: {content_type}")
return {"statusCode": 200, "body": json.dumps({"status": "error"})}
request = web.Request(web.RequestHandler(web.Application(), {}), {}, method='POST', path='/webhook', headers=event['headers'], content_type=content_type)
request._read_bytes = data
response = await handle_webhook(request)
return {"statusCode": response.status, "body": await response.text()}
Константин Нагибович, сейчас работаю с яндекс.функциями и API-шлюзами. Возможно, требуется что-то ещё, но я пока копаюсь с тем, что есть. (Кстати, пошаманив, заставил бота говорить (приветствовать без остановки - что-то вызывает функцию постоянно, без команд, и вроде бы не в ответ на сообщения; в прошлых версиях кода этого не было, разбираюсь что сделал не так), но на команды всё же не реагирует)
Чтобы реагировал на команды, нужно домен в телеге прописать для твоего бота. Сделайте это через BotFather.
Нужно еще зарегистрировать вебхук. Гугли setWebhook.
Домен нужно использовать с сертификатом SSL.
__
Найди себе vps рублей за 200, поставь себе mysql, nginx, php, прикрепи домен и сертификат и разрабатывай бота.
Но, если с Питоном дружишь, то делай на нем.
mysql нужен чтобы хранить истории, ответы, расписание и прочее.
Антон, Константин Нагибович, Спасибо большое! Всё получилось и заработало. Правда пришлось асинхронность кода убрать, но первый шаг сделан, и уже на рабочем варианте несомненно проще будет тестить и применять новый функционал и оптимизацию. Ещё раз благодарю!