async def who(ctx: commands.Context, username: str) -> None: user = get_user(username, bot) if user is not None: info = get_user_info(user) add_footer(info.embed, ctx.message.author) await info.send(ctx) return # Look for nearest matches, if they exist users = bot.get_guild(guild_id).members # type: List[discord.Member] # Just using sequencematcher because its simple and no need to install extra Library # If keen on better distrance metrics, look at installing Jellyfish or Fuzzy Wuzzy similarities = [(member, max( SequenceMatcher(None, username.lower(), member.display_name.lower()).ratio(), SequenceMatcher(None, username.lower(), member.name.lower()).ratio())) for member in users] similarities.sort(key=lambda tup: tup[1], reverse=True) # unlikely to get 5 with >70% match anyway... top_matches = [x for x in similarities[:5] if x[1] > 0.7] # type: List[Tuple[discord.Member, float]] uids = [x[0].id for x in top_matches] infos = requests.get("https://dev.openstudyroom.org/league/discord-api/", params={ 'uids': uids }).json() # Split and recombine so that OSR members appear top of list osr_members = [ x for x in top_matches if infos.get(str(x[0].id)) is not None ] not_osr_members = [x for x in top_matches if x not in osr_members] top_matches = osr_members + not_osr_members message = '' for _i, x in enumerate(top_matches): message += '\n{}\N{COMBINING ENCLOSING KEYCAP}**{}**#{} {}'.format( _i + 1, x[0].display_name, x[0].discriminator, user_rank(x[0], infos)) if username in roles_dict: message += "\n\n However, `" + username + "` is a valid role. Did you mean `!list " + username + "`?" nearest_or_sorry = '", nearest matches:' if top_matches else '", sorry' embed = discord.Embed(description=message, title='No users by the exact name "' + username + nearest_or_sorry) add_footer(embed, ctx.message.author) msg = await ctx.send(embed=embed) for _i, match in enumerate( top_matches): # type: Tuple[int, Tuple[discord.Member, float]] await msg.add_reaction(str(_i + 1) + '\N{COMBINING ENCLOSING KEYCAP}') SPECIAL_MESSAGES[msg.id] = WhoMessage(msg, ctx.message.author, [x[0] for x in top_matches])
async def rank(ctx: commands.Context, username: str = None) -> None: """Show rank graphs for OGS and KGS servers.""" if username is None: last_message = await ctx.message.channel.history(limit=1).flatten() user = last_message[0].author else: user = get_user(username, bot) if user is not None: infos = requests.get("https://openstudyroom.org/league/discord-api/", params={ 'uids': [user.id] }).json() info = infos.get(str(user.id)) if info is not None: kgs_username = info.get('kgs_username') ogs_username = info.get('ogs_username') ogs_id = info.get('ogs_id') if kgs_username is not None: embed = discord.Embed(title="KGS rank history for " + str(username), color=0xeee657) embed.set_image(url="http://www.gokgs.com/servlet/graph/" + kgs_username + "-en_US.png") add_footer(embed, ctx.author) await ctx.send(embed=embed) if ogs_username is not None: def format_gorank(value): if value == 0: return "1d" elif value > 0: return str(int(value)) + "k" elif value < 0: return str(1 + abs(int(value))) + "d" r = requests.get( 'https://online-go.com/termination-api/player/' + str(ogs_id) + '/rating-history?speed=overall&size=0') rl = r.text.split('\n') rank = [] dates = [] for game in range(1, len(rl) - 1): rank.append(30 - (31.25) * math.log(float(rl[game].split('\t')[4]) / 850)) dates.append( datetime.utcfromtimestamp(int( rl[game].split('\t')[0])).strftime('%d/%m/%Y')) x = [datetime.strptime(d, '%d/%m/%Y').date() for d in dates] y = range(len(x)) fig, ax = plt.subplots(nrows=1, ncols=1) plt.plot(x, rank, color=(0, 194 / 255, 0)) ax.xaxis.set_major_formatter(mdates.DateFormatter('\n%Y')) ax.xaxis.set_major_locator(mdates.YearLocator()) ax.invert_yaxis() fig.canvas.draw() labels = [ format_gorank(float(item.get_text().replace('−', '-'))) for item in ax.get_yticklabels() ] ax.set_yticklabels(labels) fig.patch.set_facecolor((236 / 255, 236 / 255, 176 / 255)) ax.patch.set_facecolor('black') ax.yaxis.grid(linewidth=0.2) plt.title("OGS Rank history for " + ogs_username) fig.savefig('Rank.png', bbox_inches='tight') file = discord.File('Rank.png', filename="OGS Rank history for " + ogs_username + ".png") await ctx.send(file=file) os.remove('Rank.png') return # Look for nearest matches, if they exist users = bot.get_guild(guild_id).members # type: List[discord.Member] # Just using sequencematcher because its simple and no need to install extra Library # If keen on better distrance metrics, look at installing Jellyfish or Fuzzy Wuzzy similarities = [(member, max( SequenceMatcher(None, username.lower(), member.display_name.lower()).ratio(), SequenceMatcher(None, username.lower(), member.name.lower()).ratio())) for member in users] similarities.sort(key=lambda tup: tup[1], reverse=True) # unlikely to get 5 with >70% match anyway... top_matches = [x for x in similarities[:5] if x[1] > 0.7] # type: List[Tuple[discord.Member, float]] uids = [x[0].id for x in top_matches] infos = requests.get("https://openstudyroom.org/league/discord-api/", params={ 'uids': uids }).json() # Split and recombine so that OSR members appear top of list osr_members = [ x for x in top_matches if infos.get(str(x[0].id)) is not None ] not_osr_members = [x for x in top_matches if x not in osr_members] top_matches = osr_members + not_osr_members message = '' for _i, x in enumerate(top_matches): message += '\n{}\N{COMBINING ENCLOSING KEYCAP}**{}**#{} {}'.format( _i + 1, x[0].display_name, x[0].discriminator, user_rank(x[0], infos)) if username in roles_dict: message += "\n\n However, `" + username + "` is a valid role. Did you mean `!list " + username + "`?" nearest_or_sorry = '", nearest matches:' if top_matches else '", sorry' embed = discord.Embed(description=message, title='No users by the exact name "' + username + nearest_or_sorry) add_footer(embed, ctx.message.author) msg = await ctx.send(embed=embed)