import discord from discord.ext import commands from . import utils import random import re import calendar import pendulum import datetime import psutil class Miscallaneous: """Core commands, these are the miscallaneous commands that don't fit into other categories'""" def __init__(self, bot): self.bot = bot self.help_embeds = [] self.results_per_page = 10 self.commands = None self.process = psutil.Process() self.process.cpu_percent() @commands.command() @utils.custom_perms(send_messages=True) @utils.check_restricted() async def help(self, ctx, *, command=None): """This command is used to provide a link to the help URL. This can be called on a command to provide more information about that command You can also provide a page number to pull up that page instead of the first page EXAMPLE: !help help RESULT: This information""" groups = {} entries = [] if command is not None: command = self.bot.get_command(command) if command is None: for cmd in utils.get_all_commands(self.bot): if not await cmd.can_run(ctx) or not cmd.enabled: continue cog = cmd.cog_name if cog in groups: groups[cog].append(cmd) else: groups[cog] = [cmd] for cog, cmds in groups.items(): entry = {'title': "{} Commands".format(cog), 'fields': []} for cmd in cmds: if not cmd.help: # Assume if there's no description for a command, it's not supposed to be used # I.e. the !command command. It's just a parent continue description = cmd.help.partition('\n')[0] name_fmt = "{ctx.prefix}**{cmd.qualified_name}** {aliases}".format( ctx=ctx, cmd=cmd, aliases=cmd.aliases if len(cmd.aliases) > 0 else "" ) entry['fields'].append({ 'name': name_fmt, 'value': description, 'inline': False }) entries.append(entry) entries = sorted(entries, key=lambda x: x['title']) try: pages = utils.DetailedPages(self.bot, message=ctx.message, entries=entries) pages.embed.set_thumbnail(url=self.bot.user.avatar_url) await pages.paginate() except utils.CannotPaginate as e: await ctx.send(str(e)) else: # Get the description for a command description = command.help if description is not None: # Split into examples, results, and the description itself based on the string description, _, rest = command.help.partition('EXAMPLE:') example, _, result = rest.partition('RESULT:') else: example = None result = None # Also get the subcommands for this command, if they exist subcommands = [x.qualified_name for x in utils.get_all_subcommands(command) if x != command] # The rest is simple, create the embed, set the thumbail to me, add all fields if they exist embed = discord.Embed(title=command.qualified_name) embed.set_thumbnail(url=self.bot.user.avatar_url) if description: embed.add_field(name="Description", value=description.strip(), inline=False) if example: embed.add_field(name="Example", value=example.strip(), inline=False) if result: embed.add_field(name="Result", value=result.strip(), inline=False) if subcommands: embed.add_field(name='Subcommands', value="\n".join(subcommands), inline=False) await ctx.send(embed=embed) @commands.command() @utils.custom_perms(send_messages=True) @utils.check_restricted() async def say(self, ctx, *, msg: str): """Tells the bot to repeat what you say EXAMPLE: !say I really like orange juice RESULT: I really like orange juice""" fmt = "\u200B{}".format(msg) await ctx.send(fmt) try: await ctx.message.delete() except: pass @commands.command() @utils.custom_perms(send_messages=True) @utils.check_restricted() async def calendar(self, ctx, month: str = None, year: int = None): """Provides a printout of the current month's calendar Provide month and year to print the calendar of that year and month EXAMPLE: !calendar january 2011""" # calendar takes in a number for the month, not the words # so we need this dictionary to transform the word to the number months = { "january": 1, "february": 2, "march": 3, "april": 4, "may": 5, "june": 6, "july": 7, "august": 8, "september": 9, "october": 10, "november": 11, "december": 12 } # In month was not passed, use the current month if month is None: month = datetime.date.today().month else: month = months.get(month.lower()) if month is None: await ctx.send("Please provide a valid Month!") return # If year was not passed, use the current year if year is None: year = datetime.datetime.today().year # Here we create the actual "text" calendar that we are printing cal = calendar.TextCalendar().formatmonth(year, month) await ctx.send("```\n{}```".format(cal)) @commands.command(aliases=['about']) @utils.custom_perms(send_messages=True) @utils.check_restricted() async def info(self, ctx): """This command can be used to print out some of my information""" # fmt is a dictionary so we can set the key to it's output, then print both # The only real use of doing it this way is easier editing if the info # in this command is changed # Create the original embed object opts = {'title': 'Dev Server', 'description': 'Join the server above for any questions/suggestions about me.', 'url': utils.dev_server} embed = discord.Embed(**opts) # Add the normal values embed.add_field(name='Total Servers', value=len(self.bot.guilds)) embed.add_field(name='Total Members', value=len(self.bot.users)) hm = self.bot.get_cog('Hangman') ttt = self.bot.get_cog('TicTacToe') bj = self.bot.get_cog('Blackjack') interaction = self.bot.get_cog('Blackjack') music = self.bot.get_cog('Music') # Count the variable values; hangman, tictactoe, etc. if hm: hm_games = len(hm.games) if hm_games: embed.add_field(name='Total Hangman games running', value=hm_games) if ttt: ttt_games = len(ttt.boards) if ttt_games: embed.add_field(name='Total TicTacToe games running', value=ttt_games) if bj: bj_games = len(bj.games) if bj_games: embed.add_field(name='Total blackjack games running', value=bj_games) if interaction: count_battles = 0 for battles in self.bot.get_cog('Interaction').battles.values(): count_battles += len(battles) if count_battles: embed.add_field(name='Total battles games running', value=count_battles) if music: songs = len([x for x in music.voice_states.values() if x.playing]) if songs: embed.add_field(name='Total songs playing', value=songs) if hasattr(self.bot, 'uptime'): embed.add_field(name='Uptime', value=(pendulum.utcnow() - self.bot.uptime).in_words()) memory_usage = self.process.memory_full_info().uss / 1024 ** 2 cpu_usage = self.process.cpu_percent() / psutil.cpu_count() embed.add_field(name='Memory Usage', value='{:.2f} MiB'.format(memory_usage)) embed.add_field(name='CPU Usage', value='{}%'.format(cpu_usage)) embed.set_footer(text=self.bot.description) await ctx.send(embed=embed) @commands.command() @utils.custom_perms(send_messages=True) @utils.check_restricted() async def uptime(self, ctx): """Provides a printout of the current bot's uptime EXAMPLE: !uptime RESULT: A BAJILLION DAYS""" if hasattr(self.bot, 'uptime'): await ctx.send("Uptime: ```\n{}```".format((pendulum.utcnow() - self.bot.uptime).in_words())) else: await ctx.send("I've just restarted and not quite ready yet...gimme time I'm not a morning pony :c") @commands.command(aliases=['invite']) @utils.custom_perms(send_messages=True) @utils.check_restricted() async def addbot(self, ctx): """Provides a link that you can use to add me to a server EXAMPLE: !addbot RESULT: http://discord.gg/yo_mama""" perms = discord.Permissions.none() perms.read_messages = True perms.send_messages = True perms.manage_roles = True perms.ban_members = True perms.kick_members = True perms.manage_messages = True perms.embed_links = True perms.read_message_history = True perms.attach_files = True perms.speak = True perms.connect = True perms.attach_files = True perms.add_reactions = True app_info = await self.bot.application_info() await ctx.send("Use this URL to add me to a server that you'd like!\n{}" .format(discord.utils.oauth_url(app_info.id, perms))) @commands.command(enabled=False) @utils.custom_perms(send_messages=True) @utils.check_restricted() async def joke(self, ctx): """Prints a random riddle EXAMPLE: !joke RESULT: An absolutely terrible joke.""" # Currently disabled until I can find a free API pass @commands.command() @utils.custom_perms(send_messages=True) @utils.check_restricted() async def roll(self, ctx, notation: str = "d6"): """Rolls a die based on the notation given Format should be #d# EXAMPLE: !roll d50 RESULT: 51 :^)""" # Use regex to get the notation based on what was provided try: # We do not want to try to convert the dice, because we want d# to # be a valid notation dice = re.search("(\d*)d(\d*)", notation).group(1) num = int(re.search("(\d*)d(\d*)", notation).group(2)) # Check if something like ed3 was provided, or something else entirely # was provided except (AttributeError, ValueError): await ctx.send("Please provide the die notation in #d#!") return # Dice will be None if d# was provided, assume this means 1d# dice = dice or 1 # Since we did not try to convert to int before, do it now after we # have it set dice = int(dice) if dice > 10: await ctx.send("I'm not rolling more than 10 dice, I have tiny hands") return if num > 100: await ctx.send("What die has more than 100 sides? Please, calm down") return if num <= 1: await ctx.send("A {} sided die? You know that's impossible right?".format(num)) return nums = [random.SystemRandom().randint(1, num) for _ in range(0, int(dice))] total = sum(nums) value_str = ", ".join("{}".format(x) for x in nums) if dice == 1: fmt = '{0.message.author.name} has rolled a {2} sided die and got the number {3}!' else: fmt = '{0.message.author.name} has rolled {1}, {2} sided dice and got the numbers {3}, for a total of {4}!' await ctx.send(fmt.format(ctx, dice, num, value_str, total)) def setup(bot): bot.add_cog(Miscallaneous(bot))