command 2
This commit is contained in:
@@ -365,13 +365,6 @@ async function registerCommands() {
|
|||||||
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
|
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
|
|
||||||
new SlashCommandBuilder()
|
|
||||||
.setName('ids')
|
|
||||||
.setDescription('List all channel and role IDs in this server')
|
|
||||||
.setContexts([InteractionContextType.Guild])
|
|
||||||
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
|
|
||||||
|
|
||||||
new SlashCommandBuilder()
|
new SlashCommandBuilder()
|
||||||
.setName('accountinfo')
|
.setName('accountinfo')
|
||||||
.setDescription('Look up website account info by email or Discord user')
|
.setDescription('Look up website account info by email or Discord user')
|
||||||
|
|||||||
@@ -967,49 +967,6 @@ async function handleCommand(interaction) {
|
|||||||
await interaction.editReply('❌ An error occurred while fetching statistics.');
|
await interaction.editReply('❌ An error occurred while fetching statistics.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /ids – list channel and role IDs (like discord.py guild.channels / guild.roles)
|
|
||||||
if (interaction.commandName === 'ids') {
|
|
||||||
trackInteraction('commands', 'ids', interaction.user.tag);
|
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
const guild = interaction.guild;
|
|
||||||
if (!guild) {
|
|
||||||
await interaction.editReply('This command can only be used in a server.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await guild.channels.fetch().catch(() => {});
|
|
||||||
|
|
||||||
const channelTypeName = type =>
|
|
||||||
Object.entries(ChannelType).find(([, v]) => v === type)?.[0] ?? String(type);
|
|
||||||
|
|
||||||
const lines = ['**Channels:**'];
|
|
||||||
const channels = [...guild.channels.cache.values()].sort((a, b) => a.rawPosition - b.rawPosition);
|
|
||||||
for (const ch of channels) {
|
|
||||||
lines.push(`\`${ch.id}\` — ${ch.name} (${channelTypeName(ch.type)})`);
|
|
||||||
}
|
|
||||||
lines.push('');
|
|
||||||
lines.push('**Roles:**');
|
|
||||||
const roles = [...guild.roles.cache.values()].sort((a, b) => b.position - a.position);
|
|
||||||
for (const role of roles) {
|
|
||||||
lines.push(`\`${role.id}\` — ${role.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = lines.join('\n');
|
|
||||||
if (text.length <= 1900) {
|
|
||||||
await interaction.editReply(text);
|
|
||||||
} else {
|
|
||||||
const buf = Buffer.from(text, 'utf8');
|
|
||||||
await interaction.editReply({
|
|
||||||
content: 'List is long; sent as a file.',
|
|
||||||
files: [new AttachmentBuilder(buf, { name: `guild-ids-${guild.id}.txt` })]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
trackError('ids-command', err, interaction);
|
|
||||||
await interaction.editReply('Failed to build ID list.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1182,4 +1139,11 @@ async function handleAutocomplete(interaction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { handleCommand, handleContextMenu, handleAutocomplete, runEscalation, runDeescalation };
|
module.exports = {
|
||||||
|
handleCommand,
|
||||||
|
handleContextMenu,
|
||||||
|
handleAutocomplete,
|
||||||
|
runEscalation,
|
||||||
|
runDeescalation,
|
||||||
|
hasStaffRole
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,20 +1,91 @@
|
|||||||
/**
|
/**
|
||||||
* Discord messageCreate handler – forwards staff replies to Gmail.
|
* Discord messageCreate handler – prefix commands and forwards staff replies to Gmail.
|
||||||
*/
|
*/
|
||||||
|
const { ChannelType, AttachmentBuilder } = require('discord.js');
|
||||||
const { mongoose } = require('../db-connection');
|
const { mongoose } = require('../db-connection');
|
||||||
const { CONFIG } = require('../config');
|
const { CONFIG } = require('../config');
|
||||||
const { extractRawEmail } = require('../utils');
|
const { extractRawEmail } = require('../utils');
|
||||||
const { getGmailClient, sendGmailReply } = require('../services/gmail');
|
const { getGmailClient, sendGmailReply } = require('../services/gmail');
|
||||||
const { updateTicketActivity } = require('../services/tickets');
|
const { updateTicketActivity } = require('../services/tickets');
|
||||||
|
const { hasStaffRole } = require('./commands');
|
||||||
|
const { trackInteraction, trackError } = require('./analytics');
|
||||||
|
|
||||||
const Ticket = mongoose.model('Ticket');
|
const Ticket = mongoose.model('Ticket');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `!ids` — list channel and role IDs (same rules as slash staff-gated commands).
|
||||||
|
* @returns {Promise<boolean>} true if the message was consumed
|
||||||
|
*/
|
||||||
|
async function tryHandleIdsCommand(m) {
|
||||||
|
if (m.content?.trim().toLowerCase() !== '!ids') return false;
|
||||||
|
|
||||||
|
if (!m.guild) {
|
||||||
|
await m.reply('This command can only be used in a server.').catch(() => {});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staffConfigured =
|
||||||
|
CONFIG.ROLE_ID_TO_PING || (CONFIG.ADDITIONAL_STAFF_ROLES && CONFIG.ADDITIONAL_STAFF_ROLES.length > 0);
|
||||||
|
if (staffConfigured && !hasStaffRole(m.member)) {
|
||||||
|
const roleMention = CONFIG.ROLE_ID_TO_PING ? `<@&${CONFIG.ROLE_ID_TO_PING}>` : 'support';
|
||||||
|
await m.reply({
|
||||||
|
content: `This command is only available to the support team (${roleMention}).`,
|
||||||
|
allowedMentions: { users: [], roles: [], repliedUser: false }
|
||||||
|
}).catch(() => {});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackInteraction('commands', 'ids-prefix', m.author.tag);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await m.guild.channels.fetch().catch(() => {});
|
||||||
|
|
||||||
|
const channelTypeName = type =>
|
||||||
|
Object.entries(ChannelType).find(([, v]) => v === type)?.[0] ?? String(type);
|
||||||
|
|
||||||
|
const lines = ['**Channels:**'];
|
||||||
|
const channels = [...m.guild.channels.cache.values()].sort((a, b) => a.rawPosition - b.rawPosition);
|
||||||
|
for (const ch of channels) {
|
||||||
|
lines.push(`\`${ch.id}\` — ${ch.name} (${channelTypeName(ch.type)})`);
|
||||||
|
}
|
||||||
|
lines.push('');
|
||||||
|
lines.push('**Roles:**');
|
||||||
|
const roles = [...m.guild.roles.cache.values()].sort((a, b) => b.position - a.position);
|
||||||
|
for (const role of roles) {
|
||||||
|
lines.push(`\`${role.id}\` — ${role.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = lines.join('\n');
|
||||||
|
const baseReply = { allowedMentions: { users: [], roles: [], repliedUser: false } };
|
||||||
|
if (text.length <= 1900) {
|
||||||
|
await m.reply({ content: text, ...baseReply }).catch(() => {});
|
||||||
|
} else {
|
||||||
|
const buf = Buffer.from(text, 'utf8');
|
||||||
|
await m.reply({
|
||||||
|
content: 'List is long; sent as a file.',
|
||||||
|
files: [new AttachmentBuilder(buf, { name: `guild-ids-${m.guild.id}.txt` })],
|
||||||
|
...baseReply
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
trackError('ids-prefix-command', err, null);
|
||||||
|
await m.reply({
|
||||||
|
content: 'Failed to build ID list.',
|
||||||
|
allowedMentions: { repliedUser: false }
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a Discord message in a ticket channel → relay to Gmail (email tickets only).
|
* Handle a Discord message in a ticket channel → relay to Gmail (email tickets only).
|
||||||
*/
|
*/
|
||||||
async function handleDiscordReply(m) {
|
async function handleDiscordReply(m) {
|
||||||
if (m.author.bot || m.interaction) return;
|
if (m.author.bot || m.interaction) return;
|
||||||
|
|
||||||
|
if (await tryHandleIdsCommand(m)) return;
|
||||||
|
|
||||||
const ticket = await Ticket.findOne({ discordThreadId: m.channel.id }).lean();
|
const ticket = await Ticket.findOne({ discordThreadId: m.channel.id }).lean();
|
||||||
if (!ticket) return;
|
if (!ticket) return;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user