Almost two years ago Telegram let developers create bots quite painlessly. You can read an introduction about it on Telegram website. In this article we will create a simple bot in python, it’ll be hosted in Azure using Bottle framework. The bot will not do anything fancy, consider it as a template for your python based bots.

Here are the things which we’ll cover:

Registering your bot in Telegram app Creating a Bottle App in Azure Configuring Bottle server to run as HTTPS (which is required by Telegram) Creating an API endpoint for communication with Telegram

After that, I will also introduce you to inline bots.

Registering bot in Telegram app

You can do it without leaving the Telegram app. Your contact person is, well, a bot named @BotFather. Start a conversation with him:

He’s a really cool guy and I’m sure you’ll not have any problems with creating a new bot. Just write /newbot, then choose a name and username for it (the latter should end with bot).

If everything goes smoothly, you’ll receive your bot’s token, which we’ll use in the next step.

Creating a Bottle App in Azure

Bottle is a lightweight server framework for python. It’s available in Azure as a Web Application. In order to create it, open Azure Portal, look for „bottle” in new app search window.

Click on it, then choose a name for your app and resource group.

After about a minute, your app should be ready. You should be able to see it on http://APPNAME.azurewebsites.net

Open it in azure, and search for “Deployment options”.

By default azure bottle app is deployed from this git repository. Let’s create a local git repository just for your project. In Deployment options, click Disconnect and confirm. Then, click Setup button, select Local git repository and confirm. This action may take about a minute to reflect in the UI, after that you should see an address to your git repo in Overview screen:

Copy that link, then switch to cmd/terminal, go to directory of your choice and clone your repo (I hope you have git installed!):



git clone (repolink)

You will be asked for user password, you can set it up in Deployment Credentials:

Let’s take a look at the files:

app.py — this file contains initialization of your bottle server

routes.py — it specifies logic which should be executed for API routes (and returns data)

requirements.txt — all python dependencies have to be listed here

.skipPythonDeployment — if this file exists, azure will not proceed with full python deployment each time you commit (you should remove it for commits which add new dependencies)

Other files and folders are less significant for this tutorial, you can figure out their importance by yourself (or ask in comments, I’ll be happy to help).

Configuring Bottle server to run as HTTPS

Telegram doesn’t support HTTP requests for bot API, so we need to make sure our method is reachable using HTTPS. Bottle doesn’t offer HTTPS server out of the box. To archive this, we need to create a ServerAdapter. You can use one of the implementations floating on the Internet, like this.

Copy the code of server class and paste it into your app.py file (right under the imports is fine):

class SSLWSGIRefServer(bottle.ServerAdapter):

def run(self, handler):

from wsgiref.simple_server import make_server, WSGIRequestHandler

import ssl

if self.quiet:

class QuietHandler(WSGIRequestHandler):

def log_request(*args, **kw): pass

self.options['handler_class'] = QuietHandler

srv = make_server(self.host, self.port, handler, **self.options)

srv.socket = ssl.wrap_socket (srv.socket,

certfile='server.pem', server_side=True)

srv.serve_forever()

certfile=’server.pem’ is a path to your server certificate. It’s easy to create it using OpenSSL by running the command below, you can provide empty input for all certificate fields except for Common Name, for it you have to use your domain address (APPNAME.azurewebsites.net):

openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes

Place generated server.pem file in the root folder of your app.

Now, let’s switch to using new class. Change your startup code to look like this and remember to use your app’s name:

if __name__ == '__main__':

app = bottle.Bottle()

srv = SSLWSGIRefServer(host="APPNAME.azurewebsites.net", port=80)

bottle.run(server=srv)

python-telegram-bot

To communicate with Telegram API, we’ll use a wrapper called python-telegram-bot.

This package is not available in your azure environment. In order to add it, you need to do two things:

add line python-telegram-bot==5.3.0 to requirements.txt

to requirements.txt remove file .skipPythonDeployment

Azure will make sure all package requirements are met during deployment (which happens after each commit).

Communication between Telegram and our server can be done in 2 different ways:

1. Our server can check for updates from Telegram in a loop using getUpdates method

2. We can hook telegram to our server’s API method, so everytime there is some user message waiting, Telegram will send us an HTTPS POST request with details.

In this tutorial we’ll use thesecond approach.

Creating an API in Bottle

Let’s open routes.py file. You can see many routes specified here, all of them useless for our use case, you can safely remove them. We will add two methods:

/botHook will respond to telegram’s requests. Our bot will take the incoming message, assume it’s in format a+b, split it into 2 ints and answer with the sum of these numbers.

/setWebhook will be responsible for hooking telegram to our API. You could move this logic to app.py file, so it’d be done with each deployment/server restart, but I prefer to have quick access to it this way. We’ll pass the address of the /bootHook method. It will return a boolean value, which will indicate if the hooking was successful.

Here’s the full code for routes.py which you can use. Make sure to use your app name and bot token:

from bottle import route, view, run, request

import telegram TOKEN = 'TOKEN'

APPNAME = 'APPNAME'

def setWebhook():

bot = telegram.Bot(TOKEN)

botWebhookResult = bot.setWebhook(webhook_url='

return str(botWebhookResult) @route ('/setWebhook')def setWebhook():bot = telegram.Bot(TOKEN)botWebhookResult = bot.setWebhook(webhook_url=' https://{}.azurewebsites.net/botHook'.format(APPNAME)) return str(botWebhookResult)

def botHook():

bot = telegram.Bot(TOKEN)

update = telegram.update.Update.de_json(request.json, bot)

bot.sendMessage(chat_id=update.message.chat_id, text=getSum(update.message.text, update.message.from_user.username))

return 'OK' @route ('/botHook', method='POST')def botHook():bot = telegram.Bot(TOKEN)update = telegram.update.Update.de_json(request.json, bot)bot.sendMessage(chat_id=update.message.chat_id, text=getSum(update.message.text, update.message.from_user.username))return 'OK' def getSum(query, userName):

try:

splittedBySum = query.split('+')

if len(splittedBySum) != 2:

raise ValueError('Too complicated stuff')

return str(int(splittedBySum[0]) + int(splittedBySum[1]))

except:

return "I'm sorry, {}. I'm afraid I can't do that".format(userName)

Done!

Now upload all our changes:

git add .

git commit -m 'Simple bot'

git push origin master

After the deployment is done, call webook method in your browser:

https://APPNAME.azurewebsites.net/setWebhook

If it resulted with True, then everything should be ready to have your first conversation with your awesome bot, give it a try!

Inline bots

We created a bot which answers with one result. Sometimes we’d like our bot to provide user couple of options. In Telegram it’s possible with inline bots. To see that multiple results you need to add your bot to some group conversation and start typing @botname query.

To archive it, we need to activate this feature by talking to @BotFather. After switching it on with /setinline, it’s also required to provide text which will be shown to user after writing @YourBot in chat

We also need to change /bootHook method (it has to support both normal messages and inline queries) and provide a small helper function:



def botHook():

bot = telegram.Bot(TOKEN)

update = telegram.update.Update.de_json(request.json, bot) @route ('/botHook', method='POST')def botHook():bot = telegram.Bot(TOKEN)update = telegram.update.Update.de_json(request.json, bot) if update.message is not None:

bot.sendMessage(chat_id=update.message.chat_id, text=getSum(update.message.text, update.message.from_user.username))

elif update.inline_query is not None:

results = list()

results.append(getArticleFromText("Just what do you think you're doing, {}?".format(update.inline_query.from_user.username)))

results.append(getArticleFromText(getSum(update.inline_query.query, update.inline_query.from_user.username)))

bot.answerInlineQuery(update.inline_query.id, results) return 'OK' def getArticleFromText(text):

return telegram.InlineQueryResultArticle(id=text.upper(),title=text,input_message_content=telegram.InputTextMessageContent(text))

Here’s how it looks like in action: