Esempio n. 1
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')
Esempio n. 2
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)

        handlers: List[logging.Handler] = logging.getLogger().handlers
        if not handlers or len(handlers) > 1:
            return Response.build_message(message,
                                          'Cannot determine the logfile.')

        if not isinstance(handlers[0], logging.FileHandler):
            return Response.build_message(message, 'No logfile in use.')

        # Upload the logfile. (see https://zulip.com/api/upload-file)
        with open(handlers[0].baseFilename, 'rb') as lf:
            result: Dict[str, Any] = self.client.call_endpoint('user_uploads',
                                                               method='POST',
                                                               files=[lf])

        if result['result'] != 'success':
            return Response.build_message(message,
                                          'Could not upload the logfile.')

        return Response.build_message(message,
                                      '[logfile]({})'.format(result['uri']))
Esempio n. 3
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)
Esempio n. 4
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)
Esempio n. 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)

        # Ask the parent process to restart.
        os.kill(os.getpid(), signal.SIGUSR1)

        # dead code
        return Response.none()
Esempio n. 6
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)

        stream_regexes: Optional[List[Any]] = split(
            message['command'], converter=[validate_and_return_regex])
        if stream_regexes is None or None in stream_regexes:
            return Response.build_message(
                message, 'Found invalid regular expressions.')

        response: List[str] = []

        for stream_regex in stream_regexes:
            streams: List[str] = self.client.get_streams_from_regex(
                stream_regex)
            removed: int = 0

            for stream in streams:
                result: Dict[str, Any] = self.client.get_stream_id(stream)
                if result['result'] != 'success':
                    continue
                stream_id: int = result['stream_id']

                # Check if stream is empty.
                result = self.client.get_messages({
                    'anchor':
                    'oldest',
                    'num_before':
                    0,
                    'num_after':
                    1,
                    'narrow': [{
                        'operator': 'stream',
                        'operand': stream_id
                    }]
                })
                if result['result'] != 'success' or result['messages']:
                    continue

                # Archive the stream: https://zulip.com/help/archive-a-stream
                result = self.client.delete_stream(stream_id)
                if result['result'] == 'success':
                    removed += 1

            response.append('"%s" - found %d matching streams, removed %d' %
                            (stream_regex, len(streams), removed))

        return Response.build_message(message, '\n'.join(response))
Esempio n. 7
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)
Esempio n. 8
0
    def handle_message(self, message: Dict[str, Any],
                       **kwargs: Any) -> Union[Response, Iterable[Response]]:
        result_sql: List[Tuple[Any, ...]]

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

        try:
            if message['command'] == 'list':
                result_sql = self._db.execute(self._list_sql)
            else:
                result_sql = self._db.execute(message['command'])
        except Exception as e:
            return Response.build_message(message, str(e))

        result: str = '```text\n' + '\n'.join(map(str, result_sql)) + '\n```'

        return Response.build_message(message, result)
Esempio n. 9
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))
Esempio n. 10
0
    def handle_message(
        self,
        message: Dict[str, Any],
        **kwargs: Any
    ) -> Union[Response, Iterable[Response]]:
        result: Optional[Tuple[str, CommandParser.Opts, CommandParser.Args]]

        # 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 == 'subscribe':
            return self._subscribe(message['sender_id'], args.group_id, message)
        if command == 'unsubscribe':
            return self._unsubscribe(message['sender_id'], args.group_id, message)

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

        if command == 'list':
            return self._list(message)
        if command == 'announce':
            if message['type'] != 'stream':
                return Response.build_message(message, 'Claim only stream messages.')
            return self._announce(message)
        if command == 'unannounce':
            return self._unannounce(message, args.message_id)
        if command == 'claim':
            if message['type'] != 'stream':
                return Response.build_message(message, 'Claim only stream messages.')
            return self._claim(message, args.group_id)
        if command == 'unclaim':
            return self._unclaim(message, args.group_id, args.message_id)
        if command == 'add':
            return self._add(message, args.group_id, args.emoji)
        if command == 'remove':
            return self._remove(message, args.group_id)
        if command in ['add_streams', 'remove_streams']:
            return self._change_streams(message, args.group_id, command, args.streams)

        return Response.command_not_found(message)
Esempio n. 11
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)
Esempio n. 12
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')
Esempio n. 13
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)

        if 'stream_id' not in message:
            return Response.build_message(message, 'Error: Not a stream topic.')

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

        dest: str = ' '.join(args.dest)

        if opts.m is not None:
            return self._move_topic(message, dest, opts.m)
        return self._move_stream(message, dest)
Esempio n. 14
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)

        # Get the dirname of this file (which is located in the git repo).
        git_dir: Path = Path(__file__).parent.absolute()

        try:
            os.chdir(git_dir)
        except Exception as e:
            logging.exception(e)
            return Response.build_message(
                message,
                f'Cannot access the directory of my git repo {git_dir}. Please contact the admin.'
            )

        # Execute command and capture stdout and stderr into one stream (stdout).
        try:
            result: sp.CompletedProcess[Any] = sp.run(
                self._git_pull_cmd,
                stdout=sp.PIPE,
                stderr=sp.STDOUT,
                text=True,
                timeout=self._timeout,
            )
        except sp.TimeoutExpired:
            return Response.build_message(
                message,
                f'{self._git_pull_cmd} failed: timeout ({self._timeout} seconds) expired'
            )

        return Response.build_message(
            message,
            f'Return code: {result.returncode}\nOutput:\n```text\n{result.stdout}\n```'
        )