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)
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
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)
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()
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)