Getting started with Django | Create your first Telegram Bot with Django and Telegram Bot API | A Step-by-Step Guide
Getting started with Django | Create your first Telegram Bot with Django and Telegram Bot API | A Step-by-Step Guide
Introduction
Django is a powerful web framework for Python that allows developers to build and deploy web applications quickly. It provides a lot of built-in features and is highly customizable, making it a popular choice for web development.
Telegram is a messaging platform that allows users to send and receive messages, photos, videos, and other media. It also has a powerful API that allows developers to create bots that can interact with users and perform various tasks.
In this tutorial, we will show you how to get started with Django and create your first Telegram bot using the Django web framework and the Telegram Bot API. We will provide a step-by-step guide on how to set up your development environment, create a Django project, and build a basic Telegram bot that can send and receive messages. By the end of this tutorial, you will have a solid foundation for building more advanced Telegram bots using Django.
Setting up a directory
Firstly, let’s setup a directory.
mkdir attendanceTaker
cd attendanceTaker
Setting up a new environment
Before we do anything else we’ll create a new virtual environment, using venv. This will make sure our package configuration is kept nicely isolated from any other projects we’re working on.
python3 -m venv env
source env/bin/activate
Now that we’re inside a virtual environment, we can install our package requirements.
pip install django
pip install djangorestframework
Getting started
Okay, we’re ready to get coding. To get started, let’s create a new project to work with.
django-admin startproject config
cd config
Once that’s done we can create an app that we’ll use to create a simple Web API.
python manage.py startapp app
We’ll need to add our new app
app and the rest_framework
app to INSTALLED_APPS
. Let's edit the config/settings.py
file:
INSTALLED_APPS = [
...
'rest_framework',
'app',
]
Okay, we’re ready to roll.
Creating a model to work with
Go to app/models.py
file and add following lines of code:
from django.db import models
class Person(models.Model):
tg_id = models.CharField(max_length=254)
tg_username = models.CharField(max_length=254)
tg_fullname = models.CharField(max_length=254)
arrived_at = models.DateTimeField(blank=True, default=None, null=True)
left_at = models.DateTimeField(blank=True, default=None, null=True)
def __str__(self):
return self.tg_id
We’ll also need to create an initial migration for our app model, and sync the database for the first time.
python manage.py makemigrations app
python manage.py migrate app
Creating a Serializing class
In app folder create new file serializers.py
from rest_framework import serializers
from .models import Person
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
Telegram Bot
Go back to config folder using command:
cd ..
Now create bot.py
file.
We will use it for writing our Telegram Bot.
But!
In order to, work with Django Rest Framework properly we need to add in the beginning of bot.py
add following:
import sys
import time
sys.dont_write_bytecode = True
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
import django
django.setup()
from app import models, serializers
from asgiref.sync import sync_to_async
Now import all necessary libraries
import logging
from datetime import datetime
import pytz
from config import settings
import pandas
from telegram import (
InlineKeyboardButton,
InlineKeyboardMarkup,
Update)
from telegram.ext import (
Application,
CallbackQueryHandler,
CommandHandler,
ContextTypes,
ConversationHandler,
)
Just add following: ๐
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
logger = logging.getLogger(__name__)
# States
START_STATE, END_STATE = range(2)
# Callback data
PLUS, MINUS = range(2)
# File path
FILE_PATH = 'file/report.xlsx'
It is the main entry point, of our Python Telegram Bot. ๐ค
def main():
"""Run the bot."""
application = Application.builder().token(settings.TELEGRAM_BOT_TOKEN).build()
conv_handler = ConversationHandler(
entry_points=[
CommandHandler("start", start),
CommandHandler('report', report)],
states={
START_STATE: [
CallbackQueryHandler(plus, pattern="^" + str(PLUS) + "$"),
CallbackQueryHandler(minus, pattern="^" + str(MINUS) + "$"),
],
END_STATE: [
CallbackQueryHandler(end),
],
},
fallbacks=[CommandHandler("start", start)],
)
application.add_handler(conv_handler)
application.run_polling()
Do not forget to add Bot Token. ๐️
Go to config/settings.py and add:
TELEGRAM_BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
Call main function, to get started. ๐ฌ
if __name__ == "__main__":
main()
Handling /start command.
Whenever student sends /start to the bot.
Calls following function.
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Send message on `/start`."""
# Get user that sent /start and log his name
user = update.effective_user
logger.info("User %s started the conversation.", user.username)
keyboard = [
[
InlineKeyboardButton("+", callback_data=str(PLUS)),
InlineKeyboardButton("-", callback_data=str(MINUS)),
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Choose an option", reply_markup=reply_markup,)
return START_STATE
Next function will be handled, if student sends ➕.
It means that a student came to class.
async def plus(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show confirm button"""
user = update.effective_user
query = update.callback_query
await query.answer(text='Saved')
message = await query.edit_message_text(text=".")
await context.bot.delete_message(message.chat.id, message.message_id)
await post_person(user)
return END_STATE
Next function will be handled, if student sends➖.
It means that a student left the class. ๐♂️
It works only, if student already came to class. ๐ง๐
async def minus(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Show confirm button"""
user = update.effective_user
active_user_id = await get_last_id(user)
if active_user_id:
query = update.callback_query
await query.answer(text="Saved")
message = await query.edit_message_text(text=".")
await context.bot.delete_message(message.chat.id, message.message_id)
print('-'*50)
print('active_user_id:', active_user_id)
await put_person(user, active_user_id)
return END_STATE
else:
query = update.callback_query
await query.answer(text="+ then -")
Registers student as attended the class. ๐
@sync_to_async
def post_person(user):
models.Person(
tg_id=user.id,
tg_username=user.username,
tg_fullname=user.full_name,
arrived_at=get_time(),
).save()
Update student left time ⌛️
@sync_to_async
def put_person(user, user_id):
models.Person.objects.select_related().filter(pk=user_id, tg_id=user.id).update(left_at=get_time())
Following, function filters particular student’s last came time.
@sync_to_async
def get_last_id(user):
last_id = models.Person.objects.select_related() \
.filter(tg_id=user.id, left_at=None).values_list("pk", flat=True).last()
active_id = models.Person.objects.select_related() \
.filter(tg_id=user.id).values_list("pk", flat=True).last()
if last_id >= active_id:
return last_id
else:
return False
Next function collects all students data.
We will use it further to make a report. ๐งฎ
@sync_to_async
def get_data():
persons = models.Person.objects.all()
serializer = serializers.PersonSerializer(persons, many=True)
all_data = []
for i in range(0, len(serializer.data)):
data = [serializer.data[i]['tg_fullname'], serializer.data[i]['arrived_at'], serializer.data[i]['left_at']]
all_data.append(data)
return all_data
Let’s save data to excel file. ๐️
def set_data(info):
pandas.DataFrame(data=info, columns=['name', 'arrived', 'left']).to_excel(FILE_PATH)
return 1
Add function to get full report of attendance.
async def report(update: Update, context: ContextTypes.DEFAULT_TYPE):
active_data = await get_data()
if set_data(active_data):
await update.message.reply_document(
document=open(FILE_PATH, 'rb'),
filename='report.xlsx',
caption='Report'
)
time.sleep(2)
os.remove(FILE_PATH)
return END_STATE
Thanks for reading this article. ๐
Full code available on Github, give ⭐️ if you like it.
Consider following on Medium
Tweet me on Twitter
Learn Programming for Free
External links
- Get full source code on Github
- Python Telegram Bot
- Django Rest Framework
- Python
Do you have any questions or suggestions?
Message me:
Comments
Post a Comment