Beispiel #1
0
    def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
        # Fetch the profiles
        profiles: List[Tuple[UserID, ValidatedDict]] = []
        if idtype == APIConstants.ID_TYPE_SERVER:
            profiles.extend(self.data.local.user.get_all_profiles(self.game, self.version))
        elif idtype == APIConstants.ID_TYPE_SONG:
            raise APIException(
                'Unsupported ID for lookup!',
                405,
            )
        elif idtype == APIConstants.ID_TYPE_INSTANCE:
            raise APIException(
                'Unsupported ID for lookup!',
                405,
            )
        elif idtype == APIConstants.ID_TYPE_CARD:
            users: Set[UserID] = set()
            for cardid in ids:
                userid = self.data.local.user.from_cardid(cardid)
                if userid is not None:
                    # Don't duplicate loads for users with multiple card IDs if multiples
                    # of those IDs are requested.
                    if userid in users:
                        continue
                    users.add(userid)

                    # We can possibly find another profile for this user. This is important
                    # in the case that we returned scores for a user that doesn't have a
                    # profile on a particular version. We allow that on this network, so in
                    # order to not break remote networks, try our best to return any profile.
                    profile = self.data.local.user.get_any_profile(self.game, self.version, userid)
                    if profile is not None:
                        profiles.append((userid, profile))
        else:
            raise APIException('Invalid ID type!')

        # Now, fetch the users, and filter out profiles belonging to orphaned users
        retval: List[Dict[str, Any]] = []
        id_to_cards: Dict[UserID, List[str]] = {}
        for (userid, profile) in profiles:
            if userid not in id_to_cards:
                cards = self.data.local.user.get_cards(userid)
                if len(cards) == 0:
                    # Can't add this user, skip the profile
                    continue

                id_to_cards[userid] = cards

            # Format the profile and add it
            settings = self.data.local.game.get_settings(self.game, userid)
            if settings is None:
                settings = ValidatedDict({})

            retval.append(self.__format_profile(id_to_cards[userid], profile, settings, profile['version'] == self.version))

        return retval
Beispiel #2
0
def info() -> Dict[str, Any]:
    requestdata = request.get_json()
    if requestdata is None:
        raise APIException('Request JSON could not be decoded.')
    if requestdata:
        raise APIException('Unrecognized parameters for request.')

    return {
        'versions': SUPPORTED_VERSIONS,
        'name': g.config.get('name', 'e-AMUSEMENT Network'),
        'email': g.config.get('email', '*****@*****.**'),
    }
Beispiel #3
0
    def fetch_v1(self, idtype: str, ids: List[str],
                 params: Dict[str, Any]) -> List[Dict[str, Any]]:
        retval: List[Dict[str, Any]] = []

        # Fetch the attempts
        if idtype == APIConstants.ID_TYPE_SERVER:
            retval = self.__aggregate_global([
                attempt[1]
                for attempt in self.data.local.music.get_all_attempts(
                    self.game, self.music_version)
            ])
        elif idtype == APIConstants.ID_TYPE_SONG:
            if len(ids) == 1:
                songid = int(ids[0])
                chart = None
            else:
                songid = int(ids[0])
                chart = int(ids[1])
            retval = self.__aggregate_global([
                attempt[1] for attempt in
                self.data.local.music.get_all_attempts(self.game,
                                                       self.music_version,
                                                       songid=songid,
                                                       songchart=chart)
            ])
        elif idtype == APIConstants.ID_TYPE_INSTANCE:
            songid = int(ids[0])
            chart = int(ids[1])
            cardid = ids[2]
            userid = self.data.local.user.from_cardid(cardid)
            if userid is not None:
                retval = self.__aggregate_local(
                    {userid: self.data.local.user.get_cards(userid)},
                    self.data.local.music.get_all_attempts(self.game,
                                                           self.music_version,
                                                           songid=songid,
                                                           songchart=chart,
                                                           userid=userid))
        elif idtype == APIConstants.ID_TYPE_CARD:
            id_to_cards: Dict[int, List[str]] = {}
            attempts: List[Tuple[UserID, Attempt]] = []
            for cardid in ids:
                userid = self.data.local.user.from_cardid(cardid)
                if userid is not None:
                    # Don't duplicate loads for users with multiple card IDs if multiples
                    # of those IDs are requested.
                    if userid in id_to_cards:
                        continue

                    id_to_cards[userid] = self.data.local.user.get_cards(
                        userid)
                    attempts.extend(
                        self.data.local.music.get_all_attempts(
                            self.game, self.music_version, userid=userid))
            retval = self.__aggregate_local(id_to_cards, attempts)
        else:
            raise APIException('Invalid ID type!')

        return retval
Beispiel #4
0
    def fetch_v1(self, idtype: str, ids: List[str],
                 params: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]:
        # Verify IDs
        if idtype != APIConstants.ID_TYPE_SERVER:
            raise APIException(
                'Unsupported ID for lookup!',
                405,
            )

        # Fetch the songs
        songs = self.data.local.music.get_all_songs(self.game,
                                                    self.music_version)
        if self.game == GameConstants.JUBEAT and self.version in [
                VersionConstants.JUBEAT_CLAN, VersionConstants.JUBEAT_FESTO
        ]:
            # There's always a special case. We don't store all music IDs since those in
            # the range of 80000301-80000347 are actually the same song, but copy-pasted
            # for different prefectures and slightly different charts. So, we need to copy
            # that song data so that remote clients can resolve scores for those ID ranges.
            additions: List[Song] = []
            for song in songs:
                if song.id == 80000301:
                    for idrange in range(80000302, 80000348):
                        additions.append(
                            Song(
                                song.game,
                                song.version,
                                idrange,
                                song.chart,
                                song.name,
                                song.artist,
                                song.genre,
                                song.data,
                            ))
            songs.extend(additions)
        retval = {
            'songs': [self.__format_song(song) for song in songs],
        }

        # Fetch any optional extras per-game, return
        retval.update(self.__format_extras())
        return retval
Beispiel #5
0
    def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
        since = params.get('since')
        until = params.get('until')

        # Fetch the scores
        records: List[Tuple[UserID, Score]] = []
        if idtype == APIConstants.ID_TYPE_SERVER:
            # Because of the way this query works, we can't apply since/until to it directly.
            # If we did, it would miss higher scores earned before since or after until, and
            # incorrectly report records.
            records.extend(self.data.local.music.get_all_records(self.game, self.music_version))
        elif idtype == APIConstants.ID_TYPE_SONG:
            if len(ids) == 1:
                songid = int(ids[0])
                chart = None
            else:
                songid = int(ids[0])
                chart = int(ids[1])
            records.extend(self.data.local.music.get_all_scores(self.game, self.music_version, songid=songid, songchart=chart, since=since, until=until))
        elif idtype == APIConstants.ID_TYPE_INSTANCE:
            songid = int(ids[0])
            chart = int(ids[1])
            cardid = ids[2]
            userid = self.data.local.user.from_cardid(cardid)
            if userid is not None:
                score = self.data.local.music.get_score(self.game, self.music_version, userid, songid, chart)
                if score is not None:
                    records.append((userid, score))
        elif idtype == APIConstants.ID_TYPE_CARD:
            users: Set[UserID] = set()
            for cardid in ids:
                userid = self.data.local.user.from_cardid(cardid)
                if userid is not None:
                    # Don't duplicate loads for users with multiple card IDs if multiples
                    # of those IDs are requested.
                    if userid in users:
                        continue
                    users.add(userid)

                    records.extend([(userid, score) for score in self.data.local.music.get_scores(self.game, self.music_version, userid, since=since, until=until)])
        else:
            raise APIException('Invalid ID type!')

        # Now, fetch the users, and filter out scores belonging to orphaned users
        id_to_cards: Dict[UserID, List[str]] = {}
        retval: List[Dict[str, Any]] = []
        for (userid, record) in records:
            # Postfilter for queries that can't filter. This will save on data transferred.
            if since is not None:
                if record.update < since:
                    continue
            if until is not None:
                if record.update >= until:
                    continue

            if userid not in id_to_cards:
                cards = self.data.local.user.get_cards(userid)
                if len(cards) == 0:
                    # Can't add this user, skip the score
                    continue

                id_to_cards[userid] = cards

            # Format the score and add it
            retval.append(self.__format_record(id_to_cards[userid], record))

        return retval
Beispiel #6
0
def lookup(protoversion: str, requestgame: str,
           requestversion: str) -> Dict[str, Any]:
    requestdata = request.get_json()
    for expected in ['type', 'ids', 'objects']:
        if expected not in requestdata:
            raise APIException('Missing parameters for request.')
    for param in requestdata:
        if param not in ['type', 'ids', 'objects', 'since', 'until']:
            raise APIException('Unrecognized parameters for request.')

    args = copy.deepcopy(requestdata)
    del args['type']
    del args['ids']
    del args['objects']

    if protoversion not in SUPPORTED_VERSIONS:
        # Don't know about this protocol version
        abort(501)

    # Figure out what games we support based on config, and map those.
    gamemapping = {}
    for (gameid, constant) in [
        ('ddr', GameConstants.DDR),
        ('iidx', GameConstants.IIDX),
        ('jubeat', GameConstants.JUBEAT),
        ('museca', GameConstants.MUSECA),
        ('popnmusic', GameConstants.POPN_MUSIC),
        ('reflecbeat', GameConstants.REFLEC_BEAT),
        ('soundvoltex', GameConstants.SDVX),
    ]:
        if g.config.get('support', {}).get(constant, False):
            gamemapping[gameid] = constant
    game = gamemapping.get(requestgame)
    if game is None:
        # Don't support this game!
        abort(404)

    if requestversion[0] == 'o':
        omnimix = True
        requestversion = requestversion[1:]
    else:
        omnimix = False

    version = {
        GameConstants.DDR: {
            '12': VersionConstants.DDR_X2,
            '13': VersionConstants.DDR_X3_VS_2NDMIX,
            '14': VersionConstants.DDR_2013,
            '15': VersionConstants.DDR_2014,
            '16': VersionConstants.DDR_ACE,
        },
        GameConstants.IIDX: {
            '20': VersionConstants.IIDX_TRICORO,
            '21': VersionConstants.IIDX_SPADA,
            '22': VersionConstants.IIDX_PENDUAL,
            '23': VersionConstants.IIDX_COPULA,
            '24': VersionConstants.IIDX_SINOBUZ,
            '25': VersionConstants.IIDX_CANNON_BALLERS,
        },
        GameConstants.JUBEAT: {
            '5': VersionConstants.JUBEAT_SAUCER,
            '5a': VersionConstants.JUBEAT_SAUCER_FULFILL,
            '6': VersionConstants.JUBEAT_PROP,
            '7': VersionConstants.JUBEAT_QUBELL,
            '8': VersionConstants.JUBEAT_CLAN,
        },
        GameConstants.MUSECA: {
            '1': VersionConstants.MUSECA,
            '1p': VersionConstants.MUSECA_1_PLUS,
        },
        GameConstants.POPN_MUSIC: {
            '19': VersionConstants.POPN_MUSIC_TUNE_STREET,
            '20': VersionConstants.POPN_MUSIC_FANTASIA,
            '21': VersionConstants.POPN_MUSIC_SUNNY_PARK,
            '22': VersionConstants.POPN_MUSIC_LAPISTORIA,
            '23': VersionConstants.POPN_MUSIC_ECLALE,
            '24': VersionConstants.POPN_MUSIC_USANEKO,
        },
        GameConstants.REFLEC_BEAT: {
            '1': VersionConstants.REFLEC_BEAT,
            '2': VersionConstants.REFLEC_BEAT_LIMELIGHT,
            # We don't support non-final COLETTE, so just return scores for
            # final colette to any network that asks.
            '3w': VersionConstants.REFLEC_BEAT_COLETTE,
            '3sp': VersionConstants.REFLEC_BEAT_COLETTE,
            '3su': VersionConstants.REFLEC_BEAT_COLETTE,
            '3a': VersionConstants.REFLEC_BEAT_COLETTE,
            '3as': VersionConstants.REFLEC_BEAT_COLETTE,
            # We don't support groovin'!!, so just return upper scores.
            '4': VersionConstants.REFLEC_BEAT_GROOVIN,
            '4u': VersionConstants.REFLEC_BEAT_GROOVIN,
            '5': VersionConstants.REFLEC_BEAT_VOLZZA,
            '5a': VersionConstants.REFLEC_BEAT_VOLZZA_2,
            '6': VersionConstants.REFLEC_BEAT_REFLESIA,
        },
        GameConstants.SDVX: {
            '1': VersionConstants.SDVX_BOOTH,
            '2': VersionConstants.SDVX_INFINITE_INFECTION,
            '3': VersionConstants.SDVX_GRAVITY_WARS,
            '4': VersionConstants.SDVX_HEAVENLY_HAVEN,
        },
    }.get(game, {}).get(requestversion)
    if version is None:
        # Don't support this version!
        abort(404)

    idtype = requestdata['type']
    ids = requestdata['ids']
    if idtype not in [
            APIConstants.ID_TYPE_CARD, APIConstants.ID_TYPE_SONG,
            APIConstants.ID_TYPE_INSTANCE, APIConstants.ID_TYPE_SERVER
    ]:
        raise APIException('Invalid ID type provided!')
    if idtype == APIConstants.ID_TYPE_CARD and len(ids) == 0:
        raise APIException('Invalid number of IDs given!')
    if idtype == APIConstants.ID_TYPE_SONG and len(ids) not in [1, 2]:
        raise APIException('Invalid number of IDs given!')
    if idtype == APIConstants.ID_TYPE_INSTANCE and len(ids) != 3:
        raise APIException('Invalid number of IDs given!')
    if idtype == APIConstants.ID_TYPE_SERVER and len(ids) != 0:
        raise APIException('Invalid number of IDs given!')

    responsedata = {}
    for obj in requestdata['objects']:
        handler = {
            'records': RecordsObject,
            'profile': ProfileObject,
            'statistics': StatisticsObject,
            'catalog': CatalogObject,
        }.get(obj)
        if handler is None:
            # Don't support this object type
            abort(404)

        inst = handler(g.data, game, version, omnimix)
        try:
            fetchmethod = getattr(inst, f'fetch_{protoversion}')
        except AttributeError:
            # Don't know how to handle this object for this version
            abort(501)

        responsedata[obj] = fetchmethod(idtype, ids, args)

    return responsedata
Beispiel #7
0
 def fetch_v1(self, idtype: str, ids: List[str], params: Dict[str,
                                                              Any]) -> Any:
     raise APIException('Object fetch not supported for this version!')