From 9e86472b64680f7fa552af970b2607111ff7c39c Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 10 Mar 2017 18:07:39 -0600 Subject: [PATCH] First implementation for the new osu for rewrite --- cogs/osu.py | 221 ++++++++++++++++--------------------------- cogs/utils/checks.py | 1 + 2 files changed, 84 insertions(+), 138 deletions(-) diff --git a/cogs/osu.py b/cogs/osu.py index 9618b26..89172c1 100644 --- a/cogs/osu.py +++ b/cogs/osu.py @@ -3,6 +3,8 @@ from . import utils from discord.ext import commands import discord +from osuapi import OsuApi, AHConnector + # https://github.com/ppy/osu-api/wiki BASE_URL = 'https://osu.ppy.sh/api/' MAX_RETRIES = 5 @@ -11,156 +13,99 @@ MAX_RETRIES = 5 class Osu: def __init__(self, bot): self.bot = bot - self.key = utils.osu_key - self.payload = {'k': self.key} + self.api = OsuApi(utils.osu_key, connector=AHConnector()) + self.bot.loop.create_task(get_users()) + self.osu_users = {} - async def find_beatmap(self, query): - """Finds a beatmap ID based on the first match of searching a beatmap""" - pass + async def get_user(self, member, username): + """A function used to get and save user data in cache""" + user = self.osu_users.get(member.id) + if user is None: + user = await self.get_user_from_api(username) + if user is not None: + self.osu_users[member.id] = user + return user + else: + if user.username.lower() == username.lower(): + return user + else: + user = await self.get_user_from_api(username) + if user is not None: + self.osu_users[member.id] = user + return user - async def get_beatmap(self, b_id): - """Gets beatmap info based on the ID provided""" - payload = self.payload.copy() - payload['b'] = b_id - url = BASE_URL + 'get_beatmaps' - data = await utils.request(url, payload=payload) + async def get_user_from_api(self, username): + """A simple helper function to parse the list given and handle failures""" + user = await self.api.get_user(username) try: - return data[0] - except (IndexError, TypeError): + return user[0] + except IndexError: return None + async def get_users(self): + """A task used to 'cache' all member's and their Osu profile's""" + data = await utils.get_content('osu') + if data is None: + return + + for result in data: + member = int(data['member_id']) + user = await self.get_user_from_api(data['osu_username']) + if user: + self.osu_users[member] = user + @commands.group(invoke_without_command=True) @utils.custom_perms(send_messages=True) - async def osu(self, ctx): - pass + async def osu(self, ctx, member=None): + """Provides basic information about a specific user - @osu.command(name='scores', aliases=['score']) - @utils.custom_perms(send_messages=True) - async def osu_user_scores(self, ctx, user, song=1): - """Used to get the top scores for a user - You can provide either your Osu ID or your username - However, if your username is only numbers, this will confuse the API - If you have only numbers in your username, you will need to provide your ID - This will by default return the top song, provide song [up to 100] to get that song, in order from best to worst + EXAMPLE: !osu @Person + RESULT: Informationa bout that person's osu account""" + if member is None: + member = ctx.message.author - EXAMPLE: !osu MyUsername 5 - RESULT: Info about your 5th best song""" - - await ctx.send("Looking up your Osu information...") - # To make this easy for the user, it's indexed starting at 1, so lets subtract by 1 - song -= 1 - # Make sure the song is not negative however, if so set to 0 - if song < 0: - song = 0 - - # A list of the possible values we'll receive, that we want to display - wanted_info = ['username', 'maxcombo', 'count300', 'count100', 'count50', 'countmiss', - 'perfect', 'enabled_mods', 'date', 'rank' 'pp', 'beatmap_title', 'beatmap_version', - 'max_combo', 'artist', 'difficulty'] - - # A couple of these aren't the best names to display, so setup a map to change these just a little bit - key_map = {'maxcombo': 'combo', - 'count300': '300 hits', - 'count100': '100 hits', - 'count50': '50 hits', - 'countmiss': 'misses', - 'perfect': 'got_max_combo'} - - payload = self.payload.copy() - payload['u'] = user - payload['limit'] = 100 - # The endpoint that we're accessing to get this information - url = BASE_URL + 'get_user_beat' - data = await utils.request(url, payload=payload) - - try: - data = data[song] - except (IndexError, TypeError): - if data is not None and len(data) == 0: - await ctx.send("I could not find any top songs for the user {}".format(user)) - return - else: - data = data[len(data) - 1] - - # There's a little bit more info that we need, some info specific to the beatmap. - # Due to this we'll need to make a second request - beatmap_data = await self.get_beatmap(data.get('beatmap_id', None)) - - # Lets add the extra data we want - data['beatmap_title'] = beatmap_data.get('title') - data['beatmap_version'] = beatmap_data.get('version') - data['max_combo'] = beatmap_data.get('max_combo') - data['artist'] = beatmap_data.get('artist') - # Lets round this, no need for such a long number - data['difficulty'] = round(float(beatmap_data.get('difficultyrating')), 2) - - # Now lets create our dictionary needed to create the image - # The dict comprehension we're using is simpler than it looks, it's simply they key: value - # If the key is in our wanted_info list - # We also get the wanted value from the key_map if it exists, using the key itself if it doesn't - # We then title it and replace _ with a space to ensure nice formatting - fmt = [(key_map.get(k, k).title().replace('_', ' '), v) for k, v in data.items() if k in wanted_info] - - # Attempt to create our banner and upload that - # If we can't find the images needed, or don't have permissions, just send a message instead - try: - banner = await utils.create_banner(ctx.message.author, "Osu User Stats", fmt) - await self.bot.upload(banner) - except (FileNotFoundError, discord.Forbidden): - _fmt = "\n".join("{}: {}".format(k, r) for k, r in fmt) - await ctx.send("```\n{}```".format(_fmt)) - - @osu.command(name='user', pass_context=True) - @utils.custom_perms(send_messages=True) - async def osu_user_info(self, ctx, *, user): - """Used to get information about a specific user - You can provide either your Osu ID or your username - However, if your username is only numbers, this will confuse the API - If you have only numbers in your username, you will need to provide your ID - - EXAMPLE: !osu user MyUserName - RESULT: Info about your user""" - - await ctx.send("Looking up your Osu information...") - # A list of the possible values we'll receive, that we want to display - wanted_info = ['username', 'playcount', 'ranked_score', 'pp_rank', 'level', 'pp_country_rank', - 'accuracy', 'country', 'pp_country_rank', 'count_rank_s', 'count_rank_a'] - - # A couple of these aren't the best names to display, so setup a map to change these just a little bit - key_map = {'playcount': 'play_count', - 'count_rank_ss': 'total_SS_ranks', - 'count_rank_s': 'total_s_ranks', - 'count_rank_a': 'total_a_ranks'} - - # The paramaters that we'll send to osu to get the information needed - payload = self.payload.copy() - payload['u'] = user - # The endpoint that we're accessing to get this information - url = BASE_URL + 'get_user' - data = await utils.request(url, payload=payload) - - # Make sure we found a result, we should only find one with the way we're searching - try: - data = data[0] - except (IndexError, TypeError): - await ctx.send("I could not find anyone with the user name/id of {}".format(user)) + user = self.osu_users[member.id] + if user is None: + await ctx.send("I do not have {}'s Osu user saved!".format(member.display_name)) return - # Now lets create our dictionary needed to create the image - # The dict comprehension we're using is simpler than it looks, it's simply they key: value - # If the key is in our wanted_info list - # We also get the wanted value from the key_map if it exists, using the key itself if it doesn't - # We then title it and replace _ with a space to ensure nice formatting - fmt = {key_map.get(k, k).title().replace('_', ' '): v for k, v in data.items() if k in wanted_info} + e = discord.Embed(title='Osu profile for {}'.format(user.username)) + e.set_thumbnail(url='https://a.ppy.sh/{}'.format(user_id)) + e.add_field(name='Rank', value=user.pp_rank, inline=False) + e.add_field(name='Level', value=user.level, inline=False) + e.add_field(name='Performance Points', value=user.pp_raw, inline=False) + e.add_field(name='Accuracy', value=user.accuracy, inline=False) + e.add_field(name='SS Ranks', value=user.count_rank_ss, inline=False) + e.add_field(name='S Ranks', value=user.count_rank_s, inline=False) + e.add_field(name='A Ranks', value=user.count_rank_a, inline=False) + e.add_field(name='Country', value=user.country, inline=False) + e.add_field(name='Country Rank', value=user.pp_country_rank, inline=False) + e.add_field(name='Playcount', value=user.playcount, inline=False) + e.add_field(name='Ranked Score', value=user.ranked_score, inline=False) + e.add_field(name='Total Score', value=user.total_score, inline=False) - # Attempt to create our banner and upload that - # If we can't find the images needed, or don't have permissions, just send a message instead - try: - banner = await utils.create_banner(ctx.message.author, "Osu User Stats", fmt) - await ctx.send(file=banner) - except (FileNotFoundError, discord.Forbidden): - _fmt = "\n".join("{}: {}".format(k, r) for k, r in fmt.items()) - await ctx.send("```\n{}```".format(_fmt)) + @osu.command(aliases=['create', 'connect']) + @utils.custom_perms(send_messages=True) + async def osu_add(self, ctx, *, username): + """Links an osu account to your discord account + + EXAMPLE: !osu add username + RESULT: Links your username to your account, and allows stats to be pulled from it""" + author = ctx.message.author + user = await self.get_user(author, username) + if user is None: + await ctx.send("I couldn't find an osu user that matches {}".format(username)) + else + + entry = { + 'member_id': str(author.id), + 'osu_username': user.username + } + + if not await utils.add_content('osu', entry): + await utils.update_content('osu', entry, str(author.id)) + + await ctx.send("I have just saved your Osu user {}".format(author.display_name)) def setup(bot): diff --git a/cogs/utils/checks.py b/cogs/utils/checks.py index da587d9..4f3cbb1 100644 --- a/cogs/utils/checks.py +++ b/cogs/utils/checks.py @@ -19,6 +19,7 @@ required_tables = { 'server_settings': 'server_id', 'raffles': 'id', 'strawpolls': 'server_id', + 'osu', 'member_id', 'tags': 'server_id', 'tictactoe': 'member_id', 'twitch': 'member_id'