Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@
BREAKFAST,
DINNER,
SETTINGS,
HIDE_CUISINE
HIDE_CUISINE,
ADD_FAVORITE,
REMOVE_FAVORITE
)
from commands.meal import handle_menu
from commands.general import (
handle_start,
handle_help,
handle_error
)
from commands.settings import handle_settings, handle_hidden_cuisine, handle_hide_cuisine
from commands.settings import (handle_settings, handle_hidden_cuisine, handle_hide_cuisine,
handle_add_favorite, handle_remove_favorite, handle_remove_favorite_callback)
from database.database import connect

# Enable logging
Expand All @@ -41,12 +44,15 @@ def main():
dispatcher.add_handler(CommandHandler(DINNER, handle_menu(meal=DINNER)))
dispatcher.add_handler(CommandHandler(SETTINGS, handle_settings))
dispatcher.add_handler(CommandHandler(HIDE_CUISINE, handle_hidden_cuisine))
dispatcher.add_handler(CommandHandler(ADD_FAVORITE, handle_add_favorite))
dispatcher.add_handler(CommandHandler(REMOVE_FAVORITE, handle_remove_favorite))

# add callback_query handler
dispatcher.add_handler(CallbackQueryHandler(handle_start, pattern='^start.+'))
dispatcher.add_handler(CallbackQueryHandler(handle_settings, pattern='^settings.home'))
dispatcher.add_handler(CallbackQueryHandler(handle_hidden_cuisine, pattern='^settings.hidden'))
dispatcher.add_handler(CallbackQueryHandler(handle_hide_cuisine, pattern='^menu.+'))
dispatcher.add_handler(CallbackQueryHandler(handle_remove_favorite_callback, pattern='^favorites.+'))

# log all errors
dispatcher.add_error_handler(handle_error)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

def handle_start(update, context):
context.bot.send_message(chat_id=update.effective_chat.id, text=welcome_msg(
update.message.chat.first_name))
update.effective_chat.first_name))


def handle_help(update, context):
Expand Down
2 changes: 1 addition & 1 deletion src/commands/meal.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def get_breakfast_or_dinner_menu(update, context):
if menu is None: # if no menu, reply with no menu message
context.bot.send_message(chat_id=update.effective_chat.id, text=no_menu_msg(meal))
else: # else reply user of the menu
menu = menu_msg(date.today(), meal, parse_menu(menu))
menu = menu_msg(date.today(), meal, parse_menu(menu, hidden_cuisines))
# send formatted menu to client
update.message.reply_text(menu, parse_mode='HTML')

Expand Down
50 changes: 47 additions & 3 deletions src/commands/settings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from util.messages import settings_msg
from util.kb_mark_up import settings_kb, hidden_cuisine_kb
from util.kb_mark_up import settings_kb, hidden_cuisine_kb, favorites_kb
from util.util import parse_callback
from database.database import get_hidden_cuisines, update_hidden_cuisine
from util.messages import no_hidden_cuisine_msg, hidden_cuisine_msg
from database.database import get_hidden_cuisines, update_hidden_cuisine, get_favorite_foods, update_favorite_foods
from util.messages import (no_hidden_cuisine_msg, hidden_cuisine_msg, add_favorite_no_input_msg,
add_favorite_already_exists_msg, added_favorites_msg, no_favorites_msg, favorites_msg)
from util.formatting import normalize
import logging


def handle_settings(update, context):
Expand All @@ -19,3 +22,44 @@ def handle_hide_cuisine(update, context):
cuisine_to_hide = parse_callback(update.callback_query.data)[1]
updated_hidden_cuisine = update_hidden_cuisine(update.effective_chat.id, cuisine_to_hide)
update.callback_query.edit_message_reply_markup(reply_markup=hidden_cuisine_kb(updated_hidden_cuisine))


def handle_add_favorite(update, context):
food_to_add = ' '.join(context.args)
if food_to_add == '':
update.message.reply_text(add_favorite_no_input_msg())
return

food_to_add = normalize(food_to_add)
favorites = update_favorite_foods(update.effective_chat.id, food_to_add)

if favorites is None:
update.message.reply_text(add_favorite_already_exists_msg())
return

update.message.reply_text(added_favorites_msg(favorites))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reduce verbosity with one-liner
update.message.reply_text(added_favorites_msg(favorites) if favorites is None else add_favorite_already_exists_msg())



def handle_remove_favorite(update, context):
favorites = get_favorite_foods(update.effective_chat.id)
if len(favorites) == 0:
update.message.reply_text(no_favorites_msg())
return

update.message.reply_text('Select your favorite food to remove:', reply_markup=favorites_kb(favorites))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abstract away the reply text to const.py or message.py?



def handle_remove_favorite_callback(update, context):
food_to_remove = parse_callback(update.callback_query.data)[1]
favorites = update_favorite_foods(update.effective_chat.id, food_to_remove, False)

if favorites is None:
update.callback_query.edit_message_text('You already removed that!')
return

if len(favorites) == 0:
context.bot.edit_message_text(text=no_favorites_msg(), chat_id=update.effective_chat.id,
message_id=update.callback_query.message.message_id, reply_markup=favorites_kb(favorites))
else:
context.bot.edit_message_text(text=favorites_msg(favorites), chat_id=update.effective_chat.id,
message_id=update.callback_query.message.message_id, reply_markup=favorites_kb(favorites))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above the one-liner?

62 changes: 54 additions & 8 deletions src/database/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import psycopg2.extras

from database.queries import menu_query, settings_query, settings_insert, settings_update
from util.const import HIDE_CUISINE
from util.const import HIDE_CUISINE, FAVORITES

# global connection
_connection = None
Expand Down Expand Up @@ -49,7 +49,17 @@ def get_menu(meal, date):
conn = connect()
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cursor.execute(menu_query(meal), (date,))
return cursor.fetchone()
menu = cursor.fetchone()
cursor.close()
return menu


def insert_default_user_pref(chat_id):
conn = connect()
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cursor.execute(settings_insert(), (chat_id, '{}', '{}'))
conn.commit()
cursor.close()


def get_hidden_cuisines(chat_id):
Expand All @@ -60,11 +70,13 @@ def get_hidden_cuisines(chat_id):

if data is None:
# insert default settings
cursor.execute(settings_insert(), (chat_id, '{}', '{}'))
conn.commit()
return [] # return empty hidden food array
insert_default_user_pref(chat_id)
hidden = []
else:
hidden = data[HIDE_CUISINE]

return data[HIDE_CUISINE] # returns hidden cuisines in user_pref
cursor.close()
return hidden # returns hidden cuisines in user_pref


def update_hidden_cuisine(chat_id, cuisine_to_hide):
Expand All @@ -80,7 +92,41 @@ def update_hidden_cuisine(chat_id, cuisine_to_hide):
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cursor.execute(settings_update(HIDE_CUISINE), (hidden_cuisines, chat_id))
conn.commit()
cursor.execute(settings_query(HIDE_CUISINE), (chat_id,))
cursor.close()

return hidden_cuisines


def get_favorite_foods(chat_id):
conn = connect()
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cursor.execute(settings_query(FAVORITES), (chat_id,))
data = cursor.fetchone()

return data[HIDE_CUISINE]
if data is None:
insert_default_user_pref(chat_id)
favorites = []
else:
favorites = data[FAVORITES]

cursor.close()
return favorites # returns favorites in user_pref
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we practice DRY by taking in the type of user_pref as a function parameter and extract data accordingly?
These few lines are almost exactly the same as line 65-79



def update_favorite_foods(chat_id, favorite_food, is_add=True):
favorites = get_favorite_foods(chat_id)

if favorite_food in favorites and is_add or favorite_food not in favorites and not is_add:
return None
elif favorite_food in favorites and not is_add:
favorites.remove(favorite_food)
else:
favorites.append(favorite_food)

conn = connect()
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
cursor.execute(settings_update(FAVORITES), (favorites, chat_id))
conn.commit()
cursor.close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, can we DRY the updating process?


return favorites
11 changes: 6 additions & 5 deletions src/util/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@
DINNER = 'dinner'
MENU = 'menu'
SETTINGS = 'settings'
FAVORITE = 'favorite'
FAVORITES = 'favorites'
NOTIFICATION = 'notification'
HOME = 'home'
HIDE_CUISINE = 'hidden'
ADD_FAVORITE = 'add_favorite'
REMOVE_FAVORITE = 'remove_favorite'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be Singapore readable 'favourite' for the users or left Americanized?

# menu messages
BREAKFAST_COMMAND = f'/{BREAKFAST}'
BREAKFAST_DESC = f'{BREAKFAST_COMMAND} - view today\'s breakfast menu\n'
DINNER_COMMAND = f'DINNER'
DINNER_COMMAND = f'/{DINNER}'
DINNER_DESC = f'{DINNER_COMMAND} - view today\'s dinner menu\n'
# setting messages
SETTINGS_COMMAND = f'/{SETTINGS}'
SETTINGS_DESC = f'{SETTINGS_COMMAND} - customize menu visibility and display settings\n'
ADD_FAVORITE_COMMAND = '/add_favorite'
ADD_FAVORITE_COMMAND = f'/{ADD_FAVORITE}'
ADD_FAVORITE_DESC = f'{ADD_FAVORITE_COMMAND} <food> - add favorite food for notifications\n'
REMOVE_FAVORITE_COMMAND = '/remove_favorite'
REMOVE_FAVORITE_COMMAND = f'/{REMOVE_FAVORITE}'
REMOVE_FAVORITE_DESC = f'{REMOVE_FAVORITE_COMMAND} - remove favorite food from notifications\n'
NO_FAVORITES_MSG = f'You have no favorite foods! Use {ADD_FAVORITE_COMMAND} <food> to add one!'
HELP_COMMAND = '/help'
HELP_DESC = f'{HELP_COMMAND} - show the help message\n'
SET_BREAKFAST_NOTIFICATION_COMMAND = '/set_breakfast_time'
Expand Down
14 changes: 12 additions & 2 deletions src/util/kb_mark_up.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from telegram import InlineKeyboardButton, InlineKeyboardMarkup


def back_to_start_btn():
return InlineKeyboardButton("Back to start", callback_data="start.home")


def settings_kb():
button_list = [
InlineKeyboardButton("Toggle Menu Visibility", callback_data="settings.hidden"),
InlineKeyboardButton("View Favourite Foods", callback_data="settings.favorite"),
InlineKeyboardButton("View Favourite Foods", callback_data="settings.favorites"),
InlineKeyboardButton("View Notification Settings", callback_data="settings.notification"),
InlineKeyboardButton("Back to start", callback_data="start.home")
back_to_start_btn()
]
return InlineKeyboardMarkup(build_menu(button_list, n_cols=1))

Expand All @@ -25,6 +29,12 @@ def hidden_cuisine_kb(hidden_cuisine):
return InlineKeyboardMarkup(build_menu(kb_markup, n_cols=2, footer_buttons=[setting_button]))


def favorites_kb(favorites):
kb_markup = list(map(lambda food:
InlineKeyboardButton(food, callback_data=f'favorites.{food}'), favorites))
return InlineKeyboardMarkup(build_menu(kb_markup, 1, footer_buttons=[back_to_start_btn()]))


def build_menu(buttons,
n_cols,
header_buttons=None,
Expand Down
16 changes: 14 additions & 2 deletions src/util/messages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from util.const import COMMAND_LIST
from util.const import COMMAND_LIST, ADD_FAVORITE_DESC, ADD_FAVORITE_COMMAND


# general messages
Expand Down Expand Up @@ -38,12 +38,24 @@ def hidden_cuisine_msg(name):


def favorites_msg(favorites):
return f'These are your current favorites:\n{favorites}'
return f"These are your current favorites:\n{', '.join(favorites)}"


def add_favorite_no_input_msg():
return f'You did not indicate a favorite food!\n{ADD_FAVORITE_DESC}'


def add_favorite_already_exists_msg():
return 'You already favorited this food!'


def added_favorites_msg(favorites):
return f'You have updated your favorites. {favorites_msg(favorites)}'


def no_favorites_msg():
return f'You have no favorite foods! Use {ADD_FAVORITE_COMMAND} <food> to add one!'


def menu_has_favorite_msg(favorite):
return f'Hey! This meal contains {favorite}'
4 changes: 2 additions & 2 deletions src/util/util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from util.formatting import bold, italicize
from util.formatting import bold, italicize, normalize


def parse_menu(data, hidden_cuisines):
menu = ''
for key in data.keys():
if key == 'date' or key in hidden_cuisines:
continue
menu += bold(key.capitalize()) + '\n'
menu += bold(normalize(key)) + '\n'
for item in data[key]:
if item == 'OR':
menu += italicize(item) + '\n'
Expand Down