Skip to content
This repository was archived by the owner on Feb 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions cogs/osu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""The isobot cog file for osu! commands."""

# Imports
import discord
from ossapi import *
from discord import option, ApplicationContext
from discord.ext import commands

# Commands
class Osu(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.api = OssapiV2(13110, 'UDGR1XA2e406y163lRzzJgs4tQCvu94ehbkXU8w2')

@commands.slash_command(
name="osu_user",
description="View information on an osu! player."
)
@option(name="user", description="The name of the user", type=str)
async def osu_user(self, ctx, *, user:str):
try:
compact_user = self.api.search(query=user).users.data[0]
e = discord.Embed(title=f'osu! stats for {user}', color=0xff66aa)
e.set_thumbnail(url='https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Osu%21_Logo_2016.svg/2048px-Osu%21_Logo_2016.svg.png')
e.add_field(name='Rank (Global)', value=f'#{compact_user.expand().statistics.global_rank}')
e.add_field(name='Rank (Country)', value=f'#{compact_user.expand().statistics.country_rank}')
e.add_field(name='Ranked Score', value=f'{compact_user.expand().statistics.ranked_score}')
e.add_field(name='Level', value=f'Level {compact_user.expand().statistics.level.current} ({compact_user.expand().statistics.level.progress}% progress)')
e.add_field(name='pp', value=round(compact_user.expand().statistics.pp))
e.add_field(name='Max Combo', value=f'{compact_user.expand().statistics.maximum_combo}x')
e.add_field(name='Total Hits', value=f'{compact_user.expand().statistics.total_hits}')
e.add_field(name='Play Count', value=compact_user.expand().statistics.play_count)
e.add_field(name='Accuracy', value=f'{round(compact_user.expand().statistics.hit_accuracy, 2)}%')
e.add_field(name='Replays Watched by Others', value=f'{compact_user.expand().statistics.replays_watched_by_others}')
await ctx.respond(embed=e)
except: await ctx.respond(f':warning: {user} was not found in osu!.', ephemeral=True)

@commands.slash_command(
name="osu_beatmap",
description="View information on an osu! beatmap."
)
@option(name="query", description="The beatmap's id", type=int)
async def osu_beatmap(self, ctx, *, query:int):
try:
beatmap = self.api.beatmap(beatmap_id=query)
e = discord.Embed(title=f'osu! beatmap info for {beatmap.expand()._beatmapset.title} ({beatmap.expand()._beatmapset.title_unicode})', color=0xff66aa)
e.set_thumbnail(url='https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Osu%21_Logo_2016.svg/2048px-Osu%21_Logo_2016.svg.png')
#.beatmap.data[0]
e.add_field(name='Artist', value=f'{beatmap.expand()._beatmapset.artist} ({beatmap.expand()._beatmapset.artist_unicode})')
e.add_field(name='Mapper', value=beatmap.expand()._beatmapset.creator)
e.add_field(name='Difficulty', value=f'{beatmap.expand().difficulty_rating} stars')
e.add_field(name='BPM', value=beatmap.expand().bpm)
e.add_field(name='Circles', value=beatmap.expand().count_circles)
e.add_field(name='Sliders', value=beatmap.expand().count_sliders)
e.add_field(name='HP Drain', value=beatmap.expand().drain)
await ctx.respond(embed=e)
except Exception as f: await ctx.respond(f"An error occured when trying to execute this command.\n```{f.__type__()}: {f}```", ephemeral=True)

# Cog Initialization
def setup(bot): bot.add_cog(Osu(bot))
674 changes: 674 additions & 0 deletions oss-licenses/ossapi_LICENSE

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions ossapi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import logging
# we need to explicitly set a handler for the logging module to be happy
handler = logging.StreamHandler()
logging.getLogger("ossapi").addHandler(handler)

from ossapi.ossapi import (Ossapi, ReplayUnavailableException,
InvalidKeyException, APIException)
from ossapi.ossapiv2 import OssapiV2, Grant, Scope
from ossapi.models import (Beatmap, BeatmapCompact, BeatmapUserScore,
ForumTopicAndPosts, Search, CommentBundle, Cursor, Score,
BeatmapsetSearchResult, ModdingHistoryEventsBundle, User, Rankings,
BeatmapScores, KudosuHistory, Beatmapset, BeatmapPlaycount, Spotlight,
Spotlights, WikiPage, _Event, Event, BeatmapsetDiscussionPosts, Build,
ChangelogListing, MultiplayerScores, MultiplayerScoresCursor,
BeatmapsetDiscussionVotes, CreatePMResponse, BeatmapsetDiscussions,
UserCompact, BeatmapsetCompact)
from ossapi.enums import (GameMode, ScoreType, RankingFilter, RankingType,
UserBeatmapType, BeatmapDiscussionPostSort, UserLookupKey,
BeatmapsetEventType, CommentableType, CommentSort, ForumTopicSort,
SearchMode, MultiplayerScoresSort, BeatmapsetDiscussionVote,
BeatmapsetDiscussionVoteSort, BeatmapsetStatus, MessageType)
from ossapi.mod import Mod
from ossapi.replay import Replay
from ossapi.version import __version__
from ossapi.encoder import ModelEncoder, serialize_model

from oauthlib.oauth2 import AccessDeniedError, TokenExpiredError
from oauthlib.oauth2.rfc6749.errors import InsufficientScopeError


__all__ = [
# OssapiV1
"Ossapi", "ReplayUnavailableException", "InvalidKeyException",
"APIException",
# OssapiV2 core
"OssapiV2", "Grant", "Scope",
# OssapiV2 models
"Beatmap", "BeatmapCompact", "BeatmapUserScore", "ForumTopicAndPosts",
"Search", "CommentBundle", "Cursor", "Score", "BeatmapsetSearchResult",
"ModdingHistoryEventsBundle", "User", "Rankings", "BeatmapScores",
"KudosuHistory", "Beatmapset", "BeatmapPlaycount", "Spotlight",
"Spotlights", "WikiPage", "_Event", "Event", "BeatmapsetDiscussionPosts",
"Build", "ChangelogListing", "MultiplayerScores", "MultiplayerScoresCursor",
"BeatmapsetDiscussionVotes", "CreatePMResponse",
"BeatmapsetDiscussions", "UserCompact", "BeatmapsetCompact",
# OssapiV2 enums
"GameMode", "ScoreType", "RankingFilter", "RankingType",
"UserBeatmapType", "BeatmapDiscussionPostSort", "UserLookupKey",
"BeatmapsetEventType", "CommentableType", "CommentSort", "ForumTopicSort",
"SearchMode", "MultiplayerScoresSort", "BeatmapsetDiscussionVote",
"BeatmapsetDiscussionVoteSort", "BeatmapsetStatus", "MessageType",
# OssapiV2 exceptions
"AccessDeniedError", "TokenExpiredError", "InsufficientScopeError",
# misc
"Mod", "Replay", "__version__", "ModelEncoder",
"serialize_model"
]
32 changes: 32 additions & 0 deletions ossapi/encoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import json
from json import JSONEncoder
from datetime import datetime
from enum import Enum

from ossapi.models import Model
from ossapi.mod import Mod

class ModelEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return 1000 * int(o.timestamp())
if isinstance(o, Enum):
return o.value
if isinstance(o, Mod):
return o.value

to_serialize = {}
if isinstance(o, Model):
for name, value in o.__dict__.items():
# don't seriailize private attributes, like ``_api``.
if name.startswith("_"):
continue
to_serialize[name] = value
return to_serialize

return super().default(o)


def serialize_model(model, ensure_ascii=False, **kwargs):
return json.dumps(model, cls=ModelEncoder, ensure_ascii=ensure_ascii,
**kwargs)
Loading