Example #1
0
    async def toggledessle(self, ctx, *args):
        user_id = str(ctx.message.author.id)
        MAIN_COLOR = get_supporter(user_id)

        invalid = False

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

        accounts = load_accounts()

        try:
            cur = accounts[user_id]['desslejusted']
            accounts[user_id]['desslejusted'] = not cur
        except KeyError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    'Discord account must be linked to TypeRacer account with '
                    f"`{get_prefix(ctx, ctx.message)}register [typeracer_username]`"
                )))
            return

        update_accounts(accounts)

        await ctx.send(embed=discord.Embed(
            color=discord.Color(MAIN_COLOR),
            description=(
                f"<@{user_id}> has been set to `desslejusted` **{not cur}**")))
        return
Example #2
0
    async def delete_supporter(self, ctx, *args):
        if len(args) != 1: return

        try:
            int(args[0])
            if len(args[0]) > 18:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"**{args[0]}** is not a valid Discord ID"))
            return

        supporters = load_supporters()

        if not args[0] in list(supporters.keys()):
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               f"<@{args[0]}> is not in the system"))
            return

        try:
            del supporters[args[0]]
        except KeyError:
            pass

        update_supporters(supporters)

        await ctx.send(embed=discord.Embed(
            description=f"<@{args[0]}> removed from supporters list",
            color=discord.Color(0)))
        return
Example #3
0
    async def keegan(self, ctx, *args):
        user_id = ctx.message.author.id

        actions = {'setup': self.records_setup, 'update': self.records_update}

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

        if len(args) == 0:
            file_ = discord.File(
                TYPERACER_RECORDS_JSON,
                filename=f"typeracer_records_{self.last_updated.lower()}.json")
            await ctx.send(file=file_)
            return

        action = args[0].lower()
        try:
            action = actions[action]
        except KeyError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    f"Must provide a valid action: `{'`, `'.join(actions.keys())}`"
                ))
            return

        await action(ctx)
        return
Example #4
0
    async def calc(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        expression = urllib.parse.quote_plus(''.join(args))
        urls = [Urls().eval_math(expression)]

        try:
            response = await fetch(urls, 'json')
        except:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"Please provide a valid expression"))
            return

        embed = discord.Embed(title=f"`{''.join(args)}` =",
                              color=discord.Color(MAIN_COLOR),
                              description=f"```{response[0]}```")

        await ctx.send(embed=embed)
        return
Example #5
0
    async def clip(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        with open(CLIPS_JSON, 'r') as jsonfile:
            clips = json.load(jsonfile)

        if len(args) == 1:
            clip = args[0].lower()
            if clip == '*':
                await ctx.send(file=discord.File(CLIPS_JSON, f"clips.json"))
                return
            try:
                clip_url = clips[clip]
            except KeyError:
                calls = list(clips.keys())
                calls_ = ''
                for clip_ in calls:
                    calls_ += f"`{clip_}`, "
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        f"Must provide a valid clip: {calls_[:-2]}"))
                return

        await ctx.send(clip_url)
        return
Example #6
0
    async def setprefix(self, ctx, *args):
        if len(args) == 0:
            prefix = get_prefix(self.bot, ctx.message)
            await ctx.send(embed=discord.Embed(
                color=discord.Color(HELP_BLACK),
                title=f"The prefix is `{prefix}`",
                description=f"`{prefix}setprefix [prefix]`\n`{prefix}help`"))
            return
        elif len(args) > 1:
            await ctx.send(embed=Error(ctx, ctx.message).parameters(
                f"{ctx.invoked_with} <prefix>"))
            return

        prefix = args[0]

        if len(prefix) > 14:
            await ctx.send(
                f"<@{ctx.message.author.id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    '`prefix` can not be longer than 14 characters'))
            return

        prefixes = load_prefixes()
        prefixes[str(ctx.guild.id)] = prefix
        update_prefixes(prefixes)
        await ctx.send(embed=discord.Embed(title=f"Updated prefix to {prefix}",
                                           color=discord.Color(0)))
        return
Example #7
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
Example #8
0
    async def art(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        with open(ART_JSON, 'r') as jsonfile:
            works = json.load(jsonfile)

        artists = list(works.keys())
        if len(args) == 1:
            artist = args[0].lower()
            if artist == '*':
                await ctx.send(
                    file=discord.File(ART_JSON, f"typeracer_art.json"))
                return
            if artist not in artists:
                artists_ = ''
                for artist_ in artists:
                    artists_ += f"`{artist_}`, "
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        f"Must provide a valid artist: {artists_[:-2]}"))
                return
            works, trid = works[artist]['art'], works[artist]['trid']
            work = random.choice(works)
        else:
            works_ = []
            for key, value in works.items():
                for art_work in value['art']:
                    works_.append({
                        'artist': key,
                        'trid': value['trid'],
                        'title': art_work['title'],
                        'url': art_work['url']
                    })
            work = random.choice(works_)
            artist, trid = work['artist'], work['trid']

        title = work['title'] if work['title'] else "Untitled"

        embed = discord.Embed(title=title, color=discord.Color(MAIN_COLOR))
        embed.set_author(name=artist,
                         url=Urls().user(trid, 'play'),
                         icon_url=Urls().thumbnail(trid))
        embed.set_image(url=work['url'])

        await ctx.send(embed=embed)
        return
Example #9
0
    async def add_supporter(self, ctx, *args):
        if len(args) != 2: return

        try:
            int(args[0])
            if len(args[0]) > 18:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"**{args[0]}** is not a valid Discord ID"))
            return

        try:
            tier = int(args[1])
            if tier < 1 or tier > 4:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"Tier level must be between 1 and 4"))
            return

        supporters = load_supporters()

        if args[0] in list(supporters.keys()):
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               f"<@{args[0]}> already in system"))
            return

        supporters.update({
            args[0]: {
                'color': MAIN_COLOR,
                'tier': tier,
                'graph_color': {
                    'bg': None,
                    'graph_bg': None,
                    'axis': None,
                    'line': None,
                    'text': None,
                    'grid': None,
                    'cmap': None
                }
            }
        })

        update_supporters(supporters)

        await ctx.send(embed=discord.Embed(
            description=
            f"**Tier {tier}** supporter <@{args[0]}> added to the list",
            color=discord.Color(0)))
        return
Example #10
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
Example #11
0
    async def unixreference(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        if len(args) == 0:
            embed = discord.Embed(title="Unix Timestamp Conversions",
                                  color=discord.Color(MAIN_COLOR))
            embed.set_footer(text="All converted times are in UTC")
            embed.add_field(name="Times",
                            value=('1.25e9 - August 11, 2009\n'
                                   '1.30e9 - March 13, 2011\n'
                                   '1.35e9 - October 12, 2012\n'
                                   '1.40e9 - May 13, 2014\n'
                                   '1.45e9 - December 13, 2015\n'
                                   '1.50e9 - July 14, 2017\n'
                                   '1.55e9 - February 12, 2019\n'
                                   '1.60e9 - September 13, 2020\n'
                                   '1.65e9 - April 15, 2022'))
            await ctx.send(embed=embed)
            return

        try:
            time = int(args[0])
            await ctx.send(embed=discord.Embed(
                color=discord.Color(MAIN_COLOR),
                description=datetime.datetime.fromtimestamp(time).strftime(
                    "%B %d, %Y, %-I:%M:%S %p")))
            return
        except ValueError:
            try:
                scientific_notation_lst = args[0].lower().split('e')
                await ctx.send(embed=discord.Embed(
                    color=discord.Color(MAIN_COLOR),
                    description=datetime.datetime.fromtimestamp(
                        float(scientific_notation_lst[0]) *
                        10**(int(scientific_notation_lst[1]))).strftime(
                            "%B %d, %Y, %-I:%M:%S %p")))
                return
            except:
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).incorrect_format(
                        '`timestamp` must be an integer or scientific notation (e.g. 1.0365e9)'
                    ))
                return
Example #12
0
    async def setuniverse(self, ctx, *args):
        user_id = str(ctx.message.author.id)
        MAIN_COLOR = get_supporter(user_id)

        invalid = False

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

        if len(args) == 0:
            args = ('play', )
        universe = args[0].lower()
        if len(universe) > 50:
            invalid = True
        else:
            with open(UNIVERSES_FILE_PATH, 'r') as txtfile:
                universes = txtfile.read().split('\n')
            if not universe in universes:
                invalid = True
        if invalid:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               ('`universe` must be a [TypeRacer universe]'
                                '(http://typeracerdata.com/universes)')))
            return

        accounts = load_accounts()

        try:
            accounts[user_id]['universe'] = universe
        except KeyError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    'Discord account must be linked to TypeRacer account with '
                    f"`{get_prefix(ctx, ctx.message)}register [typeracer_username]`"
                )))
            return

        update_accounts(accounts)

        await ctx.send(embed=discord.Embed(
            color=discord.Color(MAIN_COLOR),
            description=
            (f"<@{user_id}> has been linked to the {href_universe(universe)} universe"
             )))
        return
Example #13
0
    async def register(self, ctx, *args):
        user_id = str(ctx.message.author.id)
        MAIN_COLOR = get_supporter(user_id)
        show_user_count = ctx.invoked_with[
            -1] == '*' and ctx.message.author.id in BOT_ADMIN_IDS

        invalid = False

        if len(args) != 1:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [typeracer_username]"))
            return
        player = args[0].lower()
        urls = [Urls().get_user(player, 'play')]
        try:
            test_response = await fetch(urls, 'json')
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    '`typeracer_username` must be a TypeRacer username'))
            return
        accounts = load_accounts()

        try:
            accounts[user_id]['main'] = player
        except KeyError:
            accounts.update({
                user_id: {
                    'main': player,
                    'alts': [],
                    'desslejusted': False,
                    'speed': 'lag',
                    'universe': 'play'
                }
            })

        update_accounts(accounts)

        user_count = ''
        if show_user_count:
            user_count = f"\n{len(accounts)} users registered"

        await ctx.send(embed=discord.Embed(
            color=discord.Color(MAIN_COLOR),
            description=(f"<@{user_id}> has been linked to [**{player}**]"
                         f"({Urls().user(player, 'play')}){user_count}")))
        return
Example #14
0
async def on_command_error(ctx, error):
    if isinstance(error, commands.CommandOnCooldown):
        await ctx.send(content=f"<@{ctx.message.author.id}>",
                       embed=Error(ctx, ctx.message).cooldown(
                           (f"Maximum number of `{ctx.invoked_with}`"
                            ' request(s) are running\nTry again later')))
        return
    if isinstance(error, commands.CheckFailure):
        await ctx.send(content=f"<@{ctx.message.author.id}>",
                       embed=Error(ctx, ctx.message).lacking_permissions(
                           'You lack the perms for that command'))
        return
    else:
        ctx.command.reset_cooldown(ctx)
        raise error
Example #15
0
    async def serverinfo(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        embed = discord.Embed(title=f"Server Information for {ctx.guild.name}",
                              color=discord.Color(MAIN_COLOR),
                              description=ctx.guild.description)
        embed.set_thumbnail(url=ctx.guild.icon_url)
        embed.add_field(
            name='Stats',
            value=(
                f"**Owner:** <@{ctx.guild.owner_id}>\n"
                f"**Region:** {ctx.guild.region}\n"
                f"**Created At:** {ctx.guild.created_at}\n"
                f"**Member Count:** {f'{ctx.guild.member_count:,}'}\n"
                f"**Text Channels:** {f'{len(ctx.guild.text_channels):,}'}\n"
                f"**Roles:** {f'{len(ctx.guild.roles):,}'}"))
        embed.set_image(url=ctx.guild.banner_url)

        await ctx.send(embed=embed)
        return
Example #16
0
    async def raceline(self, ctx, *args):
        user_id = ctx.message.author.id

        rl = ctx.invoked_with.lower() in ['raceline'] + get_aliases('raceline')
        pl = ctx.invoked_with.lower() in ['pointline'
                                          ] + get_aliases('pointline')

        units = 'Races' if rl else 'Points'
        retroactive = ctx.invoked_with[-1] == '*' and pl

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

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

        start, end = 0, 0
        if len(args) > 1:
            try:
                args[0].index('-')
                start = (
                    datetime.datetime.strptime(args[0], "%Y-%m-%d").date() -
                    datetime.date(1970, 1, 1)).total_seconds()
                if start <= 1_250_000_000 or start > time.time():
                    raise ValueError
                args = args[1:]
            except ValueError:
                pass
Example #17
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"))
Example #18
0
    async def searchid(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

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

        if args[0] == '*':
            tid = get_cached_id(ctx.message.channel.id)
            if not tid:
                tid = args[0]
        else:
            tid = args[0]
        urls = [Urls().text(tid)]

        text = await fetch(urls, 'read', scrape_text)
        if text[0]:
            cache_id(ctx.message.channel.id, tid)
            value_1 = f"\"{text[0]}\""
            value_2 = f" [{TR_INFO}]({urls[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 = discord.Embed(title=f"Search Result for {tid}",
                                  color=discord.Color(MAIN_COLOR))
            embed.add_field(name=f"Race Text ID: {tid}",
                            value=value,
                            inline=False)
            await ctx.send(embed=embed)
            return

        await ctx.send(content=f"<@{user_id}>",
                       embed=Error(ctx, ctx.message).incorrect_format(
                           f"**{tid}** is not a valid text ID"))
        return
Example #19
0
    async def upgrade_supporter(self, ctx, *args):
        if len(args) != 2: return

        try:
            int(args[0])
            if len(args[0]) > 18:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"**{args[0]}** is not a valid Discord ID"))
            return

        try:
            tier = int(args[1])
            if tier < 1 or tier > 4:
                raise ValueError
        except ValueError:
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"Tier level must be between 1 and 4"))
            return

        supporters = load_supporters()

        if not args[0] in list(supporters.keys()):
            await ctx.send(content=f"<@{ctx.message.author.id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               f"<@{args[0]}> is not in the system"))
            return

        supporters[args[0]]['tier'] = tier

        update_supporters(supporters)

        await ctx.send(embed=discord.Embed(
            description=f"<@{args[0]}> upgraded to **Tier {tier}**",
            color=discord.Color(0)))
        return
Example #20
0
    async def setcolor(self, ctx, *args):
        if len(args) > 1:
            await ctx.send(
                content=f"<@{ctx.message.author.id}>",
                embed=Error(
                    ctx,
                    ctx.message).parameters(f"{ctx.invoked_with} [hex_value]"))
            return

        if len(args) == 0:
            color = MAIN_COLOR

        elif len(args) == 1:
            try:
                color = int(f"0x{args[0]}", 16)
                if color < 0 or color > 16777216:
                    raise ValueError
            except ValueError:
                try:
                    colors = get_colors()
                    color = colors[args[0].lower()]
                except KeyError:
                    await ctx.send(
                        content=f"<@{ctx.message.author.id}>",
                        embed=Error(ctx, ctx.message).incorrect_format((
                            f"[**{args[0]}** is not a valid hex_value]"
                            '(https://www.w3schools.com/colors/colors_picker.asp)'
                        )))
                    return

        supporters = load_supporters()

        supporters[str(ctx.message.author.id)]['color'] = color

        update_supporters(supporters)

        await ctx.send(embed=discord.Embed(title='Color updated',
                                           color=discord.Color(color)))
        return
Example #21
0
    async def records_update(self, ctx):
        user_id = ctx.message.author.id

        try:
            updated_file_raw = ctx.message.attachments[0]
        except IndexError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'Please upload a file and comment the command call'))
            return

        try:
            updated_file = json.loads(await updated_file_raw.read())
        except json.JSONDecodeError:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'The uploaded file is not a properly formatted JSON file'))
            return

        with open(TYPERACER_RECORDS_JSON, 'w') as jsonfile:
            json.dump(updated_file, jsonfile, indent=4)

        try:
            await self.edit_record_messages()
        except NotImplementedError:
            await ctx.send(
                content=f"<@{ctx.message.author.id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    f"Must set-up the records with `{ctx.invoked_with} setup` first"
                ))
            return

        await ctx.send(embed=discord.Embed(title='Records Updated',
                                           color=discord.Color(0)))
        return
Example #22
0
    async def lastonline(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

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

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

        player = get_player(user_id, args[0])

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

        time_difference = time.time() - response

        await ctx.send(embed=discord.Embed(
            colour=discord.Colour(MAIN_COLOR),
            description=(
                f"**{player}** last played {seconds_to_text(time_difference)}\n"
                f"ago on the {href_universe(universe)} universe")))
        return
Example #23
0
    async def dicey(self, ctx, *args):
        question = ' '.join(args)

        if not question:
            await ctx.send(
                content=f"<@{ctx.message.author.id}>",
                embed=Error(
                    ctx,
                    ctx.message).incorrect_format('You must ask a question!'))
            return

        affirmative = [
            'It is certain.', 'It is decidedly so.', 'Without a doubt.',
            'Yes – definitely.', 'You may rely on it.', 'As I see it, yes.',
            'Most likely.', 'Outlook good.', 'Yes.', 'Signs point to yes.',
            'Absolutely', 'Of course.', 'For sure.', 'YES.',
            'By all means, yes.', 'Yeah, I\'d say so.', 'Totally.',
            'Clearly, yes.'
        ]

        uncertain = [
            'Reply hazy, try again.', 'Ask again later.',
            'Better not tell you now.', 'Cannot predict now.',
            'Concentrate and ask again.'
        ]

        negative = [
            'Don\'t count on it.', 'My reply is no.', 'My sources say no.',
            'Outlook not so good.', 'Very doubtful.', 'No.', 'Definitely not.',
            'Certainly not.', 'No way.', 'Definitely not.', 'Of course not.',
            'Nah.', 'Nope.', 'NO.', 'Are you stupid?', 'Obviously not.'
        ]

        category = random.randint(1, 100)
        if category == 1:
            await ctx.send(embed=discord.Embed(
                title='How am I supposed to know?'))
        elif category <= 41:
            await ctx.send(random.choice(affirmative))
        elif category <= 60:
            await ctx.send(random.choice(uncertain))
        else:
            await ctx.send(random.choice(negative))
        return
Example #24
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
Example #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
Example #26
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
Example #27
0
    async def adjustedgraph(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        text_length = len(race_text) > 9

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

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

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

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

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

        await ctx.send(file=file_, embed=embed)
        return
Example #28
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
Example #29
0
                    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

        text_data = load_texts_json()
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        data_x, data_y = [], []
Example #30
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