Example #1
0
    async def on_message(self, message):
        # If no prefix is found discard the message
        if not message.content.startswith(settings.PREFIX):
            return

        # If the message has a prefix but no known command we
        # convert the message to an !i command before passing it
        view = StringView(message.content)
        view.skip_string(settings.PREFIX)
        invoker = view.get_word()

        for command in self.commands:
            if invoker == command.name:
                break
        else:
            message.content = message.content[:1] + "i " + message.content[1:]

        # if invoker not in self.commands.name:
        #     message.content = message.content[:1] + "i " + message.content[1:]

        # Allow for the command to be processed by discord.py
        await self.process_commands(message)

        # Put the message in the database queue to be stored
        self.db.add_message_to_queue(message)
Example #2
0
def argsplit(args):
    view = StringView(args)
    args = []
    while not view.eof:
        view.skip_ws()
        args.append(quoted_word(view))
    return args
Example #3
0
    def handle_alias_arguments(self, command, message):
        """Takes an alias name, alias value, and message and handles percent-encoded args.
        Returns: string"""
        rawargs = " ".join(self.bot.prefix.join(message.content.split(self.bot.prefix)[1:]).split(' ')[1:])
        view = StringView(rawargs)
        args = []
        while not view.eof:
            view.skip_ws()
            args.append(quoted_word(view))
        tempargs = args[:]
        new_command = command
        if '%*%' in command:
            new_command = new_command.replace('%*%', shlex.quote(rawargs) if ' ' in rawargs else rawargs)
            tempargs = []
        if '&*&' in command:
            new_command = new_command.replace('&*&', rawargs.replace("\"", "\\\"").replace("'", "\\'"))
            tempargs = []
        for index, value in enumerate(args):
            key = '%{}%'.format(index + 1)
            to_remove = False
            if key in command:
                new_command = new_command.replace(key, shlex.quote(value) if ' ' in value else value)
                to_remove = True
            key = '&{}&'.format(index + 1)
            if key in command:
                new_command = new_command.replace(key, value.replace("\"", "\\\"").replace("'", "\\'"))
                to_remove = True
            if to_remove:
                try:
                    tempargs.remove(value)
                except ValueError:
                    pass

        return self.bot.prefix + new_command + " " + ' '.join((shlex.quote(v) if ' ' in v else v) for v in tempargs)
Example #4
0
 def get_extra_args_from_alias(
     self, message: discord.Message, prefix: str, alias: AliasEntry
 ) -> str:
     """
     When an alias is executed by a user in chat this function tries
         to get any extra arguments passed in with the call.
         Whitespace will be trimmed from both ends.
     :param message: 
     :param prefix: 
     :param alias: 
     :return: 
     """
     known_content_length = len(prefix) + len(alias.name)
     extra = message.content[known_content_length:]
     view = StringView(extra)
     view.skip_ws()
     extra = []
     while not view.eof:
         prev = view.index
         word = quoted_word(view)
         if len(word) < view.index - prev:
             word = "".join((view.buffer[prev], word, view.buffer[view.index - 1]))
         extra.append(word)
         view.skip_ws()
     return extra
Example #5
0
            async def parse_line(line):
                '''
                Parses a macro line
                '''
                if line.split()[0].lower() == "wait":
                    if print_queue:
                        # We have run into a wait, send ze messages
                        await message.channel.send("\n".join(print_queue))
                        print_queue.clear()
                    await asyncio.sleep(float(line.split()[1]))
                else:
                    # Trick discord.py into parsing this command as a bot
                    view = StringView(line)
                    ctx = commands.Context(prefix=bot_prefix,
                                           view=view,
                                           bot=bot,
                                           message=message)
                    invoker = view.get_word()
                    ctx.invoked_with = invoker

                    if (command := bot.all_commands.get(invoker)
                        ) and not command.qualified_name == "macro":
                        if print_queue:
                            # We have run into a command, send ze messages
                            await ctx.send("\n".join(print_queue))
                            print_queue.clear()

                        # invoke a command
                        ctx.command = command
                        await bot.invoke(ctx)
                    else:
Example #6
0
    async def newcontributors_interactive(self, ctx: GuildContext) -> None:
        """Interactively add contributors.

        Integrates with Red.
        """
        member_converter = commands.MemberConverter()
        pending_contributors = await self.__config.pending_contributors()
        new_added_contributors = {}

        early_exit = False
        for user_id, author_data in pending_contributors.items():
            discord_user_id_line = (
                f"**Discord user ID:** {discord_user_id}\n"
                if (discord_user_id := author_data.get('discord_user_id')) is not None
                else ""
            )
            bot_msg = await ctx.send(
                f"**GitHub Username:** {author_data['username']}\n"
                f"**Commit author name:** {author_data['name']}\n"
                f"**Commit author email:** {author_data['email']}\n"
                f"{discord_user_id_line}"
                "Use Red's `?assign` command or send user ID to add contributor."
                " Type `exit` to finish, use `skip` to skip the contributor."
            )

            while not early_exit:
                user_msg = await self.bot.wait_for(
                    "message_without_command", check=MessagePredicate.same_context(ctx)
                )
                content = user_msg.content

                if content == "exit":
                    early_exit = True
                    continue
                if content == "skip":
                    break

                if user_msg.content.startswith("?assign "):
                    view = StringView(user_msg.content)
                    view.skip_string("?assign ")
                    content = view.get_quoted_word()

                try:
                    member = await member_converter.convert(ctx, content)
                except commands.BadArgument as e:
                    await ctx.send(
                        f"{e}. Please try passing user ID"
                        " or using `?assign` command again."
                    )
                    continue

                author_data["discord_user_id"] = member.id
                new_added_contributors[user_id] = author_data
                break
            else:
                # early-exit by breaking out of for loop
                await safe_delete_message(bot_msg)
                break

            await safe_delete_message(bot_msg)
Example #7
0
    async def get_context(self, message, *, cls=cmd.Context):
        # This function is called internally by discord.py.
        # We have to fiddle with it because we are using a dynamic prefix (our mention string),
        # as well as no prefix inside of DMs.
        # The included prefix matching functions could not deal with this case.
        # If it ever becomes possible, we should probably switch to that.

        # Frankly, I don't really remember what I did here, but it might be good
        # to periodically check the get_context method on the base class and
        # port over any changes that happened there. ~hmry (2019-08-14, 02:25)

        if self.command_regex is None:
            return cls(prefix=None, view=None, bot=self, message=message)

        cmd_regex = self.command_dms_regex if message.guild is None else self.command_regex
        match = cmd_regex.match(message.content)

        if not match:
            return cls(prefix=None, view=None, bot=self, message=message)

        view = StringView(match.group(1).strip())
        ctx = cls(prefix=None, view=view, bot=self, message=message)

        if self._skip_check(message.author.id, self.user.id):
            return ctx

        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.command = self.all_commands.get(invoker)
        return ctx
Example #8
0
    async def get_context(self, message, *, cls=commands.Context):
        """
        Returns the invocation context from the message.
        Supports getting the prefix from database.
        """

        view = StringView(message.content)
        ctx = cls(prefix=self.prefix, view=view, bot=self, message=message)

        if self._skip_check(message.author.id, self.user.id):
            return ctx

        ctx.thread = await self.threads.find(channel=ctx.channel)

        prefixes = await self.get_prefix()

        invoked_prefix = discord.utils.find(view.skip_string, prefixes)
        if invoked_prefix is None:
            return ctx

        invoker = view.get_word().lower()

        ctx.invoked_with = invoker
        ctx.command = self.all_commands.get(invoker)

        return ctx
Example #9
0
    async def on_thread_ready(self, thread, creator, category,
                              initial_message):
        """Sends out menu to user"""
        menu_config = await self.db.find_one({'_id': 'config'})
        if menu_config:
            message = DummyMessage(copy.copy(initial_message))
            message.author = self.bot.modmail_guild.me
            message.content = menu_config['content']
            #genesis = thread.recipient_genesis_message
            #await genesis.delete()
            msg, _ = await thread.reply(message, anonymous=True)
            for r in menu_config['options']:
                await msg.add_reaction(r)
                await asyncio.sleep(0.3)

            try:
                reaction, _ = await self.bot.wait_for(
                    'reaction_add',
                    check=lambda r, u: r.message == msg and u == thread.
                    recipient and str(r.emoji) in menu_config['options'],
                    timeout=120)
            except asyncio.TimeoutError:
                message.content = 'No reaction recieved in menu... timing out'
                await thread.reply(message)
            else:
                alias = menu_config['options'][str(reaction.emoji)]

                ctxs = []
                if alias is not None:
                    ctxs = []
                    aliases = normalize_alias(alias)
                    for alias in aliases:
                        view = StringView(self.bot.prefix + alias)
                        ctx_ = commands.Context(prefix=self.bot.prefix,
                                                view=view,
                                                bot=self.bot,
                                                message=message)
                        ctx_.thread = thread
                        discord.utils.find(view.skip_string, await
                                           self.bot.get_prefix())
                        ctx_.invoked_with = view.get_word().lower()
                        ctx_.command = self.bot.all_commands.get(
                            ctx_.invoked_with)
                        ctxs += [ctx_]

                for ctx in ctxs:
                    if ctx.command:
                        old_checks = copy.copy(ctx.command.checks)
                        ctx.command.checks = [
                            checks.has_permissions(PermissionLevel.INVALID)
                        ]

                        await self.bot.invoke(ctx)

                        ctx.command.checks = old_checks
                        continue
Example #10
0
    async def invoke(self, ctx: SlashContext, command):
        view = StringView(command)
        m = await ctx.send(f"Invoking command `{command}`...")
        ctx = commands.Context(
            prefix=bot_prefix, view=view, bot=self.bot, message=m)
        invoker = view.get_word()
        ctx.invoked_with = invoker

        if command := self.bot.all_commands.get(invoker):
            # invoke a command
            ctx.command = command
            await self.bot.invoke(ctx)
Example #11
0
    async def get_context(self, message, prefixes=None):
        prefixes = prefixes or await get_prefix(self.bot, message)
        for prefix in prefixes:
            message_regex = re.compile(
                rf"^({re.escape(prefix)})[\s]*(\w+)(.*)", re.I | re.X | re.S)
            match = message_regex.findall(message.content)
            if match:
                break
        if not match:
            return
        match = match[0]
        invoked_prefix = match[0].lower()
        message.content = f"{invoked_prefix}{match[1].lower()}{match[2]}"
        #piping operator
        pop = self.pop
        piped = pop in message.content
        if self.piping_enabled:
            if piped:
                #edge case for prefix being piping operator
                if invoked_prefix == pop:
                    split = message.content.rsplit(
                        pop,
                        message.content.count(pop) - 1)
                else:
                    split = message.content.split(pop)

                #modify message content to first command invoked
                if not split[-1].strip():
                    piped = False
                else:
                    piped = await self.process_piping(split, message.author,
                                                      invoked_prefix)
                    if piped:
                        message.content = split[0]

        view = StringView(message.content)
        ctx = NotSoContext(prefix=None,
                           view=view,
                           bot=self.bot,
                           message=message)
        view.skip_string(invoked_prefix)
        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.prefix = invoked_prefix
        command = self.bot.all_commands.get(invoker)
        if command is None:
            return
        ctx.command = command
        if piped:
            ctx.piped = piped
            ctx.cached = []
        return ctx, piped
Example #12
0
 async def convert(self, ctx, argument):
     converted = []
     view = StringView(argument)
     while not view.eof:
         args = []
         for converter in self.converters:
             view.skip_ws()
             arg = view.get_quoted_word()
             if arg is None:
                 raise commands.UserInputError(_('Not enough arguments.'))
             args.append(await self._do_conversion(ctx, converter, arg))
         converted.append(tuple(args))
     return converted
    def process_commands(self, message):

        _internal_channel = message.channel
        _internal_author = message.author

        view = StringView(message.content)
        if message.author == self.user:
            return

        prefix = self._get_prefix(message)
        invoked_prefix = prefix
        
        if not isinstance(prefix, (tuple, list)):
            if not view.skip_string(prefix):
                
                ## procesar emoticons
                yield from self.process_emotes(message, view)
                return
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return

        invoker = view.get_word()
        tmp = {
            'bot': self,
            'invoked_with': invoker,
            'message': message,
            'view': view,
            'prefix': invoked_prefix
        }
        ctx = Context(**tmp)
        del tmp

        if invoker in self.commands:
            command = self.commands[invoker]
            self.dispatch('command', command, ctx)
            ctx.command = command
            yield from command.invoke(ctx)
            self.dispatch('command_completion', command, ctx)
        else:
            exc = CommandNotFound('Command "{}" is not found'.format(invoker))
            self.dispatch('command_error', exc, ctx)

            ## Invocar Kaomojis si existen
            key = invoker
            if(key not in kaomoji.keys()):
                key = random.sample(kaomoji.keys(), 1)[0]
            kao = random.sample(kaomoji[key],1)[0]
            yield from self.say(kao)
Example #14
0
async def parse(bot, msg, view: StringView, command: str, discord: bool):
    args = []
    v = view.get_quoted_word()
    while v:
        args.append(v.strip())
        v = view.get_quoted_word()

    if discord:
        try:
            await parse_discord_specifics(bot, msg, command, args)
        except Exception as e:
            ctx = await bot.get_context(msg)
            await ctx.paginate("".join(
                traceback.format_exception(type(e), e, e.__traceback__)))
    return None
Example #15
0
async def special_get_context(bot, message, cmdtext, *, cls=Context):
    # similar to bot's get_context method but use cmdtext rather than message.content to build the context
    # notice that if any command uses context.message.content be behavior may not be as expected
    # the documentation can be viewed in discord.ext.commands -> bot.get_context

    view = StringView(cmdtext)
    ctx = cls(prefix=None, view=view, bot=bot, message=message)

    if bot._skip_check(message.author.id, bot.user.id):
        return ctx

    prefix = await bot.get_prefix(message)
    invoked_prefix = prefix

    if isinstance(prefix, str):
        if not view.skip_string(prefix):
            return ctx
    else:
        try:
            # if the context class' __init__ consumes something from the view this
            # will be wrong.  That seems unreasonable though.
            if cmdtext.startswith(tuple(prefix)):
                invoked_prefix = discord.utils.find(view.skip_string, prefix)
            else:
                return ctx

        except TypeError:
            if not isinstance(prefix, list):
                raise TypeError(
                    "get_prefix must return either a string or a list of string, "
                    "not {}".format(prefix.__class__.__name__))

            # It's possible a bad command_prefix got us here.
            for value in prefix:
                if not isinstance(value, str):
                    raise TypeError(
                        "Iterable command_prefix or list returned from get_prefix must "
                        "contain only strings, not {}".format(
                            value.__class__.__name__))

            # Getting here shouldn't happen
            raise

    invoker = view.get_word()
    ctx.invoked_with = invoker
    ctx.prefix = invoked_prefix
    ctx.command = bot.all_commands.get(invoker)
    return ctx
Example #16
0
async def autoscan(ctx):
    if ctx.guild and not sql.guild_is_autoscan(ctx.guild.id):
        return

    canvas = sql.guild_get_canvas_by_id(
        ctx.guild.id) if ctx.guild else "pixelcanvas"

    cmd = None
    view = ""
    m_pc = re.search('pixelcanvas\.io/@(-?\d+),(-?\d+)(?:(?: |#| #)(-?\d+))?',
                     ctx.message.content)
    m_pzi = re.search('pixelz\.io/@(-?\d+),(-?\d+)(?:(?: |#| #)(-?\d+))?',
                      ctx.message.content)
    m_pz = re.search(
        'pixelzone\.io/\?p=(-?\d+),(-?\d+)(?:,(\d+))?(?:(?: |#| #)(-?\d+))?',
        ctx.message.content)
    m_ps = re.search(
        'pxls\.space/#x=(\d+)&y=(\d+)(?:&scale=(\d+))?(?:(?: |#| #)(-?\d+))?',
        ctx.message.content)
    m_pre_def = re.search('@(-?\d+)(?: |,|, )(-?\d+)(?:(?: |#| #)(-?\d+))?',
                          ctx.message.content)
    m_dif_def = re.search(
        '(?:(-l) )?(-?\d+)(?: |,|, )(-?\d+)(?:(?: |#| #)(-?\d+))?',
        ctx.message.content)
    if m_pc:
        cmd = dget(dget(ctx.bot.commands, name='preview').commands,
                   name='pixelcanvas')
        view = ' '.join(m_pc.groups(default='1'))
    elif m_pzi:
        cmd = dget(dget(ctx.bot.commands, name='preview').commands,
                   name='pixelzio')
        view = ' '.join(m_pzi.groups(default='1'))
    elif m_pz:
        cmd = dget(dget(ctx.bot.commands, name='preview').commands,
                   name='pixelzone')
        view = '{} {} {}'.format(m_pz.group(1), m_pz.group(2),
                                 m_pz.group(4) or m_pz.group(3) or '1')
    elif m_ps:
        cmd = dget(dget(ctx.bot.commands, name='preview').commands,
                   name='pxlsspace')
        view = '{} {} {}'.format(m_ps.group(1), m_ps.group(2),
                                 m_ps.group(4) or m_ps.group(3) or '1')
    elif m_pre_def:
        cmd = dget(dget(ctx.bot.commands, name='preview').commands,
                   name=canvas)
        view = ' '.join(m_pre_def.groups(default='1'))
    elif m_dif_def and len(
            ctx.message.attachments
    ) > 0 and ctx.message.attachments[0].filename[-4:].lower() == ".png":
        cmd = dget(dget(ctx.bot.commands, name='diff').commands, name=canvas)
        view = '{} {} {} {}'.format(
            m_dif_def.group(1) or "", m_dif_def.group(2), m_dif_def.group(3),
            m_dif_def.group(4) or 1)

    if cmd:
        ctx.command = cmd
        ctx.view = StringView(view)
        ctx.is_autoscan = True
        await ctx.bot.invoke(ctx)
        return True
Example #17
0
    async def get_contexts(self, message, *, cls=commands.Context):
        """
        Returns all invocation contexts from the message.
        Supports getting the prefix from database as well as command aliases.
        """

        view = StringView(message.content)
        ctx = cls(prefix=self.prefix, view=view, bot=self, message=message)
        ctx.thread = await self.threads.find(channel=ctx.channel)

        if self._skip_check(message.author.id, self.user.id):
            return [ctx]

        prefixes = await self.get_prefix()

        invoked_prefix = discord.utils.find(view.skip_string, prefixes)
        if invoked_prefix is None:
            return [ctx]

        invoker = view.get_word().lower()

        # Check if there is any aliases being called.
        alias = self.aliases.get(invoker)
        if alias is not None:
            aliases = parse_alias(alias)
            if not aliases:
                logger.warning("Alias %s is invalid, removing.", invoker)
                self.aliases.pop(invoker)
            else:
                len_ = len(f"{invoked_prefix}{invoker}")
                contents = parse_alias(message.content[len_:])
                if not contents:
                    contents = [message.content[len_:]]

                ctxs = []
                for alias, content in zip_longest(aliases, contents):
                    if alias is None:
                        break
                    ctx = cls(prefix=self.prefix,
                              view=view,
                              bot=self,
                              message=message)
                    ctx.thread = await self.threads.find(channel=ctx.channel)

                    if content is not None:
                        view = StringView(f"{alias} {content.strip()}")
                    else:
                        view = StringView(alias)
                    ctx.view = view
                    ctx.invoked_with = view.get_word()
                    ctx.command = self.all_commands.get(ctx.invoked_with)
                    ctxs += [ctx]
                return ctxs

        ctx.invoked_with = invoker
        ctx.command = self.all_commands.get(invoker)
        return [ctx]
Example #18
0
 def get_extra_args_from_alias(self, message: discord.Message,
                               prefix: str) -> str:
     """
     When an alias is executed by a user in chat this function tries
         to get any extra arguments passed in with the call.
         Whitespace will be trimmed from both ends.
     :param message:
     :param prefix:
     :param alias:
     :return:
     """
     known_content_length = len(prefix) + len(self.name)
     extra = message.content[known_content_length:]
     view = StringView(extra)
     view.skip_ws()
     extra = []
     while not view.eof:
         prev = view.index
         word = view.get_quoted_word()
         if len(word) < view.index - prev:
             word = "".join(
                 (view.buffer[prev], word, view.buffer[view.index - 1]))
         extra.append(word)
         view.skip_ws()
     return extra
Example #19
0
    async def get_context(self,
                          message,
                          *,
                          cls=TwitchContext) -> TwitchContext:
        view = StringView(message.content)
        ctx = cls(prefix=None, view=view, bot=self, message=message)

        prefix = await self._get_prefixes(message)
        invoked_prefix = prefix

        if isinstance(prefix, str):
            if not view.skip_string(prefix):
                return ctx
        else:
            try:
                # if the context class' __init__ consumes something from the view this
                # will be wrong.  That seems unreasonable though.
                if message.content.startswith(tuple(prefix)):
                    invoked_prefix = discord.utils.find(
                        view.skip_string, prefix)
                else:
                    return ctx

            except TypeError:
                if not isinstance(prefix, list):
                    raise TypeError(
                        "get_prefix must return either a string or a list of string, "
                        "not {}".format(prefix.__class__.__name__))

                # It's possible a bad command_prefix got us here.
                for value in prefix:
                    if not isinstance(value, str):
                        raise TypeError(
                            "Iterable command_prefix or list returned from get_prefix must "
                            "contain only strings, not {}".format(
                                value.__class__.__name__))

                # Getting here shouldn't happen
                raise

        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.prefix = invoked_prefix
        ctx.command = self.all_commands.get(invoker)
        return ctx
Example #20
0
    async def on_message(self, message):
        if message.author.bot or not self.bot.is_running():
            return
        if message.content.startswith(self.bot.command_prefix):
            name = message.content.lower().lstrip(self.bot.command_prefix).split(" ")[0]
            if command := config.Commands.fetch(name):
                attachment = None
                if command["attachment"]:
                    async with aiohttp.ClientSession() as session:
                        async with session.get(command["attachment"]) as resp:
                            buff = io.BytesIO(await resp.read())
                            attachment = discord.File(filename=command["attachment"].split("/")[-1], fp=buff)
                args = []
                view = StringView(message.content.lstrip(self.bot.command_prefix))
                view.get_word()  # command name
                while not view.eof:
                    view.skip_ws()
                    args.append(view.get_quoted_word())

                text = re.sub(
                    r'{(\d+)}',
                    lambda match: args[int(match.group(1))]
                    if int(match.group(1)) < len(args)
                    else '(missing argument)',
                    command["content"]
                ).replace('{...}', ' '.join(args))

                await self.bot.reply_to_msg(message, text, file=attachment)
                return
Example #21
0
    def add_message(self, message):
        view = StringView(message.content)
        command = view.get_word()

        request_data = {
            "message_id": str(message.id),
            "user_id": str(message.author.id),
            "server_id": str(message.guild.id),
            "command": command,
            "content": view.read_rest(),
            "timestamp": message.created_at
        }

        try:
            self.firestore.collection('requests').add(
                document_data=request_data, document_id=str(message.id))
        except Conflict as e:
            logger.error(e)
Example #22
0
    async def get_context(self, message, *, cls=Context):
        view = StringView(message.content)
        ctx = cls(prefix=None, view=view, bot=self, message=message)

        if self._skip_check(message.author.id, self.user.id):
            return ctx

        prefix = await self.get_prefix(message)
        invoked_prefix = prefix

        if isinstance(prefix, str):
            if not view.skip_string(prefix):
                return ctx
        elif isinstance(prefix, list) \
                and any([isinstance(p, list) for p in prefix]):
            # Regex time
            for p in prefix:
                if isinstance(p, list):
                    if p[1]:
                        # regex prefix parsing
                        reg = re.match(p[0], message.content)
                        if reg:

                            if message.content == reg.groups()[0]:
                                # ignore * prefixes
                                continue

                            # Matches, this is the prefix
                            invoked_prefix = p

                            # redo the string view with the capture group
                            view = StringView(reg.groups()[0])

                            invoker = view.get_word()
                            ctx.invoked_with = invoker
                            ctx.prefix = invoked_prefix
                            ctx.command = self.all_commands.get(invoker)
                            ctx.view = view
                            return ctx
                    else:
                        # regex has highest priority or something idk
                        # what I'm doing help
                        continue

            # No prefix found, use the branch below
            prefix = [p[0] for p in prefix if not p[1]]
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return ctx
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return ctx

        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.prefix = invoked_prefix
        ctx.command = self.all_commands.get(invoker)
        return ctx
Example #23
0
    async def get_context(self, message, *, cls=Context):
        view = StringView(message.content)
        ctx = cls(prefix=None, view=view, bot=self, message=message)

        if self._skip_check((await message.author()).id, (await
                                                          self.user()).id):
            return ctx

        prefix = await self.get_prefix(message)
        invoked_prefix = prefix

        if isinstance(prefix, str):
            if not view.skip_string(prefix):
                return ctx
        else:
            try:
                if message.content.startswith(tuple(prefix)):
                    invoked_prefix = utils.find(view.skip_string, prefix)
                else:
                    return ctx

            except TypeError:
                if not isinstance(prefix, list):
                    raise TypeError(
                        "get_prefix must return either a string or a list of string, "
                        "not {}".format(prefix.__class__.__name__))

                for value in prefix:
                    if not isinstance(value, str):
                        raise TypeError(
                            "Iterable command_prefix or list returned from get_prefix must "
                            "contain only strings, not {}".format(
                                value.__class__.__name__))

                raise

        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.prefix = invoked_prefix
        ctx.command = self.all_commands.get(invoker)
        return ctx
Example #24
0
    async def process_commands(self, message):
        """
        Override of process_commands to use our own context.
        """
        _internal_channel = message.channel
        _internal_author = message.author

        view = StringView(message.content)
        if self._skip_check(message.author, self.user):
            return

        prefix = await self._get_prefix(message)
        invoked_prefix = prefix

        if not isinstance(prefix, (tuple, list)):
            if not view.skip_string(prefix):
                return
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return

        invoker = view.get_word()
        tmp = {
            'bot': self,
            'invoked_with': invoker,
            'message': message,
            'view': view,
            'prefix': invoked_prefix
        }
        ctx = Context(**tmp)
        del tmp

        if invoker in self.commands:
            command = self.commands[invoker]
            self.dispatch('command', command, ctx)
            await command.invoke(ctx)
            self.dispatch('command_completion', command, ctx)
        elif invoker:
            exc = CommandNotFound('Command "{}" is not found'.format(invoker))
            self.dispatch('command_error', exc, ctx)
Example #25
0
    async def event_message(self, message: twitchio.Message):
        if not message.channel:
            return

        if message.channel.name != self.bot._ws.nick:
            return

        prefixes = await self.bot._get_prefixes(message)
        view = StringView(message.content)
        if isinstance(prefixes, list):
            for prefix in prefixes:
                if view.skip_string(prefix):
                    cmd = view.get_word()
                    command = await self.system.get_command(cmd)
                    if command is not None:
                        user = await self.system.get_user_twitch_name(
                            message.author.name, id=message.author.id)
                        if command.can_run_twitch(message, user):
                            return await self.process_commands(
                                message, command, view)

        else:
            if view.skip_string(prefixes):
                cmd = view.get_word()
                command = await self.system.get_command(cmd)
                if command is not None:
                    user = await self.system.get_user_twitch_name(
                        message.author.name, id=message.author.id)
                    if command.can_run_twitch(message, user):
                        return await self.process_commands(
                            message, command, view)
Example #26
0
    async def on_message(self, message: discord.Message):
        if not message.guild:
            return
        try:
            if message.guild.id != self.system.config.getint("general", "server_id", fallback=None):
                return
        except:pass

        prefixes = await self.bot.get_prefix(message)
        view = StringView(message.content)
        if isinstance(prefixes, list):
            for prefix in prefixes:
                if view.skip_string(prefix):
                    cmd = view.get_word()
                    command = await self.system.get_command(cmd)
                    if command is not None:
                        user = await self.system.get_user_discord_id(message.author.id)
                        if command.can_run_discord(message, user):
                            return await self.process_commands(message, command, view)

        else:
            if view.skip_string(prefixes):
                cmd = view.get_word()
                command = await self.system.get_command(cmd)
                if command is not None:
                    user = await self.system.get_user_discord_id(message.author.id)
                    if command.can_run_discord(message, user):
                        return await self.process_commands(message, command, view)
Example #27
0
    async def get_context(self, message, *, cls=commands.Context):
        """
        Returns the invocation context from the message.
        Supports getting the prefix from database as well as command aliases.
        """

        view = StringView(message.content)
        ctx = cls(prefix=None, view=view, bot=self, message=message)

        if self._skip_check(message.author.id, self.user.id):
            return ctx

        prefixes = [self.prefix, f'<@{bot.user.id}> ', f'<@!{bot.user.id}>']

        invoked_prefix = discord.utils.find(view.skip_string, prefixes)
        if invoked_prefix is None:
            return ctx

        invoker = view.get_word().lower()

        # Check if there is any aliases being called.
        alias = self.config.get('aliases', {}).get(invoker)
        if alias is not None:
            ctx._alias_invoked = True
            _len = len(f'{invoked_prefix}{invoker}')
            ctx.view = view = StringView(
                f'{alias}{ctx.message.content[_len:]}')
            invoker = view.get_word()

        ctx.invoked_with = invoker
        ctx.prefix = self.prefix  # Sane prefix (No mentions)
        ctx.command = self.all_commands.get(invoker)

        if ctx.command is self.get_command('eval') and hasattr(
                ctx, '_alias_invoked'):
            # ctx.command.checks = None # Let anyone use the command.
            pass

        return ctx
Example #28
0
    async def quote(self, ctx, *, num_or_member: str = None):
        """
        Show/mange server quotes

        If no valid subcommand is given, attempts to look up quote ID or member.
        [p]quote random also works. Without any argument, invokes [p]quote list.
        [p]quote help displays this help and all subcommands.
        """
        if num_or_member and num_or_member.strip(" '\"`").lower() == 'random':
            ctx.view = StringView('yes')
            await self.quote_list.invoke(ctx)
        elif num_or_member and num_or_member.strip(" '\"`").lower() == 'help':
            await self.bot.send_cmd_help(ctx)
        elif num_or_member:
            ctx.view = StringView(num_or_member)

            if num_or_member.isdecimal():
                await self.quote_show.invoke(ctx)
            else:
                await self.quote_by.invoke(ctx)
        else:
            await self.quote_list.invoke(ctx)
Example #29
0
    async def get_context(client: Client, msg: Message) -> Context:
        view = StringView(msg.content)
        ctx = Context(prefix=None, view=view, bot=client, message=msg)

        if client._skip_check(msg.author.id, client.user.id):
            return ctx

        prefix = await client._get_prefix(msg)
        invoked_prefix = prefix

        if isinstance(prefix, str):
            if not view.skip_string(prefix):
                return ctx
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return ctx

        invoker = view.get_word()
        ctx.invoked_with = invoker
        ctx.prefix = invoked_prefix
        ctx.command = client.all_commands.get(invoker)
        return ctx
Example #30
0
 async def process_commands(self, message):
     _internal_channel = message.channel
     _internal_author = message.author
     view = StringView(message.content)
     if self._skip_check(message.author, self.user):
         return
     prefix = await self._get_prefix(message)
     invoked_prefix = prefix
     if not isinstance(prefix, (tuple, list)):
         if not view.skip_string(prefix):
             return
     else:
         invoked_prefix = discord.utils.find(view.skip_string, prefix)
         if invoked_prefix is None:
             return
     invoker = view.get_word().lower()  # case-insensitive commands
     tmp = {
         'bot': self,
         'invoked_with': invoker,
         'message': message,
         'view': view,
         'prefix': invoked_prefix
     }
     ctx = Context(**tmp)
     del tmp
     if invoker in self.commands:
         command = self.commands[invoker]
         self.dispatch('command', command, ctx)
         try:
             await command.invoke(ctx)
         except CommandError as e:
             ctx.command.dispatch_error(e, ctx)
         else:
             self.dispatch('command_completion', command, ctx)
     elif invoker:
         exc = CommandNotFound('Command "{}" is not found'.format(invoker))
         self.dispatch('command_error', exc, ctx)
Example #31
0
    async def find_commands_on_message(self, message: models.PartialMessage):
        prefixes = self.system.get_dpy_prefix(self.system.discord_bot, message)
        if message.content.startswith(tuple(prefixes)):
            for pref in prefixes:
                if message.content.startswith(pref):
                    message.view = StringView(message.content.replace(pref, "", 1))
                    name = message.view.get_word()
                    if name in self.communicator.commands:
                        if not self.current_spec.get("commands", {}).get(name, {"enabled": False}).get("enabled"):
                            return

                        try:
                            await self.communicator.commands[name](message)
                        except Exception as e:
                            logger.error(f"Error in script {self.name} ({self.identifier})", exc_info=e)

                    return
Example #32
0
def _create_arguments_view(interaction: Interaction,
                           command: Command) -> StringView:
    # create option string by joining all of the arguments together, so we can delegate to discord.py
    # note that slash commands can accept spaces in their args, whereas discord.py required "bash style quotes"
    options = interaction.data.get("options", [])
    parts = []
    if options and not options[0].get("value", None):  # a command group
        if command._discordpy_include_subcommand_name[options[0]["name"]]:
            parts.append(options[0]["name"])
        parts = parts + [
            f'「{x.get("value", "")}」' for x in options[0].get("options", [])
        ]
    else:
        parts = parts + [f'「{x.get("value", "")}」' for x in options]

    # remove wrapping quotes if there's only one arg
    view = " ".join(parts)[1:-1] if len(parts) == 1 else " ".join(parts)
    log.debug("args string=%s", view)
    return StringView(view)
Example #33
0
    async def get_contexts(self, message, *, cls=commands.Context):
        """
        Returns all invocation contexts from the message.
        Supports getting the prefix from database as well as command aliases.
        """

        view = StringView(message.content)
        ctx = cls(prefix=self.prefix, view=view, bot=self, message=message)
        thread = await self.threads.find(channel=ctx.channel)

        if self._skip_check(message.author.id, self.user.id):
            return [ctx]

        prefixes = await self.get_prefix()

        invoked_prefix = discord.utils.find(view.skip_string, prefixes)
        if invoked_prefix is None:
            return [ctx]

        invoker = view.get_word().lower()

        # Check if there is any aliases being called.
        alias = self.aliases.get(invoker)
        if alias is not None:
            ctxs = []
            aliases = normalize_alias(
                alias, message.content[len(f"{invoked_prefix}{invoker}"):])
            if not aliases:
                logger.warning("Alias %s is invalid, removing.", invoker)
                self.aliases.pop(invoker)

            for alias in aliases:
                view = StringView(invoked_prefix + alias)
                ctx_ = cls(prefix=self.prefix,
                           view=view,
                           bot=self,
                           message=message)
                ctx_.thread = thread
                discord.utils.find(view.skip_string, prefixes)
                ctx_.invoked_with = view.get_word().lower()
                ctx_.command = self.all_commands.get(ctx_.invoked_with)
                ctxs += [ctx_]
            return ctxs

        ctx.thread = thread
        ctx.invoked_with = invoker
        ctx.command = self.all_commands.get(invoker)
        return [ctx]
Example #34
0
    def process_commands(self, message):
        bot = self.bot
        log_channel = bot.get_server("107883969424396288").get_channel("257926036728184832")
        error_channel = bot.get_server("107883969424396288").get_channel("257922447205072897")
        _internal_channel = message.channel
        _internal_author = message.author
        self = bot

        view = StringView(message.content)
        if self._skip_check(message.author, self.user):
            pass

        prefix = yield from self._get_prefix(message)
        invoked_prefix = prefix

        if not isinstance(prefix, (tuple, list)):
            if not view.skip_string(prefix):
                return
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return

        if invoked_prefix is "c." and message.author != message.server.me:
            return


        invoker = view.get_word()
        invoker_orig = invoker
        diff = difflib.get_close_matches(invoker, self.commands, n=1, cutoff=0.8)
        if diff != []:
            invoker = diff[0]
        tmp = {
            'bot': self,
            'invoked_with': invoker,
            'message': message,
            'view': view,
            'prefix': invoked_prefix
        }
        ctx = Context(**tmp)
        del tmp

        if invoker in self.commands:
            command = self.commands[invoker]
            self.dispatch('command', command, ctx)
            try:
                yield from command.invoke(ctx)
            except discord.ext.commands.errors.DisabledCommand as e:
                yield from self.say("*`{}` is disabled*".format(str(e).split(" ")[0]))
            except discord.ext.commands.errors.CheckFailure as e:
                yield from self.say("```You do not have permission for this command!```")
            except discord.ext.commands.errors.CommandOnCooldown as e:
                yield from self.say("```{}```".format(str(e)))
            except discord.ext.commands.errors.BadArgument as e:
                yield from self.say("```{}```".format(str(e)))
            except discord.ext.commands.errors.CommandError as e:
                ctx.command.dispatch_error(e, ctx)
                yield from self.say("An Error Has Occurred: ```py\n{}```".format(traceback.format_exc().replace("/home/seacow/Discord-Bot/","./").split("The above exception was the direct cause of the following exception:")[0]))
                if not message.author == message.server.me:
                    yield from self.send_message(error_channel,"{}```py\n{}```".format("\"{}\" produced an error on \"{}\" / \"{}\" | Invoked by \"{}\"".format(message.content, _internal_channel.name, _internal_channel.server.name, _internal_author.name+"#"+_internal_author.discriminator), traceback.format_exc().replace("/home/seacow/Discord-Bot/","./").replace("\"penny123\"", "********").split("The above exception was the direct cause of the following exception:")[0]))
            except discord.ext.commands.errors.MissingRequiredArgument as e:
                yield from self.say("```{}```".format(str(e)))
            except:
                pass
            else:
                self.dispatch('command_completion', command, ctx)
        elif invoker:
            close_matches = difflib.get_close_matches(invoker, self.commands)
            extra = ""
            if close_matches != []:
                extra = "\nDid you mean?: {}{}".format(invoked_prefix, ", {}".format(invoked_prefix).join(close_matches))
            yield from self.say("```\"{}\" is not a command{}```".format(invoked_prefix+invoker, extra))
            exc = discord.ext.commands.errors.CommandNotFound('Command "{}" is not found'.format(invoker))
            logging.warning('Command "{}" is not found'.format(invoker))
            self.dispatch('command_error', exc, ctx)