def _add_product_keyboard_response_callback(self, update: Update, context: CallbackContext, product: Product, data: dict): """ Called when the user has selected a product to add to a shopping list :param update: the chat update object :param context: telegram context :param product: the selected product :param data: callback data """ bot = context.bot chat_id = update.effective_chat.id message_id = update.effective_message.message_id amount = data["amount"] shopping_list_id = data["shopping_list_id"] self._grocy.add_product_to_shopping_list(product.id, shopping_list_id, amount) text = "Added {}x {}".format(amount, product.name) send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message_id, menu=ReplyKeyboardRemove(selective=True))
def _inventory_add_execute(self, update: Update, context: CallbackContext, product: Product, amount: int, exp: datetime, price: float): """ Adds a product to the inventory :param update: the chat update object :param context: telegram context :param product: product entity :param amount: amount :param exp: expiration date :param price: price """ bot = context.bot chat_id = update.effective_chat.id message_id = update.effective_message.message_id self._grocy.add_product(product_id=product.id, amount=amount, price=price, best_before_date=exp) text = "Added {}x {} (Exp: {}, Price: {})".format( amount, product.name, "Never" if exp == NEVER_EXPIRES_DATE else exp, price) send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message_id, menu=ReplyKeyboardRemove(selective=True))
def _shopping_lists_callback(self, update: Update, context: CallbackContext, id: int, add_missing: bool or None) -> None: """ Show a list of all shopping lists :param update: the chat update object :param context: telegram context :param id: shopping list id :param add_missing: whether to add missing products to the shopping list before displaying """ bot = context.bot chat_id = update.effective_chat.id if add_missing: self._grocy.add_missing_product_to_shopping_list( shopping_list_id=id) # TODO: when supported, pass shopping list id here shopping_list_items = self._grocy.shopping_list(True) shopping_list_items = sorted(shopping_list_items, key=lambda x: x.product.name.lower()) item_texts = list( list(map(shopping_list_item_to_str, shopping_list_items))) text = "\n".join([ "*=> Shopping List <=*", *item_texts, ]).strip() send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN)
def notify(self, message: str): """ Send notification to all enabled chats :param message: the message to send """ for chat_id in self._chat_ids: send_message(self._updater.bot, chat_id, message)
def _chat_id_callback(self, update: Update, context: CallbackContext) -> None: """ Print the current chat id, to make it easier to add it to notifications :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id send_message(bot, chat_id, f"{chat_id}", parse_mode=ParseMode.MARKDOWN)
def help_command_callback(self, update: Update, context: CallbackContext): bot = context.bot message = update.effective_message chat_id = update.effective_chat.id from telegram_click import generate_command_list text = generate_command_list(update, context) send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message.message_id)
def await_user_selection(self, update: Update, context: CallbackContext, selection: str or None, choices: List[Any], key: callable, callback: callable, callback_data: dict): """ Sends a ReplyKeyboard to the user and waits for a valid selection. :param update: Update :param context: CallbackContext :param selection: the current user selection (if any) :param choices: list of choices to select from :param key: function to create unique string key for a choice :param callback: the function to call, when a selection was made :param callback_data: data to pass to the callback function """ bot = context.bot chat_id = update.effective_chat.id message_id = update.effective_message.message_id user_id = update.effective_user.id fuzzy_matches = fuzzy_match(selection, choices=choices, key=key, limit=5) # check if something matches perfectly perfect_matches = list(filter(lambda x: x[1] == 100, fuzzy_matches)) if len(perfect_matches) == 1: choice = perfect_matches[0][0] callback(update, context, choice, callback_data) return # send reply keyboard with fuzzy matches to user keyboard_texts = list( map(lambda x: "{}".format(key(x[0])), fuzzy_matches)) keyboard = self.build_reply_keyboard(keyboard_texts) text = "No unique perfect match found, please select one of the menu options" self.await_response(user_id=user_id, options=keyboard_texts, callback=self._on_user_selection, callback_data={ "choices": choices, "key": key, "callback": callback, "callback_data": callback_data, }) send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message_id, menu=keyboard)
def _version_command_callback(self, update: Update, context: CallbackContext) -> None: """ /stats command handler :param update: the chat update object :param context: telegram context """ bot = context.bot message = update.effective_message chat_id = update.effective_chat.id from grocy_telegram_bot import __version__ text = "{}".format(__version__) send_message(bot, chat_id, text, reply_to=message.message_id)
def _stats_callback(self, update: Update, context: CallbackContext) -> None: """ /stats command handler :param update: the chat update object :param context: telegram context """ bot = context.bot message = update.effective_message chat_id = update.effective_chat.id text = format_metrics() send_message(bot, chat_id, text, reply_to=message.message_id)
def _shopping_callback(self, update: Update, context: CallbackContext) -> None: """ Show a list of all shopping lists :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id shopping_list_items = self._grocy.shopping_list(True) # TODO: sort by parent product / product category # TODO: let the user specify the order of product categories, according to the grocery store of his choice shopping_list_items = sorted(shopping_list_items, key=lambda x: x.product.name.lower()) # generate message text = "*=> Shopping List <=*" # generate keyboard initial_button_tuples = self._create_shopping_list_item_button_tuples(shopping_list_items) inline_keyboard_items = self._create_shopping_list_keyboard_items(initial_button_tuples) inline_keyboard_markup = self._inline_keyboard_handler.build_inline_keyboard(inline_keyboard_items) # send message result = send_message(bot, chat_id, text, menu=inline_keyboard_markup, parse_mode=ParseMode.MARKDOWN) # register callback for button presses self._inline_keyboard_handler.register_listener( chat_id=chat_id, message_id=result.message_id, command_id=ShoppingListItemButtonCallbackData.command_id, callback=self._shopping_button_pressed_callback, callback_data={ "shopping_list_items": shopping_list_items, "initial_keyboard_items": initial_button_tuples } )
def cancel_keyboard_callback(self, update: Update, context: CallbackContext): bot = context.bot chat_id = update.effective_chat.id user_id = update.effective_user.id message_id = update.effective_message.message_id text = "Cancelled" send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message_id, menu=ReplyKeyboardRemove(selective=True)) if user_id in self.awaiting_response: self.awaiting_response.pop(user_id)
def _config_command_callback(self, update: Update, context: CallbackContext): """ /config command handler :param update: the chat update object :param context: telegram context """ from container_app_conf.formatter.toml import TomlFormatter bot = context.bot chat_id = update.effective_message.chat_id message_id = update.effective_message.message_id text = self._config.print(formatter=TomlFormatter()) text = "```\n{}\n```".format(text) send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN, reply_to=message_id)
def _start_callback(self, update: Update, context: CallbackContext) -> None: """ Welcomes a new user with a greeting message :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id user_first_name = update.effective_user.first_name if not CONFIG_ADMINS.evaluate(update, context): send_message( bot, chat_id, "Sorry, you do not have permissions to use this bot.") return send_message( bot, chat_id, f"Welcome {user_first_name},\nthis is your grocy-telegram-bot instance, ready to go!" )
def _chores_callback(self, update: Update, context: CallbackContext, all: bool) -> None: """ Show a list of all chores :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id chores = self._grocy.chores(True) chores = sorted( chores, key=lambda x: datetime.now().astimezone() if x.next_estimated_execution_time is None else x.next_estimated_execution_time ) overdue_chores = filter_overdue_chores(chores) other = [item for item in chores if item not in overdue_chores] overdue_item_texts = list(map(chore_to_str, overdue_chores)) other_item_texts = list(map(chore_to_str, other)) lines = ["*=> Chores <=*"] if all and len(other_item_texts) > 0: lines.extend([ "", *other_item_texts ]) if len(overdue_item_texts) > 0: lines.extend([ "", "*Overdue:*", *overdue_item_texts ]) text = "\n".join(lines).strip() send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN)
def _inventory_callback(self, update: Update, context: CallbackContext, missing: bool) -> None: """ Show a list of all products in the inventory :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id products = self._grocy.get_all_products() if missing: products = list(filter(lambda x: x.amount == 0, products)) products = sorted(products, key=lambda x: x.name.lower()) item_texts = list(list(map(product_to_str, products))) text = "\n".join([ "*=> Inventory <=*", *item_texts, ]).strip() send_message(bot, chat_id, text, parse_mode=ParseMode.MARKDOWN)