87 lines
3.2 KiB
JavaScript
87 lines
3.2 KiB
JavaScript
/**
|
|
* Chat monitoring — tracks unresponded messages in configured channels
|
|
* and alerts staff when thresholds are crossed.
|
|
*/
|
|
const { EmbedBuilder } = require('discord.js');
|
|
const { CONFIG } = require('../config');
|
|
const { setCooldown, isOnCooldown } = require('./patternStore');
|
|
|
|
// channelId → { lastStaffMessageAt, unrespondedCount, lastAlertAt }
|
|
const chatState = new Map();
|
|
|
|
function initChatMonitoring(client) {
|
|
for (const channelId of CONFIG.CHAT_ALERT_CHANNEL_IDS) {
|
|
chatState.set(channelId, {
|
|
lastStaffMessageAt: new Date(),
|
|
unrespondedCount: 0,
|
|
lastAlertAt: null
|
|
});
|
|
}
|
|
}
|
|
|
|
function isStaff(member) {
|
|
if (!member?.roles?.cache) return false;
|
|
if (CONFIG.ROLE_ID_TO_PING && member.roles.cache.has(CONFIG.ROLE_ID_TO_PING)) return true;
|
|
const additional = CONFIG.ADDITIONAL_STAFF_ROLES || [];
|
|
return additional.some(roleId => member.roles.cache.has(roleId));
|
|
}
|
|
|
|
async function handleChatMessage(msg, client) {
|
|
if (msg.author.bot) return;
|
|
if (!chatState.has(msg.channel.id)) return;
|
|
|
|
const state = chatState.get(msg.channel.id);
|
|
if (isStaff(msg.member)) {
|
|
state.lastStaffMessageAt = new Date();
|
|
state.unrespondedCount = 0;
|
|
} else {
|
|
state.unrespondedCount++;
|
|
}
|
|
}
|
|
|
|
async function runChatAlertChecks(client) {
|
|
const alertChannelId = CONFIG.ALL_STAFF_CHAT_ALERT_CHANNEL_ID;
|
|
if (!alertChannelId || !client) return;
|
|
|
|
for (const [channelId, state] of chatState) {
|
|
// Message count threshold
|
|
if (state.unrespondedCount >= CONFIG.CHAT_ALERT_MESSAGE_COUNT) {
|
|
const cooldownKey = `chat:messages:${channelId}`;
|
|
if (!isOnCooldown(cooldownKey, CONFIG.CHAT_ALERT_COOLDOWN_MINUTES)) {
|
|
setCooldown(cooldownKey);
|
|
const embed = new EmbedBuilder()
|
|
.setTitle('Chat needs attention')
|
|
.setDescription(`<#${channelId}> has ${state.unrespondedCount} unresponded messages.`)
|
|
.setColor(0xFF8800)
|
|
.setTimestamp();
|
|
try {
|
|
const alertChan = await client.channels.fetch(alertChannelId);
|
|
const content = CONFIG.SURGE_ROLE_ID ? `<@&${CONFIG.SURGE_ROLE_ID}>` : undefined;
|
|
if (alertChan) await alertChan.send({ content, embeds: [embed] });
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
|
|
// Time threshold
|
|
const hoursSinceStaff = (Date.now() - state.lastStaffMessageAt.getTime()) / 3600000;
|
|
if (hoursSinceStaff >= CONFIG.CHAT_ALERT_HOURS_WITHOUT_RESPONSE && state.unrespondedCount > 0) {
|
|
const cooldownKey = `chat:time:${channelId}`;
|
|
if (!isOnCooldown(cooldownKey, CONFIG.CHAT_ALERT_COOLDOWN_MINUTES)) {
|
|
setCooldown(cooldownKey);
|
|
const embed = new EmbedBuilder()
|
|
.setTitle('Chat without staff response')
|
|
.setDescription(`<#${channelId}> has had no staff response for ${Math.floor(hoursSinceStaff)} hour(s) with ${state.unrespondedCount} pending message(s).`)
|
|
.setColor(0xFF8800)
|
|
.setTimestamp();
|
|
try {
|
|
const alertChan = await client.channels.fetch(alertChannelId);
|
|
const content = CONFIG.SURGE_ROLE_ID ? `<@&${CONFIG.SURGE_ROLE_ID}>` : undefined;
|
|
if (alertChan) await alertChan.send({ content, embeds: [embed] });
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { initChatMonitoring, handleChatMessage, runChatAlertChecks };
|