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
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
async def maintain_top_tens(): tids = [] with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile: reader = csv.reader(csvfile) next(reader) for row in reader: tids.append(row[0]) def scraper(response): soup = BeautifulSoup(response, 'html.parser') top_10 = soup.findAll('a', class_="userProfileTextLink") top_10_dict = {} for i in range(0, len(top_10)): top_10_dict.update({i + 1: top_10[i].text}) return top_10_dict text_top_tens = {} urls, partition = [], [] for i, tid in enumerate(tids): partition.append(Urls().tr_text(tid)) if i % 10 == 0: urls.append(partition) partition = [] for partition in urls: print(partition[0]) data = (await fetch(partition, 'text', scraper, lambda x: re.findall('[0-9]+', x)[0])) for text in data: text_top_tens.update(text) await asyncio.sleep(25) with open(TOPTENS_JSON_FILE_PATH, 'w') as jsonfile: json.dump(text_top_tens, jsonfile) with open(TOPTENS_JSON_FILE_PATH, 'r') as jsonfile: text_top_tens = json.load(jsonfile) player_top_tens = {} for value in text_top_tens.values(): for i in range(1, 11): try: player = value[str(i)] try: current = player_top_tens[player] current.update({i: current[i] + 1}) except KeyError: temp = {} for j in range(1, 11): if j == i: temp.update({j: 1}) else: temp.update({j: 0}) player_top_tens.update({player: temp}) except KeyError: pass player_top_tens.update({'last updated': time.time()}) with open(TOPTENS_FILE_PATH, 'w') as jsonfile: json.dump(player_top_tens, jsonfile)
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
async def get_registered(player, universe, start, end): urls = [Urls().get_races(player, universe, start, end)] try: api_response = await fetch(urls, 'json') except: return [] api_response = api_response[0] if len(api_response) < 1000: return api_response else: return api_response + await get_registered( player, universe, start, float(api_response[-1]['t']) - 0.01)
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
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
async def check_if_leader(self, user, kind): urls = [Urls().get_competition(1, kind, 'points', 'play')] competition = await fetch(urls, 'json') competition = competition[0] return competition[0][1]['typeracerUid'][3:] == user
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
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
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
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
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
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
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
async def leaderboard(self, ctx, *args): user_id = ctx.message.author.id MAIN_COLOR = get_supporter(user_id) num_lb = 10 error_one = Error(ctx, ctx.message) \ .parameters(f"{ctx.invoked_with} [races/points/textbests/textstyped/toptens] <num>") error_two = Error(ctx, ctx.message) \ .incorrect_format('`num` must be a positive integer between 1 and 10') if len(args) == 0: await ctx.send(content=f"<@{user_id}>", embed=error_one) return elif len(args) == 2: if args[0].lower() != "toptens": await ctx.send(content=f"<@{user_id}>", embed=error_one) return try: num_lb = int(args[1]) if num_lb < 1 or num_lb > 10: await ctx.send(content=f"<@{user_id}>", embed=error_two) return except ValueError: await ctx.send(content=f"<@{user_id}>", embed=error_two) return elif len(args) > 2: await ctx.send(content=f"<@{user_id}>", embed=error_one) return category_dict = { 'races': 'races', 'points': 'points', 'textbests': 'wpm_textbests', 'textstyped': 'texts_raced', 'toptens': 'toptens' } try: category = category_dict[args[0].lower()] urls = [Urls().leaders(category)] except KeyError: await ctx.send(content=f"<@{user_id}>", embed=error_two) return def helper_formatter(player, country, parameter, index, *args): formatted = NUMBERS[index - 1] if country: formatted += f":flag_{country}: " else: formatted += '<:flagblank:744520567113252926> ' if isinstance(parameter, str): formatted += f"{player} - {parameter}\n" return formatted if args: formatted += f"{player} - {f'{parameter:,}'} WPM\n" return formatted formatted += f"{player} - {f'{round(parameter):,}'}\n" return formatted value = '' if category == 'races': name = '**Races Leaderboard**' elif category == 'points': name = '**Points Leaderboard**' elif category == 'wpm_textbests': name = '**Text Bests Leaderboard**' elif category == 'texts_raced': name = '**Texts Raced Leaderboard**' top_players = [] if category != 'toptens': response = await fetch(urls, 'read') soup = BeautifulSoup(response[0], 'html.parser') rows = soup.select('table')[0].select('tr') for i in range(1, 16): player = rows[i].select('td')[1].select('a')[0]['href'][18:] player_urls = [Urls().get_user(player, 'play')] player_response = await fetch(player_urls, 'json') player_response = player_response[0] country = player_response['country'] if category == 'races': parameter = int(player_response['tstats']['cg']) elif category == 'points': parameter = float(player_response['tstats']['points']) elif category == 'wpm_textbests': parameter = float(rows[i].select('td')[2].text) elif category == 'texts_raced': parameter = rows[i].select('td')[4].text top_players.append([player, parameter, country]) if category != 'texts_raced': top_players = sorted(top_players, key=lambda x: x[1], reverse=True) if category != 'wpm_textbests': for i in range(0, 10): player_info = top_players[i] value += helper_formatter(player_info[0], player_info[2], player_info[1], i + 1) else: for i in range(0, 10): player_info = top_players[i] value += helper_formatter(player_info[0], player_info[2], player_info[1], i + 1, True) else: with open(TOPTENS_FILE_PATH, 'r') as jsonfile: player_top_tens = json.load(jsonfile) last_updated = float(player_top_tens['last updated']) del player_top_tens['last updated'] for player, top_tens in player_top_tens.items(): top_count = 0 top_tens_values = list(top_tens.values()) for count in range(0, num_lb): top_count += int(top_tens_values[count]) top_players.append([player, top_count]) top_players = [player for player in top_players if player[1] != 0] top_players = sorted(top_players, key=lambda x: x[1]) players_with_top_tens = len(top_players) value += f"{f'{players_with_top_tens:,}'} players have top {num_lb}s\n" for i in range(0, 10): num = NUMBERS[i] value += (f"{num} {top_players[-(i + 1)][0]} " f"- {f'{top_players[-(i + 1)][1]:,}'}\n") value = value[:-1] name = { 1: 'Ones', 2: 'Twos', 3: 'Threes', 4: 'Fours', 5: 'Fives', 6: 'Sixes', 7: 'Sevens', 8: 'Eights', 9: 'Nines', 10: 'Tens' } name = f"Text Top {name[num_lb]}" embed = discord.Embed(color=discord.Color(MAIN_COLOR)) embed.add_field(name=name, value=value) if category == 'wpm_textbests': embed.set_footer( text= 'All users have at least 1,000 races and \n 400 texts typed') elif category == 'toptens': embed.set_footer( text=("Text top 10 data was last updated\n" f"{seconds_to_text(time.time() - last_updated)} ago")) await ctx.send(embed=embed) if category != 'toptens': urls = [] for player in top_players: urls.append(Urls().trd_import(player)) await fetch(urls, 'text') return
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
async def find_registered(player, universe, gn, timestamp): urls = [Urls().get_races(player, universe, timestamp - 1, timestamp + 1)] api_response = await fetch(urls, 'json') for race in api_response[0]: if race['gn'] == gn: return race
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
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
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
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
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
async def stats(self, ctx, *args): user_id = ctx.message.author.id MAIN_COLOR = get_supporter(user_id) account = account_information(user_id) universe = account['universe'] if len(args) == 0: args = check_account(user_id)(args) if len(args) != 1: await ctx.send( content=f"<@{user_id}>", embed=Error( ctx, ctx.message).parameters(f"{ctx.invoked_with} [user]")) return player = get_player(user_id, args[0]) urls = [Urls().get_user(player, universe)] try: user_api = (await fetch(urls, 'json'))[0] except: await ctx.send(content=f"<@{user_id}>", embed=Error(ctx, ctx.message).missing_information( (f"[**{player}**]({urls[0]}) " "doesn't exist"))) return country = f":flag_{user_api['country']}: " if user_api[ 'country'] else '' name = user_api['name'] if user_api['name'] else '' name += ' ' if user_api['name'] and user_api['lastName'] else '' name += user_api['lastName'] if user_api['lastName'] else '' premium = 'Premium' if user_api['premium'] else 'Basic' try: banned = user_api['tstats']['disqualified'] banned = '' if banned == 'false' or not banned else '\n**Status:** Banned' except KeyError: banned = '' urls = [Urls().trd_user(player, universe)] try: if universe != 'play': raise NotImplementedError trd_user_api = (await fetch(urls, 'json'))[0] textbests = round(float(trd_user_api['account']['wpm_textbests']), 2) textsraced = trd_user_api['account']['texts_raced'] extra_stats = (f"**Text Bests: **{textbests} WPM\n" f"**Texts Typed: **{textsraced}\n") except: textbests, textsraced, extra_stats = ('', ) * 3 urls = [Urls().user(player, universe)] try: response = (await fetch(urls, 'text'))[0] soup = BeautifulSoup(response, 'html.parser') rows = soup.select("table[class='profileDetailsTable']")[0].select( 'tr') medal_count = 0 for row in rows: cells = row.select('td') if len(cells) < 2: continue if cells[0].text.strip() == 'Racing Since': date_joined = cells[1].text.strip() rows = soup.select("table[class='personalInfoTable']")[0].select( 'tr') for row in rows: cells = row.select('td') if len(cells) < 2: continue if cells[0].text.strip() == 'Awards': medal_count = len(cells[1].select('a')) except: await ctx.send(content=f"<@{user_id}>", embed=Error(ctx, ctx.message).missing_information( (f"[**{player}**]({urls[0]}) " "doesn't exist"))) return if banned: color = 0xe0001a else: color = MAIN_COLOR embed = discord.Embed( title=f"{country}{player}", colour=discord.Colour(color), description=f"**Universe:** {href_universe(universe)}", url=urls[0]) embed.set_thumbnail(url=Urls().thumbnail(player)) embed.add_field(name="General", value=(f"**Name:** {name}\n" f"**Joined: **{date_joined}\n" f"**Membership: **{premium}{banned}"), inline=False) embed.add_field( name="Stats", value= (f"""**Races: **{f"{user_api['tstats']['cg']:,}"}\n""" f"""**Races Won: **{f"{user_api['tstats']['gamesWon']:,}"}\n""" f"""**Points: **{f"{round(user_api['tstats']['points']):,}"}\n""" f"""**Full Average: **{round(user_api['tstats']['wpm'], 2)} WPM\n""" f"""**Fastest Race: **{round(user_api['tstats']['bestGameWpm'], 2)} WPM\n""" f"""**Captcha Speed: **{round(user_api['tstats']['certWpm'], 2)} WPM\n""" f"""{extra_stats}**Medals: **{f'{medal_count:,}'}\n"""), inline=False) await ctx.send(embed=embed) await fetch([Urls().trd_import(player)], 'text') return
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
async def competition(self, ctx, *args): user_id = ctx.message.author.id MAIN_COLOR = get_supporter(user_id) account = account_information(user_id) universe = account['universe'] categories = { 'races': 'gamesFinished', 'points': 'points', 'wpm': 'wpm' } try: if len(args) == 0: sort, category = 'day', 'points' elif len(args) == 1: param = args[0].lower() if param in categories.keys(): sort, category = 'day', categories[param] elif param in ['day', 'week', 'month', 'year']: sort, category = param, 'points' else: raise KeyError elif len(args) == 2: sort, category = args[0].lower(), categories[args[1].lower()] else: raise ValueError except KeyError: await ctx.send( content=f"<@{user_id}>", embed=Error(ctx, ctx.message).incorrect_format( 'Must provide a valid category: `races/points/wpm`')) return except ValueError: await ctx.send(content=f"<@{user_id}>", embed=Error(ctx, ctx.message).parameters( f"{ctx.invoked_with} [races/points/wpm]")) return urls = [Urls().get_competition(12, sort, category, universe)] competition = await fetch(urls, 'json') competition = competition[0] def helper_formatter(player, country, points, races, wpm_sum, index): formatted = NUMBERS[index] if country: formatted += f":flag_{country}: " else: formatted += "<:flagblank:744520567113252926> " formatted += f"{player} - {f'{points:,}'} | {f'{races:,}'} | {f'{round(wpm_sum / races, 2):,}'} WPM\n" return formatted players = [] if sort == 'day': conn = sqlite3.connect(TEMPORARY_DATABASE_PATH) c = conn.cursor() for competitor in competition: player = competitor[1]['typeracerUid'][3:] if competitor[0]['country']: country = competitor[0]['country'] else: country = '' today_timestamp = (datetime.datetime.utcnow().date() \ - datetime.date(1970, 1, 1)).total_seconds() file_name = f"t_{player}_{universe}_{today_timestamp}_{today_timestamp + 86400}".replace( '.', '_') try: user_data = c.execute( f"SELECT * FROM {file_name} ORDER BY gn DESC LIMIT 1") last_race = user_data.fetchone() time_stamp = last_race[1] + 0.01 except sqlite3.OperationalError: time_stamp = today_timestamp c.execute( f"CREATE TABLE {file_name} (gn integer PRIMARY KEY, t, tid, wpm, pts)" ) races = await fetch_data(player, universe, time_stamp, today_timestamp + 86400) if races: c.executemany( f"INSERT INTO {file_name} VALUES (?, ?, ?, ?, ?)", races) conn.commit() points, wpm = [], [] data = c.execute(f"SELECT * FROM {file_name}").fetchall() for row in data: points.append(row[4]) wpm.append(row[3]) players.append([ player, country, round(sum(points)), len(points), round(sum(wpm), 2) ]) conn.close() else: for competitor in competition: player = competitor[1]['typeracerUid'][3:] if competitor[0]['country']: country = competitor[0]['country'] else: country = '' comp_stats = competitor[1] players.append([ player, country, round(comp_stats['points']), round(comp_stats['gamesFinished']), comp_stats['gamesFinished'] * comp_stats['wpm'] ]) if category == 'points': players = sorted(players, key=lambda x: x[2])[-10:][::-1] elif category == 'gamesFinished': players = sorted(players, key=lambda x: x[3])[-10:][::-1] elif category == 'wpm': players = sorted(players, key=lambda x: x[4] / x[3])[-10:][::-1] value = '' for i in range(0, 10): player = players[i] value += helper_formatter(player[0], player[1], player[2], player[3], player[4], i) today = datetime.datetime.utcnow().date() if sort == 'day': date = today.strftime('%B %-d, %Y') elif sort == 'week': normalizer = today.isocalendar()[2] start_time = today - datetime.timedelta(days=normalizer - 1) end_time = today + datetime.timedelta(days=7 - normalizer) date = f"{start_time.strftime('%B %-d')}—{end_time.strftime('%-d, %Y')}" elif sort == 'month': date = today.strftime('%B %Y') elif sort == 'year': date = today.strftime('%Y') formatted_sort = { 'day': 'Daily', 'week': 'Weekly', 'month': 'Monthly', 'year': 'Yearly' }[sort] formatted_category = { 'points': 'points', 'gamesFinished': 'races', 'wpm': 'wpm' }[category] embed = discord.Embed( title=f"{formatted_sort} Competition ({formatted_category})", color=discord.Color(MAIN_COLOR), description=f"**Universe:** {href_universe(universe)}", url=Urls().competition(sort, category, '', universe)) embed.add_field(name=date, value=value) await ctx.send(embed=embed) return
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
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
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 }
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