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))
예제 #2
0
    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)
예제 #4
0
 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)
예제 #5
0
 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)
예제 #6
0
    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)
예제 #7
0
    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)
예제 #8
0
    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)
예제 #9
0
    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)
예제 #10
0
    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
            }
        )
예제 #11
0
    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)
예제 #12
0
    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)
예제 #13
0
    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!"
        )
예제 #14
0
    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)
예제 #15
0
    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)