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 _handle_take(user: User, provider_str: str, supply_user_id: str, message_id: str): message_record = get_supply_message_record_by_id(message_id=message_id) supply_user = get_supply_user(supply_user_id, Provider(provider_str)) if message_record is None or supply_user 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: return build_food_taken_message(user, message_record.demand_user_id, info) set_next_command( user, Command( name=DemandCommandName.TAKE.value, arguments=[provider_str, supply_user_id, message_id], )) coordinates = supply_user.approved_coordinates() buttons = _get_review_buttons(user) if coordinates is not None: buttons.append([{ 'text': _('🌍 Map'), 'data': DemandCommandName.MAP_TAKE.build(provider_str, supply_user_id, message_id), }]) buttons.append([{ 'text': _('❌ Cancel'), 'data': f'{DemandCommandName.SHORT_INFO.value}|' f'{provider_str}|{supply_user_id}|{message_id}', }, { 'text': _('Confirm 🆗✅'), 'data': f'{DemandCommandName.FINISH_TAKE.value}|' f'{provider_str}|{supply_user_id}|{message_id}', }]) return Reply( text=_('%(info)s\n-----------\n%(ask_for_approve)s') % { 'info': info, 'ask_for_approve': _('Please, confirm/edit your contact information to proceed.'), }, buttons=buttons, )
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_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 handle(self, text: str, data: str, *args, **kwargs): if data == 'view-info': return Reply(next_state=SupplyState.VIEW_INFO) if not text: return if not self.db_user.info.get(UserInfoField.IS_APPROVED_SUPPLY.value): return create_supply_message(self.db_user, text, provider=self.provider) return Reply(next_state=SupplyState.POSTING)
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 _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 handle(self, text: str, data: Optional[str], *args, **kwargs): if data == 'edit-name': return Reply(next_state=SupplyState.EDIT_NAME) if data == 'edit-address': return Reply(next_state=SupplyState.EDIT_ADDRESS) if data == 'edit-phone': return Reply(next_state=SupplyState.EDIT_PHONE) if data == 'edit-coordinates': return Reply(next_state=SupplyState.EDIT_COORDINATES) if data == 'back': return Reply(next_state=SupplyState.READY_TO_POST)
def get_intro(self): return Reply( text=_('You can edit your contact info here'), buttons=[ [{ 'text': _('Name: %s') % self.db_user.info['name'], 'data': 'edit-name', }], [{ 'text': _('Address: %s') % self.db_user.info['address'], 'data': 'edit-address', }], [{ 'text': _('Coordinates: %s') % ('✅' if self.db_user.approved_coordinates() else '❌'), 'data': 'edit-coordinates', }], [{ 'text': _('Phone: %s') % (self.db_user.info['phone'] if 'phone' in self.db_user.info else '❌'), 'data': 'edit-phone', }, { 'text': _('Back'), 'data': 'back', }], ])
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 process(self, serialized_data: str): data = json.loads(serialized_data) try: send_messages(tg_chat_id=data['chat_id'], replies=[Reply(**data['reply'])], workflow=Workflow(data['workflow'])) except Exception as e: logger.exception('Message was not send:\n%s', data)
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 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 handle(self, text: str, data: Optional[str] = None, coordinates: Optional[tuple] = None): if data == 'cancel': return Reply(next_state=SupplyState.VIEW_INFO) return self.handle_text(text)
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 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_finish_take(user: User, provider_str: str, supply_user_db_id: str, message_id: str): supply_user = get_user(supply_user_db_id, provider=Provider(provider_str), workflow=Workflow.SUPPLY) is_successfully_booked = mark_message_as_booked(demand_user=user, message_id=message_id) if not is_successfully_booked: return Reply(text=_('Someone has already taken it.')) notify_supply_for_booked(supply_user=supply_user, message_id=message_id, demand_user=user) return Reply(text=_( "%s is notified that you'll take the food. Please, wait for approval.") % supply_user.info[UserInfoField.NAME.value])
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)) ], )
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 _handle_edit_social_status(user: User): buttons = [[{ 'text': soc_status_translation.get(x) or '~~', 'data': f'{DemandCommandName.SET_SOCIAL_STATUS.value}|{x.value}', }] for x in SocialStatus] buttons.append([get_demand_back_button(user)]) return Reply(text=_('Choose your social status:'), 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 approve_supplier(user: User, supplier_id: str): supply_user = get_user_by_id(supplier_id) set_info(supply_user, UserInfoField.IS_APPROVED_SUPPLY, True) notify_supplier_is_approved(supply_user) return Reply(text=build_supplier_approved_text(supply_user), buttons=[[{ 'text': _('Nope, decline it'), 'data': f'c|{SupplyCommand.DECLINE_SUPPLIER}|{supplier_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 decline_supplier(user: User, supplier_id: str): supply_user = get_user_by_id(supplier_id) set_info(supply_user, UserInfoField.IS_APPROVED_SUPPLY, False) notify_supplier_is_declined(supply_user) return Reply(text=build_supplier_declined_text(supply_user), buttons=[[{ 'text': _('Sorry, approve it'), 'data': f'c|{SupplyCommand.APPROVE_SUPPLIER}|{supplier_id}' }]])
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 process(self, data: str): try: data = json.loads(data) send_messages( tg_chat_id=data['tg_chat_id'], original_message=(data['original_message'] and TgMessage.de_json( data['original_message'], None)), replies=[Reply(**x) for x in data['replies']], workflow=Workflow(data['workflow']), ) except Exception: logger.exception('Message was not sent. Data:\n%s', data)
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_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 handle(self, text: str, data: Optional[str] = None, coordinates: Optional[tuple] = None): if data == 'change-coordinates': set_info(self.db_user, UserInfoField.COORDINATES, None) return if data == 'approve-coordinates': set_info(self.db_user, UserInfoField.IS_APPROVED_COORDINATES, True) return Reply(next_state=self.get_next_state()) if data == 'later': set_info(self.db_user, UserInfoField.IS_APPROVED_COORDINATES, True) return Reply(next_state=self.get_next_state()) if coordinates: set_info(self.db_user, UserInfoField.COORDINATES, [str(x) for x in coordinates]) set_info(self.db_user, UserInfoField.IS_APPROVED_COORDINATES, True) return Reply(next_state=self.get_next_state()) return super().handle(text, data)
class ReadyToPostState(State): intro = Reply(buttons=[ [{ 'text': _('Edit restaurant info'), 'data': 'view-info', }], ], ) 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 get_intro(self) -> Reply: reply = super().get_intro() reply.text = self._get_intro_text() messages = list_messages(self.db_user) if messages: reply.buttons.append([{ 'text': _('View posted products'), 'data': f'c|{SupplyCommand.LIST_MESSAGES}', }]) return reply def handle(self, text: str, data: str, *args, **kwargs): if data == 'view-info': return Reply(next_state=SupplyState.VIEW_INFO) if not text: return if not self.db_user.info.get(UserInfoField.IS_APPROVED_SUPPLY.value): return create_supply_message(self.db_user, text, provider=self.provider) return Reply(next_state=SupplyState.POSTING)