How to Write a Telegram Bot to Send Messages with Python Part 2

Made-Up Masters
5 min readAug 10, 2020

--

In this guide we’ll build on the basic setup from Part 1 and write a few real-world examples including:

  1. An example where we have a long-running loop and use code to send ourselves updates
  2. A callback with the machine-learning library pytorch-lightning to update us on how our model is training
  3. How to make our code work in Google Colab without having to set it up every single time.

Example 1 is meant for beginners whereas examples 2 and 3 are much more specific and are meant for people who already use lightning or Colab.

The old way:

Skip to “Example 1” if you’re familiar with printing updates and tqdm.

total_items = len(my_list)
for i, item in enumerate(my_list):
do_something(item)
if i % 100 == 0:
print(f"Completed {i}/{total_items})

Above is a very typical piece of code. We are iterating over the items in a list and doing something to them. If that thing takes a lot of time, we might print out a periodic update of how much we’ve completed. Another way to keep track would be to use the progress-bar library tqdm.

from tqdm import tqdmfor item in tqdm(my_list):
do_something(item)

This is cleaner but still doesn’t help us if we’re going to be away from the computer

Example 1: Sending updates during and after a loop

This code will do the same thing as the previous section, but instead of printing the update, it sends the update message to our telegram.

import telegram_sendtotal_items = len(my_list)
for i, item in enumerate(my_list):
do_something(item)
if i % 100 == 0:
message = f"Completed {i}/{total_items}
try:
telegram_send.send(messages=[message]))
except Exception as e:
print("Unable to send", e)

Note that it is very important that you wrap your telegram_sendcode in a try/except block as you might have a brief internet outage and don’t want it to crash your program. This will be a rare event, but if you want to make sure you minimize the chances of missing an update, you can write code to try multiple times.

Only interested in knowing when the code is finished? Just add a telegram_send.send(message) call after your loop completes

import telegram_sendfor item in my_list:
do_something(item)
telegram_send.send(messages=["All done. Get back to work!"]))

Example 2: A callback to update you on an ML model

Callbacks are now an almost ubiquitous feature of machine learning libraries and frameworks. If you know how to write a callback in your framework of choice, you’ll have no problem adapting this code to work with it. I’ve been trying pytorch-lightning lately, so the code below uses that, but it should apply equally to fastai, pytorch and even tensorflow.

Version 1: A simple alert when training is complete

This is pretty straightforward if you’re familiar with callbacks. Remember to wrap your `telegram_send.send` call in a try except block, you don’t want your training to crash because of a brief outage!

class TelegramCallback(Callback):
def on_fit_end(self, trainer):
try:
telegram_send.send(messages=["Finished Training!"])
except Exception as e:
print("Unable to send: ", e)

Version 2: Send metrics after every epoch

Inside our `on_epoch_end` callback, we have access to trainer and pl_module We can access metrics via trainer.callback_metrics() which I believe is a dictionary of the scalar metrics you’re logging in lightning. Add whichever values you want to the message and then send as usual.

class TelegramCallback(Callback):
def on_epoch_end(self, trainer, pl_module):
m = trainer.callback_metrics.items()
message = f"Epoch: {m["epoch"]}\n"
message += f"Train Loss: {m["train_loss"]}
message += f"Accuracy:" {m["accuracy"]}
try:
telegram_send.send(messages=[message])
except Exception as e:
print("Unable to send:", e)

Version 3: A quick refactoring and putting it all together

The above version isn’t too bad, but I want it to be easier to add/remove metrics without the code getting too long. I also want to round the output so my loss doesn’t include 10 digits after the decimal point.

def on_epoch_end(self, trainer, pl_module):
include = ["epoch", "train_loss", "val_loss", "avg_cer", "avg_wer"]
d = {k:round(float(v), 3) for k,v in trainer.callback_metrics.items() if k in include}
messages = [f"{k} : {v}" for k,v in d.items()]
try:
telegram_send.send(messages=[messages])
except Exception as e:
print("Unable to send:", e)

Example 3: Making it work in Colab

Google Colab is a fantastic free resource, but it can be a bit hard to work with as it wipes the system clean every time you end a session. You can install telegram-send and go through the configuration process every time, but it’s a bit annoying. A better solution is to make a cell to do it for you by manually writing the same telegram-send.conf file that is created during that process.

First you’ll need to find your chat id by visiting api.telegram.org/bot<yourtokenhere>/getUpdates and look through the json for “chat” and then “id”. You are looking for a 9 or 10 digit number that represents the chat id between the bot and yourself. A few notes:

  1. The url format can be a bit confusing so here’s mine: api.telegram.org/bot139 — — — — — — — — — — _UdeY/getUpdates
  2. To get the correct chat id, you must have sent yourself at least one message using the bot

Once you have the chat id, just include this cell in the top of your notebook and you’ll be able to send yourself telegram messages from Google Colab!

!pip install telegram-send
import telegram_send
telegram_token = '139-------------------------_UdeY'
chat_id = '5--------4'
path_config = telegram_send.get_config_path()
with open(path_config, 'w') as f:
f.write(f'[telegram]\ntoken = {telegram_token}\nchat_id = {chat_id}')
telegram_send.send(messages=["Telegram bot synced!"])

That’s it. Now you can go outside knowing your python code/ML model/colab setup will alert you when it’s finished running. Happy hacking!

--

--

Made-Up Masters

All the skills of going back to school, with none of the debt.