class Images(wheel(desc='image manipulation')): def __init__(self, bot): super().__init__(bot) self.frothy_images = glob('images/frothy/*.png') @command(brief='the scrub mentality', help=""" // get a random picture of frothy omen &frothy // get a specific picture of frothy omen &frothy 0 """) async def frothy(self, ctx, idx: int = None): if not idx: await ctx.send(file=File(choice(self.frothy_images))) elif idx not in range(len(self.frothy_images)): await ctx.send( f'index must be between 0 and {len(self.frothy_images)}') else: await ctx.send(file=File(self.frothy_images[idx])) @command(brief='get a picture of a cat', help=""" // get a cat picture from the internet &cat """) async def cat(self, ctx): obj = await json_request('https://api.thecatapi.com/v1/images/search') return await ctx.send( embed=ctx.embed('cat', 'thecatapi.com', image=obj[0]['url']))
class Media(wheel(desc='social media websites')): def __init__(self, bot): super().__init__(bot) if rcfg := bot.config.get('reddit', None): self.rapi = Reddit(client_id=rcfg['id'], client_secret=rcfg['secret'], user_agent='python:discord.claymore:v2') else:
class Admin(wheel(desc='server administration tools')): def __init__(self, bot): super().__init__(bot) @bot.listen() async def on_member_join(member): if roles := await self.db.roles.find_one({'id': member.guild.id}): await member.edit(roles=[ it for each in roles['roles'] if (it := member.guild.get_role(each)) ])
class Fun(wheel(desc = 'fun commands')): @command() async def coinflip(self, ctx): pass @command() async def rate(self, ctx, *, thing: str): pass @command( brief = 'ask the magic 8ball a question', aliases = [ '8', '8ball' ] ) async def magic(self, ctx): pass @command() async def rank(self, ctx, *, items: str): pass
class Star(wheel(desc='starboard')): def __init__(self, bot): super().__init__(bot) @bot.listen() async def on_raw_reaction_add(event): guild = self.bot.get_guild(event.guild_id) chan = guild.get_channel(event.channel_id) user = event.member msg = await chan.fetch_message(event.message_id) if emote := await self.db.star.find_one({'id': user.guild.id}): if not (channel := user.guild.get_channel(emote['channel'])): return num = len([ react for react in msg.reactions if react.emoji == emote['emote'] ]) if num >= emote.get('limit', 10): await channel.send(embed=star_embed(msg.author, msg))
class Moderation(wheel(desc='server moderation')): #TODO: all these @command() @has_permissions(manage_messages=True) async def warn(self, ctx, user: Member): pass @command() async def warnings(self, ctx, user: Member = None): pass @command() @has_permissions(administrator=True) async def kick(self, ctx, user: Member): pass @command() @has_permissions(administrator=True) async def ban(self, ctx, user: Member, *, reason: str = 'no reason provided'): pass @command() @has_permissions(manage_messages=True) async def mute(self, ctx, user: Member, time: str = None, *, reason: str = 'no reason provided'): pass @command() @has_permissions(manage_messages=True) async def unmute(self, ctx, user: Member): pass
class Utils(wheel(desc='helpful tools')): def __init__(self, bot): super().__init__(bot) if wkey := bot.config['keys'].get('wolfram', None): self.wapi = Wolfram(wkey) else:
class Text(wheel(desc = 'automatic messages')): def __init__(self, bot): super().__init__(bot) @bot.listen() async def on_message(message): if message.author.bot: return reacts = await self.db.reacts.find_one({ 'id': message.guild.id }) for text, emotes in (reacts or {}).items(): if 'id' in text: continue if text in message.content: for emote in emotes: await message.add_reaction(emote) @bot.listen() async def on_member_join(member): if member.bot: return message = await self.db.welcome.find_one({ 'id': member.guild.id }) if not message or not message['msg']: return await member.guild.get_channel(message['channel']).send(message['msg'].replace('$user', member.mention)) @bot.listen() async def on_member_remove(member): if member.bot: return message = await self.db.leave.find_one({ 'id': member.guild.id }) if not message or not message['msg']: return await member.guild.get_channel(message['channel']).send(message['msg'].replace('$user', member.mention)) @group( invoke_without_command = True, brief = 'list all reacts', help = """ // list all current autoreacts for the server &reacts """ ) @guild_only() async def reacts(self, ctx): async def fail(): await ctx.send(embed = ctx.embed('no autoreacts', 'add autoreacts using `reacts add`')) if not (current := await self.db.reacts.find_one({ 'id': ctx.guild.id })): return await fail() embed = PagedEmbed('all embeds', f'`{len(current)}` total phrases') for phrase, emotes in current.items(): if 'id' in phrase: continue embed.add_field(phrase, ', '.join(emotes)) if not embed.fields: return await fail() await ctx.page_embeds(embed)
class Help(wheel(desc='bot usage commands')): def __init__(self, bot: Claymore): super().__init__(bot) bot.add_listener(self.on_command_error) async def on_command_error(self, ctx, err): options = { CommandNotFound: self.command_not_found, PermissionError: self.permission_denied, CommandOnCooldown: self.cooldown, BotMissingPermissions: self.missing_permissions } await ctx.send(options.get(type(err), self.report_error)(ctx, err)) def report_error(self, ctx, err: Exception) -> str: stack = '\n'.join(format_exception(type(err), err, err.__traceback__)) self.log.error(f'encountered {err} exception {stack}') return 'encountered an unexpected issue, this interaction has been reported' def command_not_found(self, ctx, _) -> str: return f'command `{ctx.invoked_with}` not found' def permission_denied(self, ctx, _) -> str: return f'you do not have sufficient privileges to use the `{ctx.invoked_with}` command`' def cooldown(self, ctx, err) -> str: return f'command is still on cooldown, try again in `{err.retry_after:.2f}s`' def missing_permissions(self, ctx, err) -> str: return f'i am missing permissions to execute that command\nrequired permissions: {", ".join(err.missing_perms)}' def search_fuzz(self, ctx, items, item: str, msg: str): res = fuzzy(items, item) if not res: return f'no {msg} named {item}' names = " \n".join(res) return ctx.embed(f'no {msg} named {item}', f'possible results\n```{names}```') def all_cogs(self, ctx, prefix) -> Embed: fields = { name: body.description for name, body in self.bot.cogs.items() if not body.hidden } desc = f'`{", ".join(prefix)}` are the current prefixes' if isinstance( prefix, tuple) else f'`{prefix}` is the current prefix' return ctx.embed('all cogs', f'all cogs you have permission to use\n{desc}', fields) def cog_help(self, ctx, cog_name: str) -> Embed: cog = first(self.bot.cogs.items(), cog_name) if not cog or cog.hidden: return self.search_fuzz(ctx, self.bot.cogs.keys(), cog_name, 'cog') cmds = { cmd.name: (cmd.brief or cmd.usage) if cmd.enabled else '(disabled)' for cmd in cog.get_commands() if not cmd.hidden } return ctx.embed(f'all commands in {cog_name}', f'{len(cmds)} total commands', cmds) async def cmd_help(self, ctx, name): if not name: return False cmd = self.bot.get_command(name.lower()) if not cmd or cmd.hidden: return False details = { 'signature': f'```\n{cmd.qualified_name} {cmd.signature}```', 'enabled': str(cmd.enabled).lower(), 'aliases': ', '.join(cmd.aliases) if cmd.aliases else 'no aliases', 'usage': (f'```{dedent(cmd.help)}```', False) if cmd.help else 'see signature' } if isinstance(cmd, Group): details[ 'subcommands'] = f'```\n{", ".join([it.name for it in cmd.commands])}```' await ctx.send(embed=ctx.embed(cmd.name, cmd.brief, details)) return True @command(brief='bot usermanual', help=""" // get general help &help // get help about a cog &help utils // get help for a specific command &help ping """) async def help(self, ctx: Context, *, name: str = None): # self.cmd_help prints and returns true if it finds a command if not await self.cmd_help(ctx, name): await ctx.push( self.cog_help(ctx, name.lower()) if name else self. all_cogs(ctx, await self.bot.get_prefix(ctx))) def cog_commands(self, ctx, name: str): cog = first(self.bot.cogs.items(), name) if not cog: return self.search_fuzz(ctx, self.bot.cogs.keys(), name, 'cog') cmds = cog.get_commands() names = " \n".join([format_cmds(cmd) for cmd in cmds]) return ctx.embed(f'all commands for {name}', f'{len(cmds)} total commands```\n{names}```') def all_commands(self, ctx): ncogs = 0 ncmds = 0 fields = {} for it, cog in self.bot.cogs.items(): if cog.hidden: continue cmds = cog.get_commands() ncogs += 1 ncmds += len(cmds) names = '' names = '\n'.join([format_cmds(cmd) for cmd in cmds]) fields[it] = f'```\n{names}```' return ctx.embed('all commands', f'{ncogs} cogs containing {ncmds} commands', fields) @command(brief='command & module listings', help=""" // list all commands &commands // list all commands in a specific module &help utils """) async def commands(self, ctx: Context, mod: str = None): await ctx.send(embed=self.cog_commands(ctx, mod.lower( )) if mod else self.all_commands(ctx))