fix: normalize slack channel types for sessions
This commit is contained in:
62
src/slack/monitor/context.test.ts
Normal file
62
src/slack/monitor/context.test.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import type { App } from "@slack/bolt";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
|
import { createSlackMonitorContext, normalizeSlackChannelType } from "./context.js";
|
||||||
|
|
||||||
|
const baseParams = () => ({
|
||||||
|
cfg: {} as ClawdbotConfig,
|
||||||
|
accountId: "default",
|
||||||
|
botToken: "token",
|
||||||
|
app: { client: {} } as App,
|
||||||
|
runtime: {} as RuntimeEnv,
|
||||||
|
botUserId: "B1",
|
||||||
|
teamId: "T1",
|
||||||
|
apiAppId: "A1",
|
||||||
|
historyLimit: 0,
|
||||||
|
sessionScope: "per-sender" as const,
|
||||||
|
mainKey: "main",
|
||||||
|
dmEnabled: true,
|
||||||
|
dmPolicy: "open" as const,
|
||||||
|
allowFrom: [],
|
||||||
|
groupDmEnabled: true,
|
||||||
|
groupDmChannels: [],
|
||||||
|
defaultRequireMention: true,
|
||||||
|
groupPolicy: "open" as const,
|
||||||
|
useAccessGroups: false,
|
||||||
|
reactionMode: "off" as const,
|
||||||
|
reactionAllowlist: [],
|
||||||
|
replyToMode: "off" as const,
|
||||||
|
slashCommand: {
|
||||||
|
enabled: false,
|
||||||
|
name: "clawd",
|
||||||
|
sessionPrefix: "slack:slash",
|
||||||
|
ephemeral: true,
|
||||||
|
},
|
||||||
|
textLimit: 4000,
|
||||||
|
ackReactionScope: "group-mentions",
|
||||||
|
mediaMaxBytes: 1,
|
||||||
|
removeAckAfterReply: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("normalizeSlackChannelType", () => {
|
||||||
|
it("infers channel types from ids when missing", () => {
|
||||||
|
expect(normalizeSlackChannelType(undefined, "C123")).toBe("channel");
|
||||||
|
expect(normalizeSlackChannelType(undefined, "D123")).toBe("im");
|
||||||
|
expect(normalizeSlackChannelType(undefined, "G123")).toBe("group");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prefers explicit channel_type values", () => {
|
||||||
|
expect(normalizeSlackChannelType("mpim", "C123")).toBe("mpim");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("resolveSlackSystemEventSessionKey", () => {
|
||||||
|
it("defaults missing channel_type to channel sessions", () => {
|
||||||
|
const ctx = createSlackMonitorContext(baseParams());
|
||||||
|
expect(ctx.resolveSlackSystemEventSessionKey({ channelId: "C123" })).toBe(
|
||||||
|
"agent:main:slack:channel:C123",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,6 +13,28 @@ import { normalizeAllowList, normalizeAllowListLower, normalizeSlackSlug } from
|
|||||||
import { resolveSlackChannelConfig } from "./channel-config.js";
|
import { resolveSlackChannelConfig } from "./channel-config.js";
|
||||||
import { isSlackRoomAllowedByPolicy } from "./policy.js";
|
import { isSlackRoomAllowedByPolicy } from "./policy.js";
|
||||||
|
|
||||||
|
export function inferSlackChannelType(
|
||||||
|
channelId?: string | null,
|
||||||
|
): SlackMessageEvent["channel_type"] | undefined {
|
||||||
|
const trimmed = channelId?.trim();
|
||||||
|
if (!trimmed) return undefined;
|
||||||
|
if (trimmed.startsWith("D")) return "im";
|
||||||
|
if (trimmed.startsWith("C")) return "channel";
|
||||||
|
if (trimmed.startsWith("G")) return "group";
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeSlackChannelType(
|
||||||
|
channelType?: string | null,
|
||||||
|
channelId?: string | null,
|
||||||
|
): SlackMessageEvent["channel_type"] {
|
||||||
|
const normalized = channelType?.trim().toLowerCase();
|
||||||
|
if (normalized === "im" || normalized === "mpim" || normalized === "channel" || normalized === "group") {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return inferSlackChannelType(channelId) ?? "channel";
|
||||||
|
}
|
||||||
|
|
||||||
export type SlackMonitorContext = {
|
export type SlackMonitorContext = {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
@@ -147,15 +169,15 @@ export function createSlackMonitorContext(params: {
|
|||||||
}) => {
|
}) => {
|
||||||
const channelId = p.channelId?.trim() ?? "";
|
const channelId = p.channelId?.trim() ?? "";
|
||||||
if (!channelId) return params.mainKey;
|
if (!channelId) return params.mainKey;
|
||||||
const channelType = p.channelType?.trim().toLowerCase() ?? "";
|
const channelType = normalizeSlackChannelType(p.channelType, channelId);
|
||||||
const isRoom = channelType === "channel" || channelType === "group";
|
const isDirectMessage = channelType === "im";
|
||||||
const isGroup = channelType === "mpim";
|
const isGroup = channelType === "mpim";
|
||||||
const from = isRoom
|
const from = isDirectMessage
|
||||||
? `slack:channel:${channelId}`
|
? `slack:${channelId}`
|
||||||
: isGroup
|
: isGroup
|
||||||
? `slack:group:${channelId}`
|
? `slack:group:${channelId}`
|
||||||
: `slack:${channelId}`;
|
: `slack:channel:${channelId}`;
|
||||||
const chatType = isRoom ? "room" : isGroup ? "group" : "direct";
|
const chatType = isDirectMessage ? "direct" : isGroup ? "group" : "room";
|
||||||
return resolveSessionKey(
|
return resolveSessionKey(
|
||||||
params.sessionScope,
|
params.sessionScope,
|
||||||
{ From: from, ChatType: chatType, Provider: "slack" },
|
{ From: from, ChatType: chatType, Provider: "slack" },
|
||||||
@@ -249,7 +271,7 @@ export function createSlackMonitorContext(params: {
|
|||||||
channelName?: string;
|
channelName?: string;
|
||||||
channelType?: SlackMessageEvent["channel_type"];
|
channelType?: SlackMessageEvent["channel_type"];
|
||||||
}) => {
|
}) => {
|
||||||
const channelType = p.channelType;
|
const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
|
||||||
const isDirectMessage = channelType === "im";
|
const isDirectMessage = channelType === "im";
|
||||||
const isGroupDm = channelType === "mpim";
|
const isGroupDm = channelType === "mpim";
|
||||||
const isRoom = channelType === "channel" || channelType === "group";
|
const isRoom = channelType === "channel" || channelType === "group";
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import type { SlackMessageEvent } from "../../types.js";
|
|||||||
import { allowListMatches, resolveSlackUserAllowed } from "../allow-list.js";
|
import { allowListMatches, resolveSlackUserAllowed } from "../allow-list.js";
|
||||||
import { isSlackSenderAllowListed, resolveSlackEffectiveAllowFrom } from "../auth.js";
|
import { isSlackSenderAllowListed, resolveSlackEffectiveAllowFrom } from "../auth.js";
|
||||||
import { resolveSlackChannelConfig } from "../channel-config.js";
|
import { resolveSlackChannelConfig } from "../channel-config.js";
|
||||||
import type { SlackMonitorContext } from "../context.js";
|
import { normalizeSlackChannelType, type SlackMonitorContext } from "../context.js";
|
||||||
import { resolveSlackMedia, resolveSlackThreadStarter } from "../media.js";
|
import { resolveSlackMedia, resolveSlackThreadStarter } from "../media.js";
|
||||||
|
|
||||||
import type { PreparedSlackMessage } from "./types.js";
|
import type { PreparedSlackMessage } from "./types.js";
|
||||||
@@ -45,7 +45,7 @@ export async function prepareSlackMessage(params: {
|
|||||||
channelType = channelType ?? channelInfo.type;
|
channelType = channelType ?? channelInfo.type;
|
||||||
}
|
}
|
||||||
const channelName = channelInfo?.name;
|
const channelName = channelInfo?.name;
|
||||||
const resolvedChannelType = channelType;
|
const resolvedChannelType = normalizeSlackChannelType(channelType, message.channel);
|
||||||
const isDirectMessage = resolvedChannelType === "im";
|
const isDirectMessage = resolvedChannelType === "im";
|
||||||
const isGroupDm = resolvedChannelType === "mpim";
|
const isGroupDm = resolvedChannelType === "mpim";
|
||||||
const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
|
const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
|
||||||
|
|||||||
Reference in New Issue
Block a user