def __stop_conversation(self, conversation, notify_current=False):

        # Devo disabilitare anche il search, in maniera tale che l'utente b clicchi su Search se vuole ancora cercare.
        # Questo mi permette di eliminare il problema webchat..
        # ---------
        # Ora la webchat viene gestita tramite un expire_at, aggiornato ad ogni messaggio pervenuto da quel conversation_id

        # Nel caso l'utente non esiste più (viene caricato quando accedo a reference chat_with) allora non serve che effettuo invii
        if not conversation.chat_with_exists:
            return

        notify_user = ConversationModel.objects(id=conversation.chat_with.id,
                                                chat_with=conversation).modify(
                                                    chat_with=None, new=True)
        if not notify_current:
            ConversationModel.objects(
                id=conversation.id,
                chat_with=conversation.chat_with).modify(chat_with=None)

        if notify_user:
            self._internal_send_text(
                notify_user,
                self.translate('conversation_stopped_by_other_geostranger'))

        if notify_current:
            self._internal_send_text(
                conversation,
                self.translate('conversation_stopped_by_other_geostranger'))
def index_page():
    # Add new Fake conversation for that ips...

    # todo: TO REMOVE AFTER SOME TIMES
    try:
        geo = geoip()
        if geo:
            longitude = geo.get('location', {}).get('longitude', '')
            latitude = geo.get('location', {}).get('latitude', '')
            country = geo.get('country', {}).get('names', {}).get('en', '')
            continent = geo.get('continent', {}).get('names', {}).get('en', '')

            if longitude and latitude and country and continent:
                conv = ConversationModel()
                conv.conversation_id = '%s%s' % (longitude, latitude)
                conv.chat_type = '_geoip'
                conv.location = [longitude, latitude]
                conv.location_text = country + ', ' + continent
                conv.completed = True
                conv.save()
    except Exception as e:
        # print(e)
        pass

    return render_template('pages/index.html')
	def get(self):
		args = self.reqparse.parse_args()

		users = ConversationModel.objects().count()
		# users_completed = ConversationModel.objects(completed=True).count()
		# users_not_completed = ConversationModel.objects(completed=False).count()
		chats_times = ConversationModel.objects.sum('chatted_times')
		# users_stopped = ConversationModel.objects(is_searchable=False).count()
		# users_deleted = ConversationModel.objects(deleted_at__ne=None).count()
		# users_expired = ConversationModel.objects(expire_at__lt=datetime.datetime.utcnow()).count()
		# users_in_chat = ConversationModel.objects(chat_with__ne=None).count()
		messages = ConversationModel.objects.sum('messages_sent')

		pipeline = [
			{'$group':
				 {'_id': {'location': '$location', 'count': {'$sum': 1}}}
			 }
		]

		locations = ConversationModel.objects.aggregate(*pipeline)

		count = 0
		for location in locations:
			count += location.get('_id', {}).get('count', 0)

		return {'users': users*10, 'messages': messages*95, 'chats': chats_times*15, 'locations': count}
	def get(self):
		args = self.reqparse.parse_args()
		pipeline = [
			{'$group':
				 {'_id': {'location': '$location', 'location_text': '$location_text'}}
			 }]

		# print((args.south, args.west), (args.north, args.east))
		try:
			# loc.objects(point__geo_within_box=[ < bottom left coordinates >, < upper right coordinates >])
			users = ConversationModel.objects(deleted_at=None, completed=True,
											  location__geo_within_box=[(args.west, args.south),
																		(args.east, args.north)]) \
				.aggregate(*pipeline)

			locations = []
			for user in users:
				# locations.append(user.location)
				locations.append(
					{'location': user.get('_id', {}).get('location'),
					 'location_text': user.get('_id', {}).get('location_text')})

			return locations
		except GeneratorExit:
			return []
	def get(self):
		args = self.reqparse.parse_args()

		conversation = ConversationModel.objects(id=args._id).first()

		if not conversation:
			return []

		return ConversationModel.objects(Q(id__nin=[conversation.id]) & \
										 Q(chat_with=None) & \
										 (Q(is_searchable=True) | Q(allow_search=True)) & \
										 Q(completed=True) & \
										 Q(location__near=conversation.location) & \
										 (Q(expire_at__exists=False) | Q(
											 expire_at__gte=datetime.datetime.utcnow()))) \
			.order_by("+created_at") \
			.order_by("+messages_sent") \
			.order_by("+messages_received") \
			.order_by("+last_engage_at").limit(args.limit).to_json()
    def _get_conversation_from_message(self, message):
        conversation_id = self.get_conversation_id_from_message(message)

        self.current_conversation = self._get_conversation(
            self.__class__.__name__, conversation_id, False)

        if not self.current_conversation:
            conversation_id = self.get_conversation_id_from_message(message)
            language = self.get_user_language_from_message(message)

            self.current_conversation = ConversationModel(
                chat_type=str(self.__class__.__name__),
                conversation_id=str(conversation_id),
                language=language)
            self.current_conversation.save()

        extra_data = self.get_extra_data(message)
        if self.current_conversation.extra_data != extra_data:
            self.current_conversation.extra_data = extra_data
            self.current_conversation.save()
    def _get_conversation(chat_type, conversation_id, strict=True):

        user = ConversationModel.objects(Q(chat_type=str(chat_type)) & \
                 Q(conversation_id=str(conversation_id)) & \
                 Q(deleted_at=None)) \
         .first()

        if not user and strict:
            logging.exception('User required, but not found.')
            raise RuntimeError

        return user
    def _handle_stop_step1(self):

        if not self._check_response('yes'):
            self._internal_send_text(self.current_conversation,
                                     self.translate('not_stopped'))
            return

        self.__stop_conversation(self.current_conversation)

        self.current_conversation = ConversationModel.objects(
            id=self.current_conversation.id,
            chat_with=self.current_conversation.chat_with).modify(
                chat_with=None,
                is_searchable=False,
                allow_search=False,
                new=True)
        self._internal_send_text(self.current_conversation,
                                 self.translate('stop'))
	def get(self):
		args = self.reqparse.parse_args()

		users = ConversationModel.objects().count()
		users_completed = ConversationModel.objects(completed=True).count()
		users_not_completed = ConversationModel.objects(completed=False).count()
		users_chatted_times = ConversationModel.objects.sum('chatted_times')
		users_stopped = ConversationModel.objects(is_searchable=False).count()
		users_deleted = ConversationModel.objects(deleted_at__ne=None).count()
		users_expired = ConversationModel.objects(expire_at__lt=datetime.datetime.utcnow()).count()
		users_in_chat = ConversationModel.objects(chat_with__ne=None).count()
		messages = ConversationModel.objects.sum('messages_sent')

		return {'users': {'totals': users, 'completed': users_completed, 'not_completed': users_not_completed,
						  'stopped': users_stopped, 'deleted': users_deleted, 'in_chat': users_in_chat,
						  'expired': users_expired,
						  'chatted_times': users_chatted_times}, 'messages': {'totals': messages}}
    def __engage_conversation(self, from_conversation, count=0):

        if not count:
            self._internal_send_text(from_conversation,
                                     self.translate('in_search'),
                                     commands=False)

        if not from_conversation.is_searchable:
            from_conversation.is_searchable = True
            from_conversation.save()

        # L'utente fa il search. Posso utilizzarlo solamente se l'utente non è al momento sotto altra conversation.
        # Questo vuol dire che non devo fare nessun ciclo. E' UNA FUNZIONE ONE SHOT!
        # E se non trovo nulla, devo aspettare che sia un altro a fare questa operazione e "Trovarmi"..

        # http://docs.mongoengine.org/guide/querying.html#further-aggregation

        # Now all search are with meritocracy!
        # Order users in bases of distance, Last engage, messages received and sent, and when are created.

        next_user = ConversationModel.objects.engage(from_conversation) \
         .order_by("+created_at") \
         .order_by("+messages_sent") \
         .order_by("+messages_received") \
         .order_by("+last_engage_at").first()
        """ Memurandum: Il più vuol dire crescendo (0,1,2,3) il meno vuol dire decrescendo (3,2,1,0) """
        """ Memurandum: Prima ordino per quelli meno importanti, poi per quelli più importanti """
        """ Se non ho trovato nessuno, devo solo aspettare che il sistema mi associ ad un altro geostranger. """
        if not next_user:
            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))
            """ Se non ho trovato nulla e non sono stato selezionato, aspetto.. succederà prima o poi :) """
            return

        logging.debug('Found %s user' % str(next_user.id))

        from_conversation = ConversationModel.objects(id=from_conversation.id, chat_with=None) \
         .modify(chat_with=next_user,
           last_engage_at=datetime.datetime.utcnow(),
           inc__chatted_times=1,
           new=True)

        if not from_conversation:
            """ Se sono già stato scelto durante questa query.  """
            return

        # TODO: here need to be checked and notified user with disconnect.
        # controllo che l'utente non sia stato preso.
        # SE convesation_found è None, allora è stato scelto da qualcuno altro e devo dire di aspettare..
        # altrimenti continuo
        conversation_found = ConversationModel.objects(
            id=next_user.id, chat_with=next_user.chat_with).modify(
                chat_with=from_conversation,
                last_engage_at=datetime.datetime.utcnow(),
                inc__chatted_times=1,
                new=True)

        if not conversation_found:
            ConversationModel.objects(id=from_conversation.id,
                                      chat_with=next_user).modify(
                                          chat_with=None,
                                          inc__chatted_times=-1)

            # Vuol dire che nel frangente tra la scelta del next_user e la bollata dell'utente l'utente mi è stato fregato.
            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))
            """ succederà prima o poi :) """
            return

        if next_user.chat_with_exists:
            self.__stop_conversation(next_user, notify_current=True)
        """Invia il messaggio al utente selezionato """

        if conversation_found.first_time_chat:
            send_text = 'found_new_geostranger_first_time'
        else:
            send_text = 'found_new_geostranger'

        sent = self._internal_send_text(
            conversation_found,
            self.translate(send_text,
                           location_text=from_conversation.location_text))

        if not sent:

            # TODO: Dopo aver resettato i contatori, posso ricercare di nuovo un nuova convesazione.
            """ il messaggio non è stato inviato.. probabilmente l'utente non è più disponibile, provvedo a togliere le associazioni"""
            from_conversation = ConversationModel.objects(
                id=from_conversation.id,
                chat_with=conversation_found).modify(chat_with=None,
                                                     new=True,
                                                     inc__chatted_times=-1)
            ConversationModel.objects(id=conversation_found.id,
                                      chat_with=from_conversation).modify(
                                          chat_with=None,
                                          inc__chatted_times=-1)

            if not from_conversation:
                # Se l'utente non viene modificato, vuol dire che, per qualche strana ragione è già stato abinato a qualcun'altro e non alla conversazione corrente.
                # Questo vuol dire che posso ritornare e NON devo ritentare.
                return

            if count < 3:
                self.__engage_conversation(from_conversation, count=count + 1)
                return

            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))

            return

        self._internal_send_text(conversation_found,
                                 self.translate('new_chat'))

        if conversation_found.first_time_chat:
            conversation_found.first_time_chat = False
            conversation_found.save()

        if from_conversation.first_time_chat:
            send_text = 'found_new_geostranger_first_time'
        else:
            send_text = 'found_new_geostranger'

        self._internal_send_text(
            from_conversation,
            self.translate(send_text,
                           location_text=conversation_found.location_text))
        self._internal_send_text(from_conversation, self.translate('new_chat'))

        if from_conversation.first_time_chat:
            from_conversation.first_time_chat = False
            from_conversation.save()
class Handler(Abstract):
    @property
    def list_commands(self):

        if self._commands:
            return self._commands

        for x in dir(self):
            if x.endswith('_command'):
                self._commands.append(x.replace('_command', ''))

        return self._commands

    def __init__(self, request=None):

        self._commands = []

        self.current_conversation = None
        self.message_text = ''
        self.message_attachments = []
        self._need_rewrite = False
        self._have_keyboard = True

        self._actual_message = None

        if request:

            # TODO: Correct this.. now not do anythin.... ahahha
            self.verify_signature(request)

            messages = self.extract_message(request)

            if isinstance(messages, (list, tuple)) or (hasattr(
                    messages, '__iter__') and not isinstance(messages, dict)):
                for message in messages:
                    self._process_message(message)

            else:
                self._process_message(messages)

    def _process_message(self, message):

        self._actual_message = message

        self._get_conversation_from_message(message)

        self._refresh_expire(message)

        if not self.have_keyboard(message):
            self._have_keyboard = False

        if not self.can_continue(message):
            return

        if not self.is_compatible(message):
            self.not_compatible()
            return

        if self.is_group(message):
            # TODO: Response with correct item?
            return

        if not self._is_user_allowed():
            pass

        self.message_text = self.get_text_from_message(message)
        self.message_attachments = self.get_attachments_url_from_message(
            message)
        self._check_attachment()

        self.generic_command()

    def _check_attachment(self):

        allowed_attachments = ['audio', 'video', 'image']
        all_ok = True
        for (i, item) in enumerate(self.message_attachments):

            if not item[0].startswith(tuple(allowed_attachments)):
                del self.message_attachments[i]
                all_ok = False

        if not all_ok:
            self._internal_send_text(
                self.current_conversation,
                self.translate('attachment_not_compatible',
                               allowed_attachments=allowed_attachments),
                commands=False)

    def _is_user_allowed(self):
        return True

    def _refresh_expire(self, message):
        seconds = self.expire_after_seconds(message)
        if seconds:
            self.current_conversation.expire_at = datetime.datetime.utcnow(
            ) + datetime.timedelta(seconds=seconds)
            self.current_conversation.save()
        else:
            if self.current_conversation.expire_at:
                self.current_conversation.expire_at = None
                self.current_conversation.save()

    @staticmethod
    def _registry_handler(user_model, handler_name):

        if hasattr(handler_name, '__name__'):
            handler_name = handler_name.__name__
        try:
            user_model.modify(next_function=handler_name)
        except DoesNotExist as e:
            logging.warning(e)

    def _get_conversation_from_message(self, message):
        conversation_id = self.get_conversation_id_from_message(message)

        self.current_conversation = self._get_conversation(
            self.__class__.__name__, conversation_id, False)

        if not self.current_conversation:
            conversation_id = self.get_conversation_id_from_message(message)
            language = self.get_user_language_from_message(message)

            self.current_conversation = ConversationModel(
                chat_type=str(self.__class__.__name__),
                conversation_id=str(conversation_id),
                language=language)
            self.current_conversation.save()

        extra_data = self.get_extra_data(message)
        if self.current_conversation.extra_data != extra_data:
            self.current_conversation.extra_data = extra_data
            self.current_conversation.save()

    def _rewrite_commands(self, text):
        if self.need_rewrite_commands():
            if isinstance(text, (list, tuple)):
                for (i, item) in enumerate(text):
                    if item[1:] in self.list_commands:
                        text[i] = item.replace('/' + item[1:], '!' + item[1:])
                return text

            for command in self.list_commands:
                text = text.replace('/' + command, '!' + command)
        return text

    def _translate(self, text, **variables):
        if self.current_conversation and self.current_conversation.language:
            language = self.current_conversation.language

            with force_locale(language[:2]):
                text = gettext(text, **variables)

                return text

        text = gettext(text, **variables)
        return text

    def translate(self, original_text, **variables):

        text = lang.bot_messages.get(original_text, original_text)
        text = self._translate(text, **variables)

        if original_text in messages_to_not_botting:
            return text

        return '🤖' + ' ' + text

    @staticmethod
    def _md5(_str):

        return hashlib.md5((config.SECRET_KEY + _str).encode()).hexdigest()

    @staticmethod
    def _get_conversation(chat_type, conversation_id, strict=True):

        user = ConversationModel.objects(Q(chat_type=str(chat_type)) & \
                 Q(conversation_id=str(conversation_id)) & \
                 Q(deleted_at=None)) \
         .first()

        if not user and strict:
            logging.exception('User required, but not found.')
            raise RuntimeError

        return user

    def _check_response(self, check, strict=False):

        if not self.message_text:
            return False

        w = self.translate(check)

        if not strict:
            return self.message_text.lower().strip() == w.lower().strip()

        return self.message_text == w

    @staticmethod
    def _get_sender(sender_class, with_user):
        mod = importlib.import_module('UniversalBot')
        cls = getattr(mod, sender_class)()
        cls.current_conversation = with_user
        return cls

    @staticmethod
    def _correct_content_type(file_type, file_url):

        m = mimetypes.guess_type(file_url)[0]

        if m and m.startswith(file_type):
            return m

        _suffix = ''

        if file_type == 'image':
            _suffix = '/png'
        if file_type == 'audio':
            _suffix = '/mp3'
        if file_type == 'video':
            _suffix = '/mp4'
        if file_type == 'file':
            file_type = m or 'application/octet-stream'

        return file_type + _suffix

    def _secure_download(self, file_url, file_type=None):

        proxy = ProxyUrlModel(url=file_url,
                              file_type=file_type,
                              headers=self.authorization()).save()

        return str(proxy.id)

    def _select_commands(self, user_model):

        # TODO: need to bee only see in some situations. Not always.

        commands = ['/terms', '/help', '/delete']

        if user_model.completed:
            commands = ['/new', '/location', '/terms', '/help', '/delete']

        if user_model.chat_with_exists:
            commands = ['/new', '/stop']

        return commands

    def _generate_keyboard(self, user_model, commands):
        if self._have_keyboard:

            if commands is None:
                commands = self._select_commands(user_model)

            if commands:
                commands = self._rewrite_commands(commands)
                return self.new_keyboard(*commands)

    def _internal_send_text(self, user_model, text, commands=None):

        if user_model.deleted_at:
            return False

        sender = self
        if self.__class__.__name__ != user_model.chat_type:
            sender = self._get_sender(user_model.chat_type, user_model)

        keyboard = sender._generate_keyboard(user_model, commands)

        text = sender._rewrite_commands(text)

        try:
            sender.bot_send_text(user_model, text, keyboard=keyboard)
            if not keyboard and commands:
                sender.bot_send_text(
                    user_model,
                    self.translate('commands_available', commands=commands))
            return True
        except Exception as e:
            # Need monitoring exceptions in this times!
            logging.exception(e)
            return False

    def _internal_send_attachment(self, user_model, attachment, commands=None):

        if user_model.deleted_at:
            return False

        sender = self
        if self.__class__.__name__ != user_model.chat_type:
            sender = self._get_sender(user_model.chat_type, user_model)

        keyboard = sender._generate_keyboard(user_model, commands)

        file_url = attachment[1]
        file_type = self._correct_content_type(attachment[0], file_url)

        _id = self._secure_download(file_url, file_type)

        secure_url = url_for('index.download_action',
                             _id=_id,
                             _external=True,
                             _scheme='https')

        try:
            sender.bot_send_attachment(user_model,
                                       secure_url,
                                       file_type,
                                       keyboard=keyboard)
            if not keyboard and commands:
                sender.bot_send_text(
                    user_model,
                    self.translate('commands_available', commands=commands))

            return True
        except Exception as e:

            logging.warning(e)

            if file_type and file_type.startswith('image'):
                secure_url = url_for('index.image_page',
                                     _id=_id,
                                     _external=True,
                                     _scheme='https')

                return self._internal_send_text(user_model,
                                                self.translate(
                                                    'show_image',
                                                    file_url=secure_url),
                                                commands=commands)

            if file_type and file_type.startswith('video'):
                secure_url = url_for('index.video_page',
                                     _id=_id,
                                     _external=True,
                                     _scheme='https')

                return self._internal_send_text(user_model,
                                                self.translate(
                                                    'play_video',
                                                    file_url=secure_url),
                                                commands=commands)

            if file_type and file_type.startswith('audio'):
                secure_url = url_for('index.audio_page',
                                     _id=_id,
                                     _external=True,
                                     _scheme='https')

                return self._internal_send_text(user_model,
                                                self.translate(
                                                    'play_audio',
                                                    file_url=secure_url),
                                                commands=commands)

            return self._internal_send_text(user_model,
                                            self.translate(
                                                'show_file',
                                                file_url=secure_url),
                                            commands=commands)

    def not_compatible(self):
        if self.current_conversation:
            self._internal_send_text(self.current_conversation,
                                     self.translate('not_compatible'))

    def start_command(self):
        return self.welcome_command()

    def generic_command(self):

        logging.debug('Entering into Generic Command')
        # logging.debug('Message: \n\n%s' % json.dumps(message, indent=2, default=str))

        logging.debug('Conversation: %s ' % str(self.current_conversation.id))

        # logging.debug('Text with message: %s (len: %s)' % (str(execute_command), len(str(execute_command))))

        # logging.debug('User next_function: %s ' % str(self.current_conversation.next_function))
        if self.current_conversation.next_function:
            next_f = self.current_conversation.next_function
            self.current_conversation.next_function = None
            self.current_conversation.save()
            try:
                logging.debug('Executing function: %s ' % str(next_f))
                getattr(self, next_f)()
            except Exception as e:
                logging.exception(e)
                self._internal_send_text(self.current_conversation,
                                         self.translate('error'))
                self._registry_handler(self.current_conversation,
                                       self.current_conversation.next_function)

            return

        if not self.current_conversation.completed:
            logging.debug('Conversation not completed')
            self.welcome_command()
            return

        if self.message_text and len(
                self.message_text) and (self.message_text[0] == '/'
                                        or self.message_text[0] == '!'):

            logging.debug('Text (%s) is a command' %
                          str(self.message_text.encode('utf-8')))
            # logging.debug('Text (%s) is a command' % execute_command)

            command = self.message_text[1:].strip()

            # annullo l'ultimo comando..
            next_f = self.current_conversation.next_function
            if next_f:
                logging.debug('Delete next_function of user (%s) ' %
                              str(next_f))
                self.current_conversation.next_function = None
                self.current_conversation.save()

            if not hasattr(self, str(command) + '_command'):
                self._internal_send_text(
                    self.current_conversation,
                    self.translate('command_not_found', command_text=command))
                return

            logging.debug('Executing command')
            try:
                getattr(self, str(command) + '_command')()
                return
            except Exception as e:
                logging.exception(e)
                self.current_conversation.next_function = next_f
                self.current_conversation.save()

                self._internal_send_text(self.current_conversation,
                                         self.translate('error'))

                return

        # Qua deve andare in errore in quanto mi devo disabilitare l'utente in caso.
        if self.current_conversation.chat_with_exists:
            self.__proxy_message()
            return
        """ Proxy the message to other user """

        self.help_command()

    def welcome_command(self):

        self._internal_send_text(self.current_conversation,
                                 self.translate('welcome'),
                                 commands=False)

        if not self.current_conversation.location or not self.current_conversation.completed:
            self.location_command()
            return
        """User is completed, need a search!"""
        self.__engage_conversation(self.current_conversation)

    def location_command(self):

        self._internal_send_text(self.current_conversation,
                                 self.translate('ask_location'),
                                 commands=False)
        self._registry_handler(self.current_conversation,
                               self._handler_location_step1)

    def delete_command(self):
        self._internal_send_text(
            self.current_conversation,
            self.translate('ask_delete_sure'),
            commands=[self.translate('yes'),
                      self.translate('no')])
        self._registry_handler(self.current_conversation,
                               self._handle_delete_step1)

    def terms_command(self):

        self._internal_send_text(
            self.current_conversation,
            self.translate('terms',
                           terms_url=url_for('index.terms_page',
                                             _external=True,
                                             _scheme='https')))

    def help_command(self):
        self._internal_send_text(self.current_conversation,
                                 self.translate('help'))

    def notify_command(self):
        self._internal_send_text(
            self.current_conversation,
            self.translate('notification',
                           contact_us_url=url_for('index.contact_us_page',
                                                  _external=True,
                                                  _scheme='https')))

    def stop_command(self):

        yes_no_keyboard = [self.translate('yes'), self.translate('no')]

        if self.current_conversation.chat_with_exists:
            self._internal_send_text(
                self.current_conversation,
                self.translate('ask_stop_also_current_chat'),
                commands=yes_no_keyboard)
            self._registry_handler(self.current_conversation,
                                   self._handle_stop_step1)
            return

        self._internal_send_text(self.current_conversation,
                                 self.translate('ask_stop_sure'),
                                 commands=yes_no_keyboard)
        self._registry_handler(self.current_conversation,
                               self._handle_stop_step1)

    def new_command(self):
        # I not need here retrieve all user info

        if not self.current_conversation.chat_with_exists:
            self.__engage_conversation(self.current_conversation)
            return

        self._internal_send_text(
            self.current_conversation,
            self.translate('sure_search_new'),
            commands=[self.translate('yes'),
                      self.translate('no')])
        self._registry_handler(self.current_conversation,
                               self._handle_new_step1)

    def _handle_new_step1(self):

        if not self._check_response('yes'):
            self._internal_send_text(self.current_conversation,
                                     self.translate('not_stopped'))
            return

        self.__stop_conversation(self.current_conversation)

        self.__engage_conversation(self.current_conversation)

        return

    def _handle_stop_step1(self):

        if not self._check_response('yes'):
            self._internal_send_text(self.current_conversation,
                                     self.translate('not_stopped'))
            return

        self.__stop_conversation(self.current_conversation)

        self.current_conversation = ConversationModel.objects(
            id=self.current_conversation.id,
            chat_with=self.current_conversation.chat_with).modify(
                chat_with=None,
                is_searchable=False,
                allow_search=False,
                new=True)
        self._internal_send_text(self.current_conversation,
                                 self.translate('stop'))

    def _handle_delete_step1(self):

        if not self._check_response('yes'):
            self._internal_send_text(self.current_conversation,
                                     self.translate('not_deleted'))
            return

        self._internal_send_text(self.current_conversation,
                                 self.translate('delete_completed'))

        self.current_conversation.deleted_at = datetime.datetime.utcnow()
        self.current_conversation.save()

    def _handler_location_step1(self):

        if not self.message_text:
            self._internal_send_text(self.current_conversation,
                                     self.translate('location_error'),
                                     commands=False)
            self._registry_handler(self.current_conversation,
                                   self._handler_location_step1)
            return

        location = search_street(self.message_text, lang='en')

        if not location:
            """ Location non trovata.. """
            self._internal_send_text(self.current_conversation,
                                     self.translate(
                                         'location_not_found',
                                         location_text=self.message_text),
                                     commands=False)
            self._registry_handler(self.current_conversation,
                                   self._handler_location_step1)
            return
        """ Location trovata! Per il momento la salvo e chiedo se è corretta :) """
        self.current_conversation.location = [
            location.longitude, location.latitude
        ]
        self.current_conversation.location_text = location.address
        self.current_conversation.save()

        self._internal_send_text(
            self.current_conversation,
            self.translate('ask_location_is_correct',
                           location_text=location.address),
            commands=[self.translate('yes'),
                      self.translate('no')])
        self._registry_handler(self.current_conversation,
                               self._handler_location_step2)

    def _handler_location_step2(self):

        if not self._check_response('yes'):
            """ Richiedo allora nuovamente la posizione """

            self._internal_send_text(self.current_conversation,
                                     self.translate('re_ask_location'),
                                     commands=False)
            self._registry_handler(self.current_conversation,
                                   self._handler_location_step1)
            return

        self._internal_send_text(
            self.current_conversation,
            self.translate(
                'location_saved',
                location_text=self.current_conversation.location_text),
            commands=False)
        """ Location ok! """

        if not self.current_conversation.completed:
            self.current_conversation.completed = True
            self.current_conversation.save()

        self._internal_send_text(self.current_conversation,
                                 self.translate('completed'),
                                 commands=False)
        """User is completed, need a search"""
        self.__engage_conversation(self.current_conversation)

    def __stop_conversation(self, conversation, notify_current=False):

        # Devo disabilitare anche il search, in maniera tale che l'utente b clicchi su Search se vuole ancora cercare.
        # Questo mi permette di eliminare il problema webchat..
        # ---------
        # Ora la webchat viene gestita tramite un expire_at, aggiornato ad ogni messaggio pervenuto da quel conversation_id

        # Nel caso l'utente non esiste più (viene caricato quando accedo a reference chat_with) allora non serve che effettuo invii
        if not conversation.chat_with_exists:
            return

        notify_user = ConversationModel.objects(id=conversation.chat_with.id,
                                                chat_with=conversation).modify(
                                                    chat_with=None, new=True)
        if not notify_current:
            ConversationModel.objects(
                id=conversation.id,
                chat_with=conversation.chat_with).modify(chat_with=None)

        if notify_user:
            self._internal_send_text(
                notify_user,
                self.translate('conversation_stopped_by_other_geostranger'))

        if notify_current:
            self._internal_send_text(
                conversation,
                self.translate('conversation_stopped_by_other_geostranger'))

    def __engage_conversation(self, from_conversation, count=0):

        if not count:
            self._internal_send_text(from_conversation,
                                     self.translate('in_search'),
                                     commands=False)

        if not from_conversation.is_searchable:
            from_conversation.is_searchable = True
            from_conversation.save()

        # L'utente fa il search. Posso utilizzarlo solamente se l'utente non è al momento sotto altra conversation.
        # Questo vuol dire che non devo fare nessun ciclo. E' UNA FUNZIONE ONE SHOT!
        # E se non trovo nulla, devo aspettare che sia un altro a fare questa operazione e "Trovarmi"..

        # http://docs.mongoengine.org/guide/querying.html#further-aggregation

        # Now all search are with meritocracy!
        # Order users in bases of distance, Last engage, messages received and sent, and when are created.

        next_user = ConversationModel.objects.engage(from_conversation) \
         .order_by("+created_at") \
         .order_by("+messages_sent") \
         .order_by("+messages_received") \
         .order_by("+last_engage_at").first()
        """ Memurandum: Il più vuol dire crescendo (0,1,2,3) il meno vuol dire decrescendo (3,2,1,0) """
        """ Memurandum: Prima ordino per quelli meno importanti, poi per quelli più importanti """
        """ Se non ho trovato nessuno, devo solo aspettare che il sistema mi associ ad un altro geostranger. """
        if not next_user:
            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))
            """ Se non ho trovato nulla e non sono stato selezionato, aspetto.. succederà prima o poi :) """
            return

        logging.debug('Found %s user' % str(next_user.id))

        from_conversation = ConversationModel.objects(id=from_conversation.id, chat_with=None) \
         .modify(chat_with=next_user,
           last_engage_at=datetime.datetime.utcnow(),
           inc__chatted_times=1,
           new=True)

        if not from_conversation:
            """ Se sono già stato scelto durante questa query.  """
            return

        # TODO: here need to be checked and notified user with disconnect.
        # controllo che l'utente non sia stato preso.
        # SE convesation_found è None, allora è stato scelto da qualcuno altro e devo dire di aspettare..
        # altrimenti continuo
        conversation_found = ConversationModel.objects(
            id=next_user.id, chat_with=next_user.chat_with).modify(
                chat_with=from_conversation,
                last_engage_at=datetime.datetime.utcnow(),
                inc__chatted_times=1,
                new=True)

        if not conversation_found:
            ConversationModel.objects(id=from_conversation.id,
                                      chat_with=next_user).modify(
                                          chat_with=None,
                                          inc__chatted_times=-1)

            # Vuol dire che nel frangente tra la scelta del next_user e la bollata dell'utente l'utente mi è stato fregato.
            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))
            """ succederà prima o poi :) """
            return

        if next_user.chat_with_exists:
            self.__stop_conversation(next_user, notify_current=True)
        """Invia il messaggio al utente selezionato """

        if conversation_found.first_time_chat:
            send_text = 'found_new_geostranger_first_time'
        else:
            send_text = 'found_new_geostranger'

        sent = self._internal_send_text(
            conversation_found,
            self.translate(send_text,
                           location_text=from_conversation.location_text))

        if not sent:

            # TODO: Dopo aver resettato i contatori, posso ricercare di nuovo un nuova convesazione.
            """ il messaggio non è stato inviato.. probabilmente l'utente non è più disponibile, provvedo a togliere le associazioni"""
            from_conversation = ConversationModel.objects(
                id=from_conversation.id,
                chat_with=conversation_found).modify(chat_with=None,
                                                     new=True,
                                                     inc__chatted_times=-1)
            ConversationModel.objects(id=conversation_found.id,
                                      chat_with=from_conversation).modify(
                                          chat_with=None,
                                          inc__chatted_times=-1)

            if not from_conversation:
                # Se l'utente non viene modificato, vuol dire che, per qualche strana ragione è già stato abinato a qualcun'altro e non alla conversazione corrente.
                # Questo vuol dire che posso ritornare e NON devo ritentare.
                return

            if count < 3:
                self.__engage_conversation(from_conversation, count=count + 1)
                return

            self._internal_send_text(from_conversation,
                                     self.translate('search_not_found'))

            return

        self._internal_send_text(conversation_found,
                                 self.translate('new_chat'))

        if conversation_found.first_time_chat:
            conversation_found.first_time_chat = False
            conversation_found.save()

        if from_conversation.first_time_chat:
            send_text = 'found_new_geostranger_first_time'
        else:
            send_text = 'found_new_geostranger'

        self._internal_send_text(
            from_conversation,
            self.translate(send_text,
                           location_text=conversation_found.location_text))
        self._internal_send_text(from_conversation, self.translate('new_chat'))

        if from_conversation.first_time_chat:
            from_conversation.first_time_chat = False
            from_conversation.save()

    def __proxy_message(self):
        try:
            logging.debug(
                'Proxy the message from ConversationModel(%s) to ConversationModel(%s)'
                % (self.current_conversation.id,
                   self.current_conversation.chat_with.id))

            # TODO: If conversation return false, then need to stop it!

            self.current_conversation.update(
                inc__messages_sent=1,
                last_message_sent_at=datetime.datetime.utcnow())

            self.current_conversation.chat_with.update(
                inc__messages_received=1,
                last_message_received_at=datetime.datetime.utcnow())

            if len(self.message_attachments):
                for attachment in self.message_attachments:
                    self._internal_send_attachment(
                        self.current_conversation.chat_with, attachment)

            if self.message_text:
                self._internal_send_text(self.current_conversation.chat_with,
                                         self.message_text)

        except Exception as e:
            logging.exception(e)