def build_demand_description(user: User) -> str: message = _('{} will take the food.\n').format( user.info[UserInfoField.NAME.value]) is_provided_contact_info = False if user.info.get(UserInfoField.PHONE.value): message += _('Phone: {}\n').format( user.info[UserInfoField.PHONE.value]) is_provided_contact_info = True if (user.info.get(UserInfoField.USERNAME.value) and user.info.get(UserInfoField.DISPLAY_USERNAME.value)): message += _('Telegram: @{}\n').format( user.info[UserInfoField.USERNAME.value]) is_provided_contact_info = True if not is_provided_contact_info: message += _('No contact info was provided.\n') social_status_verbose = translate_social_status_string( user.info.get(UserInfoField.SOCIAL_STATUS.value)) if social_status_verbose is not None: message += (_('Social status: %s') % social_status_verbose) return message
class PostingState(State): intro = Reply(buttons=[[{ 'text': _('Set time and send'), 'data': 'set-time', }, { 'text': _('Cancel'), 'data': 'cancel', }]]) def get_intro(self): reply = super().get_intro() reply.text = _('Food you can share:\n{}').format( build_active_food_message(self.db_user)) return reply def handle(self, text: str, data: str, *args, **kwargs): if data == 'set-time': return Reply(next_state=SupplyState.SET_TIME) if data == 'cancel': cancel_supply_message(self.db_user, provider=self.provider) return Reply( text=_('Product list is cleared.'), next_state=SupplyState.READY_TO_POST, ) if text: extend_supply_message(self.db_user, text)
def validate_phone_number(text): if len(text) > 100: raise ValidationError(_('Please, provide only pone number.')) number_of_digits = len(re.findall(r'\d', text)) if number_of_digits < 7: raise ValidationError(_('This is not a valid phone number.'))
def handle(self, text: str, data: str, *args, **kwargs): if data == 'cancel': cancel_supply_message(self.db_user, provider=self.provider) return Reply( text=_('Product list is cleared.'), next_state=SupplyState.READY_TO_POST, ) if text: if not self.db_user.info.get( UserInfoField.IS_APPROVED_SUPPLY.value): logger.warning( 'There is an attempt to post a message by user %s', self.db_user.user_id) cancel_supply_message(self.db_user, provider=self.provider) return Reply(next_state=SupplyState.READY_TO_POST) message_id = self.db_user.editing_message_id set_message_time(message_id, text) publish_supply_event(self.db_user) set_message_publication_time(message_id) return Reply( text=_( "Information is sent. " "I'll notify you when there is someone to take this food." ), next_state=SupplyState.READY_TO_POST, )
def show_non_demanded_message(user, message_id: str): message = _('Not yet booked.\n\n%s') % build_short_message_text_by_id( message_id=message_id) return Reply(text=message, buttons=[[{ 'text': _('View all messages'), 'data': f'c|{SupplyCommand.LIST_MESSAGES}', }]])
def build_supply_user_description(user: User): msg = _("Restaurant name: {name}\n" "Address: {address}").format( name=user.info[UserInfoField.NAME.value], address=user.info[UserInfoField.ADDRESS.value]) if user.info.get(UserInfoField.PHONE.value): msg += _('\nPhone: %s') % user.info[UserInfoField.PHONE.value] return msg
def get_intro(self) -> Reply: return Reply(text=_('What to tell the foodsaver?'), buttons=[[{ 'text': _('Back to the message'), 'data': f'c|{SupplyCommand.SHOW_DEMANDED_MESSAGE}|' f'{self.db_user.context["booking_to_cancel"]}' }]])
def _handle_info(user: User, provider_str: str, supply_user_id: str, message_id: str): supply_user = get_user(supply_user_id, provider=Provider(provider_str), workflow=Workflow.SUPPLY) message_record = get_supply_message_record_by_id(message_id=message_id) if supply_user is None or message_record is None: return Reply(_('Information was not found.')) info = build_demand_side_full_message_text(supply_user, message_record) if message_record.demand_user_id is not None: return build_food_taken_message(user, message_record.demand_user_id, info) set_next_command( user, Command( name=DemandCommandName.INFO.value, arguments=[provider_str, supply_user_id, message_id], )) coordinates = supply_user.approved_coordinates() buttons = [] if coordinates is not None: buttons.append([{ 'text': _('🌍 Map'), 'data': DemandCommandName.MAP_INFO.build(provider_str, supply_user_id, message_id), }]) take_it_button = { 'text': _('Take it'), 'data': f'{DemandCommandName.TAKE.value}|' f'{supply_user.provider.value}|' f'{supply_user.user_id}|' f'{message_id}', } back_button = { 'text': _('Back'), 'data': DemandCommandName.SHORT_INFO.build(provider_str, supply_user_id, message_id), } buttons.append([back_button, take_it_button]) return Reply(text=info, buttons=buttons)
def build_new_supplier_notification(supply_user: User) -> Reply: return Reply( text=build_new_supplier_notification_text(supply_user), buttons=[[{ 'text': _('Approve'), 'data': f'c|{SupplyCommand.APPROVE_SUPPLIER}|{supply_user.id}', }, { 'text': _('Decline'), 'data': f'c|{SupplyCommand.DECLINE_SUPPLIER}|{supply_user.id}', }]] )
def view_messages(user): messages = list_messages(user) buttons = [ _get_demanded_message_button(x) if x.demand_user_id else _get_non_demanded_message_button(x) for x in messages ] buttons.append([{ 'text': _('Go to product posting'), 'data': f'c|{SupplyCommand.BACK_TO_POSTING}' }]) return Reply(text=_('Last messages'), buttons=buttons)
def notify_supplier_is_approved(user: User): queue_messages( tg_chat_id=user.chat_id, workflow=Workflow.SUPPLY, replies=[ Reply(text=_('Your account is approved!'), buttons=[[{ 'data': f'c|{SupplyCommand.BACK_TO_POSTING}', 'text': _('OK ✅'), }]]) ], )
def get_intro(self) -> Reply: buttons = [[{ 'text': _('❌ Dismiss') }, { 'text': _('Send phone'), 'request_contact': True }]] return Reply( text=_('Please, send your contact number.'), buttons=buttons, is_text_buttons=True, )
def _build_approve_intro(self): return Reply( coordinates=self.db_user.info[UserInfoField.COORDINATES.value], buttons=[[{ 'text': _('No! Edit! 🌍'), 'data': 'change-coordinates', }], [{ 'text': _('Yes! Approve ✅'), 'data': 'approve-coordinates', }], [{ 'text': _('Cancel'), 'data': 'cancel', }]])
def _get_action_buttons(self, message_id: str): return [{ 'text': _('Back'), 'data': DemandCommandName.INFO.build(self.supply_user.provider.value, self.supply_user.user_id, message_id) }, { 'text': _('Take it'), 'data': DemandCommandName.TAKE.build(self.supply_user.provider.value, self.supply_user.user_id, message_id) }]
def _get_intro_text(self): if self.db_user.info.get(UserInfoField.IS_APPROVED_SUPPLY.value): return _('Enter food you can share and click "send"') if self.db_user.info.get( UserInfoField.IS_APPROVED_SUPPLY.value) is False: return (_( 'Your account was declined. Please, contact %s for any clarifications.' ) % FEEDBACK_TG_BOT) notify_admin_about_new_supply_user_if_necessary(self.db_user) return (_( "We'll notify you when your account is approved. Also, you can contact us with %s" ) % FEEDBACK_TG_BOT)
def _build_set_intro(self): reply = Reply( text=_('Please, send me your coordinates. (Attach -> Location)')) if self.info_field_is_set(): reply.buttons = [[{ 'text': _('Cancel'), 'data': 'cancel', }]] else: reply.buttons = [[{ 'text': _('Provide later'), 'data': 'later', }]] return reply
def build_demanded_message_text(*, demand_user: User, supply_user: User, message_id: str) -> str: demand_description = build_demand_description(demand_user) food_description = build_short_message_text_by_id(message_id=message_id) return _("{}\n\nYour message was:\n\n{}").format(demand_description, food_description)
def build_new_supplier_notification_text(supply_user: User): return (_( '{user_name} wants to join as a supplier. Provided description is:\n\n{description}' ).format( user_name=_introduce_new_user(supply_user), description=build_supply_user_description(supply_user), ))
def message_to_text(message: Message) -> str: text_message = '\n'.join([x for x in message.products if x]) if message.take_time: text_message += _('\nTime: {}').format(message.take_time) return text_message
def get_intro(self) -> Reply: buttons = [[{ 'text': _('← Back') }, { 'text': _('Send phone'), 'request_contact': True }]] if self.info_field_is_set(): buttons[0].insert(1, {'text': _('❌ Delete')}) return Reply( text=_('Please, send your contact number.'), buttons=buttons, is_text_buttons=True, )
def _handle_booked(user: User, supply_provider: str, supply_user_id: str, message_id: str): supply_user = get_supply_user(user_id=supply_user_id, provider=Provider(supply_provider)) return build_demand_side_message_by_id(supply_user, message_id, intro=_("You've booked this"))
def get_intro(self) -> Reply: buttons = [[{ 'text': _('← Back') }, { 'text': _('Send phone'), 'request_contact': True }]] if self._info_field.value in self.db_user.info: buttons[0].insert(1, {'text': _('❌ Delete')}) return Reply( text=_('Send your phone number'), buttons=buttons, is_text_buttons=True, )
def build_supplier_declined_text(user: User): return _('{user_name} was DECLINED as a supplier. ' 'Provided description was:\n\n{description}\n\n' 'DB id: {id}').format( user_name=_introduce_new_user(user), description=build_supply_user_description(user), id=user.id, )
def handle(self, text: str, data=None, coordinates=None): notify_demand_for_cancel( supply_user=self.db_user, message_id=self.db_user.context['booking_to_cancel'], message=text) cancel_booking(supply_user=self.db_user, message_id=self.db_user.context['booking_to_cancel']) return Reply(text=_('Cancelled'), next_state=SupplyState.READY_TO_POST)
def build(self, message_id: str): coordinates = self.supply_user.approved_coordinates() if coordinates is None: logger.error('Map is requested while coordinates where not set.') return Reply(text=_('Coordinates where not provided.')) buttons = [[{ 'text': _('Open in app'), 'url': f'https://dzmitry.by/redirect?to=geo:{coordinates[0]},{coordinates[1]}?z=21', }]] buttons.append(self._get_action_buttons(message_id)) return Reply(coordinates=coordinates, buttons=buttons)
def _get_non_demanded_message_button(message: Message): return [{ 'text': _('%s (not booked)') % db_time_to_user(message.dt_published, '%d-%m %H:%M'), 'data': f'c|{SupplyCommand.SHOW_NON_DEMANDED_MESSAGE}|{message.message_id}' }]
def get_intro(self) -> Reply: reply = Reply(text=self._message) if self.info_field_is_set(): reply.buttons = [[{ 'text': _('Cancel'), 'data': 'cancel', }]] return reply
def handle_text(self, text): text = text or '' if text.startswith('❌'): unset_info(self.db_user, self._info_to_edit) return Reply(text=_('OK ✅'), next_state=self.get_next_state()) if text.startswith('←'): return Reply(text=_('OK ✅'), next_state=self.get_next_state()) try: validate_phone_number(text) except ValidationError as e: return Reply(text=e.message) reply = super().handle_text(text) reply.text = _( 'OK ✅' ) # Text response is required to clear telegram text keyboard. return reply
def build_demand_side_short_message(supply_user: User, message_id: str): text_message = build_short_message_text_by_id(message_id=message_id) return Reply( text=_('{} can share the following:\n{}').format( supply_user.info[UserInfoField.NAME.value], text_message), buttons=[[{ 'text': _('Take it'), 'data': DemandCommandName.TAKE.build(supply_user.provider.value, supply_user.user_id, message_id), }, { 'text': _('Info'), 'data': DemandCommandName.INFO.build(supply_user.provider.value, supply_user.user_id, message_id) }]], )
def notify_supplier_is_declined(user: User): queue_messages( tg_chat_id=user.chat_id, workflow=Workflow.SUPPLY, replies=[ Reply(text=(_( 'Your account was declined. Please, contact %s for any clarifications.' ) % FEEDBACK_TG_BOT)) ], )