@tinyburg/pocket-planes-sdk 0.0.0-stage → 0.1.0
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 +8 -2
- package/dist/Endpoints.d.ts +239 -0
- package/dist/Endpoints.d.ts.map +1 -0
- package/dist/Endpoints.js +383 -0
- package/dist/Endpoints.js.map +1 -0
- package/dist/PocketPlanes.d.ts +115 -0
- package/dist/PocketPlanes.d.ts.map +1 -0
- package/dist/PocketPlanes.js +246 -0
- package/dist/PocketPlanes.js.map +1 -0
- package/dist/Scopes.d.ts +78 -0
- package/dist/Scopes.d.ts.map +1 -0
- package/dist/Scopes.js +83 -0
- package/dist/Scopes.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -3
- package/src/Endpoints.ts +407 -0
- package/src/PocketPlanes.ts +264 -0
- package/src/Scopes.ts +92 -0
- package/src/index.ts +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Pocket Planes (game code `pp`) NBSync api endpoints.
|
|
6
|
+
*
|
|
7
|
+
* Signing: a random `salt` (signed int32 - the game emits negatives too) goes
|
|
8
|
+
* in the path, and `hash` is `md5(<preimage> + <game secret salt>)` where the
|
|
9
|
+
* preimage is `pp/<playerId>/<salt><playerAuthKey>` (variants noted per
|
|
10
|
+
* endpoint). The secret salt is the value extracted from `NBSync._ss`.
|
|
11
|
+
*
|
|
12
|
+
* Verification (against the live server + Frida inspection of the game,
|
|
13
|
+
* 2026-08-26):
|
|
14
|
+
* - Signing is MD5, VERIFIED server-side: `player_details` accepts the MD5 hash
|
|
15
|
+
* and rejects a garbage hash, no hash, and a SHA-256 hash. (The game itself
|
|
16
|
+
* computes SHA-256 in `NBSync.genHash` for the unauthenticated `register`
|
|
17
|
+
* call, which the server also accepts via MD5 - so it does not affect the
|
|
18
|
+
* authenticated endpoints this SDK signs.)
|
|
19
|
+
* - Response envelopes VERIFIED by reading the game's DTO classes at runtime
|
|
20
|
+
* (immune to the metadata obfuscation): register (NBSyncRegisterResponse),
|
|
21
|
+
* player_details (NBSyncPlayerDetailsResponse), sync/pull
|
|
22
|
+
* (NBSyncGameSaveResponse: success/h/id/data), current_version
|
|
23
|
+
* (NBSyncCurrentCheckResponse), verify_device (NBSyncVerifyCodeResponse:
|
|
24
|
+
* success/player_id/player_ss/player_email - no photo/nickname on pp),
|
|
25
|
+
* get_visits/get_gifts (NBSyncItemsGetResponse), send_item
|
|
26
|
+
* (NBSyncItemsSendResponse), receive_item (NBSyncItemsReceiveResponse: also
|
|
27
|
+
* carries an `id`), friend/pull_game (NBSyncFriendGameSaveResponse),
|
|
28
|
+
* friend/pull_meta (NBSyncFriendGameMetaResponse). register/player_details/
|
|
29
|
+
* get_visits/get_gifts/request_items were also exercised live with a burnbot.
|
|
30
|
+
* - Still open: pp appears to have NO snapshots (`sync/current_snapshots`
|
|
31
|
+
* returns Invalid Request), so `pull_snapshot` here is unconfirmed and may
|
|
32
|
+
* not exist. register_email is DERIVED from Tiny Tower.
|
|
33
|
+
*
|
|
34
|
+
* Game-specific payload/item schemas (the `gifts` element - real shape is
|
|
35
|
+
* NBSyncItemsGift {gift_id, gift_to, gift_from, gift_type, item_type, gift_str,
|
|
36
|
+
* h, c} - friend meta, save push meta) are `Schema.Unknown` placeholders for
|
|
37
|
+
* the data-schema work.
|
|
38
|
+
*
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @category Endpoints
|
|
41
|
+
*/
|
|
42
|
+
export * as Endpoints from "./Endpoints.js";
|
|
43
|
+
/**
|
|
44
|
+
* Pocket Planes SDK: signed calls against Nimblebit's NBSync service.
|
|
45
|
+
*
|
|
46
|
+
* Signing reuses `@tinyburg/nimblebit-sdk` (`NimblebitAuth`): the game secret
|
|
47
|
+
* salt (from `NBSync._ss`) is provided as the auth key, and each request is
|
|
48
|
+
* `md5(<preimage> + <secret salt>)` with a random uint32 `salt` in the path.
|
|
49
|
+
* All method preimages below are the Tiny Tower formula with the `pp` game code.
|
|
50
|
+
*
|
|
51
|
+
* The methods that were exercised live against sync.nimblebit.com are marked
|
|
52
|
+
* VERIFIED; save-bearing / write paths follow the same pattern but were not
|
|
53
|
+
* exercised. Save bytes are returned raw (base64-decoded); inflating (pako) and
|
|
54
|
+
* parsing into a save schema is left to the caller.
|
|
55
|
+
*
|
|
56
|
+
* @since 1.0.0
|
|
57
|
+
* @category SDK
|
|
58
|
+
*/
|
|
59
|
+
export * as PocketPlanes from "./PocketPlanes.js";
|
|
60
|
+
/**
|
|
61
|
+
* The scopes the Pocket Planes api is guarded by, as a game-rooted tree, built with
|
|
62
|
+
* `@tinyburg/nimblebit-sdk`'s `defineGame`. Only the endpoints this sdk defines
|
|
63
|
+
* are covered; areas without both a read and a write side are left out until
|
|
64
|
+
* their endpoints exist.
|
|
65
|
+
*
|
|
66
|
+
* @since 1.0.0
|
|
67
|
+
* @category Scopes
|
|
68
|
+
*/
|
|
69
|
+
export * as Scopes from "./Scopes.js";
|
|
70
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,OAAO,KAAK,SAAS,MAAM,gBAAgB;AAE3C;;;;;;;;;;;;;;;;AAgBA,OAAO,KAAK,YAAY,MAAM,mBAAmB;AAEjD;;;;;;;;;AASA,OAAO,KAAK,MAAM,MAAM,aAAa","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinyburg/pocket-planes-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "api for the mobile game Pocket Planes by Nimblebit",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nimblebit",
|
|
7
|
+
"pocketplanes"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/leonitousconforti/tinyburg",
|
|
10
|
+
"license": "GPL-3.0-only",
|
|
11
|
+
"author": "Leo Conforti <leo@leoconforti.us> (https://leoconforti.us)",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/leonitousconforti/tinyburg.git",
|
|
15
|
+
"directory": "packages/pocket-planes-sdk"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src/**/*.ts",
|
|
19
|
+
"dist/**/*.js",
|
|
20
|
+
"dist/**/*.js.map",
|
|
21
|
+
"dist/**/*.d.ts",
|
|
22
|
+
"dist/**/*.d.ts.map"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"exports": {
|
|
27
|
+
"./package.json": "./package.json",
|
|
28
|
+
".": "./dist/index.js",
|
|
29
|
+
"./*": "./dist/*.js",
|
|
30
|
+
"./internal/*": null
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@tinyburg/nimblebit-sdk": "0.2.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@yielded/auth": "0.1.0-beta.47",
|
|
40
|
+
"effect": "4.1.1"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@yielded/auth": "0.1.0-beta.47",
|
|
44
|
+
"effect": "4.1.1"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -b tsconfig.build.json && babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
|
|
48
|
+
"check": "tsc -b tsconfig.json",
|
|
49
|
+
"codegen": "build-utils prepare-v4"
|
|
50
|
+
}
|
|
6
51
|
}
|
package/src/Endpoints.ts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pocket Planes (game code `pp`) NBSync api endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Signing: a random `salt` (signed int32 - the game emits negatives too) goes
|
|
5
|
+
* in the path, and `hash` is `md5(<preimage> + <game secret salt>)` where the
|
|
6
|
+
* preimage is `pp/<playerId>/<salt><playerAuthKey>` (variants noted per
|
|
7
|
+
* endpoint). The secret salt is the value extracted from `NBSync._ss`.
|
|
8
|
+
*
|
|
9
|
+
* Verification (against the live server + Frida inspection of the game,
|
|
10
|
+
* 2026-08-26):
|
|
11
|
+
* - Signing is MD5, VERIFIED server-side: `player_details` accepts the MD5 hash
|
|
12
|
+
* and rejects a garbage hash, no hash, and a SHA-256 hash. (The game itself
|
|
13
|
+
* computes SHA-256 in `NBSync.genHash` for the unauthenticated `register`
|
|
14
|
+
* call, which the server also accepts via MD5 - so it does not affect the
|
|
15
|
+
* authenticated endpoints this SDK signs.)
|
|
16
|
+
* - Response envelopes VERIFIED by reading the game's DTO classes at runtime
|
|
17
|
+
* (immune to the metadata obfuscation): register (NBSyncRegisterResponse),
|
|
18
|
+
* player_details (NBSyncPlayerDetailsResponse), sync/pull
|
|
19
|
+
* (NBSyncGameSaveResponse: success/h/id/data), current_version
|
|
20
|
+
* (NBSyncCurrentCheckResponse), verify_device (NBSyncVerifyCodeResponse:
|
|
21
|
+
* success/player_id/player_ss/player_email - no photo/nickname on pp),
|
|
22
|
+
* get_visits/get_gifts (NBSyncItemsGetResponse), send_item
|
|
23
|
+
* (NBSyncItemsSendResponse), receive_item (NBSyncItemsReceiveResponse: also
|
|
24
|
+
* carries an `id`), friend/pull_game (NBSyncFriendGameSaveResponse),
|
|
25
|
+
* friend/pull_meta (NBSyncFriendGameMetaResponse). register/player_details/
|
|
26
|
+
* get_visits/get_gifts/request_items were also exercised live with a burnbot.
|
|
27
|
+
* - Still open: pp appears to have NO snapshots (`sync/current_snapshots`
|
|
28
|
+
* returns Invalid Request), so `pull_snapshot` here is unconfirmed and may
|
|
29
|
+
* not exist. register_email is DERIVED from Tiny Tower.
|
|
30
|
+
*
|
|
31
|
+
* Game-specific payload/item schemas (the `gifts` element - real shape is
|
|
32
|
+
* NBSyncItemsGift {gift_id, gift_to, gift_from, gift_type, item_type, gift_str,
|
|
33
|
+
* h, c} - friend meta, save push meta) are `Schema.Unknown` placeholders for
|
|
34
|
+
* the data-schema work.
|
|
35
|
+
*
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category Endpoints
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup } from "effect/http-api";
|
|
41
|
+
import * as Schema from "effect/Schema";
|
|
42
|
+
|
|
43
|
+
import * as NimblebitConfig from "@tinyburg/nimblebit-sdk/NimblebitConfig";
|
|
44
|
+
import * as OAuthScope from "@yielded/auth/OAuthScope";
|
|
45
|
+
|
|
46
|
+
import * as Scopes from "./Scopes.ts";
|
|
47
|
+
|
|
48
|
+
/** @internal */
|
|
49
|
+
export const U32 = Schema.Int.check(Schema.isUint32());
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
export const ApiErrors = [
|
|
53
|
+
HttpApiError.Forbidden,
|
|
54
|
+
HttpApiError.BadRequest,
|
|
55
|
+
HttpApiError.Unauthorized,
|
|
56
|
+
HttpApiError.InternalServerError,
|
|
57
|
+
] as const;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* NBSync returns errors as `{ error: <string> }`. Observed values include
|
|
61
|
+
* `"Invalid Request"`, `"NoSave"`, and `"SaveNotFound"`.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export const ErrorResponse = Schema.Struct({ error: Schema.String });
|
|
66
|
+
|
|
67
|
+
/** @internal */
|
|
68
|
+
export const FoundResponse = Schema.Struct({ success: Schema.Literal("Found") });
|
|
69
|
+
|
|
70
|
+
/** @internal */
|
|
71
|
+
export const NotFoundResponse = Schema.Struct({ success: Schema.Literal("NotFound") });
|
|
72
|
+
|
|
73
|
+
/** @internal */
|
|
74
|
+
export const SavedResponse = Schema.Struct({ success: Schema.Literal("Saved") });
|
|
75
|
+
|
|
76
|
+
/** @internal */
|
|
77
|
+
export const NotSavedResponse = Schema.Struct({ success: Schema.Literal("NotSaved") });
|
|
78
|
+
|
|
79
|
+
/** @internal */
|
|
80
|
+
export const SentResponse = Schema.Struct({ success: Schema.Literal("Sent") });
|
|
81
|
+
|
|
82
|
+
/** @internal */
|
|
83
|
+
export const NotSentResponse = Schema.Struct({ success: Schema.Literal("NotSent") });
|
|
84
|
+
|
|
85
|
+
/** @internal */
|
|
86
|
+
export const ReceivedResponse = Schema.Struct({ success: Schema.Literal("Received") });
|
|
87
|
+
|
|
88
|
+
/** @internal */
|
|
89
|
+
export const NotReceivedResponse = Schema.Struct({ success: Schema.Literal("NotReceived") });
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* One gift/visit item. Shape is game-specific; left open for the data-schema
|
|
93
|
+
* work. Verified only that `get_visits`/`get_gifts` return `{ gifts: [...] }`.
|
|
94
|
+
*
|
|
95
|
+
* @since 1.0.0
|
|
96
|
+
* @category Schemas
|
|
97
|
+
*/
|
|
98
|
+
export const Gift = Schema.Unknown;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Player summary. VERIFIED shape:
|
|
102
|
+
* `{"success":"Found","player":{"player_id","registered","email","blacklisted","saveVersion","h"}}`.
|
|
103
|
+
*
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
export const DeviceNewPlayerEndpoint = HttpApiEndpoint.get("DeviceNewPlayer", "/register/pp/:salt1/:salt2/:hash", {
|
|
107
|
+
params: { salt1: U32, salt2: U32, hash: Schema.String },
|
|
108
|
+
error: ApiErrors,
|
|
109
|
+
success: [
|
|
110
|
+
ErrorResponse,
|
|
111
|
+
Schema.Struct({
|
|
112
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
113
|
+
playerSs: NimblebitConfig.PlayerAuthKeySchema,
|
|
114
|
+
}).pipe(Schema.encodeKeys({ playerId: "player_id", playerSs: "player_ss" })),
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/** @internal */
|
|
119
|
+
export const DevicePlayerDetailsEndpoint = HttpApiEndpoint.get(
|
|
120
|
+
"DevicePlayerDetails",
|
|
121
|
+
"/player_details/pp/:playerId/:salt/:hash",
|
|
122
|
+
{
|
|
123
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
124
|
+
error: ApiErrors,
|
|
125
|
+
success: [
|
|
126
|
+
ErrorResponse,
|
|
127
|
+
Schema.Struct({
|
|
128
|
+
success: Schema.Literal("Found"),
|
|
129
|
+
player: Schema.Struct({
|
|
130
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
131
|
+
playerEmail: NimblebitConfig.PlayerEmailSchema,
|
|
132
|
+
registered: Schema.NumberFromString.pipe(Schema.decodeTo(Schema.BooleanFromBit)),
|
|
133
|
+
blacklisted: Schema.NumberFromString.pipe(Schema.decodeTo(Schema.BooleanFromBit)),
|
|
134
|
+
saveVersion: Schema.NumberFromString.pipe(Schema.decodeTo(Schema.Int)),
|
|
135
|
+
checksum: Schema.String,
|
|
136
|
+
}).pipe(Schema.encodeKeys({ playerId: "player_id", playerEmail: "email", checksum: "h" })),
|
|
137
|
+
}),
|
|
138
|
+
],
|
|
139
|
+
}
|
|
140
|
+
).annotate(OAuthScope.Required, Scopes.Device.read.player_details.required);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* DERIVED from Tiny Tower; not yet confirmed for pp.
|
|
144
|
+
*
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
export const DeviceVerifyDeviceEndpoint = HttpApiEndpoint.get(
|
|
148
|
+
"DeviceVerifyDevice",
|
|
149
|
+
"/verify_device/pp/:playerId/:verificationCode",
|
|
150
|
+
{
|
|
151
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, verificationCode: Schema.String },
|
|
152
|
+
error: ApiErrors,
|
|
153
|
+
success: [
|
|
154
|
+
ErrorResponse,
|
|
155
|
+
Schema.Struct({
|
|
156
|
+
success: Schema.Literal("NewDevice"),
|
|
157
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
158
|
+
playerAuthKey: NimblebitConfig.PlayerAuthKeySchema,
|
|
159
|
+
playerEmail: NimblebitConfig.PlayerEmailSchema,
|
|
160
|
+
}).pipe(
|
|
161
|
+
Schema.encodeKeys({ playerId: "player_id", playerAuthKey: "player_ss", playerEmail: "player_email" })
|
|
162
|
+
),
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
).annotate(OAuthScope.Required, Scopes.Device.write.verify_device.required);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* DERIVED from Tiny Tower; not yet confirmed for pp.
|
|
169
|
+
*
|
|
170
|
+
* @internal
|
|
171
|
+
*/
|
|
172
|
+
export const DeviceRegisterEmailEndpoint = HttpApiEndpoint.post(
|
|
173
|
+
"DeviceRegisterEmail",
|
|
174
|
+
"/register_email/pp/:playerId/:salt/:hash",
|
|
175
|
+
{
|
|
176
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
177
|
+
payload: Schema.Struct({ promote: Schema.Literal(1), email: Schema.Redacted(Schema.String) }),
|
|
178
|
+
error: ApiErrors,
|
|
179
|
+
success: [
|
|
180
|
+
ErrorResponse,
|
|
181
|
+
Schema.Struct({ success: Schema.Literal("NewEmail") }),
|
|
182
|
+
Schema.Struct({ success: Schema.Literal("NewDevice") }),
|
|
183
|
+
],
|
|
184
|
+
}
|
|
185
|
+
).annotate(OAuthScope.Required, Scopes.Device.write.register_email.required);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Pull the player's cloud save. `data` is base64 pako-deflated bytes (parse into
|
|
189
|
+
* the game save schema separately). VERIFIED the no-save case returns
|
|
190
|
+
* `{ error: "SaveNotFound" }`; the Found envelope is DERIVED from Tiny Tower.
|
|
191
|
+
*
|
|
192
|
+
* @internal
|
|
193
|
+
*/
|
|
194
|
+
export const SyncPullSaveEndpoint = HttpApiEndpoint.get("SyncPullSave", "/sync/pull/pp/:playerId/:salt/:hash", {
|
|
195
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
196
|
+
error: ApiErrors,
|
|
197
|
+
success: [
|
|
198
|
+
ErrorResponse,
|
|
199
|
+
NotFoundResponse,
|
|
200
|
+
Schema.Struct({
|
|
201
|
+
success: Schema.Literal("Found"),
|
|
202
|
+
data: Schema.Uint8ArrayFromBase64,
|
|
203
|
+
checksum: Schema.String,
|
|
204
|
+
saveId: Schema.NumberFromString,
|
|
205
|
+
}).pipe(Schema.encodeKeys({ checksum: "h", saveId: "id" })),
|
|
206
|
+
],
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* VERIFIED the no-save case returns `{ error: "NoSave" }`; Found envelope
|
|
211
|
+
* DERIVED from Tiny Tower.
|
|
212
|
+
*
|
|
213
|
+
* @internal
|
|
214
|
+
*/
|
|
215
|
+
export const SyncCheckForNewerSaveEndpoint = HttpApiEndpoint.get(
|
|
216
|
+
"SyncCheckForNewerSave",
|
|
217
|
+
"/sync/current_version/pp/:playerId/:salt/:hash",
|
|
218
|
+
{
|
|
219
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
220
|
+
error: ApiErrors,
|
|
221
|
+
success: [
|
|
222
|
+
ErrorResponse,
|
|
223
|
+
NotFoundResponse,
|
|
224
|
+
Schema.Struct({
|
|
225
|
+
success: Schema.Literal("Found"),
|
|
226
|
+
checksum: Schema.String,
|
|
227
|
+
saveId: Schema.NumberFromString,
|
|
228
|
+
}).pipe(Schema.encodeKeys({ checksum: "h", saveId: "id" })),
|
|
229
|
+
],
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* VERIFIED: `/get_visits/pp/:playerId/:salt/:hash` -> `{ success:"Found", gifts:[...], total }`.
|
|
235
|
+
*
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
export const SocialGetVisitsEndpoint = HttpApiEndpoint.get("SocialGetVisits", "/get_visits/pp/:playerId/:salt/:hash", {
|
|
239
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
240
|
+
error: ApiErrors,
|
|
241
|
+
success: [
|
|
242
|
+
ErrorResponse,
|
|
243
|
+
NotFoundResponse,
|
|
244
|
+
Schema.Struct({ success: Schema.Literal("Found"), gifts: Schema.Array(Gift), total: Schema.Int }),
|
|
245
|
+
],
|
|
246
|
+
}).annotate(OAuthScope.Required, Scopes.Social.read.get_visits.required);
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* VERIFIED: `/get_gifts/pp/:playerId/:salt/:hash` -> `{ success:"Found", gifts:[...], total }`.
|
|
250
|
+
*
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
export const SocialGetGiftsEndpoint = HttpApiEndpoint.get("SocialGetGifts", "/get_gifts/pp/:playerId/:salt/:hash", {
|
|
254
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
255
|
+
error: ApiErrors,
|
|
256
|
+
success: [
|
|
257
|
+
ErrorResponse,
|
|
258
|
+
NotFoundResponse,
|
|
259
|
+
Schema.Struct({ success: Schema.Literal("Found"), gifts: Schema.Array(Gift), total: Schema.Int }),
|
|
260
|
+
],
|
|
261
|
+
}).annotate(OAuthScope.Required, Scopes.Social.read.get_gifts.required);
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* VERIFIED: `/request_items/pp/:playerId/:salt/:hash` -> `{ success:"Found", gifts:[...] }`
|
|
265
|
+
* (note: no `total`, unlike get_visits/get_gifts). Pocket Planes specific.
|
|
266
|
+
*
|
|
267
|
+
* @internal
|
|
268
|
+
*/
|
|
269
|
+
export const SocialRequestItemsEndpoint = HttpApiEndpoint.get(
|
|
270
|
+
"SocialRequestItems",
|
|
271
|
+
"/request_items/pp/:playerId/:salt/:hash",
|
|
272
|
+
{
|
|
273
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
274
|
+
error: ApiErrors,
|
|
275
|
+
success: [
|
|
276
|
+
ErrorResponse,
|
|
277
|
+
NotFoundResponse,
|
|
278
|
+
Schema.Struct({ success: Schema.Literal("Found"), gifts: Schema.Array(Gift) }),
|
|
279
|
+
],
|
|
280
|
+
}
|
|
281
|
+
).annotate(OAuthScope.Required, Scopes.Social.read.request_items.required);
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* DERIVED from Tiny Tower. Sends an item to a friend (write path, not exercised).
|
|
285
|
+
*
|
|
286
|
+
* @internal
|
|
287
|
+
*/
|
|
288
|
+
export const SocialSendItemEndpoint = HttpApiEndpoint.post(
|
|
289
|
+
"SocialSendItem",
|
|
290
|
+
"/send_item/pp/:itemType/:playerId/:friendId/:salt/:hash",
|
|
291
|
+
{
|
|
292
|
+
params: {
|
|
293
|
+
itemType: Schema.String,
|
|
294
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
295
|
+
friendId: NimblebitConfig.PlayerIdSchema,
|
|
296
|
+
salt: U32,
|
|
297
|
+
hash: Schema.String,
|
|
298
|
+
},
|
|
299
|
+
payload: Schema.Struct({ itemStr: Schema.String }),
|
|
300
|
+
error: ApiErrors,
|
|
301
|
+
success: [ErrorResponse, SentResponse, NotSentResponse],
|
|
302
|
+
}
|
|
303
|
+
).annotate(OAuthScope.Required, Scopes.Social.write.send_item.required);
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Preimage `pp/<playerId>/<giftId>/<salt><playerAuthKey>` (path derived from Tiny
|
|
307
|
+
* Tower). Response DTO NBSyncItemsReceiveResponse also carries an `id`.
|
|
308
|
+
*
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
311
|
+
export const SocialReceiveItemEndpoint = HttpApiEndpoint.get(
|
|
312
|
+
"SocialReceiveItem",
|
|
313
|
+
"/receive_item/pp/:playerId/:giftId/:salt/:hash",
|
|
314
|
+
{
|
|
315
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, giftId: Schema.Int, salt: U32, hash: Schema.String },
|
|
316
|
+
error: ApiErrors,
|
|
317
|
+
success: [
|
|
318
|
+
ErrorResponse,
|
|
319
|
+
Schema.Struct({ success: Schema.Literal("Received"), id: Schema.optionalKey(Schema.String) }),
|
|
320
|
+
NotReceivedResponse,
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
).annotate(OAuthScope.Required, Scopes.Social.write.receive_item.required);
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* DERIVED from Tiny Tower; preimage `pp/<playerId>/<salt><friendId><playerAuthKey>`.
|
|
327
|
+
* `meta` element shape is game-specific (placeholder).
|
|
328
|
+
*
|
|
329
|
+
* @internal
|
|
330
|
+
*/
|
|
331
|
+
export const SocialPullFriendMetaEndpoint = HttpApiEndpoint.post(
|
|
332
|
+
"SocialPullFriendMeta",
|
|
333
|
+
"/friend/pull_meta/pp/:playerId/:salt/:hash",
|
|
334
|
+
{
|
|
335
|
+
params: { playerId: NimblebitConfig.PlayerIdSchema, salt: U32, hash: Schema.String },
|
|
336
|
+
payload: Schema.Struct({ friends: NimblebitConfig.PlayerIdSchema }),
|
|
337
|
+
error: ApiErrors,
|
|
338
|
+
success: [
|
|
339
|
+
ErrorResponse,
|
|
340
|
+
NotFoundResponse,
|
|
341
|
+
Schema.Struct({
|
|
342
|
+
success: Schema.Literal("Found"),
|
|
343
|
+
meta: Schema.Record(NimblebitConfig.PlayerIdSchema, Schema.Unknown),
|
|
344
|
+
}),
|
|
345
|
+
],
|
|
346
|
+
}
|
|
347
|
+
).annotate(OAuthScope.Required, Scopes.Social.read.pull_friend_meta.required);
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* DERIVED from Tiny Tower; preimage `pp/<playerId>/<friendId>/<salt><playerAuthKey>`.
|
|
351
|
+
*
|
|
352
|
+
* @internal
|
|
353
|
+
*/
|
|
354
|
+
export const SocialPullFriendGameEndpoint = HttpApiEndpoint.get(
|
|
355
|
+
"SocialPullFriendGame",
|
|
356
|
+
"/friend/pull_game/pp/:playerId/:friendId/:salt/:hash",
|
|
357
|
+
{
|
|
358
|
+
params: {
|
|
359
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
360
|
+
friendId: NimblebitConfig.PlayerIdSchema,
|
|
361
|
+
salt: U32,
|
|
362
|
+
hash: Schema.String,
|
|
363
|
+
},
|
|
364
|
+
error: ApiErrors,
|
|
365
|
+
success: [
|
|
366
|
+
ErrorResponse,
|
|
367
|
+
NotFoundResponse,
|
|
368
|
+
Schema.Struct({
|
|
369
|
+
success: Schema.Literal("Found"),
|
|
370
|
+
data: Schema.Uint8ArrayFromBase64,
|
|
371
|
+
checksum: Schema.String,
|
|
372
|
+
saveId: Schema.NumberFromString,
|
|
373
|
+
playerId: NimblebitConfig.PlayerIdSchema,
|
|
374
|
+
}).pipe(Schema.encodeKeys({ checksum: "h", saveId: "id", playerId: "player_id" })),
|
|
375
|
+
],
|
|
376
|
+
}
|
|
377
|
+
).annotate(OAuthScope.Required, Scopes.Social.read.pull_friend_game.required);
|
|
378
|
+
|
|
379
|
+
/** @internal */
|
|
380
|
+
export const DeviceManagementGroup = HttpApiGroup.make("DeviceManagementGroup")
|
|
381
|
+
.add(DeviceNewPlayerEndpoint)
|
|
382
|
+
.add(DevicePlayerDetailsEndpoint)
|
|
383
|
+
.add(DeviceVerifyDeviceEndpoint)
|
|
384
|
+
.add(DeviceRegisterEmailEndpoint);
|
|
385
|
+
|
|
386
|
+
/** @internal */
|
|
387
|
+
export const SyncManagementGroup = HttpApiGroup.make("SyncManagementGroup")
|
|
388
|
+
.add(SyncPullSaveEndpoint)
|
|
389
|
+
.add(SyncCheckForNewerSaveEndpoint);
|
|
390
|
+
|
|
391
|
+
/** @internal */
|
|
392
|
+
export const SocialGroup = HttpApiGroup.make("SocialGroup")
|
|
393
|
+
.add(SocialGetVisitsEndpoint)
|
|
394
|
+
.add(SocialGetGiftsEndpoint)
|
|
395
|
+
.add(SocialRequestItemsEndpoint)
|
|
396
|
+
.add(SocialSendItemEndpoint)
|
|
397
|
+
.add(SocialReceiveItemEndpoint)
|
|
398
|
+
.add(SocialPullFriendMetaEndpoint)
|
|
399
|
+
.add(SocialPullFriendGameEndpoint);
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* The Pocket Planes NBSync api.
|
|
403
|
+
*
|
|
404
|
+
* @since 1.0.0
|
|
405
|
+
* @category Api
|
|
406
|
+
*/
|
|
407
|
+
export const Api = HttpApi.make("PocketPlanesApi").add(DeviceManagementGroup).add(SyncManagementGroup).add(SocialGroup);
|