mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-07 22:09:57 +00:00
refactor: dedupe channel and gateway surfaces
This commit is contained in:
@@ -36,6 +36,18 @@ function createContext(overrides?: {
|
||||
} as Parameters<typeof createSlackMessageHandler>[0]["ctx"];
|
||||
}
|
||||
|
||||
function createHandlerWithTracker(overrides?: {
|
||||
markMessageSeen?: (channel: string | undefined, ts: string | undefined) => boolean;
|
||||
}) {
|
||||
const trackEvent = vi.fn();
|
||||
const handler = createSlackMessageHandler({
|
||||
ctx: createContext(overrides),
|
||||
account: { accountId: "default" } as Parameters<typeof createSlackMessageHandler>[0]["account"],
|
||||
trackEvent,
|
||||
});
|
||||
return { handler, trackEvent };
|
||||
}
|
||||
|
||||
describe("createSlackMessageHandler", () => {
|
||||
beforeEach(() => {
|
||||
enqueueMock.mockClear();
|
||||
@@ -68,14 +80,7 @@ describe("createSlackMessageHandler", () => {
|
||||
});
|
||||
|
||||
it("does not track duplicate messages that are already seen", async () => {
|
||||
const trackEvent = vi.fn();
|
||||
const handler = createSlackMessageHandler({
|
||||
ctx: createContext({ markMessageSeen: () => true }),
|
||||
account: { accountId: "default" } as Parameters<
|
||||
typeof createSlackMessageHandler
|
||||
>[0]["account"],
|
||||
trackEvent,
|
||||
});
|
||||
const { handler, trackEvent } = createHandlerWithTracker({ markMessageSeen: () => true });
|
||||
|
||||
await handler(
|
||||
{
|
||||
@@ -93,14 +98,7 @@ describe("createSlackMessageHandler", () => {
|
||||
});
|
||||
|
||||
it("tracks accepted non-duplicate messages", async () => {
|
||||
const trackEvent = vi.fn();
|
||||
const handler = createSlackMessageHandler({
|
||||
ctx: createContext(),
|
||||
account: { accountId: "default" } as Parameters<
|
||||
typeof createSlackMessageHandler
|
||||
>[0]["account"],
|
||||
trackEvent,
|
||||
});
|
||||
const { handler, trackEvent } = createHandlerWithTracker();
|
||||
|
||||
await handler(
|
||||
{
|
||||
|
||||
68
src/slack/monitor/message-handler/prepare.test-helpers.ts
Normal file
68
src/slack/monitor/message-handler/prepare.test-helpers.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { App } from "@slack/bolt";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../../runtime.js";
|
||||
import type { ResolvedSlackAccount } from "../../accounts.js";
|
||||
import { createSlackMonitorContext } from "../context.js";
|
||||
|
||||
export function createInboundSlackTestContext(params: {
|
||||
cfg: OpenClawConfig;
|
||||
appClient?: App["client"];
|
||||
defaultRequireMention?: boolean;
|
||||
replyToMode?: "off" | "all" | "first";
|
||||
channelsConfig?: Record<string, { systemPrompt: string }>;
|
||||
}) {
|
||||
return createSlackMonitorContext({
|
||||
cfg: params.cfg,
|
||||
accountId: "default",
|
||||
botToken: "token",
|
||||
app: { client: params.appClient ?? {} } as App,
|
||||
runtime: {} as RuntimeEnv,
|
||||
botUserId: "B1",
|
||||
teamId: "T1",
|
||||
apiAppId: "A1",
|
||||
historyLimit: 0,
|
||||
sessionScope: "per-sender",
|
||||
mainKey: "main",
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open",
|
||||
allowFrom: [],
|
||||
allowNameMatching: false,
|
||||
groupDmEnabled: true,
|
||||
groupDmChannels: [],
|
||||
defaultRequireMention: params.defaultRequireMention ?? true,
|
||||
channelsConfig: params.channelsConfig,
|
||||
groupPolicy: "open",
|
||||
useAccessGroups: false,
|
||||
reactionMode: "off",
|
||||
reactionAllowlist: [],
|
||||
replyToMode: params.replyToMode ?? "off",
|
||||
threadHistoryScope: "thread",
|
||||
threadInheritParent: false,
|
||||
slashCommand: {
|
||||
enabled: false,
|
||||
name: "openclaw",
|
||||
sessionPrefix: "slack:slash",
|
||||
ephemeral: true,
|
||||
},
|
||||
textLimit: 4000,
|
||||
ackReactionScope: "group-mentions",
|
||||
mediaMaxBytes: 1024,
|
||||
removeAckAfterReply: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function createSlackTestAccount(
|
||||
config: ResolvedSlackAccount["config"] = {},
|
||||
): ResolvedSlackAccount {
|
||||
return {
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
botTokenSource: "config",
|
||||
appTokenSource: "config",
|
||||
userTokenSource: "none",
|
||||
config,
|
||||
replyToMode: config.replyToMode,
|
||||
replyToModeByChatType: config.replyToModeByChatType,
|
||||
dm: config.dm,
|
||||
};
|
||||
}
|
||||
@@ -1,64 +1,26 @@
|
||||
import type { App } from "@slack/bolt";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../../runtime.js";
|
||||
import type { ResolvedSlackAccount } from "../../accounts.js";
|
||||
import type { SlackMessageEvent } from "../../types.js";
|
||||
import { createSlackMonitorContext } from "../context.js";
|
||||
import { prepareSlackMessage } from "./prepare.js";
|
||||
import { createInboundSlackTestContext, createSlackTestAccount } from "./prepare.test-helpers.js";
|
||||
|
||||
function buildCtx(overrides?: { replyToMode?: "all" | "first" | "off" }) {
|
||||
return createSlackMonitorContext({
|
||||
const replyToMode = overrides?.replyToMode ?? "all";
|
||||
return createInboundSlackTestContext({
|
||||
cfg: {
|
||||
channels: {
|
||||
slack: { enabled: true, replyToMode: overrides?.replyToMode ?? "all" },
|
||||
slack: { enabled: true, replyToMode },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
accountId: "default",
|
||||
botToken: "token",
|
||||
app: { client: {} } as App,
|
||||
runtime: {} as RuntimeEnv,
|
||||
botUserId: "B1",
|
||||
teamId: "T1",
|
||||
apiAppId: "A1",
|
||||
historyLimit: 0,
|
||||
sessionScope: "per-sender",
|
||||
mainKey: "main",
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open",
|
||||
allowFrom: [],
|
||||
groupDmEnabled: true,
|
||||
groupDmChannels: [],
|
||||
appClient: {} as App["client"],
|
||||
defaultRequireMention: false,
|
||||
groupPolicy: "open",
|
||||
allowNameMatching: false,
|
||||
useAccessGroups: false,
|
||||
reactionMode: "off",
|
||||
reactionAllowlist: [],
|
||||
replyToMode: overrides?.replyToMode ?? "all",
|
||||
threadHistoryScope: "thread",
|
||||
threadInheritParent: false,
|
||||
slashCommand: {
|
||||
enabled: false,
|
||||
name: "openclaw",
|
||||
sessionPrefix: "slack:slash",
|
||||
ephemeral: true,
|
||||
},
|
||||
textLimit: 4000,
|
||||
ackReactionScope: "group-mentions",
|
||||
mediaMaxBytes: 1024,
|
||||
removeAckAfterReply: false,
|
||||
replyToMode,
|
||||
});
|
||||
}
|
||||
|
||||
const account: ResolvedSlackAccount = {
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
botTokenSource: "config",
|
||||
appTokenSource: "config",
|
||||
userTokenSource: "none",
|
||||
config: {},
|
||||
};
|
||||
const account: ResolvedSlackAccount = createSlackTestAccount();
|
||||
|
||||
describe("thread-level session keys", () => {
|
||||
it("uses thread-level session key for channel messages", async () => {
|
||||
|
||||
@@ -510,11 +510,11 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
const [
|
||||
{ resolveConversationLabel },
|
||||
{ createReplyPrefixOptions },
|
||||
{ recordSessionMetaFromInbound, resolveStorePath },
|
||||
{ recordInboundSessionMetaSafe },
|
||||
] = await Promise.all([
|
||||
import("../../channels/conversation-label.js"),
|
||||
import("../../channels/reply-prefix.js"),
|
||||
import("../../config/sessions.js"),
|
||||
import("../../channels/session-meta.js"),
|
||||
]);
|
||||
|
||||
const route = resolveAgentRoute({
|
||||
@@ -578,18 +578,14 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
OriginatingTo: `user:${command.user_id}`,
|
||||
});
|
||||
|
||||
const storePath = resolveStorePath(cfg.session?.store, {
|
||||
await recordInboundSessionMetaSafe({
|
||||
cfg,
|
||||
agentId: route.agentId,
|
||||
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
||||
ctx: ctxPayload,
|
||||
onError: (err) =>
|
||||
runtime.error?.(danger(`slack slash: failed updating session meta: ${String(err)}`)),
|
||||
});
|
||||
try {
|
||||
await recordSessionMetaFromInbound({
|
||||
storePath,
|
||||
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
||||
ctx: ctxPayload,
|
||||
});
|
||||
} catch (err) {
|
||||
runtime.error?.(danger(`slack slash: failed updating session meta: ${String(err)}`));
|
||||
}
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
cfg,
|
||||
|
||||
Reference in New Issue
Block a user