import os from pathlib import Path from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters, CallbackQueryHandler, CallbackContext from telegram import InlineKeyboardButton, InlineKeyboardMarkup from .models import ListModel, set_db, db PERSISTENCE_DIR = Path(os.getenv("PERSISTENCE_DIR")) NAME, NEW, ACTION, ITEMADD, ITEMREMOVE, ITEMTOGGLE = range(6) from ..basehandler import BaseHandler class ListHandler(BaseHandler): """Create and edit lists""" def __init__(self, entry_string, models): del models # not needed here, but part of the template self.entry_string = entry_string set_db(PERSISTENCE_DIR / "lists.sqlite") self.list_overview_keyboard = [ [InlineKeyboardButton("Add item", callback_data="add")], [InlineKeyboardButton("Toggle item", callback_data="toggle")], [InlineKeyboardButton("Remove item", callback_data="remove")], [InlineKeyboardButton("Clear list", callback_data="clear")], [InlineKeyboardButton("Print list", callback_data="print")], [InlineKeyboardButton("Delete list", callback_data="delete")], ] self.handler = ConversationHandler( entry_points=[CommandHandler(entry_string, self.entry_point)], states={ NAME: [ CallbackQueryHandler(self.choose_list, pattern="^list-"), CallbackQueryHandler(self.new_list, pattern="^new$"), ], NEW : [MessageHandler(filters.TEXT, callback=self.new_listname)], ACTION: [ CallbackQueryHandler(self.list_add, pattern="^add$"), CallbackQueryHandler(self.list_toggle, pattern="^toggle$"), CallbackQueryHandler(self.list_remove, pattern="^remove$"), CallbackQueryHandler(self.list_clear, pattern="^clear$"), CallbackQueryHandler(self.list_delete, pattern="^delete$"), CallbackQueryHandler(self.list_print, pattern="^print$"), CallbackQueryHandler(self.list_menu, pattern="^overview$"), ], ITEMADD : [MessageHandler(filters.TEXT, callback=self.list_add_item)], ITEMTOGGLE: [CallbackQueryHandler(self.list_toggle_index)], ITEMREMOVE : [CallbackQueryHandler(self.list_remove_index)] }, fallbacks=[CommandHandler('list', self.entry_point)], ) async def entry_point(self, update, context) -> None: await super().entry_point(update, context) with db: lists = ListModel.select().where(ListModel.chat_id == update.effective_chat.id) keyboard = [[InlineKeyboardButton(k.name, callback_data=f"list-{k.id}")] for k in lists] + \ [[InlineKeyboardButton("New list", callback_data="new")]] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text(text="Here are the existing lists. You can also create a new one:", reply_markup=reply_markup) return NAME async def choose_list(self, update, context: CallbackContext) -> None: query = update.callback_query data = query.data id = data.replace("list-","") await query.answer() context.user_data["current_list"] = ListModel.get(id = id) reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup) return ACTION async def list_menu(self, update, context) -> None: query = update.callback_query await query.answer() reply_markup = InlineKeyboardMarkup(self.list_overview_keyboard) await query.edit_message_text(f"Using {context.user_data['current_list'].name}. Available actions:", reply_markup=reply_markup) return ACTION async def new_list(self, update, context) -> None: query = update.callback_query await query.answer() await query.edit_message_text("What's the name of the new list?") return NEW async def new_listname(self, update, context) -> None: name = update.message.text try: with db: context.user_data["current_list"] = ListModel.create(name = name, chat_id=update.effective_chat.id) keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("To the menu!", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text("Thanks. List " + name + " was successfully created.", reply_markup=reply_markup) return ACTION except Exception as e: await update.message.reply_text("Oh no! Encountered exception: {}".format(e)) return ConversationHandler.END async def list_add(self, update, context) -> None: query = update.callback_query await query.answer() await query.edit_message_text("What would you like to add?") return ITEMADD async def list_toggle(self, update, context) -> None: query = update.callback_query await query.answer() list_object = context.user_data["current_list"] keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("Which item would you like to toggle?", reply_markup = reply_markup) return ITEMTOGGLE async def list_remove(self, update, context) -> None: query = update.callback_query await query.answer() list_object = context.user_data["current_list"] keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in list_object.content.items()] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text("Which item would you like to remove?", reply_markup = reply_markup) return ITEMREMOVE async def list_clear(self, update, context) -> None: query = update.callback_query await query.answer() list_object = context.user_data["current_list"] list_object.content = {} keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"List {list_object.name} cleared", reply_markup=reply_markup) return ACTION async def list_delete(self, update, context) -> None: query = update.callback_query await query.answer() list_object = context.user_data["current_list"] list_object.delete_instance() await query.edit_message_text(f"List {list_object.name} deleted") return ConversationHandler.END async def list_print(self, update, context) -> None: query = update.callback_query await query.answer() list_object = context.user_data["current_list"] content_it = list_object.content.values() done_it = [ "· " if e is None \ else "✅ " if e \ else "❌ " \ for e in list_object.done_dict.values()] if content_it: msg_content = "\n".join([f"{d} {c}" for d, c in zip(done_it, content_it)]) else: msg_content = "List empty" keyboard = [[InlineKeyboardButton("Add an item", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"Content of {list_object.name}:\n{msg_content}", reply_markup=reply_markup) return ACTION async def list_add_item(self, update, context) -> None: item = update.message.text list_object = context.user_data["current_list"] new = list_object.content new.update({"random_key": item}) list_object.content = new # TODO test me! keyboard = [[InlineKeyboardButton("Add some more", callback_data="add"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text(f"Added {item}", reply_markup=reply_markup) return ACTION async def list_toggle_index(self, update, context) -> None: query = update.callback_query toggle_key = int(query.data) await query.answer() list_object = context.user_data["current_list"] old = list_object.done_dict[toggle_key] # if all None or all False (first toggle or all false) then set all dones to False if not any(list_object.done_dict.values()): new_done_dict = dict.fromkeys(list_object.done_dict, False) else: new_done_dict = list_object.done_dict new_done_dict[toggle_key] = not old list_object.done_dict = new_done_dict keyboard = [[InlineKeyboardButton("Toggle another", callback_data="toggle"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"Toggled {list_object.content[toggle_key]}", reply_markup=reply_markup) return ACTION async def list_remove_index(self, update, context) -> None: query = update.callback_query ind = int(query.data) await query.answer() list_object = context.user_data["current_list"] old = list_object.content name = old.pop(ind) list_object.content = old keyboard = [[InlineKeyboardButton("Remove another", callback_data="remove"), InlineKeyboardButton("Back to the menu", callback_data="overview")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.edit_message_text(f"Removed {name}", reply_markup=reply_markup) return ACTION