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)
def argsplit(args): view = StringView(args) args = [] while not view.eof: view.skip_ws() args.append(quoted_word(view)) return args
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)
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
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:
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)
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
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
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
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)
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
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)
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
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
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
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]
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
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
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
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)
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
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
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)
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)
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)
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
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)
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
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)
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
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)
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]
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)