Welcome to Part - 3 in the Telegram Python Bot series; in this post, we will be building an interactive bot that will send us songs from our playlists.
This Tutorial/Post is for educational purposes only.
Why make a music bot?
We listen to music every day, and no one hates music; it’s something that everyone enjoys while going through different emotional phases of their day and life; before music streaming became a success, music used to be sold on vinyl disks and broadcasted over the radio on Radio Stations, nowadays everyone has their personally curated playlists on various platforms. Let’s discuss how we will implement some of the online streaming features.
- Random songs from the playlist chosen
- Playlist in shuffle
- Random Shuffle of all the songs available
- Send all songs available
The first three options will be based on user input, and the last is just sending all the data available.
We have two options in which we can send songs to the user
- Files from local storage
- Files from Telegram servers
Using local storage to store and serve our songs is an excellent idea if songs are already stored on the machine, but it has drawbacks.
- Takes up a lot of disk space
- While sending many songs, bandwidth and data transfer issues may arise.
The above two problems can be solved using the files from Telegram Servers, Telegram is a chat service that uses the cloud to provide multi-device support, all of our data is stored in a data centre, and each and every file on Telegram, when uploaded, is assigned a unique
64-bit client identifier called
file_id, these files are stored on the servers and can be sent by referencing these
file_id. We can upload any number of files and reference them; using this, we can overcome the two issues above; however, these
file_id are unique to chats and bots alike. For example, we upload a file
bot1 and get
hexa123quad We can’t use this
file_id to send the file from
bot2. The bots are uniquely identified using
API TOKENS, when a token is reset, all these files are lost, as in we lose access to the files though they might continue to exist on the servers until they are deleted as they are no longer in use, saving Telegram their server space. We’ll be writing the bot script keeping this drawback in mind; this can be eliminated by using persistent data stores/spaces, which can be discussed in a later post but are currently out of scope in this post.
Making the bot
- A Telegram BOT API Token
- Few songs/audio files on the local machine
Setting up a virtual environment
Setting up a virtual environment helps us isolate our dependencies and keep track of them on a per-project basis. We can set up and activate our virtual environment on Linux-based machines using these two commands.
# python3 -m venv <- name of environment -> python3 -m venv venv source ./venv/bin/activate
pip3 install python-telegram-bot ### List installed lib/packages pip3 list ### Save list of installed lib/packages to file pip3 freeze >> requirements.txt ### Install dependencies from requirements.txt pip3 install -r requirements.txt
Please refer to PART-2 of this series to familiarise yourself with making a simple bot that echoes our message.
Working with Files
All music/audio files on Telegram can be identified as objects of
Audio class in the
telegram package, we need the
file_id attribute of this file.
Handling Audio files.
Messagehandler let’s filter all
audio files, and
file_id_command to handle it.
Now, all audio files will be handled by this function; let’s echo this message via the bot.
I’ll be uploading some songs, and I will categorize them into 3 different playlists to demonstrate the multiple playlists’ functionality, upload numerous songs and take the
file_id and add them to the list
playlist, this is a nested list, where each item in
playlist is another list. Make sure you change the
playlist = [ ["CQACAgUAAxkBAANjYqUAAWP9cuxE7EgxTvTf-DG1IPRcAAJ0BgACVhYpVYcf3_AvxxpFJAQ",], ["CQACAgUAAxkBAANvYqUAAXISqEMcoFaCru-FpQ7TV7s6AAJ6BgACVhYpVbItmvoqnv5_JAQ",], ["CQACAgUAAxkBAANxYqUAAXRA6qTTIMbAPQ_LfeQ2jeb3AAJ7BgACVhYpVSuUG82IMlJvJAQ"], ]
Using the song command, let’s start to serve songs; we will provide 4 options,
all in a keyboard fashion; Telegram provides keyboards in two ways.
- Telegram keyboard that replaces general keyboard.
- Inline keyboard - a keyboard that is shown in the chat itself.
We’ll be using the
Inline Keyboard, it provides a clean user experience and has callback functions. We’ll be having two callbacks showing new options based on user input, to keep track of user data and use it in other functions we will be using a nested global dictionary.
- Options callback
- Playlist Callback
Let’s create the keyboard with
InlineKeyboardButton is used to identify a button and
InlineKeyboardMarkup is used to give information to the client for rendering the keyboard; the layout of the keyboard is similar to describing a matrix using
#Defining Keyboards main_options = [ [InlineKeyboardButton(text="Random", callback_data="random")], [InlineKeyboardButton(text="Shuffle", callback_data="shuffle")], [InlineKeyboardButton(text="Random Shuffle", callback_data="random_shuffle")], [InlineKeyboardButton(text="All", callback_data="all")], ] playlist_option = [ [InlineKeyboardButton(text="Playlist 1", callback_data='0')], [InlineKeyboardButton(text="Playlist 2", callback_data='1')], [InlineKeyboardButton(text="Playlist 3", callback_data='2')], ] #Creating Markups main_options_markup = InlineKeyboardMarkup(main_options) playlist_option_markup = InlineKeyboardMarkup(playlist_option)
To handle callbacks from the
InlineKeyboards we need a dedicated handler called the
CallbackQueryHandler, since we have multiple handlers, we mention the callbacks that we can expect using the
pattern argument, which accepts bitwise operations.
dp.add_handler(CallbackQueryHandler(options_choice, pattern="^random|shuffle|random_shuffle|all$")) dp.add_handler(CallbackQueryHandler(playlist_choice, pattern="^0|1|2$"))
Options and Playlist Callback HandlerWhen user chooses
shuffle we provide them with
playlist choice, for
random_shuffle we ask for the number of songs as input and proceed to send songs. All the user choices are stored in the global dictionary.
Taking user choiceUsing a message handler and
Filters we’ll accept only messages while ignoring commands, typecast them into
int and store them in the global dict; let’s also make sure that we only process user input when required using flags such as
accept within the dict.
dp.add_handler(MessageHandler((Filters.text & (~Filters.command)), user_req)) def user_req(update, context): if data[update.effective_user.id]["accept"] == 0: data[update.effective_user.id]["num_songs"] = int(update.message.text) print(update.message.text) data[update.effective_user.id]["accept"] = 1 send_songs(update, context)
Sending SongsWe will be using
send_songs method and will be accessing all the data stored until now, to send songs as per the user choice, and use the
random module to randomize the order of the list using the
random.shuffle method.To merge all the playlists, we can use the
extend method creating a new list
all with all the songs.
all = list() # Using list comprehension all.extend(song for play in playlist for song in play) # regular way # all.extend(playlist) # all.extend(playlist) # all.extend(playlist
def send_songs(update, context): if ( data[update.effective_user.id]["option"] == "random" or data[update.effective_user.id]["option"] == "shuffle" or data[update.effective_user.id]["option"] == "random_shuffle" ): if data[update.effective_user.id]["option"] == "random": # songs = random.shuffle(playlist[data[update.effective_user.id]["playlist"]]) songs = playlist[data[update.effective_user.id]["playlist"]] random.shuffle(songs) random_songs = songs[0:data[update.effective_user.id]["num_songs"]] for _ in random_songs: bot.send_audio(chat_id=update.effective_user.id, audio=_) elif data[update.effective_user.id]["option"] == "shuffle": songs = playlist[data[update.effective_user.id]["playlist"]] random.shuffle(songs) for _ in songs: bot.send_audio(chat_id=update.effective_user.id, audio=_) elif data[update.effective_user.id]["option"] == "random_shuffle": all_songs = all.copy() random.shuffle(all_songs) songs = all_songs[0 : data[update.effective_user.id]["num_songs"]] for _ in songs: bot.send_audio(chat_id=update.effective_user.id, audio=_) elif data[update.effective_user.id]["option"] == "all": for song in all: bot.send_audio(chat_id=update.effective_user.id, audio=song)
We’ve successfully built a bot that can send us songs stored on Telegram Servers while providing a neat user experience throughout the option selection process; using the high-level classes in
python-telegram-bot , we can build bots faster, providing more functionality. I believe that by using the above example, you can make a great music bot based on existing features or create a new one from scratch. You can build much more interesting, functional and unique bots using this platform provided by Telegram; the only limits are our imaginations.
You can find the code on my Github Repository.
Thank you for reading until the end, and see you next time; until then, happy learning~ Kalyan Mudumby