async def colour( self, ctx: vbu.Context, *, colour: typing.Union[vbu.converters.ColourConverter, discord.Colour, discord.Role, discord.Member], ): """ Get you a colour. """ # https://www.htmlcsscolor.com/preview/gallery/5dadec.png if isinstance(colour, discord.Role): colour = colour.colour elif isinstance(colour, discord.Member): try: colour = [i for i in colour.roles if i.colour.value > 0][-1].colour except IndexError: colour = discord.Colour(0) hex_colour = colour.value with vbu.Embed(colour=hex_colour, title=f"#{hex_colour:0>6X}") as embed: embed.set_image( url= f"https://www.htmlcsscolor.com/preview/gallery/{hex_colour:0>6X}.png" ) await ctx.send(embed=embed)
async def rtdm_js(self, ctx, *, obj: str): """ Gives you a documentation link for a Javascript entity. """ url = "https://developer.mozilla.org/api/v1/search" params = { 'q': obj, 'local': 'en-US', } headers = { 'User-Agent': self.bot.user_agent, } async with self.bot.session.get(url, params=params, headers=headers) as r: data = await r.json() if not data['documents']: return await ctx.send("I couldn't find anything. Sorry.") embed = vbu.Embed(use_random_colour=True, description="") for i in data['documents']: if 'web/javascript' not in i['slug']: continue embed.description += f"[`{i['title']}`](https://developer.mozilla.org{i['mdn_url']})\n" if embed.description.count("\n") >= 8: break return await ctx.send(embed=embed)
async def rtdm_dotnet(self, ctx, *, obj: str): """ Gives you a documentation link for a .NET entity. """ url = "https://docs.microsoft.com/api/apibrowser/dotnet/search" params = { 'api-version': '0.2', 'search': obj, 'locale': 'en-us', '$filter': "monikers/any(t: t eq 'net-5.0')", } headers = { 'User-Agent': self.bot.user_agent, } async with self.bot.session.get(url, params=params, headers=headers) as r: data = await r.json() if not data['results']: return await ctx.send("I couldn't find anything. Sorry.") embed = vbu.Embed(use_random_colour=True, description="") for i in data['results']: embed.description += f"[`{i['displayName']}`]({i['url']})\n" if embed.description.count("\n") >= 8: break return await ctx.send(embed=embed)
async def wolfram(self, ctx: vbu.Context, *, search: str): """ Send a query to WolframAlpha. """ # Build our request params = { "input": search, "appid": self.bot.config['api_keys']['wolfram'], "format": "image", "output": "json", } headers = { "User-Agent": self.bot.user_agent, } # Send our request async with self.bot.session.get( "https://api.wolframalpha.com/v2/query", params=params, headers=headers) as r: data = json.loads(await r.text()) # Send output try: pod = data['queryresult']['pods'][1] embed = vbu.Embed( title=pod['title'], use_random_colour=True, ).set_image(url=pod['subpods'][0]['img']['src'], ) return await ctx.send(embed=embed) except (KeyError, IndexError): return await ctx.send("No results for that query!")
async def dnd_class(self, ctx: commands.Context, *, class_name: str): """ Gives you information on a D&D class. """ # Get our data async with ctx.typing(): data = await self.send_web_request("classes", class_name) if not data: return await ctx.send( "I couldn't find any information for that class.") # Make embed embed = vbu.Embed( use_random_colour=True, title=data['name'], ).add_field( "Proficiencies", ", ".join([i['name'] for i in data['proficiencies']]), ).add_field( "Saving Throws", ", ".join([i['name'] for i in data['saving_throws']]), ).add_field( "Starting Equipment", "\n".join([ f"{i['quantity']}x {i['equipment']['name']}" for i in data['starting_equipment'] ]), ) # And send return await ctx.send(embed=embed)
async def points_leaderboard_show(self, ctx: vbu.Context): """ Show the points leaderboard without creating a new one. """ # Make some assertions assert ctx.guild # See if they're running a subcommand if ctx.invoked_subcommand is not None: return # Get data async with vbu.Database() as db: rows = await db( "SELECT * FROM user_points WHERE guild_id=$1 AND points > 0 ORDER BY points DESC", ctx.guild.id) # Format it into an embed valid_users = [] for i in rows: member = ctx.guild.get_member(i['user_id']) if member is None: continue valid_users.append(f"{member.mention}: {i['points']:,}") if len(valid_users) >= 30: break with vbu.Embed(use_random_colour=True) as embed: embed.description = '\n'.join( valid_users) or 'Nobody is on here :c' # Output await ctx.send(embed=embed)
async def whois(self, ctx: vbu.Context, user: discord.Member = None): """ Give you some information about a user. """ # Set up our intial vars user = user or ctx.author embed = vbu.Embed(use_random_colour=True) embed.set_author_to_user(user) # Get the user account creation time create_value = f"{discord.utils.format_dt(user.created_at)}\n{discord.utils.format_dt(user.created_at, 'R')}" embed.add_field("Account Creation Time", create_value, inline=False) # Get the user guild join time if ctx.guild: join_value = f"{discord.utils.format_dt(user.joined_at)}\n{discord.utils.format_dt(user.joined_at, 'R')}" embed.add_field("Guild Join Time", join_value, inline=False) # Set the embed thumbnail embed.set_thumbnail(user.display_avatar.with_size(1024).url) # Sick if isinstance(ctx, commands.SlashContext): return await ctx.interaction.response.send_message(embed=embed) else: return await ctx.send(embed=embed)
def get_embed_from_cache(self, cache, obj) -> dict: """ Build up some kwargs from the cache and a given input object. """ item_casefold = obj.casefold() item_underscore_casefold = item_casefold.replace(" ", "_") split = item_casefold.split('.') split_underscore = [i.replace(" ", "_") for i in split] outputs = [] for key, link in cache.items(): # Key would be something like 'TextChannel.fetch', now 'textchannel.fetch' key_casefold = key.casefold() # See if we got an exact match if item_casefold in key_casefold or item_underscore_casefold in key_casefold: if item_casefold == key_casefold or item_underscore_casefold == key_casefold: outputs.append((key, link, 20,)) else: outputs.append((key, link, 15,)) # See if we're looking for a method if len(split) == 1: continue # Search by method if split[1] and split[1] in key_casefold or split_underscore[1] in key_casefold: if split[1] == key_casefold or split_underscore[1] == key_casefold: outputs.append((key, link, 12,)) else: outputs.append((key, link, 10,)) # Search by class if split[0] and split[0] in key_casefold or split_underscore[0] in key_casefold: if split[0] == key_casefold or split_underscore[0] == key_casefold: outputs.append((key, link, 7,)) else: outputs.append((key, link, 5,)) # Hey maybe they didn't say anything useful if len(outputs) == 0: return {"content": "I couldn't find anything. Sorry."} # Yo there's a result. Let's embed that. embed = vbu.Embed(use_random_colour=True) outputs.sort(key=lambda i: (i[2], i[0]), reverse=True) embed.description = "" for key, url, weight in outputs: v = f"[`{key}`]({url})\n" if v in embed.description: continue embed.description += v if embed.description.count("\n") >= 8: break return {"embed": embed}
async def pypi(self, ctx: vbu.Context, package_name: str): """ Grab data from PyPi. """ # Get data async with self.bot.session.get(f"https://pypi.org/pypi/{package_name}/json") as r: if r.status != 200: with vbu.Embed(use_random_colour=True) as embed: embed.title = f"Module `{package_name}` not found" embed.description = f"[Search Results](https://pypi.org/search/?q={package_name})" return await ctx.send(embed=embed) data = await r.json() # Format into an embed with vbu.Embed(use_random_colour=True) as embed: embed.set_author(name=data['info']['name'], url=f"https://pypi.org/project/{package_name}") embed.description = data['info']['summary'] return await ctx.send(embed=embed)
def formatter(menu, items): # Create the embed commands_embed = vbu.Embed(title="Command Data (times run)") # Add the total count footer commands_embed.set_footer(text=f"Total: {total_count}") # Add the command list to the emebd commands_embed.description = "\n".join(items) # Return the embed return commands_embed
async def fox(self, ctx: commands.Context): """ Give you a foxxo picture! """ await ctx.trigger_typing() headers = {"User-Agent": self.bot.user_agent} async with self.bot.session.get("https://randomfox.ca/floof/", headers=headers) as r: data = await r.json() with vbu.Embed(use_random_colour=True) as embed: embed.set_image(url=data['image']) await ctx.send(embed=embed)
def default_ranked_list_formatter(m: "Paginator", d: typing.List[str]): """ The default list formatter for embeds. Takes the paginator instance and the list of strings to be displayed, and returns a dictionary of kwargs for a `Message.edit`. """ return vbu.Embed( use_random_colour=True, description="\n".join([ f"{i}. {o}" for i, o in enumerate(d, start=(m.current_page * m.per_page) + 1) ]), ).set_footer(f"Page {m.current_page + 1}/{m.max_pages}", )
async def dog(self, ctx: commands.Context): """ Give you a doggo picture! """ await ctx.trigger_typing() headers = {"User-Agent": self.bot.user_agent} url = "https://dog.ceo/api/breeds/image/random" async with self.bot.session.get(url, headers=headers) as r: data = await r.json() if data['status'] == "error": return await ctx.send("No dogs were found :(") with vbu.Embed(use_random_colour=True) as embed: embed.set_image(url=data['message']) await ctx.send(embed=embed)
async def enlarge( self, ctx: vbu.Context, target: typing.Union[discord.Member, discord.User, discord.Emoji, discord.PartialEmoji] = None, ): """ Enlarges the avatar or given emoji. """ target = target or ctx.author if isinstance(target, (discord.User, discord.Member, discord.ClientUser)): url = target.display_avatar.url elif isinstance(target, (discord.Emoji, discord.PartialEmoji)): url = target.url with vbu.Embed(color=0x1) as embed: embed.set_image(url=str(url)) await ctx.send(embed=embed)
async def on_leaderboard_update(self, guild: discord.Guild): """ Update the leaderboard message. """ # See if we can get the leaderboard message class FakeContext: bot = self.bot leaderboard_message_url = self.bot.guild_settings[ guild.id]['leaderboard_message_url'] if not leaderboard_message_url: return try: message = await commands.MessageConverter().convert( FakeContext, leaderboard_message_url) except commands.BadArgument: return if message is None: return # Get data async with vbu.Database() as db: rows = await db( "SELECT * FROM user_points WHERE guild_id=$1 AND points > 0 ORDER BY points DESC", guild.id) # Format it into an embed valid_users = [] for i in rows: member = guild.get_member(i['user_id']) if member is None: continue valid_users.append(f"{member.mention}: {i['points']:,}") if len(valid_users) >= 30: break with vbu.Embed(use_random_colour=True) as embed: embed.description = '\n'.join( valid_users) or 'Nobody is on here :c' # Output await message.edit(content=None, embed=embed)
async def _show_pp(self, ctx: commands.SlashContext) -> None: async with vbu.DatabaseConnection() as db: cache: utils.CachedUser = await utils.get_user_cache( ctx.author.id, db=db, bot=self.bot, logger=self.logger, ) with vbu.Embed() as embed: embed.colour = utils.BLUE embed.set_author( name=f"{ctx.author.display_name}'s PP", icon_url=ctx.author.avatar.url, ) embed.description = f"8{('='*(cache.pp.size // 50))[:1000]}D" embed.add_field( "Stats", f"Size - {cache.pp.size}\nMultiplier - {cache.pp.multiplier}", ) await ctx.interaction.response.send_message(embed=embed)
async def npm(self, ctx: vbu.Context, package_name: str): """ Check NPM for a package. """ # Get our data async with self.bot.session.get(f"http://registry.npmjs.com/{package_name}/") as e: if e.status == 404: await ctx.send(f"I could not find anything about `{package_name}` :c") return if e.status != 200: await ctx.send("Something went wrong, try again later...") return data = await e.json() # make a lil embed with vbu.Embed(use_random_colour=True) as embed: embed.set_author(name=data['name'], url=f"https://www.npmjs.com/package/{package_name}") embed.description = data['description'] await ctx.send(embed=embed)
async def cat(self, ctx: commands.Context): """ Give you a cat picture! """ await ctx.trigger_typing() headers = { "User-Agent": self.bot.user_agent, "x-api-key": self.bot.config['api_keys']['cat_api'] } params = {"limit": 1} async with self.bot.session.get( "https://api.thecatapi.com/v1/images/search", params=params, headers=headers) as r: data = await r.json() if not data: return await ctx.send("I couldn't find that breed of cat.") with vbu.Embed(use_random_colour=True) as embed: embed.set_image(url=data[0]['url']) await ctx.send(embed=embed)
def formatter( menu: utils.Paginator, items: typing.List[utils.LootableItem]) -> vbu.Embed: with vbu.Embed() as embed: output = [] for item in items: output.append( f"2x {item.emoji} **{item.name}** ─ {item.type.replace('_', ' ').title()}" + f"\n **{item.rarity.replace('_', ' ').title()}**" + f" ─ `{item.id}` {item.description}") embed.set_author( name=f"{ctx.author.display_name}'s inventory", icon_url=ctx.author.avatar.url, ) embed.description = ( f"use [/item-info [item]]({self.bot.hyperlink}) for more information.\n\n" + "\n\n".join(output)) embed.set_footer( f"Page {menu.current_page + 1}/{menu.max_pages}") return embed
async def nuget(self, ctx: vbu.Context, package_name: str): """ Check nuget for a package. """ # Get our data async with self.bot.session.get(f"https://azuresearch-usnc.nuget.org/query?q={package_name}") as e: if e.status != 200: await ctx.send("Something went wrong, try again later...") return data = await e.json() # make a lil embed with vbu.Embed(use_random_colour=True) as embed: if data['data']: embed.set_author(name=data['data'][0]['title'], url=f"https://www.nuget.org/packages/{data['data'][0]['id']}") embed.description = data['data'][0]['description'] else: await ctx.send(f"I could not find anything for `{package_name}` :c") return await ctx.send(embed=embed)
async def dnd_condition(self, ctx: commands.Context, *, condition_name: str): """ Gives you information on a D&D condition. """ # Get our data async with ctx.typing(): data = await self.send_web_request("conditions", condition_name) if not data: return await ctx.send( "I couldn't find any information for that condition.") # Make an embed embed = vbu.Embed( use_random_colour=True, title=data['name'], description="\n".join(data['desc']), ) # And send return await ctx.send(embed=embed)
async def grandexchange(self, ctx: vbu.Context, *, item: str): """ Get the value of an item on the grand exchange (OSRS). """ async with ctx.typing(): if item.lower() in ['random']: item_id = random.choice(list(self.item_ids.values())) else: item = item.capitalize() item_id = self.item_ids.get(item) if item_id: item_dict = await self.get_item_details_by_id(item_id) item_value = await self.parse_item_value(item_dict, return_int=False) name = item_dict['name'] item_page_url = API_BASE_URL + f"a=373/{name.replace(' ', '+')}/viewitem?obj={item_id}" with vbu.Embed() as embed: embed.set_author(name=name, url=item_page_url, icon_url=item_dict['icon']) embed.set_thumbnail(url=item_dict['icon_large']) embed.add_field('Value', f'{item_value} coins', inline=False) embed.add_field(f'Examine {name}', item_dict['description'], inline=False) embed.add_field('Members', MEMBERS_MAPPING[item_dict['members']], inline=False) return await ctx.send(embed=embed) else: return await ctx.send('Item not found')
def default_list_formatter( m: "Paginator", d: _EmbedOrStringList) -> typing.Union[_EmbedsDict, discord.Embed]: """ The default list formatter for embeds. Takes the paginator instance and the list of data to be displayed, and returns a dictionary of kwargs for a `Message.edit`. """ if all(isinstance(i, discord.Embed) for i in d): if typing.TYPE_CHECKING: typed_embeds_d = typing.cast(_EmbedsDict, {"embeds": d}) else: typed_embeds_d = {"embeds": d} return typed_embeds_d else: if typing.TYPE_CHECKING: typed_str_d = typing.cast(typing.List[str], d) else: typed_str_d = d return vbu.Embed( use_random_colour=True, description="\n".join(typed_str_d), ).set_footer(f"Page {m.current_page + 1}/{m.max_pages}", )
async def upgrades(self, ctx: commands.Context): """ Show you your upgrades and the price of the next level. """ # The output that we want to build message = [] # A list of text to send emote_string_list = [] # Their emoji progress bar # Grab their upgrades from the database if not await utils.check_registered(self.bot, ctx.author.id): return await ctx.send("Please use the `register` command before using this bot!") async with vbu.Database() as db: upgrades = await db( """SELECT rod_upgrade, bait_upgrade, line_upgrade, lure_upgrade, crate_chance_upgrade, weight_upgrade, crate_tier_upgrade, bleach_upgrade, toys_upgrade, amazement_upgrade, mutation_upgrade, big_servings_upgrade, hygienic_upgrade, feeding_upgrade FROM user_upgrades WHERE user_id = $1""", ctx.author.id, ) # Find the positions for the different upgrades positions = [(2140, 910), (1300, 1070), (1310, 1260), (1710, 1080), (2200, 1470), (1760, 1290), (2180, 1450), (2670, 170), (240, 210), (840, 110), (550, 1460), (840, 750), (1930, 190), (1020, 1290)] # Makes a list of the upgrades names list_of_upgrades = [single for single in upgrades[0].keys()] # Create an id for the image id = "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(10) ) # Set the file prefix, shadow path, and file name file_prefix = "C:/Users/JT/Pictures/Aqua/assets/images" shadow = f"{file_prefix}/background/Room Walls/shadow-export.png" file_name = f"{file_prefix}/background/Room Walls/Upgrades_Wall/User Walls/{id}user_upgrade_room.png" # Open the shadow and background, make a copy of background shadow = Image.open(shadow).convert("RGBA") background = Image.open( f"{file_prefix}/background/Room Walls/Upgrade_Wall-export.png" ).convert("RGBA") new_background = background.copy() # For each upgrade open the upgrade at it's tier and paste it to the background for i, upgrade in enumerate(upgrades[0]): added_upgrade = Image.open( f"{file_prefix}/background/Room Walls/Upgrades_Wall/{list_of_upgrades[i]}_tier_{upgrades[0][list_of_upgrades[i]]+1}-export.png").convert("RGBA") new_background.paste(added_upgrade, positions[i], added_upgrade) # Paste the shadow on top finally new_background.paste(shadow, (0, 0), shadow) # Save the background as a png with the file name and send it new_background.save(file_name, format="PNG") await ctx.send(file=discord.File(file_name)) # Set up variables tier = 0 tree_number = 0 unknown_str = "???" # For each upgrade... for upgrade, level in upgrades[0].items(): # If it isnt the user id if upgrade != "user_id": # Set the tier to 1 more than it was, set the description and name tier += 1 description = self.UPGRADE_DESCRIPTIONS[upgrade] name = " ".join(upgrade.split("_")).title() # Get the cost of an upgrade cost_string = f"{self.UPGRADE_COST_LIST[int(level)]:,} {EMOJIS['sand_dollar']}" # If its the first tier its also the parent upgrade if tier == 1: parent_one = upgrade # Set up variables upgrade specific left_bar = EMOJIS["bar_L"] start = "" start_two = "" emote = EMOJIS["bar_1"] # If the tier is 2 or 5 it's in the second tier of upgrades if tier in (2, 5): # Set the cost with the second list and set it to the second parent upgrade cost_string = f"{self.UPGRADE_COST_LIST_TWO[int(level)]:,} {EMOJIS['sand_dollar']}" parent_two = upgrade # If they don't have the full upgrade of the previous one, make them unknown if upgrades[0][parent_one] != 5: description = unknown_str name = unknown_str cost_string = unknown_str # Set the start emotes to the correct emojis start = EMOJIS["straight_branch"] start_two = EMOJIS["straight"] emote = EMOJIS["bar_2"] left_bar = EMOJIS["bar_L_branch"] # If its tier 5 set some more emojis if tier == 5: start = EMOJIS["branch"] start_two = EMOJIS["straight"] # If its in tiers 3 or 6 (the first set of tier 3) elif tier in (3, 6): # Set the cost of upgrade to cost list three cost_string = f"{self.UPGRADE_COST_LIST_THREE[int(level)]:,} {EMOJIS['sand_dollar']}" # If the parent isnt fully upgraded, set these to unknown if upgrades[0][parent_two] != 5: description = unknown_str name = unknown_str cost_string = unknown_str # Set variables to the correct emotes start = f"{EMOJIS['bar_empty']}{EMOJIS['straight_branch']}" emote = EMOJIS["bar_3"] left_bar = EMOJIS["bar_L_straight"] # If the tier is 3 set some other correct emotes if tier == 3: start_two = f"{EMOJIS['straight']}{EMOJIS['straight']}" start = ( f"{EMOJIS['straight']}{EMOJIS['straight_branch']}" ) # If not set another correct emote else: start_two = f"{EMOJIS['bar_empty']}{EMOJIS['straight']}" # Else if its in tier 4 or 7 (in the second set of tier 3 upgrades) elif tier in (4, 7): # Set the cost of upgrade to cost list three cost_string = f"{self.UPGRADE_COST_LIST_THREE[int(level)]:,} {EMOJIS['sand_dollar']}" # If the parent isnt fully upgraded, set these to unknown if upgrades[0][parent_two] != 5: description = unknown_str name = unknown_str cost_string = unknown_str # Set variables to the correct emotes emote = EMOJIS["bar_3"] left_bar = EMOJIS["bar_L_straight"] # If the tier is 4 set some other correct emotes if tier == 4: start_two = f"{EMOJIS['straight']}{EMOJIS['straight']}" start = f"{EMOJIS['straight']}{EMOJIS['branch']}" # If not set other correct emotes else: start_two = f"{EMOJIS['bar_empty']}{EMOJIS['straight']}" start = f"{EMOJIS['bar_empty']}{EMOJIS['branch']}" # If they're fully upgraded if level == 5: cost_string = "Fully upgraded." # Each level they have is a full bar emoji, up to 5 characters long emote_string_list.clear() # Clear our emoji list first for _ in range(level): emote_string_list.append(emote) while len(emote_string_list) < 5: emote_string_list.append(EMOJIS["bar_e"]) # Set up the progress bar progress_bar = ( f"{left_bar}{''.join(emote_string_list)}{EMOJIS['bar_R']}" ) # New line for f strings new_line = "\n" # Set up the message for the upgrade message.append( ( f"{start}{progress_bar}", f"{start_two}**{name}: (Lvl. {level}.): {cost_string}**{new_line}{start_two}*{description}*", ) ) # If its the last tier of the tree if tier == 7: # Append some empty spaces message.append(("** **", "** **")) # Set tree to 2 and tier back to 0 tree_number += 1 tier = 0 # Set up the embed with titles for each field for time, message_data in enumerate(message): if time <= 7: if time == 0: embed = vbu.Embed() embed.add_field( name="The Way of the Fish", value="These upgrades have to do with fishing", inline=False, ) embed.add_field( name=message_data[1], value=message_data[0], inline=False ) if time > 7: if time == 8: embed_2 = vbu.Embed() embed_2.add_field( name="The Way of the Tank", value="These upgrades have to do with owning fish in aquariums", inline=False, ) embed_2.add_field( name=message_data[1], value=message_data[0], inline=False ) # Send the embed await ctx.send(embed=embed) await ctx.send(embed=embed_2)
async def _blackjack_command(self, ctx: commands.SlashContext, amount: int): with utils.UsingCommand(ctx): async with vbu.DatabaseConnection() as db: cache: utils.CachedUser = await utils.get_user_cache( ctx.author.id, db=db, bot=self.bot, logger=self.logger, ) if amount < 25: return await ctx.interaction.response.send_message( content= f"No can do, you need to gamble atleast **25 inches**") if amount > cache.pp.size: return await ctx.interaction.response.send_message( content= f"How you gamble that amount when you dont even have that many inches LMAO. You're missing {utils.format_rewards(inches=amount - cache.pp.size)}" ) game = utils.BlackjackGame(utils.Deck()) cache.pp.size -= amount with vbu.Embed() as embed: embed.colour = 0x2C82C9 kwargs = {"name": f"{ctx.author.name}'s game of Blackjack"} if ctx.author.avatar: kwargs["icon_url"] = ctx.author.avatar.url embed.set_author(**kwargs) embed.add_field( name=f"{ctx.author.name} 🎮", value= f"Hand - {game.player}\nTotal - `{game.player.total_value()}`", ) embed.add_field( name="Pp bot <:ppevil:871396299830861884>", value=f"Hand - {game.dealer.hidden()}\nTotal - `?`", ) components = discord.ui.MessageComponents( discord.ui.ActionRow( discord.ui.Button( label="Hit", custom_id="HIT", style=discord.ui.ButtonStyle.success, ), discord.ui.Button( label="Stand", custom_id="STAND", style=discord.ui.ButtonStyle.danger, ), )) if game.state == utils.BlackjackState.PLAYER_BLACKJACK: reward = int(amount * 2.5) cache.pp.size += reward with embed: embed.colour = utils.GREEN embed.description = f"**BLACKJACK!**\n{ctx.author.name} walks away with {utils.format_rewards(inches=reward)} (50% bonus)" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) return await ctx.interaction.response.send_message( embed=embed, components=components.disable_components()) elif game.state == utils.BlackjackState.DEALER_BLACKJACK: with embed: embed.colour = utils.RED embed.description = f"**DEALER BLACKJACK!**\n{ctx.author.name} loses {utils.format_rewards(inches=-amount)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) return await ctx.interaction.response.send_message( embed=embed, components=components.disable_components()) elif game.state == utils.BlackjackState.PUSH: cache.pp.size += amount with embed: embed.colour = utils.YELLOW embed.description = f"**PUSH!**\nSomehow you both got a blackjack LMAO, it's a tie" return await ctx.interaction.response.send_message( embed=embed, components=components.disable_components()) await ctx.interaction.response.send_message( embed=embed, components=components) original_message: discord.InteractionMessage = ( await ctx.interaction.original_message()) actions: typing.List[str] = [] def formatted_actions() -> str: if not actions: return "" reversed_actions = list(reversed(actions)) if len(actions) > 2: return "```diff\n{}\n{} previous {}...```".format( "\n".join(reversed_actions[:2]), len(reversed_actions) - 2, "actions" if len(reversed_actions) - 3 else "action", ) return "```diff\n{}```".format("\n".join(reversed_actions)) def action_check( action_interaction: discord.Interaction) -> bool: if (action_interaction.message is not None and action_interaction.message.id != original_message.id or action_interaction.user != ctx.author): return False if action_interaction.user != ctx.author: self.bot.loop.create_task( action_interaction.response.send_message( content="Bro this is not meant for you LMAO", ephemeral=True, )) return False if (action_interaction.data is not None and not utils.BlackjackAction.get( typing.cast( str, action_interaction.data.get( "custom_id", "")))): x = action_interaction.data["custom_id"] self.bot.loop.create_task( action_interaction.response.send_message( content= "Something went wrong lmao try using this command again with different button", ephemeral=True, )) raise asyncio.TimeoutError() return return True while game.state == utils.BlackjackState.PLAYER_TURN: try: action_interaction: discord.Interaction = ( await self.bot.wait_for("component_interaction", check=action_check, timeout=15)) except asyncio.TimeoutError: game.state = utils.BlackjackState.TIMEOUT break assert action_interaction.data is not None action = getattr(utils.BlackjackAction, action_interaction.data["custom_id"]) game.player_action(action) if action == utils.BlackjackAction.HIT: actions.append( f"+ {ctx.author.name} hits and received a {game.player.cards[-1]}." ) elif action == utils.BlackjackAction.STAND: actions.append(f"! {ctx.author.name} stands.") else: actions.append( f"? {ctx.author.name} {action.name.lower()}s.") with embed: embed.edit_field_by_index( 0, name=f"{ctx.author.name} 🎮", value= f"Hand - {game.player}\nTotal - `{game.player.total_value()}`", ) embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value=f"Hand - {game.dealer.hidden()}\nTotal - `?`", ) embed.description = formatted_actions() or None await action_interaction.response.edit_message(embed=embed) if game.state == utils.BlackjackState.TIMEOUT: actions.append(f"- {ctx.author.name} doesn't respond.") with embed: embed.colour = utils.YELLOW embed.description = f"**TIMED OUT!**\nWhile {ctx.author.name} was AFK, the dealer ran away with his {utils.format_rewards(inches=-amount)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() elif game.state == utils.BlackjackState.PLAYER_BUST: actions.append(f"- {ctx.author.name} busts.") with embed: embed.colour = utils.RED embed.description = f"**BUST!**\n{ctx.author.name} got a bit to greedy, and busted. You lose {utils.format_rewards(inches=-amount)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() else: while game.state == utils.BlackjackState.DEALER_TURN: with embed: embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) embed.description = formatted_actions() or None await original_message.edit( embed=embed, components=components.disable_components()) await asyncio.sleep(1) game.dealer_action() actions.append( f"+ pp bot hits and received a {game.dealer.cards[-1]}." ) if game.state == utils.BlackjackState.DEALER_BUST: actions.append(f"- pp bot busts.") reward = amount * 2 cache.pp.size += reward with embed: embed.colour = utils.GREEN embed.description = f"**DEALER BUST!**\npp bot got absolutely destroyed by {ctx.author.name}. You win {utils.format_rewards(inches=reward)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() await original_message.edit( embed=embed, components=components.disable_components()) elif game.state == utils.BlackjackState.DEALER_WIN: actions.append(f"+ pp bot wins.") with embed: embed.colour = utils.RED embed.description = f"**DEALER WIN!**\n{ctx.author.name} got dunked on by pp bot. You lose {utils.format_rewards(inches=-amount)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() elif game.state == utils.BlackjackState.PLAYER_WIN: actions.append(f"+ {ctx.author.name} wins.") reward = amount * 2 cache.pp.size += reward with embed: embed.colour = utils.GREEN embed.description = f"**YOU WIN!**\n{ctx.author.name} has proved their extreme gambling skill against pp bot. You win {utils.format_rewards(inches=reward)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() else: actions.append(f"+ {ctx.author.name} and pp bot push.") with embed: cache.pp.size += amount embed.colour = utils.YELLOW embed.description = f"**PUSH!**\n{ctx.author.name} and pp bot ended up in a tie. You win {utils.format_rewards(inches=0)}" embed.edit_field_by_index( 1, name="Pp bot <:ppevil:871396299830861884>", value= f"Hand - {game.dealer}\nTotal - `{game.dealer.total_value()}`", ) if actions: embed.description += formatted_actions() await original_message.edit( embed=embed, components=components.disable_components())
async def issue_create(self, ctx: vbu.Context, repo: GitRepo, *, title: str, body: str = ""): """ Create a Github issue on a given repo. """ # Get the database because whatever why not async with vbu.Database() as db: user_rows = await db("SELECT * FROM user_settings WHERE user_id=$1", ctx.author.id) if not user_rows or not user_rows[0][f'{repo.host.lower()}_username']: return await ctx.send( ( f"You need to link your {repo.host} account to Discord to run this " f"command - see the website at `{ctx.clean_prefix}info`." ), ) # Work out what components we want to use embed = vbu.Embed(title=title, description=body, use_random_colour=True).set_footer(text=str(repo)) components = discord.ui.MessageComponents.boolean_buttons() components.components[0].components.append(discord.ui.Button(label="Set title", custom_id="TITLE")) components.components[0].components.append(discord.ui.Button(label="Set body", custom_id="BODY")) components.components[0].components.append(discord.ui.Button(label="Set repo", custom_id="REPO")) options = [ discord.ui.SelectOption(label=i.name, value=i.name, description=i.description) for i in await repo.get_labels(user_rows[0]) ] components.add_component(discord.ui.ActionRow( discord.ui.SelectMenu( custom_id="LABELS", min_values=0, max_values=len(options), options=options, ) )) labels = [] # Ask if we want to do this m = None while True: # See if we want to update the body embed = vbu.Embed( title=title, description=body or "...", use_random_colour=True, ).set_footer( text=str(repo), ).add_field( "Labels", ", ".join([f"`{i}`" for i in labels]) or "...", ) if m is None: m = await ctx.send("Are you sure you want to create this issue?", embed=embed, components=components) else: await m.edit(embed=embed, components=components.enable_components()) try: payload = await self.bot.wait_for("component_interaction", check=vbu.component_check(ctx.author, m), timeout=120) except asyncio.TimeoutError: return await ctx.send("Timed out asking for issue create confirmation.") # Disable components if payload.component.custom_id not in ["LABELS"]: await payload.response.edit_message(components=components.disable_components()) # Get the body if payload.component.custom_id == "BODY": # Wait for their body message n = await payload.followup.send("What body content do you want to be added to your issue?") try: check = lambda n: n.author.id == ctx.author.id and n.channel.id == ctx.channel.id body_message = await self.bot.wait_for("message", check=check, timeout=60 * 5) except asyncio.TimeoutError: return await payload.followup.send("Timed out asking for issue body text.") # Grab the attachments attachment_urls = [] for i in body_message.attachments: try: async with self.bot.session.get(i.url) as r: data = await r.read() file = discord.File(io.BytesIO(data), filename=i.filename) cache_message = await ctx.author.send(file=file) attachment_urls.append((file.filename, cache_message.attachments[0].url)) except discord.HTTPException: break # Delete their body message and our asking message try: await n.delete() await body_message.delete() except discord.HTTPException: pass # Fix up the body body = body.strip() + "\n\n" + body_message.content + "\n\n" for name, url in attachment_urls: body += f"![{name}]({url})\n" # Get the title elif payload.component.custom_id == "TITLE": # Wait for their body message n = await payload.followup.send("What do you want to set the issue title to?") try: check = lambda n: n.author.id == ctx.author.id and n.channel.id == ctx.channel.id title_message = await self.bot.wait_for("message", check=check, timeout=60 * 5) except asyncio.TimeoutError: return await payload.followup.send("Timed out asking for issue title text.") # Delete their body message and our asking message try: await n.delete() await title_message.delete() except discord.HTTPException: pass title = title_message.content # Get the repo elif payload.component.custom_id == "REPO": # Wait for their body message n = await payload.followup.send("What do you want to set the repo to?") try: check = lambda n: n.author.id == ctx.author.id and n.channel.id == ctx.channel.id repo_message = await self.bot.wait_for("message", check=check, timeout=60 * 5) except asyncio.TimeoutError: return await payload.followup.send("Timed out asking for issue title text.") # Delete their body message and our asking message try: await n.delete() await repo_message.delete() except discord.HTTPException: pass # Edit the message try: repo = await GitRepo.convert(ctx, repo_message.content) except Exception: await ctx.send(f"That repo isn't valid, {ctx.author.mention}", delete_after=3) # Get the labels elif payload.component.custom_id == "LABELS": await payload.response.defer_update() labels = payload.values # Check for exiting elif payload.component.custom_id == "NO": return await payload.followup.send("Alright, cancelling issue add.") elif payload.component.custom_id == "YES": break # Work out our args headers = {} if repo.host == "Github": json = {'title': title, 'body': body.strip(), 'labels': labels} headers = {'Accept': 'application/vnd.github.v3+json', 'Authorization': f"token {user_rows[0]['github_access_token']}"} elif repo.host == "Gitlab": json = {'title': title, 'description': body.strip(), 'labels': ",".join(labels)} headers = {'Authorization': f"Bearer {user_rows[0]['gitlab_bearer_token']}"} else: raise Exception("Invalid host") headers.update({'User-Agent': self.bot.user_agent}) # Make the post request async with self.bot.session.post(repo.issue_api_url, json=json, headers=headers) as r: data = await r.json() self.logger.info(f"Received data from git {r.url!s} - {data!s}") if not r.ok: return await ctx.send(f"I was unable to create an issue on that repository - `{data}`.",) await ctx.send(f"Your issue has been created - <{data.get('html_url') or data.get('web_url')}>.",) await self.increase_repo_usage_counter(ctx.author, repo)
async def dnd_monster(self, ctx: commands.Context, *, monster_name: str): """ Gives you information on a D&D monster. """ # Get our data data = await self.send_web_request("monsters", monster_name) if not data: return await ctx.send( "I couldn't find any information for that monster.") # Make an embed embed = vbu.Embed( use_random_colour=True, title=data['name'], description="\n".join([ f"{data['size'].capitalize()} | {data['type']} | {data['hit_points']:,} ({data['hit_dice']}) HP | {data['xp']:,} XP", ", ".join( [f"{o} {data[i]}" for i, o in self.ATTRIBUTES.items()]), ])).add_field( name="Proficiencies", value=", ".join([ f"{i['proficiency']['name']} {i['value']}" for i in data['proficiencies'] ]) or "None", ).add_field( name="Damage Vulnerabilities", value="\n".join(data['damage_vulnerabilities']).capitalize() or "None", ).add_field( name="Damage Resistances", value="\n".join(data['damage_resistances']).capitalize() or "None", ).add_field( name="Damage Immunities", value="\n".join(data['damage_immunities']).capitalize() or "None", ).add_field( name="Condition Immunities", value="\n".join( [i['name'] for i in data['condition_immunities']]).capitalize() or "None", ).add_field( name="Senses", value="\n".join([ f"{i.replace('_', ' ').capitalize()} {o}" for i, o in data['senses'].items() ]) or "None", ) self.group_field_descriptions(embed, "Actions", data['actions']) self.group_field_descriptions(embed, "Legendary Actions", data.get('legendary_actions', list())) if data.get('special_abilities'): embed.add_field( name="Special Abilities", value="\n".join([ f"**{i['name']}**\n{i['desc']}" for i in data['special_abilities'] if i['name'] != 'Spellcasting' ]) or "None", inline=False, ) spellcasting = [ i for i in data.get('special_abilities', list()) if i['name'] == 'Spellcasting' ] if spellcasting: spellcasting = spellcasting[0] embed.add_field( name="Spellcasting", value=spellcasting['desc'].replace('\n\n', '\n'), inline=False, ) # And send return await ctx.send(embed=embed)
async def commit_poster_loop(self, embed_title: str, api_url: str, repo_url: str): """ Grab the data from the Discord datamining repo and post it to the coding channel of VFL. """ # Grab the data self.logger.info(f"Grabbing repo data from {api_url}") async with self.bot.session.get(api_url) as r: data = await r.json() # Let's work out the new data new_commits = [] if self.last_posted_commit[repo_url] is None: self.last_posted_commit[repo_url] = data[0]['sha'] self.logger.info("Setting to last commit - no commits cached") return for index, i in enumerate(data): if i['sha'] == self.last_posted_commit[repo_url]: new_commits = data[:index] break # See if we have anything to talk about if not new_commits: self.logger.info("No new commits found") return self.logger.info(f"Logging info of {len(new_commits)} commits") # Work out which of our new commits have comments comment_urls = [ i['comments_url'] for i in new_commits if i['commit']['comment_count'] > 0 ] comment_text = [] for url in comment_urls: async with self.bot.session.get(url) as r: data = await r.json() comment_text.extend([(i['commit_id'], re.sub(r"^## (.+)", r"**\1**", i['body'])) for i in data]) self.logger.info(f"Logging info of {len(comment_text)} comments") # Get the full description description_unsplit = '\n'.join([ f"{i['commit']['message']} - [Link]({repo_url}commit/{i['sha']})" for i in new_commits ]) # Inform the bot owner that the description is larger than allowed by discord if len(description_unsplit) > 2048: self.logger.info( f"Our description is longer than 2048 characters and this isnt really poggers, " f"we need to make it in multiple embeds. It was {len(description_unsplit)} characters." ) # Split the description into a list of descriptions each 2048 or less description_array = [ description_unsplit[i:i + 2048] for i in range(0, len(description_unsplit), 2048) ] # Format one into an embed with vbu.Embed(use_random_colour=True) as embed: embed.title = embed_title embed.description = description_array.pop(0) for sha, body in comment_text: embed.add_field(sha, body, inline=False) # And send the first one channel = self.bot.get_channel(self.VFL_CODING_CHANNEL_ID) assert isinstance(channel, discord.TextChannel) m = await channel.send(embed=embed) await m.publish() # If there are more than 2048 characters send the other parts for description in description_array: with vbu.Embed(use_random_colour=True) as embed: embed.title = "Continuation of " + embed_title embed.description = description m = await channel.send(embed=embed) await m.publish() # We are done PogChamp self.logger.info("Sent data to channel") self.last_posted_commit[repo_url] = new_commits[0]['sha']
async def steam(self, ctx: vbu.Context, *, app_name: str): """ Search Steam for an item. """ # Load cache if not self.game_cache: await self.load_game_cache() # Get app app_object = None appid = None await ctx.trigger_typing() # By url match = self.GAME_URL_REGEX.search(app_name) if match is not None: app_name = match.group(1) # By app id if app_name.isdigit(): appid = int(app_name) try: app_object = [i for i in self.game_cache if i['appid'] == int(app_name)][0] except IndexError: pass # By app name if app_object is None and appid is None: app_name = self.get_valid_name(app_name) valid_items = [i for i in self.game_cache if app_name.lower() in self.get_valid_name(i['name']).lower()] full_title_match = [i for i in valid_items if app_name.lower() == self.get_valid_name(i['name']).lower()] if full_title_match: valid_items = [full_title_match[0]] if len(valid_items) > 1: output_items = valid_items[:10] output_ids = [f"`{i['appid']}` - {i['name']}" for i in output_items] return await ctx.send("There are multiple results with that name:\n" + "\n".join(output_ids)) # TODO elif len(valid_items) == 0: return await ctx.send("There are no results with that name.") app_object = valid_items[0] appid = app_object['appid'] # Get info params = { "appids": appid } headers = { "User-Agent": self.bot.user_agent } async with self.bot.session.get(self.GAME_DATA_URL, params=params, headers=headers) as r: game_data = await r.json() if game_data[str(appid)]['success'] is False: return await ctx.send(f"I couldn't find an application with ID `{appid}`.") game_object = game_data[str(appid)]['data'] # See if it's NSFW if int(game_object.get('required_age', '0')) >= 18 and ctx.channel.nsfw is False: return await ctx.send("That game is marked as an 18+, so can't be sent in a non-NSFW channel.") # Embed it babey with vbu.Embed(use_random_colour=True) as embed: embed.title = game_object['name'] embed.set_footer(text=f"AppID: {appid}") embed.description = game_object['short_description'] embed.add_field("Developer", ', '.join(game_object.get('developers', list())) or 'None', inline=True) embed.add_field("Publisher", ', '.join(game_object.get('publishers', list())) or 'None', inline=True) embed.add_field("Genre", ', '.join(i['description'] for i in game_object['genres']) or 'None', inline=True) if game_object.get('price_overview') is not None: initial_price = game_object['price_overview']['initial_formatted'] final_price = game_object['price_overview']['final_formatted'] embed.add_field("Price", f"~~{initial_price}~~ {final_price}" if initial_price else final_price, inline=True) embed.add_field("Link", f"Open with Steam - steam://store/{appid}\nOpen in browser - [Link](https://store.steampowered.com/app/{appid}/)", inline=False) screenshots = [i['path_full'] for i in game_object['screenshots']] embed.set_image(url=screenshots[0]) # Send m = await ctx.send(embed=embed)
def generate_embed(self, data) -> typing.Optional[vbu.Embed]: """ Make an embed based on some OMDB data. """ search = data.get('Title') is None if search and data.get('Search') is None: return None embed = vbu.Embed(use_random_colour=True) if not search: embed.title = f"{data['Title']} ({data['Year']})" valid_info = lambda v: v not in [None, 'N/A', 'n/a'] # List short details of up to 10 results if search: description_list = [] for index, row in enumerate(data['Search'][:10], start=1): if valid_info(row.get('Poster')): description_list.append( f"{index}. **{row['Title']}** ({row['Year']}) - [Poster]({row['Poster']})" ) else: description_list.append( f"{index}. **{row['Title']}** ({row['Year']})") embed.description = '\n'.join(description_list) return embed # List full details if data.get('Plot'): embed.description = data['Plot'] if data.get('Released'): embed.add_field("Release Date", data['Released']) if data.get('Rated'): embed.add_field("Age Rating", data['Rated']) if data.get('Runtime'): embed.add_field("Runtime", data['Runtime']) if data.get('Genre'): embed.add_field(f"Genre{'s' if ',' in data['Genre'] else ''}", data['Genre']) if data.get('imdbRating'): embed.add_field("IMDB Rating", data['imdbRating']) if data.get('Production'): embed.add_field( f"Production Compan{'ies' if ',' in data['Production'] else 'y'}", data['Production']) if data.get('Director'): embed.add_field( f"Director{'s' if ',' in data['Director'] else ''}", data['Director']) if data.get('Writer'): embed.add_field(f"Writer{'s' if ',' in data['Writer'] else ''}", data['Writer'], inline=False) if data.get('imdbID'): embed.add_field( "IMDB Page", f"[Direct Link](https://www.imdb.com/title/{data['imdbID']}/) - IMDB ID `{data['imdbID']}`", inline=False) if valid_info(data.get('Poster')): embed.set_thumbnail(data['Poster']) return embed