I am creating a class inheriting from a parent class. The parent class is Telebot and the child class is my own Telegram BOT class. I am doing this to create a standard BOT that multiple scripts will call and implement with default actions etc.
Then I am trying to implement a decorator to treat default incoming messages on this child class. Here is how I am doing it (file is telegram.py
):
# Importo a biblioteca que vou usar pra criar meu BOT
import telebot
class TelegramBot(telebot.TeleBot):
"""
Essa classe é chamada para criar uma instância de um BOT do Telegram.
Ela herda a classe Telebot do módulo telebot.
Doc: https://pytba.readthedocs.io/en/latest/sync_version
"""
def __init__(self, token):
"""Esta função é chamada no momento que a classe é instanciada.
Herda os atributos do telebot"""
super().__init__(token)
def polling(self):
"""Este método está sendo incrementado com algumas funcionalidades
próprias do bot, ao invés de ser feito o override/polimorfismo."""
super().polling()
@telebot.TeleBot.message_handler(commands=["testar_bot"])
def resposta_teste(self, mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
self.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")
When doing it like this, I receive the error:
TypeError: TeleBot.message_handler() missing 1 required positional argument: 'self'
The weirdest thing is that if I remove the decorator alongside the method and implement in my bot object on my main.py
file, it works like a charm.
Here is the code that works - I remove the decorator from the class and put it on main.py
:
import telegram
bot = telegram.TelegramBot("blablabla_mytokenhidden")
@bot.message_handler(commands=["testar_bot"])
def resposta_teste(mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
bot.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")
bot.polling()
This does not gives me any error, and the decorator works like a charm. I really don't understand the logic behind this.
Why the decorator I used on my object works but on my child class (telegram.py with TelegramBot class) doesn't?
Edit #1
Following the object logic, I tried replacing telebot.TeleBot
with self
, also tested with object
and it still did not work...
So I am clearly NOT getting the logic here.
A method defined in a class requires an instance of the class to be passed as the first argument (self
) when it is called. (This is done for you when you invoke it using dot notation, i.e. instance.method()
.) The decorator @telebot.TeleBot.message_handler()
is called during class definition, before there are any instances. Therefore the handler doesn't know what instance it is attached to, which is why it wants you to pass self
in yourself. Which of course the bot framework is not set up to do.
Naturally, this isn't a problem when the handler is defined outside the class because it doesn't require an instance of the class it's attached to, not being attached to a class and all.
One solution is to instead apply the decorator when an instance is created:
def __init__(self, token):
"""Esta função é chamada no momento que a classe é instanciada.
Herda os atributos do telebot"""
super().__init__(token)
self.resposta_teste = telebot.TeleBot.message_handler(
commands=["testar_bot"])(self. resposta_teste)
This makes the handler a bound method of its instance, with self
already baked in. Note that this might cause problems if you create multiple instances of the class, because you'll have multiple handlers attached to the same event. The framework you're using might be OK with that, or it might not.
Another thing you can do, if you won't have multiple instances of the bot (or if the handler doesn't need to know what instance it's attached to) is to make the method a static method. Then it behaves as a regular function (albeit in the class namespace) and doesn't need to be passed an instance.
class TelegramBot(telebot.TeleBot):
# ...
@telebot.TeleBot.message_handler(commands=["testar_bot"])
@staticmethod
def resposta_teste(mensagem):
"""Esse método utiliza do Decorator para ter uma nova caracteristica e funcionalidade,
que define o padrão para cada mensagem recebida.
O método testa a resposta padrão para fazer o teste no BOT.
"""
self.reply_to(mensagem, "Olá! Teste passou. Tudo funcionando :)")