refactor: split discord gateway helpers
This commit is contained in:
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
|
|||||||
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { waitForDiscordGatewayStop } from "./monitor.js";
|
import { waitForDiscordGatewayStop } from "./monitor.gateway.js";
|
||||||
|
|
||||||
describe("waitForDiscordGatewayStop", () => {
|
describe("waitForDiscordGatewayStop", () => {
|
||||||
it("resolves on abort and disconnects gateway", async () => {
|
it("resolves on abort and disconnects gateway", async () => {
|
||||||
@@ -46,4 +46,16 @@ describe("waitForDiscordGatewayStop", () => {
|
|||||||
abort.abort();
|
abort.abort();
|
||||||
expect(disconnect).toHaveBeenCalledTimes(1);
|
expect(disconnect).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("resolves on abort without a gateway", async () => {
|
||||||
|
const abort = new AbortController();
|
||||||
|
|
||||||
|
const promise = waitForDiscordGatewayStop({
|
||||||
|
abortSignal: abort.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
abort.abort();
|
||||||
|
|
||||||
|
await expect(promise).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
63
src/discord/monitor.gateway.ts
Normal file
63
src/discord/monitor.gateway.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import type { EventEmitter } from "node:events";
|
||||||
|
|
||||||
|
export type DiscordGatewayHandle = {
|
||||||
|
emitter?: Pick<EventEmitter, "on" | "removeListener">;
|
||||||
|
disconnect?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getDiscordGatewayEmitter(
|
||||||
|
gateway?: unknown,
|
||||||
|
): EventEmitter | undefined {
|
||||||
|
return (gateway as { emitter?: EventEmitter } | undefined)?.emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function waitForDiscordGatewayStop(params: {
|
||||||
|
gateway?: DiscordGatewayHandle;
|
||||||
|
abortSignal?: AbortSignal;
|
||||||
|
onGatewayError?: (err: unknown) => void;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { gateway, abortSignal, onGatewayError } = params;
|
||||||
|
const emitter = gateway?.emitter;
|
||||||
|
return await new Promise<void>((resolve, reject) => {
|
||||||
|
let settled = false;
|
||||||
|
const cleanup = () => {
|
||||||
|
abortSignal?.removeEventListener("abort", onAbort);
|
||||||
|
emitter?.removeListener("error", onGatewayErrorEvent);
|
||||||
|
};
|
||||||
|
const finishResolve = () => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
cleanup();
|
||||||
|
try {
|
||||||
|
gateway?.disconnect?.();
|
||||||
|
} finally {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const finishReject = (err: unknown) => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
cleanup();
|
||||||
|
try {
|
||||||
|
gateway?.disconnect?.();
|
||||||
|
} finally {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onAbort = () => {
|
||||||
|
finishResolve();
|
||||||
|
};
|
||||||
|
const onGatewayErrorEvent = (err: unknown) => {
|
||||||
|
onGatewayError?.(err);
|
||||||
|
finishReject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (abortSignal?.aborted) {
|
||||||
|
onAbort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
||||||
|
emitter?.on("error", onGatewayErrorEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import type { EventEmitter } from "node:events";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
Client,
|
Client,
|
||||||
@@ -63,6 +61,10 @@ import type { RuntimeEnv } from "../runtime.js";
|
|||||||
import { loadWebMedia } from "../web/media.js";
|
import { loadWebMedia } from "../web/media.js";
|
||||||
import { resolveDiscordAccount } from "./accounts.js";
|
import { resolveDiscordAccount } from "./accounts.js";
|
||||||
import { chunkDiscordText } from "./chunk.js";
|
import { chunkDiscordText } from "./chunk.js";
|
||||||
|
import {
|
||||||
|
getDiscordGatewayEmitter,
|
||||||
|
waitForDiscordGatewayStop,
|
||||||
|
} from "./monitor.gateway.js";
|
||||||
import { fetchDiscordApplicationId } from "./probe.js";
|
import { fetchDiscordApplicationId } from "./probe.js";
|
||||||
import { reactMessageDiscord, sendMessageDiscord } from "./send.js";
|
import { reactMessageDiscord, sendMessageDiscord } from "./send.js";
|
||||||
import { normalizeDiscordToken } from "./token.js";
|
import { normalizeDiscordToken } from "./token.js";
|
||||||
@@ -84,62 +86,6 @@ type DiscordMediaInfo = {
|
|||||||
placeholder: string;
|
placeholder: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DiscordGatewayHandle = {
|
|
||||||
emitter?: Pick<EventEmitter, "on" | "removeListener">;
|
|
||||||
disconnect?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function waitForDiscordGatewayStop(params: {
|
|
||||||
gateway?: DiscordGatewayHandle;
|
|
||||||
abortSignal?: AbortSignal;
|
|
||||||
onGatewayError?: (err: unknown) => void;
|
|
||||||
}): Promise<void> {
|
|
||||||
const { gateway, abortSignal, onGatewayError } = params;
|
|
||||||
const emitter = gateway?.emitter;
|
|
||||||
return await new Promise<void>((resolve, reject) => {
|
|
||||||
let settled = false;
|
|
||||||
const cleanup = () => {
|
|
||||||
abortSignal?.removeEventListener("abort", onAbort);
|
|
||||||
emitter?.removeListener("error", onGatewayErrorEvent);
|
|
||||||
};
|
|
||||||
const finishResolve = () => {
|
|
||||||
if (settled) return;
|
|
||||||
settled = true;
|
|
||||||
cleanup();
|
|
||||||
try {
|
|
||||||
gateway?.disconnect?.();
|
|
||||||
} finally {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const finishReject = (err: unknown) => {
|
|
||||||
if (settled) return;
|
|
||||||
settled = true;
|
|
||||||
cleanup();
|
|
||||||
try {
|
|
||||||
gateway?.disconnect?.();
|
|
||||||
} finally {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const onAbort = () => {
|
|
||||||
finishResolve();
|
|
||||||
};
|
|
||||||
const onGatewayErrorEvent = (err: unknown) => {
|
|
||||||
onGatewayError?.(err);
|
|
||||||
finishReject(err);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (abortSignal?.aborted) {
|
|
||||||
onAbort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
||||||
emitter?.on("error", onGatewayErrorEvent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiscordHistoryEntry = {
|
type DiscordHistoryEntry = {
|
||||||
sender: string;
|
sender: string;
|
||||||
body: string;
|
body: string;
|
||||||
@@ -461,8 +407,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
|||||||
runtime.log?.(`logged in to discord${botUserId ? ` as ${botUserId}` : ""}`);
|
runtime.log?.(`logged in to discord${botUserId ? ` as ${botUserId}` : ""}`);
|
||||||
|
|
||||||
const gateway = client.getPlugin<GatewayPlugin>("gateway");
|
const gateway = client.getPlugin<GatewayPlugin>("gateway");
|
||||||
const gatewayEmitter = (gateway as unknown as { emitter?: EventEmitter })
|
const gatewayEmitter = getDiscordGatewayEmitter(gateway);
|
||||||
?.emitter;
|
|
||||||
await waitForDiscordGatewayStop({
|
await waitForDiscordGatewayStop({
|
||||||
gateway: gateway
|
gateway: gateway
|
||||||
? {
|
? {
|
||||||
|
|||||||
Reference in New Issue
Block a user