class ItchIntegration(Plugin):
    def __init__(self, reader, writer, token):
        super().__init__(Platform.ItchIo, __version__, reader, writer, token)
        self.http_client = HTTPClient(self.store_credentials)
        self.session_cookie = None

    async def shutdown(self):
        await self.http_client.close()

    # implement methods
    async def authenticate(self, stored_credentials=None):
        logging.debug("authenticate")
        confirmation_uri = 'https://itch.io/user/oauth?client_id=3821cecdd58ae1a920be15f6aa479f7e&scope=profile&response_type=token&redirect_uri=http%3A%2F%2F127.0.0.1%3A7157%2Fgogg2itchintegration'
        if not stored_credentials:
            return NextStep("web_session", {
                "window_title":
                "Log in to Itch.io",
                "window_width":
                536,
                "window_height":
                675,
                "start_uri":
                confirmation_uri,
                "end_uri_regex":
                r"^(http://127\.0\.0\.1:7157/gogg2itchintegration#access_token=.+)",
            },
                            js={
                                r'^https://itch\.io/my-feed.*':
                                [f'window.location = "{confirmation_uri}"']
                            })
        else:
            self.http_client.update_cookies(stored_credentials)
            try:
                user = await self.get_user_data()
                return Authentication(str(user.get("id")),
                                      str(user.get("username")))
            except AccessDenied:
                raise InvalidCredentials()

    async def pass_login_credentials(self, step, credentials, cookies):
        session_cookies = {
            cookie['name']: cookie['value']
            for cookie in cookies if cookie['name']
        }
        self.http_client.update_cookies(session_cookies)

        user = await self.get_user_data()
        logging.debug(type(id))
        logging.debug(user.get("id"))
        logging.debug(user.get("username"))
        return Authentication(str(user.get("id")), str(user.get("username")))

    async def get_owned_games(self):
        page = 1
        games = []
        while True:
            try:
                resp = await self.http_client.get(
                    f"https://api.itch.io/profile/owned-keys?classification=game&page={page}"
                )
            except AuthenticationRequired:
                self.lost_authentication()
                raise
            if len(resp.get("owned_keys")) == 0:
                return games
            await self.parse_json_into_games(resp.get("owned_keys"), games)
            page += 1
        return games

    async def get_user_data(self):
        resp = await self.http_client.get(f"https://api.itch.io/profile?")
        self.authenticated = True
        return resp.get("user")

    async def parse_json_into_games(self, resp, games):
        for key in resp:
            game = key.get("game")
            if not game.get("classification") == "game":
                continue
            game_name = game.get("title")
            game_num = str(game.get("id"))
            logging.debug('Parsed %s, %s', game_name, game_num)
            self.persistent_cache[game_num] = game
            this_game = Game(game_id=game_num,
                             game_title=game_name,
                             license_info=LicenseInfo(
                                 LicenseType.SinglePurchase),
                             dlcs=[])
            games.append(this_game)

            if CHECK_GAMES_AGAINST_IGDB:
                itch_id = game.get("id")
                game_gog_url = GOG_GAME_URL.format(itch_id)
                start_time = time.time()

                try:
                    json_response = await self.http_client.get(game_gog_url)
                    if "error" in json_response:
                        log_unknown_game(
                            "No IGDB data found for {} (itch.io game ID is {})"
                            .format(game_name, itch_id))

                except ClientResponseError as e:
                    log_unknown_game("No IGDB data found for {}: {}".format(
                        game_name, e))

                stop_time = time.time()
                elapsed_seconds = stop_time - start_time
                if elapsed_seconds <= GOG_API_RATE_LIMIT_SECONDS:
                    diff_seconds = GOG_API_RATE_LIMIT_SECONDS - elapsed_seconds
                    await asyncio.sleep(diff_seconds)

    async def get_os_compatibility(self, game_id, context):
        try:
            compat = self.persistent_cache[str(game_id)].get("traits")
            os = (OSCompatibility.Windows
                  if "p_windows" in compat else OSCompatibility(0)) | (
                      OSCompatibility.MacOS if "p_osx" in compat else
                      OSCompatibility(0)) | (OSCompatibility.Linux if "p_linux"
                                             in compat else OSCompatibility(0))
            logging.debug("Compat value: %s", os)
            if not os == 0:
                return os
        except KeyError:
            logging.error("Key not found in cache: %s", game_id)
Exemple #2
0
class ItchIntegration(Plugin):
    def __init__(self, reader, writer, token):
        super().__init__(Platform.ItchIo, __version__, reader, writer, token)
        self.http_client = HTTPClient(self.store_credentials)
        self.session_cookie = None

    async def shutdown(self):
        await self.http_client.close()

    # implement methods
    async def authenticate(self, stored_credentials=None):
        logging.debug("authenticate")
        if not (stored_credentials.get("access_token")
                if stored_credentials else None):
            return NextStep(
                "web_session", {
                    "window_title":
                    "Log in to Itch.io",
                    "window_width":
                    536,
                    "window_height":
                    675,
                    "start_uri":
                    r"https://itch.io/user/oauth?client_id=3821cecdd58ae1a920be15f6aa479f7e&scope=profile&response_type=token&redirect_uri=http%3A%2F%2F127.0.0.1%3A7157%2Fgogg2itchintegration",
                    "end_uri_regex":
                    r"^http://127\.0\.0\.1:7157/gogg2itchintegration#access_token=.+",
                })
        else:
            try:
                user = await self.get_user_data(
                    stored_credentials["access_token"])

                return Authentication(user["id"], user["username"])
            except AccessDenied:
                raise InvalidCredentials()


    async def pass_login_credentials(self, step: str, credentials: Dict[str, str], cookies: List[Dict[str, str]]) -> \
            Union[NextStep, Authentication]:
        session_cookies = {
            cookie['name']: cookie['value']
            for cookie in cookies if cookie['name']
        }
        self.http_client.update_cookies(session_cookies)
        api_key = re.search(
            r"^http://127\.0\.0\.1:7157/gogg2itchintegration#access_token=(.+)",
            credentials["end_uri"])
        key = api_key.group(1)
        self.store_credentials({"access_token": key})

        user = await self.get_user_data(key)
        return Authentication(user["id"], user["username"])

    async def get_owned_games(self):
        page = 1
        games = []
        while True:
            try:
                resp = await self.http_client.get(
                    f"https://api.itch.io/profile/owned-keys?page={page}")
            except AuthenticationRequired:
                self.lost_authentication()
                raise
            if len(resp.get("owned_keys")) == 0:
                return games
            self.parse_json_into_games(resp.get("owned_keys"), games)
            page += 1
        return games

    async def get_user_data(self, api_key):
        resp = await self.http_client.get(f"https://api.itch.io/profile?")
        self.authenticated = True
        return resp.get("user")

    @staticmethod
    def parse_json_into_games(resp, games):
        for key in resp:
            game = key.get("game")
            if not game.get("classification") == "game":
                continue
            game_name = game.get("title")
            game_num = game.get("id")
            logging.debug('Parsed %s, %s', game_name, game_num)
            this_game = Game(game_id=game_num,
                             game_title=game_name,
                             license_info=LicenseInfo(
                                 LicenseType.SinglePurchase),
                             dlcs=[])
            games.append(this_game)