Gmail folder auto-advance + /forward command
- Reply-cycle auto-advance: staff reply files the thread to "Awaiting Reply", a customer response files it to "Needs Response" (new GMAIL_LABEL_AWAITING_REPLY / GMAIL_LABEL_NEEDS_RESPONSE labels + autoAdvanceFolder, which only moves threads still in the auto-cycle and leaves hand-filed folders alone) - /forward: forward a ticket's email to another address (handlers/commands/forward.js + forward composition in services/gmail.js) - Tests for the auto-advance cycle; label fixtures updated for the new labels
This commit is contained in:
59
handlers/commands/forward.js
Normal file
59
handlers/commands/forward.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* /forward — forward this ticket's email thread to a third-party address.
|
||||
*
|
||||
* Builds a fresh outbound email to the target only; the original customer is
|
||||
* never looped in (see services/gmail.js forwardThread).
|
||||
*/
|
||||
const { MessageFlags } = require('discord.js');
|
||||
const { findTicketForChannel } = require('../sharedHelpers');
|
||||
const { forwardThread } = require('../../services/gmail');
|
||||
const { logError, logTicketEvent } = require('../../services/debugLog');
|
||||
|
||||
const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
|
||||
|
||||
async function handleForward(interaction) {
|
||||
const ticket = await findTicketForChannel(interaction);
|
||||
if (!ticket) return;
|
||||
|
||||
// Discord-origin tickets have no Gmail thread to forward.
|
||||
if (ticket.gmailThreadId.startsWith('discord-')) {
|
||||
return interaction.reply({
|
||||
content: "This ticket has no email thread, so there's nothing to forward.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const target = interaction.options.getString('email');
|
||||
const note = interaction.options.getString('note') || '';
|
||||
|
||||
// Defer: fetching the thread + downloading attachments can exceed the 3s window.
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
try {
|
||||
const { messageCount, attachmentCount, skipped } = await forwardThread(
|
||||
ticket.gmailThreadId, target, note
|
||||
);
|
||||
|
||||
logTicketEvent('Email thread forwarded', [
|
||||
{ name: 'To', value: target },
|
||||
{ name: 'Messages', value: String(messageCount) },
|
||||
{ name: 'Forwarded by', value: interaction.user.tag }
|
||||
], interaction).catch(() => {});
|
||||
|
||||
const skippedNote = skipped ? ` (${plural(skipped, 'attachment')} skipped — over the size limit)` : '';
|
||||
return interaction.editReply({
|
||||
content: `Forwarded ${plural(messageCount, 'message')} (${plural(attachmentCount, 'attachment')}) to **${target}**.${skippedNote}`
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code === 'EBADRECIPIENT') {
|
||||
return interaction.editReply({ content: "That doesn't look like a valid email address." });
|
||||
}
|
||||
if (err.code === 'EEMPTY') {
|
||||
return interaction.editReply({ content: 'This thread has no messages to forward.' });
|
||||
}
|
||||
logError('handleForward', err, interaction).catch(() => {});
|
||||
return interaction.editReply({ content: `Failed to forward: ${err.message}` });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handleForward };
|
||||
Reference in New Issue
Block a user