示例#1
0
    async def boxplot(self, ctx, *args):
        user_id = ctx.message.author.id

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

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

        args = list(args)
        for i, player in enumerate(args):
            args[i] = get_player(user_id, args[i])
            if escape_sequence(player):
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).missing_information(
                        (f"[**{player}**]({Urls().user(player, 'play')}) "
                         "doesn't exist")))
                return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        data = []
        try:
            title_text = ''
            for user in args:
                title_text += f"{user} vs. "
                user_data = c.execute(f"SELECT wpm FROM t_{user}")
                temp = [i[0] for i in user_data]
                data.append(temp)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()
        title_text = title_text[:-4]
        title_text += 'WPM'

        ax = plt.subplots()[1]
        ax.boxplot(data, showfliers=False)
        ax.set_xticklabels(list(args))
        ax.set_ylabel('WPM')
        ax.set_title(title_text)
        plt.grid(True)
        file_name = f"{title_text}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, True)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        wpm_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=wpm_picture)
        os.remove(file_name)
        plt.close()
        return
示例#2
0
    async def histogram(self, ctx, *args):
        user_id = ctx.message.author.id

        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])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(f"SELECT wpm FROM t_{player}")
            data = [i[0] for i in player_data.fetchall()]
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        ax = plt.subplots()[1]
        max_, min_ = max(data), min(data)

        if int(max_ - min_) // 10 == 0:
            patches = ax.hist(data, bins=1)[2]
        else:
            patches = ax.hist(data, bins=int(max_ - min_) // 10)[2]

        ax.set_xlabel('WPM')
        ax.set_ylabel('Frequency')
        plt.grid(True)
        ax.set_title(f"{player}'s WPM Histogram")
        file_name = f"{player} WPM.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False, patches)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        wpm_picture = discord.File(file_name, filename=file_name)
        await ctx.send(file=wpm_picture)
        os.remove(file_name)
        plt.close()
        return
示例#3
0
    async def droptable(self, ctx, *args):
        user_id = ctx.message.author.id

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

        player = args[0].lower()
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        await ctx.send(embed=discord.Embed(title='Type "YES" to confirm',
                                           color=discord.Color(0)),
                       delete_after=10)

        msg = await self.bot.wait_for('message',
                                      check=check(ctx.message.author),
                                      timeout=10)

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        try:
            c.execute(f"DROP TABLE t_{player}")
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               "The user's table does not exist"))
            return

        conn.close()

        await ctx.send(embed=discord.Embed(color=discord.Color(HELP_BLACK),
                                           title=f"{player} table dropped"))
示例#4
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
示例#5
0
    async def construct_embeds(self):
        self.last_updated = datetime.datetime.utcnow().strftime(
            '%B %-d, %Y, %X %p')
        self.country_tally = dict()
        self.user_tally = dict()

        faq_thumbnail = HELP_IMG
        speed_thumbnail, speed_color = 'https://i.imgur.com/bXAjl4C.png', 0x001359
        three_hundred_thumbnail, three_hundred_color = 'https://i.imgur.com/i8kXn3K.png', 0x1B038C
        races_thumbnail, races_color = 'https://i.imgur.com/DspJLUH.png', 0x14328C
        points_thumbnail, points_color = 'https://i.imgur.com/Xm0VNQV.png', 0x0B4A9E
        speedruns_thumbnail, speedruns_color = 'https://i.imgur.com/lPdQvvQ.png', 0x0E61D1
        awards_thumbnail, awards_color = 'https://i.imgur.com/W9NEYb2.png', 0x0790E8
        records_held_thumbnail, records_held_color = 'https://i.imgur.com/3gJNZRO.png', 0x00BFFF
        last_updated_color = 0x00EEFF

        faq_information = self.records_information['information']
        faq_embed = discord.Embed(**faq_information)
        for field in faq_information['fields']:
            faq_embed.add_field(**field)
        faq_embed.set_thumbnail(url=faq_thumbnail)
        faq_embed.set_footer(**faq_information['footer'])

        all_records_information = self.records_information['all_records']

        speed_information = all_records_information['speed']['records']
        speed_embed = discord.Embed(title='Speed Records',
                                    color=discord.Color(speed_color))
        for speed_record in speed_information:
            speed_embed.add_field(
                **self.record_field_constructor(speed_record, ' WPM'))
        speed_embed.set_thumbnail(url=speed_thumbnail)

        three_hundred_information = all_records_information['300_wpm_club'][
            'records']
        description, members_list = '', []

        for member, url in three_hundred_information.items():
            try:
                result = (await fetch([url], 'text', rs_typinglog_scraper))[0]
                wpm = round(
                    12000 * (result['length'] - 1) /
                    (result['duration'] - result['start']), 3)
                date = datetime.datetime.fromtimestamp(
                    result['timestamp']).strftime('%-m/%-d/%y')
                members_list.append([member, wpm, url, date])
            except TypeError:
                pass
            await asyncio.sleep(5)

        members_list = sorted(members_list, key=lambda x: x[1], reverse=True)

        for i, member in enumerate(members_list):
            if i < 3: rank = self.medals[i]
            else: rank = f"{i + 1}."
            if member[1] >= 400: wpm = f"**{member[1]}**"
            else: wpm = member[1]
            description += f"{rank} {self.get_flag(member[0])} {member[0]} - "
            description += f"[{wpm} WPM]({member[2]}) - {member[3]}\n"

        description = description[:-1]
        three_hundred_embed = discord.Embed(
            title='300 WPM Club',
            color=discord.Color(three_hundred_color),
            description=description)
        three_hundred_embed.set_thumbnail(url=three_hundred_thumbnail)
        three_hundred_embed.set_footer(
            text='All speeds measured according to adjusted metric')

        all_time_leaders = [
            user for category in self.races_alltime.values()
            for user in category
        ]
        all_time_leaders += [
            user for category in self.points_alltime.values()
            for user in category
        ]
        all_time_leaders += [
            user for category in self.awards_alltime.values()
            for user in category
        ]
        all_time_leaders = set(all_time_leaders)
        all_time_data = dict()

        url_constructor = Urls()
        right_now = datetime.datetime.utcnow().timestamp()
        texts_data = load_texts_json()
        with open(COUNTRY_CODES, 'r') as jsonfile:
            country_codes = json.load(jsonfile)
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        for user in all_time_leaders:
            await asyncio.sleep(5)
            medals = {1: 0, 2: 0, 3: 0}
            try:
                response = (await fetch([url_constructor.user(user, 'play')],
                                        'text'))[0]
                soup = BeautifulSoup(response, 'html.parser')

                rows = soup.select(
                    "table[class='personalInfoTable']")[0].select('tr')

                medal_html_objects = None
                for row in rows:
                    cells = row.select('td')
                    if len(cells) < 2: continue
                    if cells[0].text.strip() == 'Awards':
                        medal_html_objects = cells[1].select('a')
                        break

                if medal_html_objects:
                    for medal in medal_html_objects:
                        medals[int(medal.select('img')[0]['title'][0])] += 1

                if escape_sequence(user):
                    raise FileNotFoundError
                urls = [Urls().get_races(user, 'play', 1)]

                user_data = c.execute(
                    f"SELECT * FROM t_{user} ORDER BY t DESC LIMIT 1")
                last_race_timestamp = user_data.fetchone()[1]

                data = await fetch_data(user, 'play',
                                        last_race_timestamp + 0.01, right_now)
                if data:
                    c.executemany(
                        f"INSERT INTO t_{user} VALUES (?, ?, ?, ?, ?)", data)
                conn.commit()
                data = c.execute(f"SELECT * FROM t_{user}")

                races, chars_typed, points, total_points, seconds_played = (
                    0, ) * 5
                for race in data:
                    races += 1
                    race_text_id = str(race[2])
                    text_length = texts_data.get(race_text_id,
                                                 {"length": 0})['length']
                    chars_typed += text_length
                    points += race[4]
                    total_points += race[4] if race[4] else texts_data.get(
                        race_text_id,
                        {"word count": 0})['word count'] * race[3] / 60
                    try:
                        seconds_played += 12 * text_length / race[3]
                    except ZeroDivisionError:
                        seconds_played += 0

                first_race_timestamp = c.execute(
                    f"SELECT * FROM t_{user} ORDER BY t ASC LIMIT 1").fetchone(
                    )[1]
                time_difference = right_now - first_race_timestamp
                points_time_difference = min(right_now - 1_501_113_600,
                                             time_difference)

                all_time_data.update({
                    user: {
                        'races': races,
                        'races_daily_average': 86400 * races / time_difference,
                        'chars_typed': chars_typed,
                        'points': points,
                        'points_daily_average':
                        86400 * points / points_time_difference,
                        'total_points': total_points,
                        'medals': medals,
                        'seconds_played': seconds_played,
                        'seconds_played_daily_average':
                        86400 * seconds_played / time_difference,
                        'time_difference': time_difference,
                        'points_time_difference': points_time_difference
                    }
                })
            except Exception as exception:
                print(user, exception)
                continue

        conn.close()

        def helper_formatter(tally_dict, name_formatter, results_formatter,
                             *args):
            rankings = dict()
            args = args[0]
            for key, value in tally_dict.items():
                try:
                    rankings[value].append(key)
                except KeyError:
                    rankings[value] = [key]

            rankings = [[k, v] for k, v in sorted(
                rankings.items(), key=lambda x: x[0], reverse=True)][0:3]
            value = ''
            for i, ranking in enumerate(rankings):
                if i == 0 and args[0]:
                    for user in ranking[1]:
                        self.tally(user)

                if len(args) == 1:
                    optional = ''
                else:
                    try:
                        optional = args[1][ranking[1][0]]
                    except KeyError:
                        optional = ''

                ranking[1] = [name_formatter(j) for j in ranking[1]]
                value += f"{self.medals[i]} {' / '.join(ranking[1])} - {results_formatter(ranking[0])}{optional}\n"
            return value

        races_information = all_records_information['races']['records']
        races_embed = discord.Embed(title='Race Records',
                                    color=discord.Color(races_color))
        races_embed.set_thumbnail(url=races_thumbnail)

        helper_sorter = lambda param: {
            k: v[param]
            for k, v in all_time_data.items()
        }
        helper_flag = lambda x: f"{self.get_flag(x)} {x}"
        helper_round = lambda x: f'{round(x):,}'

        races_daily_average_dict = dict()
        points_daily_average_dict = dict()
        chars_typed_metadata_dict = dict()
        medal_breakdown_dict = dict()
        for key, value in all_time_data.items():
            days = round(value['time_difference'] / 86400)
            days_ = round(value['points_time_difference'] / 86400)
            medal_breakdown = value['medals']

            races_daily_average_dict.update({
                key:
                f""" ({f"{round(value['races'], 2):,}"} races over {f"{days:,}"} days)"""
            })

            points_daily_average_dict.update({
                key:
                f""" ({f"{round(value['points']):,}"} points over {f"{days_:,}"} days)"""
            })

            chars_typed_metadata_dict.update({
                key:
                f""" ({f"{round(value['chars_typed'] / value['races'], 2):,}"} average chars over {f"{value['races']:,}"} races)"""
            })

            medal_breakdown_dict.update({
                key:
                f""" (:first_place: {f"{medal_breakdown[1]:,}"} :second_place: {f"{medal_breakdown[2]:,}"} :third_place: {f"{medal_breakdown[3]:,}"})"""
            })

        races_embed.add_field(name='All-Time Leaders',
                              value=helper_formatter(helper_sorter('races'),
                                                     helper_flag, helper_round,
                                                     (True, )),
                              inline=False)
        races_embed.add_field(name='Highest Average Daily Races',
                              value=helper_formatter(
                                  helper_sorter('races_daily_average'),
                                  helper_flag, helper_round,
                                  (True, races_daily_average_dict)),
                              inline=False)
        races_embed.add_field(name='Most Characters Typed',
                              value=helper_formatter(
                                  helper_sorter('chars_typed'), helper_flag,
                                  helper_round,
                                  (True, chars_typed_metadata_dict)),
                              inline=False)
        races_embed.add_field(name='Most Time Spent Racing',
                              value=helper_formatter(
                                  helper_sorter('seconds_played'), helper_flag,
                                  seconds_to_text, (True, )),
                              inline=False)
        races_embed.add_field(
            name='Highest Average Daily Time Spent Racing',
            value=helper_formatter(
                helper_sorter('seconds_played_daily_average'), helper_flag,
                seconds_to_text, (True, )),
            inline=False)

        for races_record in races_information:
            races_embed.add_field(
                **self.record_field_constructor(races_record, ''))

        points_information = all_records_information['points']['records']
        points_embed = discord.Embed(title='Point Records',
                                     color=discord.Color(points_color))
        points_embed.set_thumbnail(url=points_thumbnail)

        points_embed.add_field(name='All-Time Leaders',
                               value=helper_formatter(helper_sorter('points'),
                                                      helper_flag,
                                                      helper_round, (True, )),
                               inline=False)
        points_embed.add_field(name='Highest Average Daily Points',
                               value=helper_formatter(
                                   helper_sorter('points_daily_average'),
                                   helper_flag, helper_round,
                                   (True, points_daily_average_dict)),
                               inline=False)
        points_embed.add_field(
            name='All-Time Total Points (Includes Retroactive Points)',
            value=helper_formatter(helper_sorter('total_points'), helper_flag,
                                   helper_round, (True, )),
            inline=False)
        points_embed.set_footer(
            text=
            'Retroactive points represent the total number of points a user would have gained, before points were introduced in 2017'
        )

        for points_record in points_information:
            points_embed.add_field(
                **self.record_field_constructor(points_record, ''))

        speedruns_information = all_records_information['speedruns']['records']
        speedruns_embed = discord.Embed(title='Speedrun Records',
                                        color=discord.Color(speedruns_color))
        speedruns_embed.set_thumbnail(url=speedruns_thumbnail)

        for speedruns_record in speedruns_information:
            speedruns_embed.add_field(
                **self.record_field_constructor(speedruns_record, ''))

        awards_embed = discord.Embed(title='Awards Records',
                                     color=discord.Color(awards_color))
        awards_embed.set_thumbnail(url=awards_thumbnail)

        medal_tally = {
            k: sum(v['medals'].values())
            for k, v in all_time_data.items()
        }

        awards_embed.add_field(
            name='All-Time Leaders',
            value=helper_formatter(medal_tally, helper_flag, helper_round,
                                   (True, medal_breakdown_dict)),
            inline=False)

        records_held_embed = discord.Embed(
            title='Records Held', color=discord.Color(records_held_color))
        records_held_embed.set_thumbnail(url=records_held_thumbnail)

        top_countries_list = [[k, v] for k, v in self.country_tally.items()]
        records_held_embed.add_field(name = 'Top Countries',
                                     value = helper_formatter(self.country_tally,\
                                                              lambda x: f"{x} {country_codes[re.findall(':flag.+:', x)[0][5:-1].strip('_').upper()]}",
                                                              helper_round,
                                                              (False,)),
                                     inline = False)
        records_held_embed.add_field(name='Top Users',
                                     value=helper_formatter(
                                         self.user_tally, helper_flag,
                                         helper_round, (False, )),
                                     inline=False)

        last_updated_embed = discord.Embed(
            color=discord.Color(last_updated_color),
            description=f"Last updated **{self.last_updated} UTC**")

        return {
            'faq': faq_embed,
            'speed': speed_embed,
            '300_wpm_club': three_hundred_embed,
            'races': races_embed,
            'points': points_embed,
            'speedruns': speedruns_embed,
            'awards': awards_embed,
            'records_held': records_held_embed,
            'last_updated': last_updated_embed
        }
示例#6
0
    async def pbgraph(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) <= 1: args = check_account(user_id)(args)

        if len(args) == 1: args += ('races', )

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

        player = get_player(user_id, args[0])
        if args[1].lower() not in {'time', 'races'}:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               'Must provide a valid category: `time/races`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        else:
            q_category = 'gn'
            category = 'Races'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y = [], []
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT {q_category}, wpm FROM t_{player}")
            temp_x, temp_y = player_data.fetchone()
            if q_category == 't':
                data_x.append(datetime.datetime.fromtimestamp(temp_x))
            else:
                data_x.append(temp_x)
            data_y.append(temp_y)
            for row in player_data:
                if data_y[-1] > row[1]: continue
                if q_category == 't':
                    data_x.append(datetime.datetime.fromtimestamp(row[0]))
                else:
                    data_x.append(row[0])
                data_y.append(row[1])
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        data_x = reduce_list(data_x)
        data_y = reduce_list(data_y)

        ax = plt.subplots()[1]
        ax.plot(data_x, data_y)
        ax.set_title(f"{player}'s PBs Over {category}")

        if q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.set_xlabel('Race #')

        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"PBs Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
示例#7
0
    async def top(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) <= 1: args = check_account(user_id)(args)

        if len(args) == 1: args += ('wpm', )

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        categories = ['wpm', 'points', 'weightedwpm']

        if not args[1].lower() in categories:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`category` must be `wpm/points/weightedwpm`'))
            return

        if args[1].lower() == 'points':
            category = 'pts'
        elif args[1].lower() == 'wpm':
            category = 'wpm'
        else:
            category = 'weightedwpm'

        texts_length = load_texts_json()
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            if ctx.invoked_with in ['top'] + get_aliases('top'):
                order_by, reverse = 'DESC', True
            else:
                order_by, reverse = 'ASC', False
            if category != 'weightedwpm':
                user_data = c.execute(
                    f"SELECT * FROM t_{player} ORDER BY {category} {order_by} LIMIT 10"
                )
                user_data = user_data.fetchall()
            else:
                raw_data = c.execute(f"SELECT * FROM t_{player}")
                user_data = sorted(
                    raw_data,
                    key=lambda x: x[3] * texts_length[str(x[2])]['length'],
                    reverse=reverse)[:10]
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        embed = discord.Embed(
            title=
            f"{player}'s {['Worst', 'Top'][reverse]} {len(user_data)} Races (Lagged)",
            color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        formatted_category = {
            'pts': 'points',
            'wpm': 'WPM',
            'weightedwpm': 'WPM'
        }[category]
        texts = load_texts_large()
        for i, race in enumerate(user_data):
            value = f"{texts.get(str(race[2]), 'Missing Text')} [:cinema:]({Urls().result(player, race[0], 'play')})"
            if formatted_category == 'points':
                name = f"{i + 1}. {race[4]} {formatted_category} (Race #{f'{race[0]:,}'}, Text ID: {race[2]})"
            else:
                name = f"{i + 1}. {race[3]} {formatted_category} (Race #{f'{race[0]:,}'}, Text ID: {race[2]})"
            embed.add_field(name=name, value=value, inline=False)
            if category == 'weightedwpm':
                embed.set_footer(
                    text='Sorted by weighted WPM (text_length • wpm)')
        await ctx.send(embed=embed)
        return
示例#8
0
    async def racedetails(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        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])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        texts_length = load_texts_json()
        races, words_typed, chars_typed, points, retro, time_spent, total_wpm = (
            0, ) * 7
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        try:
            user_data = c.execute(f"SELECT * FROM t_{player}")
            first_race = user_data.fetchone()
            first_point_race = 0
            text_id = str(first_race[2])
            races += 1
            total_wpm += first_race[3]
            words_typed += texts_length.get(text_id, {"word count": 0})['word count']
            chars_typed += texts_length.get(text_id, {"length": 0})['length']
            fastest_race, slowest_race = (first_race[3],
                                          first_race[0]), (first_race[3],
                                                           first_race[0])
            try:
                time_spent += 12 * texts_length.get(text_id, {"length": 0})['length'] / first_race[3]
            except ZeroDivisionError:
                pass
            if first_race[4] == 0:
                retro += first_race[3] / 60 * texts_length.get(text_id, {"word count": 0})['word count']
            else:
                if not first_point_race:
                    first_point_race = first_race[1]
                points += first_race[4]
            first_race = first_race[1]
            for row in user_data:
                race_wpm = row[3]
                total_wpm += race_wpm
                if race_wpm > fastest_race[0]:
                    fastest_race = (race_wpm, row[0])
                if race_wpm < slowest_race[0]:
                    slowest_race = (race_wpm, row[0])
                text_id = str(row[2])
                races += 1
                words_typed += texts_length.get(text_id, {'word count': 0})['word count']
                chars_typed += texts_length.get(text_id, {'length': 0})['length']
                if row[4] == 0:
                    retro += race_wpm / 60 * texts_length.get(text_id, {'word count': 0})['word count']
                else:
                    if not first_point_race:
                        first_point_race = row[1]
                    points += row[4]
                try:
                    time_spent += 12 * texts_length.get(text_id, {'length': 0})['length'] / race_wpm
                except ZeroDivisionError:
                    races -= 1
                    pass
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        today = time.time()
        num_days = (today - first_race) / 86400
        num_point_days = (today - first_point_race) / 86400

        embed = discord.Embed(title=f"Race Details for {player}",
                              color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.set_footer(
            text=('Retroactive points represent the total number of points '
                  'a user would have gained, before points were introduced '
                  'in 2017'))

        embed.add_field(
            name='Races',
            value=
            (f"**Total Races:** {f'{races:,}'}\n"
             f"**Average Daily Races:** {f'{round(races / num_days, 2):,}'}\n"
             f"**Total Words Typed:** {f'{words_typed:,}'}\n"
             f"**Average Words Per Race:** {f'{round(words_typed / races, 2):,}'}\n"
             f"**Total Chars Typed:** {f'{chars_typed:,}'}\n"
             f"**Average Chars Per Race: **{f'{round(chars_typed / races, 2):,}'}\n"
             ))
        embed.add_field(
            name='Points',
            value=
            (f"**Current Points:** {f'{round(points):,}'}\n"
             f"**Average Daily Points:** {f'{round(points / num_point_days, 2):,}'}\n"
             f"**Average Points Per Race:** {f'{round((points + retro) / races, 2):,}'}\n"
             f"**Retroactive Points:** {f'{round(retro):,}'}\n"
             f"**Total Points:** {f'{round(points + retro):,}'}"))
        embed.add_field(
            name='Speed',
            value=
            (f"**Average (Lagged):** {f'{round(total_wpm / races, 2):,}'} WPM\n"
             f"**Fastest Race:** {f'{fastest_race[0]:,}'} WPM "
             f"[:cinema:]({Urls().result(player, fastest_race[1], 'play')})\n"
             f"**Slowest Race:** {f'{slowest_race[0]:,}'} WPM "
             f"[:cinema:]({Urls().result(player, slowest_race[1], 'play')})"),
            inline=False)
        embed.add_field(
            name='Time',
            value=
            (f"**Total Time Spent Racing:** {seconds_to_text(time_spent)}\n"
             f"**Average Daily Time:** {seconds_to_text(time_spent / num_days)}\n"
             f"**Average Time Per Race:** {seconds_to_text(time_spent / races)}"
             ))
        await ctx.send(embed=embed)
        return
示例#9
0
    async def textslessequal(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        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} [user] [num] <wpm/points/time>"))
            return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        try:
            num = float(args[1])
            if num <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    '`speed` and `length` must be positive numbers'))
            return

        if len(args) == 2:
            category = 'wpm'
        else:
            category = args[2].lower()
            if category not in ['wpm', 'points', 'times']:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        'Must provide a valid category: `wpm/points/times`'))
                return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            count = len(
                c.execute(f"SELECT DISTINCT tid from t_{player}").fetchall())
            if category == 'wpm':
                user_data = c.execute(
                    f"""SELECT tid, COUNT(tid)
                                          FROM t_{player}
                                          WHERE wpm >= ?
                                          GROUP BY tid
                                          ORDER BY COUNT(tid) DESC""",
                    (num, )).fetchall()
            elif category == 'points':
                user_data = c.execute(
                    f"""SELECT tid, COUNT(tid)
                                          FROM t_{player}
                                          WHERE pts >= ?
                                          GROUP BY tid
                                          ORDER BY COUNT(tid) DESC""",
                    (num, )).fetchall()
            else:
                user_data = c.execute(
                    f"""SELECT tid, COUNT(tid)
                                          FROM t_{player}
                                          GROUP BY tid
                                          HAVING COUNT(tid) >= ?
                                          ORDER BY COUNT(tid) DESC""",
                    (num, )).fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        texts_data = load_texts_large()
        category = {
            'wpm': 'WPM',
            'points': 'Points',
            'times': 'Times'
        }[category]
        if category == 'Times':
            num = int(num)

        embed = discord.Embed(
            title=f"{player}'s Total Texts Typed Over {f'{num:,}'} {category}",
            color=discord.Color(MAIN_COLOR),
            description=
            (f"**Texts Typed:** {f'{count:,}'}\n"
             f"**Texts Over {f'{num:,}'} {category}:** "
             f"{f'{len(user_data):,}'} ({round(100 * len(user_data) / count, 2)}%)"
             ))
        embed.set_thumbnail(url=Urls().thumbnail(player))
        for i in range(0, 10):
            try:
                value_1 = f"\"{texts_data[str(user_data[i][0])]}\" "
                value_2 = f"[{TR_INFO}]({Urls().text(user_data[i][0])})"
                value = value_1 + value_2
                if len(value) > 1024:
                    value_1 = value_1[0:1019 - len(value_2)]
                    value = value_1 + "…\" " + value_2
                embed.add_field(
                    name=(f"{i + 1}. {f'{user_data[i][1]:,}'} times "
                          f"(Race Text ID: {user_data[i][0]})"),
                    value=value,
                    inline=False)
            except:
                pass

        await ctx.send(embed=embed)
        return
示例#10
0
    async def textsunder(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        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} [user] [speed] <text_length>"))
            return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        try:
            speed = float(args[1])
            if speed <= 0:
                raise ValueError
            length = 0
            if len(args) == 3:
                length = float(args[2])
                if length <= 0:
                    raise ValueError
        except ValueError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    '`speed` and `length` must be positive numbers'))
            return

        texts_data = dict()
        with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile:
            reader = csv.reader(csvfile)
            next(reader)
            for row in reader:
                texts_data.update(
                    {int(row[0]): {
                         'text': row[1],
                         'ghost': row[2]
                     }})

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT gn, tid, MAX(wpm) FROM t_{player} GROUP BY tid ORDER BY wpm"
            ).fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        tu_dict, tids = dict(), []
        for tid in user_data:
            if tid[2] > speed:
                continue
            if length:
                if len(texts_data[tid[1]]['text']) > length:
                    continue
            tu_dict.update({tid[1]: tid[2]})
            tids.append(tid[1])

        if len(tu_dict) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    'No texts found that meet the required criteria'))
            return

        title = f"Random Texts Under {f'{speed:,}'} WPM"
        if len(args) == 3:
            title += f" and {f'{length:,}'} Length"
        title += f" for {player} ({f'{len(tu_dict):,}'} left)"

        embed = discord.Embed(title=title, color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        for i in range(0, 5):
            try:
                random_tid = random.choice(tids)
                value_1 = f"\"{texts_data[random_tid]['text']}\" "
                value_2 = f"[{TR_INFO}]({Urls().text(random_tid)}) [{TR_GHOST}]({texts_data[random_tid]['ghost']})"
                value = value_1 + value_2
                if len(value) > 1024:
                    value_1 = value_1[0:1019 - len(value_2)]
                    value = value_1 + "…\" " + value_2
                embed.add_field(
                    name=(f"{i + 1}. {f'{tu_dict[random_tid]:,}'} WPM"
                          f" (Race Text ID: {random_tid})"),
                    value=value,
                    inline=False)
                tids.remove(random_tid)
            except IndexError:
                pass

        await ctx.send(embed=embed)
        return
示例#11
0
    async def unraced(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        if len(args) == 2:
            try:
                length = int(args[1])
                if length < 1 or length > 999:
                    raise ValueError
            except ValueError:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        '`length` must be a positive integer less than 999'))
                return
        else:
            length = 0

        all_tids, user_tids, texts_data = set(), set(), dict()
        with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile:
            reader = csv.reader(csvfile)
            next(reader)
            for row in reader:
                all_tids.add(int(row[0]))
                texts_data.update(
                    {int(row[0]): {
                         'text': row[1],
                         'ghost': row[2]
                     }})

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(f"SELECT DISTINCT tid FROM t_{player}")
            for row in user_data:
                user_tids.add(row[0])
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        unraced_tids = list(all_tids - user_tids)
        if length:
            unraced_tids = list(
                filter(lambda x: len(texts_data[x]['text']) < length,
                       unraced_tids))
        if len(unraced_tids) == 0:
            description = f"{player} has completed all texts"
            if length:
                description += f" under length {length}"
            description += '!'
            await ctx.send(embed=discord.Embed(title='Nothing to Choose From',
                                               color=discord.Color(754944),
                                               description=description))
            return

        title = 'Random Unraced Texts'
        if length:
            title += f" Under Length {length}"
        title += f" for {player} ({f'{len(unraced_tids):,}'} left)"

        embed = discord.Embed(title=title, color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        try:
            for i in range(0, 5):
                random_tid = random.choice(unraced_tids)
                value_1 = f"\"{texts_data[random_tid]['text']}\" "
                value_2 = (f"[{TR_INFO}]({Urls().text(random_tid)}) "
                           f"[{TR_GHOST}]({texts_data[random_tid]['ghost']})")
                value = value_1 + value_2
                if len(value) > 1024:
                    value_1 = value_1[0:1019 - len(value_2)]
                    value = f"{value_1}…\" {value_2}"

                embed.add_field(name=f"{i + 1}. Race Text ID: {random_tid}",
                                value=value,
                                inline=False)
                unraced_tids.remove(random_tid)
        except:
            pass

        await ctx.send(embed=embed)
        return
示例#12
0
    async def textbests(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        tb = ctx.invoked_with in ['textbests'] + get_aliases('textbests')
        bd = ctx.invoked_with in ['breakdown'] + get_aliases('breakdown')

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

        try:
            if len(args) == 0:
                raise ValueError
            elif len(args) > 2:
                raise ValueError
            elif bd and len(args) != 1:
                raise ValueError
        except ValueError:
            optional = ' <num_texts>' if tb else ''
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [user]{optional}"))
            return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        filter_tb = len(args) == 2
        if filter_tb:
            try:
                num_texts = int(args[1])
                if num_texts <= 0:
                    raise TypeError
            except TypeError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`num_texts` must be a positive integer'))
                return

        sum_, count = 0, 0
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT gn, tid, MAX(wpm) FROM t_{player} GROUP BY tid ORDER BY wpm"
            ).fetchall()
            if filter_tb:
                user_data = user_data[-num_texts:]
            min_bucket = int(user_data[0][2] // 10)
            max_bucket = int(user_data[-1][2] //
                             10) if user_data[-1][2] // 10 <= 30 else 30

            if bd:
                breakdown_dict, textbest_dict = {}, {}
                for i in range(min_bucket, max_bucket + 1):
                    breakdown_dict.update({i: 0})
                    textbest_dict.update({i: {'count': 0, 'sum': 0}})

            for row in user_data:
                count += 1
                sum_ += row[2]
                if bd:
                    bucket = int(row[2] // 10) if row[2] // 10 <= 30 else 30
                    if ctx.invoked_with[-1] == '*':
                        breakdown_dict[bucket] += 1
                        textbest_dict[bucket]['sum'] += row[2]
                        textbest_dict[bucket]['count'] += 1
                    else:
                        for i in range(min_bucket, bucket + 1):
                            breakdown_dict[i] += 1

        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        if tb:
            texts_data = load_texts_large()

            if len(user_data) < 10:
                worst = []
                if len(user_data) < 5:
                    top = user_data[::-1]
                else:
                    top = user_data[-5:][::-1]
            else:
                worst = user_data[0:5]
                top = user_data[-5:][::-1]
        else:
            breakdown_text = ''
            max_count_spacer = len(f'{max(breakdown_dict.values()):,}')
            for bucket, count_ in breakdown_dict.items():
                bucket_spacer = 1 + math.floor(
                    math.log10(max_bucket)) - math.floor(math.log10(bucket))
                count_spacer = max_count_spacer - len(f'{count_:,}')
                count_spacer_ = max_count_spacer - len(f'{count - count_:,}')
                breakdown_text += f"{{{bucket * 10}+{' ' * bucket_spacer}WPM}} "
                breakdown_text += f"{' ' * count_spacer}{f'{count_:,}'} [{f'{round(100 * count_ / count, 2):6.2f}'}%] "
                if ctx.invoked_with[-1] == '*':
                    try:
                        average = f"{round(textbest_dict[bucket]['sum'] / textbest_dict[bucket]['count'], 2):6.2f}"
                    except ZeroDivisionError:
                        average = "  ——  "
                    breakdown_text += f"{{{average} WPM}}\n"
                else:
                    breakdown_text += f"({' ' * count_spacer_}{f'{count - count_:,}'} left)\n"

        title = f"{player}'s Text Bests"
        if filter_tb:
            title += f" (Top {f'{num_texts:,}'} Texts Filtered)"
        embed = discord.Embed(title=title, color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        embed.add_field(
            name='Texts',
            value=
            (f"**Texts:** {f'{count:,}'}\n"
             f"**Text Bests Average:** {f'{round(sum_ / count, 2):,}'} ("
             f"{f'{round(count * (5 - (sum_ / count) % 5), 2):,}'} total WPM gain "
             f"til {round(5 * ((sum_ / count) // 5 + 1))} WPM)"),
            inline=False)

        if tb:
            value = ''
            for i, text in enumerate(top):
                value += f"**{i + 1}. {f'{text[2]:,}'} WPM (Race #{f'{text[0]:,}'})**\n"
                value += f"{texts_data.get(str(text[1]), 'Missing Text')} [:cinema:]({Urls().result(player, text[0], 'play')})\n"
            embed.add_field(name=f"Top {i + 1} Texts",
                            value=value,
                            inline=False)

            value = ''
            for i, text in enumerate(worst):
                value += f"**{i + 1}. {f'{text[2]:,}'} WPM (Race #{f'{text[0]:,}'})**\n"
                value += f"{texts_data.get(str(text[1]), 'Missing Text')} [:cinema:]({Urls().result(player, text[0], 'play')})\n"
            embed.add_field(name=f"Worst {i + 1} Texts",
                            value=value,
                            inline=False)
        else:
            embed.add_field(name='Breakdown',
                            value=f"```css\n{breakdown_text}```",
                            inline=False)

        await ctx.send(embed=embed)
        return
示例#13
0
    async def personalbest(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        cur_wpm = 0
        if len(args) == 2:
            try:
                if args[1] == '*':
                    text_id = get_cached_id(ctx.message.channel.id)
                    if not text_id:
                        text_id = int(args[1])
                else:
                    text_id = int(args[1])
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   f"**{args[1]}** is not a valid text ID"))
                return
        else:
            try:
                urls = [Urls().get_races(player, 'play', 1)]
                response = (await fetch(urls, 'json'))[0][0]
                text_id = int(response['tid'])
                cur_wpm = float(response['wpm'])
            except:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx,
                                           ctx.message).missing_information())
                return

        with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile:
            reader = csv.reader(csvfile)
            next(reader)
            for row in reader:
                if int(row[0]) == text_id:
                    text = row[1]
                    break
                else:
                    continue

        try:
            if len(text) > 1024:
                text = f"\"{text[0:1020]}…\""
            else:
                text = f"\"{text}\""
        except NameError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"{text_id} is not a valid text ID"))
            return

        count, sum_, best, best_gn, worst_gn = (0, ) * 5
        worst = 100000
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT gn, wpm FROM t_{player} WHERE tid = ?",
                (text_id, )).fetchall()
            for row in user_data:
                count += 1
                sum_ += row[1]
                if row[1] > best:
                    best_gn, best = row
                if row[1] < worst:
                    worst_gn, worst = row
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        if cur_wpm > best:
            color, description = 754944, f"**Improved by {round(cur_wpm - best, 2)} WPM (to {round(cur_wpm, 2)} WPM)**"
        else:
            color, description = MAIN_COLOR, ''

        if not count:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               f"**{player}** has not completed the text yet"))
            return

        value = f"**Times:** {f'{count:,}'}\n"
        if cur_wpm:
            value += f"**Last Race:** {f'{cur_wpm:,}'} WPM\n"
        value += (f"**Average:** {f'{round(sum_ / count, 2):,}'} WPM\n"
                  f"**Fastest:** {f'{best:,}'} WPM "
                  f"(Race #{f'{best_gn:,}'}) [:cinema:]"
                  f"({Urls().result(player, best_gn, 'play')})\n"
                  f"**Slowest:** {f'{worst:,}'} WPM "
                  f"(Race #{f'{worst_gn:,}'}) [:cinema:]"
                  f"({Urls().result(player, worst_gn, 'play')})\n")

        title = f"Quote #{text_id} Statistics for {player}"
        cache_id(ctx.message.channel.id, text_id)
        if description:
            embed = discord.Embed(title=title,
                                  color=discord.Color(color),
                                  url=Urls().text(text_id),
                                  description=description)
        else:
            embed = discord.Embed(title=title,
                                  color=discord.Color(color),
                                  url=Urls().text(text_id))

        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.add_field(name='Quote', value=text, inline=False)
        embed.add_field(name='Speeds', value=value)

        if ctx.invoked_with[-1] == '*' and len(user_data) > 1:
            ax = plt.subplots()[1]

            data_y = [i[1] for i in user_data]
            if cur_wpm: data_y += [cur_wpm]
            length = len(data_y)
            data_x = [i + 1 for i in range(length)]

            if length < 30:
                ax.plot(data_x, data_y)
            else:
                ax.scatter(data_x,
                           data_y,
                           marker='.',
                           alpha=0.1,
                           color='#000000')
                if length < 500:
                    sma = length // 10
                else:
                    sma = 50

                moving_y = [sum(data_y[0:sma]) / sma]
                moving_y += [
                    sum(data_y[i - sma:i]) / sma for i in range(sma, length)
                ]
                moving_x = [data_x[0]] + data_x[sma:]
                moving_y = reduce_list(moving_y)
                moving_x = reduce_list(moving_x)
                ax.plot(moving_x, moving_y, color='#FF0000')
                title += f"\n(Moving Average of {sma} Races)"

            ax.set_title(title)
            ax.set_xlabel('Attempt #')
            ax.set_ylabel('WPM')
            plt.grid(True)
            file_name = f"{title}.png"

            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')
            os.remove(file_name)
            await ctx.send(file=file_, embed=embed)
            return

        await ctx.send(embed=embed)
        return
示例#14
0
    async def ginoo(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) == 0: args = ('ginoo75', 1000)

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        try:
            num_races = int(args[1])
            if num_races <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`num_races` must be a positive integer'))
            return

        monthly_races = []
        current_month = {
            'month': None,
            'races': 0,
            'words_typed': 0,
            'chars_typed': 0,
            'points': 0,
            'time_spent': 0,
            'total_wpm': 0,
            'best_wpm': 0,
            'worst_wpm': 1000000
        }

        texts_length = load_texts_json()
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        try:
            user_data = c.execute(f"SELECT * FROM t_{player}")
            for row in user_data:
                month = datetime.datetime.fromtimestamp(
                    row[1]).strftime('%Y-%-m')
                if current_month['month'] == None:
                    current_month['month'] = month
                elif current_month['month'] != month:
                    if current_month['races'] >= num_races:
                        current_month.update({
                            'average_wpm':
                            round(
                                current_month['total_wpm'] /
                                current_month['races'], 2)
                        })
                        del current_month['total_wpm']
                        current_month['time_spent'] = round(
                            current_month['time_spent'], 2)
                        current_month['points'] = round(
                            current_month['points'], 2)
                        monthly_races.append(list(current_month.values()))
                    current_month = {
                        'month': month,
                        'races': 0,
                        'words_typed': 0,
                        'chars_typed': 0,
                        'points': 0,
                        'time_spent': 0,
                        'total_wpm': 0,
                        'best_wpm': 0,
                        'worst_wpm': 1000000
                    }

                current_month['races'] += 1
                current_month['words_typed'] += texts_length.get(
                    str(row[2]), {'word count': 0})['word count']
                current_month['chars_typed'] += texts_length.get(
                    str(row[2]), {'length': 0})['length']
                current_month['points'] += row[4]
                current_month['time_spent'] += texts_length.get(
                    str(row[2]), {'length': 0})['length'] * 12 / row[3]
                current_month['total_wpm'] += row[3]
                current_month['best_wpm'] = max(current_month['best_wpm'],
                                                row[3])
                current_month['worst_wpm'] = min(current_month['worst_wpm'],
                                                 row[3])
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        if not monthly_races:
            await ctx.send(embed=discord.Embed(
                color=discord.Color(MAIN_COLOR),
                title=
                f"{f'{num_races:,}'} Races Longevity Statistics for {player}",
                description='It\'s empty here.').set_footer(
                    text="ginoo75#6666's custom command"))
            return

        if current_month['races'] >= num_races:
            current_month.update({
                'average_wpm':
                round(current_month['total_wpm'] / current_month['races'], 2)
            })
            del current_month['total_wpm']
            current_month['time_spent'] = round(current_month['time_spent'], 2)
            current_month['points'] = round(current_month['points'], 2)
            monthly_races.append(list(current_month.values()))

        index, longest_chain_index = (0, ) * 2
        max_chain, current_chain = (1, ) * 2
        for i, month_stats in enumerate(monthly_races[1:]):
            index += 1
            previous_month = datetime.datetime.strptime(
                monthly_races[i][0], '%Y-%m')
            month = month_stats[0]
            next_month = (previous_month.month + 1) % 12
            if next_month == 0: next_month = 12
            if datetime.datetime.strptime(month, '%Y-%m') == datetime.datetime(
                    previous_month.year + int((previous_month.month) / 12),
                    next_month, 1):
                current_chain += 1
                if current_chain > max_chain:
                    max_chain = current_chain
                    longest_chain_index = index - current_chain + 1
            else:
                current_chain = 1

        monthly_races.insert(0, list(current_month.keys()))

        file_name = f"{player}_longevity_{num_races}.csv"
        with open(file_name, 'w') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerows(monthly_races)

        file_ = discord.File(file_name, filename=file_name)

        embed = discord.Embed(
            color=discord.Color(MAIN_COLOR),
            title=f"{f'{num_races:,}'} Races Longevity Statistics for {player}",
            description=
            (f"**{f'{(index + 1):,}'}** months with chain of "
             f"**{f'{max_chain:,}'}** starting on "
             f"**{datetime.datetime.strptime(monthly_races[longest_chain_index + 1][0], '%Y-%m').strftime('%B %Y')}**"
             ))
        embed.set_footer(text="ginoo75#6666's custom command")

        await ctx.send(file=file_, embed=embed)
        os.remove(file_name)
        return
示例#15
0
    async def charlieog(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        if len(args) == 2:
            try:
                if args[1] == '*':
                    tid = get_cached_id(ctx.message.channel.id)
                    if not tid:
                        tid = int(args[1])
                else:
                    tid = int(args[1])
                if tid <= 0:
                    raise ValueError
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   f"{args[1]} is not a valid text ID"))
                return
        else:
            tid = 3621293

        urls = [Urls().get_races(player, 'play', 1)]
        try:
            api_response = await fetch(urls, 'json')
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist or has no races")))
            return

        file_name = f"t_{player}"
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM t_{player} ORDER BY t DESC LIMIT 1")
            last_race_timestamp = user_data.fetchone()[1]
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return

        text, ghost = '', ''
        with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile:
            reader = csv.reader(csvfile)
            next(reader)
            for row in reader:
                if int(row[0]) == tid:
                    text, ghost = row[1], row[2]
        if not text or not ghost:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"{tid} is not a valid text ID"))
            return

        cache_id(ctx.message.channel.id, tid)

        value_1 = f"\"{text}\" "
        value_2 = (f"[{TR_INFO}]({Urls().text(tid)}) "
                   f"[{TR_GHOST}]({ghost})")
        value = value_1 + value_2
        if len(value) > 1024:
            value_1 = value_1[0:1019 - len(value_2)]
            value = value_1 + "…\"" + value_2

        data = await fetch_data(player, 'play', last_race_timestamp + 0.01,
                                time.time())

        if data:
            c.executemany(f"INSERT INTO {file_name} VALUES (?, ?, ?, ?, ?)",
                          data)

        conn.commit()
        data = c.execute(
            f"""SELECT * FROM
                        (SELECT *
                        FROM {file_name}
                        WHERE t >= ?)
                WHERE tid = ?""", (time.time() - 86400, tid)).fetchall()
        conn.close()

        if len(data) < 10:
            description = 'Next save available **now**'
        else:
            description = f"Next save available in **{seconds_to_text(86400 + data[0][1] - time.time())}**"
        value_ = ''
        for i, race in enumerate(data):
            value_ += (
                f"{i + 1}. {seconds_to_text(time.time() - race[1])} ago "
                f"({race[3]} WPM)\n")

        embed = discord.Embed(
            title=f"{player}'s Text #{tid} Statistics in Last 24 Hours",
            color=discord.Color(MAIN_COLOR),
            description=description)
        embed.add_field(name=f"Text ID: {tid}", value=value, inline=False)
        if value_:
            embed.add_field(name='Races', value=value_, inline=False)
        embed.set_footer(text="snowmelt#1745's custom command")

        await ctx.send(embed=embed)
        return
示例#16
0
    async def fastestcompletion(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) == 1: args = check_account(user_id)(args) + ('races', )
        elif len(args) == 2: args += ('races', )

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return
        try:
            num = float(args[1])
            if num <= 0 or num % 1 != 0:
                raise ValueError
            num = int(num)
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`num` must be a positive number'))
            return

        category = args[2].lower()
        if not category in ['races', 'points']:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`category` must be `races/points`'))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM t_{player} ORDER BY t").fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        try:
            if category == 'races':
                user_data[num - 1]
        except IndexError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               "`num` must not exceed user's race count"))
            return

        length = len(user_data)
        if category == 'races':
            min_start, min_end, cur_start, = 0, num - 1, 0
            for i in range(num, length):
                cur_start += 1
                if user_data[i][1] - user_data[cur_start][1] <\
                user_data[min_end][1] - user_data[min_start][1]:
                    min_start, min_end = cur_start, i

        elif category == 'points':
            min_start, cur_start, cur_end, cur_points = (0, ) * 4
            min_end = length - 1
            exceeds_point_count = True
            for i in range(0, length):
                cur_points += user_data[i][4]
                if cur_points >= num:
                    exceeds_point_count = False
                    cur_end = i
                    while cur_points - user_data[cur_start][4] >= num:
                        cur_points -= user_data[cur_start][4]
                        cur_start += 1
                    if user_data[cur_end][1] - user_data[cur_start][1] <\
                    user_data[min_end][1] - user_data[min_start][1]:
                        min_start, min_end = cur_start, cur_end

            if exceeds_point_count:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   "`num` must not exceed user's point count"))
                return

        races, seconds_played, chars_typed, words_typed, points, wpm_sum, wpm_max = (
            0, ) * 7
        wpm_min = 100000
        tids = []
        text_data = load_texts_json()
        for i in range(min_start, min_end + 1):
            races += 1
            cur = user_data[i]
            wpm = cur[3]
            tid = str(cur[2])
            tids.append(tid)

            wpm_sum += wpm
            if wpm < wpm_min:
                wpm_min = wpm
            if wpm > wpm_max:
                wpm_max = wpm

            words = text_data[tid]['word count']
            chars = text_data[tid]['length']
            words_typed += words
            chars_typed += chars
            seconds_played += 12 * chars / wpm
            points += cur[4]
            if cur[4] == 0:
                points += wpm * words / 60

        f_category = {'races': 'Races', 'points': 'Points'}[category]

        embed = discord.Embed(
            title=
            f"{player}'s Fastest Time to Complete {f'{num:,}'} {f_category}",
            color=discord.Color(MAIN_COLOR),
            description=
            f"**Took:** {seconds_to_text(user_data[min_end][1] - user_data[min_start][1])}"
        )
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.set_footer(
            text=
            f"First Race (#{f'{min_start + 1:,}'}): {datetime.datetime.fromtimestamp(user_data[min_start][1]).strftime('%B %-d, %Y, %-I:%M:%S %p')}"
        )
        embed.add_field(
            name='Races',
            value=
            (f"**Total Races:** {f'{min_end - min_start + 1:,}'}\n"
             f"**Total Words Typed:** {f'{words_typed:,}'}\n"
             f"**Average Words Per Races:** {f'{round(words_typed / races, 2):,}'}\n"
             f"**Total Chars Typed:** {f'{chars_typed:,}'}\n"
             f"**Average Chars Per Race:** {f'{round(chars_typed / races, 2):,}'}\n"
             f"**Total Time Spent Racing:** {seconds_to_text(seconds_played)}\n"
             f"**Total Time Elapsed:** {seconds_to_text(user_data[min_end][1] - user_data[min_start][1])}\n"
             f"**Average Time Per Race:** {seconds_to_text(seconds_played / races)}"
             ),
            inline=False)
        embed.add_field(
            name='Points',
            value=
            (f"**Total Points:** {f'{round(points):,}'}\n"
             f"**Average Points Per Race:** {f'{round(points / races, 2):,}'}\n"
             ),
            inline=False)
        embed.add_field(
            name='Speed',
            value=
            (f"**Average (Lagged):** {f'{round(wpm_sum / races, 2):,}'} WPM\n"
             f"**Fastest Race:** {f'{wpm_max:,}'} WPM\n"
             f"**Slowest Race:** {f'{wpm_min:,}'} WPM"),
            inline=False)
        embed.add_field(
            name='Quotes',
            value=f"**Number of Unique Quotes:** {f'{len(set(tids)):,}'}",
            inline=False)

        await ctx.send(embed=embed)
        return
示例#17
0
    async def ban(self, ctx, *args):
        user_id = ctx.message.author.id

        if ctx.invoked_with == 'banned':
            conn = sqlite3.connect(DATABASE_PATH)
            c = conn.cursor()

            banned_users = c.execute(
                f"SELECT * FROM {USERS_KEY} WHERE banned = 1").fetchall()
            conn.close()

            description, banned = '', [['ID']]
            for i, user in enumerate(banned_users):
                id_ = user[0]

                if i < 10:
                    description += f"**{i + 1}.** <@{id_}>\n"
                banned.append([id_])

            description = description[:-1]
            embed = discord.Embed(title='Banned Users',
                                  color=discord.Color(0),
                                  description=description)

            if len(banned) > 10:
                with open('banned_users.csv', 'w') as csvfile:
                    writer = csv.writer(csvfile)
                    writer.writerows(banned)

                file_ = discord.File('banned_users.csv', 'banned_users.csv')

                await ctx.send(file=file_, embed=embed)
                os.remove('banned_users.csv')
                return

            await ctx.send(embed=embed)
            return

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

        def perms(id_):
            if id_ in BOT_OWNER_IDS:
                return 2
            if id_ in BOT_ADMIN_IDS:
                return 1
            return 0

        try:
            args = (args[0].strip('<@!').strip('>'), )
            if len(args[0]) > 18 or escape_sequence(args[0]):
                raise ValueError
            discord_id = int(args[0])
            if perms(discord_id) >= perms(user_id):
                raise commands.CheckFailure
                return
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"<@{args[0]}> is not a valid Discord ID"))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(f"SELECT * FROM {USERS_KEY} WHERE id = ?",
                                  (discord_id, )).fetchall()
            if not user_data:
                toggled_to = True
                c.execute(f"INSERT INTO {USERS_KEY} (id, banned) VALUES(?, ?)",
                          (
                              discord_id,
                              toggled_to,
                          ))
            else:
                toggled_to = not user_data[0][1]
                c.execute(f"UPDATE {USERS_KEY} SET banned = ? WHERE id = ?", (
                    toggled_to,
                    discord_id,
                ))
            conn.commit()
            conn.close()
        except sqlite3.OperationalError:
            c.execute(
                f"CREATE TABLE {USERS_KEY} (id integer PRIMARY KEY, banned BOOLEAN)"
            )
            conn.close()
            return

        setting = 'banned' if toggled_to else 'unbanned'
        await ctx.send(
            embed=discord.Embed(color=discord.Color(0),
                                description=(
                                    f"<@{discord_id}> has been **{setting}**\n"
                                    'from using <@742267194443956334>')))
        return
示例#18
0
    async def today(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        is_today = True
        today_timestamp = (datetime.datetime.utcnow().date() -
                           datetime.date(1970, 1, 1)).total_seconds()

        if ctx.invoked_with.lower() in ['yesterday', 'yday', 'yd']:
            today_timestamp = (datetime.datetime.utcnow().date() -
                               datetime.date(1970, 1, 2)).total_seconds()
            is_today = False
            if len(args) > 1 or len(args) == 0:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(
                        ctx,
                        ctx.message).parameters(f"{ctx.invoked_with} [user]"))
                return

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

        if len(args) == 2:
            try:
                today_timestamp_temp = (
                    datetime.datetime.strptime(args[1], "%Y-%m-%d").date() -
                    datetime.date(1970, 1, 1)).total_seconds()
                if today_timestamp_temp != today_timestamp: is_today = False
                if today_timestamp_temp > today_timestamp:
                    await ctx.send(content=f"<@{user_id}>",
                                   embed=Error(
                                       ctx, ctx.message).incorrect_format(
                                           '`date` must not exceed today'))
                    return
                today_timestamp = today_timestamp_temp
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`date` must be in the yyyy-mm-dd format'))
                return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        urls = [Urls().get_races(player, 'play', 1)]
        try:
            api_response = await fetch(urls, 'json')
            total_races = int(api_response[0][0]['gn'])
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist or has no races")))
            return

        file_name = f"t_{player}_play_{today_timestamp}_{today_timestamp + 86400}".replace(
            '.', '_')
        conn = sqlite3.connect(TEMPORARY_DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM {file_name} ORDER BY t DESC LIMIT 1")
            last_race = user_data.fetchone()
            if last_race:
                last_race_timestamp = last_race[1]
                races_remaining = total_races - last_race[0]
            else:
                races_remaining = total_races
        except sqlite3.OperationalError:
            races_remaining = total_races
            if races_remaining == 0:
                conn.close()
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx,
                                           ctx.message).missing_information(
                                               f"{player} has no races"))
            else:
                c.execute(
                    f"CREATE TABLE {file_name} (gn integer PRIMARY KEY, t, tid, wpm, pts)"
                )

        try:
            data = await fetch_data(player, 'play', last_race_timestamp + 0.01,
                                    today_timestamp + 86400)
        except UnboundLocalError:
            data = await fetch_data(player, 'play', today_timestamp,
                                    today_timestamp + 86400)

        date = datetime.datetime.fromtimestamp(today_timestamp).strftime(
            '%B %-d, %Y')

        user_is_leader = await self.check_if_leader(player, 'day')
        if user_is_leader and is_today:
            embed = discord.Embed(
                title=f"{date} Stats for {player}",
                color=discord.Color(MAIN_COLOR),
                url=Urls().user(player, 'play'),
                description=':crown: **Daily Leader** :crown:')
        else:
            embed = discord.Embed(title=f"{date} Stats for {player}",
                                  color=discord.Color(MAIN_COLOR),
                                  url=Urls().user(player, 'play'))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        if data:
            c.executemany(f"INSERT INTO {file_name} VALUES (?, ?, ?, ?, ?)",
                          data)

        conn.commit()
        data = c.execute(f"SELECT * FROM {file_name}").fetchall()
        conn.close()
        if not data:
            embed.add_field(name='Average Speed', value='—')
            embed.add_field(name='Races', value='0')
            embed.add_field(name='Points', value='0')
            await ctx.send(embed=embed)
            return

        texts_data = load_texts_json()
        races, wpm, points, seconds_played, chars_typed, words_typed = (
            0, ) * 6
        fastest_race, slowest_race = (data[0][3], data[0][0]), (data[0][3],
                                                                data[0][0])
        for row in data:
            races += 1
            race_text_id = str(row[2])
            race_wpm = row[3]
            wpm += race_wpm
            if race_wpm > fastest_race[0]: fastest_race = (race_wpm, row[0])
            if race_wpm < slowest_race[0]: slowest_race = (race_wpm, row[0])
            points += row[4]
            word_count = texts_data.get(race_text_id, {"word count": 0})['word count']
            race_text_length = texts_data.get(race_text_id, {"length": 0})['length']
            seconds_played += 12 * race_text_length / race_wpm
            chars_typed += race_text_length
            words_typed += word_count

        average_wpm = round(wpm / races, 2)
        total_points = round(points)
        embed.add_field(
            name='Summary',
            value=
            (f"**Average Speed:** {average_wpm} WPM "
             f"([{slowest_race[0]}]({Urls().result(player, slowest_race[1], 'play')})"
             f" - [{fastest_race[0]}]({Urls().result(player, fastest_race[1], 'play')}))\n"
             f"**Total Races:** {f'{races:,}'}\n"
             f"**Total Points:** {f'{total_points:,}'} ({f'{round(points / races, 2)}'} points/race)"
             ),
            inline=False)
        embed.add_field(
            name='Details',
            value=
            (f"**Total Words Typed:** {f'{words_typed:,}'}\n"
             f"**Average Words Per Race:** {round(words_typed / races, 2)}\n"
             f"**Total Chars Typed:** {f'{chars_typed:,}'}\n"
             f"**Average Chars Per Race:** {round(chars_typed / races, 2)}\n"
             f"**Total Time Spent Racing:** {seconds_to_text(seconds_played)}\n"
             f"**Average Time Per Race:** {seconds_to_text(seconds_played / races)}"
             ),
            inline=False)

        await ctx.send(embed=embed)
        return
示例#19
0
    async def longestbreak(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        duration = 0
        if len(args) == 2:
            try:
                duration = int(args[1])
                if duration <= 0 or duration > 1_000_000_000_000:
                    raise ValueError
            except ValueError:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        '`seconds` must be a positive number less than 1,000,000,000,000'
                    ))
                return

        count, longest_break = (0, ) * 2
        longest_break_gn, longest_break_time = (1, ) * 2
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(f"SELECT t, gn FROM t_{player} ORDER BY t")
            previous_time, previous_gn = user_data.fetchone()
            for row in user_data:
                break_ = row[0] - previous_time
                if break_ >= duration: count += 1
                if break_ > longest_break:
                    longest_break = break_
                    longest_break_gn = previous_gn
                    longest_break_time = previous_time
                previous_time, previous_gn = row
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        break_ = time.time() - previous_time
        on_break = break_ > longest_break
        if break_ >= duration: count += 1
        if on_break:
            longest_break = break_
            longest_break_gn = previous_gn
            longest_break_time = previous_time

        if duration:
            await ctx.send(embed=discord.Embed(
                color=discord.Color(MAIN_COLOR),
                description=
                f"**{player}** had **{f'{count:,}'}** breaks longer than **{seconds_to_text(duration)}**"
            ))
            return

        embed = discord.Embed(
            color=discord.Color(MAIN_COLOR),
            description=
            (f"**{player}**'s longest break was **{seconds_to_text(longest_break)}**, "
             f"and it started on race **{f'{longest_break_gn:,}'}** "
             f" / **{datetime.datetime.fromtimestamp(longest_break_time).strftime('%B %-d, %Y, %-I:%M:%S %p')}**"
             ))
        if on_break: embed.set_footer(text='Currently on break')

        await ctx.send(embed=embed)
        return
示例#20
0
    async def getdata(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        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])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        urls = [Urls().get_races(player, 'play', 1)]
        try:
            api_response = await fetch(urls, 'json')
            total_races = int(api_response[0][0]['gn'])
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist or has no races")))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM t_{player} ORDER BY t DESC LIMIT 1")
            last_race = user_data.fetchone()
            last_race_timestamp = last_race[1]
            races_remaining = total_races - last_race[0]
        except sqlite3.OperationalError:
            races_remaining = total_races
            if races_remaining == 0:
                conn.close()
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx,
                                           ctx.message).missing_information(
                                               f"{player} has no races"))
                return
            else:
                if races_remaining > 10000 and not user_id in BOT_ADMIN_IDS:
                    pass
                else:
                    c.execute(
                        f"CREATE TABLE t_{player} (gn integer PRIMARY KEY, t, tid, wpm, pts)"
                    )
        if races_remaining > 10000 and not user_id in BOT_ADMIN_IDS:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).lacking_permissions(
                               ('Data request exceeds 10,000 races. '
                                'Have a bot admin run the command.')))
            return
        if races_remaining == 0:
            conn.close()
            await ctx.send(embed=discord.Embed(
                title='Data Request',
                color=discord.Color(MAIN_COLOR),
                description=(f"{player}'s data successfully created/updated\n"
                             '0 races added')))
            return

        start_ = time.time()
        await ctx.send(embed=discord.Embed(
            title='Data Request',
            color=discord.Color(MAIN_COLOR),
            description=
            ('Request successful\n'
             f"Estimated download time: {seconds_to_text(0.005125 * races_remaining + 0.5)}"
             )))

        try:
            data = await fetch_data(player, 'play', last_race_timestamp + 0.01,
                                    time.time())
        except UnboundLocalError:
            data = await fetch_data(player, 'play', 1204243200, time.time())
        c.executemany(f"INSERT INTO t_{player} VALUES (?, ?, ?, ?, ?)", data)
        conn.commit()
        conn.close()

        length = round(time.time() - start_, 3)
        await ctx.send(
            content=f"<@{user_id}>",
            embed=discord.Embed(
                title='Data Request',
                color=discord.Color(MAIN_COLOR),
                description=(f"{player}'s data successfully created/updated\n"
                             f"{f'{races_remaining:,}'} races added\n"
                             f"Took {seconds_to_text(length)}")))
        return
示例#21
0
        if len(args) > 1:
            try:
                args[-1].index('-')
                end = (
                    datetime.datetime.strptime(args[-1], "%Y-%m-%d").date() -
                    datetime.date(1970, 1, 1)).total_seconds()
                if end <= 1_250_000_000 or end > time.time():
                    raise ValueError
                args = args[:-1]
            except ValueError:
                pass

        args = list(args)
        for i, player in enumerate(args):
            args[i] = get_player(user_id, args[i])
            if escape_sequence(player):
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).missing_information(
                        (f"[**{player}**]({Urls().user(player, 'play')}) "
                         "doesn't exist")))
                return

        def calculate_pts(tid, wpm):
            try:
                return text_data[str(tid)]['word count'] * wpm / 60
            except ValueError:
                return 0
            except KeyError:
                return 0
示例#22
0
    async def week(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        week = ctx.invoked_with in ['week'] + get_aliases('week')
        month = ctx.invoked_with in ['month'] + get_aliases('month')
        year = ctx.invoked_with in ['year'] + get_aliases('year')

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

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

        same_day = True
        today = datetime.datetime.utcnow().date()
        if len(args) == 2:
            try:
                parse_string = '%Y'
                date_format = len(args[1].split('-'))
                if date_format == 1:
                    pass
                elif date_format == 2:
                    parse_string += '-%m'
                elif date_format == 3:
                    parse_string += '-%m-%d'
                else:
                    raise ValueError

                today_temp = datetime.datetime.strptime(args[1],
                                                        parse_string).date()
                if today_temp != today: same_day = False
                if today_temp > today:
                    await ctx.send(content=f"<@{user_id}>",
                                   embed=Error(
                                       ctx, ctx.message).incorrect_format(
                                           '`date` must not exceed today'))
                    return
                today = today_temp
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`date` must be in the yyyy-mm-dd format'))
                return

        if week:
            normalizer = today.isocalendar()[2]
            start_time = today - datetime.timedelta(days=normalizer - 1)
            end_time = today + datetime.timedelta(days=7 - normalizer)
            formatted_sort = 'Weekly'
        elif month:
            start_time = today.replace(day=1)
            end_time = (today.replace(day=1) + datetime.timedelta(days=32)
                        ).replace(day=1) - datetime.timedelta(days=1)
            formatted_sort = 'Monthly'
        elif year:
            start_time = datetime.date(today.year, 1, 1)
            end_time = datetime.date(today.year, 12, 31)
            formatted_sort = 'Yearly'

        delta_start = start_time
        delta = end_time - start_time
        start_time = (start_time - datetime.date(1970, 1, 1)).total_seconds()
        end_time = (end_time - datetime.date(1970, 1, 1)).total_seconds()
        end_time += 86400

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        urls = [Urls().get_races(player, 'play', 1)]
        try:
            api_response = await fetch(urls, 'json')
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist or has no races")))
            return

        file_name = f"t_{player}"
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM t_{player} ORDER BY t DESC LIMIT 1")
            last_race_timestamp = user_data.fetchone()[1]
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return

        data = await fetch_data(player, 'play', last_race_timestamp + 0.01,
                                end_time)

        if data:
            c.executemany(f"INSERT INTO {file_name} VALUES (?, ?, ?, ?, ?)",
                          data)

        conn.commit()
        data = c.execute(
            f"""SELECT * FROM {file_name}
                             WHERE t > ? AND t < ?""", (
                start_time,
                end_time,
            )).fetchall()
        conn.close()

        if week:
            day_one = datetime.datetime.fromtimestamp(start_time).day
            day_two = datetime.datetime.fromtimestamp(end_time - 86400).day
            if day_one > day_two: format_string = '%B %-d, %Y'
            else: format_string = '%-d, %Y'
            title = (
                f"Weekly ({datetime.datetime.fromtimestamp(start_time).strftime('%B %-d')}—"
                f"{datetime.datetime.fromtimestamp(end_time - 86400).strftime(format_string)})"
            )
        elif month:
            title = f"Monthly ({datetime.datetime.fromtimestamp(start_time).strftime('%B %Y')})"
        elif year:
            title = f"Yearly ({datetime.datetime.fromtimestamp(start_time).strftime('%Y')})"

        title += f" Stats for {player}"
        user_is_leader = await self.check_if_leader(
            player,
            formatted_sort.lower()[:-2])
        if user_is_leader and same_day:
            embed = discord.Embed(
                title=title,
                color=discord.Color(MAIN_COLOR),
                url=Urls().user(player, 'play'),
                description=f":crown: **{formatted_sort} Leader** :crown:")
        else:
            embed = discord.Embed(title=title,
                                  color=discord.Color(MAIN_COLOR),
                                  url=Urls().user(player, 'play'))
        embed.set_thumbnail(url=Urls().thumbnail(player))

        if not data:
            embed.add_field(name='Average Speed', value='—')
            embed.add_field(name='Races', value='0')
            embed.add_field(name='Points', value='0')
            await ctx.send(embed=embed)
            return

        texts_length = load_texts_json()

        csv_dict = {}
        for i in range(delta.days + 1):
            csv_dict.update({
                (delta_start + datetime.timedelta(days=i)).isoformat(): {
                    'races': 0,
                    'words_typed': 0,
                    'chars_typed': 0,
                    'points': 0,
                    'time_spent': 0,
                    'average_wpm': 0,
                    'best_wpm': 0,
                    'worst_wpm': 0
                }
            })

        races, words_typed, chars_typed, points, retro, time_spent = (0, ) * 6
        wpm_total, wpm_best, wpm_worst = (0, ) * 3
        for row in data:
            date = datetime.datetime.fromtimestamp(row[1]).date().isoformat()
            text_id = str(row[2])
            wpm = row[3]
            races += 1
            words_typed_ = texts_length.get(text_id, {"word count": 0})['word count']
            chars_typed_ = texts_length.get(text_id, {"length": 0})['length']
            words_typed += words_typed_
            chars_typed += chars_typed_

            wpm_total += wpm
            if not wpm_best or wpm_best < wpm: wpm_best = wpm
            if not wpm_worst or wpm_worst > wpm: wpm_worst = wpm

            csv_day = csv_dict[date]
            csv_day['races'] += 1
            csv_day['words_typed'] += words_typed_
            csv_day['chars_typed'] += chars_typed_
            csv_day['average_wpm'] = (csv_day['average_wpm'] *\
                                     (csv_day['races'] - 1) + wpm) /\
                                     csv_day['races']
            if not csv_day['best_wpm'] or csv_day['best_wpm'] < wpm:
                csv_day['best_wpm'] = wpm
            if not csv_day['worst_wpm'] or csv_day['worst_wpm'] > wpm:
                csv_day['worst_wpm'] = wpm

            if row[4] == 0:
                retro_ = row[3] / 60 * texts_length.get(text_id, {"word count": 0})['word count']
                retro += retro_
                csv_day['points'] += row[4]
            else:
                points += row[4]
                csv_day['points'] += row[4]
            try:
                time_spent_ = 12 * texts_length.get(text_id, {"length": 0})['length'] / row[3]
                time_spent += time_spent_
                csv_day['time_spent'] += time_spent_
            except ZeroDivisionError:
                races -= 1
                csv_day['races'] -= 1
                pass

        today = time.time() if time.time() < end_time else end_time
        num_days = (today - start_time) / 86400

        retro_text = f"**Retroactive Points:** {f'{round(retro):,}'}\n" if retro else ""

        if retro_text:
            embed.set_footer(text=(
                'Retroactive points represent the total number of points '
                'a user would have gained, before points were introduced '
                'in 2017'))

        embed.add_field(
            name='Races',
            value=
            (f"**Total Races:** {f'{races:,}'}\n"
             f"**Average Daily Races:** {f'{round(races / num_days, 2):,}'}\n"
             f"**Total Words Typed:** {f'{words_typed:,}'}\n"
             f"**Average Words Per Race:** {f'{round(words_typed / races, 2):,}'}\n"
             f"**Total Chars Typed:** {f'{chars_typed:,}'}\n"
             f"**Average Chars Per Race: **{f'{round(chars_typed / races, 2):,}'}"
             ))
        embed.add_field(
            name='Points',
            value=
            (f"**Points:** {f'{round(points):,}'}\n"
             f"**Average Daily Points:** {f'{round(points / num_days, 2):,}'}\n"
             f"**Average Points Per Race:** {f'{round((points + retro) / races, 2):,}'}\n"
             f"{retro_text}"
             f"**Total Points:** {f'{round(points + retro):,}'}"))
        embed.add_field(
            name='Speed',
            value=
            (f"**Average (Lagged):** {f'{round(wpm_total / races, 2):,}'} WPM\n"
             f"**Fastest Race:** {f'{wpm_best:,}'} WPM\n"
             f"**Slowest Race:** {f'{wpm_worst:,}'} WPM"),
            inline=False)
        embed.add_field(
            name='Time',
            value=
            (f"**Total Time Spent Racing:** {seconds_to_text(time_spent)}\n"
             f"**Average Daily Time:** {seconds_to_text(time_spent / num_days)}\n"
             f"**Average Time Per Race:** {seconds_to_text(time_spent / races)}"
             ))

        if ctx.invoked_with[-1] == '*':
            csv_data = [['date'] + list(next(iter(csv_dict.values())).keys())]
            for key, value in csv_dict.items():
                values = [round(i, 2) for i in list(value.values())]
                csv_data.append([key] + values)

            with open('temporary.csv', 'w') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerows(csv_data)

            title_ = title.split(' (')
            await ctx.send(file=discord.File(
                'temporary.csv',
                f"{player}_{title_[0].lower()}_{title_[1].split(')')[0].lower()}.csv"
            ),
                           embed=embed)
            os.remove('temporary.csv')
            return

        await ctx.send(embed=embed)
        return
示例#23
0
    async def improvement(self, ctx, *args):
        user_id = ctx.message.author.id

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

        if len(args) == 1: args += ('races', )

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

        player = get_player(user_id, args[0])
        if args[1].lower() not in ['time', 'races']:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               'Must provide a valid category: `time/races`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        else:
            q_category = 'gn'
            category = 'Races'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y, max_point = [], [], 0
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT wpm, {q_category} FROM t_{player} ORDER by {q_category}"
            )
            for row in player_data:
                if q_category == 't':
                    data_x.append(datetime.datetime.fromtimestamp(row[1]))
                else:
                    data_x.append(row[1])
                row_wpm = row[0]
                max_point = max(max_point, row_wpm)
                data_y.append(row_wpm)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        length = len(data_x)
        if length < 15:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    f"`{ctx.invoked_with}` requires at least 15 races to generate a graph"
                ))
            return

        elif length < 7500:
            sma = length // 15
        else:
            sma = 500
        moving_y = [sum(data_y[0:sma]) / sma]
        moving_y += [sum(data_y[i - sma:i]) / sma for i in range(sma, length)]
        moving_x = [data_x[0]] + data_x[sma:]
        moving_y = reduce_list(moving_y)
        moving_x = reduce_list(moving_x)

        max_line = max(moving_y)

        ax = plt.subplots()[1]
        ax.scatter(data_x, data_y, marker='.', alpha=0.1, color='#000000')
        ax.plot(moving_x, moving_y, color='#FF0000')
        ax.set_title(
            f"{player}'s WPM Over {category}\n(Moving Average of {sma} Races)")

        if q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.xaxis.set_major_formatter(
                ticker.FuncFormatter(self.large_num_formatter))
            ax.set_xlabel('Race #')

        if max_point > 2 * max_line: ax.set_ylim(0, 2 * max_line)
        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"WPM Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
示例#24
0
    async def milestone(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        try:
            num = int(args[1])
            if num <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`num` must be a positive integer'))
            return

        categories = ['races', 'wpm', 'points']

        if not args[2] in categories:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`category` must be `races/wpm/points`'))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        try:
            first_race = c.execute(
                f"SELECT t FROM t_{player} LIMIT 1").fetchone()[0]
            if args[2] == 'wpm':
                achieved, race_num = c.execute(
                    f"SELECT t, gn FROM t_{player} WHERE wpm >= ? ORDER BY t LIMIT 1",
                    (num, )).fetchone()
            elif args[2] == 'races':
                achieved, race_num = c.execute(
                    f"SELECT t, gn FROM t_{player} WHERE gn == ?",
                    (num, )).fetchone()
            else:
                user_data = c.execute(
                    f"SELECT t, pts, gn FROM t_{player} ORDER BY t")
                sum_, achieved = 0, 0
                for row in user_data:
                    sum_ += row[1]
                    if sum_ >= num:
                        achieved = row[0]
                        race_num = row[2]
                        break
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        except TypeError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               'The user has not achieved the milestone yet'))
            return
        conn.close()

        if not achieved:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    f"[**{player}**]({Urls().user(player, 'play')}) has not achieved the milestone yet"
                ))
            return

        category_1 = {'races': '', 'wpm': ' WPM', 'points': ' Point'}[args[2]]
        category_2 = {
            'races': 'races',
            'wpm': 'WPM',
            'points': 'points'
        }[args[2]]
        embed = discord.Embed(
            title=f"{player}'s {num_to_text(num)}{category_1} Race",
            color=discord.Color(MAIN_COLOR),
            url=Urls().result(player, race_num, 'play'))
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.add_field(
            name=f"{player} achieved {f'{num:,}'} {category_2} on:",
            value=
            f"{datetime.datetime.fromtimestamp(achieved).strftime('%B %d, %Y, %I:%M:%S %p')} UTC"
        )
        embed.add_field(name='It took:',
                        value=seconds_to_text(achieved - first_race))

        await ctx.send(embed=embed)
        return
示例#25
0
    async def compare(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        player_ = get_player(user_id, args[1])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        if escape_sequence(player_):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player_}**]({Urls().user(player_, 'play')}) "
                     "doesn't exist")))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT tid, MAX(wpm) FROM t_{player} GROUP BY tid ORDER BY wpm"
            ).fetchall()
            player_data_ = c.execute(
                f"SELECT tid, MAX(wpm) FROM t_{player_} GROUP BY tid ORDER BY wpm"
            ).fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        player_dict = dict()
        for text in player_data:
            player_dict.update({text[0]: text[1]})

        first_player, second_player = [], []
        for text in player_data_:
            if player_dict.get(text[0], -1) > 0:
                difference = text[1] - player_dict[text[0]]
                if difference == 0: pass
                elif difference < 0:
                    first_player.append(-1 * difference)
                else:
                    second_player.append(difference)

        if len(first_player) + len(second_player) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    f"**{player}** and **{player_}** have no texts in common"))
            return

        fig, (ax, ax_) = plt.subplots(1, 2, sharey=True)
        if first_player:
            first_max = max(first_player)
        else:
            first_max = 0
        if second_player:
            second_max = max(second_player)
        else:
            second_max = 0

        if int(first_max) // 10 == 0:
            patches = ax.hist(first_player, bins=1,
                              orientation='horizontal')[2]
        else:
            patches = ax.hist(first_player,
                              bins=int(first_max) // 10,
                              orientation='horizontal')[2]
        if int(second_max) // 10 == 0:
            patches_ = ax_.hist(second_player,
                                bins=1,
                                orientation='horizontal')[2]
        else:
            patches_ = ax_.hist(second_player,
                                bins=int(second_max) // 10,
                                orientation='horizontal')[2]

        ax.yaxis.tick_left()
        ax.set_ylim(ax.get_ylim()[::-1])
        ax.set_ylabel('Difference (WPM)')
        ax.grid()
        ax.set_title(player)

        ax_.grid()
        ax_.set_title(player_)

        max_xlim = max(ax.get_xlim()[1], ax_.get_xlim()[1])
        ax.set_xlim(0, max_xlim)
        ax.set_xlim(ax.get_xlim()[::-1])
        ax_.set_xlim(0, max_xlim)

        title = f"{player} vs. {player_} Text Bests Comparison"
        plt.subplots_adjust(wspace=0, hspace=0)
        file_name = f"{player}_{player_}_text_bests_comparison.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False, patches)
        graph_color(ax_, graph_colors, False, patches)

        to_rgba = lambda x: (x // 65536 / 255,
                             ((x % 65536) // 256) / 255, x % 256 / 255)

        if graph_colors['text']:
            fig.suptitle(title, color=to_rgba(graph_colors['text']))
            fig.text(0.5,
                     0.025,
                     'Frequency (Texts)',
                     ha='center',
                     color=to_rgba(graph_colors['text']))
        else:
            fig.suptitle(title)
            fig.text(0.5, 0.025, 'Frequency (Texts)', ha='center')

        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        plt.close()

        embed = discord.Embed(title=title, color=MAIN_COLOR)
        file_ = discord.File(file_name, filename=file_name)
        embed.set_image(url=f"attachment://{file_name}")
        embed.add_field(
            name=player,
            value=
            f"**{f'{len(first_player):,}'}** texts (+**{f'{round(sum(first_player), 2):,}'}** WPM)"
        )
        embed.add_field(
            name=player_,
            value=
            f"**{f'{len(second_player):,}'}** texts (+**{f'{round(sum(second_player), 2):,}'}** WPM)"
        )
        await ctx.send(file=file_, embed=embed)
        os.remove(file_name)
        return
示例#26
0
    async def marathon(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) == 0:
            args = check_account(user_id)(args) + (86400, 'races')
        elif len(args) == 1:
            args += (86400, 'races')
        elif len(args) == 2:
            args += ('races', )

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return
        try:
            session_length = float(args[1])
            if session_length <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`seconds` must be a positive number'))
            return

        category = args[2].lower()
        if not category in ['races', 'points']:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               "`category` must be `races/points`"))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT * FROM t_{player} ORDER BY t").fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        length = len(user_data)
        if category == 'races':
            cur_min, max_start, max_end = (0, ) * 3
            for i in range(0, length):
                if user_data[i][1] - user_data[cur_min][1] >= session_length:
                    if i - cur_min > max_end - max_start:
                        max_start, max_end = cur_min, i
                while user_data[i][1] - user_data[cur_min][1] > session_length:
                    cur_min += 1
            if length - cur_min - 1 > max_end - max_start:
                max_start, max_end = cur_min, length
        elif category == 'points':
            cur_min, max_start, max_end, cur_points, max_points = (0, ) * 5
            for i in range(0, length):
                cur_points += user_data[i][4]
                if user_data[i][1] - user_data[cur_min][1] >= session_length:
                    if cur_points > max_points:
                        max_start, max_end, max_points = cur_min, i, cur_points
                while user_data[i][1] - user_data[cur_min][1] > session_length:
                    cur_points -= user_data[cur_min][4]
                    cur_min += 1
            if cur_points + user_data[length - 1][4] > max_points:
                max_start, max_end = cur_min, length

        races, seconds_played, chars_typed, words_typed, points, wpm_sum, wpm_max = (
            0, ) * 7
        wpm_min = 100000
        text_data = load_texts_json()
        for i in range(max_start, max_end):
            races += 1
            cur = user_data[i]
            wpm = cur[3]
            tid = str(cur[2])

            wpm_sum += wpm
            wpm_min = min(wpm, wpm_min)
            wpm_max = max(wpm, wpm_max)

            words = text_data[tid]['word count']
            chars = text_data[tid]['length']
            words_typed += words
            chars_typed += chars
            seconds_played += 12 * chars / wpm
            points += cur[4]
            if cur[4] == 0:
                points += wpm * words / 60

        f_category = {'races': 'Races', 'points': 'Points'}[category]

        max_end -= 1

        embed = discord.Embed(
            title=(f"{f_category} Marathon Stats for {player} "
                   f"({seconds_to_text(session_length, True)} period)"),
            color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.set_footer(text=(
            f"First Race (#{f'{max_start + 1:,}'}): {datetime.datetime.fromtimestamp(user_data[max_start][1]).strftime('%B %-d, %Y, %-I:%M:%S %p')} | "
            "Retroactive points represent the total number of "
            "points a user would have gained, before points were introduced in 2017"
        ))
        embed.add_field(
            name='Races',
            value=
            (f"**Total Races:** {f'{races:,}'}\n"
             f"**Total Words Typed:** {f'{words_typed:,}'}\n"
             f"**Average Words Per Races:** {f'{round(words_typed / races, 2):,}'}\n"
             f"**Total Chars Typed:** {f'{chars_typed:,}'}\n"
             f"**Average Chars Per Race:** {f'{round(chars_typed / races, 2):,}'}\n"
             f"**Total Time Spent Racing:** {seconds_to_text(seconds_played)}\n"
             f"**Total Time Elapsed:** {seconds_to_text(user_data[max_end][1] - user_data[max_start][1])}\n"
             f"**Average Time Per Race:** {seconds_to_text(seconds_played / races)}"
             ),
            inline=False)
        embed.add_field(
            name='Points (Retroactive Included)',
            value=
            (f"**Total Points:** {f'{round(points):,}'}\n"
             f"**Average Points Per Race:** {f'{round(points / races, 2):,}'}\n"
             ),
            inline=False)
        embed.add_field(
            name='Speed',
            value=
            (f"**Average (Lagged):** {f'{round(wpm_sum / races, 2):,}'} WPM\n"
             f"**Fastest Race:** {f'{wpm_max:,}'} WPM\n"
             f"**Slowest Race:** {f'{wpm_min:,}'} WPM"),
            inline=False)

        await ctx.send(embed=embed)
        return
示例#27
0
    async def textbestsgraph(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) <= 1: args = check_account(user_id)(args)

        if len(args) == 1: args += ('races', )

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

        player = get_player(user_id, args[0])
        if args[1].lower() not in {'time', 'races', 'texts'}:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'Must provide a valid category: `time/races/texts`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        elif args[1].lower() == 'races':
            q_category = 'gn'
            category = 'Races'
        elif args[1].lower() == 'texts':
            q_category = None
            category = 'Texts'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y = [], []
        text_ids = {}
        wpm_sum, wpm_count = 0, 0
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(f"SELECT gn, t, tid, wpm FROM t_{player}")
            for race_data in user_data:
                if text_ids.get(race_data[2], False):
                    if race_data[3] > text_ids[race_data[2]]:
                        if not q_category:
                            data_x.append(len(data_x))
                        elif q_category == 'gn':
                            data_x.append(race_data[0])
                        else:
                            data_x.append(
                                datetime.datetime.fromtimestamp(race_data[1]))
                        wpm_sum += race_data[3] - text_ids[race_data[2]]
                        text_ids.update({race_data[2]: race_data[3]})
                        data_y.append(wpm_sum / wpm_count)
                else:
                    if not q_category:
                        data_x.append(len(data_x))
                    elif q_category == 'gn':
                        data_x.append(race_data[0])
                    else:
                        data_x.append(
                            datetime.datetime.fromtimestamp(race_data[1]))
                    text_ids.update({race_data[2]: race_data[3]})
                    wpm_sum += race_data[3]
                    wpm_count += 1
                    data_y.append(wpm_sum / wpm_count)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        data_x = reduce_list(data_x)
        data_y = reduce_list(data_y)

        ax = plt.subplots()[1]
        ax.plot(data_x, data_y)
        ax.set_title(f"{player}'s Text Bests Average Over {category}")

        if not q_category:
            ax.set_xlabel('Text Changes (Additions/Improvements)')
        elif q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.set_xlabel('Race #')

        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"Text Bests Average Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
示例#28
0
    async def racesover(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        try:
            num = float(args[1])
            if num <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`num` must be a positive number'))
            return

        categories = ['wpm', 'points']

        if not args[2] in categories:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`category` must be `wpm/points`'))
            return

        if args[2] == 'points':
            category = 'pts'
        else:
            category = 'wpm'

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        meeting, total = 0, 0
        try:
            user_data = c.execute(f"SELECT {category} FROM t_{player}")
            for row in user_data:
                total += 1
                if row[0] > num:
                    meeting += 1
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        category = {'wpm': 'WPM', 'pts': 'points'}[category]

        embed = discord.Embed(
            title=f"{player}'s Total Races Over {f'{num:,}'} {category}",
            color=discord.Color(MAIN_COLOR),
            description=
            (f"{f'{meeting:,}'} of {player}'s {f'{total:,}'} are above "
             f"{f'{num:,}'} {category} ({f'{round(100 * meeting / total, 2):,}'}%)"
             ))
        embed.set_footer(
            text='Counts texts GREATER than specified parameter (not equal to)'
        )

        await ctx.send(embed=embed)
        return
示例#29
0
    async def sessionstats(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

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

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return
        try:
            if len(args) == 1:
                session_length = 1800
            else:
                session_length = float(args[1])
            if session_length <= 0:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               '`seconds` must be a positive number'))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        max_start, max_end, cur_start, cur_end = (0, ) * 4
        max_tstart, max_tend, cur_tstart, cur_tend = (0, ) * 4
        try:
            user_data = c.execute(f"SELECT t FROM t_{player} ORDER BY t")
            user_data = user_data.fetchall()
            for i in range(1, len(user_data)):
                if user_data[i][0] - user_data[i - 1][0] > session_length:
                    if cur_end - cur_start > max_end - max_start:
                        max_start, max_end = cur_start, cur_end
                    cur_end += 1
                    cur_start = cur_end
                    if user_data[cur_tend][0] - user_data[cur_tstart][0] > \
                    user_data[max_tend][0] - user_data[max_tstart][0]:
                        max_tstart, max_tend = cur_tstart, cur_tend
                    cur_tend += 1
                    cur_tstart = cur_tend
                else:
                    cur_end += 1
                    cur_tend += 1
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        embed = discord.Embed(
            title=
            f"Session Stats for {player} ({seconds_to_text(session_length, True)} interval)",
            color=discord.Color(MAIN_COLOR))
        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.add_field(
            name='Highest Race Session',
            value=
            (f"{f'{max_end - max_start + 1:,}'} races in "
             f"{seconds_to_text(user_data[max_end][0] - user_data[max_start][0])}"
             ))
        embed.add_field(
            name='Longest Session',
            value=
            (f"{f'{max_tend - max_tstart + 1:,}'} races in "
             f"{seconds_to_text(user_data[max_tend][0] - user_data[max_tstart][0])}"
             ))

        await ctx.send(embed=embed)
        return
示例#30
0
    async def botleaderboard(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()

        def combine_aliases(cmds_data):
            cmds_dict = dict()
            for cmd in cmds_data:
                try:
                    normalized_name = normalized_commands[cmd[0]]
                except KeyError:
                    normalized_name = cmd[0]

                try:
                    cmds_dict[normalized_name] += cmd[1]
                except KeyError:
                    cmds_dict[normalized_name] = cmd[1]

            return cmds_dict

        if ctx.invoked_with[-1] == '*':
            if len(args) == 0:
                user_count = len(
                    c.execute(
                        f"SELECT DISTINCT id FROM {TABLE_KEY}").fetchall())
                command_count = len(
                    c.execute(f"SELECT * FROM {TABLE_KEY}").fetchall())

                conn.close()
                await ctx.send(embed=discord.Embed(
                    color=discord.Color(MAIN_COLOR),
                    description=
                    f"**{f'{user_count:,}'}** users have used **{f'{command_count:,}'}** commands"
                ))
                return
            elif len(args) == 1:
                command = args[0].lower()
                if command == '*':
                    command = 'All Commands'
                    user_data = c.execute(f"""SELECT command, COUNT(command)
                                              FROM {TABLE_KEY}
                                              GROUP BY command""")
                    user_data = combine_aliases(user_data)
                    user_data = [[f"`{key}`", value]
                                 for key, value in user_data.items()]
                else:
                    try:
                        command = normalized_commands[command]
                        aliases = [command] + get_aliases(command)
                        user_data = []
                        for alias in aliases:
                            alias_data = c.execute(
                                f"""SELECT name, COUNT(id)
                                                        FROM
                                                            (SELECT * FROM {TABLE_KEY} WHERE command = ?)
                                                        GROUP BY id""",
                                (alias, ))
                            user_data += alias_data.fetchall()
                        user_data = combine_aliases(user_data)
                        user_data = [[key, value]
                                     for key, value in user_data.items()]

                    except KeyError:
                        user_data = ()

                conn.close()

            user_data = sorted(user_data, key=lambda x: x[1], reverse=True)

            value = ''
            for i, user in enumerate(user_data[:10]):
                value += f"{NUMBERS[i]} {user[0]} - {f'{user[1]:,}'}\n"
            value = value[:-1]

            if not value:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        f"`{command}` is not a command or has never been used")
                )
                return

            embed = discord.Embed(
                title=f"Bot Usage Leaderboard (`{command}` command)",
                color=discord.Color(MAIN_COLOR),
                description=value)

            embed.set_footer(text='Since December 24, 2020')
            await ctx.send(embed=embed)
            return

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

        if len(args) == 0:
            user_data = c.execute(f"""SELECT name, COUNT(id)
                                      FROM {TABLE_KEY}
                                      GROUP BY id
                                      ORDER BY COUNT(id) DESC LIMIT 10"""
                                  ).fetchall()
            conn.close()

            value = ''
            for i, user in enumerate(list(user_data)):
                value += f"{NUMBERS[i]} {user[0]} - {f'{user[1]:,}'}\n"
            value = value[:-1]

            embed = discord.Embed(title='Bot Usage Leaderboard',
                                  color=discord.Color(MAIN_COLOR),
                                  description=value)
        else:
            args = (args[0].strip('<@!').strip('>'), )
            try:
                if len(args[0]) > 18:
                    raise ValueError
                id_ = int(args[0])
                if escape_sequence(args[0].lower()):
                    raise ValueError
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   f"**{args[0]}** is not a valid Discord ID"))
                return

            user_data = []
            users_cmd_data = c.execute(
                f"""SELECT command, COUNT(command), name
                                          FROM
                                            (SELECT * FROM {TABLE_KEY} WHERE ID = ?)
                                          GROUP BY command""",
                (id_, )).fetchall()
            conn.close()
            if users_cmd_data:
                name = users_cmd_data[-1][2]
            else:
                name = id_
            user_data = combine_aliases(users_cmd_data)
            user_data = sorted([[key, value]
                                for key, value in user_data.items()],
                               key=lambda x: x[1],
                               reverse=True)
            title = f"Bot Statistics for {name}"

            value, count = '', 0
            for i, cmd in enumerate(user_data):
                count += cmd[1]
                if i < 10:
                    cmds = i + 1
                    value += f"**{cmds}.** `{cmd[0]}` - {f'{cmd[1]:,}'}\n"

            embed = discord.Embed(
                title=title,
                color=discord.Color(MAIN_COLOR),
                description=f"**Used:** {f'{count:,}'} times")
            if value:
                embed.add_field(name=f"Top {cmds} Most Used", value=value)

        embed.set_footer(text='Since December 24, 2020')
        await ctx.send(embed=embed)
        return