256 lines
10 KiB
Python
256 lines
10 KiB
Python
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):
|
|
self.entry_string = entry_string
|
|
|
|
set_db(PERSISTENCE_DIR / "lists.sqlite")
|
|
|
|
self.list_overview_keyboard = [
|
|
[InlineKeyboardButton("Print list", callback_data="print")],
|
|
[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("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_print, pattern="^print$"),
|
|
CallbackQueryHandler(self.list_add, pattern="^add$"),
|
|
CallbackQueryHandler(self.list_toggle, pattern="^toggle$"),
|
|
CallbackQueryHandler(self.list_menu, pattern="^overview$"),
|
|
CallbackQueryHandler(self.list_remove, pattern="^remove$"),
|
|
CallbackQueryHandler(self.list_clear, pattern="^clear$"),
|
|
CallbackQueryHandler(self.list_delete, pattern="^delete$"),
|
|
],
|
|
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"]
|
|
readable_it = printable_list(list_object)
|
|
|
|
if readable_it:
|
|
msg_content = "Which item would you like to toggle?"
|
|
keyboard = [[InlineKeyboardButton(v, callback_data=k)] for k,v in zip(list_object.content.keys(), readable_it)]
|
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
else:
|
|
msg_content = "List empty"
|
|
reply_markup = None
|
|
|
|
await query.edit_message_text(msg_content, 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"]
|
|
|
|
readable_it = printable_list(list_object)
|
|
|
|
if readable_it:
|
|
msg_content = "\n".join(readable_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
|
|
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] or False
|
|
# if it was previously unset (None), we can later on set it to not old = True
|
|
|
|
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
|
|
|
|
|
|
|
|
def printable_list(list_object: ListModel):
|
|
content_it = list_object.content.values()
|
|
done_bool_it = list_object.done_dict.values()
|
|
# distinguish the enumeration:
|
|
# either all done_dict values are None -> the list is not toggleable
|
|
# or at least one value is of type bool -> the list is toggleable and None === False
|
|
if any([type(e) == bool for e in done_bool_it]):
|
|
done_it = [
|
|
"✅" if e else "❌" \
|
|
for e in list_object.done_dict.values()
|
|
]
|
|
else:
|
|
done_it = ["-" for e in done_bool_it]
|
|
|
|
readable_it = [f"{d} {c}" for d, c in zip(done_it, content_it)]
|
|
return readable_it
|