Example #1
0
    def get_history(self, room_id: str, limit: int = 100):
        if limit is None:
            limit = -1

        messages = self.redis.lrange(RedisKeys.room_history(room_id), 0, limit)

        cleaned_messages = list()
        for message_entry in messages:
            message_entry = str(message_entry, 'utf-8')
            msg_id, published, user_id, user_name, target_name, channel_id, channel_name, msg = \
                message_entry.split(',', 7)

            cleaned_messages.append({
                'message_id': msg_id,
                'from_user_id': user_id,
                'from_user_name': b64d(user_name),
                'target_id': room_id,
                'target_name': b64d(target_name),
                'body': b64d(msg),
                'domain': 'room',
                'channel_id': channel_id,
                'channel_name': b64d(channel_name),
                'timestamp': published,
                'deleted': False
            })

        return cleaned_messages
Example #2
0
 def store_message(self, activity: Activity, deleted=False) -> None:
     message = b64d(activity.object.content)
     actor_name = b64d(activity.actor.display_name)
     self.driver.msg_insert(msg_id=activity.id,
                            from_user_id=activity.actor.id,
                            from_user_name=actor_name,
                            target_id=activity.target.id,
                            target_name=activity.target.display_name,
                            body=message,
                            domain=activity.target.object_type,
                            sent_time=activity.published,
                            channel_id=activity.object.url,
                            channel_name=activity.object.display_name,
                            deleted=deleted)
Example #3
0
    def create_room(arg: tuple) -> None:
        data, activity = arg
        room_name = activity.target.display_name
        room_id = activity.target.id
        channel_id = activity.object.url
        user_id = activity.actor.id
        user_name = activity.actor.display_name

        object_type = 'unknown'
        if hasattr(activity.target, 'object_type'):
            object_type = activity.target.object_type

        is_ephemeral = object_type != 'private'
        owners = OnCreateHooks._get_owners(activity)

        if utils.is_base64(room_name):
            room_name = utils.b64d(room_name)
        environ.env.db.create_room(room_name,
                                   room_id,
                                   channel_id,
                                   user_id,
                                   user_name,
                                   ephemeral=is_ephemeral)

        logger.debug('settings "{}" as owners of room {}'.format(
            ','.join(owners), room_id))
        for owner_id in owners:
            environ.env.db.set_owner(room_id, owner_id)
Example #4
0
def ban_user():
    form = request.get_json()
    target = form['target']
    target_uuid = form['target_uuid']
    user_uuid = form['user_uuid']
    duration = form['duration']

    try:
        user_manager.ban_user(user_uuid, target_uuid, duration, target)
    except ValidationException as e:
        return api_response(400, message='invalid duration: %s' % str(e))
    except UnknownBanTypeException as e:
        return api_response(400, message='could not ban user: %s' % str(e))
    except Exception as e:
        logger.exception(traceback.format_exc())
        return api_response(400, message=str(e))

    try:
        user = user_manager.get_user(user_uuid)
        user['name'] = utils.b64d(user['name'])
        user['duration'] = duration
    except NoSuchUserException:
        return api_response(400, message="No such user.")

    if target == 'channel':
        user['channel'] = {
            'uuid': target_uuid,
            'name': channel_manager.name_for_uuid(target_uuid)
        }
    elif target == 'room':
        user['room'] = {
            'uuid': target_uuid,
            'name': room_manager.name_for_uuid(target_uuid)
        }
    return api_response(200, user)
Example #5
0
        def publish_activity() -> None:
            user_name = activity.actor.display_name
            if utils.is_base64(user_name):
                user_name = utils.b64d(user_name)

            activity_json = utils.activity_for_message(user_id, user_name)
            environ.env.publish(activity_json, external=True)
Example #6
0
 def test_join_returns_activity_with_correct_owner(self):
     self.set_owner()
     act = self.activity_for_join()
     response = api.on_join(act, as_parser(act))
     attachments = response[1]['object']['attachments']
     owners = self.get_attachment_for_key(attachments, 'owner')
     user_id, user_name = owners[0]['id'], owners[0]['displayName']
     self.assertEqual(ApiJoinTest.USER_ID, user_id)
     self.assertEqual(ApiJoinTest.USER_NAME, b64d(user_name))
Example #7
0
 def test_get_acls_channel(self):
     self._create_channel()
     self.manager.add_acl_channel(BaseDatabaseTest.CHANNEL_ID,
                                  ApiActions.LIST, 'age', '25:45')
     acls = self.manager.get_acls_channel(BaseDatabaseTest.CHANNEL_ID)
     self.assertEqual(1, len(acls))
     self.assertEqual('age', acls[0]['type'])
     self.assertEqual('25:45', b64d(acls[0]['value']))
     self.assertEqual(ApiActions.LIST, acls[0]['action'])
Example #8
0
    def decode_or_throw(self, b64_word: str) -> str:
        if not utils.is_base64(b64_word):
            logger.error('word is not base64 encoded: "%s"' % b64_word)
            raise RuntimeError('word is not base64 encoded: "%s"' % b64_word)

        try:
            return utils.b64d(b64_word)
        except Exception as e:
            logger.error('could not decode base64 word "%s": %s' % (str(b64_word), str(e)))
            raise RuntimeError('could not decode base64 word "%s": %s' % (str(b64_word), str(e)))
Example #9
0
 def test_get_acls_room(self):
     self._create_channel()
     self._create_room()
     self.manager.add_acl_room(BaseDatabaseTest.ROOM_ID, ApiActions.JOIN,
                               'age', '25:45')
     acls = self.manager.get_acls_room(BaseDatabaseTest.ROOM_ID)
     self.assertEqual(1, len(acls))
     self.assertEqual('age', acls[0]['type'])
     self.assertEqual('25:45', b64d(acls[0]['value']))
     self.assertEqual(ApiActions.JOIN, acls[0]['action'])
Example #10
0
    def test_list_rooms_correct_name(self):
        self.assert_in_room(False)
        self.create_and_join_room()
        self.assert_in_room(True)

        act = self.activity_for_list_rooms()
        response_data = api.on_list_rooms(act, as_parser(act))
        self.assertEqual(
            ApiListRoomsTest.ROOM_NAME,
            b64d(response_data[1]['object']['attachments'][0]['displayName']))
Example #11
0
    def test_history_contains_correct_user_name(self):
        self.join_room()
        self.remove_owner()
        self.remove_owner_channel()
        self.send_message('my message')

        act = self.activity_for_history()
        response_data = api.on_history(act, as_parser(act))
        activity = as_parser(response_data[1])
        self.assertEqual(BaseTest.USER_NAME, b64d(activity.object.attachments[0].author.display_name))
Example #12
0
    def test_users_in_room_is_correct_name(self):
        self.assert_in_room(False)
        act = self.activity_for_join()
        api.on_join(act, as_parser(act))
        self.assert_in_room(True)

        act = self.activity_for_users_in_room()
        response_data = api.on_users_in_room(act, as_parser(act))
        self.assertEqual(
            ApiUsersInRoomTest.USER_NAME,
            b64d(response_data[1]['object']['attachments'][0]['displayName']))
Example #13
0
    def test_join_returns_correct_history(self):
        msg = 'this is a test message'
        self.set_owner()
        self.assert_join_succeeds()
        msg_response = self.send_message(msg)[1]
        self.leave_room()

        act = self.activity_for_join()
        response = api.on_join(act, as_parser(act))
        attachments = response[1]['object']['attachments']
        from pprint import pprint
        pprint(self.get_attachment_for_key(attachments, 'history'))
        all_history = self.get_attachment_for_key(attachments, 'history')
        self.assertEqual(1, len(all_history))
        history_obj = all_history[0]

        self.assertEqual(msg_response['id'], history_obj['id'])
        self.assertEqual(msg, b64d(history_obj['content']))
        self.assertEqual(msg_response['published'], history_obj['published'])
        self.assertEqual(ApiJoinTest.USER_NAME, b64d(history_obj['author']['displayName']))
Example #14
0
def banned():
    ban_form = BanForm(request.form)

    if request.method == 'POST' and ban_form.validate():
        try:
            user_manager.ban_user(ban_form.uuid.data, ban_form.target_id.data,
                                  ban_form.duration.data,
                                  ban_form.target_type.data)
            return redirect('/banned')
        except ValidationException as e:
            ban_form.target_type.errors.append('Ban not valid: "%s"' % e.msg)
        except UnknownBanTypeException as e:
            ban_form.target_type.errors.append('Unkonwn ban type "%s"' %
                                               e.ban_type)

    bans = user_manager.get_banned_users()
    channel_bans = bans['channels']
    for channel_id in channel_bans:
        channel_bans[channel_id]['name'] = utils.b64d(
            channel_bans[channel_id]['name'])
        for user_id in channel_bans[channel_id]['users']:
            channel_bans[channel_id]['users'][user_id]['name'] = \
                utils.b64d(channel_bans[channel_id]['users'][user_id]['name'])

    room_bans = bans['rooms']
    for room_id in room_bans:
        room_bans[room_id]['name'] = utils.b64d(room_bans[room_id]['name'])
        for user_id in room_bans[room_id]['users']:
            room_bans[room_id]['users'][user_id]['name'] = \
                utils.b64d(room_bans[room_id]['users'][user_id]['name'])

    global_bans = bans['global']
    for user_id in global_bans:
        global_bans[user_id]['name'] = \
            utils.b64d(global_bans[user_id]['name'])

    return render_template('banned.html',
                           form=ban_form,
                           globally=global_bans,
                           channels=channel_bans,
                           rooms=room_bans)
Example #15
0
    def test_history_contains_correct_sent_message(self):
        self.join_room()
        self.remove_owner()
        self.remove_owner_channel()

        message = 'my message'
        self.send_message(message)

        act = self.activity_for_history()
        response_data = api.on_history(act, as_parser(act))
        activity = as_parser(response_data[1])
        self.assertEqual(message, b64d(activity.object.attachments[0].content))
Example #16
0
def add_room_moderator(room_uuid: str):
    form = request.get_json()
    user_uuid = form['moderator']

    if is_blank(user_uuid):
        return api_response(400, message='Blank user id is not allowed.')
    try:
        user = user_manager.get_user(user_uuid)
        user['name'] = utils.b64d(user['name'])
    except NoSuchUserException:
        return api_response(400, message='No Such User.')
    user_manager.add_room_moderator(room_uuid, user_uuid)
    return api_response(200, user)
Example #17
0
def get_channel(channel_uuid: str):
    """ Get channel owners/admins/acls """
    acls = acl_manager.get_acls_channel(channel_uuid)
    acls_decoded = list()
    for acl in acls:
        acl['value'] = utils.b64d(acl['value'])
        acls_decoded.append(acl)

    return api_response(
        200, {
            'owners': channel_manager.get_owners(channel_uuid),
            'admins': channel_manager.get_admins(channel_uuid),
            'acls': acls_decoded,
        })
Example #18
0
def banned_users():
    bans = user_manager.get_banned_users()
    result = {'global': list(), 'channel': list(), 'room': list()}

    channel_bans = bans['channels']
    for channel_id in channel_bans:
        channel = {
            'name': utils.b64d(channel_bans[channel_id]['name']),
            'uuid': channel_id
        }
        for user_id in channel_bans[channel_id]['users']:
            user = channel_bans[channel_id]['users'][user_id]
            user['uuid'] = user_id
            user['name'] = utils.b64d(user['name'])
            user['channel'] = channel
            result['channel'].append(user)

    room_bans = bans['rooms']
    for room_id in room_bans:
        room = {
            'name': utils.b64d(room_bans[room_id]['name']),
            'uuid': room_id
        }
        for user_id in room_bans[room_id]['users']:
            user = room_bans[room_id]['users'][user_id]
            user['uuid'] = user_id
            user['name'] = utils.b64d(user['name'])
            user['room'] = room
            result['room'].append(user)

    global_bans = bans['global']
    for user_id in global_bans:
        user = global_bans[user_id]
        user['uuid'] = user_id
        user['name'] = utils.b64d(user['name'])
        result['global'].append(user)
    return api_response(200, result)
Example #19
0
    def on_create(self, activity: Activity) -> (bool, int, str):
        if not hasattr(activity, 'object') or not hasattr(
                activity.object, 'url'):
            return False, ECodes.MISSING_OBJECT_URL, 'no channel id set'
        if not hasattr(activity.target, 'display_name'):
            return False, ECodes.MISSING_TARGET_DISPLAY_NAME, 'no room name set'

        room_name = activity.target.display_name
        channel_id = activity.object.url

        if not hasattr(activity, 'actor') or not hasattr(activity.actor, 'id'):
            return False, ECodes.MISSING_ACTOR_ID, 'need actor.id (user uuid)'

        try:
            activity.object.display_name = utils.get_channel_name(channel_id)
        except NoSuchChannelException:
            return False, ECodes.NO_SUCH_CHANNEL, 'channel does not exist'

        if room_name is None or room_name.strip() == '':
            return False, ECodes.MISSING_TARGET_DISPLAY_NAME, 'got blank room name, can not create'

        if not utils.is_base64(room_name):
            return False, ECodes.NOT_BASE64, 'invalid room name, not base64 encoded'
        room_name = utils.b64d(room_name)

        if not environ.env.db.channel_exists(channel_id):
            return False, ECodes.NO_SUCH_CHANNEL, 'channel does not exist'

        if utils.room_name_restricted(room_name):
            return False, ECodes.ROOM_NAME_RESTRICTED, 'restricted room name'

        if environ.env.db.room_name_exists(channel_id, room_name):
            return False, ECodes.ROOM_ALREADY_EXISTS, 'a room with that name already exists'

        if not hasattr(activity.target, 'object_type') or \
                activity.target.object_type is None or \
                len(str(activity.target.object_type).strip()) == 0:
            # for acl validation to know we're trying to create a room
            activity.target.object_type = 'room'

        channel_acls = utils.get_acls_in_channel_for_action(
            channel_id, ApiActions.CREATE)
        is_valid, msg = validation.acl.validate_acl_for_action(
            activity, ApiTargets.CHANNEL, ApiActions.CREATE, channel_acls)

        if not is_valid:
            return False, ECodes.NOT_ALLOWED, msg

        return True, None, None
Example #20
0
    def _process(self, data: dict, activity: Activity):
        message = activity.object.content
        if message is None or len(message.strip()) == 0:
            return True, None, None

        if not utils.is_base64(message):
            return False, ErrorCodes.NOT_BASE64, \
                   'invalid message content, not base64 encoded'

        message = utils.b64d(message)
        if len(message) > self.max_length:
            return False, ErrorCodes.MSG_TOO_LONG, \
                   'message content needs to be shorter than %s characters' % self.max_length

        return True, None, None
Example #21
0
    def update_session_and_join_private_room(arg: tuple) -> None:
        data, activity = arg
        user_id = activity.actor.id
        user_name = utils.b64d(activity.actor.display_name)
        environ.env.session[SessionKeys.user_id.value] = user_id
        environ.env.session[SessionKeys.user_name.value] = user_name

        try:
            user_agent_string = environ.env.request.user_agent.string
            user_agent_platform = environ.env.request.user_agent.platform
            user_agent_browser = environ.env.request.user_agent.browser
            user_agent_version = environ.env.request.user_agent.version
            user_agent_language = environ.env.request.user_agent.language
        except Exception as e:
            logger.error('could not get user agent for user "{}": {}'.format(
                user_id, str(e)))
            logger.exception(traceback.format_exc())
            environ.env.capture_exception(sys.exc_info())
            user_agent_string = ''
            user_agent_platform = ''
            user_agent_browser = ''
            user_agent_version = ''
            user_agent_language = ''

        environ.env.session[
            SessionKeys.user_agent.value] = user_agent_string or ''
        environ.env.session[
            SessionKeys.user_agent_browser.value] = user_agent_browser or ''
        environ.env.session[
            SessionKeys.user_agent_version.value] = user_agent_version or ''
        environ.env.session[
            SessionKeys.user_agent_platform.value] = user_agent_platform or ''
        environ.env.session[
            SessionKeys.user_agent_language.value] = user_agent_language or ''

        if activity.actor.image is None:
            environ.env.session['image_url'] = ''
            environ.env.session[SessionKeys.image.value] = 'n'
        else:
            environ.env.session['image_url'] = activity.actor.image.url
            environ.env.session[SessionKeys.image.value] = 'y'

        sid = environ.env.request.sid
        utils.create_or_update_user(user_id, user_name)
        utils.add_sid_for_user_id(user_id, sid)

        environ.env.join_room(user_id)
        environ.env.join_room(environ.env.request.sid)
Example #22
0
def get_room(channel_uuid: str, room_uuid: str):
    acls = acl_manager.get_acls_room(room_uuid)
    acls_decoded = list()
    for acl in acls:
        acl['value'] = utils.b64d(acl['value'])
        acls_decoded.append(acl)

    return api_response(
        200, {
            'channel': {
                'uuid': channel_uuid,
                'name': channel_manager.name_for_uuid(channel_uuid)
            },
            'acls': acls_decoded,
            'owners': room_manager.get_owners(room_uuid),
            'moderators': room_manager.get_moderators(room_uuid)
        })
Example #23
0
    def async_post(self, json):
        logger.debug('POST request: %s' % str(json))

        if 'content' not in json:
            raise RuntimeError('no key [content] in json message')

        msg_content = json.get('content')
        if msg_content is None or len(msg_content.strip()) == 0:
            raise RuntimeError('content may not be blank')
        if not utils.is_base64(msg_content):
            raise RuntimeError('content in json message must be base64')

        user_id = str(json.get('user_id', 0))
        user_name = utils.b64d(json.get('user_name', utils.b64e('admin')))
        object_type = json.get('object_type')
        target_id = str(json.get('target_id'))
        namespace = json.get('namespace', '/ws')
        target_name = json.get('target_name')

        data = utils.activity_for_message(user_id, user_name)
        data['target'] = {
            'objectType': object_type,
            'id': target_id,
            'displayName': target_name,
            'url': namespace
        }
        data['object'] = {
            'content': msg_content
        }

        if not environ.env.cache.user_is_in_multicast(target_id):
            logger.info('user {} is offline, dropping message: {}'.format(target_id, str(json)))
            return

        try:
            environ.env.out_of_scope_emit('message', data, room=target_id, json=True, namespace='/ws', broadcast=True)
        except Exception as e:
            logger.error('could not /send message to target {}: {}'.format(target_id, str(e)))
            logger.exception(traceback.format_exc())
            environ.env.capture_exception(sys.exc_info())
Example #24
0
    def _process(self, data: dict, activity: Activity):
        room_name = activity.target.display_name

        if room_name is None or room_name.strip() == '':
            return False, ErrorCodes.MISSING_TARGET_DISPLAY_NAME, \
                   'got blank room name, can not create'

        if not utils.is_base64(room_name):
            return False, ErrorCodes.NOT_BASE64, \
                   'invalid room name, not base64 encoded'

        room_name = utils.b64d(room_name)

        if len(room_name) < self.min_length:
            return False, ErrorCodes.ROOM_NAME_TOO_SHORT, \
                   'room name needs to be longer than %s characters' % self.min_length

        if len(room_name) > self.max_length:
            return False, ErrorCodes.ROOM_NAME_TOO_LONG, \
                   'room name needs to be shorter than %s characters' % self.max_length

        return True, None, None
Example #25
0
    def ban_user(self, user_id: str, ban_info: dict):
        target_type = ban_info.get('type', '')
        target_id = ban_info.get('target', '')
        duration = ban_info.get('duration', '')
        reason = ban_info.get('reason', '')
        banner_id = ban_info.get('admin_id', '')

        try:
            user_name = ban_info['name']
            user_name = utils.b64d(user_name)
        except KeyError:
            logger.warning(
                'no name specified in ban info, if we have to create the user it will get the ID as name'
            )
            user_name = user_id

        try:
            self.user_manager.ban_user(user_id,
                                       target_id,
                                       duration,
                                       target_type,
                                       reason=reason,
                                       banner_id=banner_id,
                                       user_name=user_name)
        except ValueError as e:
            logger.error('invalid ban duration "%s" for user %s: %s' %
                         (duration, user_id, str(e)))
            self.env.capture_exception(sys.exc_info())
        except NoSuchUserException as e:
            logger.error('no such user %s: %s' % (user_id, str(e)))
            self.env.capture_exception(sys.exc_info())
        except UnknownBanTypeException as e:
            logger.error('unknown ban type "%s" for user %s: %s' %
                         (target_type, user_id, str(e)))
            self.env.capture_exception(sys.exc_info())
        except Exception as e:
            logger.error('could not ban user %s: %s' % (user_id, str(e)))
            logger.error(traceback.format_exc())
            self.env.capture_exception(sys.exc_info())
Example #26
0
    def _contains_blacklisted_word(self, activity: Activity):
        message = activity.object.content
        blacklist = self._get_black_list()

        if blacklist is None or len(blacklist) == 0:
            return None
        if message is not None and len(message) > 0:
            message = utils.b64d(message).lower()

        contains_forbidden_word = any(word in message for word in blacklist)

        if not contains_forbidden_word:
            return None

        for word in blacklist:
            if word not in message:
                continue

            logger.warning(
                'message from user %s used a blacklisted word "%s"' %
                (activity.actor.id, word))
            return word
        return None
Example #27
0
def rooms_for_channel(channel_uuid):
    form = CreateRoomForm(request.form)
    acl_form = CreateChannelAclForm(request.form)
    owner_form = AddOwnerForm(request.form)
    admin_form = AddAdminForm(request.form)

    acls = acl_manager.get_acls_channel(channel_uuid)
    acls_decoded = list()
    for acl in acls:
        acl['value'] = utils.b64d(acl['value'])
        acls_decoded.append(acl)

    return render_template(
        'rooms_in_channel.html',
        form=form,
        owner_form=owner_form,
        admin_form=admin_form,
        acl_form=acl_form,
        owners=channel_manager.get_owners(channel_uuid),
        admins=channel_manager.get_admins(channel_uuid),
        acls=acls_decoded,
        channel_uuid=channel_uuid,
        channel_name=channel_manager.name_for_uuid(channel_uuid),
        rooms=room_manager.get_rooms(channel_uuid))
Example #28
0
def users_for_room(channel_uuid, room_uuid):
    owner_form = AddOwnerForm(request.form)
    mod_form = AddModeratorForm(request.form)
    acl_form = CreateRoomAclForm(request.form)

    acls = acl_manager.get_acls_room(room_uuid)
    acls_decoded = list()
    for acl in acls:
        acl['value'] = utils.b64d(acl['value'])
        acls_decoded.append(acl)

    return render_template(
        'users_in_room.html',
        channel_uuid=channel_uuid,
        room_uuid=room_uuid,
        owner_form=owner_form,
        mod_form=mod_form,
        acl_form=acl_form,
        acls=acls_decoded,
        channel_name=channel_manager.name_for_uuid(channel_uuid),
        room_name=room_manager.name_for_uuid(room_uuid),
        owners=room_manager.get_owners(room_uuid),
        moderators=room_manager.get_moderators(room_uuid),
        users=user_manager.get_users_for_room(room_uuid))
Example #29
0
 def test_b64d_invalid(self):
     self.assertEqual('', utils.b64d('åäåö'))
Example #30
0
        def check_spam():
            def remove_emojis(text):
                return ''.join([
                    character for character in text
                    if character not in emoji.UNICODE_EMOJI
                ])

            def remove_custom_emojis(text):
                return re.sub(r':[a-z0-9]*:', '', text)

            def remove_multiple_consecutive_chars(text):
                return re.sub(r'(.)\1+', r'\1', text)

            def remove_numbers(text):
                return re.sub(r'\d', r'', text)

            def remove_special_chars(text):
                text = text.strip()
                text = text.replace('*', '')
                text = text.replace('+', '')
                text = text.replace('"', '')
                text = text.replace('_', '')
                text = text.replace('\'', '')
                text = text.replace('!', '')
                text = text.replace('-', '')
                text = text.replace('/', '')
                text = text.replace(';', '')
                text = text.replace('@', '')
                text = text.replace('$', '')
                text = text.replace('%', '')
                text = text.replace('&', '')
                text = text.replace(':', '')
                text = text.replace('<', '')
                text = text.replace('>', '')
                text = text.replace('(', '')
                text = text.replace(')', '')
                return text

            def replace_umlauts(text):
                text = text.replace('å', 'a')
                text = text.replace('ä', 'a')
                text = text.replace('ö', 'o')
                text = text.replace('ß', 's')
                text = text.replace('ü', 'u')
                return text

            _is_spam = False
            _spam_id = None
            _message = None

            spam_enabled = environ.env.config.get(ConfigKeys.SPAM_CLASSIFIER,
                                                  False)
            if not spam_enabled:
                return False, None

            try:
                _message = utils.b64d(activity.object.content)
                try:
                    json_body = json.loads(_message)
                    _message = json_body.get('text')
                except Exception:
                    pass  # ignore, use original
            except Exception as e:
                logger.error('could not decode message: {}'.format(str(e)))
                logger.exception(e)
                environ.env.capture_exception(sys.exc_info())
                return False, None

            if environ.env.service_config.ignore_emoji():
                try:
                    _message = remove_emojis(_message)
                    _message = remove_custom_emojis(_message)
                except Exception as e:
                    logger.error(
                        'could not check if text has emojis: {}'.format(
                            str(e)))
                    logger.exception(e)
                    environ.env.capture_exception(sys.exc_info())

            try:
                _message = remove_multiple_consecutive_chars(_message)
                _message = remove_special_chars(_message)
                _message = remove_numbers(_message)
                _message = replace_umlauts(_message)

                _is_spam, _y_hats = environ.env.spam.is_spam(_message)
                if _is_spam and environ.env.service_config.should_save_spam():
                    _spam_id = environ.env.db.save_spam_prediction(
                        activity, _message, _y_hats)
            except Exception as e:
                logger.error('could not predict spam: {}'.format(str(e)))
                logger.exception(e)
                environ.env.capture_exception(sys.exc_info())
                return False, None

            return _is_spam, _spam_id