mirror of
https://github.com/BrenBroZAYT/uptime-kuma-discord-bot.git
synced 2026-06-13 16:40:03 +00:00
refactor: enhance Discord service update logic and improve configuration handling
This commit is contained in:
+17
-1
@@ -74,6 +74,11 @@ export class ConfigStorage {
|
||||
}
|
||||
|
||||
private save(): void {
|
||||
if (process.env.JEST_WORKER_ID) {
|
||||
this.forceSave();
|
||||
return;
|
||||
}
|
||||
|
||||
// Debounce saves to prevent excessive writes
|
||||
if (this.saveTimeout) {
|
||||
clearTimeout(this.saveTimeout);
|
||||
@@ -85,6 +90,11 @@ export class ConfigStorage {
|
||||
}
|
||||
|
||||
private forceSave(): void {
|
||||
if (this.saveTimeout) {
|
||||
clearTimeout(this.saveTimeout);
|
||||
this.saveTimeout = null;
|
||||
}
|
||||
|
||||
try {
|
||||
writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
||||
this.logger.info('Saved configuration to storage');
|
||||
@@ -218,6 +228,9 @@ export class ConfigStorage {
|
||||
|
||||
public setChannelId(guildId: string, channelId: string): void {
|
||||
const config = this.getGuildConfig(guildId);
|
||||
if (config.channelId === channelId) {
|
||||
return;
|
||||
}
|
||||
config.channelId = channelId;
|
||||
this.save();
|
||||
}
|
||||
@@ -240,7 +253,10 @@ export class ConfigStorage {
|
||||
|
||||
public setMessageIds(guildId: string, ids: string[]): void {
|
||||
const config = this.getGuildConfig(guildId);
|
||||
config.messageIds = ids;
|
||||
if (config.messageIds.length === ids.length && config.messageIds.every((id, index) => id === ids[index])) {
|
||||
return;
|
||||
}
|
||||
config.messageIds = [...ids];
|
||||
this.save();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -3,6 +3,7 @@ import { configStorage } from './config/storage';
|
||||
import { UptimeKumaService } from './services/uptime-kuma.service';
|
||||
import { DiscordService } from './services/discord.service';
|
||||
import { Logger } from './utils/logger';
|
||||
import { MonitorStats } from './types/uptime-kuma';
|
||||
import * as http from 'http';
|
||||
|
||||
class UptimeKumaDiscordBot {
|
||||
@@ -68,7 +69,7 @@ class UptimeKumaDiscordBot {
|
||||
}
|
||||
|
||||
private setupEventListeners(): void {
|
||||
this.uptimeKuma.on('monitorsUpdated', (monitors: any) => {
|
||||
this.uptimeKuma.on('monitorsUpdated', (monitors: MonitorStats[]) => {
|
||||
this.discord.updateMonitorStatus(monitors).catch((error: Error) => {
|
||||
this.logger.error(`Failed to update Discord status: ${error.message}`);
|
||||
});
|
||||
@@ -120,8 +121,7 @@ class UptimeKumaDiscordBot {
|
||||
});
|
||||
};
|
||||
|
||||
// Use a default interval; guilds can have different intervals but we'll use a common update cycle
|
||||
const defaultInterval = parseInt(process.env.UPDATE_INTERVAL || '60', 10) * 1000;
|
||||
const defaultInterval = config.bot.updateInterval;
|
||||
this.updateInterval = setInterval(updateFn, defaultInterval);
|
||||
|
||||
this.logger.info(`Update interval set to ${defaultInterval / 1000} seconds`);
|
||||
|
||||
@@ -13,6 +13,12 @@ export class DiscordService {
|
||||
private maxMonitorsPerEmbed = 20;
|
||||
private commandsService: CommandsService;
|
||||
private uptimeKumaService: UptimeKumaService | null = null;
|
||||
private latestMonitors: MonitorStats[] = [];
|
||||
private isUpdateInProgress = false;
|
||||
private pendingUpdate = false;
|
||||
private forceNextUpdate = false;
|
||||
private lastUpdateTimes: Map<string, number> = new Map();
|
||||
private hasLoggedNoGuilds = false;
|
||||
|
||||
constructor() {
|
||||
this.client = new Client({
|
||||
@@ -167,22 +173,67 @@ export class DiscordService {
|
||||
this.channels.set(guildId, textChannel);
|
||||
configStorage.setMessageIds(guildId, []);
|
||||
configStorage.setChannelId(guildId, channelId);
|
||||
this.lastUpdateTimes.delete(guildId);
|
||||
this.logger.info(`Changed status channel for guild ${guildId} to: ${textChannel.name}`);
|
||||
|
||||
if (this.uptimeKumaService) {
|
||||
const monitors = this.uptimeKumaService.getMonitorStats();
|
||||
await this.updateMonitorStatus(monitors, { force: true }).catch((error: any) => {
|
||||
this.logger.error(`Failed to run immediate update for guild ${guildId}: ${error.message}`);
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to set channel: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateMonitorStatus(monitors: MonitorStats[]): Promise<void> {
|
||||
const guildIds = configStorage.getAllGuildIds();
|
||||
public async updateMonitorStatus(monitors: MonitorStats[], options: { force?: boolean } = {}): Promise<void> {
|
||||
this.latestMonitors = monitors;
|
||||
if (options.force) {
|
||||
this.forceNextUpdate = true;
|
||||
}
|
||||
|
||||
if (guildIds.length === 0) {
|
||||
this.logger.warn('No guilds configured, skipping update');
|
||||
if (this.isUpdateInProgress) {
|
||||
this.pendingUpdate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.processPendingUpdates();
|
||||
}
|
||||
|
||||
private async processPendingUpdates(): Promise<void> {
|
||||
this.isUpdateInProgress = true;
|
||||
|
||||
try {
|
||||
do {
|
||||
this.pendingUpdate = false;
|
||||
const monitorsSnapshot = [...this.latestMonitors];
|
||||
const force = this.forceNextUpdate;
|
||||
this.forceNextUpdate = false;
|
||||
|
||||
await this.performUpdate(monitorsSnapshot, force);
|
||||
} while (this.pendingUpdate);
|
||||
} finally {
|
||||
this.isUpdateInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async performUpdate(monitors: MonitorStats[], force: boolean): Promise<void> {
|
||||
const guildIds = configStorage.getAllGuildIds();
|
||||
|
||||
if (guildIds.length === 0) {
|
||||
if (!this.hasLoggedNoGuilds) {
|
||||
this.logger.warn('No guilds configured, skipping update');
|
||||
this.hasLoggedNoGuilds = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasLoggedNoGuilds = false;
|
||||
const totalMonitors = monitors.length;
|
||||
const now = Date.now();
|
||||
|
||||
for (const guildId of guildIds) {
|
||||
// Skip if guild doesn't actually exist in config (shouldn't happen but safety check)
|
||||
if (!configStorage.guildExists(guildId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -192,6 +243,14 @@ export class DiscordService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const interval = configStorage.getUpdateInterval(guildId);
|
||||
const lastUpdate = this.lastUpdateTimes.get(guildId) ?? 0;
|
||||
const dueForUpdate = force || now - lastUpdate >= interval;
|
||||
|
||||
if (!dueForUpdate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const trackedIds = configStorage.getMonitorIds(guildId);
|
||||
const filteredMonitors = trackedIds.length === 0
|
||||
@@ -202,7 +261,7 @@ export class DiscordService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const embeds = this.createEmbeds(guildId, filteredMonitors, monitors.length);
|
||||
const embeds = this.createEmbeds(guildId, filteredMonitors, totalMonitors);
|
||||
|
||||
const messageIds = configStorage.getMessageIds(guildId);
|
||||
if (messageIds.length === 0) {
|
||||
@@ -210,6 +269,8 @@ export class DiscordService {
|
||||
} else {
|
||||
await this.updateExistingMessages(guildId, channel, embeds);
|
||||
}
|
||||
|
||||
this.lastUpdateTimes.set(guildId, Date.now());
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Failed to update monitor status for guild ${guildId}: ${error.message}`);
|
||||
}
|
||||
@@ -365,6 +426,7 @@ export class DiscordService {
|
||||
// Don't create new messages here - let the main flow handle it
|
||||
// Just clear the message IDs so next update will create new ones
|
||||
configStorage.setMessageIds(guildId, []);
|
||||
this.lastUpdateTimes.delete(guildId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user