async def alias(self, ctx): """Returns a list of aliases that are currently set.""" if ctx.invoked_subcommand is not None: return embeds = [] desc = 'Here is a list of aliases that are currently configured.' if self.bot.aliases: embed = Embed(color=Color.blurple(), description=desc) else: embed = Embed( color=Color.blurple(), description='You dont have any aliases at the moment.') embed.set_author(name='Command aliases', icon_url=ctx.guild.icon_url) embed.set_footer(text=f'Do {self.bot.prefix}' 'help aliases for more commands.') embeds.append(embed) for name, value in self.bot.aliases.items(): if len(embed.fields) == 5: embed = Embed(color=Color.blurple(), description=desc) embed.set_author(name='Command aliases', icon_url=ctx.guild.icon_url) embed.set_footer(text=f'Do {self.bot.prefix}help ' 'aliases for more commands.') embeds.append(embed) embed.add_field(name=name, value=value, inline=False) session = PaginatorSession(ctx, *embeds) return await session.run()
async def ping(self, ctx: Context) -> None: """Show bot ping.""" start = time.perf_counter() embed = Embed(title="Info", description="Pong!", color=Color.blurple()) message = await ctx.send(embed=embed) end = time.perf_counter() duration = round((end - start) * 1000, 2) discord_start = time.monotonic() async with self.bot.session.get("https://discord.com/") as resp: if resp.status == 200: discord_end = time.monotonic() discord_ms = f"{round((discord_end - discord_start) * 1000)}ms" else: discord_ms = "f*****g dead" desc = textwrap.dedent(f""" :ping_pong: Pong! Bot ping: **{duration}ms** Discord Server Ping: **{discord_ms}** Speed Ping: **{round(self.bot.latency * 1000)}ms** """) embed = Embed(title="Info", description=desc, color=Color.blurple()) await message.edit(embed=embed)
async def get(self, ctx, key=None): """Shows the config variables that are currently set.""" keys = self.bot.config.allowed_to_change_in_command if key: if key in keys: desc = f'`{key}` is set to `{self.bot.config.get(key)}`' embed = Embed(color=Color.blurple(), description=desc) embed.set_author(name='Config variable', icon_url=self.bot.user.avatar_url) else: embed = Embed(title='Error', color=Color.red(), description=f'`{key}` is an invalid key.') valid_keys = [f'`{k}`' for k in keys] embed.add_field(name='Valid keys', value=', '.join(valid_keys)) else: embed = Embed(color=Color.blurple(), description='Here is a list of currently ' 'set configuration variables.') embed.set_author(name='Current config', icon_url=self.bot.user.avatar_url) config = { k: v for k, v in self.bot.config.cache.items() if v and k in keys } for k, v in reversed(list(config.items())): embed.add_field(name=k, value=f'`{v}`', inline=False) return await ctx.send(embed=embed)
async def cookie(self, ctx: Context, member: discord.Member) -> None: """Give a User a cookie.""" num = random.randint(1, 4) author = "You're a" if ctx.author == member else f"{member.mention} is" actor = f" from {ctx.author.mention}" if ctx.author != member else "" if num == 1: embed = Embed( title="Cookie Giver!", description=textwrap.dedent( f""" {author} Lucky Guy! You got a **Huge Cookie**{actor}! **You got +10 points!** """ ), color=Color.blurple(), ) embed.set_image(url="https://media.giphy.com/media/7GYHmjk6vlqY8/giphy.gif") else: embed = Embed( title="Cookie Giver!", description=textwrap.dedent( f""" {author} got a cookie{actor}! ➡ :cookie: :cookie: :cookie: *You got +{num} points!**" """ ), color=Color.blurple(), ) await ctx.send(embed=embed)
async def ping(self, ctx: Context) -> None: """Shows bot ping.""" start = time.perf_counter() embed = Embed(title="Info", description="Pong!", color=Color.blurple()) message = await ctx.send(embed=embed) end = time.perf_counter() duration = round((end - start) * 1000, 2) embed = Embed(title="Info", description=f":ping_pong: Pong! ({duration}ms)", color=Color.blurple()) await message.edit(embed=embed)
async def mars(self, ctx: Context, date: str, rover: str = None, number: int = 1) -> None: """Get images from Mars. must specify the date (in the form YYYY-MM-DD),and you can specify the rover and the number of images to retrieve.""" if rover is None: rover = random.choice(["curiosity", "opportunity", "spirit"]) if not rover.lower() in ("curiosity", "opportunity", "spirit"): await ctx.send("Sorry but this rover doesn't exist") return async with self.session.get( "https://api.nasa.gov/mars-photos/api/v1/rovers/" + rover.lower() + "/photos", params={"earth_date": date, "api_key": NASA_API} ) as response: images = await response.json() if images.get("photos", []) == []: await ctx.send(f"Couldn't Find anything! Invalid Rover : {rover.capitalize()}") return for i in range(min(number, len(images["photos"]))): embed = Embed( title="Picture from " + rover.capitalize(), description="Picture taken from the " + images["photos"][i]["camera"]["full_name"], colour=Color.blurple() ) embed.set_image(url=images["photos"][i]["img_src"]) embed.set_footer(text="Picture taken on" + images["photos"][i]["earth_date"]) await ctx.send(embed=embed)
async def nasa_patent(self, ctx: Context, *, patent: str) -> None: """Search for a NASA patent.""" async with self.session.get(f"https://api.nasa.gov/techtransfer/patent/?{patent}&api_key={NASA_API}") as resp: data = await resp.json() items = data["results"] if len(items) > 0: rand_item = random.randint(0, len(items) - 1) item = items[rand_item] if len(remove_tags(item[3][:2000])) > 2048: description = f"{item[3][:2000][:2045].strip()}..." else: description = item[3][:2000] embed = Embed( title=remove_tags(item[2]), description=remove_tags(description), color=Color.blurple() ) embed.set_footer(text="Powered by HotWired") await ctx.send(embed=embed) else: await ctx.send("No results Found!")
async def subscribe_map(self, interaction: Interaction, mapname: str): reply_embed = Embed(color=Color.blurple()) await interaction.response.defer(thinking=True, ephemeral=True) stripped_mapname = mapname.strip(" ") if stripped_mapname == "": reply_embed.description = "No mapname provided." await interaction.edit_original_message(embed=reply_embed) return if stripped_mapname not in self.installed_maps: reply_embed.description = f"Map `{stripped_mapname}` is not installed." await interaction.edit_original_message(embed=reply_embed) return db_return_value = self.db.sadd( DISCORD_MAP_SUBSCRIPTION_KEY.format(interaction.user.id), stripped_mapname) if not db_return_value: immediate_reply_message = f"You already were subscribed to map changes for map " \ f"`{self.formatted_installed_maps[stripped_mapname]}`." else: immediate_reply_message = f"You have been subscribed to map changes for map " \ f"`{self.formatted_installed_maps[stripped_mapname]}`." reply_embed.description = immediate_reply_message await interaction.edit_original_message(embed=reply_embed) subscribed_maps = self.subscribed_maps_of(interaction.user.id) formatted_maps = "`, `".join( [self.format_mapname(mapname) for mapname in subscribed_maps]) reply_embed.description = f"{immediate_reply_message}\n" \ f"You are currently subscribed to map changes for: `{formatted_maps}`" await interaction.edit_original_message(embed=reply_embed)
async def topic(self, ctx: commands.Context) -> None: """ Responds with a random topic to start a conversation. If in a Python channel, a python-related topic will be given. Otherwise, a random conversation topic will be received by the user. """ # No matter what, the form will be shown. embed = Embed( description=f'Suggest more topics [here]({SUGGESTION_FORM})!', color=Color.blurple()) try: # Fetching topics. channel_topics = TOPICS[ctx.channel.id] # If the channel isn't Python-related. except KeyError: embed.title = f'**{next(TOPICS["default"])}**' # If the channel ID doesn't have any topics. else: embed.title = f'**{next(channel_topics)}**' finally: await ctx.send(embed=embed)
async def unsubscribe_map(self, interaction: Interaction, mapname: str): reply_embed = Embed(color=Color.blurple()) await interaction.response.defer(thinking=True, ephemeral=True) stripped_mapname = mapname.strip(" ") if stripped_mapname == "": reply_embed.description = "No mapname provided." await interaction.edit_original_message(embed=reply_embed) return db_return_value = self.db.srem( DISCORD_MAP_SUBSCRIPTION_KEY.format(interaction.user.id), stripped_mapname) if not db_return_value: immediate_reply_message = f"You were not subscribed to map changes for map " \ f"`{self.format_mapname(stripped_mapname)}`. " else: immediate_reply_message = f"You have been unsubscribed from map changes for map " \ f"`{self.format_mapname(stripped_mapname)}`. " reply_embed.description = immediate_reply_message await interaction.edit_original_message(embed=reply_embed) subscribed_maps = self.subscribed_maps_of(interaction.user.id) if len(subscribed_maps) == 0: reply_embed.description = f"{immediate_reply_message}\nYou are no longer subscribed to any map changes." await interaction.edit_original_message(embed=reply_embed) return formatted_maps = "`, `".join( [self.format_mapname(mapname) for mapname in subscribed_maps]) reply_embed.description = f"{immediate_reply_message}\nYou are still subscribed to `{formatted_maps}`" await interaction.edit_original_message(embed=reply_embed)
def format_cog_help(self, ctx, cog): """Formats the text for a cog help""" prefix = self.bot.prefix fmts = [''] for cmd in sorted(self.bot.commands, key=lambda cmd: cmd.qualified_name): if cmd.instance is cog and not cmd.hidden: new_fmt = f'`{prefix + cmd.qualified_name}` - ' new_fmt += f'{cmd.short_doc}\n' if len(new_fmt) + len(fmts[-1]) >= 1024: fmts.append(new_fmt) else: fmts[-1] += new_fmt embeds = [] for fmt in fmts: embed = Embed(description='*' + inspect.getdoc(cog) + '*', color=Color.blurple()) embed.add_field(name='Commands', value=fmt) embed.set_author(name=cog.__class__.__name__ + ' - Help', icon_url=ctx.bot.user.avatar_url) embed.set_footer(text=f'Type "{prefix}help command" ' 'for more info on a command.') embeds.append(embed) return embeds
async def add_(self, ctx, name: str.lower, *, value): """Add an alias to the bot config.""" if 'aliases' not in self.bot.config.cache: self.bot.config['aliases'] = {} if self.bot.get_command(name) or self.bot.config.aliases.get(name): embed = Embed(title='Error', color=Color.red(), description='A command or alias already exists ' f'with the same name: `{name}`.') return await ctx.send(embed=embed) if not self.bot.get_command(value.split()[0]): embed = Embed( title='Error', color=Color.red(), description='The command you are attempting to point ' f'to does not exist: `{value.split()[0]}`.') return await ctx.send(embed=embed) self.bot.config.aliases[name] = value await self.bot.config.update() embed = Embed(title='Added alias', color=Color.blurple(), description=f'`{name}` points to: {value}') return await ctx.send(embed=embed)
def match_help(ctx): """ Returns match help embed """ embed = Embed(colour=Color.blurple()) embed.add_field(name='Match commands', value='`=info` (`=i`) - Display the match status and team composition\n' "`=squittal` - Display player data for integration in Chirtle's script", inline=False) embed.add_field(name='Team Captain commands', value='`=p @player` - Pick a player in your team\n' '`=p VS`/`NC`/`TR` - Pick a faction\n' '`=base` (`=b`) - Command for the base selection, use `=b help` to know more\n' '`=ready` (`=rdy`) - To toggle the ready status of your team\n' '`=sub @player` - Pick someone in queue to replace the player mentioned\n' '`=swap @player1 @player2` - Swap the two players from one team to the other\n' '`=bench`/`=unbench @player` - Bench or un-bench player from the match', inline=False) if is_admin(ctx.author): embed.add_field(name="Staff commands", value='`=clear` - Clear the match\n' '`=captain @player` - Make @player a team captain\n' '`=sub @player1 @player2` - Replace player1 by player2\n' '`=check account/online` - Disable account or online check\n' '`=channel freeze`/`unfreeze` - Prevent / Allow players to send messages', inline=False) return embed
def _registerHelp(msg): """ Returns register help embed """ embed = Embed( colour=Color.blurple(), title='How to register?', description= f'You have to accept the rules in <#{cfg.discord_ids["rules"]}> to register' ) embed.add_field(name='If you don\'t have a Jaeger account', value='`=r no account`\n', inline=False) embed.add_field( name='If you have a Jaeger account', value='`=r charName` - If your character names have faction suffixes\n' '`=r charName1 charName2 charName3` - If your character names don\'t have faction suffixes\n', inline=False) embed.add_field( name='Notify feature', value='`=notify` - To join or leave the Notify feature\n' f'When suscribed to Notify, you can be mentionned with <@&{cfg.discord_ids["notify_role"]}> by other players\n', inline=False) try: if isAdmin(msg.author): embed.add_field( name="Staff Commands", value= '`=unregister @player` - Permanently remove player profile from the system', inline=False) except AttributeError: pass # if msg is from bot return embed
async def thanks(self, ctx: Context, member: Member, *, reason: str = None) -> None: """Thank a Member.""" if ctx.author == member: embed = Embed( title="WARNING", description= f"{ctx.author.mention} **You Cannot Thank Yourself!**", color=Color.orange(), ) await ctx.send(embed=embed) else: embed = Embed( title="THANKS", description=textwrap.dedent(f""" {member.mention} was thanked by {ctx.author.mention}! {'**MESSAGE**:' + reason if reason else ''} """), color=Color.blurple(), ) embed.set_image( url="https://media.giphy.com/media/6tHy8UAbv3zgs/giphy.gif") await ctx.send(embed=embed)
def random(): c = [ Color.teal(), Color.dark_teal(), Color.green(), Color.dark_green(), Color.blue(), Color.dark_blue(), Color.purple(), Color.dark_purple(), Color.magenta(), Color.dark_magenta(), Color.gold(), Color.dark_gold(), Color.orange(), Color.dark_orange(), Color.red(), Color.dark_red(), Color.lighter_grey(), Color.darker_grey(), Color.blurple(), tan, icedcoffee, chilipepper ] return c[randint(0, len(c) - 1)]
async def run(self, message: Message, args: List[str]): args = re.split( "(((title):\s*'(.*?)')\s*)|" "(((time):\s*'(.*?)')\s*)|" "(((desc):\s*'(.*?)')\s*)|" "((winners):\s*(\d+))", message.content) if 'title' not in args: raise CommandError("You need to provide more arguments, " "`title` missing or it's malformatted...\n" "Format: `title: 'remember the quotes'`") if 'time' not in args: raise CommandError("You need to provide more arguments, " "`time` missing or it's malformatted...\n" "Format: `time: 'remember the quotes'`") title = args[args.index('title') + 1] time = args[args.index('time') + 1] winners = 1 if 'winners' not in args else args[args.index('winners') + 1] desc = None if 'desc' not in args else args[args.index('desc') + 1] ptime = maya.when(time, prefer_dates_from='future') embed = Embed(title=title.capitalize(), timestamp=ptime.datetime(), color=Color.blurple()) embed.description = f"{desc if desc else ''}\n" \ f"Winners: {winners}\n" \ f"Ends {ptime.slang_time()}\n\n" \ f"*React with 🎊 to enter*" msg = await message.channel.send(embed=embed) await msg.add_reaction('🎊')
def admin_help(ctx): """ Returns admin help embed """ embed = Embed(colour=Color.blurple()) embed.add_field(name='Debug commands', value='`=channel (un)freeze` - Prevent users from typing in a channel\n' '`=pog version` - Display current version and lock status\n' '`=pog (un)lock` - Prevent users from interacting with the bot (but admins still can)\n' '`=reload accounts`/`bases`/`weapons`/`config` - Reload specified element from the database\n' '`=spam clear` - Clear the spam filter\n', inline=False) embed.add_field(name='Lobby commands', value='`=remove @player` - Remove the player from queue\n' '`=clear` - Clear queue\n' '`=lobby save`/`get`/`restore` - Will save, get or restore the lobby from player IDs', inline=False) embed.add_field(name='Player Management commands', value='`=rename @player New Name` - Rename a player within the system\n' '`=timeout @player duration` - Mute the player from POF for a given time\n' '`=unregister @player` - Forcibly unregisters and removes a user from the system', inline=False) embed.add_field(name='Match commands', value='`=sub @player1 @player2` - Sub player1 by player2\n' '`=clear` - Clear the match\n' '`=check account/online` - Disable account or online check', inline=False) return embed
async def handle_winner(self, winner, guild, action, duration, channel): duck_user = await self.get_duck_user(winner.id, guild.id) if not duck_user: duck_user = self.models.DuckUser( author_id=str(winner.id), guild_id=str(guild.id), befriend_count=0, kill_count=0, ) await duck_user.create() if action == "befriended": await duck_user.update(befriend_count=duck_user.befriend_count + 1).apply() else: await duck_user.update(kill_count=duck_user.kill_count + 1).apply() await duck_user.update(updated=datetime.datetime.now()).apply() embed = discord.Embed( title=f"Duck {action}!", description=f"{winner.mention} {action} the duck in {duration} seconds!", ) embed.color = ( embed_colors.blurple() if action == "befriended" else embed_colors.red() ) embed.add_field(name="Friends", value=duck_user.befriend_count) embed.add_field(name="Kills", value=duck_user.kill_count) embed.set_thumbnail( url=self.BEFRIEND_URL if action == "befriended" else self.KILL_URL ) await channel.send(embed=embed)
async def send_command_help(self, ctx, command): help_message = Embed(color=Color.blurple(), description='') destination = ctx.channel help_message.description += f"{ctx.prefix}{command.name} {command.usage}\n\n{command.help}" await destination.send(embed=help_message)
async def on_message_edit(self, message_before, message_after): if message_before.guild is None: return channel = get(message_before.guild.text_channels, name="action_log") if channel is None: return if message_before.author.bot is True: return if message_before.content == message_after.content: return e = Embed( description= f"**Message edited in** <#{message_after.channel.id}> [Jump to message]({message_after.jump_url})", color=Color.blurple(), timestamp=datetime.utcnow()) try: e.add_field(name="Before", value=f"{message_before.content}", inline=False) e.add_field(name="After", value=f"{message_after.content}") e.set_author(name=message_before.author, icon_url=message_before.author.avatar_url) e.set_footer(text=f"Author: {message_after.author.id}") await channel.send(embed=e) except HTTPException: return
def register_help(ctx): """ Returns register help embed """ embed = Embed( colour=Color.blurple(), title='How to register?', description=f'You have to accept the rules in <#{cfg.channels["rules"]}> to register' ) embed.add_field(name='If you don\'t have a Jaeger account', value='`=r no account`\n', inline=False) embed.add_field(name='If you have a Jaeger account', value='`=r charName` - If your character names have faction suffixes ' '(charNameVS, charNameTR, charNameNC)\n' '`=r charName1 charName2 charName3` - If your character names don\'t have faction suffixes', inline=False) embed.add_field(name='Notify feature', value='`=notify` - To join or leave the Notify feature\n' f'When subscribed to Notify, you can be mentioned with <@&{cfg.roles["notify"]}> ' 'when the queue is almost full', inline=False) embed.add_field(name='Quit command', value='`=quit` - To temporarily leave the channels\n' f'You will be removed from the active POG channels. Accept the rules in <#{cfg.channels["rules"]}> to come back.', inline=False) if is_admin(ctx.author): embed.add_field(name="Staff commands", value='`=rename @player New Name` - Change the player name within the system\n' '`=unregister @player` - Permanently remove player profile from the system\n' '`=channel freeze`/`unfreeze` - Prevent / Allow players to send messages', inline=False) return embed
async def shorten(self, ctx: Context, *, link: str) -> None: """Make a link shorter using the tinyurl api.""" if not link.startswith("https://"): await ctx.send(f"Invalid link: `{link}`. Enter a valid URL.") return url = link.strip("<>") url = f"http://tinyurl.com/api-create.php?url={url}" async with self.bot.session.get(url) as resp: if resp.status != 200: await ctx.send( "Error retrieving shortened URL, please try again in a minute." ) return shortened_link = await resp.text() embed = Embed(color=Color.blurple()) embed.add_field(name="Original Link", value=link, inline=False) embed.add_field(name="Shortened Link", value=shortened_link, inline=False) await ctx.send(embed=embed) with suppress(Forbidden): await ctx.message.delete()
def random(): # type: () -> Color chilipepper = Color(0x9B1B30) tan = Color(0xBEAA3E) icedcoffee = Color(0xB18F6A) return choice([ Color.teal(), Color.dark_teal(), Color.green(), Color.dark_green(), Color.blue(), Color.dark_blue(), Color.purple(), Color.dark_purple(), Color.magenta(), Color.dark_magenta(), Color.gold(), Color.dark_gold(), Color.orange(), Color.dark_orange(), Color.red(), Color.dark_red(), Color.lighter_grey(), Color.darker_grey(), Color.blurple(), tan, icedcoffee, chilipepper, ])
def random(): # type: () -> Color tan = Color(0xBEAA3E) return choice([ Color.teal(), Color.dark_teal(), Color.green(), Color.dark_green(), Color.blue(), Color.dark_blue(), Color.purple(), Color.dark_purple(), Color.magenta(), Color.dark_magenta(), Color.gold(), Color.dark_gold(), Color.orange(), Color.dark_orange(), Color.red(), Color.dark_red(), Color.lighter_grey(), Color.darker_grey(), Color.blurple(), tan, ])
async def smbc(self, ctx: Context) -> None: """Saturday Morning comic.""" url = "http://www.smbc-comics.com/comic/archive" async with ctx.typing(): async with self.session.get(url, headers={"Connection": "keep-alive"}) as response: soup = BeautifulSoup(await response.text(), "html.parser") all_comics = soup.find("select", attrs={"name": "comic"}) all_comics_url_stubs = [ option["value"] for option in all_comics.findChildren() ] random_comic = random.choice(all_comics_url_stubs) comic_url = f"http://www.smbc-comics.com/{random_comic}" async with self.session.get(comic_url, headers={"Connection": "keep-alive"}) as resp: soup = BeautifulSoup(await resp.text(), "html.parser") img_url = soup.find(property="og:image")["content"] async with self.session.get(img_url) as response: img = io.BytesIO(await response.read()) embed = Embed(title="Random Sunday Morning", color=Color.blurple()) embed.set_image(url="attachment://smbc.png") file = File(img, "smbc.png") await ctx.send(file=file, embed=embed)
async def unsubscribe_member(self, interaction: Interaction, member: Member): reply_embed = Embed(color=Color.blurple()) await interaction.response.defer(thinking=True, ephemeral=True) db_return_value = self.db.srem( DISCORD_MEMBER_SUBSCRIPTION_KEY.format(interaction.user.id), member.id) if not db_return_value: immediate_reply_message = f"You were not subscribed to Quake Live activities of {member.mention}." else: immediate_reply_message = f"You have been unsubscribed from Quake Live activities of {member.mention}." reply_embed.description = immediate_reply_message await interaction.edit_original_message(embed=reply_embed) subscribed_users = self.subscribed_users_of(interaction.user.id) if len(subscribed_users) == 0: reply_embed.description = f"{immediate_reply_message}\n" \ f"You are no longer subscribed to Quake Live activities of anyone." await interaction.edit_original_message(embed=reply_embed) return formatted_users = ", ".join( [user.mention for user in subscribed_users]) reply_embed.description = f"{immediate_reply_message}\n" \ f"You are still subscribed to Quake Live activities of {formatted_users}" await interaction.edit_original_message(embed=reply_embed)
async def qlstats(interaction: Interaction, _item: Union[Member, User]) -> None: embed = Embed(color=Color.blurple()) url = Plugin.get_cvar("qlx_discord_ext_qlstats_url") embed.url = url embed.title = "QL stats page" await interaction.response.send_message(embed=embed, ephemeral=True)
async def embed(self, **attrs): e = Embed(title=attrs.get("title"), color=attrs.get("color", Color.blurple()), description=attrs.get("description"), url=attrs.get("url")) if "image" in attrs: e.set_image(url=attrs["image"]) if attrs.get("thumbnail") is not None: e.set_thumbnail(url=attrs["thumbnail"]) if attrs.get("footer_default"): e.set_footer(text=self.author.display_name, icon_url=self.author.avatar_url) elif "footer_text" in attrs and "footer_icon" in attrs: e.set_footer(text=attrs["footer_text"], icon_url=attrs["footer_icon"]) elif "footer_text" in attrs: e.set_footer(text=attrs["footer_text"]) if "header_text" in attrs and "header_icon" in attrs: e.set_author(name=attrs["header_text"], icon_url=attrs["header_icon"]) elif "header_text" in attrs: e.set_author(name=attrs["header_text"]) # fields will be a dictionary if "fields" in attrs: inline = attrs.get("inline", True) for name, value in attrs["fields"].items(): e.add_field(name=name, value=value, inline=inline) await self.send(embed=e)
async def embed(self, **attrs): e = Embed( title=attrs.get("title"), color=attrs.get("color", Color.blurple()), description=attrs.get("description"), ) if (url := attrs.get("url")): e.url = url