Beispiel #1
0
    async def lastonline(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        if len(args) == 0: args = check_account(user_id)(args)

        if len(args) != 1:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(
                    ctx, ctx.message).parameters(f"{ctx.invoked_with} [user]"))
            return

        player = get_player(user_id, args[0])

        try:
            urls = [Urls().get_races(player, universe, 1)]
            response = (await fetch(urls, 'json', lambda x: x[0]['t']))[0]
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    f"[**{player}**](https://data.typeracer.com/pit/race_history?user={player}&universe={universe}) "
                    "doesn't exist or has no races in the "
                    f"{href_universe(universe)} universe")))
            return

        time_difference = time.time() - response

        await ctx.send(embed=discord.Embed(
            colour=discord.Colour(MAIN_COLOR),
            description=(
                f"**{player}** last played {seconds_to_text(time_difference)}\n"
                f"ago on the {href_universe(universe)} universe")))
        return
Beispiel #2
0
    async def adjustedgraph(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        ag = ctx.invoked_with.lower() in ['adjustedgraph'
                                          ] + get_aliases('adjustedgraph')
        mg = ctx.invoked_with.lower() in ['matchgraph'
                                          ] + get_aliases('matchgraph')

        if len(args) == 0 or (len(args) == 1 and args[0][0] == '-'):
            args = check_account(user_id)(args)

        if len(args) > 2 or len(args) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] [race_num]` or `{ctx.invoked_with} [url]"
                ))
            return

        race_num = 0
        if len(args) == 2 and args[1][0] == '-':
            try:
                race_num = int(args[1])
                args = (args[0], )
            except ValueError:
                pass

        if len(args) == 1:
            try:
                args[0].index('result?')
                replay_url = args[0]
                urls = [replay_url]
            except ValueError:
                try:
                    player = get_player(user_id, args[0])
                    urls = [Urls().get_races(player, universe, 1)]
                    race_api_response = await fetch(urls, 'json')
                    last_race = race_api_response[0][0]['gn']
                    if race_num < 0: last_race += race_num
                    race_api_response = race_api_response[0][0]
                    replay_url = Urls().result(player, last_race, universe)
                    urls = [replay_url]
                except:
                    await ctx.send(
                        content=f"<@{user_id}>",
                        embed=Error(ctx, ctx.message).missing_information((
                            f"[**{player}**](https://data.typeracer.com/pit/race_history?user={player}&universe={universe}) "
                            "doesn't exist or has no races in the "
                            f"{href_universe(universe)} universe")))
                    return

        elif len(args) == 2:
            try:
                player = get_player(user_id, args[0])
                replay_url = Urls().result(player, int(args[1]), universe)
                urls = [replay_url]
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`race_num` must be a positive integer'))
                return

        def helper_scraper(soup):
            escapes = ''.join([chr(char) for char in range(1, 32)])
            try:
                typinglog = re.sub(
                    '\\t\d', 'a',
                    re.search(
                        r'typingLog\s=\s"(.*?)";',
                        response).group(1).encode().decode(
                            'unicode-escape').translate(escapes)).split('|')
                return [int(c) for c in re.findall(r"\d+", typinglog[0])][2:]
            except:
                return None

        try:
            response = (await fetch(urls, 'text'))[0]
            if not response:
                raise KeyError
            soup = BeautifulSoup(response, 'html.parser')
            times = helper_scraper(soup)

            race_text = soup.select("div[class='fullTextStr']")[0].text.strip()
            player = soup.select(
                "a[class='userProfileTextLink']")[0]["href"][13:]
            race_details = soup.select("table[class='raceDetails']")[0].select(
                'tr')
            universe = 'play'
            opponents = []
            for detail in race_details:
                cells = detail.select('td')
                category = cells[0].text.strip()
                if category == 'Race Number':
                    race_number = int(cells[1].text.strip())
                elif category == 'Universe':
                    universe = cells[1].text.strip()
                elif category == 'Opponents':
                    opponents = [i['href'] for i in cells[1].select('a')]
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    '`var typingLog` was not found in the requested URL;\n'
                    f"Currently linked to the {href_universe(universe)} universe\n\n"
                )))
            return

        if universe == 'lang_ko':
            mult = 24000
        elif universe == 'lang_zh' or universe == 'new_lang_zh-tw' or universe == 'lang_zh-tw' or universe == 'lang_ja':
            mult = 60000
        else:
            mult = 12000

        def wpm_helper(times):
            temp, total_time = [], 0
            for i, time_ in enumerate(times):
                total_time += time_
                try:
                    temp.append((i + 1) * mult / total_time)
                except ZeroDivisionError:
                    pass
            return temp

        if ag:
            times.pop(0)
            data_y = wpm_helper(times)
        else:
            unl = wpm_helper(times)
            data = {
                player: [
                    unl, unl[-1], times[0],
                    replay_url.split('https://data.typeracer.com/pit/')[1]
                ]
            }
            for opponent in opponents:
                try:
                    urls = ["https://data.typeracer.com/pit/" + opponent]
                    response = (await fetch(urls, 'text'))[0]
                    if not response:
                        raise KeyError
                    soup = BeautifulSoup(response, 'html.parser')
                    times = helper_scraper(soup)
                    unl = wpm_helper(times)
                    data.update({
                        opponent.split('|')[1][3:]:
                        [unl, unl[-1], times[0], opponent]
                    })
                except:
                    pass
            data = {
                k: v
                for k, v in sorted(
                    data.items(), key=lambda x: x[1][1], reverse=True)
            }

        if ag:
            title_1 = f"Adjusted WPM Over {player}'s {num_to_text(race_number)} Race"
            title = f"{title_1}\nUniverse: {universe}"
        else:
            title_1 = f"Unlagged WPM Over {player}'s {num_to_text(race_number)} Race"
            title = f"{title_1}\nUniverse: {universe}"

        description = f"**Quote**\n\"{race_text[0:1008]}\""

        text_length = len(race_text) > 9

        ax = plt.subplots()[1]
        if ag:
            if text_length:
                starts = data_y[0:9]
                remaining = data_y[9:]
            ax.plot([i for i in range(1, len(data_y) + 1)], data_y)
        else:
            value, i, starts, remaining = '', 0, [], []
            for name, data_y in data.items():
                wpm_ = data_y[0]
                if text_length:
                    starts += wpm_[0:9]
                    remaining += wpm_[9:]
                ax.plot([i for i in range(1, len(wpm_) + 1)], wpm_, label=name)
                segment = (
                    f"{NUMBERS[i]} [{name}]({f'https://data.typeracer.com/pit/{data_y[3]}'})"
                    f" - {round(data_y[1], 2)} WPM ({f'{data_y[2]:,}'}ms start)\n"
                )
                if len(value + segment) <= 1024:
                    value += segment
                i += 1
            if len(data) > 1:
                plt.tight_layout(rect=[0.02, 0.02, 0.75, 0.92])
                ax.legend(loc='upper left',
                          bbox_to_anchor=(1.03, 1),
                          shadow=True,
                          ncol=1)

        ax.set_title(title)
        ax.set_xlabel('Keystrokes')
        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = 'WPM Over Race.png'

        embed = discord.Embed(title=title_1,
                              color=discord.Color(MAIN_COLOR),
                              description=description,
                              url=replay_url)
        if text_length:
            max_starts, max_remaining = max(starts), max(remaining)
            messed_up_scaled = max_starts > max_remaining
            if messed_up_scaled:
                if ctx.invoked_with[-1] != '*':
                    ax.set_ylim(0, 1.05 * max_remaining)
                    embed.set_footer(
                        text=
                        f"The `y`-axis has been scaled; run `{ctx.invoked_with}*` to see the entire graph"
                    )

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        plt.close()

        file_ = discord.File(file_name, filename='image.png')
        embed.set_image(url='attachment://image.png')
        if mg:
            embed.add_field(name='Ranks (ranked by unlagged WPM)',
                            value=value[:-1])
        os.remove(file_name)

        await ctx.send(file=file_, embed=embed)
        return
Beispiel #3
0
    async def timebetween(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        url_param = False
        if len(args) < 2 or len(args) > 3:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    (f"{ctx.invoked_with} [url] [url]` or "
                     f"`{ctx.invoked_with} [user] [race_one] [race_two]")))
            return
        if len(args) == 2:
            try:
                args[0].index('result?') and args[1].index('result?')
                url_param = True
            except ValueError:
                args = check_account(user_id)(args)
        if len(args) == 3:
            player = get_player(user_id, args[0])
            try:
                race_one = int(args[1])
                race_two = int(args[2])
                if race_one <= 0 or race_two <= 1:
                    raise ValueError
                args = (race_one, race_two)
            except ValueError:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        f"Refer to `help {ctx.invoked_with}` for correct parameter formats"
                    ))
                return

        urls = []
        if url_param: urls = [url for url in args]
        else:
            urls = [
                Urls().result(player, race_num, universe) for race_num in args
            ]

        responses = await fetch(urls, 'text', timestamp_scraper)
        try:
            conn = sqlite3.connect(DATABASE_PATH)
            c = conn.cursor()
            player = responses[0]['player']
            difference = abs(responses[1]['timestamp'] -
                             responses[0]['timestamp'])
            universe = responses[0]['universe']
            race_nums = [response['race_number'] for response in responses]
            race_one = min(race_nums)
            race_two = max(race_nums)
        except TypeError:
            try:
                if escape_sequence(player):
                    raise sqlite3.OperationalError
                difference = abs(c.execute(f"SELECT t FROM t_{player} WHERE gn = ?", (race_one,))
                                           .fetchone()[0] -\
                                 c.execute(f"SELECT t FROM t_{player} WHERE gn = ?", (race_two,))
                                           .fetchone()[0])
            except:
                conn.close()
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).missing_information(
                        '`timestamp` was not found in either race'))
                return
        conn.close()

        description = (f"The time between **{player}**'s "
                       f"**{num_to_text(race_one)}** and "
                       f"**{num_to_text(race_two)}** race in the\n"
                       f"{href_universe(universe)} universe was "
                       f"**{seconds_to_text(difference)}**")

        await ctx.send(embed=discord.Embed(color=discord.Color(MAIN_COLOR),
                                           description=description))
        return
Beispiel #4
0
    async def competition(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        categories = {
            'races': 'gamesFinished',
            'points': 'points',
            'wpm': 'wpm'
        }

        try:
            if len(args) == 0:
                sort, category = 'day', 'points'
            elif len(args) == 1:
                param = args[0].lower()
                if param in categories.keys():
                    sort, category = 'day', categories[param]
                elif param in ['day', 'week', 'month', 'year']:
                    sort, category = param, 'points'
                else:
                    raise KeyError
            elif len(args) == 2:
                sort, category = args[0].lower(), categories[args[1].lower()]
            else:
                raise ValueError
        except KeyError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'Must provide a valid category: `races/points/wpm`'))
            return
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [races/points/wpm]"))
            return

        urls = [Urls().get_competition(12, sort, category, universe)]
        competition = await fetch(urls, 'json')
        competition = competition[0]

        def helper_formatter(player, country, points, races, wpm_sum, index):
            formatted = NUMBERS[index]
            if country:
                formatted += f":flag_{country}: "
            else:
                formatted += "<:flagblank:744520567113252926> "
            formatted += f"{player} - {f'{points:,}'} | {f'{races:,}'} | {f'{round(wpm_sum / races, 2):,}'} WPM\n"
            return formatted

        players = []
        if sort == 'day':
            conn = sqlite3.connect(TEMPORARY_DATABASE_PATH)
            c = conn.cursor()
            for competitor in competition:
                player = competitor[1]['typeracerUid'][3:]
                if competitor[0]['country']:
                    country = competitor[0]['country']
                else:
                    country = ''

                today_timestamp = (datetime.datetime.utcnow().date() \
                                - datetime.date(1970, 1, 1)).total_seconds()
                file_name = f"t_{player}_{universe}_{today_timestamp}_{today_timestamp + 86400}".replace(
                    '.', '_')

                try:
                    user_data = c.execute(
                        f"SELECT * FROM {file_name} ORDER BY gn DESC LIMIT 1")
                    last_race = user_data.fetchone()
                    time_stamp = last_race[1] + 0.01
                except sqlite3.OperationalError:
                    time_stamp = today_timestamp
                    c.execute(
                        f"CREATE TABLE {file_name} (gn integer PRIMARY KEY, t, tid, wpm, pts)"
                    )

                races = await fetch_data(player, universe, time_stamp,
                                         today_timestamp + 86400)
                if races:
                    c.executemany(
                        f"INSERT INTO {file_name} VALUES (?, ?, ?, ?, ?)",
                        races)
                    conn.commit()
                points, wpm = [], []
                data = c.execute(f"SELECT * FROM {file_name}").fetchall()
                for row in data:
                    points.append(row[4])
                    wpm.append(row[3])
                players.append([
                    player, country,
                    round(sum(points)),
                    len(points),
                    round(sum(wpm), 2)
                ])

            conn.close()
        else:
            for competitor in competition:
                player = competitor[1]['typeracerUid'][3:]
                if competitor[0]['country']:
                    country = competitor[0]['country']
                else:
                    country = ''
                comp_stats = competitor[1]
                players.append([
                    player, country,
                    round(comp_stats['points']),
                    round(comp_stats['gamesFinished']),
                    comp_stats['gamesFinished'] * comp_stats['wpm']
                ])

        if category == 'points':
            players = sorted(players, key=lambda x: x[2])[-10:][::-1]
        elif category == 'gamesFinished':
            players = sorted(players, key=lambda x: x[3])[-10:][::-1]
        elif category == 'wpm':
            players = sorted(players, key=lambda x: x[4] / x[3])[-10:][::-1]

        value = ''
        for i in range(0, 10):
            player = players[i]
            value += helper_formatter(player[0], player[1], player[2],
                                      player[3], player[4], i)

        today = datetime.datetime.utcnow().date()

        if sort == 'day': date = today.strftime('%B %-d, %Y')
        elif sort == 'week':
            normalizer = today.isocalendar()[2]
            start_time = today - datetime.timedelta(days=normalizer - 1)
            end_time = today + datetime.timedelta(days=7 - normalizer)
            date = f"{start_time.strftime('%B %-d')}—{end_time.strftime('%-d, %Y')}"
        elif sort == 'month':
            date = today.strftime('%B %Y')
        elif sort == 'year':
            date = today.strftime('%Y')

        formatted_sort = {
            'day': 'Daily',
            'week': 'Weekly',
            'month': 'Monthly',
            'year': 'Yearly'
        }[sort]

        formatted_category = {
            'points': 'points',
            'gamesFinished': 'races',
            'wpm': 'wpm'
        }[category]

        embed = discord.Embed(
            title=f"{formatted_sort} Competition ({formatted_category})",
            color=discord.Color(MAIN_COLOR),
            description=f"**Universe:** {href_universe(universe)}",
            url=Urls().competition(sort, category, '', universe))
        embed.add_field(name=date, value=value)
        await ctx.send(embed=embed)
        return
Beispiel #5
0
    async def stats(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        if len(args) == 0: args = check_account(user_id)(args)

        if len(args) != 1:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(
                    ctx, ctx.message).parameters(f"{ctx.invoked_with} [user]"))
            return

        player = get_player(user_id, args[0])
        urls = [Urls().get_user(player, universe)]
        try:
            user_api = (await fetch(urls, 'json'))[0]
        except:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               (f"[**{player}**]({urls[0]}) "
                                "doesn't exist")))
            return

        country = f":flag_{user_api['country']}: " if user_api[
            'country'] else ''
        name = user_api['name'] if user_api['name'] else ''
        name += ' ' if user_api['name'] and user_api['lastName'] else ''
        name += user_api['lastName'] if user_api['lastName'] else ''
        premium = 'Premium' if user_api['premium'] else 'Basic'
        try:
            banned = user_api['tstats']['disqualified']
            banned = '' if banned == 'false' or not banned else '\n**Status:** Banned'
        except KeyError:
            banned = ''

        urls = [Urls().trd_user(player, universe)]
        try:
            if universe != 'play':
                raise NotImplementedError
            trd_user_api = (await fetch(urls, 'json'))[0]
            textbests = round(float(trd_user_api['account']['wpm_textbests']),
                              2)
            textsraced = trd_user_api['account']['texts_raced']
            extra_stats = (f"**Text Bests: **{textbests} WPM\n"
                           f"**Texts Typed: **{textsraced}\n")
        except:
            textbests, textsraced, extra_stats = ('', ) * 3

        urls = [Urls().user(player, universe)]
        try:
            response = (await fetch(urls, 'text'))[0]
            soup = BeautifulSoup(response, 'html.parser')
            rows = soup.select("table[class='profileDetailsTable']")[0].select(
                'tr')
            medal_count = 0
            for row in rows:
                cells = row.select('td')
                if len(cells) < 2: continue
                if cells[0].text.strip() == 'Racing Since':
                    date_joined = cells[1].text.strip()

            rows = soup.select("table[class='personalInfoTable']")[0].select(
                'tr')
            for row in rows:
                cells = row.select('td')
                if len(cells) < 2: continue
                if cells[0].text.strip() == 'Awards':
                    medal_count = len(cells[1].select('a'))
        except:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               (f"[**{player}**]({urls[0]}) "
                                "doesn't exist")))
            return

        if banned:
            color = 0xe0001a
        else:
            color = MAIN_COLOR

        embed = discord.Embed(
            title=f"{country}{player}",
            colour=discord.Colour(color),
            description=f"**Universe:** {href_universe(universe)}",
            url=urls[0])
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.add_field(name="General",
                        value=(f"**Name:** {name}\n"
                               f"**Joined: **{date_joined}\n"
                               f"**Membership: **{premium}{banned}"),
                        inline=False)
        embed.add_field(
            name="Stats",
            value=
            (f"""**Races: **{f"{user_api['tstats']['cg']:,}"}\n"""
             f"""**Races Won: **{f"{user_api['tstats']['gamesWon']:,}"}\n"""
             f"""**Points: **{f"{round(user_api['tstats']['points']):,}"}\n"""
             f"""**Full Average: **{round(user_api['tstats']['wpm'], 2)} WPM\n"""
             f"""**Fastest Race: **{round(user_api['tstats']['bestGameWpm'], 2)} WPM\n"""
             f"""**Captcha Speed: **{round(user_api['tstats']['certWpm'], 2)} WPM\n"""
             f"""{extra_stats}**Medals: **{f'{medal_count:,}'}\n"""),
            inline=False)

        await ctx.send(embed=embed)
        await fetch([Urls().trd_import(player)], 'text')
        return
Beispiel #6
0
    async def realspeed(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        desslejusted, universe = account['desslejusted'], account['universe']
        race_api_response = None
        replay_url = ''

        rs = ctx.invoked_with.lower() in ['realspeed'
                                          ] + get_aliases('realspeed')
        lr = ctx.invoked_with.lower() in ['lastrace'] + get_aliases('lastrace')
        raw = ctx.invoked_with.lower() in ['raw'] + get_aliases('raw')

        if len(args) == 0 or (len(args) == 1 and args[0][0] == '-'):
            args = check_account(user_id)(args)

        if len(args) > 2 or len(args) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] [race_num]` or `{ctx.invoked_with} [url]"
                ))
            return

        race_num = 0
        if len(args) == 2 and args[1][0] == '-':
            try:
                race_num = int(args[1])
                args = (args[0], )
            except ValueError:
                pass

        players = []
        if len(args) == 1:
            try:
                args[0].index('result?')
                replay_url = args[0]
                urls = [replay_url]
            except ValueError:
                try:
                    player = get_player(user_id, args[0])
                    urls = [Urls().get_races(player, universe, 1)]
                    race_api_response = await fetch(urls, 'json')
                    last_race = race_api_response[0][0]['gn']
                    if race_num < 0:
                        last_race += race_num
                        race_api_response = None
                    else:
                        race_api_response = race_api_response[0][0]
                    replay_url = Urls().result(player, last_race, universe)
                    urls = [replay_url]
                except:
                    await ctx.send(
                        content=f"<@{user_id}>",
                        embed=Error(ctx, ctx.message).missing_information((
                            f"[**{player}**](https://data.typeracer.com/pit/race_history?user={player}&universe={universe}) "
                            "doesn't exist or has no races in the "
                            f"{href_universe(universe)} universe")))
                    return
        elif len(args) == 2:
            try:
                player = get_player(user_id, args[0])
                replay_url = Urls().result(player, int(args[1]), universe)
                urls = [replay_url]
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`race_num` must be a positive integer'))
                return
        try:
            if raw:
                responses = await fetch(urls, 'text', raw_typinglog_scraper)
            else:
                if not lr: urls, responses = self.check_cache(urls)
                if urls:
                    responses = await fetch(urls, 'text', rs_typinglog_scraper,
                                            True)
                    self.update_cache(responses)
                    responses = [
                        list(response.values())[0] for response in responses
                    ]
            result = responses[0]
            if not result:
                raise KeyError
            if not race_api_response:
                timestamp = result['timestamp']
                player = result['player']
                universe = result['universe']
                race_api_response = await find_registered(
                    player, universe, result['race_number'], timestamp)
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    '`var typingLog` was not found in the requested URL;\n'
                    f"Currently linked to the {href_universe(universe)} universe\n\n"
                )))
            return

        lagged = race_api_response['wpm']
        try:
            realspeeds = compute_realspeed(result['length'],
                                           result['duration'], result['start'],
                                           lagged, desslejusted, universe)
        except ZeroDivisionError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               ('∞ adjusted WPM')))

        race_number, color = result['race_number'], MAIN_COLOR
        title = f"Real Speeds for {player}'s {num_to_text(race_number)} Race"
        description = f"**Universe:** {href_universe(universe)}\n"

        if rs or raw:
            start, unlagged, adjusted, ping, desslejusted_wpm = tuple(
                realspeeds.values())
            if ping <= 0:
                color = 0xe0001a
                description += f"{TR_WARNING} This score is reverse lagged {TR_WARNING}"
            if raw:
                start, unlagged, adjusted, ping, desslejusted_wpm = tuple(
                    realspeeds.values())
                correction, adj_correction, length = result[
                    'correction'], result['adj_correction'], result['duration']
                raw_unlagged = (length * unlagged) / (length - correction)
                raw_adjusted = ((length - start) *
                                adjusted) / (length - start - adj_correction)
        elif lr:
            players.append([player, urls[0]] + list((realspeeds.values())))
            for opponent in result['opponents']:
                urls = ["https://data.typeracer.com/pit/" + opponent]
                opponent_data = await fetch(urls, 'text', rs_typinglog_scraper)
                result_ = opponent_data[0]
                timestamp_ = result_['timestamp']
                player_ = result_['player']
                opponent_api_response = await find_registered(
                    player_, universe, result_['race_number'], timestamp_)
                lagged_ = opponent_api_response['wpm']
                try:
                    realspeeds = compute_realspeed(result_['length'],
                                                   result_['duration'],
                                                   result_['start'], lagged_,
                                                   False, universe)
                    players.append([player_, urls[0]] +
                                   list((realspeeds.values())))
                except ZeroDivisionError:
                    pass

        embed = discord.Embed(title=title,
                              colour=discord.Colour(color),
                              url=replay_url,
                              description=description)
        embed.set_thumbnail(
            url=f"https://data.typeracer.com/misc/pic?uid=tr:{player}")
        embed.set_footer(
            text=
            "Adjusted speed is calculated by removing the start time from the race"
        )
        value = f"\"{result['race_text']}\""

        if len(value) > 1023:
            value = value[0:1020] + "…\""
        embed.add_field(
            name=f"Quote (Race Text ID: {race_api_response['tid']})",
            value=value,
            inline=False)

        cache_id(ctx.message.channel.id, race_api_response['tid'])

        if rs or raw:
            real_speeds = (f"**Lagged:** {f'{lagged:,}'} WPM "
                           f"({f'{round(unlagged - lagged, 2):,}'} WPM lag)\n"
                           f"**Unlagged:** {f'{unlagged:,}'} WPM"
                           f" ({f'{round(ping):,}'}ms ping)\n"
                           f"**Adjusted:** {f'{adjusted:,}'} WPM"
                           f" ({f'{start:,}'}ms start)")
            if desslejusted:
                real_speeds += f"\n**Desslejusted:** {f'{desslejusted_wpm:,}'} WPM"
            if raw:
                real_speeds += (
                    f"\n**Raw Unlagged:** {f'{round(raw_unlagged, 2):,}'} WPM "
                    f"({f'{correction:,}'}ms correction time, {round(100 * correction / length, 2)}%)"
                    f"\n**Raw Adjusted:** {f'{round(raw_adjusted, 3):,}'} WPM")
            embed.add_field(name="Speeds", value=real_speeds, inline=False)
        elif lr:
            value = ''
            players = sorted(players, key=lambda x: x[3], reverse=True)
            for i, player in enumerate(players):
                segment = (f"{NUMBERS[i]} "
                           f"[{player[0]}]({player[1]}) - "
                           f"{player[3]} unlagged WPM / "
                           f"{player[4]} adjusted WPM\n")
                if len(value + segment) > 1024: break
                value += segment
            value = value[:-1]
            embed.add_field(name='Ranks (ranked by unlagged WPM)',
                            value=value,
                            inline=False)

        await ctx.send(embed=embed)
        return
Beispiel #7
0
    async def realspeedaverage(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        desslejusted, universe = account['desslejusted'], account['universe']
        race_api_response = None
        replay_url = ''
        redact = not ctx.invoked_with[-1] == '*'
        rawsa = 'raw' in ctx.invoked_with

        if len(args) == 0 or (len(args) == 1 and len(args[0]) < 4):
            args = check_account(user_id)(args)

        if len(args) > 3 or len(args) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] <first_race> <last_race>"))
            return

        try:
            player = get_player(user_id, args[0])
            urls = [Urls().get_races(player, universe, 1)]
            race_api_response = (await fetch(urls, 'json'))[0][0]
            last_race = int(race_api_response['gn'])
        except:
            await ctx.send(content = f"<@{user_id}>",
                           embed = Error(ctx, ctx.message)
                                   .missing_information((f"[**{player}**](https://data.typeracer.com/pit/race_history?user={player}&universe={universe}) "
                                                         "doesn't exist or has no races in the " \
                                                         f"{href_universe(universe)} universe")))
            return

        invalid = False
        if len(args) == 1:
            if last_race < 10:
                first_race = 1
            else:
                first_race = last_race - 9
        elif len(args) == 2:
            try:
                num = int(args[1])
                first_race = last_race - num + 1
            except ValueError:
                invalid = True
        elif len(args) == 3:
            try:
                first_race, last_race = int(args[1]), int(args[2])
            except ValueError:
                invalid = True
        race_interval = last_race - first_race
        if first_race <= 0 or last_race <= 0 or race_interval <= 0:
            invalid = True
        if invalid:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'The number of races must be a positive integer'))
            return
        if race_interval >= 10 and not user_id in BOT_ADMIN_IDS:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).lacking_permissions(
                               'You may only request up to 10 races'))
            return
        elif race_interval >= 10 and not user_id in BOT_OWNER_IDS:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).lacking_permissions(
                               'You may only request up to 10 races'))
            return
        elif race_interval >= 500:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).lacking_permissions(
                               'You may only request up to 500 races'))
            return

        urls = []
        for i in range(first_race, last_race + 1):
            replay_url = Urls().result(player, i, universe)
            urls.append(replay_url)

        responses = []
        urls, responses = self.check_cache(urls, True)
        if urls:
            new_responses = await fetch(urls, 'text', raw_typinglog_scraper,
                                        True)
            self.update_cache(new_responses)
            new_responses = [
                list(response.values())[0] for response in new_responses
            ]
            responses += new_responses
        responses = [i for i in responses if i]
        if len(responses) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    '`var typingLog` was not found in any of the races;\n'
                    f"Currently linked to the {href_universe(universe)} universe\n\n"
                )))
            return

        responses = sorted(responses,
                           key=lambda x: int(x['race_number']),
                           reverse=True)

        urls = [
            Urls().get_races(player, universe, responses[-1]['timestamp'] - 1,
                             responses[0]['timestamp'] + 1)
        ]
        race_api_responses = (await fetch(urls, 'json'))[0]
        race_api_responses = sorted(race_api_responses,
                                    key=lambda x: x['gn'],
                                    reverse=True)
        computed_responses = []
        j = 0
        lagged_sum, unlagged_sum, adjusted_sum, ping_sum = (0, ) * 4
        start_sum, desslejusted_sum, lag_sum, reverse_lag_count = (0, ) * 4
        raw_unlagged_sum, raw_adjusted_sum, correction_time_sum, correction_percentage_sum = (
            0, ) * 4
        for i in range(0, len(race_api_responses)):
            response, race_api_response = responses[j], race_api_responses[i]
            if response['race_number'] == race_api_response['gn']:
                j += 1
                try:
                    realspeeds = compute_realspeed(response['length'],
                                                   response['duration'],
                                                   response['start'],
                                                   race_api_response['wpm'],
                                                   desslejusted, universe)
                    computed_responses.append({
                        'url':
                        Urls().result(player, response['race_number'],
                                      universe),
                        'race_number':
                        response['race_number'],
                        'lagged':
                        race_api_response['wpm'],
                        'unlagged':
                        realspeeds['unlagged'],
                        'adjusted':
                        realspeeds['adjusted'],
                        'ping':
                        realspeeds['ping'],
                        'start':
                        realspeeds['start'],
                        'desslejusted':
                        realspeeds['desslejusted']
                    })
                    lagged_sum += race_api_response['wpm']
                    unlagged_sum += realspeeds['unlagged']
                    adjusted_sum += realspeeds['adjusted']
                    ping_sum += realspeeds['ping']
                    start_sum += realspeeds['start']
                    lag_sum += realspeeds['unlagged'] - race_api_response['wpm']
                    if realspeeds['ping'] <= 0:
                        reverse_lag_count += 1
                    if desslejusted:
                        desslejusted_sum += realspeeds['desslejusted']
                    if rawsa:
                        correction, adj_correction, length = response[
                            'correction'], response[
                                'adj_correction'], response['duration']
                        raw_unlagged = (length * realspeeds['unlagged']) / (
                            length - correction)
                        raw_adjusted = (
                            (length - realspeeds['start']) *
                            realspeeds['adjusted']) / (
                                length - realspeeds['start'] - adj_correction)
                        computed_responses[-1].update({
                            'raw_unlagged':
                            raw_unlagged,
                            'raw_adjusted':
                            raw_adjusted,
                            'correction_time':
                            correction,
                            'correction_percentage':
                            round(100 * correction / length, 2)
                        })
                        raw_unlagged_sum += raw_unlagged
                        raw_adjusted_sum += raw_adjusted
                        correction_time_sum += correction
                        correction_percentage_sum += 100 * correction / length

                except ZeroDivisionError:
                    continue
            else:
                continue

        description = f"**Universe:** {href_universe(universe)}\n\n"
        if reverse_lag_count:
            color = 0xe0001a
            description += (
                f"{TR_WARNING} This interval contains "
                f"{reverse_lag_count} reverse lagged score(s) {TR_WARNING}\n")
        else:
            color = MAIN_COLOR

        race_count = len(computed_responses)

        title = f"""Real Speed Average for {player} (Races {f"{responses[-1]['race_number']:,}"} to {f"{responses[0]['race_number']:,}"})"""
        real_speeds = f"**Lagged Average:** {f'{round(lagged_sum / race_count, 2):,}'} WPM\n"
        delays = (
            f"**Average Lag:** {f'{round(lag_sum / race_count, 2):,}'} WPM\n"
            f"**Average Ping:** {f'{round(ping_sum / race_count, 3):,}'}ms\n"
            f"**Average Start:** {f'{round(start_sum / race_count, 3):,}'}ms")
        real_speeds += f"**Unlagged Average:** {f'{round(unlagged_sum / race_count, 2):,}'} WPM\n"
        real_speeds += f"**Adjusted Average:** {f'{round(adjusted_sum / race_count, 3):,}'} WPM"
        if desslejusted:
            real_speeds += f"\n**Desslejusted Average:** {f'{round(desslejusted_sum / race_count, 3):,}'} WPM"
        if rawsa:
            real_speeds += f"\n**Raw Unlagged Average:** {f'{round(raw_unlagged_sum / race_count, 3):,}'} WPM"
            real_speeds += f"\n**Raw Adjusted Average:** {f'{round(raw_adjusted_sum / race_count, 3):,}'} WPM"
            delays += f"\n**Correction Time:** {f'{round(correction_time_sum / race_count):,}'}ms"
            delays += f" ({round(correction_percentage_sum  / race_count, 2)}%)"

        if race_count >= 20 or redact:
            delays = f"\n{delays}"
            if not redact:
                csv_data = [list(computed_responses[0].keys())[1:]]
                csv_data += [
                    list(computed_response.values())[1:]
                    for computed_response in computed_responses
                ]
                filename = f"{player}_real_speed_average_{responses[-1]['race_number']}_to_{responses[0]['race_number']}.csv"
                with open(filename, 'w') as csvfile:
                    writer = csv.writer(csvfile)
                    writer.writerows(csv_data)
                file_ = discord.File(filename, filename)
            if description:
                embed = discord.Embed(title=title,
                                      color=discord.Color(color),
                                      description=description)
            else:
                embed = discord.Embed(title=title, color=discord.Color(color))
            embed.set_thumbnail(
                url=f"https://data.typeracer.com/misc/pic?uid=tr:{player}")
            embed.set_footer(
                text=
                "(Adjusted speed is calculated by removing the start time from the race)"
            )
            embed.add_field(name="Speed", value=real_speeds, inline=False)
            embed.add_field(name="Delays", value=delays, inline=False)
            if not redact:
                await ctx.send(file=file_,
                               content=f"<@{user_id}>",
                               embed=embed)
                os.remove(filename)
            else:
                await ctx.send(content=f"<@{user_id}>", embed=embed)
            return

        embed = discord.Embed(
            title=title,
            color=discord.Colour(color),
            description=
            f"{description}**Speed**\n{real_speeds}\n**Delays**\n{delays}")
        for computed_response in computed_responses:
            name = f"""Real Speeds for Race #{f"{computed_response['race_number']:,}"}"""
            if computed_response['ping'] < 0:
                name += f" {TR_WARNING} Reverse Lagged {TR_WARNING}"
            value = (
                f"""**Lagged Speed:** {f"{round(computed_response['lagged'], 2)}"} WPM """
                f"""({f"{round(computed_response['unlagged'] - computed_response['lagged'], 2):,}"} WPM lag) """
                f"""[:cinema:]({computed_response['url']})\n"""
                f"""**Unlagged Speed:** {f"{round(computed_response['unlagged'], 2):,}"} WPM """
                f"""({f"{round(computed_response['ping']):,}"}ms ping)\n"""
                f"""**Adjusted Speed:** {f"{round(computed_response['adjusted'], 3):,}"} WPM """
                f"""({f"{round(computed_response['start'], 3):,}"}ms start)""")
            if desslejusted:
                value += f"""\n**Desslejusted Speed:** {f"{round(computed_response['desslejusted'], 3):,}"} WPM"""
            embed.add_field(name=name, value=value, inline=False)
        await ctx.send(embed=embed)
        return