# Minecraft PlayFab Library # Created by Li # Public Domain import requests import os import json import binascii import base64 import struct import hashlib import datetime from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import padding # Production PlayFab Environment TITLE_ID = "20CA2" TITLE_SHARED_SECRET = "S8RS53ZEIGMYTYG856U3U19AORWXQXF41J7FT3X9YCWAC7I35X" PLAYFAB_HEADERS = { "User-Agent": "libhttpclient/1.0.0.0", "Content-Type": "application/json", "Accept-Language": "en-US" } PLAYFAB_SESSION = requests.Session() PLAYFAB_SESSION.headers.update(PLAYFAB_HEADERS) PLAYFAB_DOMAIN = "https://" + TITLE_ID.lower() + ".playfabapi.com" SETTING_FILE = "settings.json" PLAYFAB_SETTINGS = {} def sendPlayFabRequest(endpoint, data, hdrs={}): rsp = PLAYFAB_SESSION.post(PLAYFAB_DOMAIN+endpoint, json=data, headers=hdrs).json() if rsp['code'] != 200: print(rsp) else: return rsp['data'] def genCustomId(): return "MCPF"+binascii.hexlify(os.urandom(16)).decode("UTF-8").upper() def genPlayerSecret(): return base64.b64encode(os.urandom(32)).decode("UTF-8") def getMojangCsp(): return base64.b64decode(sendPlayFabRequest("/Client/GetTitlePublicKey", { "TitleId":TITLE_ID, "TitleSharedSecret": TITLE_SHARED_SECRET })['RSAPublicKey']) def importCspKey(csp): e = struct.unpack("I", csp[0x10:0x14])[0] n = bytearray(csp[0x14:]) n.reverse() n = int(binascii.hexlify(n), 16) return rsa.RSAPublicNumbers(e, n).public_key() def genPlayFabTimestamp(): return datetime.datetime.now().isoformat()+"Z" def genPlayFabSignature(requestBody, timestamp): sha256 = hashlib.sha256() sha256.update(requestBody.encode("UTF-8") + b"." + timestamp.encode("UTF-8") + b"." + configGet("PLAYER_SECRET").encode("UTF-8")) return base64.b64encode(sha256.digest()) def configLoad(): global PLAYFAB_SETTINGS global SETTING_FILE if os.path.exists(SETTING_FILE): PLAYFAB_SETTINGS = json.loads(open(SETTING_FILE, "r").read()) def configGet(key): global PLAYFAB_SETTINGS configLoad() if key in PLAYFAB_SETTINGS: return PLAYFAB_SETTINGS[key] return None def configSet(key, newValue): global PLAYFAB_SETTINGS configLoad() PLAYFAB_SETTINGS[key] = newValue open(SETTING_FILE, "w").write(json.dumps(PLAYFAB_SETTINGS)) return newValue def LoginWithCustomId(): global TITLE_ID customId = configGet("CUSTOM_ID") playerSecret = configGet("PLAYER_SECRET") createNewAccount = False if customId == None: customId = genCustomId() createNewAccount = True if playerSecret == None: playerSecret = genPlayerSecret() createNewAccount = True configSet("CUSTOM_ID", customId) configSet("PLAYER_SECRET", playerSecret) payload = { "CreateAccount" : None, "CustomId": None, "EncryptedRequest" : None, "InfoRequestParameters" : { "GetCharacterInventories" : False, "GetCharacterList" : False, "GetPlayerProfile" : True, "GetPlayerStatistics" : False, "GetTitleData" : False, "GetUserAccountInfo" : True, "GetUserData" : False, "GetUserInventory" : False, "GetUserReadOnlyData" : False, "GetUserVirtualCurrency" : False, "PlayerStatisticNames" : None, "ProfileConstraints" : None, "TitleDataKeys" : None, "UserDataKeys" : None, "UserReadOnlyDataKeys" : None }, "PlayerSecret" : None, "TitleId" : TITLE_ID } req = None if createNewAccount: toEnc = json.dumps({"CustomId":customId, "PlayerSecret": playerSecret}).encode("UTF-8") pubkey = importCspKey(getMojangCsp()) payload["CreateAccount"] = True payload["EncryptedRequest"] = base64.b64encode(pubkey.encrypt(toEnc, padding.PKCS1v15())).decode("UTF-8") req = sendPlayFabRequest("/Client/LoginWithCustomID", payload) else: payload["CustomId"] = customId ts = genPlayFabTimestamp() sig = genPlayFabSignature(json.dumps(payload), ts) req = sendPlayFabRequest("/Client/LoginWithCustomID", payload, {"X-PlayFab-Signature": sig, "X-PlayFab-Timestamp": ts}) entitytoken = req["EntityToken"]["EntityToken"] PLAYFAB_SESSION.headers.update({"X-EntityToken": entitytoken}) return req def GetEntityToken(playfabId, accType): req = sendPlayFabRequest("/Authentication/GetEntityToken", { "Entity" : { "Id" : playfabId, "Type" : accType } }) entitytoken = req["EntityToken"] PLAYFAB_SESSION.headers.update({"X-EntityToken": entitytoken}) return req def GetPublishedItem(itemId): return sendPlayFabRequest("/Catalog/GetPublishedItem", { "ETag": "", "ItemId": itemId}) def Search(query, sfilter, orderBy, select, top, skip): return sendPlayFabRequest("/Catalog/Search", { "count": True, "query": query, "filter": sfilter, "orderBy": orderBy, "scid": "4fc10100-5f7a-4470-899b-280835760c07", "select": select, "top": top, "skip": skip })