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 { 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();
|
||||
});
|
||||
});
|
||||
|
||||
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 {
|
||||
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
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user