@lootlog/sdk 0.1.0 → 0.1.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/README.md +261 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,50 +1,271 @@
|
|
|
1
1
|
# @lootlog/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Read Lootlog timers, loot and battle data from your own scripts, bots and server
|
|
4
|
+
applications. Includes typed HTTP functions and a WebSocket client for realtime
|
|
5
|
+
updates. Works with Node.js and Bun; React is not required.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[Documentation](https://developer.lootlog.pl/docs/sdk) ·
|
|
8
|
+
[API reference](https://developer.lootlog.pl/reference)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @lootlog/sdk
|
|
14
|
+
# or
|
|
15
|
+
bun add @lootlog/sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The package uses ES modules and includes TypeScript declarations. Use a runtime
|
|
19
|
+
with built-in `fetch`, such as Node.js 22+ or Bun.
|
|
20
|
+
|
|
21
|
+
## Set up your API key
|
|
22
|
+
|
|
23
|
+
Create a key at [developer.lootlog.pl/keys](https://developer.lootlog.pl/keys).
|
|
24
|
+
Select the Organizations you want to access and read-only permissions to follow
|
|
25
|
+
these examples. The key's owner must also have access to the requested data.
|
|
26
|
+
|
|
27
|
+
Keep the key on your server. Never put it in frontend code or a distributed
|
|
28
|
+
userscript. For in-game addons, use the
|
|
29
|
+
[game client API](https://developer.lootlog.pl/docs/game-client).
|
|
30
|
+
|
|
31
|
+
Set `LOOTLOG_API_KEY` in your deployment secrets or a local `.env` file excluded
|
|
32
|
+
from version control:
|
|
33
|
+
|
|
34
|
+
```dotenv
|
|
35
|
+
LOOTLOG_API_KEY=replace-with-your-key
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Find your organization
|
|
39
|
+
|
|
40
|
+
Save this complete example as `organizations.mjs`:
|
|
41
|
+
|
|
42
|
+
```js
|
|
8
43
|
import { configureLootlogApi } from "@lootlog/sdk";
|
|
9
44
|
import { usersControllerGetCurrentUserGuilds } from "@lootlog/sdk/main";
|
|
10
45
|
|
|
11
|
-
const
|
|
46
|
+
const apiKey = process.env.LOOTLOG_API_KEY;
|
|
47
|
+
if (!apiKey) throw new Error("Set LOOTLOG_API_KEY");
|
|
48
|
+
|
|
49
|
+
configureLootlogApi({ apiKey, environment: "production" });
|
|
50
|
+
|
|
51
|
+
const organizations = await usersControllerGetCurrentUserGuilds({
|
|
52
|
+
signal: AbortSignal.timeout(10_000),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
console.table(
|
|
56
|
+
organizations.map((organization) => ({
|
|
57
|
+
id: organization.id,
|
|
58
|
+
name: organization.name,
|
|
59
|
+
accessible: organization.hasLootlogAccess,
|
|
60
|
+
stale: organization.isAccessDataStale,
|
|
61
|
+
})),
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run it with your `.env` file:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
node --env-file=.env organizations.mjs
|
|
69
|
+
# or: Bun loads .env automatically
|
|
70
|
+
bun organizations.mjs
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Copy an accessible Organization's `id` into `LOOTLOG_GUILD_ID` in your `.env`:
|
|
74
|
+
|
|
75
|
+
```dotenv
|
|
76
|
+
LOOTLOG_GUILD_ID=replace-with-the-organization-id
|
|
77
|
+
LOOTLOG_WORLD=fobos
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The API calls this identifier `guildId`: it is the Organization's Discord guild
|
|
81
|
+
ID. Replace `fobos` with your Margonem world. If `stale` is true, the returned
|
|
82
|
+
access information is outdated.
|
|
83
|
+
|
|
84
|
+
For the development environment, create a key at
|
|
85
|
+
[dev-developer.lootlog.pl/keys](https://dev-developer.lootlog.pl/keys) and use
|
|
86
|
+
`environment: "development"`. Keys are specific to their environment.
|
|
87
|
+
|
|
88
|
+
## Fetch timers and the latest loot
|
|
89
|
+
|
|
90
|
+
Save this complete example as `lootlog.mjs`. It prints NPC spawn windows and the
|
|
91
|
+
latest 20 loot records for your chosen Organization and world.
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
import { configureLootlogApi } from "@lootlog/sdk";
|
|
95
|
+
import {
|
|
96
|
+
lootsControllerFetchLootsByGuildId,
|
|
97
|
+
timersControllerGetTimers,
|
|
98
|
+
} from "@lootlog/sdk/main";
|
|
99
|
+
|
|
100
|
+
const apiKey = process.env.LOOTLOG_API_KEY;
|
|
101
|
+
const guildId = process.env.LOOTLOG_GUILD_ID;
|
|
102
|
+
const world = process.env.LOOTLOG_WORLD;
|
|
103
|
+
if (!apiKey || !guildId || !world) {
|
|
104
|
+
throw new Error("Set LOOTLOG_API_KEY, LOOTLOG_GUILD_ID and LOOTLOG_WORLD");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
configureLootlogApi({ apiKey, environment: "production" });
|
|
108
|
+
|
|
109
|
+
const timers = await timersControllerGetTimers(
|
|
110
|
+
{ guildId },
|
|
111
|
+
{ world },
|
|
112
|
+
{ signal: AbortSignal.timeout(10_000) },
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
console.table(
|
|
116
|
+
timers.map((timer) => ({
|
|
117
|
+
npc: timer.npc.name,
|
|
118
|
+
level: timer.npc.lvl,
|
|
119
|
+
location: timer.npc.location,
|
|
120
|
+
earliestSpawn: timer.minSpawnTime,
|
|
121
|
+
latestSpawn: timer.maxSpawnTime,
|
|
122
|
+
})),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const loots = await lootsControllerFetchLootsByGuildId(
|
|
126
|
+
{ guildId },
|
|
127
|
+
{ world, limit: 20 },
|
|
128
|
+
{ signal: AbortSignal.timeout(10_000) },
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
console.table(
|
|
132
|
+
loots.map((loot) => ({
|
|
133
|
+
id: loot.id,
|
|
134
|
+
date: loot.createdAt,
|
|
135
|
+
npcs: loot.npcs.map((npc) => npc.name).join(", "),
|
|
136
|
+
items: loot.items.map((item) => item.name).join(", "),
|
|
137
|
+
players: loot.players.map((player) => player.name).join(", "),
|
|
138
|
+
})),
|
|
139
|
+
);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Run `node --env-file=.env lootlog.mjs` or `bun lootlog.mjs`.
|
|
143
|
+
|
|
144
|
+
Both functions return arrays of parsed objects. Dates are ISO strings; use
|
|
145
|
+
`new Date(value)` to format them in your time zone. A timer describes a spawn
|
|
146
|
+
window, not confirmation that an NPC is currently alive. Results reflect the
|
|
147
|
+
key's scope and the owner's permissions; an empty array is a successful request
|
|
148
|
+
with no visible results.
|
|
149
|
+
|
|
150
|
+
## Filter loot and fetch the next page
|
|
151
|
+
|
|
152
|
+
In `lootlog.mjs`, replace the `const loots = ...` request with this block to read
|
|
153
|
+
legendary loot from the last seven days:
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
const filters = {
|
|
157
|
+
world,
|
|
158
|
+
limit: 20,
|
|
159
|
+
rarities: ["LEGENDARY"],
|
|
160
|
+
createdAtMin: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const loots = await lootsControllerFetchLootsByGuildId({ guildId }, filters, {
|
|
164
|
+
signal: AbortSignal.timeout(10_000),
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
You can also filter by `npcs`, `players`, `itemNames` and level ranges. Your
|
|
169
|
+
editor shows the available query fields; the
|
|
170
|
+
[API reference](https://developer.lootlog.pl/reference) describes their values.
|
|
171
|
+
|
|
172
|
+
To load one more page, add this after the first request:
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
const lastLoot = loots.at(-1);
|
|
176
|
+
const nextPage = lastLoot
|
|
177
|
+
? await lootsControllerFetchLootsByGuildId(
|
|
178
|
+
{ guildId },
|
|
179
|
+
{ ...filters, cursor: lastLoot.id },
|
|
180
|
+
{ signal: AbortSignal.timeout(10_000) },
|
|
181
|
+
)
|
|
182
|
+
: [];
|
|
183
|
+
|
|
184
|
+
console.log(`Next page: ${nextPage.length} loot records`);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Loot is ordered by descending ID. Pass the last record's `id` as `cursor` and
|
|
188
|
+
keep the same filters to fetch older records. `limit` accepts 1–100. There is
|
|
189
|
+
no `nextCursor` response wrapper; an empty page means there are no more results.
|
|
190
|
+
|
|
191
|
+
## TypeScript and imports
|
|
192
|
+
|
|
193
|
+
Import operations and response types from the service you need:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import {
|
|
197
|
+
timersControllerGetTimers,
|
|
198
|
+
type TimerResponseDto,
|
|
199
|
+
} from "@lootlog/sdk/main";
|
|
200
|
+
|
|
201
|
+
// After configuring the SDK as shown above:
|
|
202
|
+
const timers: TimerResponseDto[] = await timersControllerGetTimers(
|
|
203
|
+
{ guildId: "123456789012345678" },
|
|
204
|
+
{ world: "fobos" },
|
|
205
|
+
);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Return types are inferred, so the annotation is optional. There is no separate
|
|
209
|
+
`@types/lootlog` package. For Node.js TypeScript projects, use `"type": "module"`
|
|
210
|
+
in `package.json`, `module: "NodeNext"` and `moduleResolution: "NodeNext"` in
|
|
211
|
+
`tsconfig.json`, and install `@types/node`.
|
|
212
|
+
|
|
213
|
+
| Import | Contents |
|
|
214
|
+
| -------------------------------- | ------------------------------------------------------------------------------ |
|
|
215
|
+
| `@lootlog/sdk` | Authentication and environment configuration |
|
|
216
|
+
| `@lootlog/sdk/main` | Organizations, timers, loot and main API types |
|
|
217
|
+
| `@lootlog/sdk/activity` | Activity operations and types |
|
|
218
|
+
| `@lootlog/sdk/battlelog` | Battle records and statistics |
|
|
219
|
+
| `@lootlog/sdk/search` | Item, NPC and player search |
|
|
220
|
+
| `@lootlog/sdk/realtime` | WebSocket client and event types |
|
|
221
|
+
| `@lootlog/sdk/openapi/main.json` | OpenAPI specification; also available for `activity`, `battlelog` and `search` |
|
|
222
|
+
|
|
223
|
+
For the HTTP examples above, arguments are **path parameters, query parameters,
|
|
224
|
+
request options**. Other operations may accept a body; follow their generated
|
|
225
|
+
signature.
|
|
226
|
+
|
|
227
|
+
## Handle errors
|
|
228
|
+
|
|
229
|
+
Failed HTTP requests throw an error named `ApiError` with `status` and `data`.
|
|
230
|
+
The constructor is not exported. Wrap your requests in `try`/`catch` and inspect
|
|
231
|
+
the status:
|
|
232
|
+
|
|
233
|
+
```js
|
|
12
234
|
try {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
235
|
+
const timers = await timersControllerGetTimers(
|
|
236
|
+
{ guildId },
|
|
237
|
+
{ world },
|
|
238
|
+
{ signal: AbortSignal.timeout(10_000) },
|
|
239
|
+
);
|
|
240
|
+
console.log(`Loaded ${timers.length} timers`);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const status =
|
|
243
|
+
error instanceof Error && "status" in error ? error.status : undefined;
|
|
244
|
+
|
|
245
|
+
if (status === 401) {
|
|
246
|
+
console.error("Check the API key, expiration and environment");
|
|
247
|
+
} else if (status === 403) {
|
|
248
|
+
console.error("Check the key's scope and your organization permissions");
|
|
249
|
+
} else if (status === 429) {
|
|
250
|
+
console.error("Rate limit reached; wait before making another request");
|
|
251
|
+
} else {
|
|
252
|
+
console.error("Request failed", { status });
|
|
253
|
+
}
|
|
16
254
|
}
|
|
17
255
|
```
|
|
18
256
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`bun run client:generate` projects the reviewed operation inventory and generates
|
|
36
|
-
these functions from service OpenAPI documents. `bun run client:check` detects
|
|
37
|
-
drift; `bun run build` builds portable ESM and bundled declarations. Maintainers
|
|
38
|
-
publish through the repository's **Publish SDK** workflow; see the
|
|
39
|
-
[publication guide](https://github.com/lootlog/monorepo/blob/main/packages/sdk/PUBLISHING.md).
|
|
40
|
-
|
|
41
|
-
The Organization loot count endpoint (`GET /guilds/{guildId}/loots/count`) and
|
|
42
|
-
`lootsControllerCountLootsByGuildId` SDK function have been removed. Integrations
|
|
43
|
-
must stop requesting a total count before the API removal is deployed. Use the
|
|
44
|
-
paginated loot list to browse records; it does not provide a replacement total.
|
|
45
|
-
|
|
46
|
-
Builds use the repository's TypeScript 7 catalog version and tsdown. The shared
|
|
47
|
-
`packages/tsconfig.public-api.json` includes workspace source dependencies so
|
|
48
|
-
declarations are bundled without requiring consumers to install private packages.
|
|
49
|
-
Run `bun run test:package` to install both public packages in an isolated
|
|
50
|
-
consumer and verify all SDK entry points with runtime checks and TypeScript.
|
|
257
|
+
Network errors and timeouts have no HTTP status; `cause` retains the underlying
|
|
258
|
+
failure. The SDK does not automatically retry HTTP requests. Keep errors distinct
|
|
259
|
+
from an empty result and mark previously loaded data stale after a failed refresh.
|
|
260
|
+
|
|
261
|
+
## Next steps
|
|
262
|
+
|
|
263
|
+
`configureLootlogApi` sets process-wide HTTP configuration. Configure it once
|
|
264
|
+
when your application starts. Its returned function restores the previous
|
|
265
|
+
configuration when called. Do not change global configuration for individual requests in a server handling
|
|
266
|
+
multiple keys concurrently.
|
|
267
|
+
|
|
268
|
+
- [Realtime subscriptions](https://developer.lootlog.pl/docs/realtime): receive timer updates over WebSocket.
|
|
269
|
+
- [Keys and permissions](https://developer.lootlog.pl/docs/keys): scope access and rotate credentials.
|
|
270
|
+
- [Errors and limits](https://developer.lootlog.pl/docs/errors): handle failed requests and rate limits.
|
|
271
|
+
- [Changelog](https://developer.lootlog.pl/docs/changelog): published contract changes.
|