added settings menu
This commit is contained in:
parent
41d7dd066b
commit
b7bf0e7fa2
167
src/beebot.py
167
src/beebot.py
@ -11,8 +11,8 @@ from typing import Optional
|
|||||||
import requests
|
import requests
|
||||||
import telegram.constants
|
import telegram.constants
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from telegram import Update
|
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
|
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, CallbackQueryHandler
|
||||||
|
|
||||||
log_dir = os.getenv("BEEBOT_LOGS")
|
log_dir = os.getenv("BEEBOT_LOGS")
|
||||||
if not log_dir:
|
if not log_dir:
|
||||||
@ -35,6 +35,12 @@ logging.basicConfig(
|
|||||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def flag(code):
|
||||||
|
offset = 127462 - ord('A')
|
||||||
|
code = code.upper()
|
||||||
|
return chr(ord(code[0]) + offset) + chr(ord(code[1]) + offset)
|
||||||
|
|
||||||
class BeeBot:
|
class BeeBot:
|
||||||
MENU_URL = "https://www.epfl.ch/campus/restaurants-shops-hotels/fr/industrie21-epfl-valais-wallis/?date={date}"
|
MENU_URL = "https://www.epfl.ch/campus/restaurants-shops-hotels/fr/industrie21-epfl-valais-wallis/?date={date}"
|
||||||
RESTO_ID = 545
|
RESTO_ID = 545
|
||||||
@ -43,6 +49,34 @@ class BeeBot:
|
|||||||
DB_PATH = os.path.join(BASE_DIR, "database.db")
|
DB_PATH = os.path.join(BASE_DIR, "database.db")
|
||||||
TEMPLATE_PATH = os.path.join(BASE_DIR, "menu.typ")
|
TEMPLATE_PATH = os.path.join(BASE_DIR, "menu.typ")
|
||||||
IMG_DIR = "/tmp/menu_images"
|
IMG_DIR = "/tmp/menu_images"
|
||||||
|
LANGUAGES = {
|
||||||
|
"en": {
|
||||||
|
"emoji": flag("gb"),
|
||||||
|
"name": "English"
|
||||||
|
},
|
||||||
|
"fr": {
|
||||||
|
"emoji": flag("fr"),
|
||||||
|
"name": "Français"
|
||||||
|
},
|
||||||
|
"de": {
|
||||||
|
"emoji": flag("de"),
|
||||||
|
"name": "Deutsch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CATEGORIES = {
|
||||||
|
"E": {
|
||||||
|
"name": "Étudiant"
|
||||||
|
},
|
||||||
|
"D": {
|
||||||
|
"name": "Doctorant"
|
||||||
|
},
|
||||||
|
"C": {
|
||||||
|
"name": "Campus"
|
||||||
|
},
|
||||||
|
"V": {
|
||||||
|
"name": "Visiteur"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, token: str):
|
def __init__(self, token: str):
|
||||||
self.tg_token: str = token
|
self.tg_token: str = token
|
||||||
@ -63,6 +97,8 @@ class BeeBot:
|
|||||||
app = ApplicationBuilder().token(self.tg_token).build()
|
app = ApplicationBuilder().token(self.tg_token).build()
|
||||||
app.add_handler(CommandHandler("week", self.cmd_week))
|
app.add_handler(CommandHandler("week", self.cmd_week))
|
||||||
app.add_handler(CommandHandler("today", self.cmd_today))
|
app.add_handler(CommandHandler("today", self.cmd_today))
|
||||||
|
app.add_handler(CommandHandler("settings", self.cmd_settings))
|
||||||
|
app.add_handler(CallbackQueryHandler(self.cb_handler))
|
||||||
|
|
||||||
logger.info("Starting bot")
|
logger.info("Starting bot")
|
||||||
app.run_polling()
|
app.run_polling()
|
||||||
@ -75,6 +111,74 @@ class BeeBot:
|
|||||||
logger.debug("Received /today")
|
logger.debug("Received /today")
|
||||||
await self.request_menu(update, context, True)
|
await self.request_menu(update, context, True)
|
||||||
|
|
||||||
|
async def cmd_settings(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
logger.debug("Received /settings")
|
||||||
|
menu = self.get_settings_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_chat.send_message(text="Your preferences", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def cb_handler(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
query = update.callback_query
|
||||||
|
if query.data == "change_language":
|
||||||
|
await self.cb_change_language(update, context)
|
||||||
|
elif query.data.startswith("set_language"):
|
||||||
|
await self.cb_set_language(update, context)
|
||||||
|
elif query.data == "change_categories":
|
||||||
|
await self.cb_change_categories(update, context)
|
||||||
|
elif query.data.startswith("toggle_category"):
|
||||||
|
await self.cb_toggle_category(update, context)
|
||||||
|
elif query.data == "back_to_settings":
|
||||||
|
menu = self.get_settings_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_message.edit_text(text="Your preferences", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def cb_change_language(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
logger.debug("Clicked 'Change language'")
|
||||||
|
menu = self.get_language_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_message.edit_text(text="Choose a language", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def cb_set_language(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
lang = update.callback_query.data.split(":")[1]
|
||||||
|
logger.debug(f"Clicked 'Set language to {lang}'")
|
||||||
|
cur = self.db_con.cursor()
|
||||||
|
cur.execute(
|
||||||
|
"UPDATE user SET lang=? WHERE telegram_id=?",
|
||||||
|
(lang, update.effective_user.id)
|
||||||
|
)
|
||||||
|
self.db_con.commit()
|
||||||
|
cur.close()
|
||||||
|
menu = self.get_language_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_message.edit_text(text="Choose a language", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def cb_change_categories(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
logger.debug("Clicked 'Change categories'")
|
||||||
|
menu = self.get_categories_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_message.edit_text(text="Choose the price categories to display", reply_markup=reply_markup)
|
||||||
|
|
||||||
|
async def cb_toggle_category(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
category = update.callback_query.data.split(":")[1]
|
||||||
|
logger.debug(f"Clicked 'Toggle category {category}'")
|
||||||
|
categories: set[str] = self.get_user_pref(update)["categories"]
|
||||||
|
|
||||||
|
if category in categories:
|
||||||
|
categories.remove(category)
|
||||||
|
else:
|
||||||
|
categories.add(category)
|
||||||
|
|
||||||
|
cur = self.db_con.cursor()
|
||||||
|
cur.execute(
|
||||||
|
"UPDATE user SET categories=? WHERE telegram_id=?",
|
||||||
|
(",".join(categories), update.effective_user.id)
|
||||||
|
)
|
||||||
|
self.db_con.commit()
|
||||||
|
cur.close()
|
||||||
|
menu = self.get_categories_menu(update)
|
||||||
|
reply_markup = InlineKeyboardMarkup(menu)
|
||||||
|
await update.effective_message.edit_text(text="Choose the price categories to display", reply_markup=reply_markup)
|
||||||
|
|
||||||
async def request_menu(self, update: Update, context: ContextTypes.DEFAULT_TYPE, today_only: bool) -> None:
|
async def request_menu(self, update: Update, context: ContextTypes.DEFAULT_TYPE, today_only: bool) -> None:
|
||||||
chat_id = update.effective_chat.id
|
chat_id = update.effective_chat.id
|
||||||
await context.bot.send_chat_action(chat_id=chat_id, action=telegram.constants.ChatAction.TYPING)
|
await context.bot.send_chat_action(chat_id=chat_id, action=telegram.constants.ChatAction.TYPING)
|
||||||
@ -113,6 +217,7 @@ class BeeBot:
|
|||||||
"id" INTEGER,
|
"id" INTEGER,
|
||||||
"telegram_id" INTEGER NOT NULL UNIQUE,
|
"telegram_id" INTEGER NOT NULL UNIQUE,
|
||||||
"categories" TEXT,
|
"categories" TEXT,
|
||||||
|
"lang" TEXT NOT NULL DEFAULT 'fr',
|
||||||
"created_at" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"created_at" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
PRIMARY KEY("id" AUTOINCREMENT)
|
||||||
);
|
);
|
||||||
@ -123,27 +228,29 @@ class BeeBot:
|
|||||||
def get_user_pref(self, update: Update) -> dict:
|
def get_user_pref(self, update: Update) -> dict:
|
||||||
user_id = update.effective_user.id
|
user_id = update.effective_user.id
|
||||||
cur = self.db_con.cursor()
|
cur = self.db_con.cursor()
|
||||||
res = cur.execute("SELECT categories FROM user WHERE telegram_id=?", (user_id,))
|
res = cur.execute("SELECT categories, lang FROM user WHERE telegram_id=?", (user_id,))
|
||||||
user = res.fetchone()
|
user = res.fetchone()
|
||||||
cur.close()
|
cur.close()
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
self.create_user(user_id)
|
self.create_user(user_id)
|
||||||
return {
|
return {
|
||||||
"categories": {"E", "D", "C", "V"}
|
"categories": {"E", "D", "C", "V"},
|
||||||
|
"lang": "fr"
|
||||||
}
|
}
|
||||||
|
|
||||||
categories = set(user[0].split(","))
|
categories = set(user[0].split(","))
|
||||||
return {
|
return {
|
||||||
"categories": categories
|
"categories": categories,
|
||||||
|
"lang": user[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_user(self, telegram_id: int) -> None:
|
def create_user(self, telegram_id: int) -> None:
|
||||||
logger.debug(f"New user with id {telegram_id}")
|
logger.debug(f"New user with id {telegram_id}")
|
||||||
cur = self.db_con.cursor()
|
cur = self.db_con.cursor()
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"INSERT INTO user (telegram_id, categories) VALUES (?, ?)",
|
"INSERT INTO user (telegram_id, categories, lang) VALUES (?, ?, ?)",
|
||||||
(telegram_id, "E,D,C,V")
|
(telegram_id, "E,D,C,V", "fr")
|
||||||
)
|
)
|
||||||
self.db_con.commit()
|
self.db_con.commit()
|
||||||
cur.close()
|
cur.close()
|
||||||
@ -270,6 +377,52 @@ class BeeBot:
|
|||||||
|
|
||||||
return menus
|
return menus
|
||||||
|
|
||||||
|
def get_settings_menu(self, update: Update) -> list[list[InlineKeyboardButton]]:
|
||||||
|
prefs = self.get_user_pref(update)
|
||||||
|
menu = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(f"Language: {prefs['lang']}", callback_data="change_language")
|
||||||
|
],
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(f"Categories: {' / '.join(prefs['categories'])}", callback_data="change_categories")
|
||||||
|
]
|
||||||
|
]
|
||||||
|
return menu
|
||||||
|
|
||||||
|
def get_language_menu(self, update: Update) -> list[list[InlineKeyboardButton]]:
|
||||||
|
prefs = self.get_user_pref(update)
|
||||||
|
buttons = []
|
||||||
|
for lang, data in self.LANGUAGES.items():
|
||||||
|
extra = " ✅" if prefs["lang"] == lang else ""
|
||||||
|
buttons.append(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
data["emoji"] + extra,
|
||||||
|
callback_data=f"set_language:{lang}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
menu = [buttons[i:i+2] for i in range(0, len(buttons), 2)]
|
||||||
|
menu.append([
|
||||||
|
InlineKeyboardButton("Back to settings", callback_data="back_to_settings")
|
||||||
|
])
|
||||||
|
return menu
|
||||||
|
|
||||||
|
def get_categories_menu(self, update: Update) -> list[list[InlineKeyboardButton]]:
|
||||||
|
prefs = self.get_user_pref(update)
|
||||||
|
buttons = []
|
||||||
|
for categ, data in self.CATEGORIES.items():
|
||||||
|
extra = " ✅" if categ in prefs["categories"] else " ❌"
|
||||||
|
buttons.append(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
data["name"] + extra,
|
||||||
|
callback_data=f"toggle_category:{categ}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
menu = [buttons[i:i+2] for i in range(0, len(buttons), 2)]
|
||||||
|
menu.append([
|
||||||
|
InlineKeyboardButton("Back to settings", callback_data="back_to_settings")
|
||||||
|
])
|
||||||
|
return menu
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logger.info("Welcome to BeeBot !")
|
logger.info("Welcome to BeeBot !")
|
||||||
bot = BeeBot(os.getenv("TELEGRAM_TOKEN"))
|
bot = BeeBot(os.getenv("TELEGRAM_TOKEN"))
|
||||||
|
Loading…
Reference in New Issue
Block a user