PyFab/PlayFab.py

182 lines
5.8 KiB
Python

# 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
})