staff notifications
This commit is contained in:
@@ -13,7 +13,7 @@ const {
|
||||
const { mongoose } = require('../db-connection');
|
||||
const { CONFIG, TICKET_TAGS } = require('../config');
|
||||
const { getPriorityEmoji, getPriorityColor, replaceVariables, escapeRegex } = require('../utils');
|
||||
const { canRename, makeTicketName, getOrCreateTicketCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit } = require('../services/tickets');
|
||||
const { canRename, makeTicketName, resolveCreatorNickname, getSenderLocal, toDiscordSafeName, getOrCreateTicketCategory, createDiscordTicketAsThread, checkTicketCreationRateLimit } = require('../services/tickets');
|
||||
const { sendTicketNotificationEmail } = require('../services/gmail');
|
||||
const { getTicketActionRow } = require('../utils/ticketComponents');
|
||||
const { getEmailRouting } = require('../services/guildSettings');
|
||||
@@ -26,6 +26,7 @@ const { handleSetupCommand } = require('./setup');
|
||||
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.
|
||||
@@ -66,20 +67,19 @@ async function runEscalation(interaction, ticket, nextTier, reason) {
|
||||
? (isDiscordTicket ? (CONFIG.DISCORD_ESCALATED2_CHANNEL_ID || CONFIG.DISCORD_ESCALATED_CATEGORY_ID) : (CONFIG.EMAIL_ESCALATED2_CHANNEL_ID || CONFIG.EMAIL_ESCALATED_CATEGORY_ID))
|
||||
: (isDiscordTicket ? CONFIG.DISCORD_ESCALATED3_CHANNEL_ID : CONFIG.EMAIL_ESCALATED3_CHANNEL_ID);
|
||||
|
||||
// Clear claim on escalation
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { escalated: true, escalationTier: nextTier } }
|
||||
{ $set: { escalated: true, escalationTier: nextTier, claimedBy: null, claimerId: null } }
|
||||
);
|
||||
ticket.escalated = true;
|
||||
ticket.escalationTier = nextTier;
|
||||
ticket.claimedBy = null;
|
||||
|
||||
const creatorNickname = await resolveCreatorNickname(interaction.guild, ticket);
|
||||
const renameInfo = await canRename(ticket);
|
||||
if (renameInfo.ok) {
|
||||
const newName = makeTicketName(
|
||||
{ escalated: true, claimed: false },
|
||||
ticket,
|
||||
interaction.guild
|
||||
);
|
||||
const newName = makeTicketName('escalated', ticket, creatorNickname);
|
||||
try {
|
||||
await enqueueRename(interaction.channel, newName);
|
||||
} catch (e) {
|
||||
@@ -97,19 +97,6 @@ async function runEscalation(interaction, ticket, nextTier, reason) {
|
||||
await enqueueMove(interaction.channel, categoryId);
|
||||
}
|
||||
|
||||
if (!interaction.channel.isThread()) {
|
||||
try {
|
||||
const emoji = nextTier === 1 ? CONFIG.PRIORITY_MEDIUM_EMOJI : CONFIG.PRIORITY_HIGH_EMOJI;
|
||||
const baseName = interaction.channel.name.replace(/^[🟢🟡🔴]/, '');
|
||||
const renameInfoEsc = await canRename(ticket);
|
||||
if (renameInfoEsc.ok) await enqueueRename(interaction.channel, `${emoji}${baseName}`);
|
||||
const tierCategory = nextTier === 1 ? CONFIG.STAFF_T2_CATEGORY : CONFIG.STAFF_T3_CATEGORY;
|
||||
if (tierCategory) await enqueueMove(interaction.channel, tierCategory);
|
||||
} catch (e) {
|
||||
console.error('Staff tier category (escalate):', e);
|
||||
}
|
||||
}
|
||||
|
||||
const pendingEmbed = new EmbedBuilder()
|
||||
.setDescription('Ticket will be escalated in a few seconds.')
|
||||
.setColor(CONFIG.EMBED_COLOR_INFO);
|
||||
@@ -188,20 +175,18 @@ async function runDeescalation(interaction, ticket) {
|
||||
|
||||
await Ticket.updateOne(
|
||||
{ gmailThreadId: ticket.gmailThreadId },
|
||||
{ $set: { escalated: newTier > 0, escalationTier: newTier, claimedBy: null } }
|
||||
{ $set: { escalated: newTier > 0, escalationTier: newTier, claimedBy: null, claimerId: null } }
|
||||
);
|
||||
ticket.escalated = newTier > 0;
|
||||
ticket.escalationTier = newTier;
|
||||
ticket.claimedBy = null;
|
||||
|
||||
const baseName = interaction.channel.name.replace(/^[🟢🟡🔴]/, '');
|
||||
const creatorNickname = await resolveCreatorNickname(interaction.guild, ticket);
|
||||
const state = newTier === 0 ? 'unclaimed' : 'escalated';
|
||||
const renameInfo = await canRename(ticket);
|
||||
if (renameInfo.ok) {
|
||||
try {
|
||||
const emoji = newTier === 0 ? CONFIG.PRIORITY_LOW_EMOJI : CONFIG.PRIORITY_MEDIUM_EMOJI;
|
||||
await enqueueRename(
|
||||
interaction.channel,
|
||||
newTier === 0 ? baseName : `${emoji}${baseName}`
|
||||
);
|
||||
await enqueueRename(interaction.channel, makeTicketName(state, ticket, creatorNickname));
|
||||
} catch (e) {
|
||||
console.error('Rename error (deescalate):', e);
|
||||
}
|
||||
@@ -215,8 +200,15 @@ async function runDeescalation(interaction, ticket) {
|
||||
|
||||
if (!interaction.channel.isThread()) {
|
||||
try {
|
||||
if (newTier === 0 && CONFIG.STAFF_T1_CATEGORY) await enqueueMove(interaction.channel, CONFIG.STAFF_T1_CATEGORY);
|
||||
if (newTier === 1 && CONFIG.STAFF_T2_CATEGORY) await enqueueMove(interaction.channel, CONFIG.STAFF_T2_CATEGORY);
|
||||
if (newTier === 0) {
|
||||
const homeCategory = isDiscordTicket ? CONFIG.DISCORD_TICKET_CATEGORY_ID : CONFIG.TICKET_CATEGORY_ID;
|
||||
if (homeCategory) await enqueueMove(interaction.channel, homeCategory);
|
||||
} else if (newTier === 1) {
|
||||
const t2Category = isDiscordTicket
|
||||
? (CONFIG.DISCORD_ESCALATED2_CHANNEL_ID || CONFIG.DISCORD_ESCALATED_CATEGORY_ID)
|
||||
: (CONFIG.EMAIL_ESCALATED2_CHANNEL_ID || CONFIG.EMAIL_ESCALATED_CATEGORY_ID);
|
||||
if (t2Category) await enqueueMove(interaction.channel, t2Category);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Move error (deescalate):', e);
|
||||
}
|
||||
@@ -328,6 +320,82 @@ 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) {
|
||||
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';
|
||||
|
||||
Reference in New Issue
Block a user