async def paginate(self): """Actually paginate the entries and run the interactive loop if necessary.""" await self.show_page(1, first=True) while self.paginating: try: react = await self.bot.wait_for("reaction_add", check=self.react_check, timeout=120.0) try: # Can't remove other users reactions in DMs if not is_private(self.message.channel): await self.message.remove_reaction( react[0].emoji, react[1]) except Exception as e: pass except asyncio.TimeoutError: await self.first_page() self.paginating = False try: # Can't remove other users reactions in DMs if not is_private(self.message.channel): await self.message.clear_reactions() except: pass finally: break await self.match()
async def monster(self, ctx, *, name: str = None): """Shows a monster's information Shows the monster's image, attributes and loot""" permissions = ctx.channel.permissions_for(ctx.me) if not permissions.embed_links: await ctx.send( "Sorry, I need `Embed Links` permission for this command.") return if name is None: await ctx.send( "Tell me the name of the monster you want to search.") return if is_private(ctx.channel): bot_member = self.bot.user else: bot_member = self.bot.get_member(self.bot.user.id, ctx.guild) if name.lower() == bot_member.display_name.lower(): await ctx.send( random.choice([ "**" + bot_member.display_name + "** is too strong for you to hunt!", "Sure, you kill *one* child and suddenly you're a monster!", "I'M NOT A MONSTER", "I'm a monster, huh? I'll remember that, human..." + EMOJI[":flame:"], "You misspelled *future ruler of the world*.", "You're not a good person. You know that, right?", "I guess we both know that isn't going to happen.", "You can't hunt me.", "That's funny... If only I was programmed to laugh." ])) return monster = get_monster(name) if monster is None: await ctx.send("I couldn't find a monster with that name.") return if type(monster) is list: embed = discord.Embed(title="Suggestions", description="\n".join(monster)) await ctx.send( "I couldn't find that creature, maybe you meant one of these?", embed=embed) return long = is_private( ctx.channel) or ctx.channel.name == config.ask_channel_name embed = self.get_monster_embed(ctx, monster, long) # Attach monster's image only if the bot has permissions if permissions.attach_files and monster["image"] != 0: filename = re.sub(r"[^A-Za-z0-9]", "", monster["name"]) + ".gif" embed.set_thumbnail(url=f"attachment://{filename}") await ctx.send(file=discord.File(monster["image"], f"{filename}"), embed=embed) else: await ctx.send(embed=embed)
async def wait_for_confirmation_reaction(self, ctx: Context, message: Message, deny_message: str) -> bool: """Waits for the command author (ctx.author) to reply with a Y or N reaction Returns true if the user reacted with Y, false if the user reacted with N or didn't react at all""" await message.add_reaction('\U0001f1fe') await message.add_reaction('\U0001f1f3') def check_react(reaction: Reaction, user: User): if reaction.message.id != message.id: return False if user.id != ctx.author.id: return False if reaction.emoji not in ['\U0001f1f3', '\U0001f1fe']: return False return True try: react = await self.wait_for("reaction_add", timeout=120, check=check_react) if react[0].emoji == '\U0001f1f3': await ctx.send(deny_message) return False except asyncio.TimeoutError: await ctx.send("You took too long!") return False finally: if not is_private(ctx.channel): try: await message.clear_reactions() except: pass return True
async def stop_pages(self): """stops the interactive pagination session""" # await self.bot.delete_message(self.message) try: # Can't remove reactions in DMs, so don't even try if not is_private(self.message.channel): await self.message.clear_reactions() except: pass await self.show_page(1) self.paginating = False
async def npc(self, ctx, *, name: str = None): """Shows information about an NPC Shows an NPC's picture, trade offers and location.""" permissions = ctx.channel.permissions_for(ctx.me) if not permissions.embed_links: await ctx.send( "Sorry, I need `Embed Links` permission for this command.") return if name is None: await ctx.send("Tell me the name of an NPC.") return npc = get_npc(name) if npc is None: await ctx.send("I don't know any NPC with that name.") return if type(npc) is list: embed = discord.Embed(title="Suggestions", description="\n".join(npc)) await ctx.send( "I couldn't find that NPC, maybe you meant one of these?", embed=embed) return long = is_private( ctx.channel) or ctx.channel.name == config.ask_channel_name embed = self.get_npc_embed(ctx, npc, long) # Attach spell's image only if the bot has permissions if permissions.attach_files: files = [] if npc["image"] != 0: filename = re.sub(r"[^A-Za-z0-9]", "", npc["name"]) + ".png" embed.set_thumbnail(url=f"attachment://{filename}") files.append(discord.File(npc["image"], filename)) if None not in [npc["x"], npc["y"], npc["z"]]: map_filename = re.sub(r"[^A-Za-z0-9]", "", npc["name"]) + "-map.png" map_image = get_map_area(npc["x"], npc["y"], npc["z"]) embed.set_image(url=f"attachment://{map_filename}") embed.add_field( name="Location", value= f"[Mapper link]({get_mapper_link(npc['x'],npc['y'],npc['z'])})", inline=False) files.append(discord.File(map_image, map_filename)) await ctx.send(files=files, embed=embed) else: await ctx.send(embed=embed)
def react_check(self, reaction: Reaction, user: User): if reaction.message.id != self.message.id: return False if (not is_private(self.message.channel) and user.id != self.author.id) or user.id == self.bot.user.id: return False for (emoji, func) in self.reaction_emojis: if reaction.emoji == emoji: self.match = func return True return False
async def wait_for_confirmation_reaction( self, ctx: Context, message: Message, timeout: float = 120) -> Optional[bool]: """Waits for the command author (ctx.author) to reply with a Y or N reaction Returns True if the user reacted with Y Returns False if the user reacted with N Returns None if the user didn't react at all""" YES_REACTION = '\U0001f1fe' NO_REACTION = '\U0001f1f3' try: await message.add_reaction(YES_REACTION) await message.add_reaction(NO_REACTION) except discord.Forbidden: log.error( "wait_for_confirmation_reaction: No permission to add reactions." ) return None def check_react(reaction: Reaction, user: User): if reaction.message.id != message.id: return False if user.id != ctx.author.id: return False if reaction.emoji not in [NO_REACTION, YES_REACTION]: return False return True try: react = await self.wait_for("reaction_add", timeout=timeout, check=check_react) if react[0].emoji == NO_REACTION: return False except asyncio.TimeoutError: return None finally: if not is_private(ctx.channel): try: await message.clear_reactions() except: pass return True
async def spell(self, ctx, *, name: str = None): """Shows information about a spell Shows a spell's icon, general information, price, npcs that teach it.""" permissions = ctx.channel.permissions_for(ctx.me) if not permissions.embed_links: await ctx.send( "Sorry, I need `Embed Links` permission for this command.") return if name is None: await ctx.send("Tell me the name or words of a spell.") return spell = get_spell(name) if spell is None: await ctx.send("I don't know any spell with that name or words.") return if type(spell) is list: embed = discord.Embed(title="Suggestions", description="\n".join(spell)) await ctx.send( "I couldn't find that spell, maybe you meant one of these?", embed=embed) return long = is_private( ctx.channel) or ctx.channel.name == config.ask_channel_name embed = self.get_spell_embed(ctx, spell, long) # Attach spell's image only if the bot has permissions if permissions.attach_files and spell["image"] != 0: filename = re.sub(r"[^A-Za-z0-9]", "", spell["name"]) + ".gif" embed.set_thumbnail(url=f"attachment://{filename}") await ctx.send(file=discord.File(spell["image"], f"{filename}"), embed=embed) else: await ctx.send(embed=embed)
async def show_page(self, page, *, first=False): self.current_page = page entries = self.get_page(page) p = [] if self.numerate: for t in enumerate(entries, 1 + ((page - 1) * self.per_page)): p.append('%s. %s' % t) else: for t in entries: p.append(t) self.embed.set_footer(text='Page %s/%s (%s entries)' % (page, self.maximum_pages, len(self.entries))) if self.title: self.embed.title = self.title self.embed.description = self.description + "\n" + '\n'.join(p) if not self.paginating: return await self.message.channel.send(embed=self.embed) if not first: return await self.message.edit(embed=self.embed) # verify we can actually use the pagination session if not self.permissions.add_reactions: raise CannotPaginate('Bot does not have add reactions permission.') if not self.permissions.read_message_history: raise CannotPaginate( "Bot does not have read message history permission.") self.message = await self.message.channel.send(embed=self.embed) for (reaction, _) in self.reaction_emojis: if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set continue # Stop reaction doesn't work on PMs so do not add it if is_private(self.message.channel) and reaction == '\U000023F9': continue await self.message.add_reaction(reaction)
async def item(self, ctx, *, name: str = None): """Shows an item's information Shows the item's sprite, attributes, buy and sell offers and creature drops.""" permissions = ctx.channel.permissions_for(ctx.me) if not permissions.embed_links: await ctx.send( "Sorry, I need `Embed Links` permission for this command.") return if name is None: await ctx.send("Tell me the name of the item you want to search.") return item = get_item(name) if item is None: await ctx.send("I couldn't find an item with that name.") return if type(item) is list: embed = discord.Embed(title="Suggestions", description="\n".join(item)) await ctx.send( "I couldn't find that item, maybe you meant one of these?", embed=embed) return long = is_private( ctx.channel) or ctx.channel.name == config.ask_channel_name embed = self.get_item_embed(ctx, item, long) # Attach item's image only if the bot has permissions permissions = ctx.channel.permissions_for(ctx.me) if permissions.attach_files or item["image"] != 0: filename = re.sub(r"[^A-Za-z0-9]", "", item["name"]) + ".gif" embed.set_thumbnail(url=f"attachment://{filename}") await ctx.send(file=discord.File(item["image"], f"{filename}"), embed=embed) else: await ctx.send(embed=embed)
async def diagnose(self, ctx: discord.ext.commands.Context, *, server_name=None): """Diagnose the bots permissions and channels""" # This will always have at least one server, otherwise this command wouldn't pass the is_admin check. admin_guilds = self.bot.get_user_admin_guilds(ctx.message.author.id) if server_name is None: if not is_private(ctx.message.channel): if ctx.message.guild not in admin_guilds: await ctx.send( "You don't have permissions to diagnose this server.") return guild = ctx.message.guild else: if len(admin_guilds) == 1: guild = admin_guilds[0] else: guild_list = [ str(i + 1) + ": " + admin_guilds[i].name for i in range(len(admin_guilds)) ] await ctx.send( "Which server do you want to check?\n\t0: *Cancel*\n\t" + "\n\t".join(guild_list)) def check(m): return m.author == ctx.author and m.channel == ctx.channel try: answer = await self.bot.wait_for("message", timeout=60.0, check=check) answer = int(answer.content) if answer == 0: await ctx.send("Changed your mind? Typical human.") return guild = admin_guilds[answer - 1] except IndexError: await ctx.send( "That wasn't in the choices, you ruined it. Start from the beginning." ) return except ValueError: await ctx.send("That's not a number!") return except asyncio.TimeoutError: await ctx.send("I guess you changed your mind.") return else: guild = self.bot.get_guild_by_name(server_name) if guild is None: await ctx.send("I couldn't find a server with that name.") return if guild not in admin_guilds: await ctx.send( "You don't have permissions to diagnose **{0}**.".format( guild.name)) return if guild is None: return member = self.bot.get_member(self.bot.user.id, guild) server_perms = member.guild_permissions channels = guild.text_channels not_read_messages = [] not_send_messages = [] not_add_reactions = [] not_read_history = [] not_manage_messages = [] not_embed_links = [] not_attach_files = [] not_mention_everyone = [] count = 0 for channel in channels: count += 1 channel_permissions = channel.permissions_for(member) if not channel_permissions.read_messages: not_read_messages.append(channel) if not channel_permissions.send_messages: not_send_messages.append(channel) if not channel_permissions.manage_messages: not_manage_messages.append(channel) if not channel_permissions.embed_links: not_embed_links.append(channel) if not channel_permissions.attach_files: not_attach_files.append(channel) if not channel_permissions.mention_everyone: not_mention_everyone.append(channel) if not channel_permissions.add_reactions: not_add_reactions.append(channel) if not channel_permissions.read_message_history: not_read_history.append(channel) channel_lists_list = [ not_read_messages, not_send_messages, not_manage_messages, not_embed_links, not_attach_files, not_mention_everyone, not_add_reactions, not_read_history ] permission_names_list = [ "Read Messages", "Send Messages", "Manage Messages", "Embed Links", "Attach Files", "Mention Everyone", "Add reactions", "Read Message History" ] server_wide_list = [ server_perms.read_messages, server_perms.send_messages, server_perms.manage_messages, server_perms.embed_links, server_perms.attach_files, server_perms.mention_everyone, server_perms.add_reactions, server_perms.read_message_history ] answer = "Permissions for {0.name}:\n".format(guild) i = 0 while i < len(channel_lists_list): answer += "**{0}**\n\t{1} Server wide".format( permission_names_list[i], get_check_emoji(server_wide_list[i])) if len(channel_lists_list[i]) == 0: answer += "\n\t{0} All channels\n".format( get_check_emoji(True)) elif len(channel_lists_list[i]) == count: answer += "\n\t All channels\n".format(get_check_emoji(False)) else: channel_list = ["#" + x.name for x in channel_lists_list[i]] answer += "\n\t{0} Not in: {1}\n".format( get_check_emoji(False), ",".join(channel_list)) i += 1 ask_channel = self.bot.get_channel_by_name(config.ask_channel_name, guild) answer += "\nAsk channel:\n\t" if ask_channel is not None: answer += "{0} Enabled: {1.mention}".format( get_check_emoji(True), ask_channel) else: answer += "{0} Not enabled".format(get_check_emoji(False)) log_channel = self.bot.get_channel_by_name(config.log_channel_name, guild) answer += "\nLog channel:\n\t" if log_channel is not None: answer += "{0} Enabled: {1.mention}".format( get_check_emoji(True), log_channel) else: answer += "{0} Not enabled".format(get_check_emoji(False)) await ctx.send(answer) return
async def makesay(self, ctx: discord.ext.commands.Context, *, message: str): """Makes the bot say a message If it's used directly on a text channel, the bot will delete the command's message and repeat it itself If it's used on a private message, the bot will ask on which channel he should say the message.""" if is_private(ctx.message.channel): description_list = [] channel_list = [] prev_server = None num = 1 for server in self.bot.guilds: author = self.bot.get_member(ctx.message.author.id, server) bot_member = self.bot.get_member(self.bot.user.id, server) # Skip servers where the command user is not in if author is None: continue # Check for every channel for channel in server.text_channels: author_permissions = author.permissions_in( channel) # type: discord.Permissions bot_permissions = bot_member.permissions_in( channel) # type: discord.Permissions # Check if both the author and the bot have permissions to send messages and add channel to list if (author_permissions.send_messages and bot_permissions.send_messages) and \ (ctx.message.author.id in config.owner_ids or author_permissions.administrator): separator = "" if prev_server is not server: separator = "---------------\n\t" description_list.append( "{2}{3}: **#{0}** in **{1}**".format( channel.name, server.name, separator, num)) channel_list.append(channel) prev_server = server num += 1 if len(description_list) < 1: await ctx.send( "We don't have channels in common with permissions.") return await ctx.send( "Choose a channel for me to send your message (number only): \n\t0: *Cancel*\n\t" + "\n\t".join(["{0}".format(i) for i in description_list])) def check(m): return m.author == ctx.message.author and m.channel == ctx.message.channel try: answer = await self.bot.wait_for("message", timeout=60.0, check=check) answer = int(answer.content) if answer == 0: await ctx.send("Changed your mind? Typical human.") return await channel_list[answer - 1].send(message) await ctx.send("Message sent on {0} ({1})".format( channel_list[answer - 1].mention, channel_list[answer - 1].guild)) except IndexError: await ctx.send( "That wasn't in the choices, you ruined it. Start from the beginning." ) except ValueError: await ctx.send("That's not a valid answer!") except asyncio.TimeoutError: await ctx.send("... are you there? Fine, nevermind!") else: await ctx.message.delete() await ctx.message.channel.send(message)
def __global_check(self, ctx): return is_private(ctx.channel) or \ ctx.channel.id not in self.ignored.get(ctx.guild.id, []) or checks.is_owner_check(ctx) \ or checks.check_guild_permissions(ctx, {'manage_channels': True})