async def roll(self, ctx, *args): """Generate a random number based on dice given in D&D style parameters (ex. 1d6)""" if len(args) < 1: raise UserWarning("You must specify a roll to use this command!") try: d_index = args[0].lower().index('d') qty = int(args[0].lower()[0:d_index]) faces = int(args[0].lower()[d_index + 1:]) except ValueError: raise UserWarning("Invalid formatting of dice roll!") if qty > 50: raise UserWarning("Maximum of 50 dice at once.") if faces > 10000: raise UserWarning("Maximum of 10,000 faces per die.") rolls = sample(range(1, faces + 1), qty) em = discord.Embed( title=f":game_die: Rolling {qty}d{'{:,}'.format(faces)}...", description= f"{', '.join(['{:,}'.format(i) for i in rolls])}\n\nTotal: {'{:,}'.format(sum(rolls))}", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def block_list_msgrole(self, ctx): """List all block_list entries in database.""" query = session.query(UserSettings) to_set = query.filter( UserSettings.discord_id == ctx.message.author.id).first() if to_set is None: raise UserWarning("You have no guilds blocked!") else: block_list = json.loads(to_set.msgrole_block) if not block_list: raise UserWarning("You have no guilds blocked!") em = discord.Embed(title="Msgrole Block List", description=f"Guilds that you have blocked.", color=message_color) em.set_footer(text=generate_footer(ctx)) for guild_id in block_list: guild = self.client.get_guild(guild_id) em.add_field( # Client can only see names of guilds it's in. Placeholder if it can't find the name. name=f"{guild if guild else 'No longer in this guild.'}", value=f"`ID`: {guild_id}") await ctx.send(embed=em)
async def on_command_error(ctx, error): """Error handler, parses how message author should be notified about an error.""" try: # 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)) await ctx.send(embed=em) except Exception as error: # If there is an issue with sending a message to the error channel, just ignore it. pass
async def hello_world(self, ctx): """Respond with an embedded response.""" em = discord.Embed(title="Response", description="Hello world!", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def user_count(self, ctx): """Counts the number of unique users the bot is connected to.""" em = discord.Embed( title="User Count", description=f"I can see a total of {len(ctx.bot.users):,} users!", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def epoch_time(self, ctx): """Get the system time and post it as Unix time.""" em = discord.Embed( title=":clock1130: Current Epoch Time", description= f"{time.time():,.2f}s\n\n[What?](https://en.wikipedia.org/wiki/Unix_time)", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def ping_bot(self, ctx): """Basic call and response command""" em = discord.Embed( title="Pong!", description="Hello! Everything seems to be operational.", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def message_role(self, ctx, *args): # Check if there's a mentioned role. If not, string match. if len(ctx.message.role_mentions) < 1: found_role = await find_by_name(args[0], ctx.message.guild.roles) if found_role is None: raise UserWarning("You must mention one role.") else: found_role = ctx.message.role_mentions[0] # If the set role has no members, throw error. if len(found_role.members) == 0: raise UserWarning("That role has no members!") em = discord.Embed( title=":mega: Sending messages...", description=f"Sending requested messages to {found_role.mention}", color=message_color) em.set_footer(text=generate_footer(ctx)) # Send message to all users in selected role. for member in found_role.members: try: # Query database to get member preferences. query = session.query(UserSettings) block_pref = query.filter( UserSettings.discord_id == member.id).first() # Ensure query returned something. if block_pref is not None: # Check that their blocklist is populated. if isinstance(block_pref.msgrole_block, str): # Check if guild id is in blocklist. if ctx.guild.id in json.loads( block_pref.msgrole_block): # If so, raise exception. raise UserWarning("This user has blocked msgrole.") # Send embedded msgrole. em_sent = discord.Embed( title=f"Role message from {ctx.message.author.name}", description=generate_clean_msgrole(ctx, args), color=message_color) em_sent.set_footer( text=f"Sent from: {ctx.guild.name}\n" f"Use f.block {ctx.guild.id} if you no longer wish to receive messages from this guild." ) em_sent.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url) await member.send(embed=em_sent) except Exception as e: # If sending dm failed, add entry to list. em.add_field(name=f"Failed to send message to {member.name}", value=f"`{type(e).__name__}: {e}`") pass # Send success/fail list back to guild. await ctx.send(embed=em)
async def report_bug(self, ctx): """Gives the user information on how to report bugs they find.""" em = discord.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 invite_bot(self, ctx): """Sends information on the development server, the GitHub, and the invite link.""" em = discord.Embed(title="Invite me!", description=f"[Invite link!]({bot_invite})\n" f"[Development server!]({bot_development_server})\n" f"[GitHub!]({bot_source})", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def server_count(self, ctx): """Counts the number of servers the bot is connected to.""" em = discord.Embed( title="Server Count", description= f"I am currently connected to {len(self.client.guilds):,} servers.", 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 = discord.Embed( title="Fox Utilities Permission Tags", description= "Create a role with the syntax `fox:name_of_command` to give them " "permission to access that command! Will work with any admin command!", 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 = discord.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 git_pull(self, ctx): """Pulls the latest version of the bot from Git""" # Find Git repository the bot is stored in repo = git.Repo(os.getcwd(), search_parent_directories=True) # Run git pull and post results into embed. em = discord.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 sys_uptime(self, ctx): """Get system uptime from container.""" # Read system uptime. with open("/proc/uptime", "r") as proc_ut: ut = float(proc_ut.readline().split()[0]) em = discord.Embed( title="Container Uptime", description= f"/proc/uptime: {str(datetime.timedelta(seconds=int(ut)))}", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def block_msgrole(self, ctx, args): """Push block settings to database.""" query = session.query(UserSettings) to_set = query.filter( UserSettings.discord_id == ctx.message.author.id).first() # Check that a valid ID was passed. try: int(args) except ValueError: raise UserWarning("ID given was not a valid ID.") if len(args) != 18: raise UserWarning("ID given is the incorrect length.") # If user is not in database, create new entry. if to_set is None: to_set = UserSettings(discord_id=ctx.message.author.id, msgrole_block=json.dumps([int(args)])) session.add(to_set) # Load block_list for posting in msg block_list = json.loads(to_set.msgrole_block) else: # Load in block_list and append requested guild. block_list = json.loads(to_set.msgrole_block) if int(args) in block_list: raise UserWarning("This guild is already blocked!") block_list.append(int(args)) to_set.msgrole_block = json.dumps(block_list) session.commit() em = discord.Embed( title="Msgrole Blocked", description=f"You have successfully blocked ID {args}", color=message_color) em.set_footer(text=generate_footer(ctx)) # Add fields for all guild's blocked. for guild_id in block_list: guild = self.client.get_guild(guild_id) em.add_field( name=f"{guild if guild else 'No longer in this guild.'}", value=f"`ID`: {guild_id}") await ctx.send(embed=em)
async def sniped(self, ctx): """Retrieves the last mention within a given channel, even if that mention was deleted.""" em = discord.Embed(color=message_color) em.set_footer(text=generate_footer(ctx)) try: grabbed_message = snipe_db[f"{ctx.author.id}"][f"{ctx.channel.id}"] except Exception: em.add_field( name=f"I don't see the last mention...", value=f"Mentions are stored for a limited period of time!" ) else: em.add_field( name=f"I found something:", value=f"Mentioned by: <@{grabbed_message['author_id']}>\n\n{grabbed_message['content']}" ) await ctx.send(embed=em)
async def purge_block_msgrole(self, ctx): """Delete all block_list entries from database.""" query = session.query(UserSettings) to_set = query.filter( UserSettings.discord_id == ctx.message.author.id).first() if to_set is None: raise UserWarning("You have no guilds blocked!") else: to_set.msgrole_block = json.dumps([]) session.commit() em = discord.Embed( title="Msgrole Purged", description=f"You have successfully erased your block list.", color=message_color) em.set_footer(text=generate_footer(ctx)) await ctx.send(embed=em)
async def reboot(self, ctx): """Restart the bot on the system level.""" em = discord.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 = psutil.Process(os.getpid()) for handler in p.open_files() + p.connections(): # Close all active connections and processes os.close(handler.fd) # Get python exec python = sys.executable # Start python process os.execl(python, python, *sys.argv)
async def help(self, ctx): """Help menu. Processes the list of available commands into a readable menu.""" em = discord.Embed(title="Fox Utilities Help Guide", description=bot_description, color=message_color) em.set_footer(text=generate_footer(ctx)) for cmd in sorted(self.client.commands, key=lambda command: command.cog_name): # If not developer, do not show hidden commands. if cmd.hidden and not (ctx.author.id == developer_id): pass else: # Help field formatter. em.add_field( name= f"{'#' if cmd.hidden else ''}`{cmd.cog_name}`> {cmd.name} {cmd.usage}", value=cmd.brief, inline=False) await ctx.author.send(embed=em)
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 = discord.Embed(title="**{}\'s Invites**".format(user.name), 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="Invite code: ####{}".format(str(inv.code)[4:]), value= f"Uses: {inv.uses}\nCreated at: {inv.created_at.strftime(time_formatter)}" ) 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 = discord.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)