Exemple #1
0
    def handle_message(self, message: Dict[str, Any],
                       **kwargs: Any) -> Union[Response, Iterable[Response]]:
        result: Optional[Tuple[str, CommandParser.Opts, CommandParser.Args]]

        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        result = self.command_parser.parse(message['command'])
        if result is None:
            return Response.command_not_found(message)
        command, _, args = result

        if command == 'list':
            response: str = 'Key | Value\n ---- | ----'
            for key, value in self._db.execute(self._list_sql):
                response += f'\n{key} | {value}'
            return Response.build_message(message, response)

        if command == 'remove':
            self._db.execute(self._remove_sql, args.key, commit=True)
            return Response.ok(message)

        if command == 'set':
            try:
                self._db.execute(self._update_sql,
                                 args.key,
                                 args.value,
                                 commit=True)
            except Exception as e:
                logging.exception(e)
                return Response.build_message(message, 'Failed: %s' % str(e))
            return Response.ok(message)

        return Response.command_not_found(message)
Exemple #2
0
 def _unannounce(
     self,
     message: Dict[str, Any],
     message_id: str
 ) -> Union[Response, Iterable[Response]]:
     self._db.execute(self._unclaim_msg_for_all_sql, message_id, commit = True)
     return Response.ok(message)
Exemple #3
0
    def handle_message(
        self,
        message: Dict[str, Any],
        **kwargs: Any
    ) -> Union[Response, Iterable[Response]]:
        result: Optional[Tuple[str, CommandParser.Opts, CommandParser.Args]]
        result_sql: List[Tuple[Any, ...]]

        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        # Get command and parameters.
        result = self.command_parser.parse(message['command'])
        if result is None:
            return Response.command_not_found(message)
        command, _, args = result

        if command == 'list':
            result_sql = self._db.execute(self._list_sql)
            response: str = 'Alert word or phrase | Emoji\n---- | ----'
            for (phrase, emoji) in result_sql:
                response += '\n`{0}` | {1} :{1}:'.format(phrase, emoji)
            return Response.build_message(message, response)

        # Use lowercase -> no need for case insensitivity.
        alert_phrase: str = args.alert_phrase.lower()

        if command == 'add':
            # Add binding to database or update it.
            self._db.execute(self._update_sql, alert_phrase, args.emoji, commit = True)
        elif command == 'remove':
            self._db.execute(self._remove_sql, alert_phrase, commit = True)

        return Response.ok(message)
Exemple #4
0
    def subscribe_users(
        self, message: Dict[str, Any], dest_stream: str,
        users: List[Union[str, Tuple[str, Optional[int]]]]
    ) -> Union[Response, Iterable[Response]]:
        # First, get all the ids of the users whose ids we do not already know.
        user_ids: Optional[
            List[int]] = self.client.get_user_ids_from_display_names(
                map(
                    lambda o: o[0] if isinstance(o, tuple) else o,
                    filter(
                        lambda o: isinstance(o, str) or
                        (isinstance(o, tuple) and o[1] is None), users)))
        if user_ids is None:
            return Response.build_message(
                message, 'error: could not get the user ids.')

        user_ids.extend(
            map(
                lambda t: cast(int, t[1]),
                filter(
                    lambda o: isinstance(o, tuple) and isinstance(o[1], int),
                    users)))

        if not self.client.subscribe_users(user_ids, dest_stream):
            return Response.error(message)

        return Response.ok(message)
Exemple #5
0
    def handle_message(
        self,
        message: Dict[str, Any],
        **kwargs: Any
    ) -> Union[Response, Iterable[Response]]:
        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        failed: List[str] = []

        stream_tuples: Optional[List[Any]] = split(
            message['command'], converter = [lambda t: split(
                t, sep = ',', exact_split = 2, discard_empty = False
            )]
        )
        if stream_tuples is None or None in stream_tuples:
            return Response.error(message)

        for stream, desc in stream_tuples:
            if not stream:
                failed.append('one empty stream name')
                continue
            result: Dict[str, Any] = self.client.add_subscriptions(
                streams = [{'name': stream, 'description': desc}]
            )
            if result['result'] != 'success':
                failed.append(f'stream: {stream}, description: {desc}')

        if not failed:
            return Response.ok(message)

        response: str = 'Failed to create the following streams:\n' + '\n'.join(failed)

        return Response.build_message(message, response, msg_type = 'private')
Exemple #6
0
    def handle_message(
        self,
        message: Dict[str, Any],
        **kwargs: Any
    ) -> Union[Response, Iterable[Response]]:
        result: Optional[Tuple[str, CommandParser.Opts, CommandParser.Args]]
        result_sql: List[Tuple[Any, ...]]

        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        # Get command and parameters.
        result = self.command_parser.parse(message['command'])
        if result is None:
            return Response.command_not_found(message)
        command, _, args = result

        if command == 'list':
            response: str = '***List of Identifiers and Messages***\n'
            for (ident, text) in self._db.execute(self._list_sql):
                response += '\n--------\nTitle: **{}**\n{}'.format(ident, text)
            return Response.build_message(message, response)

        # Use lowercase -> no need for case insensitivity.
        ident = args.id.lower()

        if command == 'send':
            result_sql = self._db.execute(self._search_sql, ident)
            if not result_sql:
                return Response.command_not_found(message)
            # Remove requesting message.
            self.client.delete_message(message['id'])
            return Response.build_message(message, result_sql[0][0])

        if command == 'add':
            self._db.execute(self._update_sql, ident, args.text, commit = True)
            return Response.ok(message)

        if command == 'remove':
            self._db.execute(self._delete_sql, ident, commit = True)
            return Response.ok(message)

        return Response.command_not_found(message)
Exemple #7
0
    def _claim(
        self,
        message: Dict[str, Any],
        group_id: Optional[str],
    ) -> Union[Response, Iterable[Response]]:
        """Command `group claim [id]`."""
        if group_id:
            self._db.execute(self._claim_group_sql, message['id'], group_id, commit = True)
        else:
            self._db.execute(self._claim_all_sql, message['id'], commit = True)

        return Response.ok(message)
Exemple #8
0
 def _unsubscribe(
     self,
     user_id: int,
     group_id: str,
     message: Optional[Dict[str, Any]] = None
 ) -> Union[Response, Iterable[Response]]:
     """Unsubscribe a user from a group."""
     self._db.execute(self._unsubscribe_user_sql, user_id, group_id, commit = True)
     if message is not None:
         return Response.ok(message)
     return Response.build_message(
         message = None, content = f'Unsubscribed from group {group_id}.',
         msg_type = 'private', to = [user_id]
     )
Exemple #9
0
 def _unclaim(
     self,
     message: Dict[str, Any],
     group_id: str,
     message_id: str
 ) -> Union[Response, Iterable[Response]]:
     try:
         msg_id: int = int(message_id)
     except ValueError:
         return Response.build_message(message, f'{message_id} is not an integer.')
     self._db.execute(
         self._unclaim_msg_from_group_sql, msg_id, group_id, commit = True
     )
     return Response.ok(message)
Exemple #10
0
    def subscribe_user_emails(
            self, message: Dict[str, Any], dest_stream: str,
            user_emails: List[str]) -> Union[Response, Iterable[Response]]:
        user_ids: Optional[List[int]] = self.client.get_user_ids_from_emails(
            user_emails)
        if user_ids is None:
            return Response.build_message(
                message, 'error: could not get the user ids.')

        if not self.client.subscribe_users(
                user_ids, dest_stream, allow_private_streams=True):
            return Response.error(message)

        return Response.ok(message)
Exemple #11
0
    def _remove(
        self,
        message: Dict[str, Any],
        group_id: str,
    ) -> Union[Response, Iterable[Response]]:
        msg_success: bool = self._announcements_remove_group(group_id)

        self._db.execute(self._remove_sql, group_id, commit = True)

        if msg_success:
            return Response.ok(message)

        return Response.build_message(
            message, 'Group removed, but removal failed for some announcement messages.'
        )
Exemple #12
0
    def subscribe_all_users(
        self,
        message: Dict[str, Any],
        dest_stream: str,
    ) -> Union[Response, Iterable[Response]]:
        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        result: Dict[str, Any] = self.client.get_users()
        if result['result'] != 'success':
            return Response.error(message)
        user_ids: List[int] = [user['user_id'] for user in result['members']]

        if not self.client.subscribe_users(user_ids, dest_stream):
            return Response.error(message)

        return Response.ok(message)
Exemple #13
0
    def _subscribe(
        self,
        user_id: int,
        group_id: str,
        message: Optional[Dict[str, Any]] = None
    ) -> Union[Response, Iterable[Response]]:
        """Subscribe a user to a group."""
        msg: str

        try:
            self._db.execute(self._subscribe_user_sql, user_id, group_id, commit = True)
        except IntegrityError as e:
            logging.exception(e)
            # User already subscribed.
            msg = f'I think you are already subscribed to group {group_id}.'
            if message:
                return Response.build_message(message, msg)
            return Response.build_message(
                message = None, content = msg, msg_type = 'private', to = [user_id]
            )

        stream_regs: List[str] = []
        for (stream_regs_str,) in self._db.execute(self._get_streams_sql, group_id):
            if not stream_regs_str:
                continue
            stream_regs.extend(stream_regs_str.split('\n'))

        no_success: List[str] = self._subscribe_users_to_stream_regexes([user_id], stream_regs)

        if not no_success:
            if message is not None:
                return Response.ok(message)
            return Response.build_message(
                message = None, content = f'Subscribed to group {group_id}.',
                msg_type = 'private', to = [user_id]
            )

        msg = 'Failed to subscribe you to the following streams: %s.' % str(no_success)

        if message is not None:
            return Response.build_message(message, msg)
        # Write a private message to the user.
        return Response.build_message(
            message = None, content = msg, msg_type = 'private', to = [user_id]
        )
Exemple #14
0
    def _change_streams(
        self,
        message: Dict[str, Any],
        group_id: str,
        command: str,
        change_stream_regs: List[str]
    ) -> Union[Response, Iterable[Response]]:
        """Command `group (add_streams|remove_streams) <id> <stream>...`."""
        # Validate the regexes.
        for reg in change_stream_regs:
            try:
                re.compile(reg)
            except re.error as e:
                return Response.build_message(message, 'invalid regex: %s\n%s', reg, str(e))

        result_sql: List[Tuple[Any, ...]] = self._db.execute(
            self._get_streams_sql, group_id, commit = True
        )
        if not result_sql:
            return Response.build_message(message, f'Group {group_id} does not exist.')

        # Current stream patterns.
        stream_list: List[str] = result_sql[0][0].split('\n')
        # The string containing the new list of stream patterns (newline separated).
        # The patterns have to be non-empty.
        new_streams: str = '\n'.join(filter(
            bool,
            set(stream_list + change_stream_regs) if command == 'add_streams' else
            [s for s in stream_list if s not in change_stream_regs]
        ))

        try:
            self._db.execute(self._update_streams_sql, new_streams, group_id, commit = True)
        except Exception as e:
            logging.exception(e)
            return Response.build_message(message, str(e))

        # Subscribe the group subscribers to the new streams.
        self._subscribe_users_to_stream_regexes(
            self._get_group_subscribers([group_id]), change_stream_regs
        )

        return Response.ok(message)
Exemple #15
0
    def subscribe_streams(
            self, message: Dict[str, Any], dest_stream: str,
            streams: List[str]) -> Union[Response, Iterable[Response]]:
        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        failed: List[str] = []

        for stream in streams:
            if not self.client.subscribe_all_from_stream_to_stream(
                    stream, dest_stream, None):
                failed.append(stream)

        if not failed:
            return Response.ok(message)

        return Response.build_message(
            message,
            'Failed to subscribe the following streams:\n' + '\n'.join(failed))
Exemple #16
0
    def handle_message(
        self,
        message: Dict[str, Any],
        **kwargs: Any
    ) -> Union[Response, Iterable[Response]]:
        if not self.client.user_is_privileged(message['sender_id']):
            return Response.admin_err(message)

        failed: List[str] = []

        stream_tuples: Optional[List[Any]] = split(
            message['command'], converter = [lambda t: split(t, sep = ',', exact_split = 2)]
        )
        if stream_tuples is None or None in stream_tuples:
            return Response.error(message)

        for old, new in stream_tuples:
            # Used for error messages.
            line: str = f'{old} -> {new}'

            try:
                old_id: int = self.client.get_stream_id(old)['stream_id']
            except Exception as e:
                logging.exception(e)
                failed.append(line)
                continue

            result: Dict[str, Any] = self.client.update_stream(
                {'stream_id': old_id, 'new_name': '"{}"'.format(new)}
            )
            if result['result'] != 'success':
                failed.append(line)

        if not failed:
            return Response.ok(message)

        response: str = 'Failed to perform the following renamings:\n' + '\n'.join(failed)

        return Response.build_message(message, response, msg_type = 'private')
Exemple #17
0
    def _add(
        self,
        message: Dict[str, Any],
        group_id: str,
        emoji: str
    ) -> Union[Response, Iterable[Response]]:
        """Command `group add <id> <emoji>`."""
        if '\n' in group_id:
            return Response.build_message(message, 'The group id must not contain newlines.')

        try:
            self._db.execute(
                self._insert_sql, group_id, emoji, '', commit = True
            )
        except IntegrityError as e:
            return Response.build_message(message, str(e))

        # Update the announcement messages.
        if not self._announcements_add_group(group_id):
            return Response.build_message(
                message, 'Group added, but announcement failed for some messages.'
            )

        return Response.ok(message)