async def cyrm(self, ctx): """Runs through a series of checks to ensure the bot can access the invoked channel.""" """ Check 1: Can the bot see the command? True: If this command is invoked, then the bot can see the command. Continue to Check 2. """ """ Check 2: Check if the bot can write messages to the channel. True: Report success status in channel. End command. False: Report failure status to author. Continue to Check 3. """ try: em = Embed(color=message_color) em.set_footer(text=generate_footer(ctx)) em.add_field( name="Loud and Clear!", value="I can see and respond to commands in this channel.") await ctx.send(embed=em) except Forbidden: # Unable to send message to channel. pass else: # Able to send message to channel. return """ Check 3: Check if the bot can write messages to author. True: Report failure status to author. End command. False: Attempt to report error with reactions. Continue to Check 4. """ try: em = Embed(color=error_color) em.set_footer(text=generate_footer(ctx)) em.add_field( name="I can't send messages!", value= "I do not have permission to send messages in the invoked channel. " "Check permissions or contact your server administrator!") await ctx.author.send(embed=em) except Forbidden: # Unable to send error to message author. pass else: # Able to send error to message author. return """ Check 4: Check if the bot can react to invoked message. True: React with :fox: :sos:. End command. False: Impossible to report error. Command fail. """ try: await ctx.message.add_reaction("🦊") await ctx.message.add_reaction("🆘") except Forbidden: # No action possible. return else: # Error reaction success. return
async def stats(self, ctx): logging.info("Hello!") """Posts a full application statistics page""" em = Embed(title="Application Statistics", color=message_color) # Collect system uptime ut = time() - boot_time() # Collect memory statistics memory = virtual_memory() em.add_field( name=":desktop: System Information", value=f"`Uptime` {str(timedelta(seconds=int(ut)))}\n" f"`Memory` {round(memory.used / 1000000000, 1)}/{round(memory.total / 1000000000, 1)} GB " f"({round((memory.used / memory.total) * 100)}%)\n" f"`Load (1/5/15)` {'/'.join([str(round(x / cpu_count() * 100, 2)) for x in getloadavg()])}", inline=False) em.add_field( name=":globe_with_meridians: API Information", value=f"`WebSocket Latency` {round(self.client.latency * 1000)}ms\n" f"`Client ID` {self.client.user.id}\n" f"`Shards` {len(self.client.shards)}\n" f"`Users` {len(self.client.users):,}\n" f"`Guilds` {len(self.client.guilds):,}", inline=False) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def privacy_information(self, ctx): """Sends information on what data this bot collects and how we use it.""" em = Embed( title="Privacy Information", description="Privacy is important to everyone, so this is a quick overview of the data we have stored.", color=message_color ) em.set_footer(text=generate_footer(ctx)) # Command em.add_field( name="Error Logging", value="If a command returns an error, the contents of the command will be logged for debugging purposes. " "Neither the name of the sender nor the name of the server will be logged." ) em.add_field( name="Data Persistence", value="Snipe tool data: the contents of your last mention is stored in " "memory, meaning it is wiped as soon as the bot stops running.\n" "User preferences: the user ID and guild ID (where applicable) is stored in a local database." ) em.add_field( name="Misuse Policy", value="We do not, nor will we ever use the bot to access information not specified by this privacy notice." ) em.add_field( name="Questions?", value="Feel free to ask questions in the Development Server!" ) await ctx.author.send(embed=em)
async def on_command_error(ctx, error): """Error handler, parses how message author should be notified about an error.""" # If CommandNotFound, fail silently if isinstance(error, discord.ext.commands.CommandNotFound): return # error.__cause__ finds cause of CommandInvokeError, useful if exception thrown from inside command. if isinstance(error.__cause__, UserWarning): title = "There was a user warning while running the command!" # Generate formatted string exc = f"{type(error.__cause__).__name__}: {error.__cause__}" else: title = "There was an error while running the command!" # Generate formatted string exc = f"{type(error).__name__}: {error}" em = discord.Embed(title=title, description=f"`{exc}`", color=error_color) em.set_footer(text=generate_footer(ctx)) try: await ctx.send(embed=em) except discord.Forbidden: # Was unable to send exception message, ignore. pass except discord.HTTPException as http_exception: # Was unable to send message due to HTTP error. logging.warning( f"{type(http_exception)} when sending an exception message. " f"{http_exception.status}: {http_exception.text}") pass
async def report_bug(self, ctx): """Gives the user information on how to report bugs they find.""" em = Embed( title="Found a bug? :bee:", description="You can report bugs on the " "[Fox Utilities issues](https://github.com/FevenKitsune/Fox-Utilities/issues) page on GitHub!", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def pull(self, ctx): """Pulls the latest version of the bot from Git""" # Find Git repository the bot is stored in repo = Repo(getcwd(), search_parent_directories=True) # Run git pull and post results into embed. em = Embed(title="Fox Utilities GitHub", description=f"```smalltalk\n{str(repo.git.pull())}\n```", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def tags(self, ctx): """Gives the user information on permission tags, which allow non-admins to access admin commands.""" em = Embed( title="Fox Utilities Permission Tags", description="Create a server role with the syntax `fox:name_of_command`. Assign any user to this role to " "give them access to the named command. This will work with any command that requires " "administrator permissions to access.", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def prefix(self, ctx): """Gives the user information on prefix tags. This allows per-guild prefix settings.""" em = Embed( title="Fox Utilities Prefix Tags", description= "Create a server role with the syntax `fox_prefix:desired_prefix`. Assign this role to the " "Fox Utilities bot to give it a new prefix.", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def invite_bot(self, ctx): """Sends information on the development server, the GitHub, and the invite link.""" em = Embed( title="Invite me!", description=f"[Invite link!]({bot_invite})\n" f"[Development server!]({bot_development_server})\n" f"[GitHub!]({bot_source})\n" f"[Wiki!]({bot_wiki})", color=message_color ) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def reload(self, ctx, *args): """Unload all discord.py cogs and load them back. Easier than a full reboot.""" em = Embed(title="System Reload", color=message_color) em.set_footer(text=generate_footer(ctx)) # Stores which cogs passed and which cogs failed. success = [] failed = [] # Check for extensions first. if len(extensions) == 0: em.add_field( name="Oh well.", value="Doesn't look like there are any extensions defined.") await ctx.send(embed=em) return for extension in extensions: try: # Unload extension self.client.unload_extension(extension) # Continue if unload failed. except Exception as e: pass for extension in extensions: try: # Load extension self.client.load_extension(extension) except Exception as e: # Post error to embed if load failed. expt = f"{type(e).__name__}: {e}" failed.append([extension, expt]) else: # Post to embed if load succeeded. success.append(extension) em.add_field( name=":white_check_mark: Load Passed:", value='\n'.join([f"`{i}` PASS" for i in success]) if success else "`None`") em.add_field( name=":warning: Load Failed:", value='\n'.join([f"`{i[0]}` {i[1]}" for i in failed]) if failed else "`None`") await ctx.send(embed=em)
async def reboot(self, ctx): """Restart the bot on the system level.""" em = Embed(title="Rebooting the bot!", description="Please wait while the bot reboots...", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em) # Get bot process p = Process(getpid()) for handler in p.open_files() + p.connections(): # Close all active connections and processes close(handler.fd) # Get python exec python = executable # Start python process execl(python, python, *argv)
async def help(self, ctx, *args): """Help menu. Processes the list of available commands into a readable menu.""" em = Embed( title="Fox Utilities Help Guide", description=bot_description, color=message_color ) em.set_footer(text=generate_footer(ctx)) if args and (search := args[0]): # If there is an args list, assign variable search with the first value. # User is requesting information about a specific command. if found_command := self.client.get_command(search): # Search client for given command. Assign found_command with found value. # found_command will be None if no command is found. em.add_field( name=f"{'#' if found_command.hidden else ''}`{found_command.cog_name}`" f"\n{found_command.name} {found_command.usage}", value=f"{found_command.help}\n\n**Aliases**\n{found_command.aliases}" )
async def invites(self, ctx): """Get a list of invite codes and the number of uses for a given user.""" # If no one was mentioned, assume author is target. if ctx.message.mentions: user = ctx.message.mentions[0] else: user = ctx.message.author em = Embed(title=f"{user.name}'s Invites", color=message_color) em.set_footer(text=generate_footer(ctx)) # Iterate through the guild invites and parse invites by the targeted user. for inv in await ctx.message.guild.invites(): if inv.inviter == user: time_formatter = "%b %-d, %Y at %-l:%M%p" em.add_field( name=f"Invite code: ####{str(inv.code)[4:]}", value=f"`Uses` {inv.uses}\n" f"`Created at` {inv.created_at.strftime(time_formatter)}") await ctx.send(embed=em)