def order_checkout(self, command_body, arguments: list, action):
        user = self.get_user_by_slack_user_id(command_body['user_id'])
        reminder = self.food_order_service.checkout_order(
            user, datetime.today(), command_body['channel_name'])
        self.cancel_reminder(command_body, reminder)

        if not reminder:
            self.logger.warning(
                "User {} can't check out order for this channel".format(
                    user.username))
            self.slack_client.api_call(
                "chat.postEphemeral",
                channel=command_body['channel_name'],
                user=user.slack_user_id,
                text=
                "You can't check out order for this channel. Are you order owner?"
            )
            return

        order_items: [FoodOrderItem
                      ] = self.food_order_service.get_food_order_items_by_date(
                          user, datetime.today(), command_body['channel_name'])
        order_items_text = ''
        total_order_cost = Decimal(0.0)
        if order_items:
            for order_item in order_items:
                eating_user = self.user_service.get_user_by_id(
                    order_item.eating_user_id)
                order_items_text += get_user_name(
                    eating_user) + " - " + order_item.description + " (" + str(
                        order_item.cost) + " PLN)\n"
                total_order_cost += order_item.cost
            order_items_text += "\nTotal cost: " + str(
                total_order_cost) + " PLN"

        if total_order_cost == Decimal(0):
            self.slack_client.api_call(
                "chat.postMessage",
                channel=command_body['channel_name'],
                mrkdwn=True,
                attachments=[
                    Attachment(
                        attachment_type="default",
                        text=str(
                            "*{} checked out order. No orders for today.*".
                            format(get_user_name(user))),
                        color="#3AA3E3").dump()
                ])
        else:
            self.slack_client.api_call(
                "chat.postMessage",
                channel=command_body['channel_name'],
                mrkdwn=True,
                attachments=[
                    Attachment(attachment_type="default",
                               text=str("*{} checked out order:*\n" +
                                        order_items_text).format(
                                            get_user_name(user)),
                               color="#3AA3E3").dump()
                ])
    def show_debt(self, command_body, arguments: list, action):
        requesting_user = self.user_service.get_user_by_slack_id(
            command_body['user_id'])
        debts = list(
            filter(lambda d: d.debt != 0,
                   self.food_order_service.get_debt(requesting_user)))

        if not debts:
            self.slack_client.api_call(
                "chat.postEphemeral",
                channel=command_body['channel_name'],
                user=requesting_user.slack_user_id,
                mrkdwn=True,
                as_user=True,
                attachments=[
                    Attachment(
                        attachment_type="default",
                        text="You have no debts. Good for you :raised_hands:",
                        color="#3AA3E3").dump()
                ])
        else:
            resp = self.slack_client.api_call(
                "chat.postEphemeral",
                channel=command_body['channel_name'],
                user=requesting_user.slack_user_id,
                mrkdwn=True,
                as_user=True,
                attachments=self.prepare_debts_list(debts))
            if not resp["ok"]:
                self.logger.error(resp)
                raise RuntimeError('failed')
            return None
    def prepare_debts_list(self, debts):
        attachments = []
        for debt in debts:
            user = self.user_service.get_user_by_id(debt.user_id)
            phone_text = "\nPay with BLIK using phone number: *" + user.phone + "*" if user.phone else ''

            actions = []
            debt_text = "You owe " + str(
                debt.debt) + " PLN for " + get_user_name(user) + phone_text
            if debt.debt < 0:
                actions.append(
                    Action(name="debt-pay",
                           text="I just paid " + str(-debt.debt) + " PLN",
                           type="button",
                           value="pay-" + str(debt.user_id)))
                debt_text = "You owe " + str(
                    -debt.debt) + " PLN for " + get_user_name(
                        user) + phone_text
            elif debt.debt > 0:
                debt_text = get_user_name(user) + " owes you " + str(
                    debt.debt) + " PLN"

            attachments.append(
                Attachment(callback_id=string_helper.get_full_class_name(
                    FoodOrderPayload),
                           attachment_type="default",
                           text=debt_text,
                           color="#3AA3E3",
                           actions=actions).dump())
        return attachments
    def create_select_period_for_listing_model(self, command_body,
                                               inner_user_id):
        actions = [
            Action(name=inner_user_id
                   if inner_user_id is not None else command_body['user_id'],
                   text="Select time range...",
                   type=ActionType.SELECT.value,
                   options=[
                       TextSelectOption(text=tr.value, value=tr.value)
                       for tr in TimeRanges
                   ])
        ]
        attachments = [
            Attachment(text="Show record for",
                       fallback="Select time range to list saved time records",
                       color="#3AA3E3",
                       attachment_type="default",
                       callback_id=string_helper.get_full_class_name(
                           ListCommandPayload),
                       actions=actions)
        ]

        return Message(text="I'm going to list saved time records...",
                       response_type="ephemeral",
                       mrkdwn=True,
                       attachments=attachments)
    def order_start(self, command_body, arguments: list, action):
        if len(arguments) == 0:
            return "Use format: */ni food _URL_*"

        ordering_link = arguments[0]
        user = self.get_user_by_slack_user_id(command_body['user_id'])

        self.food_order_service.remove_incomplete_food_order_items(
            datetime.today(), command_body['channel_name'])

        post_at: int = round(
            (datetime.now() + CHECKOUT_REMINDER_IN).timestamp())
        reminder_response = self.slack_client.api_call(
            "chat.scheduleMessage",
            channel=command_body['channel_name'],
            user=user.slack_user_id,
            post_at=post_at,
            text=
            "@{} Looks like you forgot to order from {}.\nUrge your friends to place an order and call */ni order*"
            .format(command_body['user_name'], ordering_link),
            link_names=True)
        if not reminder_response["ok"]:
            self.logger.error(reminder_response)
            raise RuntimeError('failed')

        scheduled_message_id = reminder_response['scheduled_message_id']
        order = self.food_order_service.create_food_order(
            user, datetime.today(), ordering_link, scheduled_message_id,
            command_body['channel_name'])
        self.logger.info("Created order %d for user %s with reminder %s",
                         order.food_order_id, command_body['user_name'],
                         scheduled_message_id)

        order_prompt_response = self.slack_client.api_call(
            "chat.postMessage",
            channel=command_body['channel_name'],
            mrkdwn=True,
            as_user=True,
            attachments=[
                Attachment(
                    callback_id=string_helper.get_full_class_name(
                        FoodOrderPayload),
                    attachment_type="default",
                    text=get_user_name(user) + " orders from " + ordering_link,
                    color="#3AA3E3",
                    actions=[
                        Action(name="order-prompt",
                               text="I'm ordering right now",
                               type="button",
                               value="order-" + str(order.food_order_id)),
                        Action(name="order-prompt",
                               text="Not today",
                               type="button",
                               value="pas-" + str(order.food_order_id)),
                    ]).dump()
            ])
        if not order_prompt_response["ok"]:
            self.logger.error(order_prompt_response)
            raise RuntimeError('failed')
        return None
    def create_project_selected_message(last_time_entries,
                                        selected_project) -> Message:

        actions = [
            Action(name="time_entries_list",
                   text="Select time entry",
                   type=ActionType.SELECT.value,
                   options=[
                       TextSelectOption(
                           text=string_helper.make_option_time_string(te),
                           value=te.time_entry_id) for te in last_time_entries
                   ],
                   confirm=Confirmation(title="Delete confirmation",
                                        text="Click 'Remove' to confirm",
                                        ok_text="Remove",
                                        dismiss_text="Cancel")),
        ]
        attachments = [
            Attachment(text="Select time entry to remove",
                       fallback="Select time entry",
                       color="#3AA3E3",
                       attachment_type="default",
                       callback_id=string_helper.get_full_class_name(
                           DeleteTimeEntryPayload),
                       actions=actions)
        ]
        return Message(text="There are the last time entries for *" +
                       selected_project.name + "*:",
                       response_type="ephemeral",
                       mrkdwn=True,
                       attachments=attachments)
Esempio n. 7
0
    def handle(self, payload: Payload):
        if 'debt-pay' in payload.actions and payload.actions[
                'debt-pay'].value.startswith('pay-'):
            settled_user = self.user_service.get_user_by_id(
                payload.actions['debt-pay'].value.replace("pay-", ""))
            user = self.user_service.get_user_by_slack_id(payload.user.id)

            self.food_order_service.pay_debts(user, settled_user)

            self.slack_client.api_call(
                "chat.postEphemeral",
                channel=payload.channel.name,
                user=user.slack_user_id,
                mrkdwn=True,
                as_user=True,
                attachments=[
                    Attachment(
                        attachment_type="default",
                        text=
                        "Lannisters always pay their debts. Glad that you too {} :tada:"
                        .format(get_user_name(settled_user)),
                        color="#3AA3E3").dump()
                ])

            self.slack_client.api_call(
                "chat.postEphemeral",
                channel=payload.channel.name,
                mrkdwn=True,
                as_user=True,
                user=settled_user.slack_user_id,
                attachments=[
                    Attachment(
                        attachment_type="default",
                        text="{} paid for you for food :heavy_dollar_sign:".
                        format(get_user_name(user)),
                        color="#3AA3E3").dump()
                ])
        else:
            raise RuntimeError("Unsupported action for food order prompt")
        return False
Esempio n. 8
0
    def reminder_show(self, command_body, arguments, action):

        command_name = command_body["command"]
        user = self.get_user_by_slack_user_id(command_body['user_id'])

        day_configuration = self.reminder_service.get_user_reminder_config(
            user)

        attachments = [
            Attachment(text=day_time,
                       color="#D72B3F" if "OFF" in day_time else "#3AA3E3",
                       attachment_type="default",
                       mrkdwn_in=["text"]) for day_time in day_configuration
        ]
        attachments.append(
            Attachment(text="",
                       footer=self.config['MESSAGE_REMINDER_SET_TIP'].format(
                           command_name),
                       mrkdwn_in=["text", "footer"]))
        return Message(text="Your reminder time is as follow:",
                       response_type="ephemeral",
                       mrkdwn=True,
                       attachments=attachments).dump()
 def create_select_user_message(action_name, subaction_name,
                                user_options_list):
     actions = [
         Action(name=action_name + ":" + subaction_name,
                text="Select user...",
                type=ActionType.SELECT.value,
                options=user_options_list),
     ]
     attachments = [
         Attachment(text="Select user for " + action_name + "...",
                    fallback="Select user",
                    color="#3AA3E3",
                    attachment_type="default",
                    callback_id=string_helper.get_full_class_name(
                        ProjectAddPayload),
                    actions=actions)
     ]
     return Message(text="",
                    response_type="ephemeral",
                    attachments=attachments)
Esempio n. 10
0
 def show_debtors(self, command_body, arguments: list, action):
     debtors_str = "It's time for a dinner.\n"
     debtors = self.food_order_service.top_debtors()
     if debtors:
         debtors_str += "Top debtors are:\n"
     for debtor in debtors:
         debtors_str += "{} has total debt {} PLN\n".format(
             get_user_name(self.user_service.get_user_by_id(debtor[0])),
             debtor[1])
     for channel_name in self.food_order_service.get_all_food_channels(
     ) or []:
         self.slack_client.api_call("chat.postMessage",
                                    channel=channel_name,
                                    mrkdwn=True,
                                    attachments=[
                                        Attachment(
                                            attachment_type="default",
                                            text=str(debtors_str),
                                            color="#ec4444").dump()
                                    ])
 def create_select_project_message(user_default_project_id,
                                   project_options_list):
     actions = [
         Action(name="projects_list",
                text="Select project...",
                type=ActionType.SELECT.value,
                value=user_default_project_id,
                options=project_options_list),
     ]
     attachments = [
         Attachment(text="Select project first",
                    fallback="Select project",
                    color="#3AA3E3",
                    attachment_type="default",
                    callback_id=string_helper.get_full_class_name(
                        DeleteTimeEntryPayload),
                    actions=actions)
     ]
     return Message(text="I'm going to remove time entry :wastebasket:...",
                    response_type="ephemeral",
                    attachments=attachments)
 def create_select_project_message(action_name, user_default_project_id,
                                   project_options_list):
     actions = [
         Action(name=str(action_name),
                text="Select project...",
                type=ActionType.SELECT.value,
                value=user_default_project_id,
                options=project_options_list),
     ]
     attachments = [
         Attachment(text="Select project first",
                    fallback="Select project",
                    color="#3AA3E3",
                    attachment_type="default",
                    callback_id=string_helper.get_full_class_name(
                        ProjectAddPayload),
                    actions=actions)
     ]
     return Message(text="I'm going to (un)assign user to the project...",
                    response_type="ephemeral",
                    attachments=attachments)
    def select_vacation_to_remove(self, command_body, argument, action):

        user = self.get_user_by_slack_user_id(command_body['user_id'])

        user_vacations: List[
            Vacation] = self.vacation_service.get_ten_newest_user_vacations(
                user.user_id)

        if not user_vacations:
            return Message(text="There is nothing to delete... ",
                           response_type="ephemeral",
                           mrkdwn=True).dump()

        actions = [
            Action(
                name="vacation_list",
                text="Select vacation to delete",
                type=ActionType.SELECT.value,
                options=[
                    TextSelectOption(
                        text=string_helper.make_option_vacations_string(vac),
                        value=vac.vacation_id) for vac in user_vacations
                ]),
        ]
        attachments = [
            Attachment(text="Select vacation to delete",
                       fallback="Select vacation to delete",
                       color="#3AA3E3",
                       attachment_type="default",
                       callback_id=string_helper.get_full_class_name(
                           RequestFreeDaysPayload),
                       actions=actions)
        ]
        return Message(
            text="I'm going to remove some vacations :wastebasket:...",
            response_type="ephemeral",
            mrkdwn=True,
            attachments=attachments).dump()
    def report_pre_dialog(self, command_body, arguments, action):

        message_text = "I'm going to generate report..."
        inner_user_id = None

        if len(arguments):
            user = arguments[0]
            inner_user_id = self.extract_slack_user_id(user)

            self.get_user_by_slack_user_id(inner_user_id)

        actions = [
            Action(name=inner_user_id
                   if inner_user_id is not None else command_body['user_id'],
                   text="Select time range...",
                   type=ActionType.SELECT.value,
                   options=[
                       TextSelectOption(text=tr.value, value=tr.value)
                       for tr in TimeRanges
                   ])
        ]

        attachments = [
            Attachment(text="Generate report for",
                       fallback="Select time range to report",
                       color="#3AA3E3",
                       attachment_type="default",
                       callback_id=string_helper.get_full_class_name(
                           ReportGenerateFormPayload),
                       actions=actions)
        ]

        return Message(text=message_text,
                       response_type="ephemeral",
                       mrkdwn=True,
                       attachments=attachments).dump()
 def create_vacation_selected_message(vacation_id):
     actions = [
         Action(name="remove",
                text="Remove",
                style="danger",
                type=ActionType.BUTTON.value,
                value=str(vacation_id)),
         Action(name="cancel",
                text="Cancel",
                type=ActionType.BUTTON.value,
                value="remove")
     ]
     attachments = [
         Attachment(text="Click 'Remove' to confirm:",
                    color="#3AA3E3",
                    attachment_type="default",
                    callback_id=string_helper.get_full_class_name(
                        RequestFreeDaysPayload),
                    actions=actions)
     ]
     return Message(text="Vacation will be removed...",
                    response_type="ephemeral",
                    mrkdwn=True,
                    attachments=attachments)
    def save_submitted_time_task(self, time_record: TimeRecordDto):

        user = self.get_user_by_slack_user_id(time_record.user_id)

        im_channel = self.slack_client.api_call("im.open",
                                                user=time_record.user_id)

        if not im_channel["ok"]:
            self.logger.error("Can't open im channel for: " +
                              str(time_record.user_id) + '. ' +
                              im_channel["error"])
            return

        # todo cache projects globally e.g. Flask-Cache
        projects = self.project_service.get_projects()
        selected_project = list_find(
            lambda p: str(p.project_id) == time_record.project, projects)

        if selected_project is None:
            self.logger.error("Project doesn't exist: " + time_record.project)
            return

        if list_find(lambda p: str(p.project_id) == time_record.project,
                     user.user_projects) is None:
            self.project_service.assign_user_to_project(
                project=selected_project, user=user)

            # check if submitted hours doesn't exceed the limit
        submitted_time_entries = self.user_service.get_user_time_entries(
            user.user_id, time_record.get_parsed_date(),
            time_record.get_parsed_date())
        duration_float: float = get_float_duration(time_record.hours,
                                                   time_record.minutes)
        if sum([te.duration for te in submitted_time_entries
                ]) + Decimal(duration_float) > DAILY_HOUR_LIMIT:
            self.slack_client.api_call(
                "chat.postMessage",
                channel=im_channel['channel']['id'],
                text="Sorry, but You can't submit more than " +
                str(DAILY_HOUR_LIMIT) + " hours for one day.",
                as_user=True)
            return

        self.project_service.report_user_time(selected_project, user,
                                              duration_float,
                                              time_record.comment,
                                              time_record.get_parsed_date())

        attachments = [
            Attachment(
                title='Submitted ' + string_helper.format_duration_decimal(
                    Decimal(duration_float)) + ' hour(s) for ' +
                ('Today' if time_record.day == date.today().isoformat() else
                 time_record.day) + ' in ' + selected_project.name + " :clap:",
                text="_" + time_record.comment + "_",
                mrkdwn_in=["text", "footer"],
                footer=self.config['MESSAGE_SUBMIT_TIME_TIP']).dump()
        ]

        resp = self.slack_client.api_call("chat.postMessage",
                                          channel=im_channel['channel']['id'],
                                          attachments=attachments,
                                          as_user=True)

        if not resp["ok"]:
            self.logger.error("Can't post message: " + resp.get("error"))
    def create_help_command_message(self, command_body, arguments, action):
        command_name = command_body["command"]

        attachments = [
            Attachment(
                text="*{0}* _(without any arguments)_: Submit working time".
                format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(
                text="*{0} list*: See reported time".format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(
                text="*{0} delete*: Remove reported time".format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(text="*{0} reminder*: See reminder settings".format(
                command_name),
                       attachment_type="default",
                       mrkdwn_in=["text"]),
            Attachment(
                text="*{0} report*: Generate report file".format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(
                text='*{0} vacation*: Submit free time within range'.format(
                    command_name),
                attachment_type="default",
                mrkdwn_in=['text']),
            Attachment(
                text='*{0} vacation delete*: Remove free time entry'.format(
                    command_name),
                attachment_type="default",
                mrkdwn_in=['text']),
            Attachment(
                text=
                "*{0} reminder set [_mon:HH:MM,tue:HH:MM..._]*: Configure reminder time for particular day, or several days at once"
                .format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(
                text="*{0} food [_URL_]*: Start food ordering for this channel"
                .format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(
                text=
                "*{0} order*: Checkout order and display orders for today for this channel"
                .format(command_name),
                attachment_type="default",
                mrkdwn_in=["text"]),
            Attachment(text="*{0} pay*: Show your bills for food".format(
                command_name),
                       attachment_type="default",
                       mrkdwn_in=["text"])
        ]

        user: User = self.get_user_by_slack_user_id(command_body['user_id'])

        if user.role.role == 'admin':
            attachments.append(
                Attachment(text="*{0} project*: Create new project".format(
                    command_name),
                           attachment_type="default",
                           mrkdwn_in=["text"]))
            attachments.append(
                Attachment(
                    text="*{0} project assign*: Assign user for project".
                    format(command_name),
                    attachment_type="default",
                    mrkdwn_in=["text"]))
            attachments.append(
                Attachment(
                    text="*{0} project unassign*: Unassign user from project".
                    format(command_name),
                    attachment_type="default",
                    mrkdwn_in=["text"]))

        return Message(
            text=
            "*Nisse* is used for reporting working time. Following commands are available:",
            mrkdwn=True,
            response_type="default",
            attachments=attachments).dump()
Esempio n. 18
0
    def get_user_time_entries(self, user: User, inner_user: User, time_range):
        user_db_id = user.slack_user_id
        inner_user_db_id = inner_user.slack_user_id
        current_user_name = "You" if inner_user_db_id == user_db_id else "{0} {1}".format(
            inner_user.first_name, inner_user.last_name).strip()

        if inner_user_db_id != user_db_id and user.role.role != 'admin':
            message = "Sorry, but only admin user can see other users records :face_with_monocle:"
            return Message(text=message,
                           response_type="ephemeral",
                           mrkdwn=True)

        start_end = get_start_end_date(time_range)

        time_records = self.user_service.get_user_time_entries(
            inner_user.user_id, start_end[0], start_end[1])
        time_records = sorted(time_records,
                              key=lambda te: te.report_date,
                              reverse=True)

        if len(time_records) == 0:
            message = "*{0}* have not reported anything for `{1}`".format(
                current_user_name, time_range)
            return Message(text=message,
                           response_type="ephemeral",
                           mrkdwn=True)

        projects = {}
        duration_total = 0
        for time in time_records:
            duration_total += time.duration
            if projects.get(time.project.name):
                projects[time.project.name].text += "\n" + \
                    string_helper.make_time_string(time)
            else:
                projects[time.project.name] = Attachment(
                    title=time.project.name,
                    text=string_helper.make_time_string(time),
                    color="#3AA3E3",
                    attachment_type="default",
                    mrkdwn_in=["text"])

        total_duration = string_helper.format_duration_decimal(duration_total)
        total_message = "*{0}* reported *{1}* for `{2}`".format(
            current_user_name, total_duration, time_range)

        projects['total'] = Attachment(title="Total",
                                       text=total_message,
                                       color="#D72B3F",
                                       attachment_type="default",
                                       mrkdwn_in=["text"])

        if inner_user_db_id == user_db_id:
            projects['footer'] = Attachment(
                text="",
                footer=self.config['MESSAGE_LIST_TIME_TIP'],
                mrkdwn_in=["footer"])

        message = "These are hours submitted by *{0}* for `{1}`".format(
            current_user_name, time_range)
        attachments = list(projects.values())

        return Message(text=message,
                       mrkdwn=True,
                       response_type="ephemeral",
                       attachments=attachments)