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 {
|
private save(): void {
|
||||||
|
if (process.env.JEST_WORKER_ID) {
|
||||||
|
this.forceSave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Debounce saves to prevent excessive writes
|
// Debounce saves to prevent excessive writes
|
||||||
if (this.saveTimeout) {
|
if (this.saveTimeout) {
|
||||||
clearTimeout(this.saveTimeout);
|
clearTimeout(this.saveTimeout);
|
||||||
@@ -85,6 +90,11 @@ export class ConfigStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private forceSave(): void {
|
private forceSave(): void {
|
||||||
|
if (this.saveTimeout) {
|
||||||
|
clearTimeout(this.saveTimeout);
|
||||||
|
this.saveTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
||||||
this.logger.info('Saved configuration to storage');
|
this.logger.info('Saved configuration to storage');
|
||||||
@@ -218,6 +228,9 @@ export class ConfigStorage {
|
|||||||
|
|
||||||
public setChannelId(guildId: string, channelId: string): void {
|
public setChannelId(guildId: string, channelId: string): void {
|
||||||
const config = this.getGuildConfig(guildId);
|
const config = this.getGuildConfig(guildId);
|
||||||
|
if (config.channelId === channelId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
config.channelId = channelId;
|
config.channelId = channelId;
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
@@ -240,7 +253,10 @@ export class ConfigStorage {
|
|||||||
|
|
||||||
public setMessageIds(guildId: string, ids: string[]): void {
|
public setMessageIds(guildId: string, ids: string[]): void {
|
||||||
const config = this.getGuildConfig(guildId);
|
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();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -3,6 +3,7 @@ import { configStorage } from './config/storage';
|
|||||||
import { UptimeKumaService } from './services/uptime-kuma.service';
|
import { UptimeKumaService } from './services/uptime-kuma.service';
|
||||||
import { DiscordService } from './services/discord.service';
|
import { DiscordService } from './services/discord.service';
|
||||||
import { Logger } from './utils/logger';
|
import { Logger } from './utils/logger';
|
||||||
|
import { MonitorStats } from './types/uptime-kuma';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
|
|
||||||
class UptimeKumaDiscordBot {
|
class UptimeKumaDiscordBot {
|
||||||
@@ -68,7 +69,7 @@ class UptimeKumaDiscordBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setupEventListeners(): void {
|
private setupEventListeners(): void {
|
||||||
this.uptimeKuma.on('monitorsUpdated', (monitors: any) => {
|
this.uptimeKuma.on('monitorsUpdated', (monitors: MonitorStats[]) => {
|
||||||
this.discord.updateMonitorStatus(monitors).catch((error: Error) => {
|
this.discord.updateMonitorStatus(monitors).catch((error: Error) => {
|
||||||
this.logger.error(`Failed to update Discord status: ${error.message}`);
|
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 = config.bot.updateInterval;
|
||||||
const defaultInterval = parseInt(process.env.UPDATE_INTERVAL || '60', 10) * 1000;
|
|
||||||
this.updateInterval = setInterval(updateFn, defaultInterval);
|
this.updateInterval = setInterval(updateFn, defaultInterval);
|
||||||
|
|
||||||
this.logger.info(`Update interval set to ${defaultInterval / 1000} seconds`);
|
this.logger.info(`Update interval set to ${defaultInterval / 1000} seconds`);
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ export class DiscordService {
|
|||||||
private maxMonitorsPerEmbed = 20;
|
private maxMonitorsPerEmbed = 20;
|
||||||
private commandsService: CommandsService;
|
private commandsService: CommandsService;
|
||||||
private uptimeKumaService: UptimeKumaService | null = null;
|
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() {
|
constructor() {
|
||||||
this.client = new Client({
|
this.client = new Client({
|
||||||
@@ -167,22 +173,67 @@ export class DiscordService {
|
|||||||
this.channels.set(guildId, textChannel);
|
this.channels.set(guildId, textChannel);
|
||||||
configStorage.setMessageIds(guildId, []);
|
configStorage.setMessageIds(guildId, []);
|
||||||
configStorage.setChannelId(guildId, channelId);
|
configStorage.setChannelId(guildId, channelId);
|
||||||
|
this.lastUpdateTimes.delete(guildId);
|
||||||
this.logger.info(`Changed status channel for guild ${guildId} to: ${textChannel.name}`);
|
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) {
|
} catch (error: any) {
|
||||||
throw new Error(`Failed to set channel: ${error.message}`);
|
throw new Error(`Failed to set channel: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateMonitorStatus(monitors: MonitorStats[]): Promise<void> {
|
public async updateMonitorStatus(monitors: MonitorStats[], options: { force?: boolean } = {}): Promise<void> {
|
||||||
const guildIds = configStorage.getAllGuildIds();
|
this.latestMonitors = monitors;
|
||||||
|
if (options.force) {
|
||||||
if (guildIds.length === 0) {
|
this.forceNextUpdate = true;
|
||||||
this.logger.warn('No guilds configured, skipping update');
|
}
|
||||||
|
|
||||||
|
if (this.isUpdateInProgress) {
|
||||||
|
this.pendingUpdate = true;
|
||||||
return;
|
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) {
|
for (const guildId of guildIds) {
|
||||||
// Skip if guild doesn't actually exist in config (shouldn't happen but safety check)
|
|
||||||
if (!configStorage.guildExists(guildId)) {
|
if (!configStorage.guildExists(guildId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -192,6 +243,14 @@ export class DiscordService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const interval = configStorage.getUpdateInterval(guildId);
|
||||||
|
const lastUpdate = this.lastUpdateTimes.get(guildId) ?? 0;
|
||||||
|
const dueForUpdate = force || now - lastUpdate >= interval;
|
||||||
|
|
||||||
|
if (!dueForUpdate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const trackedIds = configStorage.getMonitorIds(guildId);
|
const trackedIds = configStorage.getMonitorIds(guildId);
|
||||||
const filteredMonitors = trackedIds.length === 0
|
const filteredMonitors = trackedIds.length === 0
|
||||||
@@ -202,14 +261,16 @@ export class DiscordService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const embeds = this.createEmbeds(guildId, filteredMonitors, monitors.length);
|
const embeds = this.createEmbeds(guildId, filteredMonitors, totalMonitors);
|
||||||
|
|
||||||
const messageIds = configStorage.getMessageIds(guildId);
|
const messageIds = configStorage.getMessageIds(guildId);
|
||||||
if (messageIds.length === 0) {
|
if (messageIds.length === 0) {
|
||||||
await this.createNewMessages(guildId, channel, embeds);
|
await this.createNewMessages(guildId, channel, embeds);
|
||||||
} else {
|
} else {
|
||||||
await this.updateExistingMessages(guildId, channel, embeds);
|
await this.updateExistingMessages(guildId, channel, embeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lastUpdateTimes.set(guildId, Date.now());
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Failed to update monitor status for guild ${guildId}: ${error.message}`);
|
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
|
// Don't create new messages here - let the main flow handle it
|
||||||
// Just clear the message IDs so next update will create new ones
|
// Just clear the message IDs so next update will create new ones
|
||||||
configStorage.setMessageIds(guildId, []);
|
configStorage.setMessageIds(guildId, []);
|
||||||
|
this.lastUpdateTimes.delete(guildId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user