Telegram Bot: From the first line to deployment

Arush Sharma
7 min readFeb 13, 2019

--

NO! Bots are not Robots!

This will be a walk through of a bot I made to take care of a few things I thought were annoying. If all you care about is CODE then go ahead to the link. It’s not a very complicated one and should be fairly easy to understand.

I’ll try to include resources to everything that helped me along the way.

What does it do?

  1. Sending Emails

No, This is not stupid. I feel like there has to be an explanation. So, I had to send a lot of emails during my internship which also had to be in a specific format most times. Also, the gmail app is quiet annoying with all the red and stuff. The bot allowed me to send emails via chat. I would just text the bot and it would format and send it.

2. Saving Stuff

I use telegram a lot and come across important links or just info I would like to save and copying is often repetitive. The bot would save the stuff I forward to it and wanted to store in a SQL database.

Python should be in a movie now

What to use ?

There are wrappers (even multiple) available for the Telegram API in every language. From Python, PHP, Java to Node.js. We’ll of course be using Python specifically python-telegram-bot (You should check them out and contribute if you can right here.)but anyone interested in other language or wrapper should go through this awesome page below.

Plagiarism indeed!

The Bot

Chat with the BotFather to get your API key

Telegram bot creation is a novel process because much of it is centered around your own interactions with a Telegram bot. That bot is the BotFather. Inside your Telegram desktop app, you need to search his username and start a conversation with him.

Give the start command to BotFather by typing /start.

there’s no BotMother I guess..

You’ll see a list of commands that help you create, edit, and manage your bots. Since it’s your first time, you’ll want /newbot.

After giving the /newbot command, you get to pick a name and username for your bot. The name is what your users will see the bot as in their contacts list, and the username is how they’ll find it. Think of the username like a Twitter handle; it has to be unique, and it’s best if it’s short and memorable.

With that done, you’ll be given your bot’s API key. The API key is how Telegram knows the code you write is associated with this particular bot. Every bot has its own API key, and you shouldn’t share it with anyone or they could hijack your bot and have it carry out their evil deeds.

YUP!

CODE

Start with importing everything we’ll be using.

from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove
import logging
import sqlite3
from mail import send_mail
from sensitive import tok, user_id, name, username

Logging and Sqlite3 are fairly self-explanatory. They will be used for logging and storing as and when needed. Sensitive is a file that contains my personal credentials needed. telegram.ext contains all the handlers.

Updater — This class, which employs the telegram.ext.Dispatcher, provides a frontend to telegram.Bot to the programmer, so they can focus on coding the bot. Its purpose is to receive the updates from Telegram and to deliver them to said dispatcher. It also runs in a separate thread, so the user can interact with the bot, for example on the command line. The dispatcher supports handlers for different kinds of data: Updates from Telegram, basic text commands and even arbitrary types. The updater can be started as a polling service or, for production, use a webhook to receive updates. This is achieved using the WebhookServer and WebhookHandler classes.

CommandHandler - Handler class to handle Telegram commands. Commands are Telegram messages that start with /, optionally followed by an @ and the bot’s name and/or some additional text.

MessageHandler - Handler class to handle telegram messages. They might contain text, media or status updates.

ConversationHandler — A handler to hold a conversation with a single user by managing four collections of other handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple users are managed by instances of this class.

Commands

Each function is for a command. The rest is just dealing with SQL and Strings with python.

I am a great “programmer”
TOKEN = tok# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)TO, SUBJECT, MESSAGE = range(3)# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
"""Send a message when the command /start is issued."""
user = update.message.from_user
send = f"{user.username} started your bot. \n First name {user.first_name} \n ID:{user.id}"
bot.send_message(chat_id=user_id, text=send)
update.message.reply_text('Hi!')
def help(bot, update):
"""Send a message when the command /help is issued."""
update.message.reply_text('COMMANDS \n /view_events@eventattendbot \n /add_event@eventattendbot \n '
'/remove_event@eventattendbot \n')
def add_event(bot, update):
message = update.message.text
link = message.split()[1]
user = update.message.from_user['username']
with sqlite3.connect("events.db") as con:
conn = con.cursor()
# conn.execute('''CREATE TABLE EVENT
# (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
# LINK TEXT NOT NULL);''')
conn.execute("INSERT INTO EVENT (LINK) VALUES (?)", (link, ));
con.commit()
update.message.reply_text("@" + str(user) + ' Added An Event Link!')
def remove_event(bot, update):
message = update.message.text
link = message.split()[1]
user = update.message.from_user['username']
with sqlite3.connect("events.db") as con:
conn = con.cursor()
conn.execute("DELETE FROM EVENT WHERE LINK=?", (link,))
con.commit()
update.message.reply_text("@" + str(user) + ' Deleted An Event Link!')
def view_events(bot, update):
with sqlite3.connect("events.db") as conn:
cur = conn.cursor()
cur.execute("SELECT * FROM EVENT ORDER BY ID DESC")
all = cur.fetchall()
links = " LINKS \n\n"
for a in range(len(all)):
link = all[a][1]
links += str((a+1)) + ". " + str(link) + "\n\n"
links += "\n\n"
update.message.reply_text(links)
def error(bot, update, error):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)

Email

Dealing with email is a bit more complicated(not if you get ConversationHandler). To fully grasp the idea of how values from one function are transferred to another and dealt with, read this.

Please STOP!
def email(bot, update):
id = update.message.from_user.id
if id == user_id and update.message.from_user.first_name == name and update.message.from_user.username == username:
update.message.reply_text("Give me an email address",
reply_markup=ReplyKeyboardMarkup([['arushssdkbva91@gmail.com']],
one_time_keyboard=True))
return TO
def to(bot, update, user_data):
user = update.message.from_user
key = f"{user.id} to"
value = update.message.text
user_data[key] = value
logger.info("email request by %s to %s", user.first_name, update.message.text)
update.message.reply_text("Now, the Subject for the email", reply_markup=ReplyKeyboardRemove())
return SUBJECT
def subject(bot, update, user_data):
user = update.message.from_user
key = f"{user.id} subject"
value = update.message.text
user_data[key] = value
logger.info("email subject %s", update.message.text)
update.message.reply_text("Now, the Body for the email")
return MESSAGE
def body(bot, update, user_data):
user = update.message.from_user
logger.info("email body %s", update.message.text) email_to = user_data[f"{user.id} to"]
email_subject = user_data[f"{user.id} subject"]
send_mail(email_to, email_subject, update.message.text)
del user_data[f"{user.id} to"]
del user_data[f"{user.id} subject"]
update.message.reply_text(f"email sent!")
return ConversationHandler.END
def cancel(bot, update):
update.message.reply_text('Canceled.')
return ConversationHandler.END

As you can see in send_mail() in the def body it is a function I have imported from the module mail. I’ve used smtp to send emails.

def send_mail(to, subject, body, my=me):
smt = smtplib.SMTP('smtp.gmail.com', 587)
smt.ehlo()
smt.starttls()
smt.login(user=my, password=pwd)
sub = subject
body = body
message = "Subject: " + sub + "\n" + body + "\n"
smt.sendmail(my, to, message)
smt.quit()

You’ll also have to get a separate username and password to send an email via smtp here is a good resource.

That’s it

Well, not technically. You still have to start running everything.

Nothing at My Place is in it’s Place…
def main():
"""Start the bot."""
updater = Updater(TOKEN)
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
dp.add_handler(CommandHandler("add_event", add_event))
dp.add_handler(CommandHandler("view_events", view_events))
dp.add_handler(CommandHandler("remove_event", remove_event))
email_handler = ConversationHandler(
entry_points=[CommandHandler('email', email)],
states={
TO: [MessageHandler(Filters.text, to, pass_user_data=True)],
SUBJECT: [MessageHandler(Filters.text, subject, pass_user_data=True)],
MESSAGE: [MessageHandler(Filters.text, body, pass_user_data=True)]
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(email_handler)
dp.add_error_handler(error)
updater.start_polling()
updater.idle()

Deployment

There are a lot of ways of deployment depending on how you plan to use it and how much you want to use it.

No one knows..
  1. Pythonanywhere — Free and easy. Just run s terminal and run your bot file but don’t expect it to run for long.
  2. Heroku — Don’t reinvent the wheel so here it is. The link below has a great step by step Heroku deployment tutorial.

Some other awesome options you could try.

--

--

Arush Sharma
Arush Sharma

Written by Arush Sharma

Engineer who loves Machine Learning, Data Science and Python. Linux for life.

No responses yet