Slack: accept slash command names with or without leading slash
Closes #798
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
- Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm)
|
- Memory: allow custom OpenAI-compatible embedding endpoints for memory search (remote baseUrl/apiKey/headers). (#819 — thanks @mukhtharcm)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- Slack: accept slash commands with or without leading `/` for custom command configs. (#798 — thanks @thewilloftheshadow)
|
||||||
- Onboarding/Configure: refuse to proceed with invalid configs; run `clawdbot doctor` first to avoid wiping custom fields. (#764 — thanks @mukhtharcm)
|
- Onboarding/Configure: refuse to proceed with invalid configs; run `clawdbot doctor` first to avoid wiping custom fields. (#764 — thanks @mukhtharcm)
|
||||||
- Anthropic: merge consecutive user turns (preserve newest metadata) before validation to avoid “Incorrect role information” errors. (#804 — thanks @ThomsenDrake)
|
- Anthropic: merge consecutive user turns (preserve newest metadata) before validation to avoid “Incorrect role information” errors. (#804 — thanks @ThomsenDrake)
|
||||||
- Discord/Slack: centralize reply-thread planning so auto-thread replies stay in the created thread without parent reply refs.
|
- Discord/Slack: centralize reply-thread planning so auto-thread replies stay in the created thread without parent reply refs.
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { isSlackRoomAllowedByPolicy, resolveSlackThreadTs } from "./monitor.js";
|
import {
|
||||||
|
buildSlackSlashCommandMatcher,
|
||||||
|
isSlackRoomAllowedByPolicy,
|
||||||
|
resolveSlackThreadTs,
|
||||||
|
} from "./monitor.js";
|
||||||
|
|
||||||
describe("slack groupPolicy gating", () => {
|
describe("slack groupPolicy gating", () => {
|
||||||
it("allows when policy is open", () => {
|
it("allows when policy is open", () => {
|
||||||
@@ -152,3 +156,19 @@ describe("resolveSlackThreadTs", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("buildSlackSlashCommandMatcher", () => {
|
||||||
|
it("matches with or without a leading slash", () => {
|
||||||
|
const matcher = buildSlackSlashCommandMatcher("clawd");
|
||||||
|
|
||||||
|
expect(matcher.test("clawd")).toBe(true);
|
||||||
|
expect(matcher.test("/clawd")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not match similar names", () => {
|
||||||
|
const matcher = buildSlackSlashCommandMatcher("clawd");
|
||||||
|
|
||||||
|
expect(matcher.test("/clawd-bot")).toBe(false);
|
||||||
|
expect(matcher.test("clawd-bot")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -173,6 +173,15 @@ function normalizeSlackSlug(raw?: string) {
|
|||||||
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeSlackSlashCommandName(raw: string) {
|
||||||
|
return raw.replace(/^\/+/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSlackSlashCommandMatcher(name: string) {
|
||||||
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
return new RegExp(`^/?${escaped}$`);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeAllowList(list?: Array<string | number>) {
|
function normalizeAllowList(list?: Array<string | number>) {
|
||||||
return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
||||||
}
|
}
|
||||||
@@ -227,9 +236,13 @@ function resolveSlackUserAllowed(params: {
|
|||||||
function resolveSlackSlashCommandConfig(
|
function resolveSlackSlashCommandConfig(
|
||||||
raw?: SlackSlashCommandConfig,
|
raw?: SlackSlashCommandConfig,
|
||||||
): Required<SlackSlashCommandConfig> {
|
): Required<SlackSlashCommandConfig> {
|
||||||
|
const normalizedName = normalizeSlackSlashCommandName(
|
||||||
|
raw?.name?.trim() || "clawd",
|
||||||
|
);
|
||||||
|
const name = normalizedName || "clawd";
|
||||||
return {
|
return {
|
||||||
enabled: raw?.enabled === true,
|
enabled: raw?.enabled === true,
|
||||||
name: raw?.name?.trim() || "clawd",
|
name,
|
||||||
sessionPrefix: raw?.sessionPrefix?.trim() || "slack:slash",
|
sessionPrefix: raw?.sessionPrefix?.trim() || "slack:slash",
|
||||||
ephemeral: raw?.ephemeral !== false,
|
ephemeral: raw?.ephemeral !== false,
|
||||||
};
|
};
|
||||||
@@ -1980,7 +1993,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
}
|
}
|
||||||
} else if (slashCommand.enabled) {
|
} else if (slashCommand.enabled) {
|
||||||
app.command(
|
app.command(
|
||||||
slashCommand.name,
|
buildSlackSlashCommandMatcher(slashCommand.name),
|
||||||
async ({ command, ack, respond }: SlackCommandMiddlewareArgs) => {
|
async ({ command, ack, respond }: SlackCommandMiddlewareArgs) => {
|
||||||
await handleSlashCommand({
|
await handleSlashCommand({
|
||||||
command,
|
command,
|
||||||
|
|||||||
Reference in New Issue
Block a user