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 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 exec(self, ctx, *, code): """Remote executes code.""" code = code.strip() py_start = code.lower().startswith("```py") python_start = py_start and code.lower().startswith("```python") view = StringView(code) view.skip_string(ctx.prefix) view.skip_ws() view.skip_string(ctx.invoked_with) view.skip_ws() code = view.read_rest() if python_start: code = code[9:] elif py_start: code = code[5:] elif code.startswith("```"): code = code[3:] if code.endswith("```"): code = code[:-3] del py_start, python_start, view with closing(StringIO()) as log: with redirect_stdout(log): try: exec(code) except Exception: error = exc_info() for e in error: print(e) output = log.getvalue() output = "```\n" + output + "```" await reply(ctx, output)
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 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)
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 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
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 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 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 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 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)
def check(prefix): view = StringView(message.content) return view.skip_string(prefix) and view.get_word().lower() in cmds
async def get_context(self, message, *, cls=Context): """Defaults to check for prefix first.""" view = StringView(message.content) ctx = cls(prefix=None, suffix=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) suffix = await self.get_suffix(message) if prefix is not None: if isinstance(prefix, str): if view.skip_string(prefix): invoked_prefix = prefix else: try: if message.content.startswith(tuple(prefix)): invoked_prefix = discord.utils.find(view.skip_string, prefix) 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 else: if isinstance(suffix, str): if _suffix_used(suffix, message.content): invoked_suffix = suffix else: return ctx else: try: invoked_suffixes = [s for s in suffix if _suffix_used(s, message.content)] if not invoked_suffixes: return ctx for suf in invoked_suffixes: invoker = view.get_word()[:-len(suf)] command = self.all_commands.get(invoker) if command is not None: view.undo() invoked_suffix = suf break else: return ctx except TypeError: if not isinstance(suffix, list): raise TypeError("get_suffix must return either a string or a list of string, " "not {}".format(suffix.__class__.__name__)) for value in suffix: if not isinstance(value, str): raise TypeError("Iterable command_suffix or list returned from get_suffix must " "contain only strings, not {}".format(value.__class__.__name__)) raise invoker = view.get_word() try: ctx.suffix = invoked_suffix except NameError: try: ctx.prefix = invoked_prefix except NameError: pass else: invoker = invoker[:-len(invoked_suffix)] 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)
async def invoke(self, ctx, piped): if piped and ctx.piped: not_owner = not await self.is_owner(ctx.author) if not_owner: await self.check_cooldown( 1, ctx, "\N{NO ENTRY} **Cooldown** `Cannot pipe for another {:.2f} seconds.`", True) lp = len(ctx.piped) it = time() for idx, p in enumerate(ctx.piped): e = discord.Embed(title=p[0][:256]) if idx != 0: # Re-init StringView view = StringView( f"{p[0]} {' '.join(filter(lambda x: isinstance(x, str), ctx.cached))}" ) # Clear the output cache for the next run ctx.cached.clear() ctx.view = view view.skip_string("") view.get_word() ctx.command = p[1] else: if not ctx.channel.permissions_for(ctx.me).embed_links: raise CannotPaginate( 'Bot does not have `embed_links` permission for piping.' ) e.description = f"\N{HOURGLASS WITH FLOWING SAND} Piping {lp} commands" # Don't need NotSoContext.send, can't mention ping in embeds m = await super(ctx.__class__, ctx).send(embed=e) self.bot.command_messages[ctx.message.id][2].append(m.id) # Return on cooldown if ctx.cached: return await m.edit(content=ctx.cached[0].decode()) is_last = (idx + 1) == lp t = time() # 5 min cache for last result, 10s + current index to circumvent discord cache ctx.cached.append(300 if is_last else 60 + idx) # Wait on ratelimit to prevent cooldown on cooldown messages try: await self.bot.invoke(ctx) except commands.CommandOnCooldown as e: if not_owner: ra = e.retry_after # Let the user know e.description = f"\N{TIMER CLOCK} Waiting {ra:.2f} seconds for cooldown." await m.edit(embed=e) # Sleep then bypass checks await asyncio.sleep(ra) await ctx.reinvoke() # Using embed as a progress "bar" through images e.timestamp = datetime.datetime.now() if is_last: e.set_footer( text="\N{HOLE} Piping took {:.01f}s".format(time() - it)) else: e.set_footer( text=f"\N{HOLE} Piping in progress: {idx + 1}/{lp}") # Set embed for c in ctx.cached[:3]: if isinstance(c, int): continue # Differentiate between imis cache and text content if isinstance(c, str): e.set_image(url=c) else: e.description = c.decode() # Don't update the embed too fast if (time() - t) < 1: await asyncio.sleep(1.5) # Edit with new results or create message and add to cmessages await m.edit(embed=e) else: await self.bot.invoke(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)