refactor(src): split oversized modules
This commit is contained in:
147
src/slack/monitor/provider.ts
Normal file
147
src/slack/monitor/provider.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { App } from "@slack/bolt";
|
||||
|
||||
import { resolveTextChunkLimit } from "../../auto-reply/chunk.js";
|
||||
import { DEFAULT_GROUP_HISTORY_LIMIT } from "../../auto-reply/reply/history.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import type { SessionScope } from "../../config/sessions.js";
|
||||
import type { DmPolicy, GroupPolicy } from "../../config/types.js";
|
||||
import { normalizeMainKey } from "../../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
|
||||
import { resolveSlackAccount } from "../accounts.js";
|
||||
import { resolveSlackAppToken, resolveSlackBotToken } from "../token.js";
|
||||
import { resolveSlackSlashCommandConfig } from "./commands.js";
|
||||
import { createSlackMonitorContext } from "./context.js";
|
||||
import { registerSlackMonitorEvents } from "./events.js";
|
||||
import { createSlackMessageHandler } from "./message-handler.js";
|
||||
import { registerSlackMonitorSlashCommands } from "./slash.js";
|
||||
|
||||
import type { MonitorSlackOpts } from "./types.js";
|
||||
|
||||
export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
const cfg = opts.config ?? loadConfig();
|
||||
|
||||
const account = resolveSlackAccount({
|
||||
cfg,
|
||||
accountId: opts.accountId,
|
||||
});
|
||||
|
||||
const historyLimit = Math.max(
|
||||
0,
|
||||
account.config.historyLimit ??
|
||||
cfg.messages?.groupChat?.historyLimit ??
|
||||
DEFAULT_GROUP_HISTORY_LIMIT,
|
||||
);
|
||||
|
||||
const sessionCfg = cfg.session;
|
||||
const sessionScope: SessionScope = sessionCfg?.scope ?? "per-sender";
|
||||
const mainKey = normalizeMainKey(sessionCfg?.mainKey);
|
||||
|
||||
const botToken = resolveSlackBotToken(opts.botToken ?? account.botToken);
|
||||
const appToken = resolveSlackAppToken(opts.appToken ?? account.appToken);
|
||||
if (!botToken || !appToken) {
|
||||
throw new Error(
|
||||
`Slack bot + app tokens missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken/appToken or SLACK_BOT_TOKEN/SLACK_APP_TOKEN for default).`,
|
||||
);
|
||||
}
|
||||
|
||||
const runtime: RuntimeEnv = opts.runtime ?? {
|
||||
log: console.log,
|
||||
error: console.error,
|
||||
exit: (code: number): never => {
|
||||
throw new Error(`exit ${code}`);
|
||||
},
|
||||
};
|
||||
|
||||
const slackCfg = account.config;
|
||||
const dmConfig = slackCfg.dm;
|
||||
|
||||
const dmEnabled = dmConfig?.enabled ?? true;
|
||||
const dmPolicy = (dmConfig?.policy ?? "pairing") as DmPolicy;
|
||||
const allowFrom = dmConfig?.allowFrom;
|
||||
const groupDmEnabled = dmConfig?.groupEnabled ?? false;
|
||||
const groupDmChannels = dmConfig?.groupChannels;
|
||||
const channelsConfig = slackCfg.channels;
|
||||
const groupPolicy = (slackCfg.groupPolicy ?? "open") as GroupPolicy;
|
||||
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
|
||||
const reactionMode = slackCfg.reactionNotifications ?? "own";
|
||||
const reactionAllowlist = slackCfg.reactionAllowlist ?? [];
|
||||
const replyToMode = slackCfg.replyToMode ?? "off";
|
||||
const slashCommand = resolveSlackSlashCommandConfig(
|
||||
opts.slashCommand ?? slackCfg.slashCommand,
|
||||
);
|
||||
const textLimit = resolveTextChunkLimit(cfg, "slack", account.accountId);
|
||||
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
const mediaMaxBytes =
|
||||
(opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
|
||||
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
||||
|
||||
const app = new App({
|
||||
token: botToken,
|
||||
appToken,
|
||||
socketMode: true,
|
||||
});
|
||||
|
||||
let botUserId = "";
|
||||
let teamId = "";
|
||||
try {
|
||||
const auth = await app.client.auth.test({ token: botToken });
|
||||
botUserId = auth.user_id ?? "";
|
||||
teamId = auth.team_id ?? "";
|
||||
} catch {
|
||||
// auth test failing is non-fatal; message handler falls back to regex mentions.
|
||||
}
|
||||
|
||||
const ctx = createSlackMonitorContext({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
botToken,
|
||||
app,
|
||||
runtime,
|
||||
botUserId,
|
||||
teamId,
|
||||
historyLimit,
|
||||
sessionScope,
|
||||
mainKey,
|
||||
dmEnabled,
|
||||
dmPolicy,
|
||||
allowFrom,
|
||||
groupDmEnabled,
|
||||
groupDmChannels,
|
||||
channelsConfig,
|
||||
groupPolicy,
|
||||
useAccessGroups,
|
||||
reactionMode,
|
||||
reactionAllowlist,
|
||||
replyToMode,
|
||||
slashCommand,
|
||||
textLimit,
|
||||
ackReactionScope,
|
||||
mediaMaxBytes,
|
||||
removeAckAfterReply,
|
||||
});
|
||||
|
||||
const handleSlackMessage = createSlackMessageHandler({ ctx, account });
|
||||
|
||||
registerSlackMonitorEvents({ ctx, account, handleSlackMessage });
|
||||
registerSlackMonitorSlashCommands({ ctx, account });
|
||||
|
||||
const stopOnAbort = () => {
|
||||
if (opts.abortSignal?.aborted) void app.stop();
|
||||
};
|
||||
opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
|
||||
|
||||
try {
|
||||
await app.start();
|
||||
runtime.log?.("slack socket mode connected");
|
||||
if (opts.abortSignal?.aborted) return;
|
||||
await new Promise<void>((resolve) => {
|
||||
opts.abortSignal?.addEventListener("abort", () => resolve(), {
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
||||
await app.stop().catch(() => undefined);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user