strip: remove pattern/surge/chat alert monitoring + unused commands
- delete services/{patternChecker,patternStore,surgeChecker,chatAlertChecker,staffNotifications,staffChannel,notificationRegistry,notificationEnabled,staffPresence}.js
- remove /notification, /staffnotification, /tag, /priority
- /escalate: drop action param, always unclaim
- purge PATTERN_*, SURGE_*, CHAT_ALERT_*, STAFF_* env vars from config + .env.example
- drop StaffNotification model
- ~2500 LOC removed
- settings-site /internal/notifications/* endpoints gone (UI will 404 until trimmed)
This commit is contained in:
@@ -11,9 +11,9 @@ const {
|
||||
PermissionFlagsBits
|
||||
} = require('discord.js');
|
||||
const { mongoose } = require('../db-connection');
|
||||
const { CONFIG, TICKET_TAGS } = require('../config');
|
||||
const { getPriorityEmoji, getPriorityColor, replaceVariables, escapeRegex } = require('../utils');
|
||||
const { makeTicketName, resolveCreatorNickname, getSenderLocal, toDiscordSafeName, getOrCreateTicketCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit } = require('../services/tickets');
|
||||
const { CONFIG } = require('../config');
|
||||
const { getPriorityEmoji, replaceVariables, escapeRegex } = require('../utils');
|
||||
const { makeTicketName, resolveCreatorNickname, getOrCreateTicketCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit } = require('../services/tickets');
|
||||
const { sendTicketNotificationEmail } = require('../services/gmail');
|
||||
const { getTicketActionRow } = require('../utils/ticketComponents');
|
||||
const { getEmailRouting } = require('../services/guildSettings');
|
||||
@@ -24,12 +24,10 @@ const { logTicketEvent, logSecurity, logError } = require('../services/debugLog'
|
||||
const { handleAccountInfoCommand } = require('./accountinfo');
|
||||
const { handleSetupCommand } = require('./setup');
|
||||
const { pendingCloses } = require('./pendingCloses');
|
||||
const { increment } = require('../services/patternStore');
|
||||
|
||||
const Ticket = mongoose.model('Ticket');
|
||||
const Tag = mongoose.model('Tag');
|
||||
const User = mongoose.model('User');
|
||||
const StaffNotification = mongoose.model('StaffNotification');
|
||||
|
||||
/**
|
||||
* True if member has the support role (ROLE_ID_TO_PING) or any ADDITIONAL_STAFF_ROLES.
|
||||
@@ -74,17 +72,11 @@ async function runEscalation(interaction, ticket, nextTier, reason) {
|
||||
// Clear claim on escalation
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { escalated: true, escalationTier: nextTier, claimedBy: null, claimerId: null, unclaimedRemindersSent: [] } }
|
||||
{ $set: { escalated: true, escalationTier: nextTier, claimedBy: null, claimerId: null } }
|
||||
);
|
||||
ticket.escalated = true;
|
||||
ticket.escalationTier = nextTier;
|
||||
ticket.claimedBy = null;
|
||||
increment('escalations', ticket.game || 'unknown', 'today');
|
||||
increment('escalations', ticket.game || 'unknown', 'week');
|
||||
increment('user_escalations', ticket.senderEmail, 'week');
|
||||
increment('staff_escalations', interaction.user.id, 'today');
|
||||
increment('staff_escalations', interaction.user.id, 'week');
|
||||
if (ticket.game) increment(`staff_game_escalations:${interaction.user.id}`, ticket.game, 'week');
|
||||
|
||||
const creatorNickname = await resolveCreatorNickname(interaction.guild, ticket);
|
||||
const newName = makeTicketName('escalated', ticket, creatorNickname);
|
||||
@@ -265,12 +257,11 @@ async function handleCommand(interaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
// /escalate (tier 2 or 3 via level; works for both email and Discord)
|
||||
// /escalate (tier 2 or 3 via level; works for both email and Discord). Always unclaims on escalate.
|
||||
if (interaction.commandName === 'escalate') {
|
||||
const reason = null;
|
||||
const level = interaction.options.getString('level');
|
||||
const nextTier = level === '3' ? 2 : 1;
|
||||
const action = interaction.options.getString('action');
|
||||
|
||||
const ticket = await Ticket.findOne({ discordThreadId: interaction.channel.id }).lean();
|
||||
if (!ticket) {
|
||||
@@ -301,12 +292,6 @@ async function handleCommand(interaction) {
|
||||
try {
|
||||
await interaction.deferReply();
|
||||
await runEscalation(interaction, ticket, nextTier, reason);
|
||||
if (action === 'unclaim') {
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { claimedBy: null, claimerId: null } }
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Escalate error:', err);
|
||||
await interaction.editReply({ content: 'Failed to escalate this ticket.' }).catch(() =>
|
||||
@@ -315,83 +300,6 @@ async function handleCommand(interaction) {
|
||||
}
|
||||
}
|
||||
|
||||
// /notification set | /notification add
|
||||
if (interaction.commandName === 'notification') {
|
||||
const sub = interaction.options.getSubcommand();
|
||||
if (sub === 'set') {
|
||||
const hours = interaction.options.getInteger('hours');
|
||||
try {
|
||||
await StaffNotification.findOneAndUpdate(
|
||||
{ userId: interaction.user.id },
|
||||
{ $set: { cooldownHours: hours, updatedAt: new Date() } },
|
||||
{ upsert: true }
|
||||
);
|
||||
return interaction.reply({ content: `Notification cooldown set to ${hours} hour(s).`, ephemeral: true });
|
||||
} catch (err) {
|
||||
console.error('notification set error:', err);
|
||||
return interaction.reply({ content: 'Failed to update notification setting.', ephemeral: true }).catch(() => {});
|
||||
}
|
||||
}
|
||||
if (sub === 'add') {
|
||||
if (!CONFIG.STAFF_NOTIFICATION_CATEGORY_ID) {
|
||||
return interaction.reply({ content: 'STAFF_NOTIFICATION_CATEGORY_ID is not configured.', ephemeral: true });
|
||||
}
|
||||
const member = interaction.options.getMember('member');
|
||||
if (!member) {
|
||||
return interaction.reply({ content: 'Could not resolve that member.', ephemeral: true });
|
||||
}
|
||||
const displayName = member.displayName;
|
||||
const emoji = CONFIG.STAFF_EMOJIS.get(member.id) || '';
|
||||
const chanName = toDiscordSafeName(`${displayName}${emoji}`);
|
||||
try {
|
||||
const newChannel = await interaction.guild.channels.create({
|
||||
name: chanName,
|
||||
type: ChannelType.GuildText,
|
||||
parent: CONFIG.STAFF_NOTIFICATION_CATEGORY_ID,
|
||||
permissionOverwrites: [
|
||||
{ id: interaction.guild.id, deny: [PermissionFlagsBits.ViewChannel] },
|
||||
{ id: member.id, allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.ReadMessageHistory] },
|
||||
...(CONFIG.ROLE_ID_TO_PING ? [{ id: CONFIG.ROLE_ID_TO_PING, allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.SendMessages, PermissionFlagsBits.ReadMessageHistory] }] : [])
|
||||
]
|
||||
});
|
||||
await StaffNotification.findOneAndUpdate(
|
||||
{ userId: member.id },
|
||||
{ $set: { channelId: newChannel.id, guildId: interaction.guild.id, updatedAt: new Date() } },
|
||||
{ upsert: true }
|
||||
);
|
||||
return interaction.reply({ content: `Notification channel created: ${newChannel}`, ephemeral: true });
|
||||
} catch (err) {
|
||||
console.error('notification add error:', err);
|
||||
return interaction.reply({ content: 'Failed to create notification channel.', ephemeral: true }).catch(() => {});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// /staffnotification (admin only)
|
||||
if (interaction.commandName === 'staffnotification') {
|
||||
if (interaction.user.id !== CONFIG.ADMIN_ID) {
|
||||
logSecurity('Unauthorized command attempt', interaction.user, interaction.commandName).catch(() => {});
|
||||
return interaction.reply({ content: 'This command is restricted to the bot admin.', ephemeral: true });
|
||||
}
|
||||
const member = interaction.options.getMember('member');
|
||||
const hours = interaction.options.getInteger('hours');
|
||||
if (!member) {
|
||||
return interaction.reply({ content: 'Could not resolve that member.', ephemeral: true });
|
||||
}
|
||||
try {
|
||||
await StaffNotification.findOneAndUpdate(
|
||||
{ userId: member.id },
|
||||
{ $set: { cooldownHours: hours, updatedAt: new Date() } },
|
||||
{ upsert: true }
|
||||
);
|
||||
return interaction.reply({ content: `Notification cooldown for ${member.displayName} set to ${hours} hour(s).`, ephemeral: true });
|
||||
} catch (err) {
|
||||
console.error('staffnotification error:', err);
|
||||
return interaction.reply({ content: 'Failed to update notification setting.', ephemeral: true }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.commandName === 'notifydm') {
|
||||
try {
|
||||
const setting = interaction.options.getString('setting') === 'on';
|
||||
@@ -723,32 +631,6 @@ async function handleCommand(interaction) {
|
||||
}
|
||||
}
|
||||
|
||||
// /tag – ticket category dropdown only
|
||||
if (interaction.commandName === 'tag') {
|
||||
trackInteraction('commands', 'tag', interaction.user.tag);
|
||||
const categoryValue = interaction.options.getString('category');
|
||||
const ticket = await Ticket.findOne({ discordThreadId: interaction.channel.id }).lean();
|
||||
if (!ticket) {
|
||||
return interaction.reply({ content: 'This channel is not linked to a ticket.', ephemeral: true });
|
||||
}
|
||||
try {
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { ticketTag: categoryValue } }
|
||||
);
|
||||
const tagEntry = (TICKET_TAGS || []).find(t => t.value === categoryValue);
|
||||
const emoji = tagEntry ? tagEntry.emoji : '';
|
||||
const channelMessage = `Your ticket has been categorized as ${emoji} **${tagEntry ? tagEntry.name : categoryValue}** ${emoji}.`;
|
||||
await interaction.reply(channelMessage);
|
||||
increment('tag_usage', categoryValue, 'today');
|
||||
increment('tag_usage', categoryValue, 'week');
|
||||
if (ticket.game) increment(`tag_game:${categoryValue}`, ticket.game, 'week');
|
||||
} catch (err) {
|
||||
trackError('tag-command', err, interaction);
|
||||
await interaction.reply({ content: 'Failed to set ticket category.', ephemeral: true });
|
||||
}
|
||||
}
|
||||
|
||||
// /response – saved response tags (send, create, edit, delete, list)
|
||||
if (interaction.commandName === 'response') {
|
||||
trackInteraction('commands', 'response', interaction.user.tag);
|
||||
@@ -936,14 +818,14 @@ async function handleCommand(interaction) {
|
||||
},
|
||||
{
|
||||
name: 'Ticket Management',
|
||||
value: '`/transfer @staff` - Transfer ticket to another staff member\n`/move #category` - Move ticket to another category\n`/force-close` - Force close ticket without confirmation\n`/topic <text>` - Set ticket topic/description\n`/priority <level>` - Set ticket priority\n`/accountinfo email` - Look up website account by email\n`/accountinfo discord @user` - Look up website account by Discord user'
|
||||
value: '`/transfer @staff` - Transfer ticket to another staff member\n`/move #category` - Move ticket to another category\n`/force-close` - Force close ticket without confirmation\n`/topic <text>` - Set ticket topic/description\n`/accountinfo email` - Look up website account by email\n`/accountinfo discord @user` - Look up website account by Discord user'
|
||||
},
|
||||
{
|
||||
name: 'Tags & Responses',
|
||||
value: '`/tag` - Set ticket category (dropdown)\n`/response send <name>` - Send saved response\n`/response create|edit|delete|list` - Manage saved responses'
|
||||
name: 'Saved Responses',
|
||||
value: '`/response send <name>` - Send saved response\n`/response create|edit|delete|list` - Manage saved responses'
|
||||
},
|
||||
{
|
||||
name: 'Variables (for tags)',
|
||||
name: 'Variables (for responses)',
|
||||
value: '`{ticket.user}`, `{ticket.email}`, `{ticket.number}`, `{ticket.subject}`, `{staff.name}`, `{server.name}`, `{date}`, `{time}`'
|
||||
},
|
||||
{
|
||||
@@ -960,63 +842,6 @@ async function handleCommand(interaction) {
|
||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
}
|
||||
|
||||
// /priority
|
||||
if (interaction.commandName === 'priority') {
|
||||
const level = interaction.options.getString('level');
|
||||
|
||||
const ticket = await Ticket.findOne({ discordThreadId: interaction.channel.id }).lean();
|
||||
if (!ticket) {
|
||||
return interaction.reply({ content: 'This channel is not linked to a ticket.', ephemeral: true });
|
||||
}
|
||||
|
||||
const priorityOrder = ['low', 'normal', 'medium', 'high'];
|
||||
const oldIdx = priorityOrder.indexOf((ticket.priority || 'normal').toLowerCase());
|
||||
const newIdx = priorityOrder.indexOf(level.toLowerCase());
|
||||
const emoji = getPriorityEmoji(level);
|
||||
const levelLabel = level.charAt(0).toUpperCase() + level.slice(1).toLowerCase();
|
||||
|
||||
let channelMessage;
|
||||
if (level === 'normal') {
|
||||
channelMessage = 'Your ticket priority has returned to Normal.';
|
||||
} else if (newIdx > oldIdx) {
|
||||
channelMessage = `Your ticket has been upgraded to ${emoji} **${levelLabel}** ${emoji}.`;
|
||||
} else if (newIdx < oldIdx) {
|
||||
channelMessage = `Your ticket has been downgraded to ${emoji} **${levelLabel}** ${emoji}.`;
|
||||
} else {
|
||||
channelMessage = `Priority set to ${emoji} **${levelLabel}** ${emoji}.`;
|
||||
}
|
||||
|
||||
try {
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { priority: level } }
|
||||
);
|
||||
|
||||
const priorityTitle =
|
||||
newIdx === oldIdx
|
||||
? 'Priority Set'
|
||||
: `Priority ${newIdx > oldIdx ? 'Upgraded' : 'Downgraded'} → ${levelLabel}`;
|
||||
const priorityEmbed = new EmbedBuilder()
|
||||
.setTitle(priorityTitle)
|
||||
.setDescription(channelMessage)
|
||||
.setColor(getPriorityColor(level))
|
||||
.setFooter({ text: interaction.member?.displayName || interaction.user.username });
|
||||
await interaction.reply({ embeds: [priorityEmbed] });
|
||||
|
||||
if (level === 'high' && ticket.gmailThreadId && !ticket.gmailThreadId.startsWith('discord-')) {
|
||||
await sendTicketNotificationEmail(
|
||||
ticket,
|
||||
`Priority updated: ${levelLabel}`,
|
||||
channelMessage,
|
||||
interaction.member?.displayName || interaction.user.username
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Priority update error:', err);
|
||||
await interaction.reply({ content: 'Failed to update priority.', ephemeral: true });
|
||||
}
|
||||
}
|
||||
|
||||
// /panel
|
||||
if (interaction.commandName === 'panel') {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
|
||||
Reference in New Issue
Block a user