e621stream 0.0.0-stage → 0.0.2
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 +21 -0
- package/README.md +45 -2
- package/dist/E621Stream.d.ts +93 -0
- package/dist/E621Stream.js +178 -0
- package/dist/E621Stream.js.map +1 -0
- package/dist/Emitter.d.ts +11 -0
- package/dist/Emitter.js +30 -0
- package/dist/Emitter.js.map +1 -0
- package/dist/changes.d.ts +62 -0
- package/dist/changes.js +2 -0
- package/dist/changes.js.map +1 -0
- package/dist/events.d.ts +51 -0
- package/dist/events.js +153 -0
- package/dist/events.js.map +1 -0
- package/dist/generated.d.ts +1222 -0
- package/dist/generated.js +4 -0
- package/dist/generated.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -3
- package/tsconfig.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) undefined Donovan_DMC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# e621stream
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm i e621stream
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { E621Stream } from "e621stream";
|
|
11
|
+
|
|
12
|
+
const stream = new E621Stream({
|
|
13
|
+
auth: { username: "your_name", apiKey: "your e621 api key" },
|
|
14
|
+
subscriptions: ["post.create", { type: "post.update", filter: { tags: "fox -solo" } }, "comment.*"],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
stream.on("post.create", (event) => {
|
|
18
|
+
console.log(event.data.post.id, event.data.post.tags.general);
|
|
19
|
+
});
|
|
20
|
+
stream.on("post.update", (event) => {
|
|
21
|
+
// only what changed, tags are per category
|
|
22
|
+
console.log(event.data.changes?.["tags.general"]?.added);
|
|
23
|
+
});
|
|
24
|
+
stream.on("comment.update", (event) => {
|
|
25
|
+
console.log(event.data.comment.id, event.data.changes?.body?.old, "->", event.data.changes?.body?.new);
|
|
26
|
+
});
|
|
27
|
+
stream.on("error", err => console.error(err));
|
|
28
|
+
stream.connect();
|
|
29
|
+
```
|
|
30
|
+
|
|
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.
|
|
32
|
+
|
|
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 }` |
|
|
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 | `wss://e621.stream/ws` | |
|
|
42
|
+
| `WebSocket` | WebSocket | global | for older node, the `ws` package works |
|
|
43
|
+
|
|
44
|
+
Private events like `dmail.create` have to be subscribed to by name.
|
|
45
|
+
|
|
46
|
+
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`.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Emitter from "./Emitter.ts";
|
|
2
|
+
import { type EventType, type PrivateEventType, type StreamEvent, type Subscription } from "./events.ts";
|
|
3
|
+
export interface Options {
|
|
4
|
+
/** Your e621 username & api key. */
|
|
5
|
+
auth?: {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
username: string;
|
|
8
|
+
};
|
|
9
|
+
/** Shown in the server's logs. */
|
|
10
|
+
client?: string;
|
|
11
|
+
/** An event id or millisecond timestamp to replay from. */
|
|
12
|
+
cursor?: number | string;
|
|
13
|
+
/** Reconnect when the connection drops. */
|
|
14
|
+
reconnect?: boolean;
|
|
15
|
+
subscriptions: Array<Subscription>;
|
|
16
|
+
url?: string;
|
|
17
|
+
/** For runtimes without a global WebSocket (node < 22), the `ws` package works. */
|
|
18
|
+
WebSocket?: typeof WebSocket;
|
|
19
|
+
}
|
|
20
|
+
export interface Ready {
|
|
21
|
+
/** If the cursor was older than anything still kept. */
|
|
22
|
+
cursor_expired?: boolean;
|
|
23
|
+
replaying: boolean;
|
|
24
|
+
session_id: string;
|
|
25
|
+
subscriptions: Array<Subscription>;
|
|
26
|
+
user: {
|
|
27
|
+
id: number;
|
|
28
|
+
level: number;
|
|
29
|
+
level_string: string;
|
|
30
|
+
name: string;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
export interface ClientEvents {
|
|
34
|
+
close: [{
|
|
35
|
+
code: number;
|
|
36
|
+
reason: string;
|
|
37
|
+
reconnecting: boolean;
|
|
38
|
+
}];
|
|
39
|
+
/** Every event, public or private. */
|
|
40
|
+
dispatch: [StreamEvent];
|
|
41
|
+
error: [Error];
|
|
42
|
+
ready: [Ready];
|
|
43
|
+
reconnecting: [{
|
|
44
|
+
attempt: number;
|
|
45
|
+
delayMS: number;
|
|
46
|
+
}];
|
|
47
|
+
replayComplete: [{
|
|
48
|
+
cursor: string | null;
|
|
49
|
+
events: number;
|
|
50
|
+
}];
|
|
51
|
+
subscribed: [Array<Subscription>];
|
|
52
|
+
}
|
|
53
|
+
type Listeners = ClientEvents & {
|
|
54
|
+
[K in EventType | PrivateEventType]: [StreamEvent<K>];
|
|
55
|
+
};
|
|
56
|
+
export declare class StreamError extends Error {
|
|
57
|
+
readonly code: number;
|
|
58
|
+
constructor(code: number, message: string);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A connection to E621Stream, see https://e621.stream
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const stream = new E621Stream({ auth: { username, apiKey }, subscriptions: ["post.create"] });
|
|
65
|
+
* stream.on("post.create", event => console.log(event.data.post.id));
|
|
66
|
+
* stream.connect();
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class E621Stream extends Emitter<Listeners> {
|
|
70
|
+
private attempt;
|
|
71
|
+
private closing;
|
|
72
|
+
private heartbeat;
|
|
73
|
+
private lastError;
|
|
74
|
+
private lastHeartbeatAck;
|
|
75
|
+
private reconnectTimer;
|
|
76
|
+
private socket;
|
|
77
|
+
cursor: string | null;
|
|
78
|
+
readonly options: Options & {
|
|
79
|
+
url: string;
|
|
80
|
+
};
|
|
81
|
+
constructor(options: Options);
|
|
82
|
+
private handle;
|
|
83
|
+
private onClose;
|
|
84
|
+
private send;
|
|
85
|
+
private startHeartbeat;
|
|
86
|
+
private stopHeartbeat;
|
|
87
|
+
/** Close the connection. */
|
|
88
|
+
close(): void;
|
|
89
|
+
connect(): this;
|
|
90
|
+
/** Replace the current subscriptions. */
|
|
91
|
+
subscribe(subscriptions: Array<Subscription>): void;
|
|
92
|
+
}
|
|
93
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import Emitter from "./Emitter.js";
|
|
2
|
+
import { CloseCodes } from "./events.js";
|
|
3
|
+
export class StreamError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "StreamError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const FATAL = new Set([
|
|
12
|
+
CloseCodes.UNKNOWN_OP,
|
|
13
|
+
CloseCodes.DECODE_ERROR,
|
|
14
|
+
CloseCodes.AUTHENTICATION_FAILED,
|
|
15
|
+
CloseCodes.ALREADY_IDENTIFIED,
|
|
16
|
+
CloseCodes.INVALID_SUBSCRIPTION,
|
|
17
|
+
CloseCodes.INVALID_CURSOR,
|
|
18
|
+
CloseCodes.AUTHENTICATION_EXPIRED,
|
|
19
|
+
CloseCodes.DISCONNECTED,
|
|
20
|
+
CloseCodes.BANNED,
|
|
21
|
+
]);
|
|
22
|
+
const MAX_DELAY_MS = 60_000;
|
|
23
|
+
const HEARTBEAT_MS = 30_000;
|
|
24
|
+
/**
|
|
25
|
+
* A connection to E621Stream, see https://e621.stream
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const stream = new E621Stream({ auth: { username, apiKey }, subscriptions: ["post.create"] });
|
|
29
|
+
* stream.on("post.create", event => console.log(event.data.post.id));
|
|
30
|
+
* stream.connect();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class E621Stream extends Emitter {
|
|
34
|
+
attempt = 0;
|
|
35
|
+
closing = false;
|
|
36
|
+
heartbeat = null;
|
|
37
|
+
lastError = null;
|
|
38
|
+
lastHeartbeatAck = true;
|
|
39
|
+
reconnectTimer = null;
|
|
40
|
+
socket = null;
|
|
41
|
+
cursor;
|
|
42
|
+
options;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
super();
|
|
45
|
+
this.options = { reconnect: true, url: "wss://e621.stream/ws", ...options };
|
|
46
|
+
this.cursor = options.cursor === undefined ? null : String(options.cursor);
|
|
47
|
+
}
|
|
48
|
+
handle(raw) {
|
|
49
|
+
if (typeof raw !== "string") {
|
|
50
|
+
this.emit("error", new Error("compression isn't supported"));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const message = JSON.parse(raw);
|
|
54
|
+
switch (message.op) {
|
|
55
|
+
case "hello": {
|
|
56
|
+
const { auth, client, subscriptions } = this.options;
|
|
57
|
+
this.send({
|
|
58
|
+
op: "identify",
|
|
59
|
+
d: {
|
|
60
|
+
...(auth ? { auth: { username: auth.username, api_key: auth.apiKey } } : {}),
|
|
61
|
+
...(client ? { client } : {}),
|
|
62
|
+
...(this.cursor === null ? {} : { cursor: this.cursor }),
|
|
63
|
+
subscriptions,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case "ready": {
|
|
69
|
+
this.attempt = 0;
|
|
70
|
+
this.emit("ready", message.d);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "dispatch": {
|
|
74
|
+
const event = { id: message.id, type: message.type, time: message.time, data: message.data, ...(message.private ? { private: true } : {}) };
|
|
75
|
+
if (!event.private)
|
|
76
|
+
this.cursor = event.id;
|
|
77
|
+
this.emit("dispatch", event);
|
|
78
|
+
// typescript can't narrow this
|
|
79
|
+
this.emitUnchecked(event.type, event);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "replay_complete": {
|
|
83
|
+
this.emit("replayComplete", message.d);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case "subscribed": {
|
|
87
|
+
this.emit("subscribed", message.d.subscriptions);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case "heartbeat_ack": {
|
|
91
|
+
this.lastHeartbeatAck = true;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "error": {
|
|
95
|
+
const { code, message: text } = message.d;
|
|
96
|
+
this.lastError = new StreamError(code, text);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
onClose(code, reason) {
|
|
102
|
+
this.stopHeartbeat();
|
|
103
|
+
this.socket = null;
|
|
104
|
+
const error = this.lastError;
|
|
105
|
+
this.lastError = null;
|
|
106
|
+
const reconnecting = !this.closing && this.options.reconnect !== false && !FATAL.has(code);
|
|
107
|
+
if (error)
|
|
108
|
+
this.emit("error", error);
|
|
109
|
+
this.emit("close", { code, reason: error?.message ?? reason, reconnecting });
|
|
110
|
+
if (!reconnecting)
|
|
111
|
+
return;
|
|
112
|
+
this.attempt++;
|
|
113
|
+
const base = code === CloseCodes.RATE_LIMITED ? 30_000 : 1000;
|
|
114
|
+
const delayMS = Math.min(MAX_DELAY_MS, base * 2 ** (this.attempt - 1)) * (0.75 + Math.random() * 0.5);
|
|
115
|
+
this.emit("reconnecting", { attempt: this.attempt, delayMS: Math.round(delayMS) });
|
|
116
|
+
this.reconnectTimer = setTimeout(() => {
|
|
117
|
+
this.reconnectTimer = null;
|
|
118
|
+
this.connect();
|
|
119
|
+
}, delayMS);
|
|
120
|
+
}
|
|
121
|
+
send(payload) {
|
|
122
|
+
if (this.socket?.readyState === 1)
|
|
123
|
+
this.socket.send(JSON.stringify(payload));
|
|
124
|
+
}
|
|
125
|
+
startHeartbeat() {
|
|
126
|
+
this.lastHeartbeatAck = true;
|
|
127
|
+
this.heartbeat = setInterval(() => {
|
|
128
|
+
if (!this.lastHeartbeatAck) {
|
|
129
|
+
this.socket?.close(4999, "heartbeat not acknowledged");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.lastHeartbeatAck = false;
|
|
133
|
+
this.send({ op: "heartbeat" });
|
|
134
|
+
}, HEARTBEAT_MS);
|
|
135
|
+
}
|
|
136
|
+
stopHeartbeat() {
|
|
137
|
+
if (this.heartbeat)
|
|
138
|
+
clearInterval(this.heartbeat);
|
|
139
|
+
this.heartbeat = null;
|
|
140
|
+
}
|
|
141
|
+
/** Close the connection. */
|
|
142
|
+
close() {
|
|
143
|
+
this.closing = true;
|
|
144
|
+
if (this.reconnectTimer)
|
|
145
|
+
clearTimeout(this.reconnectTimer);
|
|
146
|
+
this.reconnectTimer = null;
|
|
147
|
+
this.socket?.close(1000, "closed by client");
|
|
148
|
+
}
|
|
149
|
+
connect() {
|
|
150
|
+
if (this.socket)
|
|
151
|
+
return this;
|
|
152
|
+
this.closing = false;
|
|
153
|
+
// not present before node 22
|
|
154
|
+
const global = globalThis.WebSocket;
|
|
155
|
+
const Socket = this.options.WebSocket ?? global;
|
|
156
|
+
if (!Socket)
|
|
157
|
+
throw new Error("no WebSocket available, pass one with the WebSocket option");
|
|
158
|
+
const socket = this.socket = new Socket(this.options.url);
|
|
159
|
+
socket.addEventListener("open", () => { this.startHeartbeat(); });
|
|
160
|
+
socket.addEventListener("message", (event) => {
|
|
161
|
+
try {
|
|
162
|
+
this.handle(event.data);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
socket.addEventListener("close", (event) => { this.onClose(event.code, event.reason); });
|
|
169
|
+
socket.addEventListener("error", () => null);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
/** Replace the current subscriptions. */
|
|
173
|
+
subscribe(subscriptions) {
|
|
174
|
+
this.options.subscriptions = subscriptions;
|
|
175
|
+
this.send({ op: "subscribe", d: { subscriptions } });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=E621Stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"E621Stream.js","sourceRoot":"","sources":["../lib/E621Stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Listener<Args extends Array<unknown>> = (...args: Args) => void;
|
|
2
|
+
export default class Emitter<Events extends {
|
|
3
|
+
[K in keyof Events]: Array<unknown>;
|
|
4
|
+
}> {
|
|
5
|
+
private listeners;
|
|
6
|
+
protected emit<K extends keyof Events>(event: K, ...args: Events[K]): void;
|
|
7
|
+
protected emitUnchecked(event: keyof Events, ...args: Array<unknown>): void;
|
|
8
|
+
off<K extends keyof Events>(event: K, listener: NoInfer<Listener<Events[K]>>): this;
|
|
9
|
+
on<K extends keyof Events>(event: K, listener: NoInfer<Listener<Events[K]>>): this;
|
|
10
|
+
once<K extends keyof Events>(event: K, listener: NoInfer<Listener<Events[K]>>): this;
|
|
11
|
+
}
|
package/dist/Emitter.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export default class Emitter {
|
|
2
|
+
listeners = new Map();
|
|
3
|
+
emit(event, ...args) {
|
|
4
|
+
for (const listener of this.listeners.get(event) ?? [])
|
|
5
|
+
listener(...args);
|
|
6
|
+
}
|
|
7
|
+
emitUnchecked(event, ...args) {
|
|
8
|
+
for (const listener of this.listeners.get(event) ?? [])
|
|
9
|
+
listener(...args);
|
|
10
|
+
}
|
|
11
|
+
off(event, listener) {
|
|
12
|
+
this.listeners.get(event)?.delete(listener);
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
on(event, listener) {
|
|
16
|
+
let set = this.listeners.get(event);
|
|
17
|
+
if (!set)
|
|
18
|
+
this.listeners.set(event, set = new Set());
|
|
19
|
+
set.add(listener);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
once(event, listener) {
|
|
23
|
+
const wrapped = (...args) => {
|
|
24
|
+
this.off(event, wrapped);
|
|
25
|
+
listener(...args);
|
|
26
|
+
};
|
|
27
|
+
return this.on(event, wrapped);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=Emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { LegacyPost } from "./generated.ts";
|
|
2
|
+
/** A field's old and new value. */
|
|
3
|
+
export interface ValueChange<T> {
|
|
4
|
+
new: T;
|
|
5
|
+
old: T;
|
|
6
|
+
}
|
|
7
|
+
export interface SetChange<T> {
|
|
8
|
+
added: Array<T>;
|
|
9
|
+
removed: Array<T>;
|
|
10
|
+
}
|
|
11
|
+
export type Changes<R, Field extends keyof R> = Partial<{
|
|
12
|
+
[K in Field]: ValueChange<R[K]>;
|
|
13
|
+
}>;
|
|
14
|
+
export type TagCategoryName = "artist" | "character" | "contributor" | "copyright" | "general" | "invalid" | "lore" | "meta" | "species";
|
|
15
|
+
export type PostFlagName = Extract<keyof LegacyPost["flags"], string>;
|
|
16
|
+
/** Tags are split by category, so moving a tag between categories shows up as a remove & add. */
|
|
17
|
+
export type PostChanges = Partial<Record<`tags.${TagCategoryName}`, SetChange<string>> & Record<`flags.${PostFlagName}`, ValueChange<boolean>> & {
|
|
18
|
+
approver_id: ValueChange<number | null>;
|
|
19
|
+
children: SetChange<number>;
|
|
20
|
+
description: ValueChange<string>;
|
|
21
|
+
has_notes: ValueChange<boolean>;
|
|
22
|
+
locked_tags: SetChange<string>;
|
|
23
|
+
md5: ValueChange<string>;
|
|
24
|
+
parent_id: ValueChange<number | null>;
|
|
25
|
+
pools: SetChange<number>;
|
|
26
|
+
rating: ValueChange<LegacyPost["rating"]>;
|
|
27
|
+
sources: SetChange<string>;
|
|
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/changes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../lib/changes.ts"],"names":[],"mappings":""}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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.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
|
+
export type EventType = typeof EVENT_TYPES[number];
|
|
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
|
+
export type PrivateEventType = typeof PRIVATE_EVENT_TYPES[number];
|
|
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"];
|
|
8
|
+
export declare const VERSION_EVENT_TYPES: readonly ["artist.update", "avoid_posting.update", "note.update", "pool.update", "tag.update", "wiki_page.update"];
|
|
9
|
+
export type ModActionEventType = typeof MOD_ACTION_EVENT_TYPES[number];
|
|
10
|
+
export type VersionEventType = typeof VERSION_EVENT_TYPES[number];
|
|
11
|
+
/** The data for each event type, see https://e621.stream/events/ */
|
|
12
|
+
export type EventData<T extends EventType | PrivateEventType> = EventDataMap[T];
|
|
13
|
+
export type StreamEvent<T extends EventType | PrivateEventType = EventType | PrivateEventType> = T extends EventType | PrivateEventType ? {
|
|
14
|
+
data: EventData<T>;
|
|
15
|
+
/** Pass this as `cursor` to resume after this event. Private events don't have one. */
|
|
16
|
+
id: string;
|
|
17
|
+
private?: true;
|
|
18
|
+
/** When it happened on e621. */
|
|
19
|
+
time: string;
|
|
20
|
+
type: T;
|
|
21
|
+
} : never;
|
|
22
|
+
export interface Filter {
|
|
23
|
+
actions?: Array<string>;
|
|
24
|
+
pool_ids?: Array<number>;
|
|
25
|
+
post_ids?: Array<number>;
|
|
26
|
+
set_ids?: Array<number>;
|
|
27
|
+
/** An e621 style tag query, supporting `tag`, `-tag`, `~tag` and `rating:`. */
|
|
28
|
+
tags?: string;
|
|
29
|
+
topic_ids?: Array<number>;
|
|
30
|
+
user_ids?: Array<number>;
|
|
31
|
+
}
|
|
32
|
+
export type Subscription = string | {
|
|
33
|
+
filter?: Filter;
|
|
34
|
+
type: string;
|
|
35
|
+
};
|
|
36
|
+
export declare const CloseCodes: {
|
|
37
|
+
readonly UNKNOWN_ERROR: 4000;
|
|
38
|
+
readonly UNKNOWN_OP: 4001;
|
|
39
|
+
readonly DECODE_ERROR: 4002;
|
|
40
|
+
readonly NOT_IDENTIFIED: 4003;
|
|
41
|
+
readonly AUTHENTICATION_FAILED: 4004;
|
|
42
|
+
readonly ALREADY_IDENTIFIED: 4005;
|
|
43
|
+
readonly INVALID_SUBSCRIPTION: 4006;
|
|
44
|
+
readonly INVALID_CURSOR: 4007;
|
|
45
|
+
readonly RATE_LIMITED: 4008;
|
|
46
|
+
readonly SLOW_CONSUMER: 4009;
|
|
47
|
+
readonly TOO_MANY_CONNECTIONS: 4010;
|
|
48
|
+
readonly AUTHENTICATION_EXPIRED: 4011;
|
|
49
|
+
readonly DISCONNECTED: 4012;
|
|
50
|
+
readonly BANNED: 4013;
|
|
51
|
+
};
|