Replace all console.* calls in src/ with pino child-loggers. Add rootLogger + childLogger(component) in src/runtime/logger.ts. pino-pretty used as dev transport only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
154 lines
4.5 KiB
TypeScript
154 lines
4.5 KiB
TypeScript
import {
|
|
Client,
|
|
GatewayIntentBits,
|
|
ChannelType,
|
|
REST,
|
|
Routes,
|
|
TextChannel,
|
|
Interaction,
|
|
} from "discord.js";
|
|
import { DisclawDatabase } from "./db/database";
|
|
import { DisclawConfig, EnvConfig } from "./config/loader";
|
|
import { newAgentCommand, handleNewAgent } from "./commands/new-agent";
|
|
import { routeMessage } from "./router";
|
|
import { childLogger } from "./runtime/logger";
|
|
|
|
const log = childLogger("bot");
|
|
|
|
export async function startBot(
|
|
envConfig: EnvConfig,
|
|
disclawConfig: DisclawConfig,
|
|
db: DisclawDatabase
|
|
): Promise<Client> {
|
|
const client = new Client({
|
|
intents: [
|
|
GatewayIntentBits.Guilds,
|
|
GatewayIntentBits.GuildMessages,
|
|
GatewayIntentBits.MessageContent,
|
|
],
|
|
});
|
|
|
|
// Track the management channel id once resolved
|
|
let managementChannelId: string | null = null;
|
|
|
|
// --- Event: Ready ---
|
|
client.once("ready", async () => {
|
|
if (!client.user) return;
|
|
log.info({ tag: client.user.tag }, "Bot online");
|
|
|
|
const guild = envConfig.DISCORD_GUILD_ID
|
|
? client.guilds.cache.get(envConfig.DISCORD_GUILD_ID)
|
|
: client.guilds.cache.first();
|
|
|
|
if (!guild) {
|
|
log.error("No guild found. Is the bot added to a server?");
|
|
process.exit(1);
|
|
}
|
|
|
|
log.info({ guildName: guild.name, guildId: guild.id }, "Connected to guild");
|
|
|
|
// --- Ensure management channel exists ---
|
|
const managementName = disclawConfig.management_channel;
|
|
let existingWorkspace = db.getWorkspaceByName(managementName);
|
|
|
|
if (existingWorkspace) {
|
|
// Verify the channel still exists on Discord
|
|
const existingChannel = guild.channels.cache.get(
|
|
existingWorkspace.channel_id
|
|
);
|
|
if (existingChannel) {
|
|
managementChannelId = existingWorkspace.channel_id;
|
|
log.info({ channelName: managementName }, "Management channel found");
|
|
} else {
|
|
// Channel was deleted from Discord, recreate it
|
|
existingWorkspace = undefined;
|
|
}
|
|
}
|
|
|
|
if (!existingWorkspace) {
|
|
// Look for an existing channel with that name first
|
|
let mgmtChannel: TextChannel;
|
|
|
|
const found = guild.channels.cache.find(
|
|
(ch) =>
|
|
ch.type === ChannelType.GuildText && ch.name === managementName
|
|
) as TextChannel | undefined;
|
|
|
|
if (found) {
|
|
db.createWorkspace(
|
|
found.id,
|
|
guild.id,
|
|
managementName,
|
|
"__management__"
|
|
);
|
|
managementChannelId = found.id;
|
|
mgmtChannel = found;
|
|
log.info({ channelName: managementName }, "Adopted existing channel");
|
|
} else {
|
|
const newChannel = await guild.channels.create({
|
|
name: managementName,
|
|
type: ChannelType.GuildText,
|
|
topic: "DisClaw Management -- verwende /new-agent um Agenten zu erstellen",
|
|
});
|
|
db.createWorkspace(
|
|
newChannel.id,
|
|
guild.id,
|
|
managementName,
|
|
"__management__"
|
|
);
|
|
managementChannelId = newChannel.id;
|
|
mgmtChannel = newChannel;
|
|
log.info({ channelName: managementName }, "Created management channel");
|
|
}
|
|
|
|
// Send welcome message using the direct channel reference
|
|
await mgmtChannel
|
|
.send(
|
|
"DisClaw ist bereit. Verwende `/new-agent` um einen neuen Agenten zu erstellen."
|
|
)
|
|
.catch(() => {});
|
|
}
|
|
|
|
// --- Register slash commands ---
|
|
const rest = new REST({ version: "10" }).setToken(
|
|
envConfig.DISCORD_BOT_TOKEN
|
|
);
|
|
|
|
try {
|
|
await rest.put(
|
|
Routes.applicationGuildCommands(client.user.id, guild.id),
|
|
{
|
|
body: [newAgentCommand.toJSON()],
|
|
}
|
|
);
|
|
log.info("Slash commands registered.");
|
|
} catch (err) {
|
|
log.error({ err }, "Failed to register slash commands");
|
|
}
|
|
});
|
|
|
|
// --- Event: interactionCreate (slash commands) ---
|
|
client.on("interactionCreate", async (interaction: Interaction) => {
|
|
if (!interaction.isChatInputCommand()) return;
|
|
|
|
if (interaction.commandName === "new-agent") {
|
|
await handleNewAgent(interaction, db, disclawConfig);
|
|
}
|
|
});
|
|
|
|
// --- Event: messageCreate (agent routing) ---
|
|
client.on("messageCreate", async (message) => {
|
|
// Ignore bot messages
|
|
if (message.author.bot) return;
|
|
|
|
// Only handle text channels in guilds
|
|
if (!message.guild || message.channel.type !== ChannelType.GuildText) return;
|
|
|
|
await routeMessage(message, db, disclawConfig, managementChannelId);
|
|
});
|
|
|
|
// --- Login ---
|
|
await client.login(envConfig.DISCORD_BOT_TOKEN);
|
|
|
|
return client;
|
|
}
|