Exemple #1
0
    def load_extension(self, name, lib=None):
        """
        Reworked implementation of the default bot extension loader
        It works exactly the same unless you provide the lib argument manually
        That way you can import the lib before this method to check for
        errors in the file and pass the returned lib to this function if no errors were thrown.

        Args:
            name: str
                name of the extension
            lib: Module
                lib returned from `import_module`
        Raises:
            ClientException
                Raised when no setup function is found or when lib isn't a valid module
        """
        if lib is None:
            # Fall back to default implementation when lib isn't provided
            return self.bot.load_extension(name)

        if name in self.bot.extensions:
            return

        if not isinstance(lib, ModuleType):
            raise discord.ClientException("lib isn't a valid module")

        lib = import_module(name)
        if not hasattr(lib, 'setup'):
            del lib
            del sys.modules[name]
            raise discord.ClientException('extension does not have a setup function')

        lib.setup(self.bot)
        self.bot.extensions[name] = lib
Exemple #2
0
    def add_command(self, command):
        """Adds a :class:`.Command` or its superclasses into the internal list
        of commands.

        This is usually not called, instead the :meth:`~.GroupMixin.command` or
        :meth:`~.GroupMixin.group` shortcut decorators are used instead.

        Parameters
        -----------
        command
            The command to add.

        Raises
        -------
        :exc:`.ClientException`
            If the command is already registered.
        TypeError
            If the command passed is not a subclass of :class:`.Command`.
        """

        if not isinstance(command, Command):
            raise TypeError('The command passed must be a subclass of Command')

        if isinstance(self, Command):
            command.parent = self

        if command.name in self.all_commands:
            raise discord.ClientException('Command {0.name} is already registered.'.format(command))

        self.all_commands[command.name] = command
        for alias in command.aliases:
            if alias in self.all_commands:
                raise discord.ClientException('The alias {} is already an existing command or alias.'.format(alias))
            self.all_commands[alias] = command
Exemple #3
0
 def __init__(self,
              source,
              *,
              executable='ffmpeg',
              pipe=False,
              stderr=None,
              before_options=None,
              options=None):
     stdin = None if not pipe else source
     args = [executable]
     if isinstance(before_options, str):
         args.extend(shlex.split(before_options))
     args.append('-i')
     args.append('-' if pipe else source)
     args.extend(('-f', 's16le', '-ar', '48000', '-ac', '2', '-loglevel',
                  'warning'))
     if isinstance(options, str):
         args.extend(shlex.split(options))
     args.append('pipe:1')
     self._process = None
     try:
         self._process = subprocess.Popen(args,
                                          stdin=subprocess.PIPE,
                                          stdout=subprocess.PIPE,
                                          stderr=stderr)
         self._stdout = io.BytesIO(
             self._process.communicate(input=stdin)[0])
     except FileNotFoundError:
         raise discord.ClientException(executable +
                                       ' was not found.') from None
     except subprocess.SubprocessError as exc:
         raise discord.ClientException(
             'Popen failed: {0.__class__.__name__}: {0}'.format(
                 exc)) from exc
Exemple #4
0
    def setup_config_role(self, name, role_map, checks: Iterable = tuple()):
        logger.info("Setting up managed role from config: {}".format(name))
        logger.debug("With configuration: {!r}".format(role_map))

        group = role_map.get('group', tuple())
        kwargs = copy.deepcopy(role_map)

        # Recursively get the groups
        logger.debug("Finding group.")
        current_group = self.bot  # type: commands.GroupMixin
        for command_name in group:
            try:
                current_group = current_group.commands[command_name]
            except KeyError:
                logger.warning(
                    "Group '{}' does not exist: making dummy group.".format(
                        command_name))
                self._make_dummy_group(current_group, command_name)
            except AttributeError:
                raise discord.ClientException((
                    "Cannot group role management command: parent "
                    "command '{0.name}' is not a group").format(current_group))
        else:
            kwargs['group'] = current_group

        kwargs['checks'] = checks

        try:
            self.add_managed_role(role_name=name, **kwargs)
        except TypeError as e:
            raise discord.ClientException(
                "Configuration error for managed role '{}': {}".format(
                    name, e.args[0]))
Exemple #5
0
    async def _parse_arguments(self, ctx: Context):
        ctx.args = args = [ctx] if self.instance is None else [
            self.instance, ctx
        ]
        ctx.kwargs = kwargs = {}

        view = ctx.view
        iterator = iter(self.params.items())

        if self.instance is not None:
            # we have 'self' as the first parameter so just advance
            # the iterator and resume parsing
            try:
                next(iterator)
            except StopIteration:
                raise discord.ClientException(
                    f'Callback for {self.name} command is missing "self" parameter.'
                )

        # next we have the 'ctx' as the next parameter
        try:
            next(iterator)
        except StopIteration:
            raise discord.ClientException(
                f'Callback for {self.name} command is missing "ctx" parameter.'
            )

        for name, param in iterator:
            if param.kind == param.POSITIONAL_OR_KEYWORD:
                transformed = await self.transform(ctx, param)
                args.append(transformed)

            elif param.kind == param.KEYWORD_ONLY:
                # kwarg only param denotes "consume rest" semantics
                if self.rest_is_raw:
                    converter = self._get_converter(param)
                    argument = view.read_rest()
                    kwargs[name] = await self.do_conversion(
                        ctx, converter, argument)
                    break

                kwargs[name] = await self.transform(ctx, param)

            elif param.kind == param.VAR_POSITIONAL:
                while not view.eof:
                    try:
                        transformed = await self.transform(ctx, param)
                        args.append(transformed)
                    except RuntimeError:
                        break

        if not self.ignore_extra:
            if not view.eof:
                raise commands.TooManyArguments(
                    f'Too many arguments passed to {self.qualified_name}')
Exemple #6
0
    async def _parse_arguments(self, ctx):
        ctx.args = [ctx] if self.cog is None else [self.cog, ctx]
        ctx.kwargs = {}
        args = ctx.args
        kwargs = ctx.kwargs

        view = ctx.view
        iterator = iter(self.params.items())

        if self.cog is not None:
            # we have 'self' as the first parameter so just advance
            # the iterator and resume parsing
            try:
                next(iterator)
            except StopIteration:
                raise discord.ClientException(
                    f'Callback for {self.name} command is missing "self" parameter.'
                )

        # next we have the 'ctx' as the next parameter
        try:
            next(iterator)
        except StopIteration:
            raise discord.ClientException(
                f'Callback for {self.name} command is missing "ctx" parameter.'
            )

        for name, param in iterator:
            if param.kind == param.POSITIONAL_OR_KEYWORD:
                transformed = await self.transform(ctx, param)
                args.append(transformed)
            elif param.kind == param.KEYWORD_ONLY:
                # kwarg only param denotes "consume rest" semantics
                kwargs[
                    name] = await self.callback.__lightning_argparser__.parse_args(
                        ctx)
                break
            elif param.kind == param.VAR_POSITIONAL:
                if view.eof and self.require_var_positional:
                    raise commands.MissingRequiredArgument(param)
                while not view.eof:
                    try:
                        transformed = await self.transform(ctx, param)
                        args.append(transformed)
                    except RuntimeError:
                        break
            elif param.kind == param.VAR_KEYWORD:
                await self._parse_flag_args(ctx)
                break

        if not self.ignore_extra:
            if not view.eof:
                raise commands.TooManyArguments(
                    'Too many arguments passed to ' + self.qualified_name)
Exemple #7
0
    async def _parse_arguments(self, ctx):
        """
        .. Warning::
            Argument converting had to change a lot for reaction
            commands.

            No way to get input so there is **no conversion** or parsing done here
            unless it was invoked from a message.

            When :class:`.ReactionCommand` or :class:`.ReactionGroup` is invoked from
            a reaction, args will be filled with their default value or ``None``.
        """
        is_reaction = getattr(ctx, 'reaction_command', False)

        if is_reaction:
            ctx.args = [ctx] if self.cog is None else [self.cog, ctx]
            ctx.kwargs = {}
            args = ctx.args
            kwargs = ctx.kwargs
            iterator = iter(self.params.items())

            if self.cog is not None:
                # we have 'self' as the first parameter so just advance
                # the iterator and resume parsing
                try:
                    next(iterator)
                except StopIteration:
                    raise discord.ClientException(
                        f'Callback for {self.name} command is missing "self" parameter.'
                    )

            # next we have the 'ctx' as the next parameter
            try:
                next(iterator)
            except StopIteration:
                raise discord.ClientException(
                    f'Callback for {self.name} command is missing "ctx" parameter.'
                )

            for name, param in iterator:
                converter = get_converter(param)
                if hasattr(converter, '__commands_is_flag__'):
                    arg = (await converter._construct_default(ctx)
                           ) if converter._can_be_constructible() else None
                else:
                    arg = None if param.default is param.empty else param.default
                if param.kind == param.KEYWORD_ONLY:
                    kwargs[name] = arg
                else:
                    args.append(arg)
        else:
            await super()._parse_arguments(ctx)
Exemple #8
0
 async def register(self, ctx, *user_info):
     """Makes a request to register a new user to the database providing their username and spotify ID"""
     if len(user_info) < 2:
         print(f"Error using the command: too few arguments ({ctx.author})")
         raise discord.ClientException("Improper command usage")
     display_name = " ".join(user_info[:-1])
     user_id = user_info[-1]
     if user_id is None or display_name is None:
         await ctx.send(
             "--help\nProper usage:\n$register <spotify id> <display name>"
         )
         raise discord.ClientException("Improper command usage: too few arguments")
     confirm = self.db.register_new_user(user_id, display_name)
     print(f"User registration: {user_id} as {display_name}\n{confirm}")
     await ctx.send(f"User {display_name} now registered.")
Exemple #9
0
    def add_auto_response(self, auto_response):
        """Adds a :class:`extensions.core.AutoResponse` into the internal list
        of auto responses.

        Parameters
        -----------
        auto_response
            The auto response to add.
        Raises
        -------
        discord.ClientException
            If the auto response is already registered.
        TypeError
            If the auto response passed is not a subclass of
            :class:`extensions.core.AutoResponse`.
        """

        if not isinstance(auto_response, AutoResponse):
            raise TypeError(
                'The auto response passed must be a subclass of AutoResponse')

        if auto_response.name in self.auto_responses:
            raise discord.ClientException(
                'AutoResponse {0.name} is already registered.'.format(
                    auto_response))

        self.auto_responses.append(auto_response)
Exemple #10
0
    def __init__(self, bot: PikalaxBOT):
        super().__init__(bot)
        self.connections = {}
        self.load_opus()

        with open(os.devnull, 'w') as DEVNULL:
            for executable in ('ffmpeg', 'avconv'):
                try:
                    subprocess.run([executable, '-h'],
                                   stdout=DEVNULL,
                                   stderr=DEVNULL,
                                   check=True)
                except FileNotFoundError:
                    continue
                self.ffmpeg = executable
                self.__ffmpeg_options['executable'] = executable
                break
            else:
                raise discord.ClientException('ffmpeg or avconv not installed')

        self.executor = ThreadPoolExecutor()
        self.__ytdl_extractor = youtube_dl.YoutubeDL(
            self.__ytdl_format_options)
        self.timeout_tasks = {}
        self.yt_players = defaultdict(
            lambda: YouTubePlaylistHandler(self, loop=self.bot.loop))
Exemple #11
0
    def add_cog(self, cog):
        if not isinstance(cog, Cog):
            raise discord.ClientException(
                f'cog must be an instance of {Cog.__qualname__}')

        # cog aliases
        for alias in cog.__aliases__:
            if alias in self.cog_aliases:
                raise discord.ClientException(
                    f'"{alias}" already has a cog registered')
            self.cog_aliases[alias.lower()] = cog

        super().add_cog(cog)
        cog_name = cog.__class__.__name__
        self.cog_aliases[cog.__class__.name.lower()] = self.cogs[
            cog_name.lower()] = self.cogs.pop(cog_name)
Exemple #12
0
 def _spawn_process(self, args, **subprocess_kwargs):
     # Creation flags only work in Windows
     if sys.platform == "win32":
         subprocess_kwargs["creationflags"] = self.creationflags
     try:
         return subprocess.Popen(args, **subprocess_kwargs)
     except FileNotFoundError:
         if isinstance(args, str):
             executable = args.partition(" ")[0]
         else:
             executable = args[0]
         message = f"{executable} was not found."
         raise discord.ClientException(message) from None
     except subprocess.SubprocessError as exc:
         message = f"Popen failed: {type(exc).__name__}: {exc}"
         raise discord.ClientException(message) from exc
Exemple #13
0
    def __init__(self, command_prefix, formatter=None, description=None, pm_help=False, **options):
        super().__init__(**options)
        self.command_prefix = command_prefix
        self.extra_events = {}
        self.cogs = {}
        self.extensions = {}
        self._checks = []
        self._check_once = []
        self._before_invoke = None
        self._after_invoke = None
        self.description = inspect.cleandoc(description) if description else ''
        self.pm_help = pm_help
        self.owner_id = options.get('owner_id')
        self.command_not_found = options.pop('command_not_found', 'No command called "{}" found.')
        self.command_has_no_subcommands = options.pop('command_has_no_subcommands', 'Command {0.name} has no subcommands.')

        if options.pop('self_bot', False):
            self._skip_check = lambda x, y: x != y
        else:
            self._skip_check = lambda x, y: x == y

        self.help_attrs = options.pop('help_attrs', {})

        if 'name' not in self.help_attrs:
            self.help_attrs['name'] = 'help'

        if formatter is not None:
            if not isinstance(formatter, HelpFormatter):
                raise discord.ClientException('Formatter must be a subclass of HelpFormatter')
            self.formatter = formatter
        else:
            self.formatter = HelpFormatter()

        # pay no mind to this ugliness.
        self.command(**self.help_attrs)(_default_help_command)
Exemple #14
0
    def before_invoke(self, coro):
        """A decorator that registers a coroutine as a pre-invoke hook.

        A pre-invoke hook is called directly before the command is
        called. This makes it a useful function to set up database
        connections or any type of set up required.

        This pre-invoke hook takes a sole parameter, a :class:`.Context`.

        See :meth:`.Bot.before_invoke` for more info.

        Parameters
        -----------
        coro
            The coroutine to register as the pre-invoke hook.

        Raises
        -------
        :exc:`.ClientException`
            The coroutine is not actually a coroutine.
        """
        if not asyncio.iscoroutinefunction(coro):
            raise discord.ClientException(
                "The pre-invoke hook must be a coroutine.")

        self._before_invoke = coro
        return coro
Exemple #15
0
    async def load_extension(self, spec: ModuleSpec):
        name = spec.name.split(".")[-1]
        if name in self.extensions:
            raise discord.ClientException(f"there is already a package named {name} loaded")

        lib = spec.loader.load_module()
        if not hasattr(lib, "setup"):
            del lib
            raise discord.ClientException(f"extension {name} does not have a setup function")

        if asyncio.iscoroutinefunction(lib.setup):
            await lib.setup(self)
        else:
            lib.setup(self)

        self.extensions[name] = lib
Exemple #16
0
    def __init__(self,
                 command_prefix,
                 formatter=None,
                 description=None,
                 pm_help=False,
                 **options):
        super().__init__(**options)
        self.command_prefix = command_prefix
        self.extra_events = {}
        self.cogs = {}
        self.extensions = {}
        self.description = inspect.cleandoc(description) if description else ''
        self.pm_help = pm_help
        self.command_not_found = options.pop('command_not_found',
                                             'No command called "{}" found.')
        self.command_has_no_subcommands = options.pop(
            'command_has_no_subcommands',
            'Command {0.name} has no subcommands.')

        self.help_attrs = options.pop('help_attrs', {})
        self.help_attrs['pass_context'] = True

        if 'name' not in self.help_attrs:
            self.help_attrs['name'] = 'help'

        if formatter is not None:
            if not isinstance(formatter, HelpFormatter):
                raise discord.ClientException(
                    'Formatter must be a subclass of HelpFormatter')
            self.formatter = formatter
        else:
            self.formatter = HelpFormatter()

        # pay no mind to this ugliness.
        self.command(**self.help_attrs)(_default_help_command)
Exemple #17
0
    def add_listener(self, func, name=None):
        """The non decorator alternative to :meth:`listen`.

        Parameters
        -----------
        func : coroutine
            The extra event to listen to.
        name : Optional[str]
            The name of the command to use. Defaults to ``func.__name__``.

        Example
        --------

        .. code-block:: python

            async def on_ready(): pass
            async def my_message(message): pass

            bot.add_listener(on_ready)
            bot.add_listener(my_message, 'on_message')

        """
        name = func.__name__ if name is None else name

        if not asyncio.iscoroutinefunction(func):
            raise discord.ClientException('Listeners must be coroutines')

        if name in self.extra_events:
            self.extra_events[name].append(func)
        else:
            self.extra_events[name] = [func]
Exemple #18
0
    def after_invoke(self, coro):
        r"""A decorator that registers a coroutine as a post-invoke hook.

        A post-invoke hook is called directly after the command is
        called. This makes it a useful function to clean-up database
        connections or any type of clean up required.

        This post-invoke hook takes a sole parameter, a :class:`.Context`.

        .. note::

            Similar to :meth:`~.Bot.before_invoke`\, this is not called unless
            checks and argument parsing procedures succeed. This hook is,
            however, **always** called regardless of the internal command
            callback raising an error (i.e. :exc:`.CommandInvokeError`\).
            This makes it ideal for clean-up scenarios.

        Parameters
        -----------
        coro
            The coroutine to register as the post-invoke hook.

        Raises
        -------
        :exc:`.ClientException`
            The coroutine is not actually a coroutine.
        """
        if not asyncio.iscoroutinefunction(coro):
            raise discord.ClientException(
                'The post-invoke hook must be a coroutine.')

        self._after_invoke = coro
        return coro
Exemple #19
0
def load_ext(bot, name, settings):
    lib = importlib.import_module(name)
    if not hasattr(lib, 'setup'):
        print(lib)
        raise discord.ClientException('Extension does not have a setup function.')

    lib.setup(bot, settings)
Exemple #20
0
    async def spotify(self, ctx, *, query="Today's Top Hits"):
        """Searches Spotify's playlists to unpack (default: Today's Top Hits)"""
        message = await ctx.send(f"Searching Spotify for `{query}`...")
        playlist = SpotifyPlaylist.from_file(query, loop=self.bot.loop)

        await message.edit(
            content=
            f"Should I add `{playlist}` to the queue? Or would you prefer the playlist url?"
        )
        await message.add_reaction("\U00002705")  # check mark
        await message.add_reaction("\U0000274E")  # cross mark
        await message.add_reaction("\U0001F517")  # link emoji

        def check(reaction, user):
            """Checks whether the user reacted and whether the reaction was valid"""
            if user == ctx.author:
                return str(reaction) in ("\U00002705", "\U0000274E",
                                         "\U0001F517"
                                         )  # cross or check mark or link
            return False

        try:
            reaction, user = await self.bot.wait_for("reaction_add",
                                                     timeout=30.0,
                                                     check=check)
        except asyncio.TimeoutError:
            return await ctx.send("User failed to respond in 30 seconds")
        finally:
            await message.delete()

        if str(
                reaction
        ) == "\U0000274E":  # if the user reacted with a cross mark (i.e. no)
            return
        elif str(reaction
                 ) == "\U0001F517":  # if the user reacted with link (i.e. url)
            match = re.search(
                r"https:\/\/api\.spotify\.com\/v1\/users\/(.*?)\/playlists\/(.*?)$",
                playlist.data.get("href"))
            user_id, playlist_id = match.group(1), match.group(2)
            return await ctx.send(
                f"https://open.spotify.com/user/{user_id}/playlist/{playlist_id}"
            )

        if ctx.author.voice is None:
            raise discord.ClientException("You aren't in a voice channel")

        message = await ctx.send(f"Unpacking `{playlist}`...")

        state = self.get_voice_state(ctx.guild)
        await state.join_voice_channel(ctx.author.voice.channel)
        async for song in playlist.songs(
        ):  # unpack the songs into the queue as a batch job
            success = state.add_song_to_playlist(song,
                                                 context=ctx,
                                                 batch_job=True)
            if not success:
                break
        state.batch_job = False  # end the batch job
        await message.edit(content=f"`{playlist}` has been unpacked")
Exemple #21
0
 async def playlist(self, ctx, *playlist_info):
     """Finds a playlist by its name and its owner's username and sends its URL in an embedded message"""
     if len(playlist_info) < 2:
         await ctx.send(
             "--help\nCommand `playlist`:\n\tReturns a link to the spotify playlist requested.\nProper usage:\n\t`$playlist <display name> <playlist name>`"
         )
         raise discord.ClientException("Improper command usage")
     else:
         try:
             user, keyword = playlist_info
         except ValueError as e:
             await ctx.send(str(e))
             await ctx.send(
                 "--help\nCommand `playlist`:\n\tReturns a link to the spotify playlist requested.\nProper usage:\n\t`$playlist <display name> <playlist name>`"
             )
         (
             pl_id,
             url,
             pl_name,
         ) = self.get_user_playlist_by_keyword_and_display_name(user, keyword)
         print(f"Playlist found: {pl_id}")
         pl_embed = discord.Embed(Title=pl_name, description="Playlist request")
         pl_embed.add_field(name="Requested by", value=user, inline=True)
         pl_embed.add_field(name="Link", value=url, inline=True)
         await ctx.send(embed=pl_embed)
Exemple #22
0
    def before_invoke(self, coro):
        """A decorator that registers a coroutine as a pre-invoke hook.

        A pre-invoke hook is called directly before the command is
        called. This makes it a useful function to set up database
        connections or any type of set up required.

        This pre-invoke hook takes a sole parameter, a :class:`.Context`.

        .. note::

            The :meth:`~.Bot.before_invoke` and :meth:`~.Bot.after_invoke` hooks are
            only called if all checks and argument parsing procedures pass
            without error. If any check or argument parsing procedures fail
            then the hooks are not called.

        Parameters
        -----------
        coro
            The coroutine to register as the pre-invoke hook.

        Raises
        -------
        :exc:`.ClientException`
            The coroutine is not actually a coroutine.
        """
        if not asyncio.iscoroutinefunction(coro):
            raise discord.ClientException(
                'The pre-invoke hook must be a coroutine.')

        self._before_invoke = coro
        return coro
Exemple #23
0
 async def play_from_playlist(self, ctx, *request_info):
     """Fetches a list of the songs in a playlist, given it's name and its owner's name"""
     if len(request_info) != 2:
         await ctx.send(
             "--help\nCommand `play_from`:\n\tPlays songs from the spotify playlist requested.\nProper usage:\n\t`$play_from <display name> <playlist name>`"
         )
         raise discord.ClientException("Improper command usage")
     else:
         user, keyword = request_info
         (
             pl_id,
             url,
             npl_name,
         ) = self.get_user_playlist_by_keyword_and_display_name(user, keyword)
         tracks = self.playlist_items(
             pl_id,
             offset=0,
             fields="items.track.id,items.track.name,items.track.artists,total",
             additional_types=["track"],
         )
         pl_embed = discord.Embed(title="Results", description="Query Results")
         addToQueue = []
         for track in tracks["items"]:
             name = track["track"]["name"]
             artists = track["track"]["artists"]
             artists_name = " ".join(artist["name"] for artist in artists)
             addToQueue.append(f"{name} - {artists_name}")
         total = tracks["total"]
         await ctx.send(f"{total} tracks queued!")
         return addToQueue
Exemple #24
0
    def after_invoke(self, coro):
        """A decorator that registers a coroutine as a post-invoke hook.

        A post-invoke hook is called directly after the command is
        called. This makes it a useful function to clean-up database
        connections or any type of clean up required.

        This post-invoke hook takes a sole parameter, a :class:`.Context`.

        See :meth:`.Bot.after_invoke` for more info.

        Parameters
        -----------
        coro
            The coroutine to register as the post-invoke hook.

        Raises
        -------
        :exc:`.ClientException`
            The coroutine is not actually a coroutine.
        """
        if not asyncio.iscoroutinefunction(coro):
            raise discord.ClientException(
                'The error handler must be a coroutine.')

        self._after_invoke = coro
        return coro
Exemple #25
0
    def get_prefix(self, message):
        """|coro|
        Retrieves the prefix the bot is listening to
        with the message as a context.
        Parameters
        -----------
        message: :class:`discord.Message`
            The message context to get the prefix of.
        Raises
        --------
        :exc:`.ClientException`
            The prefix was invalid. This could be if the prefix
            function returned None, the prefix list returned no
            elements that aren't None, or the prefix string is
            empty.
        Returns
        --------s
        Union[List[str], str]
            A list of prefixes or a single prefix that the bot is
            listening for.
        """
        prefix = ret = self.command_prefix
        if callable(prefix):
            ret = prefix(self, message)
            if asyncio.iscoroutine(ret):
                ret = yield from ret

        if isinstance(ret, (list, tuple)):
            if not(isinstance(message.channel, discord.DMChannel)):
                ret = [p for p in ret if p]

        if not ret:
            raise discord.ClientException('invalid prefix (could be an empty string, empty list, or None)')

        return ret
Exemple #26
0
	def load_extension(self, name):
		"""Loads an extension.

		An extension is a python module that contains commands, cogs, or
		listeners.

		An extension must have a global function, ``setup`` defined as
		the entry point on what to do when the extension is loaded. This entry
		point must have a single argument, the ``bot``.

		Parameters
		------------
		name: str
			The extension name to load. It must be dot separated like
			regular Python imports if accessing a sub-module. e.g.
			``foo.test`` if you want to import ``foo/test.py``.

		Raises
		--------
		ClientException
			The extension does not have a setup function.
		ImportError
			The extension could not be imported.
		"""

		if name in self.extensions:
			return

		lib = importlib.import_module(name)
		if not hasattr(lib, 'setup'):
			del lib
			del sys.modules[name]
			raise discord.ClientException('extension does not have a setup function')

		lib.setup(self)
		self.extensions[name] = lib

		if name.startswith('salieri'):
			return

		try:
			cog_name = name[name.rfind('.')+1:]
			with open('%s/i18n/%s.sal' % (self.root_folder, cog_name)) as f:
				self.i18n[cog_name] = {}
				for row in f:
					if row == '\n':
						continue

					r = row[:-1].split('|')
					r[0], r[1] = r[0].strip(), r[1].strip()
					if r[0] == 'lang':
						language = r[1]
						self.i18n[cog_name][language] = {}
						continue

					self.i18n[cog_name][language][r[0]] = r[1]

		except:
			pass
Exemple #27
0
    def __init__(self, callback: TaskFunction, is_unique=True):
        if not asyncio.iscoroutinefunction(callback):
            raise discord.ClientException("Task callback must be a coroutine.")

        self.callback = callback
        self.is_unique = is_unique
        self.instance = None  # instance the last time this Task was accessed as a descriptor
        self.on_error = None  # type: Callable[[Exception, TaskInstance], Awaitable[None]]
        self.on_cancel = None  # type: Callable[[TaskInstance], Awaitable[None]]
Exemple #28
0
def update_project(wizard: ProjectWizard) -> Project:
    """ Update user's current active project with the passed data. """
    user = get_or_make_user(discord.Object(wizard.user_id))
    if not user.active_project:
        raise discord.ClientException(
            "Can't edit: you don't have an active (selected) project.")
    for k, v in wizard.items():
        if v is not None:
            setattr(user.active_project, k, v)
    return user.active_project
Exemple #29
0
    def load_extension(self, name):
        if name in self.extensions:
            return

        lib = importlib.import_module(name)
        if not hasattr(lib, 'setup'):
            raise discord.ClientException(
                'extension does not have a setup function')

        lib.setup(self)
        self.extensions[name] = lib
Exemple #30
0
    def _get_converter(self, param):
        converter = param.annotation
        if converter is param.empty:
            if param.default is not param.empty:
                converter = str if param.default is None else type(param.default)
            else:
                converter = str
        elif not inspect.isclass(type(converter)):
            raise discord.ClientException('Function annotation must be a type')

        return converter