refactor: split discord gateway helpers

This commit is contained in:
Peter Steinberger
2026-01-08 20:23:06 +01:00
parent bf67b29a0e
commit 393c414e90
3 changed files with 81 additions and 61 deletions

View File

@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";
import { waitForDiscordGatewayStop } from "./monitor.js";
import { waitForDiscordGatewayStop } from "./monitor.gateway.js";
describe("waitForDiscordGatewayStop", () => {
it("resolves on abort and disconnects gateway", async () => {
@@ -46,4 +46,16 @@ describe("waitForDiscordGatewayStop", () => {
abort.abort();
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();
});
});

View 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);
});
}

View File

@@ -1,5 +1,3 @@
import type { EventEmitter } from "node:events";
import {
ChannelType,
Client,
@@ -63,6 +61,10 @@ import type { RuntimeEnv } from "../runtime.js";
import { loadWebMedia } from "../web/media.js";
import { resolveDiscordAccount } from "./accounts.js";
import { chunkDiscordText } from "./chunk.js";
import {
getDiscordGatewayEmitter,
waitForDiscordGatewayStop,
} from "./monitor.gateway.js";
import { fetchDiscordApplicationId } from "./probe.js";
import { reactMessageDiscord, sendMessageDiscord } from "./send.js";
import { normalizeDiscordToken } from "./token.js";
@@ -84,62 +86,6 @@ type DiscordMediaInfo = {
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 = {
sender: string;
body: string;
@@ -461,8 +407,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
runtime.log?.(`logged in to discord${botUserId ? ` as ${botUserId}` : ""}`);
const gateway = client.getPlugin<GatewayPlugin>("gateway");
const gatewayEmitter = (gateway as unknown as { emitter?: EventEmitter })
?.emitter;
const gatewayEmitter = getDiscordGatewayEmitter(gateway);
await waitForDiscordGatewayStop({
gateway: gateway
? {