Как сделать своего бота в телеграмме на python
Перейти к содержимому

Как сделать своего бота в телеграмме на python

  • автор:

Создайте собственного весёлого чат-бота в Telegram с помощью Python: Пошаговое руководство 2023

1. Для начала убедитесь, что на вашем компьютере установлен Python.

2. Затем получите учетные данные для аутентификации Telegram API.

В начале вам нужно будет создать нового бота и получить его shiny API token. Не волнуйтесь, это очень просто! Просто пообщайтесь с ботом BotFather в Telegram, и он поможет вам с реализацией этого процесса. Следуйте этим шагам:

Как написать Telegram-бота на Python: делаем ремайндер

Логотип компании Elbrus Bootcamp

Создаём простого бота-ремайндера в Telegram на языке Python, единственная задача которого — напоминать пользователю о важных делах.

В этой иструкции разберем процесс создания простого бота-ремайндера, единственная задача которого — напоминать пользователю о важных делах. Это базовая конструкция, которую можно усложнять и менять под свои потребности.

Инструкция подойдет для новичков, которые знают Python на базовом уровне, пробовали писать код и установили на компьютер редактор кода.

Первый этап: подготовка проекта и развертывание окружения

Найдем в поиске Telegram BotFather — официального бота мессенджера, который создает другие боты и управляет ими. В интерфейсе выбираем /start, затем — /newbot, и следом задаем имя и адрес. В этой иструкции это будут Elbrus Reminder и elbrus_reminder_bot соответственно.

После этого BotFather пришлет сообщение с токеном и ссылкой на бот:

Токен нужно хранить в безопасном месте — он дает контроль над ботом. и, как следствие, позволяет получить доступ к данным пользователей.

На время закроем Telegram и создадим на компьютере папку с именем проекта: например, reminder_bot. Откроем папку в среде разработки и создадим рабочий файл с понятным названием — bot.py.

Откроем терминал редактора кода и создадим для проекта новое окружение. В среде разработки с помощью команды python -m venv .venv создадим папку с окружением .venv .

Если окружение не активировалось автоматически, можно сделать это вручную, прописав путь к файлу активации в формате source .venv/bin/activate , где source — команда языка программирования Bash. Другой вариант — перезапустить среду разработки. Он работает для Visual Studio Code, но нужно предварительно принять предложение редактора привязать среду к папке проекта сразу после создания окружения.

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

Второй этап: подключаем библиотеки

Проект создан и окружение готово: пора переходить к написанию кода. По правилам хорошего тона в первую очередь через import добавляем несколько предустановленных библиотек Python. При создании бота нам пригодятся logging и time , которые отвечают за определение времени и логирование сообщений.

import time import logging 

Затем добавим асинхронную библиотеку aiogram, на основе которой будет работать бот. Она, например, определяет, какое сообщение пришло, как его нужно обработать и какие порты нужны. Сначала устанавливаем ее через терминал командой pip install aiogram , а в редакторе кода пишем следующее:

from aiogram import Bot, Dispatcher, executor, types 

Из этой библиотеки нам нужны только отдельные модули и классы — все ее возможности для создания базовой версии бота не пригодятся. Поэтому вместо одиночного import использована команда from <> import <> .

Когда библиотеки импортированы, создадим переменные с токеном бота и сообщением, которое он будет отправлять пользователю. Вы можете заменить это сообщение на любое другое, которое вам необходимо. Это статические переменные, поэтому их имена написаны капслоком:

TOKEN = "здесьбудетваштокенот от BotFather" MSG = "Программировал ли ты сегодня, <>?" 

Токены, ключи и прочие данные для настройки проекта лучше загружать более безопасным способом (например, создавать переменные окружения или файлы конфигурации). Но в данном случае сделаем все в одном файле для наглядности, а примеры более безопасной работы с такими переменными разберем в следующих постах.

Теперь создадим экземпляр класса Bot , передав ему в качестве аргумента наш токен, и экземпляр класса Dispatcher (dp), который в качестве аргумента получит bot . В результате получаем связку объекта класса bot с ключем, который привязан к боту, и диспетчера, который привязан к этому боту:

bot = Bot(token=TOKEN) dp = Dispatcher(bot=bot) 

Следующим шагом добавим конструкцию под названием декоратор ( massage_handler ) — она помогает получить из диспетчера нужный функционал. В качестве аргумента прописываем команды, которые обрабатывает декоратор — в данном случае это команда /start , которая запускает бот.

@dp.message_handler(commands=['start']) 

Под декоратором прописываем функцию, которая будет обрабатывать команду /start и определяет логику, в соответствии с которой будет работать бот. Поскольку мы работаем с асинхронной библиотекой, функция тоже должна быть асинхронной. Для этого перед указанием def добавим ключевое слово async :

async def start_handler(message: types.Message): 

Функция приветствует пользователя и обрабатывает сообщение, которое он отправляет в ответ. Из сообщения можно получить информацию о пользователе, который его прислал, время отправки и его ID.

Создаем переменную и сохраняем в ней user id :

user_id = message.from_user.id 

Затем получаем из сообщения короткое и полное имя пользователя:

user_name = message.from_user.first_name user_full_name = message.from_user.full_name 

Для того, чтобы в логах отображалась информация о пользователе, передаем в виде текста ID и полное имя, а также используем возможности библиотеки time , чтобы определить время, когда писал пользователь:

logging.info(f'  ') 

Здесь отойдем в сторону и проверим корректность работы модуля time . Сделать это можно в терминале: для этого напишем import time , а затем — time.asctime

Вернемся к коду. Поскольку функция, которую мы используем, асинхронна, вместо обычного для функций return используем await :

await message.reply(f"Привет, !") 

Ответить пользователю в боте можно несколькими способами — в данном случае используем reply. Выше в переменной MSG мы задали стандартное сообщение: «Программировал ли ты сегодня, <>?». Зададим частоту напоминаний: семь раз каждые семь суток (60х60х24 — количество секунд в одних сутках) с момента отправки команды /start боту от пользователя:

for i in range(7): await asyncio.sleep(60*60*24) 

Затем настроим отправку сообщения с указанием имени пользователя в этом же цикле:

await bot.send_message(user_id, MSG.format(user_name)) 

Третий этап: финал

Переходим к финальной части: в конце скрипта напишем несколько строк. Они могут показаться странными для новичка, но это общепринятая практика, к которой многие программисты прибегают при разработке. В этой строке мы проверяем, равна ли переменная __name__ строке «__main__» . Это условие всегда будет True, если мы запускаем этот файл как python-скрипт через терминал:

if __name__ == '__main__': 

Теперь делаем нашего бота доступным в сети:

executor.start_polling(dp) 

Сохраняем файл. Запускаем бота в терминале, открытом в папке проекта, с помощью команды python bot.py .

Вернемся в BotFather и перейдем по ссылке, которую получили вместе с токеном. Нажимаем «Начать» — готово, бот, написанный меньше, чем в 30 строк, работает.

Так выглядит его код целиком:

import time import logging import asyncio from aiogram import Bot, Dispatcher, executor, types TOKEN = "здесьбудетваштокенот@BotFather" MSG = "Программировал ли ты сегодня, <>?" logging.basicConfig(level=logging.INFO) bot = Bot(token=TOKEN) dp = Dispatcher(bot=bot) @dp.message_handler(commands=["start"]) async def start_handler(message: types.Message): user_id = message.from_user.id user_name = message.from_user.first_name user_full_name = message.from_user.full_name logging.info(f'  ') await message.reply(f"Привет, !") for i in range(7): await asyncio.sleep(60*60*24) await bot.send_message(user_id, MSG.format(user_name)) if __name__ == "__main__": executor.start_polling(dp) 

В следующий раз подробно расскажем, как написать подобный бот на языке программирования JavaScript. Подписывайтесь, чтобы не пропустить инструкцию.

Как разработать Telegram-бота на Python+C

Создаем Telegram-бот, работаем c I/O Bound нагрузкой, подключаем фичи Google Drive и пишем на C внутри Python.

Эта инструкция — часть курса «Как создавать Telegram-ботов».

Смотреть весь курс

Введение

В этой инструкции мы создадим приложение, которое интегрируется со сторонними API. Разберем I/O Bound нагрузку и поработаем с асинхронностью в Python.

Часть сервера Telegram-бота мы будем писать на С, так как он считается перформанс-ориентированным языком, поэтому посмотрим также модуль обработки СPU Bound нагрузки, использующийся для сложных вычислений процессора.

Мы постараемся объяснить все: от развертывания каждого модуля до настройки проекта на удаленном сервере.

Работать все это будет в вебе, поэтому для примера клиент-серверной архитектуры используется Telegram. У нас по дефолту будет мобильный+десктопный клиент, поэтому не придется писать фронтенд самому.

Теория

У многих приложений есть открытый интерфейс (API), к которому можно подключиться из своих программ. Например, так можно подключиться к YouTube и попросить сервис прислать описания всех роликов из конкретного плейлиста.

Что такое CPU Bound и I/O Bound нагрузка. Примеры

  1. Есть какая-то задача Х, которая сильно нагружает процессор. Например, одни из самых медленных операций, которые выполняет современный процессор — деление или тригонометрические функции типа sin или cos. Такие сложные для процессора операции называют CPU Bound нагрузкой.
  2. Если операции упираются в скорость сети, скорость записи или чтения какого-то файла с диска или ожидания запроса из базы данных — это I/O Bound нагрузка. Отправили запрос к серверу и ждете, пока он придет — процессор простаивает, как правило, 90% времени.

Как эти проблемы решаются в Python?

  1. Можно использовать модуль мультипроцессинга, который распараллелит вычисления на несколько процессов на нескольких реальных ядрах. Первый оверхед — создание новых процессов в операционной системе — достаточно дорогая операция. А их взаимодействие — второй оверхед, то есть обмен данными между ними — не самая простая вещь. Как минимум, чтобы передать объект между ними, объект должен быть сериализован и десериализован соответствующе. Если кратко: сериализация — это очень дорого.
  2. Мультитрединг — дешевле, особенно вскейле, то есть когда мы планируем масштабировать и создавать большие пулы потоков. Потому что поток — это более легковесная сущность с точки зрения операционной системы и в сравнении с процессом, как минимум.

Самое важное, что потоки расшаривают одну и ту же область памяти внутри одного процесса. То есть обмен данными между ними организован гораздо проще.

В Python есть GIL или блок интерпретатора, с которым могут быть проблемы. В каждый конкретный момент времени может работать только один, залоченный поток, а другим приходится ждать.

В случае решения второй проблемы и I/O Bound нагрузки, то в Python она решается асинхронным программированием.

Практика

О том, как создаются Telegram-боты, можно посмотреть здесь. Начнем с нашего интерфейса.

Возьмем первую библиотеку для Python: Python-telegram-bot.

Чтобы не захламлять компьютер чем-то лишним, будем использовать питоновскую виртуальную среду (venv). Это похоже на изолированный контейнер, в котором можно проводить любые опыты, и доступно это только внутри этой папки.

Сначала нам нужен токен от BotFather для аутентификации с API Telegram. Плюс здесь можно устанавливать ряд других настроек: изменить описание, поставить моды. BotFather по сути представляет собой админку.

Токен мы будем хранить в скрытых данных. За это в Python отвечает модуль dotenv, который также ставится внутри нашей виртуалки, и в сам файл уже переносим токен.

from bot.bot import start, list_files, upload_file, search_button, cython, pi_button, FIRST, SECOND from dotenv import load_dotenv from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, CallbackQueryHandler, ConversationHandler import os async def hello(update: Update, context: ContextTypes.DEFAULT_Type) ->Name: Name: await update.message.replay_text(f'Hello(update.effective_user.first_name)') load_dotenv() application = ApplicationBuilder().token(os.getenv('TOKEN')).build() app.add_handler(CommandHandler(“hello”, hello)) app.run.polling() 

Чтобы подружить Telegram с Google Drive воспользуемся API /quickstart/python. Здесь нам нужно получить что-то вроде токена, как и в случае аутентификации с API Telegram, только в Google API это называется(Credentials). В main.py мы должны добавить и токен, и обработчик функции start.

По этой логике нужно написать еще одну функцию и добавить ее в еще один handler уже для сэмпла от Google Drive.

Добавляем еще один CommandHandler (обработчик команд или все, что мы пишем через слэш).

В Telegram, проверить работу функции можно командой /files, которую мы можем легко добавить в админке BotFather

По коду Google: мы достаем секретки и вытаскиваем все файлы, дальше выводим их в консоль. Можно сделать, чтобы ответ приходил не в консоль сервера, а сразу в бот на клиент.

Загрузка файлов

Для демонстрации возможностей добавим еще и загрузку файлов. Google предлагает здесь обширный список того, что можно делать: Работа со всеми видами файлов, работа с UI Google Drive, и так далее.

Нам для интерактивности нужна динамика, поэтому мы будем грузить файл сначала на наш сервер через интерфейс Telegram, а уже оттуда его перенаправлять в Google Drive.

Чтобы загрузить файл сначала к нам на сервер, воспользуемся вот этим код-сниппетом из библиотеки telegram-bot-api а конкретно Download file.

Промежуточный итог

У нас есть сервер. Место, где мы разворачиваем наш код: удаленный или наш компьютер. Там мы запускаем наш Telegram-бот. Через интерфейс Telegram-бота, который нам дается по стандарту, мы можем загрузить файл, и он будет у нас лежать либо на компьютере, либо на удаленном сервере.

Этим сниппетом сначала загружаем файлы на компьютер. В отдельную функцию вынесем аутентификацию с Google Drive, чтобы было почище. Дальше во все захардкоденные сниппеты Google мы добавим наши динамические данные в виде имен файлов и путей. Все это достается из аргументов Update и Context.

Добавим также обработчик команд MessageHandler для медиасообщений.

Теперь, если вызвать эту функцию, то сначала файл появится в нашей папке Downloaded, а потом в Google Drive.

В конечном счете код в файле main.py у нас должен выглядеть так:

from bot.bot import start, list_files, upload_file, search_button, cython, pi_button, FIRST, SECOND from dotenv import load_dotenv from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, CallbackQueryHandler, ConversationHandler import os def main(): import cProfile import pstats load_dotenv() application = ApplicationBuilder().token(os.getenv('TOKEN')).build() application.add_handler(CommandHandler('start', start)) with cProfile.Profile() as pr: application.add_handler(CommandHandler('files', list_files)) stats = pstats.Stats(pr) stats.sort_stats(pstats.SortKey.TIME) stats.dump_stats(filename='profiling.prof') conv_handler = ConversationHandler( entry_points=[CommandHandler("cython", cython)], states=< FIRST: [CallbackQueryHandler(search_button)], SECOND: [CallbackQueryHandler(pi_button)], >, fallbacks=[CommandHandler("cython", cython)], ) application.add_handler(conv_handler) application.add_handler(MessageHandler(filters.Document.ALL, upload_file)) application.run_polling() if __name__ == '__main__': main() 

На этом мы закончили работу с I/O Bound нагрузкой и ее функциями. А что если мы хотим добавить функциональность по нагрузке процессора? Вариаций действительно много. Можно сделать промежуточный лэйер, который будет брать видео, которые мы грузим, монтажить их в клип, сжимать и выгружать на Google Drive уже готовые препродакшн видео.

Модуль для сложных вычислений на процессоре

Следующая фича на бэкенде. Мы сейчас добавим модуль для CPU Bound обработки именно на процессоре, и делать это будет Python в связке с языком С. Более того, делать мы это будем в нескольких потоках. То есть мы отключим питоновский GIL.

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

Первый модуль будет большой аллокацией + бинарный поиск по массиву. Заодно так проверим, как это работает. Папка lowlevel для C-файлов и сразу заведем модуль с парой простых функций.

int*allocate(int N) int* ptr = malloc(sizeof(int)* N); 

Выделяем память для массива данных. Внутри функции поиска элементов будем вызывать наши функции для аллокации и генерации данных. Так можно увидеть наглядно, как работает процессор линейно, в нескольких потоках и в нескольких процессах. Эта практика сейчас используется для наглядности, но в боевых проектах так делать не стоит.

Чтобы вызывать код на С из-под Python нам потребуется Cython. Это суперсет Python, который позволяет писать на C прямо внутри Python. Чтобы все это работало, нужно поставить это в виртуалку и добавить файл для интерфейса Python и C.

cdef extern from "lowlevel/module.c" nogil; int binary_search_imp(int index) cdef int b_search_(int index): 

Затем создаем промежуточную функцию, которая будет вызывать нашу экспортированную C функцию.

cdef int b_search(int index): return binary_search_impl(index); def binary_search(int index, results = None): cdef int result; with nogil: result = b_search(index); if results is not None: results.append(result) return result 

Самое важное — поставить аннотацию nogil, то есть здесь мы отключаем лок интерпретатора (GIL).

Итак, у нас есть С файл с core-функциями. У нас есть файл, чтобы этими функциями пользоваться в самом Python.

Теперь в новом файле опишем все Python-функции. Чтобы не путаться, назовем его, например, performance.py. В него сразу добавим функцию по вычислению дельты времени, от начала и конца исполнения. Она позволит наглядно увидеть скорость Python в линейном режиме, многопоточном и в многопроцессном.

Функция, внутри которой есть функция-обертчик, где есть вызов функции, которую мы декорируем. Чтобы протестить все, что мы написали, нам нужны три основных функции.

Линейная. Все вычисления будут происходить по очереди в одном потоке, как обычно работает Python. Асинхронность здесь не сработает, потому что все это нагружает именно процессор, а не любого рода ввод/вывод.

def linear(N) results = [] binary_search(N, results) return results 

Многопоточная. Создаем пять потоков по одному вызову в каждом.

def multithreaded(N): results = [] 

Мультипроцессинговая. Функция с изолированным друг от друга процессами: shared.list.

shared.list. def multithreaded(N): results = mp.Managed.lists() 

Теперь перейдем к следующему конфигу, по настройке Cython. Назовем его setup.py. Что здесь должно быть?

  • файл, который нужно скомпилировать,
  • модуль, который мы создаем и будем использовать,
  • функция setup, которая будет все это структурировать.

Код выглядит так:

from setuptools import setup from distutils.extension import Extension from Cython.Distutils import build_ext sourceFiles = ['bot/nogil.pyx'] ext_modules = [ Extension("nogil", sourceFiles ), ] setup( name='google drive app', cmdclass=, ext_modules=ext_modules ) 

Добавим простой bash-script, чтобы собирать все было удобнее.

# !/bin/bash [! -e "nogil.c" ] || rm "nogil.c" find . -name "*.so" -type f -delete python3 setup.py build_ext -b build mv bot/*.c bot/lowlevel/ 

Теперь все должно адекватно пересобираться и раскладываться по своим местам.

Чтобы в самом Telegram-боте это выглядело красиво, добавим пару кнопок. В Inline-боте нужны две функции: описание и кнопка + события, которые будут происходить, когда мы будем на нее нажимать.

Вызываем функцию бинарного поиска в наших 3-х тестах. Линейном, многопоточном и многопроцессном, дергаем наши три функции. Добавляем обработчики для кнопок и можно тестировать.

Почти секунда у нас уйдет, чтобы выделить необходимую память для каждого вызова функции. Многопоточность обгоняет линейную более чем на 50%. Многопроцессность также быстрее, но все же уступает многопоточности.

В конце у нас должен получиться примерно такой код в bot.py:

from __future__ import print_function import os import logging from telegram import Update from telegram.ext import ContextTypes import os.path from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError from googleapiclient.http import MediaFileUpload from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from bot.performance import linear, multithreaded, multiprocessed, linear_pi, mt_pi, mp_pi logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) def creds(): # If modifying these scopes, delete the file token.json. SCOPES = ['https://www.googleapis.com/auth/drive'] """Shows basic usage of the Drive v3 API. Prints the names and ids of the first 10 files the user has access to. """ creds = None # The file token.json stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if os.path.exists('token.json'): creds = Credentials.from_authorized_user_file('token.json', SCOPES) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open('token.json', 'w') as token: token.write(creds.to_json()) return creds async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!") async def list_files(update: Update, context: ContextTypes.DEFAULT_TYPE): try: service = build('drive', 'v3', credentials=creds()) # Call the Drive v3 API results = service.files().list( pageSize=10, fields="nextPageToken, files(id, name)").execute() items = results.get('files', []) if not items: print('No files found.') return #4.362e-06 # l = [] # for f in items: # l.append(f.get("name")) print('Files:') #4.103e-06 for item in items: await context.bot.send_message(chat_id=update.effective_chat.id, text=item.get("name")) print(u' ()'.format(item['name'], item['id'])) except HttpError as error: # TODO(developer) - Handle errors from drive API. print(f'An error occurred: ') async def upload_file(update: Update, context: ContextTypes.DEFAULT_TYPE): newFile = await update.message.effective_attachment.get_file() await newFile.download(custom_path="downloaded/" + update.message.effective_attachment.file_name) try: # create drive api client service = build('drive', 'v3', credentials=creds()) file_metadata = media = MediaFileUpload("downloaded/"+update.message.document.file_name, mimetype=update.message.document.mime_type, resumable=True) # pylint: disable=maybe-no-member file = service.files().create(body=file_metadata, media_body=media, fields='id').execute() print(F'File ID: ') except HttpError as error: await context.bot.send_message(chat_id=update.effective_chat.id, text="Failure") print(F'An error occurred: ') file = None await context.bot.send_message(chat_id=update.effective_chat.id, text="Success") return file.get('id') FIRST, SECOND = range(2) async def cython(update: Update, context: ContextTypes.DEFAULT_TYPE): keyboard = [ [InlineKeyboardButton(u"Search", callback_data=str(FIRST))] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text(u"First module", reply_markup=reply_markup) return FIRST async def search_button(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query keyboard = [ [InlineKeyboardButton(u"Pi", callback_data=(SECOND))] ] index = ((2**31-1) // 32 - 1) await context.bot.send_message(chat_id=update.effective_chat.id, text="lin: "+linear(index)) await context.bot.send_message(chat_id=update.effective_chat.id, text="mt: "+multithreaded(index)) await context.bot.send_message(chat_id=update.effective_chat.id, text="mp: "+multiprocessed(index)) #reply_markup = InlineKeyboardMarkup(keyboard) await context.bot.edit_message_text(chat_id=query.message.chat_id, message_id=query.message.message_id, text=u"Second module") reply_markup = InlineKeyboardMarkup(keyboard) await context.bot.edit_message_reply_markup(chat_id=query.message.chat_id, message_id=query.message.message_id, reply_markup=reply_markup) return SECOND async def pi_button(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await context.bot.send_message(chat_id=update.effective_chat.id, text="lin: "+linear_pi(100000000)) await context.bot.send_message(chat_id=update.effective_chat.id, text="mt: "+mt_pi(100000000)) await context.bot.send_message(chat_id=update.effective_chat.id, text="mp: "+mp_pi(100000000)) await context.bot.edit_message_text(chat_id=query.message.chat_id, message_id=query.message.message_id, text=u"You can add more CPU bound modules") 

Тестируем на Linux сервере

Мы будем использовать последнюю версию Ubuntu и выберем самое простое железо. Кроме этого, с помощью Shared Line можно использовать только 10% ресурсов, поэтому это будет еще дешевле, но под наш pet-проект хватит. В день такая конфигурация будет стоить не больше 15 рублей.

Поднимаем и обновляем там все, потому что это fresh Linux. Далее загружаем весь код бота на сервер, и можно приступать к тестированию: закинем пару файлов в наш Telegram-бот. Тестируем работу загрузки файлов. Вывода файлов, и сами функции по нагрузке CPU bound вычислений.

Заключение

Мы подключаемся к открытому API Google Drive, и вместо того, чтобы в самом диске нажимать разные кнопки, мы можем управлять им из-под Telegram-бота. Сделать это можно не только с диском, но и с любым сервисом, у которого есть открытый API.

Кроме этого, мы рассмотрели пару фич для работы с CPU Bound нагрузками, что может помочь разгрузить какие-то сложные для процессора вычисления.

Простой Telegram-бот на Python за 30 минут

На Хабре, да и не только, про ботов рассказано уже так много, что даже слишком. Но заинтересовавшись пару недель назад данной темой, найти нормальный материал у меня так и не вышло: все статьи были либо для совсем чайников и ограничивались отправкой сообщения в ответ на сообщение пользователя, либо были неактуальны. Это и подтолкнуло меня на написание статьи, которая бы объяснила такому же новичку, как я, как написать и запустить более-менее осмысленного бота (с возможностью расширения функциональности).

Часть 1: Регистрация бота

Самая простая и описанная часть. Очень коротко: нужно найти бота @BotFather, написать ему /start, или /newbot, заполнить поля, которые он спросит (название бота и его короткое имя), и получить сообщение с токеном бота и ссылкой на документацию. Токен нужно сохранить, желательно надёжно, так как это единственный ключ для авторизации бота и взаимодействия с ним.

Часть 2: Подготовка к написанию кода

Как уже было сказано в заголовке, писать бота мы будем на Python’е. В данной статье будет описана работа с библиотекой PyTelegramBotAPI (Telebot). Если у вас не установлен Python, то сперва нужно сделать это: в терминале Linux нужно ввести

sudo apt-get install python python-pip

Если же вы пользуетесь Windows, то нужно скачать Python с официального сайта .

После, в терминале Linux, или командной строке Windows вводим

pip install pytelegrambotapi

Теперь все готово для написания кода.

Часть 3: Получаем сообщения и говорим «Привет»

Небольшое отступление. Телеграмм умеет сообщать боту о действиях пользователя двумя способами: через ответ на запрос сервера (Long Poll), и через Webhook, когда сервер Телеграмма сам присылает сообщение о том, что кто-то написал боту. Второй способ явно выглядит лучше, но требует выделенного IP-адреса, и установленного SSL на сервере. В этой статье я хочу рассказать о написании бота, а не настройке сервера, поэтому пользоваться мы будем Long Poll’ом.

Открывайте ваш любимый текстовый редактор, и давайте писать код бота!

Первое, что нужно сделать это импортировать нашу библиотеку и подключить токен бота:

import telebot; bot = telebot.TeleBot('%ваш токен%');

Теперь объявим метод для получения текстовых сообщений:

@bot.message_handler(content_types=['text']) def get_text_messages(message):

В этом участке кода мы объявили слушателя для текстовых сообщений и метод их обработки. Поле content_types может принимать разные значения, и не только одно, например

@bot.message_handler(content_types=['text', 'document', 'audio'])

Будет реагировать на текстовые сообщения, документы и аудио. Более подробно можно почитать в официальной документации

Теперь добавим в наш метод немного функционала: если пользователь напишет нам «Привет», то скажем ему «Привет, чем я могу помочь?», а если нам напишут команду «/help», то скажем пользователю написать «Привет»:

if message.text == "Привет": bot.send_message(message.from_user.id, "Привет, чем я могу тебе помочь?") elif message.text == "/help": bot.send_message(message.from_user.id, "Напиши привет") else: bot.send_message(message.from_user.id, "Я тебя не понимаю. Напиши /help.")

Данный участок кода не требует комментариев, как мне кажется. Теперь нужно добавить в наш код только одну строчку (вне всех методов).

bot.polling(none_stop=True, interval=0)

Теперь наш бот будет постоянно спрашивать у сервера Телеграмма «Мне кто-нибудь написал?», и если мы напишем нашему боту, то Телеграмм передаст ему наше сообщение. Сохраняем весь файл, и пишем в консоли

python bot.py

Где bot.py – имя нашего файла.

Теперь можно написать боту и посмотреть на результат:

image

Часть 4: Кнопки и ветки сообщений

Отправлять сообщения это несомненно весело, но ещё веселее вести с пользователем диалог: задавать ему вопросы и получать на них ответы. Допустим, теперь наш бот будет спрашивать у пользователя по очереди его имя, фамилию и возраст. Для этого мы будем использовать метод register_next_step_handler бота:

name = ''; surname = ''; age = 0; @bot.message_handler(content_types=['text']) def start(message): if message.text == '/reg': bot.send_message(message.from_user.id, "Как тебя зовут?"); bot.register_next_step_handler(message, get_name); #следующий шаг – функция get_name else: bot.send_message(message.from_user.id, 'Напиши /reg'); def get_name(message): #получаем фамилию global name; name = message.text; bot.send_message(message.from_user.id, 'Какая у тебя фамилия?'); bot.register_next_step_handler(message, get_surnme); def get_surname(message): global surname; surname = message.text; bot.send_message('Сколько тебе лет?'); bot.register_next_step_handler(message, get_age); def get_age(message): global age; while age == 0: #проверяем что возраст изменился try: age = int(message.text) #проверяем, что возраст введен корректно except Exception: bot.send_message(message.from_user.id, 'Цифрами, пожалуйста'); bot.send_message(message.from_user.id, 'Тебе '+str(age)+' лет, тебя зовут '+name+' '+surname+'?') 

И так, данные пользователя мы записали. В этом примере показан очень упрощённый пример, по хорошему, хранить промежуточные данные и состояния пользователя нужно в БД, но мы сегодня работаем с ботом, а не с базами данных. Последний штрих – запросим у пользователей подтверждение того, что все введено верно, да не просто так, а с кнопками! Для этого немного отредактируем код метода get_age

def get_age(message): global age; while age == 0: #проверяем что возраст изменился try: age = int(message.text) #проверяем, что возраст введен корректно except Exception: bot.send_message(message.from_user.id, 'Цифрами, пожалуйста'); keyboard = types.InlineKeyboardMarkup(); #наша клавиатура key_yes = types.InlineKeyboardButton(text='Да', callback_data='yes'); #кнопка «Да» keyboard.add(key_yes); #добавляем кнопку в клавиатуру key_no= types.InlineKeyboardButton(text='Нет', callback_data='no'); keyboard.add(key_no); question = 'Тебе '+str(age)+' лет, тебя зовут '+name+' '+surname+'?'; bot.send_message(message.from_user.id, text=question, reply_markup=keyboard) 

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

@bot.callback_query_handler(func=lambda call: True) def callback_worker(call): if call.data == "yes": #call.data это callback_data, которую мы указали при объявлении кнопки . #код сохранения данных, или их обработки bot.send_message(call.message.chat.id, 'Запомню : )'); elif call.data == "no": . #переспрашиваем 

Остаётся только дописать в начало файла одну строку:

from telebot import types

Вот и всё, сохраняем и запускаем нашего бота:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *