August 28, 2015

885 words 5 mins read

Tutorial: PoC Telegram Bot running in AWS Lambda

2015-08-28 11_39_40-Telegram AWS Lambda Bot
My favorite “SMS Alternative” app is Telegram Messenger. It’s simple, easy to use, has decent encryption (from what I understand), is very multi-platform, and has a lot of fun features. It is my sole method of communicating with some people and was recently used for a 15 person group text at Outside Lands where it worked flawlessly. Needless to say when they announced a bot platform (that was quite well thought out), I was eager to give it a try. My first thought was to write a bot in NodeJS. My second thought was “I wonder if I could do it in Lambda? Maybe the API Gateway would work for the webhooks?". My third thought was “Let’s try it!".

telegrambot-flowchart2
What I’ve written is a very simple “proof of concept” Telegram Bot. It uses the documented API to register itself for webhook messages. This way every time a user messages your Bot, the content gets posted (in JSON) to an AWS API gateway. That data is in turn fed to AWS Lambda which stores the data (in DynamoDB) and then responds by posting back to the sendMessage Telegram API. This is the core workflow of all Telegram Bots, the only difference in my case is I’m using “AWS Lambda” instead of “NodeJS and Express on a server”.

If you’d like to give the bot a test first, you can message it on Telegram as AWSLambdaBot. What follows is my setup instructions for replicating my PoC. I’m assuming you’ve used Lambda/DynamoDB/API Gateway a little bit in the past. If you haven’t, check out my Tutorial: AWS API Gateway to Lambda to DynamoDB as it meticulously documents every step.

Section 1 — Get the code

Either visit ShakataGaNai/poc-telegram-bot-aws-lambda on GitHub or download the full zip file. The README.md file includes a more terse version of these instructions, for those of you who know what you’re doing in AWS.

Section 2 — Register a Telegram Bot

<td width="150px">

2015-08-28 12_09_56-Telegram botfather

<td>
</td>
<td>

2015-08-28 12_32_44-Telegram

<td>

2015-08-28 12_13_08-Telegram user picture

Step 1 — Message the BotFather
Additional details available from telegram
Step 2 — Save the API key.
It looks something like "99999999:ASDFKEYKEYKEYASDFSAVETHEKEY"
Step 3 — Disable group join
This is a PoC bot that only does echo. Group joins would be bad.
Step 4 (Optional) — Set a user picture

Section 3 — Create the Lambda Functions

<td width="150px">
</td>
<td>

2015-08-28 12_20_47-Store

<td width="150px">
</td>
<td>

2015-08-28 12_23_32-Store

Step 1 — Create a Lambda function named set setTelegramWebhook
Allocate 1024MB of memory
Use a "Basic Execution" role
Step 2 — Code the Lambda
Use the contents of setTelegramWebhook.js
Change the botAPIKey to your API key from section 1
Step 3 — Create a Lambda function named set telegramEcho
Allocate 1024MB of memory
Use a "Basic with DynamoDB" role
Step 4 — Code the Lambda
Use the contents of telegramEcho.js
Change the botAPIKey to your API key from section 1

Section 4 — Create a DynamoDB table

<td width="150px">

2015-08-28 12_28_05-DynamoDB · AWS Console

<td>

2015-08-28 12_28_44-DynamoDB · AWS Console

<td>

2015-08-28 12_29_52-DynamoDB · AWS Console

Step 1 — Create a DynamoDB table
Name: telegramlog
Primary Key Type: Hash and Key
Hash Name: username (string)
Range Name: updateid (number)
Step 2 — Provision capacity
My default is 5 read, 2 write. This costs ~ $1.46/mo.
The app itself does no reads, only writes on message received.
Step 3 — Confirm & Create

Section 5 — Create the API Gateways

<td width="150px">

2015-08-28 12_35_39-Store

<td>

2015-08-28 12_38_59-API Gateway

<td>

2015-08-28 11_36_27-API Gateway

<td>

2015-08-28 12_41_16-API Gateway

<td>

2015-08-28 12_42_12-API Gateway

Step 1 — Create two resources
/telegram & /setwebhook
Step 2 — /telegram POST method
Use Lambda Function
Set to the telegramEcho function you created in Section 3
Step 3 — /setwebhook POST method
Ues Lambda function
Set to the setTelegramWebhook function you created in Section 3
Step 4 — Deploy the API
Stage name: main
Step 4 — Get your Invoke URL
It should look like: https://YOURURL.execute-api.us-west-2.amazonaws.com/main/

Section 6 — Set Webhook & Test

<td width="150px">

2015-08-28 12_45_53-root is my user

<td>
</td>
<td>

2015-08-28 11_40_12-Telegram

<td>

2015-08-28 11_34_54-New notification

Step 1 — CURL the setWebook
curl -H "Content-Type: application/json" -X POST -d "{\"url\":\"https://YOURURL.execute-api.us-west-2.amazonaws.com/main/telegram\"}" https://YOURURL.execute-api.us-west-2.amazonaws.com/main/setwebhook/
You need to change YOURURL to your invoke URL from Section 5.
Note: Do NOT put a trailing forward slash in the JSON. Leave the slashes exactly as in the sample.
Step 2 — Verify you were successful!
Step 1 should have responded with "Webhook was set". Anything other than this and something went wrong.
Step 3 — Message your bot!
Step 4 (Optional) — Check DynamoDB
You should see one entry per message, with the full details of the JSON included.

While this looks really lengthy, it’s actually all quite easy. If everything goes according to plan, your bot should now respond to every message you send it by echoing back what you sent. However, it only supports text messages (that includes emoji). If you send it pictures, stickers, voices, files, or anything else — it will respond with “undefined”. You’ll also note the blue “reply quote” in the messages. This is because I chose to include the ‘reply_to_message_id’ variable which you can drop from Lambda, if you’d like.

I’ve left AWSLambdaBot online and running for now, if you’d like to test it out without doing all the work above. The README.md file on GitHub also includes a FAQ. Please feel free to fork/comment/test as you see fit. If you use this as a starter for your own bot, let me know because I’d love to see what comes out of this PoC.