Even Your Bot Deserves Love — Build One That Asks for It

Who needs your love? No one? — Well, let’s build one that does — in just a few lines of Python.

In this article, we're going to build a bot that asks a question with two inline buttons on a command and edit the message depending on the button you press.

Overview

Here's how our bot is going to work when it's done.

  1. It replies with a question "Do you like me?" and two buttons (Yes😍 | No😏) on /start command.

  2. You can click either Yes or No.

  3. If you click Yes, it edits the same message to say “I knew it😘”.

  4. If you click No, it edits the message to “Neither do I😏”.

Concepts You Will Learn

Before learning new concepts, you need to understand the previous ones. So I highly recommend checking out these previous articles:

In this article you will learn to,

  • Filter command messages using pyrogram.filters

  • Create Inline Buttons

  • React to callback queries

The Love Function

Using Multiple Filters

In the previous article, we learned to use pyrogram.filters. But didn’t you want to use more than one filter at the same time? — like filtering command messages sent in private? It's totally possible to do. You can create more advanced filters by combining multiple filters using bitwise operators.

  • | - or

  • & - and

  • ~ - not

In this article, we need to filter /start commands sent in private. Guess filters we're going to use — Yep, filters.command(...) and filters.private . But we need the message to satisfy both of these filters at the same time. Therefore, we have to use &.

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):

If you followed previous articles closely, you already know what above code does. The love function will be called when someone send a /start command to the bot in a private chat.

Sending Messages Using client Parameter

In both previous articles, we didn't use this parameter. We used a bound method to reply. But to unlock the full power of Pyrogram, we should also learn to use the standard (non-bound) methods.

Methods

Bound Methods

You cannot call these methods directly. You need to have a update like message, callback query to call them. For example, you can't reply to a message without receiving one. The reply() method, we used, is a highly use bound method.

Normal Methods

Unlike bound methods you can call these methods at your will. For example you don't need a message update to send a message. If you have the chat id and accessible for your bot, you can send a message. Not only sending a message but you can also do a bunch of cool stuff Here is a few of them. ( Full List )

MethodDescription
runRun the client
send_messageSend text messages
send_photosend photos
send_audioSend audio files
ban_chat_memberBan a user from a group, a supergroup or a channel

Requirements

Alright, we need to send a message on /start command to the same chat. I mentioned that we need to check two boxes to send a message.

  1. The chat id - You know the way we're going to get this value

  2. Have to have access to the chat - Telegram doesn’t allow your bot to message random users. Your bot will only be able to send messages to users who only have started your bot and haven't blocked. It's pretty good.

In our case, we get a /start message which means we have access to the chat and we can use message object to get chat id. Try to get the chat id.

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id

client.send_message()

This methods requires values for at least chat_id and text parameters.

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id
    client.send_message(
        chat_id=message.chat.id,
        text='Do you like me?'
    )

Your full code so far should look like this:

from pyrogram import Client, filters

bot = Client(
    name='examplebot',           
    api_id=12345,                
    api_hash='your api hash',    
    bot_token='your bot token'   
)

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id
    client.send_message(
        chat_id=message.chat.id,
        text='Do you like me?'
    )


bot.run()

We aren't done yet. But run this and send a /start to the bot. It will send back "Do you like me?" message.

Creating Inline Buttons

After adding buttons this is how your message looks like.

To create buttons we have to import InlineKeyboardMarkup and InlineKeyboardButton.

from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton

InlineKeyboardButton - Creates a inline button InlineKeyboardMarkup - Arranges the buttons into a layout

The Example Keyboard

I think the easiest way for me to teach and for you to learn the usage of this, is by examples. Here is an example inline keyboard.

example_keyboard = InlineKeyboardMarkup(
    [
        [
            InlineKeyboardButton('R1,C1',callback_data='r1c1'),
            InlineKeyboardButton('R1,C2',callback_data='r1c2'),
            InlineKeyboardButton('R1,C3',callback_data='r1c3')
        ],
        [
            InlineKeyboardButton('R2,C1',callback_data='r2c1'),
            InlineKeyboardButton('R2,C2',callback_data='r2c2'),
            InlineKeyboardButton('R2,C3',callback_data='r2c3')
        ],
        [
            InlineKeyboardButton('R3,C1',callback_data='r3c1'),
            InlineKeyboardButton('R3,C2',callback_data='r3c2')
        ]
    ]
)

Here’s how it looks when it’s sent.

Map the example keyboard with the result and you will be able to understand this concept. But you have to try it and play with it by yourself to get the real idea. “Note that here, we’re only storing the keyboard in a variable — not sending it yet. Get the full example keyboard code...” from here, on my github.

The Yes/No keyboard

Alright, we need an inline keyboard which has two columns but one row. Try to create this keyboard by yourself and check it with the following one.

keyboard = InlineKeyboardMarkup(
    [
        [
            InlineKeyboardButton('Yes😍',callback_data='yes'),
            InlineKeyboardButton('No😏',callback_data='no')
        ]
    ]
)

What Is callback_data ?

If you're not someone who’s just scrolling mindlessly, you might have this question in your head now.

Okay. I will ask you a question. How are we going to know when a button is pressed? I mean what's the point of creating a inline keyboard if it's not responsive. This is where we need a Callback Query.

If user pressed a button Telegram will send us a Callback Query which has callback_data with other important details. For example, if user pressed the Yes😍 button, we will get a Callback Query update which has yes data. If user pressed the No😏 button, guess what? — Yep, we will get a Callback Query with the no callback data.

But how we get the Callback Query? — Just wait, you will know.

How To Attach The Keyboard to The Message

Yeah, creating a keyboard and storing it to a variable does not attach your beautiful keyboard to the message. To do so, we need to pass the keyboard variable to a optional parameter in client.send_message() called reply_markup. Here's how you do it.

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id
    client.send_message(
        chat_id=chat_id,
        text='Do you like me?',
        reply_markup=keyboard         # <= here
    )

The full code so far:

from pyrogram import Client, filters, types
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton

bot = Client(
    name='examplebot',           
    api_id=12345,                
    api_hash='your api hash',    
    bot_token='your bot token'   
)

keyboard = InlineKeyboardMarkup(
    [
        [
            InlineKeyboardButton('Yes😍',callback_data='yes'),
            InlineKeyboardButton('No😏',callback_data='no')
        ]
    ]
)

@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id
    client.send_message(
        chat_id=chat_id,
        text='Do you like me?',
        reply_markup=keyboard
    )

bot.run()

The React Function

I mentioned this earlier. We are going to edit our message to "I knew it😘" or "Neither do I😏" on pressing Yes😍 or No😏. To do that, we need to know which button the user pressed — right when they pressed it.

Listening for a Callback Query

Just like for the most of problems, all we have to do is listening. But how? — Telegram sends us a callback query. Therefore, if we could listen to all the callback queries sent to us, we will be able to capture it. Isn't it? — Let's try.

@bot.on_callback_query()
def react(client, query):

To listen to message updates, we used on_message. To listen to callback query updates, why not on_callback_query. You might wonder from where do I pull these decorators. Here is the list.

  • on_message()

  • on_edited_message()

  • on_callback_query()

  • on_inline_query()

  • on_chosen_inline_result()

  • on_chat_member_updated()

  • on_chat_join_request()

  • on_deleted_messages()

  • on_user_status()

  • on_poll()

  • on_disconnect()

  • on_raw_update() Please, just don't be scared by looking at this list.

Extracting The Callback Data

In the previous article we learned to get data from updates. In this case, query is our update. You know what to do.

  • Add print(query) inside the react function.

  • Run the file.

  • Start the bot.

  • Click a button.

  • Find a way to get the data

Reacting

Here's the flow chart.

Editing The message

To edit the message, we need to get the message object. Didn't you find a message while you were looking for data in the query update. That's the jackpot.

@bot.on_callback_query()
def react(client, query):
    data = query.data                      # callback data
    message = query.message                # The message we sent with buttons

To edit the message, we use the edit() bound method.

@bot.on_callback_query()
def react(client, query):
    data = query.data
    message = query.message
    if data == 'yes':
        message.edit('I knew it😘')
    else:
        message.edit('Neither do I😏')

That’s it! Run the bot and enjoy your little digital love experiment 💖. Here is the full code.

from pyrogram import Client, filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton

bot = Client(
    name='examplebot',          
    api_id=12345,                
    api_hash='your api hash',    
    bot_token='your bot token'  
)

keyboard = InlineKeyboardMarkup(
    [
        [
            InlineKeyboardButton('Yes😍',callback_data='yes'),
            InlineKeyboardButton('No😏',callback_data='no')
        ]
    ]
)


@bot.on_message(filters.command('start') & filters.private)
def love(client, message):
    chat_id = message.chat.id
    client.send_message(
        chat_id=chat_id,
        text='Do you like me?',
        reply_markup=keyboard
    )


@bot.on_callback_query()
def react(client, query):
    data = query.data
    message = query.message
    if data == 'yes':
        message.edit('I knew it😘')
    else:
        message.edit('Neither do I😏')


bot.run()

Final Thoughts

This project might look naive in practice, but the main focus of this article was to teach you how to use inline buttons. So, if you gained even a little understanding — mission accomplished.

To get better at this, you need to practice. You don’t have to build the most practical applications to learn the basics — just create a bunch of small projects that apply what you've learned.

You can subscribe to the newsletter to get new articles delivered straight to your inbox the moment I post them. Follow me on GitHub for more contents — and maybe Instagram too.

You can support my effort by reacting to and commenting on this article. Share it with someone who would find it valuable.

References

4
Subscribe to my newsletter

Read articles from Beenuka Hettiarachchi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Beenuka Hettiarachchi
Beenuka Hettiarachchi