async def show_snipes(self, ctx, amount: int = 5, channel: discord.TextChannel = None): """ Select the last N snipes from this channel. """ if channel: if not ctx.author.guild_permissions.manage_messages: return await ctx.send( "Sorry, you need to have 'Manage Messages' to view another channel." ) if channel.is_nsfw() and not ctx.channel.is_nsfw(): return await ctx.send( "No peeping NSFW stuff in here you dirty boi/gurl.") channel = channel or ctx.channel query = "SELECT * FROM snipe_deletes WHERE guild_id = $2 AND channel_id = $3 ORDER BY id DESC LIMIT $1;" results = await self.bot.pool.fetch(query, amount, ctx.guild.id, channel.id) dict_results = [dict(result) for result in results] if results else [] local_snipes = [ snipe for snipe in self.snipe_deletes if snipe["channel_id"] == channel.id ] full_results = dict_results + local_snipes if not full_results: return await ctx.send("No snipes for this channel.") full_results = sorted(full_results, key=lambda d: d["delete_time"], reverse=True)[:amount] embeds = self._gen_delete_embeds(full_results) pages = RoboPages( source=SnipePageSource(range(0, len(embeds)), embeds), delete_message_after=True, ) await pages.start(ctx)
async def show_edit_snipes(self, ctx, amount: int = 5, channel: discord.TextChannel = None): """ Edit snipes, shows the last N from. Must have manage_messages to choose a different channel. """ # let's check that amount is an int, clear inputs if channel: if not ctx.author.guild_permissions.manage_messages: return await ctx.send( "Sorry, you need to have 'Manage Messages' to view another channel." ) if not 0 < amount < 15: raise commands.BadArgument("No more than 15 indexes at once.") channel = channel or ctx.channel query = "SELECT * FROM snipe_edits WHERE guild_id = $2 AND channel_id = $3 ORDER BY id DESC LIMIT $1;" results = await self.bot.pool.fetch(query, amount, ctx.guild.id, channel.id) dict_results = [dict(result) for result in results] local_snipes = [ snipe for snipe in self.snipe_edits if snipe["channel_id"] == channel.id ] full_results = dict_results + local_snipes full_results = sorted(full_results, key=lambda d: d["edited_time"], reverse=True)[:amount] embeds = await self._gen_edit_embeds(full_results) if not embeds: return await ctx.send("No edit snipes for this channel.") pages = RoboPages( source=SnipePageSource(range(0, len(embeds)), embeds), delete_message_after=True, ) await pages.start(ctx)
async def words(self, ctx: Context, character: str): """ Return the words a Kanji is used in, or in conjuction with. """ if len(character) > 1: raise commands.BadArgument("Only one Kanji please.") url = f"{BASE_URL}/words/{character}" async with self.bot.session.get(url) as response: data = await response.json() words_data = [WordsPayload(**payload) for payload in data] embeds = [ KanjiEmbed.from_words(character, kanji) for kanji in words_data ] real_embeds = [embed for sublist in embeds for embed in sublist] fixed_embeds = [ embed.set_footer(text=( f"{embed.footer.text} :: {real_embeds.index(embed) + 1}/{len(real_embeds)}" if embed.footer.text else f"{real_embeds.index(embed) + 1}/{len(real_embeds)}")) for embed in real_embeds ] menu = RoboPages( KanjiAPISource(fixed_embeds), delete_message_after=False, clear_reactions_after=False, ) await menu.start(ctx)
async def jisho(self, ctx: Context, *, query: str): """ Query the Jisho api with your kanji/word. """ async with self.bot.session.get(JISHO_WORDS_URL, params={"keyword": query}) as response: if response.status == 200: data = (await response.json())["data"] else: raise commands.BadArgument("Not a valid query for Jisho.") if not data: raise commands.BadArgument("Not a valid query for Jisho.") jisho_data = [JishoPayload(**payload) for payload in data] embeds = [ KanjiEmbed.from_jisho(query, item) for item in jisho_data ] fixed_embeds = [ embed.set_footer(text=( f"{embed.footer.text} :: {embeds.index(embed) + 1}/{len(embeds)}" if embed.footer. text else f"{embeds.index(embed) + 1}/{len(embeds)}")) for embed in embeds ] menu = RoboPages( KanjiAPISource(fixed_embeds), delete_message_after=False, clear_reactions_after=False, ) await menu.start(ctx)
async def send_cog_help(self, cog: commands.Cog): pages = [] await self.format_commands( cog, await self.filter_commands(cog.get_commands(), sort=True), pages=pages ) total = len(pages) for i, embed in enumerate(pages, start=1): embed.title = f"Page {i}/{total}: {embed.title}" pg = RoboPages(HelpSource(range(0, len(pages)), pages)) await pg.start(self.context)
async def stroke_order(self, ctx: Context, kanji: str) -> None: responses = [] for char in kanji: url = quote(f"https://jisho.org/search/{char}#kanji", safe="/:?&") data = await ctx.bot.session.get(url) soup = bs4.BeautifulSoup(await data.content.read(), "html.parser") response = JishoKanji(char, soup, url) responses.append(response) embeds = self._gen_kanji_embed(responses) source = KanjiAPISource(embeds) menu = RoboPages(source=source) await menu.start(ctx)
async def todo_list(self, ctx: Context) -> None: """A list of todos for you.""" query = """ SELECT * FROM todos WHERE owner_id = $1 ORDER BY id ASC LIMIT 100; """ records = await self.bot.pool.fetch(query, ctx.author.id) if not records: await ctx.send( "You appear to have no active todos, look at how productive you are." ) return embeds = self._gen_todos(records) pages = RoboPages(source=SimpleListSource(embeds), ctx=ctx) await pages.start()
async def kanji(self, ctx: Context, character: str): """ Return data on a single Kanji from the KanjiDev API. """ if len(character) > 1: raise commands.BadArgument("Only one Kanji please.") url = f"{BASE_URL}/kanji/{character}" async with self.bot.session.get(url) as response: data = await response.json() kanji_data = KanjiPayload(**data) embed = KanjiEmbed.from_kanji(kanji_data) menu = RoboPages(KanjiAPISource([embed])) await menu.start(ctx)
async def send_bot_help( self, mapping: Mapping[commands.Cog, List[Union[commands.Group, commands.Command]]], ): pages = [] for cog, cmds in mapping.items(): cmds = await self.filter_commands(cmds, sort=True) await self.format_commands(cog, cmds, pages=pages) total = len(pages) for i, embed in enumerate(pages, start=1): embed.title = f"Page {i}/{total}: {embed.title}" pg = RoboPages(HelpSource(range(0, len(pages)), pages)) await pg.start(self.context)
async def stroke_order(self, ctx: Context, kanji: str) -> None: """ Returns an animation of the stroke order of the provided kana/kanji. """ responses = [] for char in kanji: url = quote(f"https://jisho.org/search/{char}#kanji", safe="/:?&") data = await ctx.bot.session.get(url) soup = bs4.BeautifulSoup(await data.content.read(), "html.parser") response = JishoKanji(char, soup, url) responses.append(response) embeds = self._gen_kanji_embed(responses) source = SimpleListSource(embeds) menu = RoboPages(source=source, ctx=ctx) await menu.start()
async def _reddit(self, ctx: commands.Context, subreddit: str, sort_by: str = "hot"): """ Main Reddit command, subcommands to be added. """ sub_re = re.compile(r"(/?(r/)?(?P<subname>.*))") sub_search = sub_re.search(subreddit) if sub_search.group("subname"): subreddit = sub_search["subname"] embeds = await self._perform_search(str(ctx.author), ctx.channel, subreddit, sort_by) if not embeds: raise commands.BadArgument("Bad subreddit.", subreddit) pages = RoboPages(source=SubredditPageSource(embeds), delete_message_after=True) await pages.start(ctx)
async def todo_list(self, ctx): """ A list of todos for you. """ query = ( """ SELECT * FROM todos WHERE owner_id = $1 ORDER BY id ASC LIMIT 100; """ ) records = await self.bot.pool.fetch(query, ctx.author.id) if not records: return await ctx.send( "You appear to have no active todos, look at how productive you are." ) embeds = self._gen_todos(records) pages = RoboPages( source=TodoPageSource(range(0, len(embeds)), embeds), delete_message_after=True, ) await pages.start(ctx)
async def _urban(self, ctx, *, word): """Searches urban dictionary.""" url = "http://api.urbandictionary.com/v0/define" async with ctx.session.get(url, params={"term": word}) as resp: if resp.status != 200: return await ctx.send( f"An error occurred: {resp.status} {resp.reason}") js = await resp.json() data = js.get("list", []) if not data: return await ctx.send("No results found, sorry.") pages = RoboPages(UrbanDictionaryPageSource(data)) try: await pages.start(ctx) except menus.MenuError as e: await ctx.send(e)
async def danbooru(self, ctx: Context, *, params: str) -> None: """Danbooru command. Access danbooru commands. This command uses a flag style syntax. The following options are valid. `*` denotes it is a mandatory argument. `+t | ++tags`: The tags to search Gelbooru for. `*` (uses logical AND per tag) `+l | ++limit`: The maximum amount of posts to show. Cannot be higher than 30. Examples: ``` !gelbooru ++tags lemon - search for the 'lemon' tag. - NOTE: if your tag has a space in it, replace it with '_' !danbooru ++tags melon -rating:explicit - search for the 'melon' tag, removing posts marked as 'explicit` !danbooru ++tags apple orange rating:safe - Search for the 'apple' AND 'orange' tags, with only 'safe' results. ``` """ aiohttp_params = {} parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False, prefix_chars="+") parser.add_argument("+t", "++tags", nargs="+", required=True) parser.add_argument("+l", "++limit", type=int, default=40) try: real_args = parser.parse_args(shlex.split(params)) except SystemExit as f**k: raise commands.BadArgument( "Your flags could not be parsed.") from f**k except Exception as err: await ctx.send(f"Parsing your args failed: {err}.") return current_config = await self.get_booru_config( getattr(ctx.guild, "id", -1)) if real_args.limit: limit = real_args.limit if not 1 < real_args.limit <= 30: limit = 30 aiohttp_params.update({"limit": limit}) lowered_tags = [tag.lower() for tag in real_args.tags] tags = set(lowered_tags) common_elems = tags & current_config.blacklist if common_elems: raise BlacklistedBooru(common_elems) aiohttp_params.update({"tags": " ".join(lowered_tags)}) async with ctx.typing(): async with self.bot.session.get( self.danbooru_config.endpoint, params=aiohttp_params, auth=self.danbooru_config.auth, ) as resp: data = await resp.text() if not data: ctx.command.reset_cooldown(ctx) raise commands.BadArgument( "Got an empty response... bad search?") json_data = json.loads(data) if not json_data: ctx.command.reset_cooldown(ctx) raise commands.BadArgument( "The specified query returned no results.") embeds = self._gen_danbooru_embeds(json_data, current_config) if not embeds: fmt = "Your search had results but all of them contained blacklisted tags" if "loli" in lowered_tags: fmt += "\nPlease note that Danbooru does not support 'loli'." raise commands.BadArgument(fmt) pages = RoboPages(source=SimpleListSource(embeds[:30]), ctx=ctx) await pages.start()
async def gelbooru(self, ctx: Context, *, params: str): """This command uses a flag style syntax. The following options are valid. `*` denotes it is a mandatory argument. `+t | ++tags`: The tags to search Gelbooru for. `*` (uses logical AND per tag) `+l | ++limit`: The maximum amount of posts to show. Cannot be higher than 30. `+p | ++pid`: Page ID to search. Handy when posts begin to repeat. `+c | ++cid`: Change ID of the post to search for(?) Examples: ``` !gelbooru ++tags lemon - search for the 'lemon' tag. - NOTE: if your tag has a space in it, replace it with '_' !gelbooru ++tags melon -rating:explicit - search for the 'melon' tag, removing posts marked as 'explicit` !gelbooru ++tags apple orange rating:safe ++pid 2 - Search for the 'apple' AND 'orange' tags, with only 'safe' results, but on Page 2. - NOTE: if not enough searches are returned, page 2 will cause an empty response. ``` """ aiohttp_params = {} aiohttp_params.update({"json": 1}) parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False, prefix_chars="+") parser.add_argument("+l", "++limit", type=int, default=40) parser.add_argument("+p", "++pid", type=int) parser.add_argument("+t", "++tags", nargs="+", required=True) parser.add_argument("+c", "++cid", type=int) try: real_args = parser.parse_args(shlex.split(params)) except SystemExit as f**k: raise commands.BadArgument( "Your flags could not be parsed.") from f**k except Exception as err: await ctx.send(f"Parsing your args failed: {err}") return current_config = await self.get_booru_config( getattr(ctx.guild, "id", -1)) if real_args.limit: aiohttp_params.update({"limit": int(real_args.limit)}) if real_args.pid: aiohttp_params.update({"pid": real_args.pid}) if real_args.cid: aiohttp_params.update({"cid", real_args.cid}) lowered_tags = [tag.lower() for tag in real_args.tags] tags_set = set(lowered_tags) common_elems = tags_set & current_config.blacklist if common_elems: raise BlacklistedBooru(common_elems) aiohttp_params.update({"tags": " ".join(lowered_tags)}) async with ctx.typing(): async with self.bot.session.get( self.gelbooru_config.endpoint, params=aiohttp_params, auth=self.gelbooru_config.auth, ) as resp: data = await resp.text() if not data: ctx.command.reset_cooldown(ctx) raise commands.BadArgument( "Got an empty response... bad search?") json_data = json.loads(data) if not json_data: ctx.command.reset_cooldown(ctx) raise commands.BadArgument( "The specified query returned no results.") embeds = self._gen_gelbooru_embeds(json_data, current_config) if not embeds: raise commands.BadArgument( "Your search had results but all of them contain blacklisted tags." ) pages = RoboPages( source=LewdPageSource(embeds[:30]), delete_message_after=False, clear_reactions_after=True, ) await pages.start(ctx)