refactor: prune room legacy
This commit is contained in:
@@ -16,14 +16,14 @@ export function resolveMatrixGroupRequireMention(params: ChannelGroupContext): b
|
|||||||
if (roomId.toLowerCase().startsWith("room:")) {
|
if (roomId.toLowerCase().startsWith("room:")) {
|
||||||
roomId = roomId.slice("room:".length).trim();
|
roomId = roomId.slice("room:".length).trim();
|
||||||
}
|
}
|
||||||
const groupRoom = params.groupRoom?.trim() ?? "";
|
const groupChannel = params.groupChannel?.trim() ?? "";
|
||||||
const aliases = groupRoom ? [groupRoom] : [];
|
const aliases = groupChannel ? [groupChannel] : [];
|
||||||
const cfg = params.cfg as CoreConfig;
|
const cfg = params.cfg as CoreConfig;
|
||||||
const resolved = resolveMatrixRoomConfig({
|
const resolved = resolveMatrixRoomConfig({
|
||||||
rooms: cfg.channels?.matrix?.rooms,
|
rooms: cfg.channels?.matrix?.rooms,
|
||||||
roomId,
|
roomId,
|
||||||
aliases,
|
aliases,
|
||||||
name: groupRoom || undefined,
|
name: groupChannel || undefined,
|
||||||
}).config;
|
}).config;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (resolved.autoReply === true) return false;
|
if (resolved.autoReply === true) return false;
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
|
|||||||
SenderId: senderId,
|
SenderId: senderId,
|
||||||
SenderUsername: senderId.split(":")[0]?.replace(/^@/, ""),
|
SenderUsername: senderId.split(":")[0]?.replace(/^@/, ""),
|
||||||
GroupSubject: isRoom ? (roomName ?? roomId) : undefined,
|
GroupSubject: isRoom ? (roomName ?? roomId) : undefined,
|
||||||
GroupRoom: isRoom ? (room.getCanonicalAlias?.() ?? roomId) : undefined,
|
GroupChannel: isRoom ? (room.getCanonicalAlias?.() ?? roomId) : undefined,
|
||||||
GroupSystemPrompt: isRoom ? groupSystemPrompt : undefined,
|
GroupSystemPrompt: isRoom ? groupSystemPrompt : undefined,
|
||||||
Provider: "matrix" as const,
|
Provider: "matrix" as const,
|
||||||
Surface: "matrix" as const,
|
Surface: "matrix" as const,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ describe("resolveGroupRequireMention", () => {
|
|||||||
const ctx: TemplateContext = {
|
const ctx: TemplateContext = {
|
||||||
Provider: "discord",
|
Provider: "discord",
|
||||||
From: "group:123",
|
From: "group:123",
|
||||||
GroupRoom: "#general",
|
GroupChannel: "#general",
|
||||||
GroupSpace: "145",
|
GroupSpace: "145",
|
||||||
};
|
};
|
||||||
const groupResolution: GroupKeyResolution = {
|
const groupResolution: GroupKeyResolution = {
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ export function resolveGroupRequireMention(params: {
|
|||||||
const channel = normalizeChannelId(rawChannel);
|
const channel = normalizeChannelId(rawChannel);
|
||||||
if (!channel) return true;
|
if (!channel) return true;
|
||||||
const groupId = groupResolution?.id ?? ctx.From?.replace(/^group:/, "");
|
const groupId = groupResolution?.id ?? ctx.From?.replace(/^group:/, "");
|
||||||
const groupRoom = ctx.GroupRoom?.trim() ?? ctx.GroupSubject?.trim();
|
const groupChannel = ctx.GroupChannel?.trim() ?? ctx.GroupSubject?.trim();
|
||||||
const groupSpace = ctx.GroupSpace?.trim();
|
const groupSpace = ctx.GroupSpace?.trim();
|
||||||
const requireMention = getChannelDock(channel)?.groups?.resolveRequireMention?.({
|
const requireMention = getChannelDock(channel)?.groups?.resolveRequireMention?.({
|
||||||
cfg,
|
cfg,
|
||||||
groupId,
|
groupId,
|
||||||
groupRoom,
|
groupChannel,
|
||||||
groupSpace,
|
groupSpace,
|
||||||
accountId: ctx.AccountId,
|
accountId: ctx.AccountId,
|
||||||
});
|
});
|
||||||
@@ -62,13 +62,13 @@ export function buildGroupIntro(params: {
|
|||||||
? "Activation: always-on (you receive every group message)."
|
? "Activation: always-on (you receive every group message)."
|
||||||
: "Activation: trigger-only (you are invoked only when explicitly mentioned; recent context may be included).";
|
: "Activation: trigger-only (you are invoked only when explicitly mentioned; recent context may be included).";
|
||||||
const groupId = params.sessionCtx.From?.replace(/^group:/, "");
|
const groupId = params.sessionCtx.From?.replace(/^group:/, "");
|
||||||
const groupRoom = params.sessionCtx.GroupRoom?.trim() ?? subject;
|
const groupChannel = params.sessionCtx.GroupChannel?.trim() ?? subject;
|
||||||
const groupSpace = params.sessionCtx.GroupSpace?.trim();
|
const groupSpace = params.sessionCtx.GroupSpace?.trim();
|
||||||
const providerIdsLine = providerId
|
const providerIdsLine = providerId
|
||||||
? getChannelDock(providerId)?.groups?.resolveGroupIntroHint?.({
|
? getChannelDock(providerId)?.groups?.resolveGroupIntroHint?.({
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
groupId,
|
groupId,
|
||||||
groupRoom,
|
groupChannel,
|
||||||
groupSpace,
|
groupSpace,
|
||||||
accountId: params.sessionCtx.AccountId,
|
accountId: params.sessionCtx.AccountId,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -242,14 +242,19 @@ export async function initSessionState(params: {
|
|||||||
const channel = groupResolution.channel;
|
const channel = groupResolution.channel;
|
||||||
const subject = ctx.GroupSubject?.trim();
|
const subject = ctx.GroupSubject?.trim();
|
||||||
const space = ctx.GroupSpace?.trim();
|
const space = ctx.GroupSpace?.trim();
|
||||||
const explicitRoom = ctx.GroupRoom?.trim();
|
const explicitChannel = ctx.GroupChannel?.trim();
|
||||||
const normalizedChannel = normalizeChannelId(channel);
|
const normalizedChannel = normalizeChannelId(channel);
|
||||||
const isRoomProvider = Boolean(
|
const isChannelProvider = Boolean(
|
||||||
normalizedChannel &&
|
normalizedChannel &&
|
||||||
getChannelDock(normalizedChannel)?.capabilities.chatTypes.includes("channel"),
|
getChannelDock(normalizedChannel)?.capabilities.chatTypes.includes("channel"),
|
||||||
);
|
);
|
||||||
const nextRoom =
|
const nextRoom =
|
||||||
explicitRoom ?? (isRoomProvider && subject && subject.startsWith("#") ? subject : undefined);
|
explicitChannel ??
|
||||||
|
((groupResolution.chatType === "channel" || isChannelProvider) &&
|
||||||
|
subject &&
|
||||||
|
subject.startsWith("#")
|
||||||
|
? subject
|
||||||
|
: undefined);
|
||||||
const nextSubject = nextRoom ? undefined : subject;
|
const nextSubject = nextRoom ? undefined : subject;
|
||||||
sessionEntry.chatType = groupResolution.chatType ?? "group";
|
sessionEntry.chatType = groupResolution.chatType ?? "group";
|
||||||
sessionEntry.channel = channel;
|
sessionEntry.channel = channel;
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ export type MsgContext = {
|
|||||||
/** Human label for envelope headers (conversation label, not sender). */
|
/** Human label for envelope headers (conversation label, not sender). */
|
||||||
ConversationLabel?: string;
|
ConversationLabel?: string;
|
||||||
GroupSubject?: string;
|
GroupSubject?: string;
|
||||||
GroupRoom?: string;
|
/** Human label for channel-like group conversations (e.g. #general, #support). */
|
||||||
|
GroupChannel?: string;
|
||||||
GroupSpace?: string;
|
GroupSpace?: string;
|
||||||
GroupMembers?: string;
|
GroupMembers?: string;
|
||||||
GroupSystemPrompt?: string;
|
GroupSystemPrompt?: string;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function resolveConversationLabel(ctx: MsgContext): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const base =
|
const base =
|
||||||
ctx.GroupRoom?.trim() ||
|
ctx.GroupChannel?.trim() ||
|
||||||
ctx.GroupSubject?.trim() ||
|
ctx.GroupSubject?.trim() ||
|
||||||
ctx.GroupSpace?.trim() ||
|
ctx.GroupSpace?.trim() ||
|
||||||
ctx.From?.trim() ||
|
ctx.From?.trim() ||
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { resolveSlackAccount } from "../../slack/accounts.js";
|
|||||||
type GroupMentionParams = {
|
type GroupMentionParams = {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
groupId?: string | null;
|
groupId?: string | null;
|
||||||
groupRoom?: string | null;
|
groupChannel?: string | null;
|
||||||
groupSpace?: string | null;
|
groupSpace?: string | null;
|
||||||
accountId?: string | null;
|
accountId?: string | null;
|
||||||
};
|
};
|
||||||
@@ -133,13 +133,14 @@ export function resolveDiscordGroupRequireMention(params: GroupMentionParams): b
|
|||||||
);
|
);
|
||||||
const channelEntries = guildEntry?.channels;
|
const channelEntries = guildEntry?.channels;
|
||||||
if (channelEntries && Object.keys(channelEntries).length > 0) {
|
if (channelEntries && Object.keys(channelEntries).length > 0) {
|
||||||
const channelSlug = normalizeDiscordSlug(params.groupRoom);
|
const groupChannel = params.groupChannel;
|
||||||
|
const channelSlug = normalizeDiscordSlug(groupChannel);
|
||||||
const entry =
|
const entry =
|
||||||
(params.groupId ? channelEntries[params.groupId] : undefined) ??
|
(params.groupId ? channelEntries[params.groupId] : undefined) ??
|
||||||
(channelSlug
|
(channelSlug
|
||||||
? (channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`])
|
? (channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`])
|
||||||
: undefined) ??
|
: undefined) ??
|
||||||
(params.groupRoom ? channelEntries[normalizeDiscordSlug(params.groupRoom)] : undefined);
|
(groupChannel ? channelEntries[normalizeDiscordSlug(groupChannel)] : undefined);
|
||||||
if (entry && typeof entry.requireMention === "boolean") {
|
if (entry && typeof entry.requireMention === "boolean") {
|
||||||
return entry.requireMention;
|
return entry.requireMention;
|
||||||
}
|
}
|
||||||
@@ -159,7 +160,8 @@ export function resolveSlackGroupRequireMention(params: GroupMentionParams): boo
|
|||||||
const keys = Object.keys(channels);
|
const keys = Object.keys(channels);
|
||||||
if (keys.length === 0) return true;
|
if (keys.length === 0) return true;
|
||||||
const channelId = params.groupId?.trim();
|
const channelId = params.groupId?.trim();
|
||||||
const channelName = params.groupRoom?.replace(/^#/, "");
|
const groupChannel = params.groupChannel;
|
||||||
|
const channelName = groupChannel?.replace(/^#/, "");
|
||||||
const normalizedName = normalizeSlackSlug(channelName);
|
const normalizedName = normalizeSlackSlug(channelName);
|
||||||
const candidates = [
|
const candidates = [
|
||||||
channelId ?? "",
|
channelId ?? "",
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ export type ChannelLogSink = {
|
|||||||
export type ChannelGroupContext = {
|
export type ChannelGroupContext = {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
groupId?: string | null;
|
groupId?: string | null;
|
||||||
groupRoom?: string | null;
|
/** Human label for channel-like group conversations (e.g. #general). */
|
||||||
|
groupChannel?: string | null;
|
||||||
groupSpace?: string | null;
|
groupSpace?: string | null;
|
||||||
accountId?: string | null;
|
accountId?: string | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
senderDisplay && senderTag && senderDisplay !== senderTag
|
senderDisplay && senderTag && senderDisplay !== senderTag
|
||||||
? `${senderDisplay} (${senderTag})`
|
? `${senderDisplay} (${senderTag})`
|
||||||
: (senderDisplay ?? senderTag ?? author.id);
|
: (senderDisplay ?? senderTag ?? author.id);
|
||||||
const groupRoom = isGuildMessage && displayChannelSlug ? `#${displayChannelSlug}` : undefined;
|
const groupChannel = isGuildMessage && displayChannelSlug ? `#${displayChannelSlug}` : undefined;
|
||||||
const groupSubject = isDirectMessage ? undefined : groupRoom;
|
const groupSubject = isDirectMessage ? undefined : groupChannel;
|
||||||
const channelDescription = channelInfo?.topic?.trim();
|
const channelDescription = channelInfo?.topic?.trim();
|
||||||
const systemPromptParts = [
|
const systemPromptParts = [
|
||||||
channelDescription ? `Channel topic: ${channelDescription}` : null,
|
channelDescription ? `Channel topic: ${channelDescription}` : null,
|
||||||
@@ -245,7 +245,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
SenderUsername: author.username,
|
SenderUsername: author.username,
|
||||||
SenderTag: formatDiscordUserTag(author),
|
SenderTag: formatDiscordUserTag(author),
|
||||||
GroupSubject: groupSubject,
|
GroupSubject: groupSubject,
|
||||||
GroupRoom: groupRoom,
|
GroupChannel: groupChannel,
|
||||||
GroupSystemPrompt: isGuildMessage ? groupSystemPrompt : undefined,
|
GroupSystemPrompt: isGuildMessage ? groupSystemPrompt : undefined,
|
||||||
GroupSpace: isGuildMessage ? (guildInfo?.id ?? guildSlug) || undefined : undefined,
|
GroupSpace: isGuildMessage ? (guildInfo?.id ?? guildSlug) || undefined : undefined,
|
||||||
Provider: "discord" as const,
|
Provider: "discord" as const,
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { describe, expect, it } from "vitest";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
buildSlackSlashCommandMatcher,
|
buildSlackSlashCommandMatcher,
|
||||||
isSlackRoomAllowedByPolicy,
|
isSlackChannelAllowedByPolicy,
|
||||||
resolveSlackThreadTs,
|
resolveSlackThreadTs,
|
||||||
} from "./monitor.js";
|
} from "./monitor.js";
|
||||||
|
|
||||||
describe("slack groupPolicy gating", () => {
|
describe("slack groupPolicy gating", () => {
|
||||||
it("allows when policy is open", () => {
|
it("allows when policy is open", () => {
|
||||||
expect(
|
expect(
|
||||||
isSlackRoomAllowedByPolicy({
|
isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: "open",
|
groupPolicy: "open",
|
||||||
channelAllowlistConfigured: false,
|
channelAllowlistConfigured: false,
|
||||||
channelAllowed: false,
|
channelAllowed: false,
|
||||||
@@ -19,7 +19,7 @@ describe("slack groupPolicy gating", () => {
|
|||||||
|
|
||||||
it("blocks when policy is disabled", () => {
|
it("blocks when policy is disabled", () => {
|
||||||
expect(
|
expect(
|
||||||
isSlackRoomAllowedByPolicy({
|
isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: "disabled",
|
groupPolicy: "disabled",
|
||||||
channelAllowlistConfigured: true,
|
channelAllowlistConfigured: true,
|
||||||
channelAllowed: true,
|
channelAllowed: true,
|
||||||
@@ -29,7 +29,7 @@ describe("slack groupPolicy gating", () => {
|
|||||||
|
|
||||||
it("blocks allowlist when no channel allowlist configured", () => {
|
it("blocks allowlist when no channel allowlist configured", () => {
|
||||||
expect(
|
expect(
|
||||||
isSlackRoomAllowedByPolicy({
|
isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
channelAllowlistConfigured: false,
|
channelAllowlistConfigured: false,
|
||||||
channelAllowed: true,
|
channelAllowed: true,
|
||||||
@@ -39,7 +39,7 @@ describe("slack groupPolicy gating", () => {
|
|||||||
|
|
||||||
it("allows allowlist when channel is allowed", () => {
|
it("allows allowlist when channel is allowed", () => {
|
||||||
expect(
|
expect(
|
||||||
isSlackRoomAllowedByPolicy({
|
isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
channelAllowlistConfigured: true,
|
channelAllowlistConfigured: true,
|
||||||
channelAllowed: true,
|
channelAllowed: true,
|
||||||
@@ -49,7 +49,7 @@ describe("slack groupPolicy gating", () => {
|
|||||||
|
|
||||||
it("blocks allowlist when channel is not allowed", () => {
|
it("blocks allowlist when channel is not allowed", () => {
|
||||||
expect(
|
expect(
|
||||||
isSlackRoomAllowedByPolicy({
|
isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
channelAllowlistConfigured: true,
|
channelAllowlistConfigured: true,
|
||||||
channelAllowed: false,
|
channelAllowed: false,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export { buildSlackSlashCommandMatcher } from "./monitor/commands.js";
|
export { buildSlackSlashCommandMatcher } from "./monitor/commands.js";
|
||||||
export { isSlackRoomAllowedByPolicy } from "./monitor/policy.js";
|
export { isSlackChannelAllowedByPolicy } from "./monitor/policy.js";
|
||||||
export { monitorSlackProvider } from "./monitor/provider.js";
|
export { monitorSlackProvider } from "./monitor/provider.js";
|
||||||
export { resolveSlackThreadTs } from "./monitor/replies.js";
|
export { resolveSlackThreadTs } from "./monitor/replies.js";
|
||||||
export type { MonitorSlackOpts } from "./monitor/types.js";
|
export type { MonitorSlackOpts } from "./monitor/types.js";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type { SlackMessageEvent } from "../types.js";
|
|||||||
|
|
||||||
import { normalizeAllowList, normalizeAllowListLower, normalizeSlackSlug } from "./allow-list.js";
|
import { normalizeAllowList, normalizeAllowListLower, normalizeSlackSlug } from "./allow-list.js";
|
||||||
import { resolveSlackChannelConfig } from "./channel-config.js";
|
import { resolveSlackChannelConfig } from "./channel-config.js";
|
||||||
import { isSlackRoomAllowedByPolicy } from "./policy.js";
|
import { isSlackChannelAllowedByPolicy } from "./policy.js";
|
||||||
|
|
||||||
export function inferSlackChannelType(
|
export function inferSlackChannelType(
|
||||||
channelId?: string | null,
|
channelId?: string | null,
|
||||||
@@ -314,7 +314,7 @@ export function createSlackMonitorContext(params: {
|
|||||||
const channelAllowlistConfigured =
|
const channelAllowlistConfigured =
|
||||||
Boolean(params.channelsConfig) && Object.keys(params.channelsConfig ?? {}).length > 0;
|
Boolean(params.channelsConfig) && Object.keys(params.channelsConfig ?? {}).length > 0;
|
||||||
if (
|
if (
|
||||||
!isSlackRoomAllowedByPolicy({
|
!isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: params.groupPolicy,
|
groupPolicy: params.groupPolicy,
|
||||||
channelAllowlistConfigured,
|
channelAllowlistConfigured,
|
||||||
channelAllowed,
|
channelAllowed,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export function isSlackRoomAllowedByPolicy(params: {
|
export function isSlackChannelAllowedByPolicy(params: {
|
||||||
groupPolicy: "open" | "disabled" | "allowlist";
|
groupPolicy: "open" | "disabled" | "allowlist";
|
||||||
channelAllowlistConfigured: boolean;
|
channelAllowlistConfigured: boolean;
|
||||||
channelAllowed: boolean;
|
channelAllowed: boolean;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
import { resolveSlackChannelConfig, type SlackChannelConfigResolved } from "./channel-config.js";
|
import { resolveSlackChannelConfig, type SlackChannelConfigResolved } from "./channel-config.js";
|
||||||
import { buildSlackSlashCommandMatcher, resolveSlackSlashCommandConfig } from "./commands.js";
|
import { buildSlackSlashCommandMatcher, resolveSlackSlashCommandConfig } from "./commands.js";
|
||||||
import type { SlackMonitorContext } from "./context.js";
|
import type { SlackMonitorContext } from "./context.js";
|
||||||
import { isSlackRoomAllowedByPolicy } from "./policy.js";
|
import { isSlackChannelAllowedByPolicy } from "./policy.js";
|
||||||
import { deliverSlackSlashReplies } from "./replies.js";
|
import { deliverSlackSlashReplies } from "./replies.js";
|
||||||
|
|
||||||
type SlackBlock = { type: string; [key: string]: unknown };
|
type SlackBlock = { type: string; [key: string]: unknown };
|
||||||
@@ -247,7 +247,7 @@ export function registerSlackMonitorSlashCommands(params: {
|
|||||||
Boolean(ctx.channelsConfig) && Object.keys(ctx.channelsConfig ?? {}).length > 0;
|
Boolean(ctx.channelsConfig) && Object.keys(ctx.channelsConfig ?? {}).length > 0;
|
||||||
const channelAllowed = channelConfig?.allowed !== false;
|
const channelAllowed = channelConfig?.allowed !== false;
|
||||||
if (
|
if (
|
||||||
!isSlackRoomAllowedByPolicy({
|
!isSlackChannelAllowedByPolicy({
|
||||||
groupPolicy: ctx.groupPolicy,
|
groupPolicy: ctx.groupPolicy,
|
||||||
channelAllowlistConfigured,
|
channelAllowlistConfigured,
|
||||||
channelAllowed,
|
channelAllowed,
|
||||||
|
|||||||
Reference in New Issue
Block a user