Beispiel #1
0
class MojangAPI(object):
    """
    A thin wrapper for the the core portion of the Mojang API

    References
    ----------
    * http://wiki.vg/Mojang_API
    """

    def __init__(self, auth, host=DEFAULT_MOJANG_API_HOST):
        self.auth = auth
        self.api = APIHost(host)

        if self.auth.accessToken:
            bearer =  "Bearer " + self.auth.accessToken
            self.api.headers["Authorization"] = bearer


    def username_to_uuid(self, username, at_time=0):
        return self.api.get("/users/profiles/minecraft/%s?at=%i" %
                            (username, at_time))


    def uuid_name_history(self, uuid):
        return self.api.get("/user/profiles/%s/names" % uuid)


    def playernames_to_uuids(self, playernames):
        return self.api.post("/profiles/minecraft", list(playernames))


    def change_skin(self, uuid, skin_url, slim=False):
        payload = {"model": "slim" if slim else "",
                   "url": skin_url}

        return self.api.post_encoded("/user/profile/%s/skin" % uuid)


    def upload_skin(self, uuid, skin_stream, slim=False):
        payload = {"model": "slim" if slim else "",
                   "file": ("harambe.png", skin_stream, "image/png")}

        return self.api.post_form("/user/profile/%s/skin" % uuid, payload)


    def upload_skin_filename(self, uuid, skin_filename, slim=False):
        with open(skin_filename) as skin_stream:
            return upload_skin(self, uuid, skin_stream, slim)


    def reset_skin(self, uuid):
        return self.api.delete("/user/profile/%s/skin" % uuid)


    def whoami(self):
        return self.api.get("/user")


    def statistics(self, which=DEFAULT_STATISTICS):
        which = {"metricKeys": list(which)}
        return self.api.post("/orders/statistics", which)
Beispiel #2
0
class Authentication(object):
    """
    A thin wrapper for the Mojang authentiation scheme, 'Yggdrasil'

    References
    ----------
    * http://wiki.vg/Authentication
    """

    def __init__(self, username, clientToken=None, accessToken=None,
                 host=HOST_YGGDRASIL, agent=MINECRAFT_AGENT_V1):

        self.api = APIHost(host)
        self.username = username
        self.user = None
        self.agent = agent
        self.clientToken = clientToken
        self.accessToken = accessToken
        self.selectedProfile = None


    def authenticate(self, password):
        """
        generate an accessToken for this session
        """

        payload = { "username": self.username,
                    "password": password,
                    "requestUser": True }

        if self.agent:
            payload["agent"] = self.agent

        if self.clientToken:
            payload["clientToken"] = self.clientToken

        try:
            ret = self.api.post("/authenticate", payload)

        except HTTPError as err:
            # if it's just a 403, that means the auth was wrong, so
            # it's simple failure. Any other kind of error is a
            # different kind of problem, so we'll propagate it up.

            if err.response.status_code == 403:
                return False
            else:
                raise

        else:
            self.clientToken = ret["clientToken"]
            self.accessToken = ret["accessToken"]
            self.selectedProfile = ret.get("selectedProfile")
            self.user = ret.get("user")

            return True


    def refresh(self):
        """
        ensure that this session remains valid. May result in a new
        accessToken.
        """

        payload = { "accessToken": self.accessToken,
                    "clientToken": self.clientToken,
                    "requestUser": True }

        try:
            ret = self.api.post("/refresh", payload)

        except HTTPError as err:
            # a 403 just means the session was completely invalid,
            # which is expected behavior in many circumstances. In
            # that case, we just return False. Any other error gets
            # propagated up.

            if err.response.status_code == 403:
                return False
            else:
                raise

        else:
            self.clientToken = ret["clientToken"]
            self.accessToken = ret["accessToken"]
            self.selectedProfile = ret.get("selectedProfile")
            self.user = ret.get("user")

            return True


    def validate(self):
        """
        check that the session is currently valid, and can be used to
        perform other actions. An invalid session will need to be
        renewed or a full re-auth may be required.
        """

        if not self.accessToken:
            return False

        payload = { "accessToken": self.accessToken, }

        try:
            ret = self.api.post("/validate", payload)

        except HTTPError as err:
            # one again, 403 is an expected possibility. Everything
            # else is wonky.

            if err.response.status_code == 403:
                return False
            else:
                raise

        else:
            return True


    def signout(self, password):
        """
        invalidates all sessions against the specified account
        """

        payload = { "username": self.username,
                    "password": password, }

        try:
            ret = self.api.post("/signout", payload)

        except HTTPError as err:
            # 403 means bad username/password in this case
            if err.response.status_code == 403:
                return False
            else:
                raise

        else:
            return True


    def invalidate(self):
        """
        invalidates the current session
        """

        if not self.accessToken:
            return None

        payload = { "accessToken": self.accessToken,
                    "clientToken": self.clientToken, }

        # even if we're already invalidated, this won't raise an
        # HTTPError, so we won't try to filter out a 403
        ret = self.api.post("/invalidate", payload)

        self.accessToken = None
        return True


    def load(self, filename):
        """
        set the state of this session to the what is represented in the
        JSON data stored in filename. Errors (access, malformed JSON,
        etc) while loading will be propagated.
        """

        with open(filename) as fd:
            session = load(fd)

        if "host" in session:
            host = session.pop("host")
            self.api = APIHost(host)

        self.__dict__.update(session)


    def save(self, filename):
        """
        save the state of this session to JSON data and write it to
        filename
        """

        session = dict(self.__dict__)
        session["host"] = self.api._host
        del session["api"]

        with open(filename, "w") as fd:
            dump(session, fd)


    def ensureClientToken(self):
        """
        generate a clientToken for this session if one doesn't already
        exist
        """

        if not self.clientToken:
            self.clientToken = generate_clientToken()
Beispiel #3
0
class RealmsAPI(object):
    """
    A thin wrapper for the Mojang Realms API

    References
    ----------
    * http://wiki.vg/Realms_API
    """

    def __init__(self, auth, host=DEFAULT_REALMS_HOST,
                 version=DEFAULT_REALMS_VERSION):

        # compose the necessary cookies from data in the auth object
        self.auth = auth
        sid =  "token:%s:%s" % (auth.accessToken, auth.selectedProfile["id"])
        user = auth.selectedProfile["name"]

        self.api = APIHost(host)
        self.api.cookies.set("sid", sid)
        self.api.cookies.set("user", user)
        self.api.cookies.set("version", version)


    def mco_available(self):
        return self.api.get("/mco/available")


    def mco_client_outdated(self):
        return self.api.get("/mco/client/outdated")


    def mco_tos_agree(self):
        return self.api.post("/mco/tos/agreed")


    def realm_list(self):
        """
        List the realms available for the given account auth
        """

        return self.api.get("/worlds")


    def realm_info(self, realm_id):
        """
        Information about a specific realm by ID
        """

        return self.api.get("/worlds/%i" % realm_id)


    def realm_join(self, realm_id):
        """
        Wakes up a realm so that it can be joined, returns a string
        specifying the IP_ADDRESS:PORT of the running server
        """

        return self.api.get("/worlds/%i/join" % realm_id)


    def realm_backups(self, realm_id):
        """
        Show the backups available for the given realm ID
        """

        return self.api.get("/worlds/%i/backups" % realm_id)


    def realm_world_url(self, realm_id, world):
        """
        Show the download URL for the latest world backup for the given
        realm ID
        """

        return self.api.get("/worlds/%i/slot/%i/download" % (realm_id, world))


    def realm_ops_list(self, realm_id):
        return self.api.get("/ops/%i" % realm_id)


    def realm_subscription(self, realm_id):
        return self.api.get("/subscriptions/%i" % realm_id)