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 __init__(self, client): self.client = client self.client_color = Color.red() self.color_dict = { 1: [Color.teal(), 'teal'], 2: [Color.dark_teal(), 'dark_teal'], 3: [Color.green(), 'green'], 4: [Color.dark_green(), 'dark_green'], 5: [Color.blue(), 'blue'], 6: [Color.dark_blue(), 'dark_blue'], 7: [Color.purple(), 'purple'], 8: [Color.dark_purple(), 'dark_purple'], 9: [Color.magenta(), 'magenta'], 10: [Color.dark_magenta(), 'dark_magenta'], 11: [Color.gold(), 'gold'], 12: [Color.dark_gold(), 'dark_gold'], 13: [Color.orange(), 'orange'], 14: [Color.dark_orange(), 'dark_orange'], 15: [Color.red(), 'red'], 16: [Color.dark_red(), 'dark_red'], 17: [Color.lighter_grey(), 'lighter_grey'], 18: [Color.dark_grey(), 'grey'], 19: [Color.light_grey(), 'light_grey'], 20: [Color.darker_grey(), 'darker_grey'] }
async def begin(self, ctx): if self.tournament.status != Status.Opened: raise commands.BadArgument( "The tournament cannot be closed right now.") if len(self.tournament.get_participants()) < 1: raise commands.BadArgument( "There are no participants in the tournament.") no_ign = "" for player in self.tournament.get_participants(): user = User.fetch_by_id(ctx, player.id) if user.ign is None: no_ign += f"**{player.name}**\n" if no_ign != "": raise commands.BadArgument( "Some players do not have an IGN set: \n" + no_ign) await ctx.send( f"{Emote.check} The tournament has been closed. Players can no longer join!" ) self.tournament.status = Status.Closed Log("Tournament Closed", description= f"{ctx.author.mention} closed **{self.tournament.name}**.", color=Color.dark_orange()) await self.tournament.msg.clear_reactions() embed = UpdatedEmbed(self.tournament) await self.tournament.msg.edit(embed=embed)
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, ])
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 pypi(self, ctx: CommandContext, search, *, text=''): message: Message = ctx.message proc = Popen(['pip', 'search', search], stdout=PIPE, stderr=PIPE) if proc.wait() != 0: await message.edit( embed=Embed( color=Color.red(), description='Failed to search for `{search}` on pypi.')) stdout, _ = proc.communicate() stdout = stdout.decode(encoding='utf-8') lines: List[str] = stdout.split('\n') content = lines[0] + ' '.join(line.strip() for line in lines[1:] if line.startswith(' ')) match = matcher.match(content) if not match: await message.edit( embed=Embed( color=Color.dark_orange(), description=f"Weird response format:```\n{stdout[:512]}```")) name = match.group("name") version = match.group("version") description = match.group("description") await message.edit( content=text, embed=Embed( title=name, url=f"https://pypi.org/project/{name}/", description=description ).set_footer(text=version))
async def random(self, ctx): reference = await get_random_verse() verses = await get_verse(reference) embed = Embed(title=verses["heading"], description=verses["text"], url=verses["url"], color=Color.dark_orange()) await ctx.channel.send(embed=embed)
async def bible_verse(self, ctx, *, reference): """returns a bible verse or passage from the format 'book chapter:verse(s)'""" verses = await get_verse(reference) embed = Embed(title=verses["heading"], description=verses["text"], url=verses["url"], color=Color.dark_orange()) await ctx.channel.send(embed=embed)
async def getinfo(self, ctx: Context, *, sentence: str) -> None: detection = await translator.detect(sentence) embed = Embed(title="Sentence Info", color=Color.dark_orange()) embed.add_field(name="Language Code", value=detection.lang, inline=False) embed.add_field(name="Confidence", value=detection.confidence, inline=False) await ctx.send(embed=embed)
async def on_voice_state_update(self, member: Member, before: VoiceState, after: VoiceState) -> None: if before.afk != after.afk: embed = Embed(title="User went AFK" if after.afk else "User is no longer AFK", description=f"**User:** {member.mention}", color=Color.blue()) elif before.channel != after.channel: description_lines = [f"**User:** {member.mention}"] if before.channel: description_lines.append( f"**Previous Channel:** {before.channel}") if after.channel: description_lines.append(f"**New Channel:** {after.channel}") embed = Embed(title="User changed channels" if after.channel else "User left voice channel", description="\n".join(description_lines), color=Color.blue()) elif before.deaf != after.deaf: embed = Embed( title="User deafened" if after.deaf else "User undeafened", description=f"**User:** {member.mention}", color=Color.dark_orange()) elif before.mute != after.mute: embed = Embed( title="User silenced" if after.mute else "User unsilenced", description=f"**User:** {member.mention}", color=Color.dark_orange()) # Client actions cal also happen here, i.e. self mute/deaf/stream/video # we don't need to log those, so we can return early in that case else: return embed.set_thumbnail(url=member.avatar_url) await self.send_log(member.guild, embed=embed)
async def delegate_ranks(self): all_delegates = self.bot.dpops_queries.delegates.get_delegates() print("getting delegates") if isinstance(all_delegates, list): for delegate in all_delegates: delegate["total_vote_count"] = int( delegate["total_vote_count"]) newlist = sorted(all_delegates, key=itemgetter('total_vote_count'), reverse=True) top_10 = newlist[0:10] stats_chn = self.bot.get_channel(id=self.bot.stats_channel_id) await top_delegates(destination=stats_chn, c=Color.dark_orange(), data=top_10) else: error = all_delegates["error"] print(error)
async def on_member_remove(self, member: Member) -> None: if self.bot.log_is_ignored(Event.member_remove, (member.guild.id, member.id)): return description = ( f"**Mention:** {member.mention}\n" f"**Joined:** {time_elapsed(member.joined_at, max_units=3)}\n") roles = ", ".join(role.mention for role in member.roles[1:]) description += f"**Roles:** {roles}\n" if roles else '' description += f"**Members:** Server is now at {member.guild.member_count} members." embed = Embed( title="Member left", description=description, color=Color.dark_orange(), ) embed.timestamp = datetime.datetime.now() embed.set_thumbnail(url=member.avatar_url) embed.set_footer(text=f"Member ID: {member.id}") await self.send_log(member.guild, embed=embed)
def new_event_em(text: str) -> Embed: return Embed(title=':dolls: Nowy event!', color=Color.dark_orange()).add_field( name='Temat', value=text).add_field( name='\u200b', value='Szczegóły zaraz zostaną podane.')
async def hangman(self, ctx: Context) -> None: def display_hangman(tries: int) -> str: stages = [ # final state: head, torso, both arms, and both legs r"""``` -------- | | | O | \|/ | | | / \ - ```""", # head, torso, both arms, and one leg r"""``` -------- | | | O | \|/ | | | / - ```""", # head, torso, and both arms r"""``` -------- | | | O | \|/ | | | - ```""", # head, torso, and one arm r"""``` -------- | | | O | \| | | | - ```""", # head and torso r"""``` -------- | | | O | | | | | - ```""", # head r"""``` -------- | | | O | | | - ```""", # initial empty state r"""``` -------- | | | | | | - ```""", ] return stages[tries] def check(message: Message) -> bool: return message.author == ctx.author and message.channel == ctx.channel word = random.choice(word_list).upper() word_completion = "#" * len(word) guessed = False guessed_letters = [] guessed_words = [] tries = 6 await ctx.send( embed=Embed(title="Let's play Hangman!", color=Color.dark_green())) embed = Embed(title="Hangman Status", color=Color.dark_teal()) embed.add_field(name="**❯❯ Hang Status**", value=display_hangman(tries), inline=False) embed.add_field(name="**❯❯ Word Completion Status**", value=f"**{word_completion}**", inline=False) embed.set_footer(text="Powered By HotWired.") await ctx.send(embed=embed) while not guessed and tries > 0: await ctx.send( embed=Embed(description="Please guess a letter or word: ", color=Color.gold())) input = await self.bot.wait_for("message", check=check) guess = input.content.upper() if len(guess) == 1 and guess.isalpha(): if guess in guessed_letters: await ctx.send(embed=Embed( description=f"You already guessed the letter {guess}", color=Color.red())) elif guess not in word: await ctx.send( embed=Embed(description=f"{guess} is not in the word.", color=Color.dark_magenta())) tries -= 1 guessed_letters.append(guess) else: await ctx.send(embed=Embed( description=f"Good job, {guess} is in the word!", color=Color.dark_green())) guessed_letters.append(guess) word_as_list = list(word_completion) indices = [ i for i, letter in enumerate(word) if letter == guess ] for index in indices: word_as_list[index] = guess word_completion = "".join(word_as_list) if "#" not in word_completion: guessed = True elif len(guess) == len(word) and guess.isalpha(): if guess in guessed_words: await ctx.send(embed=Embed( description=f"You already guessed the word {guess}", color=Color.red())) elif guess != word: await ctx.send( embed=Embed(description=f"{guess} is not the word.", color=Color.dark_orange())) tries -= 1 guessed_words.append(guess) else: guessed = True word_completion = word else: await ctx.send(embed=Embed(description="Not a valid guess.", color=Color.blurple())) embed = Embed(title="Hangman Status", color=Color.dark_teal()) embed.add_field(name="**❯❯ Hang Status**", value=display_hangman(tries), inline=False) embed.add_field(name="**❯❯ Word Completion Status**", value=f"**{word_completion}**", inline=False) embed.set_footer(text="Powered By HotWired.") await ctx.send(embed=embed) if guessed: await ctx.send(embed=Embed( description= "Congrats, you guessed the word! You win! :partying_face: ", color=Color.dark_green())) else: await ctx.send(embed=Embed( description= f"Sorry, you ran out of tries. The word was {word}. Maybe next time! :frowning: ", color=Color.red()))
async def on_raw_message_edit(self, payload: RawMessageUpdateEvent) -> None: """ Send an embed whenever uncached message got edited, we do not have the previous contents of this message, so we can only send the current (edited) content, and the fact that it was actually edited. This listener trigers whenever a message is edited, this includes the messages that are cached and trigger `on_message_edit` directly, which means we have to ignore these. Disocrd's API also triggers this listener whenever an embed is sent. This is quite unexpected behavior for this event, and we should ignore it as it isn't an actual message edit event. In case neither of these cases are true, we may log the embed (with some further checks which are also present in `on_message_edit`, such as DM check). """ # Try to fetch the message before it may get removed, if we don't manage that # we can ignore this event try: channel = self.bot.get_channel(int(payload.channel_id)) message = await channel.fetch_message(payload.message_id) except NotFound: return # As described in docstring, this even also triggers on embed sending, if that's # the case, we can simply ignore that if "embeds" in payload.data and len(payload.data["embeds"]) > 0: return # Sleep for a while to leave enough time for normal event to execute, which will add this # channel into the handled cached set, to avoid double logging await asyncio.sleep(1) if (message.guild.id, message.id) in self._handled_cached: return if self.is_ignored(message, Event.message_edit): return response = ( f"**Author:** {message.author.mention}\n" f"**Channel:** {message.channel.mention}\n" f"**Before:** This message is an uncached message, content can't be displayed" ) if len(message.clean_content) > MAX_LOGGED_MESSAGE_SIZE: url = await upload_text( self.bot.http_session, message.content, file_name="message.md", paste_name="Automatic message upload.", paste_description= "This paste was automatically generated from edited discrod message." ) if url: response += f"\n**After:** Message too long, check [message upload]({url})" else: response += "\n**After:** Message too long (WARNING: Automatic upload failed" else: response += f"\n**After:** {message.content}" response += f"\n[Jump url]({message.jump_url})" embed = Embed(title="Uncached message edited", description=response, color=Color.dark_orange()) embed.timestamp = datetime.datetime.utcnow() embed.set_footer(text=f"Message ID: {message.id}\n") await self.send_log(message.guild, embed=embed)
async def doctor(self, ctx: 'CustomContext'): waiting_message = await ctx.send( "<a:loading:393852367751086090> Please wait, running `doctor` checks" ) # <a:loading:393852367751086090> is a loading emoji t_1 = time.perf_counter() await ctx.trigger_typing( ) # tell Discord that the bot is "typing", which is a very simple request t_2 = time.perf_counter() time_delta = round( (t_2 - t_1) * 1000) # calculate the time needed to trigger typing del self.bot.settings.settings_cache[ctx.guild] messages = {} message = [] # Permissions wanted_permissions = discord.permissions.Permissions.none() wanted_permissions.update(kick_members=True, ban_members=True, read_messages=True, send_messages=True, manage_messages=True, embed_links=True, attach_files=True, read_message_history=True, external_emojis=True, change_nickname=True, add_reactions=True) message.append("```diff") errored_in = [] for channel in ctx.guild.channels: my_permissions = ctx.message.guild.me.permissions_in(channel) if not my_permissions.is_strict_superset(wanted_permissions): errored_in.append(channel) if len(errored_in) == 0: message.append("+ Everything checks out! No permissions problems") else: message.append( f"= The following channels have permissions problems, use the {ctx.prefix}bot_permissions_check command in them to see what's missing" ) message.extend(["- #" + channel.name for channel in errored_in]) top_role = ctx.message.guild.me.top_role # Position isn't guaranteed to not have gaps # Discord pls message.append( f"= Bot top role is at position {top_role.position}/{ctx.guild.roles[-1].position} [higher is better] - " f"Any user that have a role equal or higher to <{top_role.name}> can't be kicked/banned" ) message.append("```") messages["Bot Permissions"] = discord.Embed( description="\n".join(message), color=Color.green() if len(errored_in) == 0 else Color.red()) # Settings message = [ "If a setting is enabled, the line will be in green. If it's disabled, the line will be red ```diff" ] settings_char = {True: "+ ", False: "- "} guild = ctx.guild settings_to_check = { "automod_enable": "Automod", "autotrigger_enable": "AutoTriggers (special automod rules to fight a specific kind of spam message — Require automod to be enabled too)", "thresholds_enable": "Thresholds (automatic action when a users received X strikes)", "logs_enable": "Logs", "autoinspect_enable": "AutoInspect (Verification of new members that join your server)", "rolepersist_enable": "RolePersist (VIP)" } for setting, display_name in settings_to_check.items(): setting_enabled = await self.bot.settings.get(guild, setting) message.append(settings_char[setting_enabled] + display_name) message.append( f"```\n To edit your settings, and see more of them, use the `{ctx.prefix}urls` command to see your server webpage, " "then edit settings there (You may need to login first)\n") messages["Bot Settings"] = discord.Embed( description="\n".join(message), color=Color.dark_green()) # Logs logs_enabled = await self.bot.settings.get(guild, "logs_enable") if logs_enabled: logs = { "logs_moderation_channel_id": "Moderation acts", "logs_joins_channel_id": "Users joining/leaving", "logs_rolepersist_channel_id": "Roles persist", "logs_member_edits_channel_id": "Users edits", "logs_edits_channel_id": "Message edits", "logs_delete_channel_id": "Message deletions", "logs_autoinspect_channel_id": "AutoInspect logs" } everything_good = True message = [ "Logs are globally enabled on this server. The following specific logs are activated and configured: \n```diff" ] for setting, display_name in logs.items(): try: setting_value = int(await self.bot.settings.get(guild, setting)) except ValueError: message.append( f"= {display_name} log (enabled but there is text in the ID field, I can't parse it)" ) else: if setting_value == 0: message.append(f"- {display_name} log") everything_good = False else: channel_logged = discord.utils.get(guild.channels, id=setting_value) if channel_logged: message.append( f"+ {display_name} log (in #{channel_logged.name})" ) else: message.append( f"= {display_name} log (enabled but couldn't find the channel that goes by ID {setting_value})" ) everything_good = False message.append("```") messages["Logs"] = discord.Embed( description="\n".join(message), color=Color.green() if everything_good else Color.dark_orange()) message = [] # Staff l = await get_level(ctx, ctx.author) levels_names = { 10: "Bot owner", 9: "Reserved for future use", 8: "Bot global-moderators", 7: "Reserved for future use", 6: "Reserved for future use", 5: "Server owner", 4: "Server administrator", 3: "Server moderator", 2: "Server trusted user", 1: "Member", 0: "Bot-banned" } message.append( f"Your current access level is `{l}` ({levels_names[l]}).") embed = discord.Embed( description="\n".join(message), color=Color.green() if l >= 3 else Color.orange()) ids = await self.bot.settings.get(guild, 'permissions_admins') if len(ids) > 0: message = [ "The following user(s) have been granted server **admin** (4) here " "(This is not a complete list since it does **not** include people with the `administrator` permission) \n```diff" ] for admin_id in ids: admin = discord.utils.get(guild.members, id=admin_id) if admin: message.append( f"+ {admin.name}#{admin.discriminator} ({admin_id})") else: role = discord.utils.get(guild.roles, id=admin_id) if role: message.append(f"+ (Role) {role.name} ({admin_id})") else: message.append(f"- User left the server ({admin_id})") message.append("```") embed.add_field(name="Server administrators", value="\n".join(message), inline=False) ids = await self.bot.settings.get(guild, 'permissions_moderators') if len(ids) > 0: message = [ "The following user(s) have been granted server **moderator** (3) here " "(This is not a complete list since it does **not** include people with the `ban_members` permission) \n```diff" ] for mod_id in ids: mod = discord.utils.get(guild.members, id=mod_id) if mod: message.append( f"+ {mod.name}#{mod.discriminator} ({mod_id})") else: role = discord.utils.get(guild.roles, id=mod_id) if role: message.append(f"+ (Role) {role.name} ({mod_id})") else: message.append(f"- User left the server ({mod_id})") message.append("```") embed.add_field(name="Server moderators", value="\n".join(message), inline=False) ids = await self.bot.settings.get(guild, 'permissions_trusted') if len(ids) > 0: message = [ "The following user(s) have been granted server **trusted** (2) here " "(This is not a complete list since it does **not** include people with the `kick_members` permission) \n```diff" ] for trusted_id in ids: trusted = discord.utils.get(guild.members, id=trusted_id) if trusted: message.append( f"+ {trusted.name}#{trusted.discriminator} ({trusted_id})" ) else: role = discord.utils.get(guild.roles, id=trusted_id) if role: message.append(f"+ (Role) {role.name} ({trusted_id})") else: message.append( f"- User left the server ({trusted_id})") message.append("```") embed.add_field(name="Trusted users", value="\n".join(message), inline=False) messages["Staff"] = embed embed = discord.Embed( description= "This is stuff you can't do much about it, but just wait for the problems to go away if there are some. \n" "You might want to check https://status.discordapp.com for more information", color=Color.green() if time_delta < 200 else Color.red()) embed.add_field(name="Bot ping", value=f"{time_delta}ms") embed.add_field(name="Bot latency", value=f"{round(self.bot.latency * 1000)}ms") messages["Connexion"] = embed ok = True try: widget = await guild.widget() except discord.HTTPException: widget = None ok = False invite_code = await self.bot.settings.get(guild, 'invite_code') invite_error = False if not invite_code: ok = False invite_error = "No invite is set in your server settings. Please add it on the dashboard!" else: try: await self.bot.fetch_invite(invite_code) except discord.NotFound: invite_error = "The invite code was provided but is not working correctly. Please check that the invite is valid." ok = False except discord.HTTPException: invite_error = "There was an error reading your invite code. Please check that the invite is valid." ok = False embed = discord.Embed( description="Checking discord server settings since 1990", color=Color.green() if ok else Color.red()) embed.add_field( name="Widget enabled", value=f"Yes" if widget else "You should enable the server widget for it to show up properly on your server webpage." ) embed.add_field(name="Invite provided", value=f"Yes" if not invite_error else invite_error) messages["Server Settings"] = embed # Send everything for message_title, embed in messages.items(): embed.title = message_title await ctx.send(embed=embed) # await ctx.trigger_typing() await asyncio.sleep(.8) await waiting_message.delete()
import datetime import typing import pytz import discord from discord import Color from cogs.misc import LikeUser, FakeMember colours = { 'unban': Color.green(), 'unmute': Color.dark_green(), 'note': Color.light_grey(), 'warn': Color.orange(), 'mute': Color.dark_purple(), 'kick': Color.dark_orange(), 'softban': Color.red(), 'ban': Color.dark_red() } async def ban(victim: discord.Member, reason: str = None): await victim.guild.ban(victim, reason=reason) async def softban(victim: discord.Member, reason: str = None): await victim.guild.ban(victim, reason=reason) await victim.guild.unban(victim, reason=reason) async def kick(victim: discord.Member, reason: str = None):
async def on_message_edit(self, before: Message, after: Message) -> None: """ Send a log message whenever any sent message is edited. This is useful for moderation purposes, because users often try to hide what they send, to avoid getting caught breaking some rules, logging the content of these messages will prevent that. Messages can sometimes get quite long, and we don't want to clutter the log with them, if this is the case, we upload this message to a paste service and only provide a link. """ # Add this message to set of ignored messages for raw events, these trigger even if # the message was cached, and to prevent double logging, we need to ignore them self._handled_cached.add((after.guild.id, after.id)) if self.is_ignored(message=after, event=Event.message_edit): return response = (f"**Author:** {after.author.mention}\n" f"**Channel:** {after.channel.mention}") if before.edited_at: response += f"\n**Last edited:** {time_elapsed(before.edited_at, after.edited_at, max_units=3)}" else: response += f"\n**Initially created:** {time_elapsed(before.created_at, after.edited_at, max_units=3)}" # Limit log embed to avoid huge messages cluttering the log, # if message is longer, upload it instead. if len(before.clean_content + after.clean_content) > MAX_LOGGED_MESSAGE_SIZE: # NOTE; Text uploading might be happening too often, and might have to be removed # in the future payload = [ { "name": "before", "content": { "format": "text", "value": before.content } }, { "name": "after", "content": { "format": "text", "value": after.content } }, ] url = await upload_files( self.bot.http_session, payload, paste_name="Automatic message upload.", paste_description= "This paste was automatically generated from edited discord message." ) if url: response += f"\n**Changes:** Message too long, check [message upload]({url})" else: response += "\n**Changes:** Message too long (WARNING: Automatic upload failed)" else: response += f"\n**Before:** {before.content}" response += f"\n**After:** {after.content}" response += f"\n[Jump link]({after.jump_url})" embed = Embed(title="Message edited", description=response, color=Color.dark_orange()) embed.timestamp = after.edited_at embed.set_footer(text=f"Message ID: {after.id}") await self.send_log(after.guild, embed=embed)
async def fetch_track(self): """Task for fetching warzone matches of tracked users.""" logging.info("Starting warzone tracking...") tracked = session.query(wz_model).filter_by(track=True).all() if not len(tracked): return embed_color = [ Color.gold(), Color.light_grey(), Color.dark_orange(), Color.dark_red() ] matches = dict() async with aiohttp.ClientSession() as aiosession: try: await aiosession.get("https://profile.callofduty.com/cod/login" ) cookies = aiosession.cookie_jar.filter_cookies( "https://callofduty.com") params = { "username": getenv("COD_USERNAME"), "password": getenv("COD_PASSWORD"), "remember_me": "true", "_csrf": cookies["XSRF-TOKEN"].value, } await aiosession.post( "https://profile.callofduty.com/do_login?new_SiteId=cod", params=params, allow_redirects=False) except Exception as e: logging.error("Could not acquire session for warzone tracking") return for t in tracked: try: battletag = requests.utils.quote(t.battletag) url = f"https://my.callofduty.com/api/papi-client/crm/cod/v2/title/mw/platform/battle/gamer/{battletag}/matches/wz/start/0/end/0/details?limit=1" async with aiosession.get(url) as resp: response = await resp.json() player_match = response["data"]["matches"][0] player_match["channel_id"] = t.channel_id now = int(datetime.utcnow().strftime("%s")) if t.last_match == player_match[ "matchID"] or now - player_match[ "utcEndSeconds"] > 30 * 60: continue matches.setdefault(player_match["matchID"], dict()).setdefault( player_match["player"]["team"], list()).append(player_match) t.last_match = player_match["matchID"] except Exception as e: logging.error( f"Could not retrieve warzone history for {t.battletag}", exc_info=e) for match in matches.values(): for team in match.values(): p0 = team[0] channel = self.bot.get_channel(p0["channel_id"]) ordinal = lambda n: f'{n}{"tsnrhtdd"[(n//10%10!=1)*(n%10<4)*n%10::4]}' placement = int(p0["playerStats"]["teamPlacement"]) placement_color = placement - 1 if placement < 4 else -1 embed = Embed( title= f"{p0['player']['username']}'s team finished in __{ordinal(placement)}__ against {p0['teamCount']} teams.", color=embed_color[placement_color], timestamp=datetime.fromtimestamp(p0["utcStartSeconds"]), ) embed.add_field( name="Match duration", value=f"{int(p0['duration'] // 60000)} minutes", inline=True) embed.add_field( name="Team survived", value= f"{int(p0['playerStats']['teamSurvivalTime']) // 60000} minutes", inline=True, ) embed.add_field(name=chr(173), value=chr(173), inline=True) # field skip for player in team: stats = f"""**KDA:** {round(player["playerStats"]["kdRatio"], 2)} **Kills:** {int(player["playerStats"]["kills"])} **Deaths:** {int(player["playerStats"]["deaths"])} **Damage:** {int(player["playerStats"]["damageDone"])} """ embed.add_field(name=player["player"]["username"], value=inspect.cleandoc(stats), inline=True) await channel.send(embed=embed) session.commit() logging.info("Warzone tracking finished.")