async def add_custom_command(self, ctx, command_name: str, command_type: CommandType, *text: str): """ Add a custom command to the Discord server. The only supported command_type currently is "text". You must have one of the following to be able to use the command: - The legendarybot-admin role. - The Manage Server or the Administrator permission. """ custom_command = GuildCustomCommand.objects.filter( guild_id=ctx.guild.id, name=command_name).first() if custom_command: custom_command.type = GuildCustomCommand.TEXT custom_command.value = " ".join(text) custom_command.save() await ctx.message.author.send( _("Command {command_name} updated!").format( command_name=command_name)) else: custom_command = GuildCustomCommand(guild_id=ctx.guild.id, name=command_name, type=GuildCustomCommand.TEXT, value=" ".join(text)) custom_command.save() await ctx.message.author.send( _("Command {command_name} created!").format( command_name=command_name))
async def log(self, ctx): """ Retrieve the latest log from WarcraftLogs for your guild. """ # TODO Allow a parameter to give the guild name. guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if not guild_server: raise commands.BadArgument(_("The owner of the server needs to configure at least 1 default guild first!")) key = os.getenv("WARCRAFTLOGS_KEY") wc_request = requests.get(f"https://www.warcraftlogs.com/v1/reports/guild/{guild_server.guild_name}/{guild_server.server_slug}/{guild_server.get_region_display()}", params={"api_key": key}) if not wc_request.ok: await ctx.send(content=_("The guild is not found on WarcraftLogs. Does the guild exist on the website?")) return wc_json = wc_request.json() if wc_json: log = wc_json[0] embed = Embed() embed.title = log["title"] embed.url = f"https://www.warcraftlogs.com/reports/{log['id']}" embed.set_thumbnail(url=f"https://dmszsuqyoe6y6.cloudfront.net/img/warcraft/zones/zone-{log['zone']}-small.jpg") embed.add_field(name=_("Created by"), value=log['owner'], inline=True) embed.timestamp = datetime.datetime.utcfromtimestamp(log['start'] / 1000).replace(tzinfo=simple_utc()) wc_zones = requests.get("https://www.warcraftlogs.com/v1/zones", params={"api_key": key}) if wc_zones.ok: zones_json = wc_zones.json() for zone in zones_json: if log['zone'] == zone['id']: embed.add_field(name="Zone", value=zone['name'], inline=True) await ctx.send(embed=embed) else: await ctx.send(_("The guild {guild_name} got no public logs!").format(guild_name=guild_server.guild_name))
def __sub_format_ranking(self, difficulty): if difficulty is not None: ranking = _("World: **{world}**").format(world=difficulty['world']) + "\n" ranking += _("Region: **{region}**").format(region=difficulty['region']) + "\n" ranking += _("Realm: **{realm}**").format(difficulty['realm']) + "\n" else: ranking = _("**Not started**")+"\n" return ranking
async def ensure_voice(self, ctx): if ctx.voice_client is None: if ctx.author.voice: await ctx.author.voice.channel.connect() else: await ctx.send(_("You are not connected to a voice channel.")) raise commands.CommandError( _("Author not connected to a voice channel."))
async def prefix(self, ctx): """Manages the Guild's custom prefixes. If called without a subcommand, this will list the currently set prefixes. """ prefixes = GuildPrefix.objects.filter(guild_id=ctx.guild.id).all() embed = Embed(title=_('LegendaryBot prefixes configured.'), colour=Colour.blurple()) if prefixes: embed.description = '\n'.join(f'{prefix.prefix}' for prefix in prefixes) else: embed.description = _("No prefixes set.") await ctx.message.author.send(embed=embed)
async def resume(self, ctx): """Resume the currently paused song.""" vc = ctx.voice_client if not vc or not vc.is_connected(): return await ctx.send(_('I am not currently playing anything!'), delete_after=20) elif not vc.is_paused(): return vc.resume() await ctx.send( _('**`{user}`**: Resumed the song!').format(user=ctx.author))
async def on_command_error(self, ctx, error): if isinstance(error, commands.NoPrivateMessage): await ctx.author.send( _('This command cannot be used in private messages.')) elif isinstance(error, commands.DisabledCommand): await ctx.author.send( _('Sorry. This command is disabled and cannot be used.')) elif isinstance(error, commands.BadArgument) or isinstance( error, commands.MissingRequiredArgument): await ctx.author.send(error) else: exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_exception(exc_type, error, exc_traceback)
async def info(self, ctx): """ Get information about the bot. """ embed = Embed(title=_("LegendaryBot")) embed.set_author( name="Greatman", url="https://github.com/LegendaryBot/bot", icon_url="https://avatars3.githubusercontent.com/u/95754?v=3&s=460" ) embed.description = _( "Created using Discord.py. Type @LegendaryBot help to show all the commands." ) await ctx.send(embed=embed)
async def token(self, ctx, region: str = None): """ Get the WoW token price of your region """ if region is None and ctx.guild: guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if guild_server: region = guild_server.get_region_display() else: raise commands.BadArgument(_('You are required to type the region. Supported regions are: NA/EU/CN/TW/KR')) token_request = requests.get("https://data.wowtoken.info/snapshot.json") token_json = token_request.json() if region.upper() == "US": region = "NA" if region.upper() in token_json: region_json = token_json[region.upper()]['formatted'] embed = Embed(title=_("Price for 1 WoW Token in the {region} region").format(region=region.upper()), color=Colour.from_rgb(255, 215, 0)) embed.set_footer(text=_("Information taken from https://wowtoken.info"), icon_url="http://wow.zamimg.com/images/wow/icons/large/wow_token01.jpg") embed.set_thumbnail(url="http://wow.zamimg.com/images/wow/icons/large/wow_token01.jpg") embed.add_field(name=_("Current Price"), value=region_json['buy'], inline=True) embed.add_field(name=_("Minimum 24H"), value=region_json['24min'], inline=True) embed.add_field(name=_("Maximum 24H"), value=region_json['24max'], inline=True) embed.add_field(name=_("Percentage 24H Range"), value="%s %%" % region_json['24pct']) await ctx.send(embed=embed) else: raise commands.BadArgument(_('Region not found. Supported regions are: NA/EU/CN/TW/KR'))
async def pause(self, ctx): """Pause the currently playing song.""" vc = ctx.voice_client if not vc or not vc.is_playing(): return await ctx.send(_('I am not currently playing anything!'), delete_after=20) elif vc.is_paused(): return vc.pause() await ctx.send( _('**`{user}`**: Paused the song!').format(user=ctx.author), delete_after=60)
async def now_playing_(self, ctx): """Display information about the currently playing song.""" vc = ctx.voice_client if not vc or not vc.is_connected(): return await ctx.send(_('I am not currently connected to voice!'), delete_after=20) player = self.get_player(ctx) if not player.current: return await ctx.send(_('I am not currently playing anything!')) await ctx.send( _('**Now Playing:** `{title}` requested by `{requester}`').format( title=vc.source.title, requester=vc.source.requester))
async def sync(self, ctx): ''' Sync your rank on this Discord server. ''' setting = GuildSetting.objects.filter(setting_name="rank_enabled", guild=ctx.guild.id).first() if setting: await self.run_user_sync(ctx.guild, setting, ctx.author) await ctx.message.author.send( _("Your rank is being synced. It may take some minutes to apply." )) else: await ctx.message.author.send( _("The Rank System is not enabled. Please ask bot author to enable it." ))
async def create_source(cls, ctx, search: str, *, loop, download=False): loop = loop or asyncio.get_event_loop() to_run = partial(ytdl.extract_info, url=search, download=download) data = await loop.run_in_executor(None, to_run) if 'entries' in data: # take first item from a playlist data = data['entries'][0] await ctx.send( _('```ini\n[Added {song_title} to the Queue.]\n```').format( song_title=data["title"]), delete_after=15) if download: source = ytdl.prepare_filename(data) else: return { 'webpage_url': data['webpage_url'], 'requester': ctx.author, 'title': data['title'] } return cls(discord.FFmpegPCMAudio(source), data=data, requester=ctx.author)
async def skip_(self, ctx): """Skip the song.""" vc = ctx.voice_client if not vc or not vc.is_connected(): return await ctx.send(_('I am not currently playing anything!'), delete_after=20) if vc.is_paused(): pass elif not vc.is_playing(): return vc.stop() await ctx.send( _('**`{user}`**: Skipped the song!').format(user=ctx.author))
async def syncguild(self, ctx): ''' Sync the Guild ranks with all users ''' setting = GuildSetting.objects.filter(setting_name="rank_enabled", guild=ctx.guild.id).first() if setting: await self.run_sync(setting) await ctx.message.author.send( _("Guild Rank Sync started. It may take some minutes to apply. Check the server Audit log for any changes." )) else: await ctx.message.author.send( _("The Rank System is not enabled. Please ask bot author to enable it." ))
def __format_ranking(self, raid_json): return_string = "" normal = raid_json['normal'] heroic = raid_json['heroic'] mythic = raid_json['mythic'] if normal['world'] != 0 and heroic['world'] == 0 and mythic['world'] == 0: return_string += _("**Normal**") + "\n" return_string += self.__sub_format_ranking(normal) elif heroic['world'] != 0 and mythic['world'] == 0: return_string += "\n" + _("**Heroic**") + "\n" return_string += self.__sub_format_ranking(heroic) elif mythic['world'] != 0: return_string += "\n" + _("**Mythic**") + "\n" return_string += self.__sub_format_ranking(mythic) else: return_string += self.__sub_format_ranking(None) return return_string
async def volume(self, ctx, volume: int): """Changes the player's volume""" if volume < 0 or volume > 100: raise commands.CommandError( _("The minimum volume is 0 and the maximum is 100.")) if ctx.voice_client is None: return await ctx.send(_("Not connected to a voice channel.")) player = self.get_player(ctx) if player.current: player.current.volume = volume / 100 player.volume = volume / 100 await ctx.send(_("Changed volume to {volume}%").format(volume=volume), delete_after=20)
async def remove_custom_command(self, ctx, command_name: str): """ Remove a custom command from a Discord server. You must have one of the following to be able to use the command: - The legendarybot-admin role. - The Manage Server or the Administrator permission. """ custom_command = GuildCustomCommand.objects.filter( guild_id=ctx.guild.id, name=command_name).first() if custom_command: custom_command.delete() await ctx.message.author.send( _("Command {command_name} removed!").format( command_name=command_name)) else: await ctx.message.author.send( _("Command {command_name} not found!").format( command_name=command_name))
async def prefix_remove(self, ctx, prefix: Prefix): """ Remove a prefix from the list of custom prefixes for this Discord server. You must have one of the following to be able to use the command: - The legendarybot-admin role. - The Manage Server or the Administrator permission. """ prefix_entry = GuildPrefix.objects.filter(guild_id=ctx.guild.id, prefix=prefix).first() if prefix_entry: prefix_entry.delete() await ctx.send( _("Prefix {prefix} removed from the server").format( prefix=prefix)) else: await ctx.send( _("Prefix {prefix} does not exist.").format(prefix=prefix))
async def custom_commands(self, ctx): """ Manage the custom commands of the Discord server. This system allows you to create custom commands that the bot will reply with. Example: You can create a !ping text command that will reply Pong! """ embed = Embed( title=_("Custom commands for the {guild_name} server.").format( guild_name=ctx.guild.name), colour=Colour.blurple()) custom_commands = GuildCustomCommand.objects.filter( guild_id=ctx.guild.id).all() if custom_commands: embed.description = '\n'.join(f'{command.name}' for command in custom_commands) else: embed.description = _('No custom commands!') await ctx.message.author.send(embed=embed)
async def synchelp(self, ctx): ''' Get information about the Sync system ''' await ctx.message.author.send( _("The Sync system allows you to have your Discord Rank Synced to your ingame WoW guild rank.\n" "For Server owners: Go on https://legendarybot.info, go in your server settings and configure the WoW Servers and the WoW Ranks section. \n" "This feature is still in BETA, which means it needs to be enabled by the bot owner (Greatman). Contact him here: https://discord.gg/Cr7G28H\n" "For Users: Go on https://legendarybot.info, go in the Myself section and sync your Battle.Net account with the website. Then, select which character is the main character in the discord server you want." ))
async def player_loop(self): await self.bot.wait_until_ready() while not self.bot.is_closed(): self.next.clear() #We retrieve the next song in the queue try: # Wait for the next song. If we timeout cancel the player and disconnect... async with timeout(300): # 5 minutes... source = await self.queue.get() except asyncio.TimeoutError: return self.destroy(self._guild) if not isinstance(source, YTDLSource): # Source was probably a stream (not downloaded) # So we should regather to prevent stream expiration try: source = await YTDLSource.regather_stream( source, loop=self.bot.loop) except Exception as e: await self._channel.send( _('There was an error processing your song.') + f"\n```css\n[{e}]\n```") continue #Set the volume to current value source.volume = self.volume #Set the current song information self.current = source self._guild.voice_client.play( source, after=lambda _: self.bot.loop.call_soon_threadsafe(self.next. set())) await self._channel.send(_( "```ini\n[Now playing {song_title}. Requested by {requester}]```" ).format(song_title=source.title, requester=source.requester), delete_after=source.duration) await self.next.wait() source.cleanup() self.current = None
async def invite(self, ctx): """ Get the bot invite link """ await ctx.send( _("To invite LegendaryBot to your server. Click this link: <{link}>" ). format( link= "https://discordapp.com/oauth2/authorize?client_id=267134720700186626&scope=bot&permissions=3165248" ))
async def prefix_add(self, ctx, prefix: Prefix): """ Add a prefix to the list of custom prefixes for this Discord server. Previously set prefixes are not overridden. You must have one of the following to be able to use the command: - The legendarybot-admin role. - The Manage Server or the Administrator permission. """ prefix_entry = GuildPrefix.objects.filter(guild_id=ctx.guild.id, prefix=prefix).first() if not prefix_entry: prefix_entry = GuildPrefix(guild_id=ctx.guild.id, prefix=prefix) prefix_entry.save() await ctx.send( _("Prefix {prefix} added to the server.").format(prefix=prefix) ) else: await ctx.send( _("Prefix {prefix} not set. Already existing.").format( prefix=prefix))
async def queue_info(self, ctx): """Retrieve a basic queue of upcoming songs.""" vc = ctx.voice_client if not vc or not vc.is_connected(): return await ctx.send(_('I am not currently connected to voice!'), delete_after=20) player = self.get_player(ctx) if player.queue.empty(): return await ctx.send( _('There are currently no more queued songs.'), delete_after=20) # Grab up to 5 entries from the queue... upcoming = list(itertools.islice(player.queue._queue, 0, 5)) fmt = '\n'.join(f'**`{_["title"]}`**' for _ in upcoming) embed = discord.Embed( title=_('Upcoming - Next {songs}').format(songs=len(upcoming)), description=fmt) await ctx.send(embed=embed)
async def rank(self, ctx): """ Retrieve your Guild Raider.IO Ranking """ guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if not guild_server: raise commands.BadArgument(_("The owner of the server needs to configure at least 1 default guild first!")) query_parameters = { "region": guild_server.get_region_display(), "realm": guild_server.server_slug, "name": guild_server.guild_name, "fields": "raid_rankings" } raiderio_request = requests.get("https://raider.io/api/v1/guilds/profile", params=query_parameters) if not raiderio_request.ok: await ctx.send(content=_("The guild is not found on Raider.IO. Does the guild exist on the website?")) return ranking_json = raiderio_request.json() raid_rankings = ranking_json['raid_rankings'] embed = Embed() embed.title = _("{guild_name}-{server_name} Raid Rankings").format(guild_name=guild_server.guild_name, server_name=guild_server.server_slug) embed.add_field(name=_("Battle of Dazar'alor"), value=self.__format_ranking(raid_rankings['battle-of-dazaralor']), inline=True) embed.add_field(name=_("Uldir"), value=self.__format_ranking(raid_rankings['uldir']), inline=True) await ctx.send(embed=embed)
async def get_realm_status(self, ctx, region: str = None, *realm: str): """ Get the status of a World of Warcraft realm. [realm]: The realm you want to check the status of. Optional if the server have a realm set during initial setup. If your realm name have spaces in it's name, please quote the name with "". Example: "Bleeding Hollow" """ if region is None or realm is None and ctx.guild: guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if guild_server: realm_slug = guild_server.server_slug else: raise commands.BadArgument(_('You are required to type a realm.')) else: realm = " ".join(realm) realm_slug = slugify(realm) if ctx.guild: guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if guild_server: region = guild_server.get_region_display() params = { "namespace": f"dynamic-{region}", "locale": "en-US" } r = battlenet_util.execute_battlenet_request(f"https://{region}.api.blizzard.com/data/wow/realm/{realm_slug}", params) if r.ok: r = battlenet_util.execute_battlenet_request(f"https://{region}.api.blizzard.com/wow/realm/status", params={"realms": realm_slug}) json_result = r.json() if 'realms' in json_result and len(json_result['realms']) > 0: realm_json = json_result['realms'][0] embed = Embed(title=f"{realm_json['name']} - {region.upper()}", colour=Colour.green() if realm_json['status'] else Colour.red()) embed.add_field(name=_("Status"), value=_("Online") if realm_json['status'] else _("Offline"), inline=True) embed.add_field(name=_("Population"), value=realm_json['population'], inline=True) embed.add_field(name=_("Currently a Queue?"), value=_("Yes") if realm_json['queue'] else _("No"), inline=True) await ctx.send(embed=embed) else: raise commands.BadArgument(_('Realm not found. Did you make a mistake?'))
async def convert(self, ctx, argument): if argument != "text": raise commands.BadArgument( _("The only valid custom command type are: text")) return argument
async def lookup(self, ctx, character_name: str = None, realm_name: str = None, region: str = None): """ Lookup a specific character character_name: The character name you want to search (Optional if your main character is set) realm_name: The realm of the character (Optional if the guild is set in this server) region: The region (US/EU) the character is in (Optional if the guild is set in this server) """ if not character_name: user_social = UserSocialAuth.objects.filter(provider='discord', uid=ctx.author.id).first() if user_social: character = Character.objects.filter(user=user_social.user, main_for_guild=ctx.guild.id).first() if character: character_name = character.name realm_name = character.server_slug region = character.get_region_display() else: raise commands.BadArgument(_("You must enter a character name.")) else: raise commands.BadArgument(_("You must enter a character name.")) if not realm_name: guild_server = GuildServer.objects.filter(guild_id=ctx.guild.id, default=True).first() if guild_server: realm_name = guild_server.server_slug region = guild_server.get_region_display() else: raise commands.BadArgument(_("You must put the realm and the region.")) if not region: raise commands.BadArgument(_("The only valid regions are US or EU.")) if region.lower() != "us" and region.lower() != "eu": raise commands.BadArgument(_("The only valid regions are US or EU.")) region = region.lower() realm_name = slugify(realm_name) not_ok = True while not_ok: payload = { "region": region, "realm": realm_name, "name": character_name, "fields": "gear,raid_progression,mythic_plus_scores,previous_mythic_plus_scores,mythic_plus_best_runs" } r = requests.get(f"https://raider.io/api/v1/characters/profile", params=payload) bnet_request = battlenet_util.execute_battlenet_request(f"https://{region}.api.blizzard.com/wow/character/{realm_name}/{character_name}", params={"fields": "achievements,stats,items"}) i = 0 if r.ok: not_ok = False else: #We did not find the character, let's see another connected realm realm_database = RealmConnected.objects.filter(server_slug=realm_name).first() connected_realms = realm_database.connected_realm.all() if len(connected_realms) > i: realm_name = realm_database.connected_realm.all()[i] i += 1 else: raise commands.BadArgument(_("Character not found! Does it exist on Raider.IO?")) raiderio = r.json() embed = Embed() embed.set_thumbnail(url=raiderio['thumbnail_url']) embed.colour = get_color_by_class_name(raiderio['class']) if raiderio['region'].lower() == "us": wow_link = f"https://worldofwarcraft.com/en-us/character/{realm_name}/{character_name}" else: wow_link = f"https://worldofwarcraft.com/en-gb/character/{realm_name}/{character_name}" embed.set_author( name=f"{raiderio['name']} {raiderio['realm']} - {raiderio['region'].upper()} | {raiderio['race']} {raiderio['active_spec_name']} {raiderio['class']}", icon_url=get_class_icon(raiderio['class']), url=wow_link) raid_progression = raiderio['raid_progression'] embed.add_field(name=_("Progression"), value=_("**Ny'alotha** : {nwcprogression} **EP** : {epprogression} **CoS**: {cosprogression} **BoD** : {bodprogression} ").format(bodprogression=raid_progression["battle-of-dazaralor"]["summary"], cosprogression=raid_progression["crucible-of-storms"]["summary"], epprogression=raid_progression["the-eternal-palace"]["summary"], nwcprogression=raid_progression["nyalotha-the-waking-city"]["summary"]), inline=False) embed.add_field(name=_("iLVL"), value=f"{raiderio['gear']['item_level_equipped']}/{raiderio['gear']['item_level_total']}", inline=True) if bnet_request.ok: bnet_json = bnet_request.json() cape = bnet_json["items"]["back"] if cape["id"] == 169223: cape_rank = int(((cape["itemLevel"] - 470) / 2) + 1) embed.add_field(name=_("Legendary Cloak Rank"), value=cape_rank, inline=True) previous = 0 seasons = [] seasons.append({ "name": "S1", "score": get_season_rank(region, realm_name, character_name, "season-bfa-1") }) seasons.append({ "name": "S2", "score": get_season_rank(region, realm_name, character_name, "season-bfa-2") }) seasons.append({ "name": "S2-Post", "score": get_season_rank(region, realm_name, character_name, "season-bfa-2-post") }) seasons.append({ "name": "S3", "score": get_season_rank(region, realm_name, character_name, "season-bfa-3") }) seasons.append({ "name": "S3-Post", "score": get_season_rank(region, realm_name, character_name, "season-bfa-3-post") }) seasons.append({ "name": "S4", "score": get_season_rank(region, realm_name, character_name, "season-bfa-4") }) rank, rank_name = get_best_season(seasons) embed.add_field(name=_("Mythic+ Score"), value=f"**Current**: {raiderio['mythic_plus_scores']['all']} | **Best**: {rank} (**{rank_name}**)", inline=False) best_runs = "" for mythicplus_run in raiderio['mythic_plus_best_runs']: best_runs += f"[{mythicplus_run['dungeon']} - **" if mythicplus_run['num_keystone_upgrades'] == 1: best_runs += "+ " elif mythicplus_run['num_keystone_upgrades'] == 2: best_runs += "++ " elif mythicplus_run['num_keystone_upgrades'] == 3: best_runs += "+++ " seconds, minutes, hour = convertMillis(mythicplus_run['clear_time_ms']) best_runs += f"{mythicplus_run['mythic_level']}** {hour}:{minutes}:{seconds}]({mythicplus_run['url']})\n" if best_runs: embed.add_field(name=_("Best Mythic+ Runs"), value=best_runs, inline=True) if bnet_request.ok: bnet_json = bnet_request.json() mplus_totals = "" try: index = bnet_json['achievements']['criteria'].index(33097) mplus_totals += f"**M+5**:{bnet_json['achievements']['criteriaQuantity'][index]}\n" except ValueError: pass try: index = bnet_json['achievements']['criteria'].index(33098) mplus_totals += f"**M+10**:{bnet_json['achievements']['criteriaQuantity'][index]}\n" except ValueError: pass try: index = bnet_json['achievements']['criteria'].index(32028) mplus_totals += f"**M+15**:{bnet_json['achievements']['criteriaQuantity'][index]}\n" except ValueError: pass embed.add_field(name=_("Mythic+ Completed"), value=mplus_totals, inline=True) stats = "" strength = bnet_json['stats']['str'] agi = bnet_json['stats']['agi'] intel = bnet_json['stats']['int'] if strength > agi and strength > intel: stats += _("**STR**: {strength}").format(strength=strength) + " - " elif agi > strength and agi > intel: stats += _("**AGI**: {agility}").format(agility=agi) + " - " else: stats += _("**INT**: {intel}").format(intel=intel) + " - " stats += _("**Crit**: {percent}% {rating}").format(percent=round(Decimal(bnet_json['stats']['crit']), 2), rating=bnet_json['stats']['critRating']) + "\n" stats += _("**Haste**: {percent}% {rating}").format(percent=round(Decimal(bnet_json['stats']['haste']), 2), rating=bnet_json['stats']['hasteRating']) + " - " stats += _("**Mastery**: {percent}% {rating}").format(percent=round(Decimal(bnet_json['stats']['mastery']), 2), rating=bnet_json['stats']['masteryRating']) + "\n" stats += _("**Versatility**: D:{percent_damage} B:{percent_block} ({rating})").format(percent_damage=round(Decimal(bnet_json['stats']['versatilityDamageDoneBonus']),2), percent_block=round(Decimal(bnet_json['stats']['versatilityDamageTakenBonus']),2), rating=bnet_json['stats']['versatility']) + "\n" embed.add_field(name=_("Stats"), value=stats, inline=False) embed.add_field(name="WoWProgress", value=_("[Click Here]({url})").format(url=f"https://www.wowprogress.com/character/{region}/{realm_name}/{character_name}"), inline=True) embed.add_field(name="Raider.IO", value=_("[Click Here]({url})").format(url=f"https://raider.io/characters/{region}/{realm_name}/{character_name}"), inline=True) embed.add_field(name="WarcraftLogs", value=_("[Click Here]({url})").format(url=f"https://www.warcraftlogs.com/character/{region}/{realm_name}/{character_name}"), inline=True) embed.set_footer(text=_("Information taken from Raider.IO")) await ctx.send(embed=embed)
async def linkwowchars(self, ctx): """ Let LegendaryBot know your characters. """ await ctx.send(_("To link the bot to your characters, please go to https://legendarybot.info/myself"))