Files
Dirigent/plugin/commands/add-guild-command.ts

137 lines
4.1 KiB
TypeScript

import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { execFileSync } from "node:child_process";
import path from "node:path";
import os from "node:os";
import fs from "node:fs";
function getSkillBaseDir(api: OpenClawPluginApi): string {
return (api.config as Record<string, unknown>)?.["dirigentStateDir"] as string || path.join(os.homedir(), ".openclaw");
}
function parseGuildTable(skillMdContent: string): Array<{ guildId: string; description: string }> {
const lines = skillMdContent.split("\n");
const rows: Array<{ guildId: string; description: string }> = [];
let inTable = false;
for (const line of lines) {
// Detect table header
if (line.includes("guild-id") && line.includes("description")) {
inTable = true;
continue;
}
// Skip separator line
if (inTable && /^\|[-\s|]+\|$/.test(line)) {
continue;
}
// Parse data rows
if (inTable) {
const match = line.match(/^\| \s*(\d+) \s*\| \s*(.+?) \s*\|$/);
if (match) {
rows.push({ guildId: match[1].trim(), description: match[2].trim() });
}
}
}
return rows;
}
export function registerAddGuildCommand(api: OpenClawPluginApi): void {
// Register add-guild command
api.registerCommand({
name: "add-guild",
description: "Add a Discord guild to the discord-guilds skill",
acceptsArgs: true,
handler: async (cmdCtx) => {
const args = (cmdCtx.args || "").trim();
if (!args) {
return {
text: "Usage: /add-guild <guild-id> <description>\nExample: /add-guild 123456789012345678 \"Production server\"",
isError: true,
};
}
const parts = args.split(/\s+/);
if (parts.length < 2) {
return {
text: "Error: Both guild-id and description are required.\nUsage: /add-guild <guild-id> <description>",
isError: true,
};
}
const guildId = parts[0];
const description = parts.slice(1).join(" ");
// Validate guild ID
if (!/^\d+$/.test(guildId)) {
return {
text: "Error: guild-id must be a numeric Discord snowflake (e.g., 123456789012345678)",
isError: true,
};
}
// Resolve the skill script path
const openClawDir = getSkillBaseDir(api);
const scriptPath = path.join(openClawDir, "skills", "discord-guilds", "scripts", "add-guild");
try {
const result = execFileSync(process.execPath, [scriptPath, guildId, description], {
encoding: "utf8",
timeout: 5000,
});
return { text: result.trim() };
} catch (e: any) {
const stderr = e?.stderr?.toString?.() || "";
const stdout = e?.stdout?.toString?.() || "";
return {
text: `Failed to add guild: ${stderr || stdout || String(e)}`,
isError: true,
};
}
},
});
// Register list-guilds command
api.registerCommand({
name: "list-guilds",
description: "List all Discord guilds in the discord-guilds skill",
acceptsArgs: false,
handler: async () => {
const openClawDir = getSkillBaseDir(api);
const skillMdPath = path.join(openClawDir, "skills", "discord-guilds", "SKILL.md");
if (!fs.existsSync(skillMdPath)) {
return {
text: "Error: discord-guilds skill not found. Run Dirigent install first.",
isError: true,
};
}
try {
const content = fs.readFileSync(skillMdPath, "utf8");
const guilds = parseGuildTable(content);
if (guilds.length === 0) {
return { text: "No guilds configured yet.\n\nUse /add-guild <guild-id> <description> to add one." };
}
const lines = [
`**Available Guilds (${guilds.length}):**`,
"",
"| guild-id | description |",
"|----------|-------------|",
...guilds.map(g => `| ${g.guildId} | ${g.description} |`),
"",
"Use /add-guild <guild-id> <description> to add more.",
];
return { text: lines.join("\n") };
} catch (e: any) {
return {
text: `Failed to read guild list: ${String(e)}`,
isError: true,
};
}
},
});
}