Разбираемся с tg ботом вместе. python

Введение

Добро пожаловать в 2020! В последний раз мы рассматривали нововведения Bot API аж в далёком 2017 году, когда появилось удаление сообщений и ограничения в чатах. С тех пор вышло много чего интересного и, возможно, о чём-то стоит выпустить отдельные уроки.

А сегодня мы познакомимся с опросами 2.0, точнее, с новой сущностью: викторинами (quiz). Викторина – это именно то, что вы и предположили; тест с одним правильными вариантом ответа и ещё N неправильными.

Поставим себе задачу сделать бота, который умеет:

  1. принимать от пользователя только викторины;
  2. запоминать их содержимое и записывать к себе в память;
  3. предлагать викторины в инлайн-запросе и отправляет их в группу;
  4. получать новые ответы и сохранять ID правильно ответивших;
  5. останавливать викторину после двух правильных ответов и поздравлять победителей.

Задач много, придётся вспомнить, что такое колбэки, инлайн-режим и классы. Но и это не самое главное…

Обработка ошибок¶

При работе бота неизбежно возникновение различных ошибок, связанных не с кодом, а с внешними событиями. Простейший пример:
попытка отправить сообщение пользователю, заблокировавшему бота. Чтобы не оборачивать каждый вызов в ,
в aiogram существует специальный хэндлер для исключений, связанных с Bot API.
Рассмотрим следующий пример кода, имитирующий задержку перед ответом пользователю:

За эти 10 секунд пользователь может успеть заблокировать бота со своей стороны и попытка вызвать метод приведёт
к появлению исключения . Напишем специальный хэндлер для этого исключения:

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

Важно

У errors_handler есть одна особенность, из-за которой его использование может быть нежелательно. Дело в том, что
после срабатывания и завершения хэндлера, управление в исходную функцию не возвращается. Проще говоря, если,
например, 57-я итерация цикла из 100 привела к срабатыванию errors_handler, остальные итерации выполнены не будут,
как и весь остальной код исходной функции. В этом случае ничего не остаётся, кроме как использовать .

Пишем программу

Наш бот должен возвращать изображение с пёсиком, когда мы отправляем команду . Генерировать случайные изображения, нам поможет публичным API от RandomDog.

Рабочий процесс нашего бота очень прост:

1. Импорт библиотек

Для начала импортируем все нужные библиотеки.

from telegram.ext import Updater, CommandHandler
import requests
import re

2. Доступ к API и получение URL изображения

Давайте создадим функцию для получения URL. Используя библиотеку запросов, мы можем обратится к API и получить json данные.

contents = requests.get('https://random.dog/woof.json').json()

Получаем URL, чтобы иметь возможность отправить изображение:

image_url = contents

Оформим этот код в виде функции  .

def get_url():
    contents = requests.get('https://random.dog/woof.json').json()
    url = contents
    return url

3. Отправляем картинку

Чтобы отправить сообщение/изображение, нам понадобится два параметра: URL изображения и ID получателя — это может быть ID группы или ID пользователя.

Получить URL изображения можно вызвав функцию  .

url = get_url()

Чтобы получить ID получателя, используйте этот код:

chat_id = update.message.chat_id

После того как мы получили URL изображения и ID получателя, пришло время отправить сообщение, т.е. изображение.

bot.send_photo(chat_id=chat_id, photo=url)

Оберните код в функцию и убедитесь, что код выглядит следующим образом:

def bop(bot, update):
    url = get_url()
    chat_id = update.message.chat_id
    bot.send_photo(chat_id=chat_id, photo=url)

4. Основная программа

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

def main():
    updater = Updater('YOUR_TOKEN')
    dp = updater.dispatcher
    dp.add_handler(CommandHandler('bop',bop))
    updater.start_polling()
    updater.idle()

if name == '__main__':
    main()

В итоге, ваш код должен выглядеть вот так:

from telegram.ext import Updater, InlineQueryHandler, CommandHandler
import requests
import re

def get_url():
    contents = requests.get('https://random.dog/woof.json').json()
    url = contents
    return url

def bop(bot, update):
    url = get_url()
    chat_id = update.message.chat_id
    bot.send_photo(chat_id=chat_id, photo=url)

def main():
    updater = Updater('YOUR_TOKEN')
    dp = updater.dispatcher
    dp.add_handler(CommandHandler('bop',bop))
    updater.start_polling()
    updater.idle()

if __name__ == '__main__':
    main()

5. Запуск программы

Отлично! Мы почти закончили. Давайте проверим работу программы. Сохраните файл и назовите его  . Теперь её можно запускать, командой:

python3 main.py

Полный код бота

# mastrobot_example2.py
import datetime
import math
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters

STATE = None
BIRTH_YEAR = 1
BIRTH_MONTH = 2
BIRTH_DAY = 3

# function to handle the /start command
def start(update, context):
    first_name = update.message.chat.first_name
    update.message.reply_text(f"Hi {first_name}, nice to meet you!")
    start_getting_birthday_info(update, context)

def start_getting_birthday_info(update, context):
    global STATE
    STATE = BIRTH_YEAR
    update.message.reply_text(
        f"I would need to know your birthday, so tell me what year did you born in...")

def received_birth_year(update, context):
    global STATE

    try:
        today = datetime.date.today()
        year = int(update.message.text)

        if year > today.year:
            raise ValueError("invalid value")

        context.user_data = year
        update.message.reply_text(
            f"ok, now I need to know the month (in numerical form)...")
        STATE = BIRTH_MONTH
    except:
        update.message.reply_text(
            "it's funny but it doesn't seem to be correct...")

def received_birth_month(update, context):
    global STATE

    try:
        today = datetime.date.today()
        month = int(update.message.text)

        if month > 12 or month < 1:
            raise ValueError("invalid value")

        context.user_data = month
        update.message.reply_text(f"great! And now, the day...")
        STATE = BIRTH_DAY
    except:
        update.message.reply_text(
            "it's funny but it doesn't seem to be correct...")

def received_birth_day(update, context):
    global STATE

    try:
        today = datetime.date.today()
        dd = int(update.message.text)
        yyyy = context.user_data
        mm = context.user_data
        birthday = datetime.date(year=yyyy, month=mm, day=dd)

        if today - birthday < datetime.timedelta(days=0):
            raise ValueError("invalid value")

        context.user_data = birthday
        STATE = None
        update.message.reply_text(f'ok, you born on {birthday}')

    except:
        update.message.reply_text(
            "it's funny but it doesn't seem to be correct...")

# function to handle the /help command
def help(update, context):
    update.message.reply_text('help command received')

# function to handle errors occured in the dispatcher
def error(update, context):
    update.message.reply_text('an error occured')

# function to handle normal text
def text(update, context):
    global STATE

    if STATE == BIRTH_YEAR:
        return received_birth_year(update, context)

    if STATE == BIRTH_MONTH:
        return received_birth_month(update, context)

    if STATE == BIRTH_DAY:
        return received_birth_day(update, context)

# This function is called when the /biorhythm command is issued
def biorhythm(update, context):
    print("ok")
    user_biorhythm = calculate_biorhythm(
        context.user_data)

    update.message.reply_text(f"Phisical: {user_biorhythm}")
    update.message.reply_text(f"Emotional: {user_biorhythm}")
    update.message.reply_text(f"Intellectual: {user_biorhythm}")

def calculate_biorhythm(birthdate):
    today = datetime.date.today()
    delta = today - birthdate
    days = delta.days

    phisical = math.sin(2*math.pi*(days/23))
    emotional = math.sin(2*math.pi*(days/28))
    intellectual = math.sin(2*math.pi*(days/33))

    biorhythm = {}
    biorhythm = int(phisical * 10000)/100
    biorhythm = int(emotional * 10000)/100
    biorhythm = int(intellectual * 10000)/100

    biorhythm = (phisical == 0)
    biorhythm = (emotional == 0)
    biorhythm = (intellectual == 0)

    return biorhythm

def main():
    TOKEN = "insert here your token and don't share it with anyone!"

    # create the updater, that will automatically create also a dispatcher and a queue to
    # make them dialoge
    updater = Updater(TOKEN, use_context=True)
    dispatcher = updater.dispatcher

    # add handlers for start and help commands
    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CommandHandler("help", help))
    # add an handler for our biorhythm command
    dispatcher.add_handler(CommandHandler("biorhythm", biorhythm))

    # add an handler for normal text (not commands)
    dispatcher.add_handler(MessageHandler(Filters.text, text))

    # add an handler for errors
    dispatcher.add_error_handler(error)

    # start your shiny new bot
    updater.start_polling()

    # run the bot until Ctrl-C
    updater.idle()


if __name__ == '__main__':
    main()

Пришло время проверить его:

Telegram bot

Поздравляем! Telegram-бот на Python полностью готов. 

Как отправить и получить документ, аудио, видео, фото и.т.д

Продолжаем пилить telegram бот на python3 и на простом примере разберёмся, как отправлять файлы пользователям telegram бота.

Как отправить файл пользователю бота

Отправляем пользователю фотографию

@bot.message_handler(content_types=)
def text(message):
if message.text == ‘photo’:
file = open(‘photo.png’, ‘rb’)
bot.send_photo(message.chat.id, file)

1
2
3
4
5

@bot.message_handler(content_types=»text»)

def text(message)

ifmessage.text==’photo’

file=open(‘photo.png’,’rb’)

bot.send_photo(message.chat.id,file)

За это отвечает метод «send_photo» Аналогично поступим и с другими типами файлов. Например нам нужно отправить документ «file.txt»

Отправляем пользователю документ

@bot.message_handler(content_types=)
def text(message):
if message.text == ‘document’:
file = open(‘file.txt’, ‘rb’)
bot.send_document(message.chat.id, file)

1
2
3
4
5

@bot.message_handler(content_types=»text»)

def text(message)

ifmessage.text==’document’

file=open(‘file.txt’,’rb’)

bot.send_document(message.chat.id,file)

Тут мы уже используем метод «send_document» аналогично поступаем с другими типами файлов аудио, видео и прочие.

Как получить файл от пользователя

Все также как и со стикерами. В этом примере получим документ от пользователя и скачаем его себе на локалку.

@bot.message_handler(content_types=)
def handle_docs_audio(message):
document_id = message.document.file_id
file_info = bot.get_file(document_id)
urllib.request.urlretrieve(f’http://api.telegram.org/file/bot{config.token}/{file_info.file_path}’, file_info.file_path)

1
2
3
4
5

@bot.message_handler(content_types=»document»)

def handle_docs_audio(message)

document_id=message.document.file_id

file_info=bot.get_file(document_id)

urllib.request.urlretrieve(f’http://api.telegram.org/file/bot{config.token}/{file_info.file_path}’,file_info.file_path)

Получим от пользователя музыку

@bot.message_handler(content_types=)
def handle_docs_document(message):
audio_id = message.audio.file_id
file_info = bot.get_file(audio_id)
urllib.request.urlretrieve(f’http://api.telegram.org/file/bot{config.token}/{file_info.file_path}’, file_info.file_path)

1
2
3
4
5

@bot.message_handler(content_types=»audio»)

def handle_docs_document(message)

audio_id=message.audio.file_id

file_info=bot.get_file(audio_id)

urllib.request.urlretrieve(f’http://api.telegram.org/file/bot{config.token}/{file_info.file_path}’,file_info.file_path)

Точно таким-же способом можно получить любой тип файла от пользователей и создать Telegram бот на Python для конвертации файлов или у кого на что фантазии хватает))

Создаем первого бота на Selenium.

Selenium — это библиотека для автоматизации действий в браузере.

Данный способ подойдет для любого сайта, однако, за все нужно платить. Selenium запускает браузер, отъедая огромный запас оперативной памяти. Используйте его только тогда, когда нужно выполнить JS код на странице.

Первым делом нужно установить библиотеку, для этого введите в консоли:

Далее, установите веб-драйвер под браузер Firefox отсюда. Также, необходимо установить браузер Mozilla Firefox, если еще не установлен.

Теперь напишем простейшего бота. Для этого, напишите следующий python скрипт.

Далее, переместите файл скрипта, в одну папку с веб-драйвером geckodriver.exe

И запустите python скрипт. У вас должен открыться браузер.

В адресной строке видна иконка робота, это значит, что браузером управляет программа.

Хорошо, бот создан, но он бесполезен. Единственное на что он способен, это заходить на сайт. Давайте добавим ему новых функций. Например, сделаем так, чтобы бот лайкал посты на сайте.

Пишем код

Лучше записывать API-токены, номера кошелька и тому подобное в отдельный файл, поэтому создадим файл config.py:

Основной код находится в bot.py

Мы создали эхо-бота. Подумаем, что будет в нашем боте:

  • База данных. В ней храним информацию по пользователям, а также создадим таблицу для хранения информации о пицце. Для взаимодействия с БД лучше написать отдельные классы — для пользователя и пиццы.
  • Клавиатуры. Помогут сделать интерфейс удобным. 
  • Оплата. Для этого будем использовать сервис QIWI.

База данных

Я пользуюсь SQLiteStudio для создания баз данных.

Структура таблицы для пользователя

  • id — уникальный идентификатор пользователя. Таким выступает id пользователя в мессенджере, поэтому без зазрения совести будем использовать его.
  • stat — статус работы с ботом. Запоминаем на каком шаге остановился пользователь.
  • ord. Здесь мы в виде строки храним заказ пользователя. Пока я не представляю как сделать по-другому, поэтому предлагайте идеи в комментариях.
  • random_code. В будущем будем генерировать рандомный код для оплаты через QIWI.
  • time. Пользователь выбирает, когда хочет получить заказ, а мы сохраняем в это поле.

Структура таблицы для пиццы

Данные по пиццам:

По name получаем описание пиццы и её стоимость.Осталось добавить фотографию. Создадим директорию data, поместим в нее img, а уже там будут храниться изображения.

Классы для взаимодействия с базой данных

Так, базу данных создали, теперь приступим к классам. Создадим файл user.py, в нём класс User. Сначала напишем пару функций для подключения к базе данных и закрытию соединения.

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

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

Основные функции для пользователя написаны. Позже будем возвращаться к этому классу, а пока приступим к пицце. Создадим файл pizza.py, в нём класс Pizza. Начальные функции такие же, как у User.

Нужно получать данные из БД.

Для админ-панели потребуется функция set_field (подобная есть в классе User), но её я пока не предусмотрел.

В следующей статье разберём клавиатуры и начнём писать код в bot.py

Спасибо за внимание!

Advanced use of the API

Using local Bot API Sever

from telebot import apihelper

apihelper.API_URL = "http://localhost:4200/bot{0}/{1}"

Note: 4200 is an example port

Asynchronous delivery of messages

There exists an implementation of TeleBot which executes all and the functions asynchronously. This can speed up your bot significantly, but it has unwanted side effects if used without caution.
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.

tb = telebot.AsyncTeleBot("TOKEN")

Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:

import telebot

tb = telebot.AsyncTeleBot("TOKEN")
task = tb.get_me() # Execute an API call
# Do some other operations...
a = 
for a in range(100):
	a += 10

result = task.wait() # Get the result of the execution

Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.

Sending large text messages

Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:

from telebot import util
large_text = open("large_text.txt", "rb").read()

# Split the text each 3000 characters.
# split_string returns a list with the splitted text.
splitted_text = util.split_string(large_text, 3000)

for text in splitted_text:
	tb.send_message(chat_id, text)

Or you can use the new function to get more meaningful substrings:

from telebot import util
large_text = open("large_text.txt", "rb").read()
# Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096)
# Splits by last '\n', '. ' or ' ' in exactly this priority.
# smart_split returns a list with the splitted text.
splitted_text = util.smart_split(large_text, chars_per_string=3000)
for text in splitted_text:
	tb.send_message(chat_id, text)

Controlling the amount of Threads used by TeleBot

The TeleBot constructor takes the following optional arguments:

threaded: True/False (default True). A flag to indicate whether
TeleBot should execute message handlers on it’s polling Thread.

The listener mechanism

As an alternative to the message handlers, one can also register a function as a listener to TeleBot.

NOTICE: handlers won’t disappear! Your message will be processed both by handlers and listeners. Also, it’s impossible to predict which will work at first because of threading. If you use threaded=False, custom listeners will work earlier, after them handlers will be called.
Example:

def handle_messages(messages):
	for message in messages:
		# Do something with the message
		bot.reply_to(message, 'Hi')

bot.set_update_listener(handle_messages)
bot.infinity_polling()

Using web hooks

When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages() when you recieve it.

There are some examples using webhooks in the examples/webhook_examples directory.

Logging

import logging

logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.

Proxy

You can use proxy for request. object will use by call proxies argument.

from telebot import apihelper

apihelper.proxy = {'http':'http://127.0.0.1:3128'}

If you want to use socket5 proxy you need install dependency and make sure, that you have the latest version of , , , and .

apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}

Testing

You can disable or change the interaction with real Telegram server by using

apihelper.CUSTOM_REQUEST_SENDER = your_handler

parameter. You can pass there your own function that will be called instead of requests.request.

For example:

def custom_sender(method, url, **kwargs):
    print("custom_sender. method: {}, url: {}, params: {}".format(method, url, kwargs.get("params")))
    result = util.CustomRequestResponse('{"ok":true,"result":{"message_id": 1, "date": 1, "chat": {"id": 1, "type": "private"}}}')
    return result

Then you can use API and proceed requests in your handler code.

apihelper.CUSTOM_REQUEST_SENDER = custom_sender
tb = TeleBot("test")
res = tb.send_message(123, "Test")

Result will be:

Создание команды

Теперь нужно обработать команду /biorhythm.

Добавьте новый обработчик команд в функцию main().

Напишите функцию расчета биоритма:

# This function is called when the /biorhythm command is issued

def biorhythm(update, context):

    user_biorhythm = calculate_biorhythm(

        context.user_data)

    update.message.reply_text(f"Phisical: {user_biorhythm}")

    update.message.reply_text(f"Emotional: {user_biorhythm}")

    update.message.reply_text(f"Intellectual: {user_biorhythm}")

def calculate_biorhythm(birthdate):

    today = datetime.date.today()

    delta = today - birthdate

    days = delta.days

    phisical = math.sin(2*math.pi*(days/23))

    emotional = math.sin(2*math.pi*(days/28))

    intellectual = math.sin(2*math.pi*(days/33))

    biorhythm = {}

    biorhythm = int(phisical * 10000)/100

    biorhythm = int(emotional * 10000)/100

    biorhythm = int(intellectual * 10000)/100

    biorhythm = (phisical == 0)

    biorhythm = (emotional == 0)

    biorhythm = (intellectual == 0)

    return biorhythm

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

Функционал для логирования

Согласно тексту справки, бот должен уметь делать две вещи:

  1. Если вы отправляете сообщение боту, он должен где-то его сохранить.
  2. При отправке боту команды он должен отправить вам последнее сообщение.

Для этого мы будем использовать встроенную в Replit базу данных ключ-значение. Начнем с импорта API:

from replit import db

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

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

def latest_key():
    ks = db.keys()
    if len(ks):
        return max(map(int, ks))
    else:
        return -1 

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

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

def log(update: Update, context: CallbackContext) -> None:
    db = update.message.text 

Этот обработчик получает последний ключ из базы данных, увеличивает его на единицу и создает новую пару ключ — сообщение.

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

dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, log))

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

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

def fetch(update: Update, context: CallbackContext) -> None:
    update.message.reply_text(db.get(str(latest_key()), 'No Messages yet.'))

Мы можем зарегистрировать его вместе с обработчиками остальных команд. Добавьте данную строку после уже существующих строк :

dispatcher.add_handler(CommandHandler("fetch", fetch))

Первый бот¶

Давайте создадим файл с базовым шаблоном бота на aiogram:

Первое, на что нужно обратить внимание: aiogram — асинхронная библиотека, поэтому ваши функции тоже должны быть асинхронными,
а перед вызовами методов API нужно ставить ключевое слово await, т.к. эти вызовы возвращают

Асинхронное программирование в Python

Не стоит пренебрегать официальной документацией!
Прекрасный туториал по asyncio доступен на сайте Python.

Если вы в прошлом работали с какой-то другой библиотекой для Telegram, например, pyTelegramBotAPI, то концепция
хэндлеров (обработчиков событий) вам сразу станет понятна, разница лишь в том, что в aiogram хэндлерами управляет диспетчер.
Диспетчер регистрирует функции-обработчики, дополнительно ограничивая перечень вызывающих их событий через фильтры.
После получения очередного апдейта (события от Telegram), диспетчер выберет нужную функцию обработки, подходящую по всем
фильтрам, например, «обработка сообщений, являющихся изображениями, в чате с ID икс и с длиной подписи игрек». Если две
функции имеют одинаковые по логике фильтры, то будет вызвана та, что зарегистрирована раньше.

Чтобы зарегистрировать функцию как обработчик сообщений, нужно сделать одно из двух действий:
1. Навесить на неё декоратор, как в примере выше.
С различными типами декораторов мы познакомимся позднее.
2. Напрямую вызвать метод регистрации у диспетчера.

Рассмотрим следующий код:

Давайте запустим с ним бота:

Функция не работает, т.к. диспетчер о ней не знает. Исправим эту ошибку
и отдельно зарегистрируем функцию:

Снова запустим бота:

Признавая сообщения, которые мы уже видели

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

Измените наш бот следующим образом:

Добавьте дополнительное Смещение Параметр на нашу функцию getupdate. Если это указано, мы передадим его на API Telegram, чтобы указать, что мы не хотим получать сообщения с меньшими идентификаторами, чем это. Модифицированная функция должна выглядеть так:

def get_updates(offset=None):
    url = URL + "getUpdates"
    if offset:
        url += "?offset={}".format(offset)
    js = get_json_from_url(url)
    return js

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

def get_last_update_id(updates):
    update_ids = []
    for update in updates:
        update_ids.append(int(update))
    return max(update_ids)

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

Добавьте функцию, чтобы отправить ответ Echo для каждого сообщения, которое мы получаем. Это должно выглядеть следующим образом:

def echo_all(updates):
    for update in updates:
        try:
            text = update
            chat = update
            send_message(text, chat)
        except Exception as e:
            print(e)

Обновите код в Главная () Так что это выглядит так:

def main():
    last_update_id = None
    while True:
        updates = get_updates(last_update_id)
        if len(updates) > 0:
            last_update_id = get_last_update_id(updates) + 1
            echo_all(updates)
        time.sleep(0.5)

Наш основной код больше не нужно беспокоиться о дублированных сообщениях, так как каждый раз, когда мы получаем новые сообщения, мы отправляем самое большое обновление_Ид вместе со следующим запросом, гарантируя, что мы когда-либо получаем сообщения, которые мы не видели раньше.

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

Попробуйте изменения, перезапустив скрипт Python и отправку некоторых сообщений на свой бот – вы должны увидеть, что он работает как раньше, но теперь это не имеет значения, если вы отправляете дубликаты сообщений или отправляете сообщения слишком быстро, оба из которых являются большие улучшения Отказ

F.A.Q.

How can I distinguish a User and a GroupChat in message.chat?

Telegram Bot API support new type Chat for message.chat.

  • Check the attribute in object:
if message.chat.type == "private":
	# private chat message

if message.chat.type == "group":
	# group chat message

if message.chat.type == "supergroup":
	# supergroup chat message

if message.chat.type == "channel":
	# channel message

How can I handle reocurring ConnectionResetErrors?

Bot instances that were idle for a long time might be rejected by the server when sending a message due to a timeout of the last used session. Add to your initialisation to force recreation after 5 minutes without any activity.

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

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

Adblock
detector