gplayapi-ts
Version:
Google Play API wrapper in TypeScript
146 lines • 4.65 kB
JavaScript
import { promises as fs } from "fs";
import { Pixel3a } from "../devices/factory";
import { GPTokenExpiredError, GPNetworkError } from "../errors";
import { doRequest, createDefaultHeaders } from "../utils/network";
import { unmarshalResponseWrapper } from "../utils/parser";
import { AuthEndpoint } from "./endpoints/auth";
import { AppsEndpoint } from "./endpoints/apps";
import { LibraryEndpoint } from "./endpoints/library";
import { HttpStatusCode } from "../models/common";
class GooglePlayClient {
authData;
deviceInfo;
sessionFile;
auth;
apps;
library;
constructor(authData, deviceInfo = Pixel3a) {
this.authData = authData;
this.deviceInfo = deviceInfo;
this.auth = new AuthEndpoint(
this.authData,
this.deviceInfo,
() => this.getAuthHeaders(),
(url, options) => this.doAuthedReq(url, options)
);
this.apps = new AppsEndpoint(
(url, options) => this.doAuthedReq(url, options)
);
this.library = new LibraryEndpoint(
(url, options) => this.doAuthedReq(url, options),
(packageName) => this.getAppDetails(packageName)
);
}
static async newClient(email, aasToken) {
return GooglePlayClient.newClientWithDeviceInfo(email, aasToken, Pixel3a);
}
static async newClientWithDeviceInfo(email, aasToken, deviceInfo) {
const authData = {
email,
aasToken,
locale: "en_GB"
};
const client = new GooglePlayClient(authData, deviceInfo);
await client.auth.generateGsfId();
const deviceConfigRes = await client.auth.uploadDeviceConfig();
authData.deviceConfigToken = deviceConfigRes.uploadDeviceConfigToken;
const token = await client.auth.generateGPToken();
authData.authToken = token;
await client.auth.toc();
return client;
}
async saveSession(file) {
await fs.writeFile(file, JSON.stringify(this.authData, null, 2));
}
static async loadSession(file) {
return GooglePlayClient.loadSessionWithDeviceInfo(file, Pixel3a);
}
static async loadSessionWithDeviceInfo(file, deviceInfo) {
const data = await fs.readFile(file, "utf-8");
const authData = JSON.parse(data);
const client = new GooglePlayClient(authData, deviceInfo);
return client;
}
async _doAuthedReq(url, options = {}) {
const { data, status } = await doRequest(url, {
...options,
headers: {
...this.getDefaultHeaders(),
...options.headers || {}
}
});
if (status === HttpStatusCode.UNAUTHORIZED) {
throw new GPTokenExpiredError();
}
if (status >= 400) {
throw new GPNetworkError(`Request failed with status ${status}`, status);
}
const resp = await unmarshalResponseWrapper(data);
return resp.payload || null;
}
async doAuthedReq(url, options = {}) {
try {
return await this._doAuthedReq(url, options);
} catch (error) {
if (error instanceof GPTokenExpiredError) {
await this.auth.regenerateGPToken();
if (this.sessionFile) {
await this.saveSession(this.sessionFile);
}
return await this._doAuthedReq(url, options);
}
throw error;
}
}
getAuthHeaders() {
return {
App: "com.google.android.gms",
"User-Agent": this.deviceInfo.authUserAgent,
...this.authData.gsfId && { Device: this.authData.gsfId }
};
}
getDefaultHeaders() {
const data = this.authData;
const headers = createDefaultHeaders(
this.deviceInfo.userAgent,
data.gsfId,
data.authToken
);
if (data.deviceCheckInConsistencyToken) {
headers["X-Dfe-Device-Checkin-Consistency-Token"] = data.deviceCheckInConsistencyToken;
}
if (data.deviceConfigToken) {
headers["X-Dfe-Device-Config-Token"] = data.deviceConfigToken;
}
if (data.dfeCookie) {
headers["X-Dfe-Cookie"] = data.dfeCookie;
}
if (this.deviceInfo.simOperator) {
headers["X-Dfe-Mccmnc"] = this.deviceInfo.simOperator;
}
return headers;
}
// Convenience methods
async getAppDetails(packageName) {
return this.apps.getAppDetails(packageName);
}
async getBuyResponse(packageName, version) {
return this.library.getBuyResponse(packageName, version);
}
async getDeliveryResponse(packageName, version) {
return this.library.getDeliveryResponse(packageName, version);
}
async purchase(packageName, version) {
return this.library.purchase(packageName, version);
}
async generateGsfId() {
return this.auth.generateGsfId();
}
async regenerateGPToken() {
return this.auth.regenerateGPToken();
}
}
export {
GooglePlayClient
};
//# sourceMappingURL=client.mjs.map