How To Create a Telegram Bot Using Python

Telegram

For the past year, Telegram has introduced tons of new features including in-app games, bots, Telegraph and Instant Views, channels, groups and many more. What's going to be a next killer feature? Nobody knows. In this humble note I would like to show you how you can create a simple telegram bot using a popular programming language called Python.

Bots are great at many things, especially at automating borings tasks. It is up to your imagination what functions your future bot will have, but today we are going to create the one which will communicate with Planet Python, popular Python news aggregator. Bot will simply parse latest content and send it back to you via Telegram.

Our app will consist of django app and its source code is available on my github as planetpython_telegrambot repo. Feel free to fork and do whatever you want to do :)

Creating a Telegram Bot

First of all in order to create a telegram bot you have to have a Telegram account. If you do, then go ahead and open your telegram app (mobile or desktop) and follow the steps:

  • Add to your contact list BotFather
  • Start a conversation with BotFather by clicking Start button. You will see a command list immediately.
  • In order to create a new telegram bot, you have to type /newbot command and follow the instructions. Be careful, your bot's username has to end with bot. For example, DjangoBot or Django_bot.
  • I decided to choose PlanetPythonBot, which is pretty straightforward considering its functionality.

Telegram bot

Python Planet бот

If everything is okay, you will see bot's token or API access key at the end.

By the way, BotFather is also able to perform following actions for you:

  • Put description to your bot
  • Upload avatar
  • Change access token
  • Delete your bot etc.

Let's Code a Telegram Bot

Previously I have mentioned that we are going to create a Django application. But it is not mandatory, you can also write a simplest Python script which will communicate with Telegram service periodically using API call getUpdates. Telegram has two mutually exclusive API communication approaches:

  • using API call getUpdates
  • setting up a Webhook

Hi there! I want you to know that I am writing a book about how to build a Telegram bot. If you want to know more, check out this post.

The idea of Webhook is about providing special URL (post-back) to your bot and when event occurs (someone starts conversation with bot for example), Telegram service will send post requests to this URL, providing necessary information (chat id, username, content and further meta information). We are going to use this approach while building our own web application. In order to set post-back URL, we are going to use API call setWebhook. But Telegram requires HTTPS, here you have two options:

  • Obtain a valid SSL certificate (buy or set up free from Let's Encrypt)
  • Generate self-signed certificate using tools like OpenSSL

More detailed information about getUpdates and setWebhook are available here and here.

To communicate with Telegram API we are going to use python library called telepot. Lets write some python code, but before that I recommend you to set up separated python environment using tools like virtualenv:

pip install telepot

The following code show the simplest way to communicate with Telegram API (start python shell for example). You have to replace the value of token variable with your bot's access token.

import telepot
token = '123456'
TelegramBot = telepot.Bot(token)
print TelegramBot.getMe()

After executing above code, you will receive something like this:

{u'username': u'PythonPlanetBot', u'first_name': u'Python Planet Bot', u'id': 199266571}

Congratulations! You made your first API call getMe to Telegram which returns information about bot such as its username, bot id etc.

Now add your newly created bot to your telegram contact list and start conversation by sending /start.

Telegram Bot

And now execute following code:

TelegramBot.getUpdates()

And we will receive:

[{u'message': {u'date': 1459927254, u'text': u'/start', u'from': {u'username': u'adilkhash', u'first_name': u'Adil', u'id': 31337}, u'message_id': 1, u'chat': {u'username': u'adilkhash', u'first_name': u'Adil', u'type': u'private', u'id': 7350}}, u'update_id': 649179764}]

getUpdates API call returns list of objects called Update. Every Update object consists of Message objects. For our bot example we are interested only in Message which has text attribute (content) and chat object which indicates a user who started conversation and chat id to reply to. Also, pay attention to update_id attribute, its value is important if you are going to use getUpdates approach for monitoring incomming requests instead of Webhooks. When calling getUpdates method, you can provide an offset, usually offset value is calculated as last call updated_id value + 1, which means that you will receive everything except what you have already got from the last call :) Sounds messy, let me illustrate with code. Send some message to your bot once again and call:

TelegramBot.getUpdates(649179764+1)
[{u'message': {u'date': 1459928527, u'text': u'hello bro', u'from': {u'username': u'adilkhash', u'first_name': u'Adil', u'id': 31337}, u'message_id': 13, u'chat': {u'username': u'adilkhash', u'first_name': u'Adil', u'type': u'private', u'id': 7350}}, u'update_id': 649179765}]

It is enough to know in order to create our telegram bot. Let's see how django app will look like.

First we have to parse Planet Python RSS feed. Following fuction does it:

# -*- coding: utf8 -*-
from xml.etree import cElementTree
import requests
def parse_planetpy_rss():
    """Parses first 10 items from http://planetpython.org/rss20.xml
    """
    response = requests.get('http://planetpython.org/rss20.xml')
    parsed_xml = cElementTree.fromstring(response.content)
    items = []
    for node in parsed_xml.iter():
        if node.tag == 'item':
            item = {}
            for item_node in list(node):
                if item_node.tag == 'title':
                    item['title'] = item_node.text
                if item_node.tag == 'link':
                    item['link'] = item_node.text
            items.append(item)
    return items[:10]

I am using requests library for http(s) things in python. I do not handle any exception just to avoid code overloading. Here is how Django view looks like:

TOKEN = '<Our_Bot_Token>'
TelegramBot = telepot.Bot(TOKEN)
def _display_help():
    return render_to_string('help.md')
def _display_planetpy_feed():
    return render_to_string('feed.md', {'items': parse_planetpy_rss()})
class CommandReceiveView(View):
    def post(self, request, bot_token):
        if bot_token != TOKEN:
            return HttpResponseForbidden('Invalid token')
        commands = {
            '/start': _display_help,
            'help': _display_help,
            'feed': _display_planetpy_feed,
        }
        try:
            payload = json.loads(request.body.decode('utf-8'))
        except ValueError:
            return HttpResponseBadRequest('Invalid request body')
        else:
            chat_id = payload['message']['chat']['id']
            cmd = payload['message'].get('text')  # command
            func = commands.get(cmd.split()[0].lower())
            if func:
                TelegramBot.sendMessage(chat_id, func(), parse_mode='Markdown')
            else:
                TelegramBot.sendMessage(chat_id, 'I do not understand you, Sir!')
        return JsonResponse({}, status=200)
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(CommandReceiveView, self).dispatch(request, *args, **kwargs)

CommandReceiveView is where magic happens. It receives POST request and handles it appropriately according to command. Full source code is available here. Take a look at a new API call - sendMessage.  You can use it when you want to send something back to user by providing chat_id and content. Chat id is a unique identification of conversation between a user and a bot. When you call getUpdates, you see it on every Update object. But Telegram has a restriction on bots which forbids them to send messages to a user if a particular user did not initiate conversation with bot (anti-spam protection).

I hope you have already cloned my repo and started django app. Now it is time to test our web app. In order to simulate intercommunication between our app and Telegram API service, I will use Chrome extention which is called Postman.

Postman is a great tool which helps you test your apps by sending GET/POST/DELETE/PUT etc requests to a particular URL. We are going to send POST request to our CommandReceiveView and see how it is going to handle it.

Run your web app by executing runserver command. Target post URL is:

http://127.0.0.1:8000/planet/b...

where BOT_TOKEN should be replaced to a given access token key.

Request body can be obtained from one of the Update objects which we got when called getUpdates method. Take a look at screenshots:

Postman REST Client telegram-bot-postman

Let's provide feed command to our POST URL.

Postman и TelegramPostman и Telegram

You can see on screenshots that Telegram bot handled our request correctly. The next step is going to be app deployment and SSL certificate installation to set webhook.

Part 2 — How to deploy a Telegram bot

Links: