mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
refactor(auto-reply): share slash set/unset command parsing
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseSetUnsetCommand, parseSetUnsetCommandAction } from "./commands-setunset.js";
|
||||
import {
|
||||
parseSetUnsetCommand,
|
||||
parseSetUnsetCommandAction,
|
||||
parseSlashCommandWithSetUnset,
|
||||
} from "./commands-setunset.js";
|
||||
|
||||
type ParsedSetUnsetAction =
|
||||
| { action: "set"; path: string; value: unknown }
|
||||
@@ -53,3 +57,60 @@ describe("parseSetUnsetCommandAction", () => {
|
||||
expect(result).toEqual({ action: "error", message: "Usage: /config set path=value" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseSlashCommandWithSetUnset", () => {
|
||||
it("returns null when the input does not match the slash command", () => {
|
||||
const result = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>({
|
||||
raw: "/debug show",
|
||||
slash: "/config",
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
usageMessage: "Usage: /config show|set|unset",
|
||||
onKnownAction: () => undefined,
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("prefers set/unset mapping and falls back to known actions", () => {
|
||||
const setResult = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>({
|
||||
raw: '/config set a.b={"ok":true}',
|
||||
slash: "/config",
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
usageMessage: "Usage: /config show|set|unset",
|
||||
onKnownAction: () => undefined,
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
expect(setResult).toEqual({ action: "set", path: "a.b", value: { ok: true } });
|
||||
|
||||
const showResult = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>({
|
||||
raw: "/config show",
|
||||
slash: "/config",
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
usageMessage: "Usage: /config show|set|unset",
|
||||
onKnownAction: (action) =>
|
||||
action === "show" ? { action: "unset", path: "dummy" } : undefined,
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
expect(showResult).toEqual({ action: "unset", path: "dummy" });
|
||||
});
|
||||
|
||||
it("returns onError for unknown actions", () => {
|
||||
const unknownAction = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>({
|
||||
raw: "/config whoami",
|
||||
slash: "/config",
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
usageMessage: "Usage: /config show|set|unset",
|
||||
onKnownAction: () => undefined,
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
expect(unknownAction).toEqual({ action: "error", message: "Usage: /config show|set|unset" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { parseSlashCommandOrNull } from "./commands-slash-parse.js";
|
||||
import { parseConfigValue } from "./config-value.js";
|
||||
|
||||
export type SetUnsetParseResult =
|
||||
@@ -60,3 +61,41 @@ export function parseSetUnsetCommandAction<T>(params: {
|
||||
? params.onSet(parsed.path, parsed.value)
|
||||
: params.onUnset(parsed.path);
|
||||
}
|
||||
|
||||
export function parseSlashCommandWithSetUnset<T>(params: {
|
||||
raw: string;
|
||||
slash: string;
|
||||
invalidMessage: string;
|
||||
usageMessage: string;
|
||||
onKnownAction: (action: string, args: string) => T | undefined;
|
||||
onSet: (path: string, value: unknown) => T;
|
||||
onUnset: (path: string) => T;
|
||||
onError: (message: string) => T;
|
||||
}): T | null {
|
||||
const parsed = parseSlashCommandOrNull(params.raw, params.slash, {
|
||||
invalidMessage: params.invalidMessage,
|
||||
});
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
if (!parsed.ok) {
|
||||
return params.onError(parsed.message);
|
||||
}
|
||||
const { action, args } = parsed;
|
||||
const setUnset = parseSetUnsetCommandAction<T>({
|
||||
slash: params.slash,
|
||||
action,
|
||||
args,
|
||||
onSet: params.onSet,
|
||||
onUnset: params.onUnset,
|
||||
onError: params.onError,
|
||||
});
|
||||
if (setUnset) {
|
||||
return setUnset;
|
||||
}
|
||||
const knownAction = params.onKnownAction(action, args);
|
||||
if (knownAction) {
|
||||
return knownAction;
|
||||
}
|
||||
return params.onError(params.usageMessage);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { parseSetUnsetCommandAction } from "./commands-setunset.js";
|
||||
import { parseSlashCommandOrNull } from "./commands-slash-parse.js";
|
||||
import { parseSlashCommandWithSetUnset } from "./commands-setunset.js";
|
||||
|
||||
export type ConfigCommand =
|
||||
| { action: "show"; path?: string }
|
||||
@@ -8,37 +7,19 @@ export type ConfigCommand =
|
||||
| { action: "error"; message: string };
|
||||
|
||||
export function parseConfigCommand(raw: string): ConfigCommand | null {
|
||||
const parsed = parseSlashCommandOrNull(raw, "/config", {
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
});
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
if (!parsed.ok) {
|
||||
return { action: "error", message: parsed.message };
|
||||
}
|
||||
const { action, args } = parsed;
|
||||
const setUnset = parseSetUnsetCommandAction<ConfigCommand>({
|
||||
return parseSlashCommandWithSetUnset<ConfigCommand>({
|
||||
raw,
|
||||
slash: "/config",
|
||||
action,
|
||||
args,
|
||||
invalidMessage: "Invalid /config syntax.",
|
||||
usageMessage: "Usage: /config show|set|unset",
|
||||
onKnownAction: (action, args) => {
|
||||
if (action === "show" || action === "get") {
|
||||
return { action: "show", path: args || undefined };
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
if (setUnset) {
|
||||
return setUnset;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "show":
|
||||
return { action: "show", path: args || undefined };
|
||||
case "get":
|
||||
return { action: "show", path: args || undefined };
|
||||
default:
|
||||
return {
|
||||
action: "error",
|
||||
message: "Usage: /config show|set|unset",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { parseSetUnsetCommandAction } from "./commands-setunset.js";
|
||||
import { parseSlashCommandOrNull } from "./commands-slash-parse.js";
|
||||
import { parseSlashCommandWithSetUnset } from "./commands-setunset.js";
|
||||
|
||||
export type DebugCommand =
|
||||
| { action: "show" }
|
||||
@@ -9,37 +8,22 @@ export type DebugCommand =
|
||||
| { action: "error"; message: string };
|
||||
|
||||
export function parseDebugCommand(raw: string): DebugCommand | null {
|
||||
const parsed = parseSlashCommandOrNull(raw, "/debug", {
|
||||
invalidMessage: "Invalid /debug syntax.",
|
||||
});
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
if (!parsed.ok) {
|
||||
return { action: "error", message: parsed.message };
|
||||
}
|
||||
const { action, args } = parsed;
|
||||
const setUnset = parseSetUnsetCommandAction<DebugCommand>({
|
||||
return parseSlashCommandWithSetUnset<DebugCommand>({
|
||||
raw,
|
||||
slash: "/debug",
|
||||
action,
|
||||
args,
|
||||
invalidMessage: "Invalid /debug syntax.",
|
||||
usageMessage: "Usage: /debug show|set|unset|reset",
|
||||
onKnownAction: (action) => {
|
||||
if (action === "show") {
|
||||
return { action: "show" };
|
||||
}
|
||||
if (action === "reset") {
|
||||
return { action: "reset" };
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onSet: (path, value) => ({ action: "set", path, value }),
|
||||
onUnset: (path) => ({ action: "unset", path }),
|
||||
onError: (message) => ({ action: "error", message }),
|
||||
});
|
||||
if (setUnset) {
|
||||
return setUnset;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "show":
|
||||
return { action: "show" };
|
||||
case "reset":
|
||||
return { action: "reset" };
|
||||
default:
|
||||
return {
|
||||
action: "error",
|
||||
message: "Usage: /debug show|set|unset|reset",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user