mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-26 16:41:49 +00:00
fix(matrix): migrate room allow aliases to enabled (#60690)
* fix(matrix): migrate room allow aliases to enabled * test(matrix): keep migration coverage on the channel seam * chore(config): refresh baselines after matrix alias cleanup
This commit is contained in:
@@ -37,7 +37,7 @@ describe("MatrixConfigSchema SecretInput", () => {
|
||||
accessToken: "token",
|
||||
groups: {
|
||||
"!room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
},
|
||||
@@ -55,7 +55,7 @@ describe("MatrixConfigSchema SecretInput", () => {
|
||||
accessToken: "token",
|
||||
rooms: {
|
||||
"!room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -45,7 +45,6 @@ const matrixRoomSchema = z
|
||||
.object({
|
||||
account: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
allow: z.boolean().optional(),
|
||||
requireMention: z.boolean().optional(),
|
||||
allowBots: z.union([z.boolean(), z.literal("mentions")]).optional(),
|
||||
tools: ToolPolicySchema,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
collectMatrixInstallPathWarnings,
|
||||
formatMatrixLegacyCryptoPreview,
|
||||
formatMatrixLegacyStatePreview,
|
||||
matrixDoctor,
|
||||
runMatrixDoctorSequence,
|
||||
} from "./doctor.js";
|
||||
|
||||
@@ -125,4 +126,50 @@ describe("matrix doctor", () => {
|
||||
});
|
||||
expect(sequence.changeNotes.join("\n")).toContain("Matrix migration snapshot");
|
||||
});
|
||||
|
||||
it("normalizes legacy Matrix room allow aliases to enabled", () => {
|
||||
const normalize = matrixDoctor.normalizeCompatibilityConfig;
|
||||
expect(normalize).toBeDefined();
|
||||
if (!normalize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = normalize({
|
||||
cfg: {
|
||||
channels: {
|
||||
matrix: {
|
||||
groups: {
|
||||
"!ops:example.org": {
|
||||
allow: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
work: {
|
||||
rooms: {
|
||||
"!legacy:example.org": {
|
||||
allow: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
expect(result.config.channels?.matrix?.groups?.["!ops:example.org"]).toEqual({
|
||||
enabled: true,
|
||||
});
|
||||
expect(result.config.channels?.matrix?.accounts?.work?.rooms?.["!legacy:example.org"]).toEqual(
|
||||
{
|
||||
enabled: false,
|
||||
},
|
||||
);
|
||||
expect(result.changes).toEqual(
|
||||
expect.arrayContaining([
|
||||
"Moved channels.matrix.groups.!ops:example.org.allow → channels.matrix.groups.!ops:example.org.enabled (true).",
|
||||
"Moved channels.matrix.accounts.work.rooms.!legacy:example.org.allow → channels.matrix.accounts.work.rooms.!legacy:example.org.enabled (false).",
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { ChannelDoctorAdapter } from "openclaw/plugin-sdk/channel-contract";
|
||||
import {
|
||||
type ChannelDoctorAdapter,
|
||||
type ChannelDoctorConfigMutation,
|
||||
type ChannelDoctorLegacyConfigRule,
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import {
|
||||
detectPluginInstallPathIssue,
|
||||
@@ -19,6 +23,161 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function hasLegacyMatrixRoomAllowAlias(value: unknown): boolean {
|
||||
const room = isRecord(value) ? value : null;
|
||||
return Boolean(room && typeof room.allow === "boolean");
|
||||
}
|
||||
|
||||
function hasLegacyMatrixRoomMapAllowAliases(value: unknown): boolean {
|
||||
const rooms = isRecord(value) ? value : null;
|
||||
return Boolean(rooms && Object.values(rooms).some((room) => hasLegacyMatrixRoomAllowAlias(room)));
|
||||
}
|
||||
|
||||
function hasLegacyMatrixAccountRoomAllowAliases(value: unknown): boolean {
|
||||
const accounts = isRecord(value) ? value : null;
|
||||
if (!accounts) {
|
||||
return false;
|
||||
}
|
||||
return Object.values(accounts).some((account) => {
|
||||
if (!isRecord(account)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
hasLegacyMatrixRoomMapAllowAliases(account.groups) ||
|
||||
hasLegacyMatrixRoomMapAllowAliases(account.rooms)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeMatrixRoomAllowAliases(params: {
|
||||
rooms: Record<string, unknown>;
|
||||
pathPrefix: string;
|
||||
changes: string[];
|
||||
}): { rooms: Record<string, unknown>; changed: boolean } {
|
||||
let changed = false;
|
||||
const nextRooms: Record<string, unknown> = { ...params.rooms };
|
||||
for (const [roomId, roomValue] of Object.entries(params.rooms)) {
|
||||
const room = isRecord(roomValue) ? roomValue : null;
|
||||
if (!room || typeof room.allow !== "boolean") {
|
||||
continue;
|
||||
}
|
||||
const nextRoom = { ...room };
|
||||
if (typeof nextRoom.enabled !== "boolean") {
|
||||
nextRoom.enabled = room.allow;
|
||||
}
|
||||
delete nextRoom.allow;
|
||||
nextRooms[roomId] = nextRoom;
|
||||
changed = true;
|
||||
params.changes.push(
|
||||
`Moved ${params.pathPrefix}.${roomId}.allow → ${params.pathPrefix}.${roomId}.enabled (${String(nextRoom.enabled)}).`,
|
||||
);
|
||||
}
|
||||
return { rooms: nextRooms, changed };
|
||||
}
|
||||
|
||||
function normalizeMatrixCompatibilityConfig(cfg: OpenClawConfig): ChannelDoctorConfigMutation {
|
||||
const channels = isRecord(cfg.channels) ? cfg.channels : null;
|
||||
const matrix = isRecord(channels?.matrix) ? channels.matrix : null;
|
||||
if (!matrix) {
|
||||
return { config: cfg, changes: [] };
|
||||
}
|
||||
|
||||
const changes: string[] = [];
|
||||
let updatedMatrix: Record<string, unknown> = matrix;
|
||||
let changed = false;
|
||||
|
||||
const normalizeTopLevelRoomScope = (key: "groups" | "rooms") => {
|
||||
const rooms = isRecord(updatedMatrix[key]) ? updatedMatrix[key] : null;
|
||||
if (!rooms) {
|
||||
return;
|
||||
}
|
||||
const normalized = normalizeMatrixRoomAllowAliases({
|
||||
rooms,
|
||||
pathPrefix: `channels.matrix.${key}`,
|
||||
changes,
|
||||
});
|
||||
if (normalized.changed) {
|
||||
updatedMatrix = { ...updatedMatrix, [key]: normalized.rooms };
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
|
||||
normalizeTopLevelRoomScope("groups");
|
||||
normalizeTopLevelRoomScope("rooms");
|
||||
|
||||
const accounts = isRecord(updatedMatrix.accounts) ? updatedMatrix.accounts : null;
|
||||
if (accounts) {
|
||||
let accountsChanged = false;
|
||||
const nextAccounts: Record<string, unknown> = { ...accounts };
|
||||
for (const [accountId, accountValue] of Object.entries(accounts)) {
|
||||
const account = isRecord(accountValue) ? accountValue : null;
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
let nextAccount: Record<string, unknown> = account;
|
||||
let accountChanged = false;
|
||||
for (const key of ["groups", "rooms"] as const) {
|
||||
const rooms = isRecord(nextAccount[key]) ? nextAccount[key] : null;
|
||||
if (!rooms) {
|
||||
continue;
|
||||
}
|
||||
const normalized = normalizeMatrixRoomAllowAliases({
|
||||
rooms,
|
||||
pathPrefix: `channels.matrix.accounts.${accountId}.${key}`,
|
||||
changes,
|
||||
});
|
||||
if (normalized.changed) {
|
||||
nextAccount = { ...nextAccount, [key]: normalized.rooms };
|
||||
accountChanged = true;
|
||||
}
|
||||
}
|
||||
if (accountChanged) {
|
||||
nextAccounts[accountId] = nextAccount;
|
||||
accountsChanged = true;
|
||||
}
|
||||
}
|
||||
if (accountsChanged) {
|
||||
updatedMatrix = { ...updatedMatrix, accounts: nextAccounts };
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return { config: cfg, changes: [] };
|
||||
}
|
||||
return {
|
||||
config: {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
matrix: updatedMatrix as OpenClawConfig["channels"]["matrix"],
|
||||
},
|
||||
},
|
||||
changes,
|
||||
};
|
||||
}
|
||||
|
||||
const MATRIX_LEGACY_CONFIG_RULES: ChannelDoctorLegacyConfigRule[] = [
|
||||
{
|
||||
path: ["channels", "matrix", "groups"],
|
||||
message:
|
||||
"channels.matrix.groups.<room>.allow is legacy; use channels.matrix.groups.<room>.enabled instead (auto-migrated on load).",
|
||||
match: hasLegacyMatrixRoomMapAllowAliases,
|
||||
},
|
||||
{
|
||||
path: ["channels", "matrix", "rooms"],
|
||||
message:
|
||||
"channels.matrix.rooms.<room>.allow is legacy; use channels.matrix.rooms.<room>.enabled instead (auto-migrated on load).",
|
||||
match: hasLegacyMatrixRoomMapAllowAliases,
|
||||
},
|
||||
{
|
||||
path: ["channels", "matrix", "accounts"],
|
||||
message:
|
||||
"channels.matrix.accounts.<id>.{groups,rooms}.<room>.allow is legacy; use channels.matrix.accounts.<id>.{groups,rooms}.<room>.enabled instead (auto-migrated on load).",
|
||||
match: hasLegacyMatrixAccountRoomAllowAliases,
|
||||
},
|
||||
];
|
||||
|
||||
function hasConfiguredMatrixChannel(cfg: OpenClawConfig): boolean {
|
||||
const channels = cfg.channels as Record<string, unknown> | undefined;
|
||||
return isRecord(channels?.matrix);
|
||||
@@ -259,6 +418,8 @@ export const matrixDoctor: ChannelDoctorAdapter = {
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
legacyConfigRules: MATRIX_LEGACY_CONFIG_RULES,
|
||||
normalizeCompatibilityConfig: ({ cfg }) => normalizeMatrixCompatibilityConfig(cfg),
|
||||
runConfigSequence: async ({ cfg, env, shouldRepair }) =>
|
||||
await runMatrixDoctorSequence({ cfg, env, shouldRepair }),
|
||||
cleanStaleConfig: async ({ cfg }) => await cleanStaleMatrixPluginConfig(cfg),
|
||||
|
||||
@@ -476,15 +476,15 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
groups: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!axis-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -503,20 +503,20 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.groups).toEqual({
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "axis" }).config.groups).toEqual({
|
||||
"!axis-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -529,15 +529,15 @@ describe("resolveMatrixAccount", () => {
|
||||
accessToken: "default-token",
|
||||
groups: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -552,20 +552,20 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.groups).toEqual({
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.groups).toEqual({
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -576,15 +576,15 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
rooms: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!axis-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -603,20 +603,20 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.rooms).toEqual({
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "axis" }).config.rooms).toEqual({
|
||||
"!axis-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "axis",
|
||||
},
|
||||
"!unassigned-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -629,15 +629,15 @@ describe("resolveMatrixAccount", () => {
|
||||
accessToken: "default-token",
|
||||
rooms: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -652,20 +652,20 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "default" }).config.rooms).toEqual({
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.rooms).toEqual({
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -683,15 +683,15 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
groups: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -700,11 +700,11 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "ops", env }).config.groups).toEqual({
|
||||
"!ops-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "ops",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -715,11 +715,11 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
groups: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -734,7 +734,7 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.groups).toEqual({
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -745,11 +745,11 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
rooms: {
|
||||
"!default-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
account: "default",
|
||||
},
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -764,7 +764,7 @@ describe("resolveMatrixAccount", () => {
|
||||
|
||||
expect(resolveMatrixAccount({ cfg, accountId: "ops" }).config.rooms).toEqual({
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -775,7 +775,7 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
groups: {
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
@@ -798,7 +798,7 @@ describe("resolveMatrixAccount", () => {
|
||||
matrix: {
|
||||
rooms: {
|
||||
"!shared-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
|
||||
@@ -122,7 +122,7 @@ describe("updateMatrixAccountConfig", () => {
|
||||
policy: "pairing",
|
||||
},
|
||||
groups: {
|
||||
"!default:example.org": { allow: true },
|
||||
"!default:example.org": { enabled: true },
|
||||
},
|
||||
accounts: {
|
||||
ops: {
|
||||
@@ -145,14 +145,14 @@ describe("updateMatrixAccountConfig", () => {
|
||||
},
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"!ops-room:example.org": { allow: true },
|
||||
"!ops-room:example.org": { enabled: true },
|
||||
},
|
||||
rooms: null,
|
||||
});
|
||||
|
||||
expect(updated.channels?.["matrix"]?.dm?.policy).toBe("pairing");
|
||||
expect(updated.channels?.["matrix"]?.groups).toEqual({
|
||||
"!default:example.org": { allow: true },
|
||||
"!default:example.org": { enabled: true },
|
||||
});
|
||||
expect(updated.channels?.["matrix"]?.accounts?.ops).toMatchObject({
|
||||
dm: {
|
||||
@@ -162,7 +162,7 @@ describe("updateMatrixAccountConfig", () => {
|
||||
},
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"!ops-room:example.org": { allow: true },
|
||||
"!ops-room:example.org": { enabled: true },
|
||||
},
|
||||
});
|
||||
expect(updated.channels?.["matrix"]?.accounts?.ops?.rooms).toBeUndefined();
|
||||
|
||||
@@ -39,13 +39,13 @@ describe("resolveMatrixMonitorConfig", () => {
|
||||
);
|
||||
|
||||
const roomsConfig: MatrixRoomsConfig = {
|
||||
"*": { allow: true },
|
||||
"*": { enabled: true },
|
||||
"room:!ops:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
users: ["Dana", "user:@Erin:Example.org"],
|
||||
},
|
||||
General: {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -62,13 +62,13 @@ describe("resolveMatrixMonitorConfig", () => {
|
||||
expect(result.allowFrom).toEqual(["@alice:example.org", "@bob:example.org"]);
|
||||
expect(result.groupAllowFrom).toEqual(["@carol:example.org"]);
|
||||
expect(result.roomsConfig).toEqual({
|
||||
"*": { allow: true },
|
||||
"*": { enabled: true },
|
||||
"!ops:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
users: ["@dana:example.org", "@erin:example.org"],
|
||||
},
|
||||
"!general:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveTargets).toHaveBeenCalledTimes(3);
|
||||
@@ -116,7 +116,7 @@ describe("resolveMatrixMonitorConfig", () => {
|
||||
groupAllowFrom: ["matrix:@known:example.org"],
|
||||
roomsConfig: {
|
||||
"channel:Project X": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
users: ["matrix:Ghost"],
|
||||
},
|
||||
},
|
||||
@@ -174,7 +174,7 @@ describe("resolveMatrixMonitorConfig", () => {
|
||||
accountId: "ops",
|
||||
roomsConfig: {
|
||||
"#allowed:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
runtime,
|
||||
@@ -183,7 +183,7 @@ describe("resolveMatrixMonitorConfig", () => {
|
||||
|
||||
expect(result.roomsConfig).toEqual({
|
||||
"!allowed-room:example.org": {
|
||||
allow: true,
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
expect(resolveTargets).toHaveBeenCalledWith(
|
||||
|
||||
@@ -4,9 +4,9 @@ import { resolveMatrixRoomConfig } from "./rooms.js";
|
||||
describe("resolveMatrixRoomConfig", () => {
|
||||
it("matches room IDs and aliases, not names", () => {
|
||||
const rooms = {
|
||||
"!room:example.org": { allow: true },
|
||||
"#alias:example.org": { allow: true },
|
||||
"Project Room": { allow: true },
|
||||
"!room:example.org": { enabled: true },
|
||||
"#alias:example.org": { enabled: true },
|
||||
"Project Room": { enabled: true },
|
||||
};
|
||||
|
||||
const byId = resolveMatrixRoomConfig({
|
||||
@@ -26,7 +26,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
expect(byAlias.matchKey).toBe("#alias:example.org");
|
||||
|
||||
const byName = resolveMatrixRoomConfig({
|
||||
rooms: { "Project Room": { allow: true } },
|
||||
rooms: { "Project Room": { enabled: true } },
|
||||
roomId: "!different:example.org",
|
||||
aliases: [],
|
||||
});
|
||||
@@ -37,7 +37,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
describe("matchSource classification", () => {
|
||||
it('returns matchSource="direct" for exact room ID match', () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: { "!room:example.org": { allow: true } },
|
||||
rooms: { "!room:example.org": { enabled: true } },
|
||||
roomId: "!room:example.org",
|
||||
aliases: [],
|
||||
});
|
||||
@@ -47,7 +47,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
|
||||
it('returns matchSource="direct" for alias match', () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: { "#alias:example.org": { allow: true } },
|
||||
rooms: { "#alias:example.org": { enabled: true } },
|
||||
roomId: "!room:example.org",
|
||||
aliases: ["#alias:example.org"],
|
||||
});
|
||||
@@ -57,7 +57,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
|
||||
it('returns matchSource="wildcard" for wildcard match', () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: { "*": { allow: true } },
|
||||
rooms: { "*": { enabled: true } },
|
||||
roomId: "!any:example.org",
|
||||
aliases: [],
|
||||
});
|
||||
@@ -67,7 +67,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
|
||||
it("returns undefined matchSource when no match", () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: { "!other:example.org": { allow: true } },
|
||||
rooms: { "!other:example.org": { enabled: true } },
|
||||
roomId: "!room:example.org",
|
||||
aliases: [],
|
||||
});
|
||||
@@ -78,8 +78,8 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
it("direct match takes priority over wildcard", () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: {
|
||||
"!room:example.org": { allow: true, systemPrompt: "room-specific" },
|
||||
"*": { allow: true, systemPrompt: "generic" },
|
||||
"!room:example.org": { enabled: true, systemPrompt: "room-specific" },
|
||||
"*": { enabled: true, systemPrompt: "generic" },
|
||||
},
|
||||
roomId: "!room:example.org",
|
||||
aliases: [],
|
||||
@@ -96,7 +96,7 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
|
||||
it("wildcard config should NOT be usable to override DM classification", () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: { "*": { allow: true, skills: ["general"] } },
|
||||
rooms: { "*": { enabled: true, skills: ["general"] } },
|
||||
roomId: "!dm-room:example.org",
|
||||
aliases: [],
|
||||
});
|
||||
@@ -108,8 +108,8 @@ describe("resolveMatrixRoomConfig", () => {
|
||||
it("explicitly configured room should be usable to override DM classification", () => {
|
||||
const result = resolveMatrixRoomConfig({
|
||||
rooms: {
|
||||
"!configured-room:example.org": { allow: true },
|
||||
"*": { allow: true },
|
||||
"!configured-room:example.org": { enabled: true },
|
||||
"*": { enabled: true },
|
||||
},
|
||||
roomId: "!configured-room:example.org",
|
||||
aliases: [],
|
||||
|
||||
@@ -9,6 +9,11 @@ export type MatrixRoomConfigResolved = {
|
||||
matchSource?: "direct" | "wildcard";
|
||||
};
|
||||
|
||||
function readLegacyRoomAllowAlias(room: MatrixRoomConfig | undefined): boolean | undefined {
|
||||
const rawRoom = room as Record<string, unknown> | undefined;
|
||||
return typeof rawRoom?.allow === "boolean" ? rawRoom.allow : undefined;
|
||||
}
|
||||
|
||||
export function resolveMatrixRoomConfig(params: {
|
||||
rooms?: Record<string, MatrixRoomConfig>;
|
||||
roomId: string;
|
||||
@@ -33,7 +38,8 @@ export function resolveMatrixRoomConfig(params: {
|
||||
wildcardKey: "*",
|
||||
});
|
||||
const resolved = matched ?? wildcardEntry;
|
||||
const allowed = resolved ? resolved.enabled !== false && resolved.allow !== false : false;
|
||||
const legacyAllow = readLegacyRoomAllowAlias(resolved);
|
||||
const allowed = resolved ? resolved.enabled !== false && legacyAllow !== false : false;
|
||||
const matchKey = matchedKey ?? wildcardKey;
|
||||
const matchSource = matched ? "direct" : wildcardEntry ? "wildcard" : undefined;
|
||||
return {
|
||||
|
||||
@@ -308,7 +308,7 @@ describe("matrix onboarding", () => {
|
||||
},
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"!ops-room:example.org": { allow: true },
|
||||
"!ops-room:example.org": { enabled: true },
|
||||
},
|
||||
});
|
||||
expect(result.cfg.channels?.["matrix"]?.dm).toBeUndefined();
|
||||
|
||||
@@ -183,7 +183,7 @@ function setMatrixGroupPolicy(
|
||||
}
|
||||
|
||||
function setMatrixGroupRooms(cfg: CoreConfig, roomKeys: string[], accountId?: string) {
|
||||
const groups = Object.fromEntries(roomKeys.map((key) => [key, { allow: true }]));
|
||||
const groups = Object.fromEntries(roomKeys.map((key) => [key, { enabled: true }]));
|
||||
return updateMatrixAccountConfig(cfg, resolveMatrixOnboardingAccountId(cfg, accountId), {
|
||||
groups,
|
||||
rooms: null,
|
||||
|
||||
@@ -23,10 +23,8 @@ export type MatrixDmConfig = {
|
||||
export type MatrixRoomConfig = {
|
||||
/** Restrict this room entry to a specific Matrix account in multi-account setups. */
|
||||
account?: string;
|
||||
/** If false, disable the bot in this room (alias for allow: false). */
|
||||
/** If false, disable the bot in this room. */
|
||||
enabled?: boolean;
|
||||
/** Legacy room allow toggle; prefer enabled. */
|
||||
allow?: boolean;
|
||||
/** Require mentioning the bot to trigger replies. */
|
||||
requireMention?: boolean;
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user