disclaw/src/lifecycle/channel-delete-handler.ts
Nick Tabeling 56b861f34c
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
feat(DIS-151): channel categorization + lifecycle management
- Add deleteWorkspaceByChannelId() to DisclawDatabase
- Add src/lifecycle/categories.ts: ensureCategories() + moveToCategory()
- Add src/lifecycle/reconcile.ts: reconcile() assigns channels to active/inactive/archive categories on startup
- Add src/lifecycle/channel-delete-handler.ts: handles channelDelete events for management, category, and agent channels
- Wire lifecycle into bot.ts: state object, ensureCategories, reconcile, channelDelete listener
- Extend handleNewAgent() with optional activeCategoryId to place new channels in the active category
- Add 13 unit tests covering all lifecycle scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 14:43:32 +02:00

83 lines
2.9 KiB
TypeScript

import { Channel, ChannelType, Guild } from "discord.js";
import * as fs from "node:fs";
import { DisclawDatabase } from "../db/database.js";
import { DisclawConfig } from "../config/loader.js";
import { ResolvedCategories, ensureCategories } from "./categories.js";
import { childLogger } from "../runtime/logger.js";
const log = childLogger("lifecycle:channel-delete");
export interface ChannelDeleteContext {
db: DisclawDatabase;
config: DisclawConfig;
guild: Guild;
categories: ResolvedCategories;
managementChannelId: string;
setManagementChannelId: (id: string) => void;
setCategories: (cats: ResolvedCategories) => void;
}
export async function handleChannelDelete(
channel: Channel,
ctx: ChannelDeleteContext
): Promise<void> {
const channelId = channel.id;
// 1. Was the management channel deleted?
if (channelId === ctx.managementChannelId) {
log.warn({ channelId }, "Management channel was deleted — recreating");
try {
const newChannel = await ctx.guild.channels.create({
name: ctx.config.management_channel,
type: ChannelType.GuildText,
topic: "DisClaw Management -- verwende /new-agent um Agenten zu erstellen",
});
ctx.setManagementChannelId(newChannel.id);
// Update DB: remove old entry, add new one
ctx.db.deleteWorkspaceByChannelId(channelId);
ctx.db.createWorkspace(newChannel.id, ctx.guild.id, ctx.config.management_channel, "__management__");
log.info({ newChannelId: newChannel.id }, "Management channel recreated");
} catch (err) {
log.error({ err }, "Failed to recreate management channel");
}
return;
}
// 2. Was one of the lifecycle category channels deleted?
const isCategoryChannel =
channelId === ctx.categories.active.id ||
channelId === ctx.categories.inactive.id ||
channelId === ctx.categories.archive.id;
if (isCategoryChannel) {
log.warn({ channelId }, "Lifecycle category channel was deleted — re-ensuring categories");
try {
const newCats = await ensureCategories(ctx.guild);
ctx.setCategories(newCats);
log.info("Categories re-ensured");
} catch (err) {
log.error({ err }, "Failed to re-ensure categories");
}
return;
}
// 3. Was an agent channel deleted?
const workspace = ctx.db.getWorkspaceByChannelId(channelId);
if (workspace) {
const workspacePath = workspace.workspace_path;
// Delete workspace directory before removing from DB
if (workspacePath && workspacePath !== "__management__") {
try {
fs.rmSync(workspacePath, { recursive: true, force: true });
log.info({ workspacePath }, "Agent workspace directory deleted");
} catch (err) {
log.warn({ workspacePath, err }, "Failed to delete agent workspace directory");
}
}
// Remove from DB
ctx.db.deleteWorkspaceByChannelId(channelId);
log.info({ channelId, agentName: workspace.agent_name }, "Agent channel and workspace removed from DB");
}
}