fix: harden Mattermost plugin gating (#1428) (thanks @damoahdominic)

This commit is contained in:
Peter Steinberger
2026-01-23 00:19:23 +00:00
parent 1d658109a8
commit 279f799388
55 changed files with 403 additions and 413 deletions

View File

@@ -35,7 +35,7 @@ function detectAutoKind(input: string): ChannelResolveKind {
if (!trimmed) return "group";
if (trimmed.startsWith("@")) return "user";
if (/^<@!?/.test(trimmed)) return "user";
if (/^(user|discord|slack|mattermost|matrix|msteams|teams|zalo|zalouser):/i.test(trimmed)) {
if (/^(user|discord|slack|matrix|msteams|teams|zalo|zalouser):/i.test(trimmed)) {
return "user";
}
return "group";

View File

@@ -52,8 +52,6 @@ const SHELL_ENV_EXPECTED_KEYS = [
"DISCORD_BOT_TOKEN",
"SLACK_BOT_TOKEN",
"SLACK_APP_TOKEN",
"MATTERMOST_BOT_TOKEN",
"MATTERMOST_URL",
"CLAWDBOT_GATEWAY_TOKEN",
"CLAWDBOT_GATEWAY_PASSWORD",
];

View File

@@ -124,7 +124,6 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_1: LegacyConfigMigration[] = [
"telegram",
"discord",
"slack",
"mattermost",
"signal",
"imessage",
"msteams",

View File

@@ -17,10 +17,6 @@ export const LEGACY_CONFIG_RULES: LegacyConfigRule[] = [
path: ["slack"],
message: "slack config moved to channels.slack (auto-migrated on load).",
},
{
path: ["mattermost"],
message: "mattermost config moved to channels.mattermost (auto-migrated on load).",
},
{
path: ["signal"],
message: "signal config moved to channels.signal (auto-migrated on load).",

View File

@@ -1,6 +1,5 @@
import type { DiscordConfig } from "./types.discord.js";
import type { IMessageConfig } from "./types.imessage.js";
import type { MattermostConfig } from "./types.mattermost.js";
import type { MSTeamsConfig } from "./types.msteams.js";
import type { SignalConfig } from "./types.signal.js";
import type { SlackConfig } from "./types.slack.js";
@@ -18,7 +17,6 @@ export type ChannelsConfig = {
telegram?: TelegramConfig;
discord?: DiscordConfig;
slack?: SlackConfig;
mattermost?: MattermostConfig;
signal?: SignalConfig;
imessage?: IMessageConfig;
msteams?: MSTeamsConfig;

View File

@@ -24,7 +24,6 @@ export type HookMappingConfig = {
| "telegram"
| "discord"
| "slack"
| "mattermost"
| "signal"
| "imessage"
| "msteams";

View File

@@ -1,40 +0,0 @@
import type { BlockStreamingCoalesceConfig } from "./types.base.js";
export type MattermostChatMode = "oncall" | "onmessage" | "onchar";
export type MattermostAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
/** Allow channel-initiated config writes (default: true). */
configWrites?: boolean;
/** If false, do not start this Mattermost account. Default: true. */
enabled?: boolean;
/** Bot token for Mattermost. */
botToken?: string;
/** Base URL for the Mattermost server (e.g., https://chat.example.com). */
baseUrl?: string;
/**
* Controls when channel messages trigger replies.
* - "oncall": only respond when mentioned
* - "onmessage": respond to every channel message
* - "onchar": respond when a trigger character prefixes the message
*/
chatmode?: MattermostChatMode;
/** Prefix characters that trigger onchar mode (default: [">", "!"]). */
oncharPrefixes?: string[];
/** Require @mention to respond in channels. Default: true. */
requireMention?: boolean;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Disable block streaming for this account. */
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
};
export type MattermostConfig = {
/** Optional per-account Mattermost configuration (multi-account). */
accounts?: Record<string, MattermostAccountConfig>;
} & MattermostAccountConfig;

View File

@@ -13,7 +13,6 @@ export type QueueModeByProvider = {
telegram?: QueueMode;
discord?: QueueMode;
slack?: QueueMode;
mattermost?: QueueMode;
signal?: QueueMode;
imessage?: QueueMode;
msteams?: QueueMode;

View File

@@ -14,7 +14,6 @@ export * from "./types.hooks.js";
export * from "./types.imessage.js";
export * from "./types.messages.js";
export * from "./types.models.js";
export * from "./types.mattermost.js";
export * from "./types.msteams.js";
export * from "./types.plugins.js";
export * from "./types.queue.js";

View File

@@ -29,7 +29,6 @@ export const HeartbeatSchema = z
z.literal("telegram"),
z.literal("discord"),
z.literal("slack"),
z.literal("mattermost"),
z.literal("msteams"),
z.literal("signal"),
z.literal("imessage"),

View File

@@ -23,7 +23,6 @@ export const HookMappingSchema = z
z.literal("telegram"),
z.literal("discord"),
z.literal("slack"),
z.literal("mattermost"),
z.literal("signal"),
z.literal("imessage"),
z.literal("msteams"),

View File

@@ -367,27 +367,6 @@ export const SlackConfigSchema = SlackAccountSchema.extend({
}
});
export const MattermostAccountSchema = z
.object({
name: z.string().optional(),
capabilities: z.array(z.string()).optional(),
enabled: z.boolean().optional(),
configWrites: z.boolean().optional(),
botToken: z.string().optional(),
baseUrl: z.string().optional(),
chatmode: z.enum(["oncall", "onmessage", "onchar"]).optional(),
oncharPrefixes: z.array(z.string()).optional(),
requireMention: z.boolean().optional(),
textChunkLimit: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
})
.strict();
export const MattermostConfigSchema = MattermostAccountSchema.extend({
accounts: z.record(z.string(), MattermostAccountSchema.optional()).optional(),
});
export const SignalAccountSchemaBase = z
.object({
name: z.string().optional(),

View File

@@ -4,7 +4,6 @@ import {
BlueBubblesConfigSchema,
DiscordConfigSchema,
IMessageConfigSchema,
MattermostConfigSchema,
MSTeamsConfigSchema,
SignalConfigSchema,
SlackConfigSchema,
@@ -28,7 +27,6 @@ export const ChannelsSchema = z
telegram: TelegramConfigSchema.optional(),
discord: DiscordConfigSchema.optional(),
slack: SlackConfigSchema.optional(),
mattermost: MattermostConfigSchema.optional(),
signal: SignalConfigSchema.optional(),
imessage: IMessageConfigSchema.optional(),
bluebubbles: BlueBubblesConfigSchema.optional(),

View File

@@ -28,18 +28,11 @@ type SendMatrixMessage = (
opts?: { mediaUrl?: string; replyToId?: string; threadId?: string; timeoutMs?: number },
) => Promise<{ messageId: string; roomId: string }>;
type SendMattermostMessage = (
to: string,
text: string,
opts?: { accountId?: string; mediaUrl?: string; replyToId?: string },
) => Promise<{ messageId: string; channelId: string }>;
export type OutboundSendDeps = {
sendWhatsApp?: typeof sendMessageWhatsApp;
sendTelegram?: typeof sendMessageTelegram;
sendDiscord?: typeof sendMessageDiscord;
sendSlack?: typeof sendMessageSlack;
sendMattermost?: SendMattermostMessage;
sendSignal?: typeof sendMessageSignal;
sendIMessage?: typeof sendMessageIMessage;
sendMatrix?: SendMatrixMessage;

View File

@@ -22,7 +22,6 @@ const MARKDOWN_CAPABLE_CHANNELS = new Set<string>([
"telegram",
"signal",
"discord",
"mattermost",
"tui",
INTERNAL_MESSAGE_CHANNEL,
]);