e621stream 0.0.0 → 0.0.3
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 +20 -12
- package/dist/E621Stream.d.ts +20 -14
- package/dist/E621Stream.js +60 -35
- package/dist/E621Stream.js.map +1 -1
- package/dist/Emitter.d.ts +0 -1
- package/dist/Emitter.js +0 -3
- package/dist/Emitter.js.map +1 -1
- package/dist/changes.d.ts +36 -4
- package/dist/events.d.ts +9 -12
- package/dist/events.js +21 -6
- package/dist/events.js.map +1 -1
- package/dist/generated.d.ts +23 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# e621stream
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A client for [E621Stream](https://e621.stream). Works in node 22+, bun, deno & browsers, has no dependencies, and every event is typed down to the record it has.
|
|
4
4
|
|
|
5
|
-
```
|
|
6
|
-
npm
|
|
5
|
+
```sh
|
|
6
|
+
npm i e621stream
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
```ts
|
|
@@ -18,7 +18,7 @@ stream.on("post.create", (event) => {
|
|
|
18
18
|
console.log(event.data.post.id, event.data.post.tags.general);
|
|
19
19
|
});
|
|
20
20
|
stream.on("post.update", (event) => {
|
|
21
|
-
// only
|
|
21
|
+
// only what changed, tags are per category
|
|
22
22
|
console.log(event.data.changes?.["tags.general"]?.added);
|
|
23
23
|
});
|
|
24
24
|
stream.on("comment.update", (event) => {
|
|
@@ -28,12 +28,20 @@ stream.on("error", err => console.error(err));
|
|
|
28
28
|
stream.connect();
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
- **Reconnecting:** the connection is reopened with backoff when it drops, and resumes from the last event it got, so nothing is missed. It isn't reopened after a close retrying won't fix, like bad credentials. `reconnect: false` turns it off.
|
|
33
|
-
- **Resuming:** pass `cursor` (an event id, or a millisecond timestamp) to replay from there. `stream.cursor` is the last event received.
|
|
34
|
-
- **Changing subscriptions:** `stream.subscribe([...])`.
|
|
35
|
-
- **Private events:** like `dmail.create`, these are polled with your own credentials and have to be subscribed to by name.
|
|
36
|
-
- **Other events:** `ready`, `replayComplete`, `subscribed`, `reconnecting`, `close` and `error`.
|
|
37
|
-
- **Older Node:** pass a WebSocket implementation, like the `ws` package, as the `WebSocket` option.
|
|
31
|
+
Listen for an event type with `on("post.create", ...)`, or everything with `on("dispatch", ...)` (check `event.type` to narrow it). The others are `ready`, `replayComplete`, `subscribed`, `reconnecting`, `close` and `error`. See [Events](https://e621.stream/events/) for what each event is.
|
|
38
32
|
|
|
39
|
-
|
|
33
|
+
#### Options
|
|
34
|
+
| Option | Type | Default | Description |
|
|
35
|
+
|:---:|:---:|:---:|:---:|
|
|
36
|
+
| `subscriptions` | Array | | what to subscribe to, `stream.subscribe([...])` changes it later |
|
|
37
|
+
| `auth` | Object | None | `{ username, apiKey }`, or `{ token }` from [/authorize](https://e621.stream/authorize) |
|
|
38
|
+
| `cursor` | String \| Number | None | an event id or ms timestamp to replay from, `stream.cursor` is the last event you got |
|
|
39
|
+
| `reconnect` | Boolean | `true` | reconnect when the connection drops (not after things like bad credentials) |
|
|
40
|
+
| `client` | String | None | shown in the server's logs |
|
|
41
|
+
| `url` | String | `https://e621.stream` | the websocket url comes from its `/info`, a `ws://` or `wss://` url is connected to directly |
|
|
42
|
+
| `fetch` | Function | global `fetch` | used for `/info` |
|
|
43
|
+
| `WebSocket` | WebSocket | global | for older node, the `ws` package works |
|
|
44
|
+
|
|
45
|
+
Private events like `dmail.create` have to be subscribed to by name.
|
|
46
|
+
|
|
47
|
+
Records like `LegacyPost`, `Comment` and `ForumTopic` come from the [e621 module](https://www.npmjs.com/package/e621)'s types, and are exported here too. Mod action events are typed per action, so checking `event.data.action` narrows `event.data.values`.
|
package/dist/E621Stream.d.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import Emitter from "./Emitter.ts";
|
|
2
2
|
import { type EventType, type PrivateEventType, type StreamEvent, type Subscription } from "./events.ts";
|
|
3
3
|
export interface Options {
|
|
4
|
-
/**
|
|
4
|
+
/** Your e621 username & api key, or a token from https://e621.stream/authorize. */
|
|
5
5
|
auth?: {
|
|
6
6
|
apiKey: string;
|
|
7
7
|
username: string;
|
|
8
|
+
} | {
|
|
9
|
+
token: string;
|
|
8
10
|
};
|
|
9
|
-
/**
|
|
11
|
+
/** Shown in the server's logs. */
|
|
10
12
|
client?: string;
|
|
11
|
-
/**
|
|
13
|
+
/** An event id or millisecond timestamp to replay from. */
|
|
12
14
|
cursor?: number | string;
|
|
13
|
-
/**
|
|
15
|
+
/** Used to get the websocket url from `/info`. */
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
/** Reconnect when the connection drops. */
|
|
14
18
|
reconnect?: boolean;
|
|
15
19
|
subscriptions: Array<Subscription>;
|
|
20
|
+
/** The site, the websocket url comes from its `/info`. A `ws://` or `wss://` url is connected to directly. */
|
|
16
21
|
url?: string;
|
|
17
|
-
/**
|
|
22
|
+
/** For runtimes without a global WebSocket (node < 22), the `ws` package works. */
|
|
18
23
|
WebSocket?: typeof WebSocket;
|
|
19
24
|
}
|
|
20
25
|
export interface Ready {
|
|
21
|
-
/**
|
|
26
|
+
/** If the cursor was older than anything still kept. */
|
|
22
27
|
cursor_expired?: boolean;
|
|
23
28
|
replaying: boolean;
|
|
24
29
|
session_id: string;
|
|
@@ -36,7 +41,7 @@ export interface ClientEvents {
|
|
|
36
41
|
reason: string;
|
|
37
42
|
reconnecting: boolean;
|
|
38
43
|
}];
|
|
39
|
-
/**
|
|
44
|
+
/** Every event, public or private. */
|
|
40
45
|
dispatch: [StreamEvent];
|
|
41
46
|
error: [Error];
|
|
42
47
|
ready: [Ready];
|
|
@@ -44,7 +49,6 @@ export interface ClientEvents {
|
|
|
44
49
|
attempt: number;
|
|
45
50
|
delayMS: number;
|
|
46
51
|
}];
|
|
47
|
-
/** replay is done, live events follow */
|
|
48
52
|
replayComplete: [{
|
|
49
53
|
cursor: string | null;
|
|
50
54
|
events: number;
|
|
@@ -59,8 +63,8 @@ export declare class StreamError extends Error {
|
|
|
59
63
|
constructor(code: number, message: string);
|
|
60
64
|
}
|
|
61
65
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
66
|
+
* A connection to E621Stream, see https://e621.stream
|
|
67
|
+
* @example
|
|
64
68
|
* ```ts
|
|
65
69
|
* const stream = new E621Stream({ auth: { username, apiKey }, subscriptions: ["post.create"] });
|
|
66
70
|
* stream.on("post.create", event => console.log(event.data.post.id));
|
|
@@ -70,12 +74,12 @@ export declare class StreamError extends Error {
|
|
|
70
74
|
export declare class E621Stream extends Emitter<Listeners> {
|
|
71
75
|
private attempt;
|
|
72
76
|
private closing;
|
|
77
|
+
private connecting;
|
|
73
78
|
private heartbeat;
|
|
74
79
|
private lastError;
|
|
75
|
-
private
|
|
80
|
+
private lastHeartbeatAck;
|
|
76
81
|
private reconnectTimer;
|
|
77
82
|
private socket;
|
|
78
|
-
/** the newest event received, which reconnecting resumes from */
|
|
79
83
|
cursor: string | null;
|
|
80
84
|
readonly options: Options & {
|
|
81
85
|
url: string;
|
|
@@ -83,13 +87,15 @@ export declare class E621Stream extends Emitter<Listeners> {
|
|
|
83
87
|
constructor(options: Options);
|
|
84
88
|
private handle;
|
|
85
89
|
private onClose;
|
|
90
|
+
private open;
|
|
86
91
|
private send;
|
|
87
92
|
private startHeartbeat;
|
|
88
93
|
private stopHeartbeat;
|
|
89
|
-
|
|
94
|
+
private websocketURL;
|
|
95
|
+
/** Close the connection. */
|
|
90
96
|
close(): void;
|
|
91
97
|
connect(): this;
|
|
92
|
-
/**
|
|
98
|
+
/** Replace the current subscriptions. */
|
|
93
99
|
subscribe(subscriptions: Array<Subscription>): void;
|
|
94
100
|
}
|
|
95
101
|
export {};
|
package/dist/E621Stream.js
CHANGED
|
@@ -8,7 +8,6 @@ export class StreamError extends Error {
|
|
|
8
8
|
this.name = "StreamError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
// closing for any of these again would just get the same result
|
|
12
11
|
const FATAL = new Set([
|
|
13
12
|
CloseCodes.UNKNOWN_OP,
|
|
14
13
|
CloseCodes.DECODE_ERROR,
|
|
@@ -17,14 +16,14 @@ const FATAL = new Set([
|
|
|
17
16
|
CloseCodes.INVALID_SUBSCRIPTION,
|
|
18
17
|
CloseCodes.INVALID_CURSOR,
|
|
19
18
|
CloseCodes.AUTHENTICATION_EXPIRED,
|
|
19
|
+
CloseCodes.DISCONNECTED,
|
|
20
|
+
CloseCodes.BANNED,
|
|
20
21
|
]);
|
|
21
22
|
const MAX_DELAY_MS = 60_000;
|
|
22
23
|
const HEARTBEAT_MS = 30_000;
|
|
23
|
-
// the server answers heartbeats and pings, nothing for this long means the connection is gone without having closed
|
|
24
|
-
const SILENT_MS = 75_000;
|
|
25
24
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* A connection to E621Stream, see https://e621.stream
|
|
26
|
+
* @example
|
|
28
27
|
* ```ts
|
|
29
28
|
* const stream = new E621Stream({ auth: { username, apiKey }, subscriptions: ["post.create"] });
|
|
30
29
|
* stream.on("post.create", event => console.log(event.data.post.id));
|
|
@@ -34,25 +33,24 @@ const SILENT_MS = 75_000;
|
|
|
34
33
|
export class E621Stream extends Emitter {
|
|
35
34
|
attempt = 0;
|
|
36
35
|
closing = false;
|
|
36
|
+
connecting = false;
|
|
37
37
|
heartbeat = null;
|
|
38
38
|
lastError = null;
|
|
39
|
-
|
|
39
|
+
lastHeartbeatAck = true;
|
|
40
40
|
reconnectTimer = null;
|
|
41
41
|
socket = null;
|
|
42
|
-
/** the newest event received, which reconnecting resumes from */
|
|
43
42
|
cursor;
|
|
44
43
|
options;
|
|
45
44
|
constructor(options) {
|
|
46
45
|
super();
|
|
47
|
-
this.options = { reconnect: true, url: "
|
|
46
|
+
this.options = { reconnect: true, url: "https://e621.stream", ...options };
|
|
48
47
|
this.cursor = options.cursor === undefined ? null : String(options.cursor);
|
|
49
48
|
}
|
|
50
49
|
handle(raw) {
|
|
51
50
|
if (typeof raw !== "string") {
|
|
52
|
-
this.emit("error", new Error("
|
|
51
|
+
this.emit("error", new Error("compression isn't supported"));
|
|
53
52
|
return;
|
|
54
53
|
}
|
|
55
|
-
this.lastMessage = Date.now();
|
|
56
54
|
const message = JSON.parse(raw);
|
|
57
55
|
switch (message.op) {
|
|
58
56
|
case "hello": {
|
|
@@ -60,7 +58,7 @@ export class E621Stream extends Emitter {
|
|
|
60
58
|
this.send({
|
|
61
59
|
op: "identify",
|
|
62
60
|
d: {
|
|
63
|
-
...(auth ? { auth: { username: auth.username, api_key: auth.apiKey } } : {}),
|
|
61
|
+
...(auth ? { auth: "token" in auth ? { token: auth.token } : { username: auth.username, api_key: auth.apiKey } } : {}),
|
|
64
62
|
...(client ? { client } : {}),
|
|
65
63
|
...(this.cursor === null ? {} : { cursor: this.cursor }),
|
|
66
64
|
subscriptions,
|
|
@@ -75,11 +73,10 @@ export class E621Stream extends Emitter {
|
|
|
75
73
|
}
|
|
76
74
|
case "dispatch": {
|
|
77
75
|
const event = { id: message.id, type: message.type, time: message.time, data: message.data, ...(message.private ? { private: true } : {}) };
|
|
78
|
-
// private events aren't in the stream, so they can't be resumed from
|
|
79
76
|
if (!event.private)
|
|
80
77
|
this.cursor = event.id;
|
|
81
78
|
this.emit("dispatch", event);
|
|
82
|
-
// typescript can't
|
|
79
|
+
// typescript can't narrow this
|
|
83
80
|
this.emitUnchecked(event.type, event);
|
|
84
81
|
break;
|
|
85
82
|
}
|
|
@@ -91,6 +88,10 @@ export class E621Stream extends Emitter {
|
|
|
91
88
|
this.emit("subscribed", message.d.subscriptions);
|
|
92
89
|
break;
|
|
93
90
|
}
|
|
91
|
+
case "heartbeat_ack": {
|
|
92
|
+
this.lastHeartbeatAck = true;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
94
95
|
case "error": {
|
|
95
96
|
const { code, message: text } = message.d;
|
|
96
97
|
this.lastError = new StreamError(code, text);
|
|
@@ -110,7 +111,6 @@ export class E621Stream extends Emitter {
|
|
|
110
111
|
if (!reconnecting)
|
|
111
112
|
return;
|
|
112
113
|
this.attempt++;
|
|
113
|
-
// a server that's rate limiting this connection wants a longer break
|
|
114
114
|
const base = code === CloseCodes.RATE_LIMITED ? 30_000 : 1000;
|
|
115
115
|
const delayMS = Math.min(MAX_DELAY_MS, base * 2 ** (this.attempt - 1)) * (0.75 + Math.random() * 0.5);
|
|
116
116
|
this.emit("reconnecting", { attempt: this.attempt, delayMS: Math.round(delayMS) });
|
|
@@ -119,18 +119,32 @@ export class E621Stream extends Emitter {
|
|
|
119
119
|
this.connect();
|
|
120
120
|
}, delayMS);
|
|
121
121
|
}
|
|
122
|
+
open(Socket, url) {
|
|
123
|
+
const socket = this.socket = new Socket(url);
|
|
124
|
+
socket.addEventListener("open", () => { this.startHeartbeat(); });
|
|
125
|
+
socket.addEventListener("message", (event) => {
|
|
126
|
+
try {
|
|
127
|
+
this.handle(event.data);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
socket.addEventListener("close", (event) => { this.onClose(event.code, event.reason); });
|
|
134
|
+
socket.addEventListener("error", () => null);
|
|
135
|
+
}
|
|
122
136
|
send(payload) {
|
|
123
137
|
if (this.socket?.readyState === 1)
|
|
124
138
|
this.socket.send(JSON.stringify(payload));
|
|
125
139
|
}
|
|
126
140
|
startHeartbeat() {
|
|
127
|
-
this.
|
|
141
|
+
this.lastHeartbeatAck = true;
|
|
128
142
|
this.heartbeat = setInterval(() => {
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
this.socket?.close(4999, "no response from the server");
|
|
143
|
+
if (!this.lastHeartbeatAck) {
|
|
144
|
+
this.socket?.close(4999, "heartbeat not acknowledged");
|
|
132
145
|
return;
|
|
133
146
|
}
|
|
147
|
+
this.lastHeartbeatAck = false;
|
|
134
148
|
this.send({ op: "heartbeat" });
|
|
135
149
|
}, HEARTBEAT_MS);
|
|
136
150
|
}
|
|
@@ -139,7 +153,20 @@ export class E621Stream extends Emitter {
|
|
|
139
153
|
clearInterval(this.heartbeat);
|
|
140
154
|
this.heartbeat = null;
|
|
141
155
|
}
|
|
142
|
-
|
|
156
|
+
// fetched every time, so it can move
|
|
157
|
+
async websocketURL() {
|
|
158
|
+
const { url } = this.options;
|
|
159
|
+
if (/^wss?:\/\//i.test(url))
|
|
160
|
+
return url;
|
|
161
|
+
const res = await (this.options.fetch ?? fetch)(new URL("/info", url));
|
|
162
|
+
if (!res.ok)
|
|
163
|
+
throw new Error(`${url}/info returned ${res.status}`);
|
|
164
|
+
const { websocket } = await res.json();
|
|
165
|
+
if (typeof websocket !== "string")
|
|
166
|
+
throw new Error(`${url}/info didn't have a websocket url`);
|
|
167
|
+
return new URL(websocket, url).href;
|
|
168
|
+
}
|
|
169
|
+
/** Close the connection. */
|
|
143
170
|
close() {
|
|
144
171
|
this.closing = true;
|
|
145
172
|
if (this.reconnectTimer)
|
|
@@ -148,30 +175,28 @@ export class E621Stream extends Emitter {
|
|
|
148
175
|
this.socket?.close(1000, "closed by client");
|
|
149
176
|
}
|
|
150
177
|
connect() {
|
|
151
|
-
if (this.socket)
|
|
178
|
+
if (this.socket || this.connecting)
|
|
152
179
|
return this;
|
|
153
180
|
this.closing = false;
|
|
154
|
-
//
|
|
181
|
+
// not present before node 22
|
|
155
182
|
const global = globalThis.WebSocket;
|
|
156
183
|
const Socket = this.options.WebSocket ?? global;
|
|
157
184
|
if (!Socket)
|
|
158
|
-
throw new Error("
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
185
|
+
throw new Error("no WebSocket available, pass one with the WebSocket option");
|
|
186
|
+
this.connecting = true;
|
|
187
|
+
this.websocketURL().then((url) => {
|
|
188
|
+
this.connecting = false;
|
|
189
|
+
if (!this.closing)
|
|
190
|
+
this.open(Socket, url);
|
|
191
|
+
}, (err) => {
|
|
192
|
+
this.connecting = false;
|
|
193
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
194
|
+
// retried the same as a dropped connection
|
|
195
|
+
this.onClose(1006, "couldn't get the websocket url");
|
|
168
196
|
});
|
|
169
|
-
socket.addEventListener("close", (event) => { this.onClose(event.code, event.reason); });
|
|
170
|
-
// the close event that follows has the details
|
|
171
|
-
socket.addEventListener("error", () => null);
|
|
172
197
|
return this;
|
|
173
198
|
}
|
|
174
|
-
/**
|
|
199
|
+
/** Replace the current subscriptions. */
|
|
175
200
|
subscribe(subscriptions) {
|
|
176
201
|
this.options.subscriptions = subscriptions;
|
|
177
202
|
this.send({ op: "subscribe", d: { subscriptions } });
|
package/dist/E621Stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"E621Stream.js","sourceRoot":"","sources":["../lib/E621Stream.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,UAAU,EAA8E,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"E621Stream.js","sourceRoot":"","sources":["../lib/E621Stream.ts"],"names":[],"mappings":"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"}
|
package/dist/Emitter.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export default class Emitter<Events extends {
|
|
|
4
4
|
}> {
|
|
5
5
|
private listeners;
|
|
6
6
|
protected emit<K extends keyof Events>(event: K, ...args: Events[K]): void;
|
|
7
|
-
/** for an event name only known at runtime, whose listeners' arguments can't be checked */
|
|
8
7
|
protected emitUnchecked(event: keyof Events, ...args: Array<unknown>): void;
|
|
9
8
|
off<K extends keyof Events>(event: K, listener: NoInfer<Listener<Events[K]>>): this;
|
|
10
9
|
on<K extends keyof Events>(event: K, listener: NoInfer<Listener<Events[K]>>): this;
|
package/dist/Emitter.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
// a minimal typed event emitter, so the client has no dependencies and works anywhere
|
|
2
1
|
export default class Emitter {
|
|
3
|
-
// listeners of every event share one map, so they're stored without their argument types
|
|
4
2
|
listeners = new Map();
|
|
5
3
|
emit(event, ...args) {
|
|
6
4
|
for (const listener of this.listeners.get(event) ?? [])
|
|
7
5
|
listener(...args);
|
|
8
6
|
}
|
|
9
|
-
/** for an event name only known at runtime, whose listeners' arguments can't be checked */
|
|
10
7
|
emitUnchecked(event, ...args) {
|
|
11
8
|
for (const listener of this.listeners.get(event) ?? [])
|
|
12
9
|
listener(...args);
|
package/dist/Emitter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Emitter.js","sourceRoot":"","sources":["../lib/Emitter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Emitter.js","sourceRoot":"","sources":["../lib/Emitter.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,OAAO;IAChB,SAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;IAEhD,IAAI,CAAyB,KAAQ,EAAE,GAAG,IAAe;QAC/D,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE;YAAG,QAAgC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvG,CAAC;IAES,aAAa,CAAC,KAAmB,EAAE,GAAG,IAAoB;QAChE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE;YAAG,QAAqC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5G,CAAC;IAED,GAAG,CAAyB,KAAQ,EAAE,QAAsC;QACxE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,EAAE,CAAyB,KAAQ,EAAE,QAAsC;QACvE,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;QACrD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAyB,KAAQ,EAAE,QAAsC;QACzE,MAAM,OAAO,GAAwB,CAAC,GAAG,IAAI,EAAE,EAAE;YAC7C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACzB,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QACtB,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;CACJ"}
|
package/dist/changes.d.ts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import type { LegacyPost } from "./generated.ts";
|
|
2
|
-
/**
|
|
2
|
+
/** A field's old and new value. */
|
|
3
3
|
export interface ValueChange<T> {
|
|
4
4
|
new: T;
|
|
5
5
|
old: T;
|
|
6
6
|
}
|
|
7
|
-
/** what was added to and removed from a list */
|
|
8
7
|
export interface SetChange<T> {
|
|
9
8
|
added: Array<T>;
|
|
10
9
|
removed: Array<T>;
|
|
11
10
|
}
|
|
12
|
-
/** what changed on a record, only the fields that did */
|
|
13
11
|
export type Changes<R, Field extends keyof R> = Partial<{
|
|
14
12
|
[K in Field]: ValueChange<R[K]>;
|
|
15
13
|
}>;
|
|
16
14
|
export type TagCategoryName = "artist" | "character" | "contributor" | "copyright" | "general" | "invalid" | "lore" | "meta" | "species";
|
|
17
15
|
export type PostFlagName = Extract<keyof LegacyPost["flags"], string>;
|
|
18
|
-
/**
|
|
16
|
+
/** Tags are split by category, so moving a tag between categories shows up as a remove & add. */
|
|
19
17
|
export type PostChanges = Partial<Record<`tags.${TagCategoryName}`, SetChange<string>> & Record<`flags.${PostFlagName}`, ValueChange<boolean>> & {
|
|
20
18
|
approver_id: ValueChange<number | null>;
|
|
21
19
|
children: SetChange<number>;
|
|
@@ -28,3 +26,37 @@ export type PostChanges = Partial<Record<`tags.${TagCategoryName}`, SetChange<st
|
|
|
28
26
|
rating: ValueChange<LegacyPost["rating"]>;
|
|
29
27
|
sources: SetChange<string>;
|
|
30
28
|
}>;
|
|
29
|
+
/** What was last known of a post, only `id` is guaranteed. */
|
|
30
|
+
export type PostSnapshot = Partial<{
|
|
31
|
+
approver_id: number | null;
|
|
32
|
+
change_seq: number;
|
|
33
|
+
children: Array<number>;
|
|
34
|
+
description: string;
|
|
35
|
+
flags: LegacyPost["flags"];
|
|
36
|
+
has_notes: boolean;
|
|
37
|
+
locked_tags: Array<string>;
|
|
38
|
+
md5: string;
|
|
39
|
+
parent_id: number | null;
|
|
40
|
+
pools: Array<number>;
|
|
41
|
+
rating: LegacyPost["rating"];
|
|
42
|
+
sources: Array<string>;
|
|
43
|
+
tags: Partial<Record<TagCategoryName, Array<string>>>;
|
|
44
|
+
} & Omit<PostStats, "changes" | "post_id">> & {
|
|
45
|
+
id: number;
|
|
46
|
+
};
|
|
47
|
+
export interface PostScore {
|
|
48
|
+
down: number;
|
|
49
|
+
total: number;
|
|
50
|
+
up: number;
|
|
51
|
+
}
|
|
52
|
+
export interface PostStats {
|
|
53
|
+
changes: Partial<{
|
|
54
|
+
comment_count: ValueChange<number>;
|
|
55
|
+
fav_count: ValueChange<number>;
|
|
56
|
+
score: ValueChange<PostScore>;
|
|
57
|
+
}>;
|
|
58
|
+
comment_count: number;
|
|
59
|
+
fav_count: number;
|
|
60
|
+
post_id: number;
|
|
61
|
+
score: PostScore;
|
|
62
|
+
}
|
package/dist/events.d.ts
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import type { EventDataMap } from "./generated.ts";
|
|
2
|
-
export declare const EVENT_TYPES: readonly ["artist.create", "artist.delete", "artist.update", "artist_url.create", "artist_version.create", "avoid_posting.create", "avoid_posting.delete", "avoid_posting.destroy", "avoid_posting.undelete", "avoid_posting.update", "avoid_posting_version.create", "ban.create", "ban.delete", "ban.update", "blip.create", "bulk_update_request.create", "bulk_update_request.update", "comment.create", "comment.delete", "comment.hide", "comment.unhide", "comment.update", "forum_post.create", "forum_post.delete", "forum_post.hide", "forum_post.unhide", "forum_post.update", "forum_topic.create", "forum_topic.delete", "forum_topic.hide", "forum_topic.unhide", "forum_topic.update", "help_page.create", "help_page.update", "mod_action.create", "news_update.create", "news_update.update", "note.create", "note.update", "note_version.create", "pool.add_posts", "pool.create", "pool.delete", "pool.remove_posts", "pool.update", "pool_version.create", "post.create", "post.update", "post_approval.create", "post_event.create", "post_flag.create", "post_flag.update", "post_replacement.create", "post_set.add_posts", "post_set.create", "post_set.delete", "post_set.remove_posts", "post_set.update", "post_version.create", "post_version.hide", "post_version.unhide", "tag.create", "tag.delete", "tag.update", "tag_alias.create", "tag_alias.update", "tag_implication.create", "tag_implication.update", "tag_type_version.create", "takedown.create", "takedown.delete", "takedown.update", "upload_karma_event.create", "user.create", "user.delete", "user.update", "user_feedback.create", "user_feedback.delete", "user_feedback.destroy", "user_feedback.undelete", "user_feedback.update", "wiki_page.create", "wiki_page.delete", "wiki_page.update", "wiki_page_version.create"];
|
|
2
|
+
export declare const EVENT_TYPES: readonly ["artist.create", "artist.delete", "artist.update", "artist_url.create", "artist_version.create", "avoid_posting.create", "avoid_posting.delete", "avoid_posting.destroy", "avoid_posting.undelete", "avoid_posting.update", "avoid_posting_version.create", "ban.create", "ban.delete", "ban.update", "blip.create", "bulk_update_request.create", "bulk_update_request.update", "comment.create", "comment.delete", "comment.hide", "comment.unhide", "comment.update", "forum_post.create", "forum_post.delete", "forum_post.hide", "forum_post.unhide", "forum_post.update", "forum_topic.create", "forum_topic.delete", "forum_topic.hide", "forum_topic.unhide", "forum_topic.update", "help_page.create", "help_page.update", "mod_action.create", "news_update.create", "news_update.update", "note.create", "note.update", "note_version.create", "pool.add_posts", "pool.create", "pool.delete", "pool.remove_posts", "pool.update", "pool_version.create", "post.create", "post.destroy", "post.stats", "post.update", "post_approval.create", "post_event.create", "post_flag.create", "post_flag.update", "post_replacement.create", "post_replacement.destroy", "post_replacement.update", "post_set.add_posts", "post_set.create", "post_set.delete", "post_set.remove_posts", "post_set.update", "post_version.create", "post_version.hide", "post_version.unhide", "tag.create", "tag.delete", "tag.update", "tag_alias.create", "tag_alias.update", "tag_implication.create", "tag_implication.update", "tag_type_version.create", "takedown.create", "takedown.delete", "takedown.update", "upload_karma_event.create", "user.create", "user.delete", "user.update", "user_feedback.create", "user_feedback.delete", "user_feedback.destroy", "user_feedback.undelete", "user_feedback.update", "wiki_page.create", "wiki_page.delete", "wiki_page.update", "wiki_page_version.create"];
|
|
3
3
|
export type EventType = typeof EVENT_TYPES[number];
|
|
4
|
-
/**
|
|
5
|
-
export declare const PRIVATE_EVENT_TYPES: readonly ["dmail.create"];
|
|
4
|
+
/** Polled with your own credentials, these need to be subscribed to by name. */
|
|
5
|
+
export declare const PRIVATE_EVENT_TYPES: readonly ["dmail.create", "private_post_set.add_posts", "private_post_set.remove_posts"];
|
|
6
6
|
export type PrivateEventType = typeof PRIVATE_EVENT_TYPES[number];
|
|
7
|
-
/** events whose data is the mod action they come from */
|
|
8
7
|
export declare const MOD_ACTION_EVENT_TYPES: readonly ["artist.delete", "avoid_posting.delete", "avoid_posting.destroy", "avoid_posting.undelete", "ban.delete", "ban.update", "comment.delete", "comment.hide", "comment.unhide", "forum_post.delete", "forum_post.hide", "forum_post.unhide", "forum_topic.delete", "forum_topic.hide", "forum_topic.unhide", "pool.delete", "post_set.delete", "post_version.hide", "post_version.unhide", "tag.delete", "takedown.delete", "user.delete", "user.update", "user_feedback.delete", "user_feedback.destroy", "user_feedback.undelete", "user_feedback.update", "wiki_page.delete"];
|
|
9
|
-
/** updates whose data is the version record they come from */
|
|
10
8
|
export declare const VERSION_EVENT_TYPES: readonly ["artist.update", "avoid_posting.update", "note.update", "pool.update", "tag.update", "wiki_page.update"];
|
|
11
9
|
export type ModActionEventType = typeof MOD_ACTION_EVENT_TYPES[number];
|
|
12
10
|
export type VersionEventType = typeof VERSION_EVENT_TYPES[number];
|
|
13
|
-
/**
|
|
11
|
+
/** The data for each event type, see https://e621.stream/events/ */
|
|
14
12
|
export type EventData<T extends EventType | PrivateEventType> = EventDataMap[T];
|
|
15
|
-
/** one event from the stream, narrowed to its type's data by checking `type` */
|
|
16
13
|
export type StreamEvent<T extends EventType | PrivateEventType = EventType | PrivateEventType> = T extends EventType | PrivateEventType ? {
|
|
17
14
|
data: EventData<T>;
|
|
18
|
-
/**
|
|
15
|
+
/** Pass this as `cursor` to resume after this event. Private events don't have one. */
|
|
19
16
|
id: string;
|
|
20
|
-
/** only for private events, which are polled with your own credentials and never stored */
|
|
21
17
|
private?: true;
|
|
22
|
-
/**
|
|
18
|
+
/** When it happened on e621. */
|
|
23
19
|
time: string;
|
|
24
20
|
type: T;
|
|
25
21
|
} : never;
|
|
@@ -28,12 +24,11 @@ export interface Filter {
|
|
|
28
24
|
pool_ids?: Array<number>;
|
|
29
25
|
post_ids?: Array<number>;
|
|
30
26
|
set_ids?: Array<number>;
|
|
31
|
-
/** e621 style tag query,
|
|
27
|
+
/** An e621 style tag query, supporting `tag`, `-tag`, `~tag` and `rating:`. */
|
|
32
28
|
tags?: string;
|
|
33
29
|
topic_ids?: Array<number>;
|
|
34
30
|
user_ids?: Array<number>;
|
|
35
31
|
}
|
|
36
|
-
/** an event type, `<resource>.*`, `*`, or one of those with a filter */
|
|
37
32
|
export type Subscription = string | {
|
|
38
33
|
filter?: Filter;
|
|
39
34
|
type: string;
|
|
@@ -51,4 +46,6 @@ export declare const CloseCodes: {
|
|
|
51
46
|
readonly SLOW_CONSUMER: 4009;
|
|
52
47
|
readonly TOO_MANY_CONNECTIONS: 4010;
|
|
53
48
|
readonly AUTHENTICATION_EXPIRED: 4011;
|
|
49
|
+
readonly DISCONNECTED: 4012;
|
|
50
|
+
readonly BANNED: 4013;
|
|
54
51
|
};
|
package/dist/events.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// keep in sync with src/events.ts
|
|
2
2
|
export const EVENT_TYPES = [
|
|
3
3
|
"artist.create",
|
|
4
4
|
"artist.delete",
|
|
@@ -47,12 +47,16 @@ export const EVENT_TYPES = [
|
|
|
47
47
|
"pool.update",
|
|
48
48
|
"pool_version.create",
|
|
49
49
|
"post.create",
|
|
50
|
+
"post.destroy",
|
|
51
|
+
"post.stats",
|
|
50
52
|
"post.update",
|
|
51
53
|
"post_approval.create",
|
|
52
54
|
"post_event.create",
|
|
53
55
|
"post_flag.create",
|
|
54
56
|
"post_flag.update",
|
|
55
57
|
"post_replacement.create",
|
|
58
|
+
"post_replacement.destroy",
|
|
59
|
+
"post_replacement.update",
|
|
56
60
|
"post_set.add_posts",
|
|
57
61
|
"post_set.create",
|
|
58
62
|
"post_set.delete",
|
|
@@ -86,9 +90,12 @@ export const EVENT_TYPES = [
|
|
|
86
90
|
"wiki_page.update",
|
|
87
91
|
"wiki_page_version.create",
|
|
88
92
|
];
|
|
89
|
-
/**
|
|
90
|
-
export const PRIVATE_EVENT_TYPES = [
|
|
91
|
-
|
|
93
|
+
/** Polled with your own credentials, these need to be subscribed to by name. */
|
|
94
|
+
export const PRIVATE_EVENT_TYPES = [
|
|
95
|
+
"dmail.create",
|
|
96
|
+
"private_post_set.add_posts",
|
|
97
|
+
"private_post_set.remove_posts",
|
|
98
|
+
];
|
|
92
99
|
export const MOD_ACTION_EVENT_TYPES = [
|
|
93
100
|
"artist.delete",
|
|
94
101
|
"avoid_posting.delete",
|
|
@@ -119,8 +126,14 @@ export const MOD_ACTION_EVENT_TYPES = [
|
|
|
119
126
|
"user_feedback.update",
|
|
120
127
|
"wiki_page.delete",
|
|
121
128
|
];
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
export const VERSION_EVENT_TYPES = [
|
|
130
|
+
"artist.update",
|
|
131
|
+
"avoid_posting.update",
|
|
132
|
+
"note.update",
|
|
133
|
+
"pool.update",
|
|
134
|
+
"tag.update",
|
|
135
|
+
"wiki_page.update",
|
|
136
|
+
];
|
|
124
137
|
export const CloseCodes = {
|
|
125
138
|
UNKNOWN_ERROR: 4000,
|
|
126
139
|
UNKNOWN_OP: 4001,
|
|
@@ -134,5 +147,7 @@ export const CloseCodes = {
|
|
|
134
147
|
SLOW_CONSUMER: 4009,
|
|
135
148
|
TOO_MANY_CONNECTIONS: 4010,
|
|
136
149
|
AUTHENTICATION_EXPIRED: 4011,
|
|
150
|
+
DISCONNECTED: 4012,
|
|
151
|
+
BANNED: 4013,
|
|
137
152
|
};
|
|
138
153
|
//# sourceMappingURL=events.js.map
|
package/dist/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../lib/events.ts"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../lib/events.ts"],"names":[],"mappings":"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"}
|
package/dist/generated.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Changes, PostChanges } from "./changes.ts";
|
|
1
|
+
import type { Changes, PostChanges, PostSnapshot, PostStats } from "./changes.ts";
|
|
2
2
|
export type PoolCategory = 'collection' | 'series';
|
|
3
3
|
export type PostEventAction = 'deleted' | 'undeleted' | 'approved' | 'unapproved' | 'flag_created' | 'flag_removed' | 'favorites_moved' | 'favorites_received' | 'rating_locked' | 'rating_unlocked' | 'status_locked' | 'status_unlocked' | 'note_locked' | 'note_unlocked' | 'comment_locked' | 'comment_unlocked' | 'replacement_accepted' | 'replacement_rejected' | 'replacement_promoted' | 'replacement_deleted' | 'expunged' | 'changed_bg_color' | 'replacement_penalty_changed' | 'owner_changed' | 'replacement_moved';
|
|
4
4
|
export type PostReplacementStatus = 'promoted' | 'approved' | 'rejected' | 'pending' | 'original';
|
|
@@ -1084,21 +1084,21 @@ export interface ModActionValues {
|
|
|
1084
1084
|
};
|
|
1085
1085
|
}
|
|
1086
1086
|
export type ModActionName = keyof ModActionValues;
|
|
1087
|
-
/**
|
|
1087
|
+
/** Checking `action` narrows `values`. */
|
|
1088
1088
|
export type ModAction<A extends ModActionName = ModActionName> = A extends ModActionName ? {
|
|
1089
1089
|
action: A;
|
|
1090
1090
|
created_at: string;
|
|
1091
1091
|
creator_id: number;
|
|
1092
1092
|
id: number;
|
|
1093
1093
|
updated_at: string;
|
|
1094
|
-
/**
|
|
1094
|
+
/** What the action applies to. */
|
|
1095
1095
|
values: ModActionValues[A];
|
|
1096
1096
|
} : never;
|
|
1097
|
-
/**
|
|
1097
|
+
/** `changes` is null when the previous state isn't known. */
|
|
1098
1098
|
export type Update<Resource extends string, R, Field extends keyof R & string> = Record<Resource, R> & {
|
|
1099
1099
|
changes: Changes<R, Field> | null;
|
|
1100
1100
|
};
|
|
1101
|
-
/**
|
|
1101
|
+
/** The `data` each event type is sent with. */
|
|
1102
1102
|
export interface EventDataMap {
|
|
1103
1103
|
"artist.create": Artist;
|
|
1104
1104
|
"artist.delete": ModAction<"artist_delete">;
|
|
@@ -1155,6 +1155,11 @@ export interface EventDataMap {
|
|
|
1155
1155
|
"post.create": {
|
|
1156
1156
|
post: LegacyPost;
|
|
1157
1157
|
};
|
|
1158
|
+
"post.destroy": {
|
|
1159
|
+
post: PostSnapshot;
|
|
1160
|
+
post_event: PostEvent;
|
|
1161
|
+
};
|
|
1162
|
+
"post.stats": PostStats;
|
|
1158
1163
|
"post.update": {
|
|
1159
1164
|
changes: PostChanges | null;
|
|
1160
1165
|
post: LegacyPost;
|
|
@@ -1164,6 +1169,11 @@ export interface EventDataMap {
|
|
|
1164
1169
|
"post_flag.create": PostFlag;
|
|
1165
1170
|
"post_flag.update": Update<"post_flag", PostFlag, "is_resolved">;
|
|
1166
1171
|
"post_replacement.create": PostReplacement;
|
|
1172
|
+
"post_replacement.destroy": {
|
|
1173
|
+
post_event: PostEvent;
|
|
1174
|
+
post_replacement: Pick<PostReplacement, "id" | "status" | "approver_id" | "reason" | "post_id">;
|
|
1175
|
+
};
|
|
1176
|
+
"post_replacement.update": Update<"post_replacement", PostReplacement, "status" | "approver_id" | "reason" | "post_id">;
|
|
1167
1177
|
"post_set.add_posts": Record<"post_set", PostSet> & {
|
|
1168
1178
|
post_ids: Array<number>;
|
|
1169
1179
|
};
|
|
@@ -1201,4 +1211,12 @@ export interface EventDataMap {
|
|
|
1201
1211
|
"wiki_page.update": WikiPageVersion;
|
|
1202
1212
|
"wiki_page_version.create": WikiPageVersion;
|
|
1203
1213
|
"dmail.create": DMail;
|
|
1214
|
+
"private_post_set.add_posts": {
|
|
1215
|
+
post_ids: Array<number>;
|
|
1216
|
+
post_set: PostSet;
|
|
1217
|
+
};
|
|
1218
|
+
"private_post_set.remove_posts": {
|
|
1219
|
+
post_ids: Array<number>;
|
|
1220
|
+
post_set: PostSet;
|
|
1221
|
+
};
|
|
1204
1222
|
}
|