First implementation for the new osu for rewrite
This commit is contained in:
parent
8699b0e303
commit
9e86472b64
221
cogs/osu.py
221
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):
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue