async def test_start_achievements_import(plugin, write, mocker): game_achievements_import_success = mocker.patch.object( plugin, "game_achievements_import_success") game_achievements_import_failure = mocker.patch.object( plugin, "game_achievements_import_failure") achievements_import_finished = mocker.patch.object( plugin, "achievements_import_finished") game_ids = ["1", "5", "9"] error = BackendError() achievements = [ Achievement(achievement_id="lvl10", unlock_time=1548421241), Achievement(achievement_name="Got level 20", unlock_time=1548422395) ] plugin.get_unlocked_achievements.coro.side_effect = [ achievements, [], error ] await plugin.start_achievements_import(game_ids) with pytest.raises(ImportInProgress): await plugin.start_achievements_import(["4", "8"]) # wait until all tasks are finished for _ in range(4): await asyncio.sleep(0) plugin.get_unlocked_achievements.coro.assert_has_calls( [call("1"), call("5"), call("9")]) game_achievements_import_success.assert_has_calls( [call("1", achievements), call("5", [])]) game_achievements_import_failure.assert_called_once_with("9", error) achievements_import_finished.assert_called_once_with()
async def test_get_achievements_success(authenticated_plugin, backend_client, steam_id): authenticated_plugin._stats_cache = {"236850": {'achievements': [{'unlock_time': 1551887210, 'name': 'name 1'}, {'unlock_time': 1551887134, 'name': 'name 2'}]}} achievements = await authenticated_plugin.get_unlocked_achievements("236850", None) assert achievements == [ Achievement(1551887210, None, "name 1"), Achievement(1551887134, None, "name 2") ]
async def test_trailing_whitespace(authenticated_plugin): authenticated_plugin._stats_cache = {"236850": {'achievements': [{'unlock_time': 1551887210, 'name': 'name 1 '}, {'unlock_time': 1551887134, 'name': 'name 2 '}]}} achievements = await authenticated_plugin.get_unlocked_achievements("236850", None) assert achievements == [ Achievement(1551887210, None, "name 1"), Achievement(1551887134, None, "name 2") ]
def cache(): cache = Cache() cache.update( "131", [Achievement(1563289123, "abc"), Achievement(1563289141, "efg")], Fingerprint(1563289641, 123)) cache.update("871", [Achievement(156327123, "yik", "Achievement")], Fingerprint(None, 13)) return cache
async def test_get_unlocked_achievements_success(plugin, read, write): plugin.prepare_achievements_context.return_value = async_return_value(5) request = { "jsonrpc": "2.0", "id": "3", "method": "start_achievements_import", "params": { "game_ids": ["14"] } } read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)] plugin.get_unlocked_achievements.return_value = async_return_value([ Achievement(achievement_id="lvl10", unlock_time=1548421241), Achievement(achievement_name="Got level 20", unlock_time=1548422395), Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633) ]) await plugin.run() plugin.prepare_achievements_context.assert_called_with(["14"]) plugin.get_unlocked_achievements.assert_called_with("14", 5) plugin.achievements_import_complete.asert_called_with() assert get_messages(write) == [ { "jsonrpc": "2.0", "id": "3", "result": None }, { "jsonrpc": "2.0", "method": "game_achievements_import_success", "params": { "game_id": "14", "unlocked_achievements": [ { "achievement_id": "lvl10", "unlock_time": 1548421241 }, { "achievement_name": "Got level 20", "unlock_time": 1548422395 }, { "achievement_id": "lvl30", "achievement_name": "Got level 30", "unlock_time": 1548495633 } ] } }, { "jsonrpc": "2.0", "method": "achievements_import_finished", "params": None } ]
async def test_achievements(api_mock, plugin): api_mock.get_me.return_value = { "user_achievements": [{ "achieved_at": "2020-03-26T19:58:36+00:00", "achievement_id": 128 }, { "achieved_at": "2020-04-20T21:32:31+00:00", "achievement_id": 54 }], } expected = [Achievement(12131132, 128), Achievement(23123123, 54)] context = await plugin.prepare_achievements_context([OSU]) await plugin.get_unlocked_achievements(OSU, context) == expected
async def test_get_achievements_success(authenticated_plugin, backend_client, steam_id): context = { "236850": GameTime(game_id="236850", time_played=86820, last_played_time=1549385500), "281990": GameTime(game_id="281990", time_played=78, last_played_time=1549385500) } backend_client.get_achievements.return_value = [ (1551887210, "name 1"), (1551887134, "name 2") ] achievements = await authenticated_plugin.get_unlocked_achievements("236850", context) assert achievements == [ Achievement(1551887210, None, "name 1"), Achievement(1551887134, None, "name 2") ] backend_client.get_achievements.assert_called_once_with(steam_id, "236850")
async def test_initialize_cache(create_authenticated_plugin, backend_client, steam_id, login): cache = { "achievements": """{ "17923": { "achievements": [ { "unlock_time": 1549383000, "achievement_id": null, "achievement_name": "name" } ], "fingerprint": { "time_played": 1549385501, "last_played_time": 180 } } }""" } plugin = await create_authenticated_plugin(steam_id, login, cache) context = { "17923": GameTime(game_id="17923", time_played=180, last_played_time=1549385501) } achievements = await plugin.get_unlocked_achievements("17923", context) assert achievements == [Achievement(1549383000, None, "name")] backend_client.get_achievements.assert_not_called()
async def get_unlocked_achievements(self, game_id: str, context: Any) -> List[Achievement]: result = list() if game_id != self.GAME_ID: logging.warn('plugin/get_unlocked_achievements: unknown game_id %s' % game_id) return result if not self.__imported_achievements: self.__imported_achievements = list() self.__imported_achievements.clear() for achievement_id in await self._gw2_api.get_account_achievements(): #check for existence if not self.__is_achievement_exists(achievement_id): continue #save unlock time cache_key = 'achievement_%s' % achievement_id if cache_key not in self.persistent_cache: self.persistent_cache[cache_key] = int(time.time()) #append to list result.append(Achievement(self.persistent_cache.get(cache_key), achievement_id, self.__get_achievement_name(achievement_id))) self.push_cache() return result
async def get_unlocked_achievements(self, game_id, context): # The Social Club API has an authentication endpoint located at https://scapi.rockstargames.com/ # achievements/awardedAchievements?title=[game-id]&platform=pc&rockstarId=[rockstar-ID], which returns a # list of the user's unlocked achievements for the specified game. It uses the Social Club standard for # authentication (a request header named Authorization containing "Bearer [Bearer-Token]"). title_id = get_game_title_id_from_ros_title_id(game_id) if games_cache[title_id]["achievementId"] is None or \ (games_cache[title_id]["isPreOrder"]): return [] log.debug("ROCKSTAR_ACHIEVEMENT_CHECK: Beginning achievements check for " + title_id + " (Achievement ID: " + get_achievement_id_from_ros_title_id(game_id) + ")...") # Now, we can begin getting the user's achievements for the specified game. achievement_id = get_achievement_id_from_ros_title_id(game_id) url = (f"https://scapi.rockstargames.com/achievements/awardedAchievements?title={achievement_id}" f"&platform=pc&rockstarId={self._http_client.get_rockstar_id()}") unlocked_achievements = await self._http_client.get_json_from_request_strict(url) achievements_dict = unlocked_achievements["awardedAchievements"] achievements_list = [] for key, value in achievements_dict.items(): # What if an achievement is added to the Social Club after the cache was already made? In this event, we # need to refresh the cache. achievement_num = key unlock_time = await get_unix_epoch_time_from_date(value["dateAchieved"]) achievements_list.append(Achievement(unlock_time, achievement_id=achievement_num)) return achievements_list
def parser(json_data: Dict) -> List[Achievement]: return [ Achievement(achievement_id=key, achievement_name=value["name"], unlock_time=value["u"]) for key, value in json_data.items() if value.get("complete") ]
async def _get_achievements(self, game_id): achievements = await self._client.get_achievements( self._steam_id, game_id) return [ Achievement(unlock_time, None, name) for unlock_time, name in achievements ]
async def test_initialize_cache(create_authenticated_plugin, backend_client, steam_id, miniprofile, login): plugin = await create_authenticated_plugin(steam_id, login,miniprofile,{}) plugin._stats_cache = {"17923": {'achievements': [{'unlock_time': 123,'name':'name'}]}} achievements = await plugin.get_unlocked_achievements("17923", None) assert achievements == [ Achievement(123, None , "name") ] backend_client.get_achievements.assert_not_called()
async def get_unlocked_achievements(self, game_id, context): me = await self._api.get_me() return [ Achievement(achievement_id=medal['achievement_id'], unlock_time=int( datetime.fromisoformat( medal['achieved_at']).timestamp())) for medal in me.get('user_achievements', []) ]
def test_success(plugin, readline, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_unlocked_achievements", "params": { "game_id": "14" } } readline.side_effect = [json.dumps(request), ""] plugin.get_unlocked_achievements.coro.return_value = [ Achievement(achievement_id="lvl10", unlock_time=1548421241), Achievement(achievement_name="Got level 20", unlock_time=1548422395), Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633) ] asyncio.run(plugin.run()) plugin.get_unlocked_achievements.assert_called_with(game_id="14") response = json.loads(write.call_args[0][0]) assert response == { "jsonrpc": "2.0", "id": "3", "result": { "unlocked_achievements": [{ "achievement_id": "lvl10", "unlock_time": 1548421241 }, { "achievement_name": "Got level 20", "unlock_time": 1548422395 }, { "achievement_id": "lvl30", "achievement_name": "Got level 30", "unlock_time": 1548495633 }] } }
async def import_games_achievements(self, _game_ids): game_ids = set(_game_ids) error = UnknownError() try: achievement_sets = { } # 'offerId' to 'achievementSet' names mapping for offer_id, achievement_set in ( await self._backend_client.get_achievements_sets(self._persona_id )).items(): if not achievement_set: self.game_achievements_import_success(offer_id, []) game_ids.remove(offer_id) else: achievement_sets[offer_id] = achievement_set if not achievement_sets: return for offer_id, achievements in ( await self._backend_client.get_achievements( self._persona_id, achievement_sets)).items(): try: self.game_achievements_import_success( offer_id, [ Achievement(achievement_id=key, achievement_name=value["name"], unlock_time=value["u"]) for key, value in achievements.items() if value["complete"] ]) except KeyError: self.game_achievements_import_failure( offer_id, UnknownBackendResponse()) except ApplicationError as error: self.game_achievements_import_failure(offer_id, error) finally: game_ids.remove(offer_id) except KeyError: error = UnknownBackendResponse() except ApplicationError as _error: error = _error except Exception: pass # handled below finally: # any other exceptions or not answered game_ids are responded with an error [ self.game_achievements_import_failure(game_id, error) for game_id in game_ids ]
async def test_game_achievements_import_success(plugin, write): achievements = [ Achievement(achievement_id="lvl10", unlock_time=1548421241), Achievement(achievement_name="Got level 20", unlock_time=1548422395) ] plugin.game_achievements_import_success("134", achievements) response = json.loads(write.call_args[0][0]) assert response == { "jsonrpc": "2.0", "method": "game_achievements_import_success", "params": { "game_id": "134", "unlocked_achievements": [{ "achievement_id": "lvl10", "unlock_time": 1548421241 }, { "achievement_name": "Got level 20", "unlock_time": 1548422395 }] } }
def from_dict(dict_: dict) -> Cache: cache = Cache() for key, value in dict_.items(): try: achievements = value["achievements"] achievements = [ Achievement(**achievement) for achievement in achievements ] fingerprint = value["fingerprint"] fingerprint = Fingerprint(**fingerprint) except (KeyError, TypeError, AssertionError) as error: raise ValueError( "Failed to deserialize cache from dictionary") from error cache.update(key, achievements, fingerprint) return cache
def achievement_parser(achievement_tag: Optional[AchievementTag]) -> Achievement: name_tag = achievement_tag.h2 if not name_tag: raise UnknownBackendResponse("Cannot find achievement name tag") achievement_name = achievement_tag.h2.get_text() if not achievement_name: raise UnknownBackendResponse("Failed to parse achievement name") return Achievement( unlock_time=self._achievements_cache.setdefault( achievement_name , Timestamp(int(datetime.utcnow().timestamp())) ) , achievement_name=achievement_name )
async def test_unlock_achievement(plugin, write): achievement = Achievement(achievement_id="lvl20", unlock_time=1548422395) plugin.unlock_achievement("14", achievement) response = json.loads(write.call_args[0][0]) assert response == { "jsonrpc": "2.0", "method": "achievement_unlocked", "params": { "game_id": "14", "achievement": { "achievement_id": "lvl20", "unlock_time": 1548422395 } } }
async def get_unlocked_achievements(self, game_id: str, context: Any) -> List[Achievement]: logger.info(f"Asked for achievs for {game_id}") game_stats = self._stats_cache.get(game_id) achievements = [] if game_stats: if 'achievements' not in game_stats: return [] for achievement in game_stats['achievements']: # Fix for trailing whitespace in some achievement names which resulted in achievements not matching with website data achi_name = achievement['name'] achi_name = achi_name.strip() if not achi_name: achi_name = achievement['name'] achievements.append(Achievement(achievement['unlock_time'], achievement_id=None, achievement_name=achi_name)) return achievements
def trop2ach(self, trophy_id: int) -> Achievement: trophy_state = self.tropusr.table6[trophy_id].trophy_state unlocked = int.from_bytes(trophy_state, byteorder='big') if bool(unlocked): unlock_timestamp = self.tropusr.table6[trophy_id].timestamp2 unlock_time = int.from_bytes(unlock_timestamp, byteorder='big') pad_tid = format(trophy_id, '03d') name = self.tropconf.root.find('.//trophy[@id="' + pad_tid + '"]/name') ach = Achievement(unlock_time, None, name.text) return ach else: return None
async def task_check_for_achievements(self): if self.__imported_achievements: for achievement_id in self._gw2_api.get_account_achievements(): if achievement_id not in self.__imported_achievements: #check for existence if not self.__is_achievement_exists(achievement_id): continue #mark as processed self.__imported_achievements.append(achievement_id) #save unlock time cache_key = 'achievement_%s' % achievement_id self.persistent_cache[cache_key] = int(time.time()) #push to galaxy self.unlock_achievement(self.GAME_ID, Achievement(self.persistent_cache.get(cache_key), achievement_id, self.__get_achievement_name(achievement_id))) await asyncio.sleep(self.SLEEP_CHECK_ACHIEVEMENTS)
async def get_unlocked_challenges(self, game_id): """Challenges are a unique uplay club feature and don't directly translate to achievements""" if not self.client.is_authenticated(): raise AuthenticationRequired() for game in self.games_collection: if game.space_id == game_id or game.launch_id == game_id: if not game.space_id: return [] challenges = await self.client.get_challenges(game.space_id) return [ Achievement(achievement_id=challenge["id"], achievement_name=challenge["name"], unlock_time=int( datetime.datetime.timestamp( dateutil.parser.parse( challenge["completionDate"])))) for challenge in challenges["actions"] if challenge["isCompleted"] and not challenge["isBadge"] ]
async def _get_wow_achievements(self): achievements = [] try: characters_data = await self.backend_client.get_wow_character_data( ) characters_data = characters_data["characters"] wow_character_data = await asyncio.gather( *[ self.backend_client.get_wow_character_achievements( character["realm"], character["name"]) for character in characters_data ], return_exceptions=True, ) for data in wow_character_data: if isinstance(data, requests.Timeout) or isinstance( data, requests.ConnectionError): raise data wow_achievement_data = [ list( zip( data["achievements"]["achievementsCompleted"], data["achievements"]["achievementsCompletedTimestamp"], )) for data in wow_character_data if type(data) is dict ] already_in = set() for char_ach in wow_achievement_data: for ach in char_ach: if ach[0] not in already_in: achievements.append( Achievement(achievement_id=ach[0], unlock_time=int(ach[1] / 1000))) already_in.add(ach[0]) except (AccessTokenExpired, BackendError) as e: log.exception(str(e)) with open('wow.json', 'w') as f: f.write(json.dumps(achievements, cls=DataclassJSONEncoder)) return achievements
async def _get_sc2_achievements(self): account_data = await self.backend_client.get_sc2_player_data(self.authentication_client.user_details["id"]) # TODO what if more sc2 accounts? assert len(account_data) == 1 account_data = account_data[0] profile_data = await self.backend_client.get_sc2_profile_data( account_data["regionId"], account_data["realmId"], account_data["profileId"] ) sc2_achievement_data = [ Achievement(achievement_id=achievement["achievementId"], unlock_time=achievement["completionDate"]) for achievement in profile_data["earnedAchievements"] if achievement["isComplete"] ] with open('sc2.json', 'w') as f: f.write(json.dumps(sc2_achievement_data, cls=DataclassJSONEncoder)) return sc2_achievement_data
async def get_unlocked_achievements(self, game_id): if not self._http_client.is_authenticated(): raise AuthenticationRequired() try: achievement_set = (await self._get_offers( [game_id]))[0]["platforms"][0]["achievementSetOverride"] if achievement_set is None: return [] achievements = await self._backend_client.get_achievements( self._persona_id, {game_id: achievement_set}) return [ Achievement(achievement_id=key, achievement_name=value["name"], unlock_time=value["u"]) for key, value in achievements[game_id].items() if value["complete"] ] except (KeyError, IndexError): raise UnknownBackendResponse()
"trophyId": 5, "fromUser": { "onlineId": "user-id", "earned": False, "earnedDate": "1987-02-07T10:14:42Z" }, "trophyName": "achievement 5", "groupId": "022" }, ] } UNLOCKED_ACHIEVEMENTS = [ Achievement( achievement_id="NPWR11556_00_1", achievement_name="achievement 1", unlock_time=538304493.0, ), Achievement(achievement_id="NPWR11556_00_2", achievement_name="achievement 2", unlock_time=1318782798.0) ] TROPHIES_CACHE = Cache() TROPHIES_CACHE._entries = { "NPWR11556_00": CacheEntry(value=UNLOCKED_ACHIEVEMENTS, timestamp=1490374318.0) } BACKEND_USER_PROFILES = { "start":
def test_initialization_no_unlock_time(): with raises(Exception): Achievement(achievement_id="lvl30", achievement_name="Got level 30")
def test_initialization_no_id_nor_name(): with raises(AssertionError): Achievement(unlock_time=1234567890)