Fix Docker restart crash: respect autoStart flag and persist identity

Root cause: on Docker restart, loadSavedBots() unconditionally connected
ALL saved bots regardless of autoStart flag, causing a rapid
connect/disconnect loop. Additionally, bot identities were regenerated
on every restart, causing TS server conflicts with stale sessions.

Changes:
- loadSavedBots() now only auto-connects bots with autoStart=true
- startBot/stopBot persist autoStart state so restart behavior matches
  user intent
- Bot TS3 identity is persisted to database and reused across restarts
- Database schema migrated to include identity column
- TS3Client.connect() cleans up existing connection before reconnecting
- Stagger bot connections by 1s to avoid overwhelming the TS server

https://claude.ai/code/session_01L2kEV2M1QFWMCyPtLU5LgC
This commit is contained in:
Claude committed 2026-04-07 12:15:39 +00:00
1 parent b97f4a1d86
commit 2559701e55
4 files changed
+82 -15

No files matched your search

+4
View File
@@ -553,4 +553,8 @@ export class BotInstance extends EventEmitter {
isConnected(): boolean {
return this.connected;
}
getIdentityExport(): string | undefined {
return this.tsClient.getIdentityExport();
}
}
+42 -9
View File
@@ -131,12 +131,25 @@ export class BotManager {
const bot = this.bots.get(id);
if (!bot) throw new Error(`Bot ${id} not found`);
await bot.connect();
// Mark as autoStart so it reconnects on Docker restart, and persist identity
const saved = this.database.getBotInstances().find((i) => i.id === id);
if (saved) {
this.database.saveBotInstance({ ...saved, autoStart: true });
this.persistBotIdentity(saved, bot);
}
}
stopBot(id: string): void {
const bot = this.bots.get(id);
if (!bot) throw new Error(`Bot ${id} not found`);
bot.disconnect();
// Mark as not autoStart so it stays stopped on Docker restart
const saved = this.database.getBotInstances().find((i) => i.id === id);
if (saved) {
this.database.saveBotInstance({ ...saved, autoStart: false });
}
}
async loadSavedBots(): Promise<void> {
@@ -150,6 +163,7 @@ export class BotManager {
port: saved.serverPort,
queryPort: 10011,
nickname: saved.nickname,
identity: saved.identity || undefined,
defaultChannel: saved.defaultChannel || undefined,
channelPassword: saved.channelPassword || undefined,
},
@@ -163,18 +177,30 @@ export class BotManager {
this.bots.set(saved.id, bot);
// Auto-connect in background (non-blocking, won't affect other bots)
bot.connect().then(() => {
// Only auto-connect bots that have autoStart enabled
if (saved.autoStart) {
bot.connect().then(() => {
// Persist identity after successful connection for future restarts
this.persistBotIdentity(saved, bot);
this.logger.info(
{ botId: saved.id, name: saved.name },
"Auto-connected saved bot"
);
}).catch((err) => {
this.logger.error(
{ err, botId: saved.id, name: saved.name },
"Failed to auto-connect bot (start manually from Settings)"
);
});
// Stagger connections to avoid overwhelming the TS server
await new Promise((resolve) => setTimeout(resolve, 1000));
} else {
this.logger.info(
{ botId: saved.id, name: saved.name },
"Auto-connected saved bot"
"Loaded bot (autoStart disabled, not connecting)"
);
}).catch((err) => {
this.logger.error(
{ err, botId: saved.id, name: saved.name },
"Failed to auto-connect bot (start manually from Settings)"
);
});
}
}
this.logger.info(
@@ -183,6 +209,13 @@ export class BotManager {
);
}
private persistBotIdentity(saved: import("../data/database.js").BotInstance, bot: BotInstance): void {
const identity = bot.getIdentityExport();
if (identity && identity !== saved.identity) {
this.database.saveBotInstance({ ...saved, identity });
}
}
shutdown(): void {
for (const bot of this.bots.values()) {
bot.disconnect();