TypeError: get_message() missing 1 required positional argument: 'message'

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

import telebot


class TelegramBot:

    __token = 'my token'
    __bot = telebot.TeleBot(token=__token)
    __chat_id = 0

    def __init__(self):
        self.__bot.polling(none_stop=True)

    @__bot.message_handler(commands=['start'])
    def send_greeting(self, message):
        self.__chat_id = message.chat.id
        greeting = f"Hello, {message.from_user.full_name}!"
        self.__bot.send_message(chat_id=__chat_id, text=greeting)

    def send_info(self, some_info):
        self.__bot.send_message(chat_id=self.__chat_id, text=some_info)

Первый метод предназначен для того чтобы поприветствовать пользователя и получить id чата, для дальнейших отправок сообщений этому пользователю. Второй метод не зависим от того, пишет пользователь боту или нет, и будет запускаться из другого класса. В первом методе при использовании аргумента self, получаю ошибку:

TypeError: get_message() missing 1 required positional argument: 'message'

Если же не буду его использовать, то не смогу отправить приветствие и не смогу записать id чата в переменную класса, и в моем втором методе бот не будет знать нужный id. Можно ли отправить приветствие без аргумента self, и как мне во втором методе узнать id чата с пользователем? Если не трудно, помогите пожалуйста. Спасибо заранее!


Ответы (1 шт):

Автор решения: Roman-Stop RU aggression in UA

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

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

message_handler сам по себе не предназначен для вызова на несвязанных методах объекта. Несвязанный метод, это метод, который просто так вызвать нельзя, чтоб его вызвать нужен экземпляр класса, который нужно передать первым параметром. TeleBot при вызове метода с декоратором @message_handler передает только message, о вашем объекте TelegramBot он ничего не знает (а тем более объектов может быть много).

Как решить?

Есть два способа. Первый это использовать только статические данные в классе:

class TelegramBot:

    @__bot.message_handler(commands=['start'])
    @staticmethod
    def send_greeting(message):
        TelegramBot.__chat_id = message.chat.id
        greeting = f"Hello, {message.from_user.full_name}!"
        TelegramBot.__bot.send_message(chat_id=TelegramBot.__chat_id, text=greeting)

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

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

class TelegramBot:
    def __init__(self):
        self.send_greeting = self.__bot.message_handler(commands=['start'])(self.send_greeting)
        self.__bot.polling(none_stop=True)
        
    def send_greeting(self, message):
        self.__chat_id = message.chat.id
        greeting = f"Hello, {message.from_user.full_name}!"
        self.__bot.send_message(chat_id=__chat_id, text=greeting)

Еще несколько замечаний общего плана:

  1. у вас __bot - статический, т.е. один на все экземпляры класса. Не уверен, что вы это специально сделали.
  2. то же самое касается и __chat_id

Я понимаю, что скорее всего вы не будете создавать больше одного экземпляра в программе. Но все же у этого решения есть минусы:

  1. Если вдруг решите создать два бота, то статические __bot и __chat_id будут проблемой
  2. Использование статических полей и полей экземпляра одновременно без четкого разделения обязанностей приводит к путанице.

Одним словом, я бы сделал оба поля полями экземпляра, т.е.:

class TelegramBot:

    __token = 'my token'

    def __init__(self):
        self.__bot = telebot.TeleBot(token=__token)
        self.__chat_id = 0
        self.__bot.polling(none_stop=True)
→ Ссылка