MensaarLecker Development Log (3) -- Telegram Bot Deployment and Integration

MensaarLecker Development Log (3) -- Telegram Bot Deployment and Integration

This blog post is trying to tell you:

  • My personal experience when developing a web crawler using Selenium
  • Explained with examples from my Repository: MensaarLecker
  • For further details, feel free to **Try the bot: @Mensaar_Bot

New Features

Previous post: MensaarLecker Development Log (2) – Web Developing and GitHub Workflow

HTW menu

After the website is published, we noticed that people now prefer to have lunch in HTW Campus Rotenbühl. Since their menu come from the same site, it is very easy to introduce new menu to our project.

New website layout

Before we used two pages to store today’s menu and the menu history. And we think in general, all menu are simple texts, so we can put all contents into the index page without any problem. We can switch the visibility using JavaScript:

switch.js
1
2
3
4
function show(id) {
document.querySelectorAll('.section').forEach(el => el.classList.remove('active'));
document.getElementById(id).classList.add('active');
}

And for the whole HTML code again we stuffed in the python script to for our daily Github workflow to run.

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<h1>Mensaar Menu - {today}</h1>
<div class="buttons">
<button class="button" onclick="show('today-uds')">📅 UDS Today</button>
<button class="button" onclick="show('today-htw')">📅 HTW Today</button>
<button class="button" onclick="show('full-uds')">📋 Full UDS</button>
<button class="button" onclick="show('full-htw')">📋 Full HTW</button>
</div>

<div id="today-uds" class="section active">
<h2>UDS – Today's Menu</h2>
{meal_cards(uds_data)}
</div>

<div id="today-htw" class="section">
<h2>HTW – Today's Menu</h2>
{meal_cards(htw_data)}
</div>

<div id="full-uds" class="section">
<h2>📋 Full UDS Menu</h2>
<table id="uds-table">
<thead><tr>
<th>Date</th>
<th>Counter</th>
<th>Meal</th>
<th>Component 1</th>
<th>Component 2</th>
<th>Component 3</th>
<th>Component 4</th>
<th>Component 5</th>
</tr></thead>
<tbody>{table_rows(uds_data)}</tbody>
</table>
</div>

<div id="full-htw" class="section">
<h2>📋 Full HTW Menu</h2>
<table id="htw-table">
<thead><tr>
<th>Date</th>
<th>Counter</th>
<th>Meal</th>
<th>Component 1</th>
<th>Component 2</th>
<th>Component 3</th>
<th>Component 4</th>
<th>Component 5</th>
</tr></thead>
<tbody>{table_rows(htw_data)}</tbody>
</table>
</div>

Telegram Bot – @Mensaar_Bot

On top of the webiste, we can reuse the scraping code to create a telegram bot. First, we need to create a bot and get its API key using botfather.

Onve you get the key, you bot is created and alive! However, it doesn’t do anything because we haven’t tell it what to do. So, we need to implement its functionalities. python-telegram-bot is a very good package that contains everything we need to control the bot.

RapidFuzz – Fuzzy Text detection for bot reply

Other than commands and buttons, we also want to make the bot to reply to text message for better interactions. When the bot is added to a group chat, users can interact to the bot by tagging the bot and sending messages. Any texts related to food, mensa and menu will be accpeted and perform the action.

This is done under fuzzy matching. Using fuzz.partial_ratio() we can compare the similarity of users’ messages with our keyword list.

Brief Walkthrough on Telegram Bot

We also have another study notes for all of the telegram bot projects in here

CommandHandler – /start

The basic way to call a bot is to send a command. Commands are defined by CommandHandler, which we can implement each commands’ functionalites respectively. To make our bot send message to users, we can use update.message.reply_text.

bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"""🦉 Hoot Hoot❗ Guess we have another mortal here❗
If you have NO soul --> /owl
If you have NO food & soul --> /menu
If you HAVE a soul --> @Mensaar_Bot And start talking""")

def main():
app = ApplicationBuilder().token(os.getenv("TOKEN")).build()
app.add_handler(CommandHandler("start", start))
app.run_polling()

InlineKeyboardMarkup – Buttons for actions

Sometimes, users may need further options to finish the command, here we used InlineKeyboardMarkup to create possible options. There is another option ReplyKeyboardMarkup that can create buttons by replacing your phone keyboard, but from user feedbacks we noticed that it may be annoying for group usage.

bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ApplicationBuilder, CommandHandler

async def panel(update: Update, context: ContextTypes.DEFAULT_TYPE):
# clean keyboard just in case
# await update.message.reply_text("Remove unwanted reply keyboard hoot hoot...", reply_markup=ReplyKeyboardRemove()) # zero-width space

keyboard = [
[InlineKeyboardButton("Button A", callback_data="Text A")],
[InlineKeyboardButton("Button B", callback_data="Text B")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
"This is a panel",
reply_markup=reply_markup
)
def main():
app = ApplicationBuilder().token(os.getenv("TOKEN")).build()
app.add_handler(CommandHandler("panel", panel))
app.run_polling()

CallbackQueryHandler – Handle your buttons and actions

When a button from InlineKeyboardMarkup is pressed, the data from callback_data attribute is then passed. We can capture it by using CallbackQueryHandler. Since we are using Query here, we need to use query.edit_message_text.

bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler

async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
user_input = query.data
print(f"📥 Callback query received: {base64.b64decode(user_input)}")

match user_input:
case "Text A": # 📜 All Menu
msg = "This is option A"
case "Text B": # 🍽️ UdS Menu
msg = "This is option B"
case _:
msg = "Error"

await query.edit_message_text(text=msg)

def main():
app = ApplicationBuilder().token(os.getenv("TOKEN")).build()
app.add_handler(CallbackQueryHandler(handle_callback)) # Now handling inline button clicks
app.run_polling()

MensaarLecker Development Log (3) -- Telegram Bot Deployment and Integration

https://greenmeeple.github.io/projects/mensaar-log3/

Author

Alex Li

Posted on

2025-05-07

Updated on

2025-05-14

Licensed under

Comments

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×