@efffrida/gplayapi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +1 -0
- package/devices/arm64_xxhdpi.properties +36 -0
- package/dist/GooglePlayApi.d.ts +88 -0
- package/dist/GooglePlayApi.d.ts.map +1 -0
- package/dist/GooglePlayApi.js +161 -0
- package/dist/GooglePlayApi.js.map +1 -0
- package/dist/generated/GooglePlay_pb.d.ts +8517 -0
- package/dist/generated/GooglePlay_pb.d.ts.map +1 -0
- package/dist/generated/GooglePlay_pb.js +1495 -0
- package/dist/generated/GooglePlay_pb.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/auth.d.ts +2 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +166 -0
- package/dist/internal/auth.js.map +1 -0
- package/dist/internal/device.d.ts +2 -0
- package/dist/internal/device.d.ts.map +1 -0
- package/dist/internal/device.js +85 -0
- package/dist/internal/device.js.map +1 -0
- package/dist/internal/http.d.ts +2 -0
- package/dist/internal/http.d.ts.map +1 -0
- package/dist/internal/http.js +88 -0
- package/dist/internal/http.js.map +1 -0
- package/package.json +71 -0
- package/protobuf/GooglePlay.proto +2264 -0
- package/src/GooglePlayApi.ts +234 -0
- package/src/generated/GooglePlay_pb.ts +10561 -0
- package/src/index.ts +11 -0
- package/src/internal/auth.ts +220 -0
- package/src/internal/device.ts +96 -0
- package/src/internal/http.ts +194 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type * as HttpClientError from "@effect/platform/HttpClientError";
|
|
2
|
+
import type * as ParseResult from "effect/ParseResult";
|
|
3
|
+
|
|
4
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
5
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
6
|
+
import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Function from "effect/Function";
|
|
9
|
+
import * as Layer from "effect/Layer";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
AndroidCheckinRequestSchema,
|
|
14
|
+
AndroidCheckinResponseSchema,
|
|
15
|
+
UploadDeviceConfigRequestSchema,
|
|
16
|
+
} from "../generated/GooglePlay_pb.js";
|
|
17
|
+
import { type Device } from "./device.js";
|
|
18
|
+
import { decodeResponse, decodeResponseFromResponseWrapper, encodeRequest } from "./http.js";
|
|
19
|
+
|
|
20
|
+
/** @internal */
|
|
21
|
+
export const makeHttpClient: (
|
|
22
|
+
device: Device
|
|
23
|
+
) => Layer.Layer<
|
|
24
|
+
HttpClient.HttpClient,
|
|
25
|
+
HttpClientError.HttpClientError | ParseResult.ParseError,
|
|
26
|
+
HttpClient.HttpClient
|
|
27
|
+
> = Effect.fnUntraced(function* (device: Device) {
|
|
28
|
+
// curl \
|
|
29
|
+
// --request GET \
|
|
30
|
+
// --header "Accept: application/json" \
|
|
31
|
+
// --header "User-Agent: com.aurora.store-4.7.5-71" \
|
|
32
|
+
// "https://auroraoss.com/api/auth"
|
|
33
|
+
const account = yield* Function.pipe(
|
|
34
|
+
HttpClientRequest.get("https://auroraoss.com/api/auth"),
|
|
35
|
+
HttpClientRequest.setHeader("User-Agent", "com.aurora.store-4.7.5-71"),
|
|
36
|
+
HttpClientRequest.acceptJson,
|
|
37
|
+
HttpClient.execute,
|
|
38
|
+
Effect.flatMap(HttpClientResponse.filterStatusOk),
|
|
39
|
+
Effect.flatMap(
|
|
40
|
+
HttpClientResponse.schemaBodyJson(
|
|
41
|
+
Schema.Struct({
|
|
42
|
+
email: Schema.String,
|
|
43
|
+
auth: Schema.String,
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const checkinResponse = yield* Function.pipe(
|
|
50
|
+
HttpClientRequest.post("https://android.clients.google.com/checkin"),
|
|
51
|
+
HttpClientRequest.setHeaders({
|
|
52
|
+
"User-Agent": device.userAgent,
|
|
53
|
+
app: "com.google.android.gms",
|
|
54
|
+
Host: "android.clients.google.com",
|
|
55
|
+
}),
|
|
56
|
+
encodeRequest(AndroidCheckinRequestSchema, {
|
|
57
|
+
id: 0n,
|
|
58
|
+
version: 3,
|
|
59
|
+
fragment: 0,
|
|
60
|
+
locale: "en",
|
|
61
|
+
timeZone: device.TimeZone,
|
|
62
|
+
checkin: {
|
|
63
|
+
userNumber: 0,
|
|
64
|
+
lastCheckinMsec: 0n,
|
|
65
|
+
roaming: device.Roaming,
|
|
66
|
+
cellOperator: device.CellOperator,
|
|
67
|
+
simOperator: device.SimOperator ?? "",
|
|
68
|
+
build: {
|
|
69
|
+
id: device["Build.FINGERPRINT"],
|
|
70
|
+
product: device["Build.HARDWARE"],
|
|
71
|
+
carrier: device["Build.BRAND"],
|
|
72
|
+
radio: device["Build.RADIO"],
|
|
73
|
+
bootloader: device["Build.BOOTLOADER"],
|
|
74
|
+
device: device["Build.DEVICE"],
|
|
75
|
+
sdkVersion: device["Build.VERSION.SDK_INT"],
|
|
76
|
+
model: device["Build.MODEL"],
|
|
77
|
+
manufacturer: device["Build.MANUFACTURER"],
|
|
78
|
+
buildProduct: device["Build.PRODUCT"],
|
|
79
|
+
client: device["Client"],
|
|
80
|
+
timestamp: BigInt(Date.now()),
|
|
81
|
+
googleServices: device["GSF.version"],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
deviceConfiguration: {
|
|
85
|
+
touchScreen: device["TouchScreen"],
|
|
86
|
+
keyboard: device["Keyboard"],
|
|
87
|
+
navigation: device["Navigation"],
|
|
88
|
+
screenLayout: device["ScreenLayout"],
|
|
89
|
+
hasHardKeyboard: device["HasHardKeyboard"],
|
|
90
|
+
hasFiveWayNavigation: device["HasFiveWayNavigation"],
|
|
91
|
+
glEsVersion: device["GL.Version"],
|
|
92
|
+
glExtension: device["GL.Extensions"] as Array<string>,
|
|
93
|
+
systemSharedLibrary: device["SharedLibraries"] as Array<string>,
|
|
94
|
+
systemAvailableFeature: device["Features"] as Array<string>,
|
|
95
|
+
nativePlatform: device["Platforms"] as Array<string>,
|
|
96
|
+
screenDensity: device["Screen.Density"],
|
|
97
|
+
screenWidth: device["Screen.Width"],
|
|
98
|
+
screenHeight: device["Screen.Height"],
|
|
99
|
+
systemSupportedLocale: device["Locales"] as Array<string>,
|
|
100
|
+
deviceClass: 0,
|
|
101
|
+
deviceFeature: device["Features"].map((val) => ({ name: val, value: 0 })),
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
Effect.flatMap(HttpClient.execute),
|
|
105
|
+
Effect.flatMap(HttpClientResponse.filterStatusOk),
|
|
106
|
+
Effect.flatMap(decodeResponse(AndroidCheckinResponseSchema))
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const uploadDeviceConfigResponse = yield* Function.pipe(
|
|
110
|
+
HttpClientRequest.post("https://android.clients.google.com/fdfe/uploadDeviceConfig"),
|
|
111
|
+
HttpClientRequest.setHeaders({
|
|
112
|
+
"X-DFE-Encoded-Targets": "",
|
|
113
|
+
"User-Agent": device.userAgent,
|
|
114
|
+
"X-DFE-Cookie": "",
|
|
115
|
+
"X-DFE-Content-Filters": "",
|
|
116
|
+
"X-DFE-Device-Checkin-Consistency-Token": checkinResponse.deviceCheckinConsistencyToken,
|
|
117
|
+
"X-DFE-Device-Config-Token": "",
|
|
118
|
+
"X-DFE-MCCMNC": "21601",
|
|
119
|
+
"X-DFE-Client-Id": "am-android-google",
|
|
120
|
+
"X-DFE-UserLanguages": "en",
|
|
121
|
+
"X-DFE-Phenotype": "",
|
|
122
|
+
"X-DFE-Device-Id": checkinResponse.androidId.toString(16),
|
|
123
|
+
"X-DFE-Network-Type": "4",
|
|
124
|
+
"Accept-Language": "en",
|
|
125
|
+
"X-DFE-Request-Params": "timeoutMs=4000",
|
|
126
|
+
"X-DFE-Enabled-Experiments": "cl:billing.select_add_instrument_by_default",
|
|
127
|
+
"X-DFE-Unsupported-Experiments":
|
|
128
|
+
"nocache:billing.use_charging_poller,market_emails,buyer_currency,prod_baseline,checkin.set_asset_paid_app_field,shekel_test,content_ratings,buyer_currency_in_app,nocache:encrypted_apk,recent_changes",
|
|
129
|
+
Host: "android.clients.google.com",
|
|
130
|
+
}),
|
|
131
|
+
encodeRequest(UploadDeviceConfigRequestSchema, {
|
|
132
|
+
deviceConfiguration: {
|
|
133
|
+
touchScreen: Number(device["TouchScreen"]),
|
|
134
|
+
keyboard: Number(device["Keyboard"]),
|
|
135
|
+
navigation: Number(device["Navigation"]),
|
|
136
|
+
screenLayout: Number(device["ScreenLayout"]),
|
|
137
|
+
hasHardKeyboard: Boolean(device["HasHardKeyboard"]),
|
|
138
|
+
hasFiveWayNavigation: Boolean(device["HasFiveWayNavigation"]),
|
|
139
|
+
glEsVersion: Number(device["GL.Version"]),
|
|
140
|
+
glExtension: device["GL.Extensions"] as Array<string>,
|
|
141
|
+
systemSharedLibrary: device["SharedLibraries"] as Array<string>,
|
|
142
|
+
systemAvailableFeature: device["Features"] as Array<string>,
|
|
143
|
+
nativePlatform: device["Platforms"] as Array<string>,
|
|
144
|
+
screenDensity: Number(device["Screen.Density"]),
|
|
145
|
+
screenWidth: Number(device["Screen.Width"]),
|
|
146
|
+
screenHeight: Number(device["Screen.Height"]),
|
|
147
|
+
systemSupportedLocale: device["Locales"] as Array<string>,
|
|
148
|
+
deviceClass: 0,
|
|
149
|
+
deviceFeature: device["Features"].map((val) => ({ name: val, value: 0 })),
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
Effect.flatMap(HttpClient.execute),
|
|
153
|
+
Effect.flatMap(HttpClientResponse.filterStatusOk),
|
|
154
|
+
Effect.flatMap(decodeResponseFromResponseWrapper("uploadDeviceConfigResponse"))
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// const authResponse = yield* Function.pipe(
|
|
158
|
+
// HttpClientRequest.post("https://android.clients.google.com/auth"),
|
|
159
|
+
// HttpClientRequest.setHeaders({
|
|
160
|
+
// app: "com.google.android.gms",
|
|
161
|
+
// device: checkinResponse.androidId.toString(16),
|
|
162
|
+
// "User-Agent": `GoogleAuth/1.4 (${device["Build.DEVICE"]} ${device["Build.ID"]})`,
|
|
163
|
+
// }),
|
|
164
|
+
// HttpClientRequest.setUrlParams({
|
|
165
|
+
// app: "com.android.vending",
|
|
166
|
+
// oauth2_foreground: "1",
|
|
167
|
+
// Email: account.email,
|
|
168
|
+
// token_request_options: "CAA4AVAB",
|
|
169
|
+
// client_sig: "38918a453d07199354f8b19af05ec6562ced5788",
|
|
170
|
+
// Token: account.auth,
|
|
171
|
+
// google_play_services_version: `${device["GSF.version"]}`,
|
|
172
|
+
// check_email: "1",
|
|
173
|
+
// system_partition: "1",
|
|
174
|
+
// sdk_version: `${device["Build.VERSION.SDK_INT"]}`,
|
|
175
|
+
// callerPkg: "com.google.android.gms",
|
|
176
|
+
// device_country: "IN",
|
|
177
|
+
// lang: "en",
|
|
178
|
+
// androidId: checkinResponse.androidId.toString(16),
|
|
179
|
+
// callerSig: "38918a453d07199354f8b19af05ec6562ced5788",
|
|
180
|
+
// service: "oauth2:https://www.googleapis.com/auth/googleplay",
|
|
181
|
+
// }),
|
|
182
|
+
// HttpClient.execute,
|
|
183
|
+
// Effect.flatMap(HttpClientResponse.filterStatusOk),
|
|
184
|
+
// Effect.flatMap((response) => response.text)
|
|
185
|
+
// );
|
|
186
|
+
|
|
187
|
+
return Layer.function(
|
|
188
|
+
HttpClient.HttpClient,
|
|
189
|
+
HttpClient.HttpClient,
|
|
190
|
+
Function.flow(
|
|
191
|
+
HttpClient.filterStatusOk,
|
|
192
|
+
HttpClient.mapRequest(
|
|
193
|
+
HttpClientRequest.updateUrl((url) =>
|
|
194
|
+
url.startsWith("/fdfe/") ? `https://android.clients.google.com${url}` : url
|
|
195
|
+
)
|
|
196
|
+
),
|
|
197
|
+
HttpClient.mapRequest(
|
|
198
|
+
HttpClientRequest.setHeaders({
|
|
199
|
+
Authorization: "Bearer " + account.auth,
|
|
200
|
+
"User-Agent": device.userAgent,
|
|
201
|
+
"X-DFE-Device-Id": checkinResponse.androidId.toString(16),
|
|
202
|
+
"Accept-Language": "en",
|
|
203
|
+
"X-DFE-Encoded-Targets":
|
|
204
|
+
"CAESN/qigQYC2AMBFfUbyA7SM5Ij/CvfBoIDgxHqGP8R3xzIBvoQtBKFDZ4HAY4FrwSVMasHBO0O2Q8akgYRAQECAQO7AQEpKZ0CnwECAwRrAQYBr9PPAoK7sQMBAQMCBAkIDAgBAwEDBAICBAUZEgMEBAMLAQEBBQEBAcYBARYED+cBfS8CHQEKkAEMMxcBIQoUDwYHIjd3DQ4MFk0JWGYZEREYAQOLAYEBFDMIEYMBAgICAgICOxkCD18LGQKEAcgDBIQBAgGLARkYCy8oBTJlBCUocxQn0QUBDkkGxgNZQq0BZSbeAmIDgAEBOgGtAaMCDAOQAZ4BBIEBKUtQUYYBQscDDxPSARA1oAEHAWmnAsMB2wFyywGLAxol+wImlwOOA80CtwN26A0WjwJVbQEJPAH+BRDeAfkHK/ABASEBCSAaHQemAzkaRiu2Ad8BdXeiAwEBGBUBBN4LEIABK4gB2AFLfwECAdoENq0CkQGMBsIBiQEtiwGgA1zyAUQ4uwS8AwhsvgPyAcEDF27vApsBHaICGhl3GSKxAR8MC6cBAgItmQYG9QIeywLvAeYBDArLAh8HASI4ELICDVmVBgsY/gHWARtcAsMBpALiAdsBA7QBpAJmIArpByn0AyAKBwHTARIHAX8D+AMBcRIBBbEDmwUBMacCHAciNp0BAQF0OgQLJDuSAh54kwFSP0eeAQQ4M5EBQgMEmwFXywFo0gFyWwMcapQBBugBPUW2AVgBKmy3AR6PAbMBGQxrUJECvQR+8gFoWDsYgQNwRSczBRXQAgtRswEW0ALMAREYAUEBIG6yATYCRE8OxgER8gMBvQEDRkwLc8MBTwHZAUOnAXiiBakDIbYBNNcCIUmuArIBSakBrgFHKs0EgwV/G3AD0wE6LgECtQJ4xQFwFbUCjQPkBS6vAQqEAUZF3QIM9wEhCoYCQhXsBCyZArQDugIziALWAdIBlQHwBdUErQE6qQaSA4EEIvYBHir9AQVLmgMCApsCKAwHuwgrENsBAjNYswEVmgIt7QJnN4wDEnta+wGfAcUBxgEtEFXQAQWdAUAeBcwBAQM7rAEJATJ0LENrdh73A6UBhAE+qwEeASxLZUMhDREuH0CGARbd7K0GlQo",
|
|
205
|
+
"X-DFE-Phenotype":
|
|
206
|
+
"H4sIAAAAAAAAAB3OO3KjMAAA0KRNuWXukBkBQkAJ2MhgAZb5u2GCwQZbCH_EJ77QHmgvtDtbv-Z9_H63zXXU0NVPB1odlyGy7751Q3CitlPDvFd8lxhz3tpNmz7P92CFw73zdHU2Ie0Ad2kmR8lxhiErTFLt3RPGfJQHSDy7Clw10bg8kqf2owLokN4SecJTLoSwBnzQSd652_MOf2d1vKBNVedzg4ciPoLz2mQ8efGAgYeLou-l-PXn_7Sna1MfhHuySxt-4esulEDp8Sbq54CPPKjpANW-lkU2IZ0F92LBI-ukCKSptqeq1eXU96LD9nZfhKHdtjSWwJqUm_2r6pMHOxk01saVanmNopjX3YxQafC4iC6T55aRbC8nTI98AF_kItIQAJb5EQxnKTO7TZDWnr01HVPxelb9A2OWX6poidMWl16K54kcu_jhXw-JSBQkVcD_fPsLSZu6joIBAAA",
|
|
207
|
+
"X-DFE-Client-Id": "am-android-google",
|
|
208
|
+
"X-DFE-Network-Type": "4",
|
|
209
|
+
"X-DFE-Content-Filters": "",
|
|
210
|
+
"X-Limit-Ad-Tracking-Enabled": "false",
|
|
211
|
+
"X-Ad-Id": "",
|
|
212
|
+
"X-DFE-UserLanguages": "en",
|
|
213
|
+
"X-DFE-Request-Params": "timeoutMs=4000",
|
|
214
|
+
"X-DFE-Device-Checkin-Consistency-Token": checkinResponse.deviceCheckinConsistencyToken,
|
|
215
|
+
"X-DFE-Device-Config-Token": uploadDeviceConfigResponse.uploadDeviceConfigToken,
|
|
216
|
+
})
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
}, Layer.unwrapEffect);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type * as PlatformError from "@effect/platform/Error";
|
|
2
|
+
import type * as ParseResult from "effect/ParseResult";
|
|
3
|
+
|
|
4
|
+
import * as FileSystem from "@effect/platform/FileSystem";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as PropertiesFile from "properties-file";
|
|
8
|
+
|
|
9
|
+
/** @internal */
|
|
10
|
+
export class Device extends Schema.Class<Device>("Device")({
|
|
11
|
+
UserReadableName: Schema.String,
|
|
12
|
+
"Build.BOOTLOADER": Schema.String,
|
|
13
|
+
"Build.BRAND": Schema.String,
|
|
14
|
+
"Build.DEVICE": Schema.String,
|
|
15
|
+
"Build.FINGERPRINT": Schema.String,
|
|
16
|
+
"Build.HARDWARE": Schema.String,
|
|
17
|
+
"Build.ID": Schema.String,
|
|
18
|
+
"Build.MANUFACTURER": Schema.String,
|
|
19
|
+
"Build.MODEL": Schema.String,
|
|
20
|
+
"Build.PRODUCT": Schema.String,
|
|
21
|
+
"Build.RADIO": Schema.String,
|
|
22
|
+
"Build.VERSION.RELEASE": Schema.String,
|
|
23
|
+
"Build.VERSION.SDK_INT": Schema.NumberFromString,
|
|
24
|
+
CellOperator: Schema.String,
|
|
25
|
+
Client: Schema.String,
|
|
26
|
+
Features: Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
27
|
+
encode: (features) => features.join(","),
|
|
28
|
+
decode: (str) => str.split(","),
|
|
29
|
+
}),
|
|
30
|
+
"GL.Extensions": Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
31
|
+
encode: (extensions) => extensions.join(","),
|
|
32
|
+
decode: (str) => str.split(","),
|
|
33
|
+
}),
|
|
34
|
+
"GL.Version": Schema.NumberFromString,
|
|
35
|
+
"GSF.version": Schema.NumberFromString,
|
|
36
|
+
HasFiveWayNavigation: Schema.BooleanFromString,
|
|
37
|
+
HasHardKeyboard: Schema.BooleanFromString,
|
|
38
|
+
Keyboard: Schema.NumberFromString,
|
|
39
|
+
Locales: Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
40
|
+
encode: (locales) => locales.join(","),
|
|
41
|
+
decode: (str) => str.split(","),
|
|
42
|
+
}),
|
|
43
|
+
Navigation: Schema.NumberFromString,
|
|
44
|
+
Platforms: Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
45
|
+
encode: (libs) => libs.join(","),
|
|
46
|
+
decode: (str) => str.split(","),
|
|
47
|
+
}),
|
|
48
|
+
Roaming: Schema.String,
|
|
49
|
+
"Screen.Density": Schema.NumberFromString,
|
|
50
|
+
"Screen.Height": Schema.NumberFromString,
|
|
51
|
+
"Screen.Width": Schema.NumberFromString,
|
|
52
|
+
ScreenLayout: Schema.NumberFromString,
|
|
53
|
+
SharedLibraries: Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
54
|
+
encode: (libs) => libs.join(","),
|
|
55
|
+
decode: (str) => str.split(","),
|
|
56
|
+
}),
|
|
57
|
+
SimCountry: Schema.String.pipe(Schema.optional),
|
|
58
|
+
SimOperator: Schema.String.pipe(Schema.optional),
|
|
59
|
+
TimeZone: Schema.String,
|
|
60
|
+
TouchScreen: Schema.NumberFromString,
|
|
61
|
+
"Vending.version": Schema.NumberFromString,
|
|
62
|
+
"Vending.versionString": Schema.String,
|
|
63
|
+
}) {
|
|
64
|
+
public static fromPropertiesFile = (
|
|
65
|
+
path: string
|
|
66
|
+
): Effect.Effect<Device, PlatformError.PlatformError | ParseResult.ParseError, FileSystem.FileSystem> =>
|
|
67
|
+
Effect.gen(function* () {
|
|
68
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
69
|
+
const content = yield* fileSystem.readFileString(path);
|
|
70
|
+
const properties = PropertiesFile.getProperties(content);
|
|
71
|
+
const device = yield* Schema.decodeUnknown(Device)(properties);
|
|
72
|
+
return device;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
public get userAgent(): string {
|
|
76
|
+
const deviceProperties = {
|
|
77
|
+
api: 3,
|
|
78
|
+
versionCode: this["Vending.version"],
|
|
79
|
+
sdk: this["Build.VERSION.SDK_INT"],
|
|
80
|
+
device: this["Build.DEVICE"],
|
|
81
|
+
hardware: this["Build.HARDWARE"],
|
|
82
|
+
product: this["Build.PRODUCT"],
|
|
83
|
+
platformVersionRelease: this["Build.VERSION.RELEASE"],
|
|
84
|
+
model: this["Build.MODEL"],
|
|
85
|
+
buildId: this["Build.ID"],
|
|
86
|
+
isWideScreen: 0,
|
|
87
|
+
supportedAbis: this["Platforms"].join(";"),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const devicePropertiesString = Object.entries(deviceProperties)
|
|
91
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
92
|
+
.join(",");
|
|
93
|
+
|
|
94
|
+
return `Android-Finsky/${this["Vending.versionString"]} (${devicePropertiesString})`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as Protobuf from "@bufbuild/protobuf";
|
|
2
|
+
import * as HttpClientError from "@effect/platform/HttpClientError";
|
|
3
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
4
|
+
import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Function from "effect/Function";
|
|
7
|
+
import * as Predicate from "effect/Predicate";
|
|
8
|
+
|
|
9
|
+
import { type PayloadSchema, ResponseWrapperSchema } from "../generated/GooglePlay_pb.js";
|
|
10
|
+
|
|
11
|
+
/** @internal */
|
|
12
|
+
export const encodeRequest = Function.dual<
|
|
13
|
+
// Data-last signature
|
|
14
|
+
<Description extends Protobuf.DescMessage>(
|
|
15
|
+
description: Description,
|
|
16
|
+
body: Protobuf.MessageInitShape<Description>,
|
|
17
|
+
options?: Partial<Protobuf.BinaryWriteOptions> | undefined
|
|
18
|
+
) => (
|
|
19
|
+
request: HttpClientRequest.HttpClientRequest
|
|
20
|
+
) => Effect.Effect<HttpClientRequest.HttpClientRequest, HttpClientError.HttpClientError, never>,
|
|
21
|
+
// Data-first signature
|
|
22
|
+
<Description extends Protobuf.DescMessage>(
|
|
23
|
+
request: HttpClientRequest.HttpClientRequest,
|
|
24
|
+
description: Description,
|
|
25
|
+
body: Protobuf.MessageInitShape<Description>,
|
|
26
|
+
options?: Partial<Protobuf.BinaryWriteOptions> | undefined
|
|
27
|
+
) => Effect.Effect<HttpClientRequest.HttpClientRequest, HttpClientError.HttpClientError, never>
|
|
28
|
+
>(
|
|
29
|
+
// Data first if the first argument is an http request
|
|
30
|
+
(arguments_) => Predicate.hasProperty(arguments_[0], HttpClientRequest.TypeId),
|
|
31
|
+
|
|
32
|
+
// Body implementation
|
|
33
|
+
Effect.fn("encodeRequest")(function* <Description extends Protobuf.DescMessage>(
|
|
34
|
+
request: HttpClientRequest.HttpClientRequest,
|
|
35
|
+
description: Description,
|
|
36
|
+
body: Protobuf.MessageInitShape<Description>,
|
|
37
|
+
options?: Partial<Protobuf.BinaryWriteOptions> | undefined
|
|
38
|
+
) {
|
|
39
|
+
const messageType = description.name;
|
|
40
|
+
yield* Effect.annotateCurrentSpan("messageType", messageType);
|
|
41
|
+
|
|
42
|
+
const bytes = yield* Effect.try({
|
|
43
|
+
try: () =>
|
|
44
|
+
Protobuf.toBinary(
|
|
45
|
+
description,
|
|
46
|
+
Protobuf.create(description, body),
|
|
47
|
+
options ?? { writeUnknownFields: true }
|
|
48
|
+
),
|
|
49
|
+
catch: (cause) =>
|
|
50
|
+
new HttpClientError.RequestError({
|
|
51
|
+
cause,
|
|
52
|
+
request,
|
|
53
|
+
reason: "Encode",
|
|
54
|
+
description: `Could not encode message of type ${messageType}`,
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const applyBody = HttpClientRequest.bodyUint8Array(bytes, "application/x-protobuf");
|
|
59
|
+
return applyBody(request);
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/** @internal */
|
|
64
|
+
export const decodeResponse = Function.dual<
|
|
65
|
+
// Data-last signature
|
|
66
|
+
<Desc extends Protobuf.DescMessage>(
|
|
67
|
+
protobufDescription: Desc,
|
|
68
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
69
|
+
) => (
|
|
70
|
+
response: HttpClientResponse.HttpClientResponse
|
|
71
|
+
) => Effect.Effect<Exclude<Protobuf.MessageShape<Desc>, undefined>, HttpClientError.HttpClientError, never>,
|
|
72
|
+
// Data-first signature
|
|
73
|
+
<Desc extends Protobuf.DescMessage>(
|
|
74
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
75
|
+
protobufDescription: Desc,
|
|
76
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
77
|
+
) => Effect.Effect<Exclude<Protobuf.MessageShape<Desc>, undefined>, HttpClientError.HttpClientError, never>
|
|
78
|
+
>(
|
|
79
|
+
// Data first if the first argument is an http response
|
|
80
|
+
(arguments_) => Predicate.hasProperty(arguments_[0], HttpClientResponse.TypeId),
|
|
81
|
+
|
|
82
|
+
// Body implementation
|
|
83
|
+
Effect.fn("decodeResponse")(function* <Desc extends Protobuf.DescMessage>(
|
|
84
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
85
|
+
protobufDescription: Desc,
|
|
86
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
87
|
+
) {
|
|
88
|
+
const messageType = protobufDescription.name;
|
|
89
|
+
yield* Effect.annotateCurrentSpan("messageType", messageType);
|
|
90
|
+
const arrayBuffer = yield* response.arrayBuffer;
|
|
91
|
+
|
|
92
|
+
const message = yield* Effect.try({
|
|
93
|
+
try: () =>
|
|
94
|
+
Protobuf.fromBinary(
|
|
95
|
+
protobufDescription,
|
|
96
|
+
new Uint8Array(arrayBuffer),
|
|
97
|
+
options ?? { readUnknownFields: true }
|
|
98
|
+
),
|
|
99
|
+
catch: (cause) =>
|
|
100
|
+
new HttpClientError.ResponseError({
|
|
101
|
+
cause,
|
|
102
|
+
response,
|
|
103
|
+
reason: "Decode",
|
|
104
|
+
request: response.request,
|
|
105
|
+
description: `Could not decode message of type ${messageType}`,
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (Predicate.isUndefined(message)) {
|
|
110
|
+
return yield* new HttpClientError.ResponseError({
|
|
111
|
+
response,
|
|
112
|
+
reason: "Decode",
|
|
113
|
+
request: response.request,
|
|
114
|
+
description: `Message of type ${messageType} is missing`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return message as Exclude<Protobuf.MessageShape<Desc>, undefined>;
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
/** @internal */
|
|
123
|
+
export const decodeResponseFromResponseWrapper = Function.dual<
|
|
124
|
+
// Data-last signature
|
|
125
|
+
<
|
|
126
|
+
ExtractPayload extends Exclude<
|
|
127
|
+
keyof Protobuf.MessageShape<typeof PayloadSchema>,
|
|
128
|
+
keyof Protobuf.Message<"Payload">
|
|
129
|
+
>,
|
|
130
|
+
>(
|
|
131
|
+
extractPayload: ExtractPayload,
|
|
132
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
133
|
+
) => (
|
|
134
|
+
response: HttpClientResponse.HttpClientResponse
|
|
135
|
+
) => Effect.Effect<
|
|
136
|
+
Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>,
|
|
137
|
+
HttpClientError.HttpClientError,
|
|
138
|
+
never
|
|
139
|
+
>,
|
|
140
|
+
// Data-first signature
|
|
141
|
+
<
|
|
142
|
+
ExtractPayload extends Exclude<
|
|
143
|
+
keyof Protobuf.MessageShape<typeof PayloadSchema>,
|
|
144
|
+
keyof Protobuf.Message<"Payload">
|
|
145
|
+
>,
|
|
146
|
+
>(
|
|
147
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
148
|
+
extractPayload: ExtractPayload,
|
|
149
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
150
|
+
) => Effect.Effect<
|
|
151
|
+
Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>,
|
|
152
|
+
HttpClientError.HttpClientError,
|
|
153
|
+
never
|
|
154
|
+
>
|
|
155
|
+
>(
|
|
156
|
+
// Data first if the first argument is an http response
|
|
157
|
+
(arguments_) => Predicate.hasProperty(arguments_[0], HttpClientResponse.TypeId),
|
|
158
|
+
|
|
159
|
+
// Body implementation
|
|
160
|
+
Effect.fn("decodeResponse")(function* <
|
|
161
|
+
ExtractPayload extends Exclude<
|
|
162
|
+
keyof Protobuf.MessageShape<typeof PayloadSchema>,
|
|
163
|
+
keyof Protobuf.Message<"Payload">
|
|
164
|
+
>,
|
|
165
|
+
>(
|
|
166
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
167
|
+
extractPayload: ExtractPayload,
|
|
168
|
+
options?: Partial<Protobuf.BinaryReadOptions> | undefined
|
|
169
|
+
) {
|
|
170
|
+
type Out = Exclude<Protobuf.MessageShape<typeof PayloadSchema>[ExtractPayload], undefined>;
|
|
171
|
+
const responseWrapper = yield* decodeResponse(response, ResponseWrapperSchema, options);
|
|
172
|
+
|
|
173
|
+
if (Predicate.isNotUndefined(responseWrapper.commands?.displayErrorMessage)) {
|
|
174
|
+
return yield* new HttpClientError.ResponseError({
|
|
175
|
+
response,
|
|
176
|
+
reason: "Decode",
|
|
177
|
+
request: response.request,
|
|
178
|
+
description: responseWrapper.commands.displayErrorMessage,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const extracted = responseWrapper.payload?.[extractPayload];
|
|
183
|
+
if (Predicate.isUndefined(extracted)) {
|
|
184
|
+
return yield* new HttpClientError.ResponseError({
|
|
185
|
+
response,
|
|
186
|
+
reason: "Decode",
|
|
187
|
+
request: response.request,
|
|
188
|
+
description: `ResponseWrapper has no payload of type ${extractPayload}`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return extracted as Out;
|
|
193
|
+
})
|
|
194
|
+
);
|