/** * Discord messageCreate handler – forwards staff replies to Gmail. */ const { mongoose } = require('../db-connection'); const { CONFIG } = require('../config'); const { extractRawEmail } = require('../utils'); const { getGmailClient, sendGmailReply } = require('../services/gmail'); const { updateTicketActivity } = require('../services/tickets'); const { getNotifyDm } = require('../services/staffSettings'); const Ticket = mongoose.model('Ticket'); /** * Handle a Discord message in a ticket channel → relay to Gmail (email tickets only). */ async function handleDiscordReply(m) { if (m.author.bot || m.interaction) return; const ticket = await Ticket.findOne({ discordThreadId: m.channel.id }).lean(); if (!ticket) return; // Track whether last message is from staff or customer const memberForCheck = await m.guild.members.fetch(m.author.id).catch(() => null); const isStaffMember = memberForCheck && CONFIG.ROLE_ID_TO_PING && memberForCheck.roles.cache.has(CONFIG.ROLE_ID_TO_PING); Ticket.updateOne( { discordThreadId: m.channel.id }, { $set: { lastMessageAuthorIsStaff: !!isStaffMember, lastActivity: new Date() } } ).catch(() => {}); // DM the claimer if they have notifydm on and a non-staff user replied. if (ticket.claimerId && !isStaffMember && m.author.id !== ticket.claimerId) { const dmEnabled = await getNotifyDm(ticket.claimerId); if (dmEnabled) { const staffMember = await m.guild.members.fetch(ticket.claimerId).catch(() => null); if (staffMember) { const jumpLink = `https://discord.com/channels/${m.guild.id}/${m.channel.id}/${m.id}`; await staffMember .send( `New customer reply in **${m.channel.name}**:\n> ${m.content.slice(0, 300)}\n[Jump to message](${jumpLink})` ) .catch(() => {}); } } } const authorName = m.member?.displayName || m.member?.nickname || m.author.globalName || m.author.username; if (ticket.gmailThreadId.startsWith('discord-')) { return; } // Email tickets: send reply via Gmail. try { const gmail = getGmailClient(); const thread = await gmail.users.threads.get({ userId: 'me', id: ticket.gmailThreadId }); const last = [...thread.data.messages].reverse().find(msg => { const from = msg.payload.headers.find(h => h.name === 'From')?.value || ''; return !from.toLowerCase().includes(CONFIG.MY_EMAIL); }); if (!last) return; let recipient = last.payload.headers.find(h => h.name === 'From')?.value || ''; const replyTo = last.payload.headers.find(h => h.name === 'Reply-To')?.value; if (replyTo) recipient = replyTo; const subject = last.payload.headers.find(h => h.name === 'Subject')?.value || 'Support'; const msgId = last.payload.headers.find(h => h.name === 'Message-ID')?.value; const recipientEmail = extractRawEmail(recipient).toLowerCase(); if (!recipientEmail || recipientEmail === CONFIG.MY_EMAIL) { console.warn('Bad recipient for reply:', recipientEmail); return; } await sendGmailReply( ticket.gmailThreadId, m.content, recipientEmail, subject, authorName, msgId, m.author.id ); await updateTicketActivity(ticket.gmailThreadId); } catch (e) { console.error('REPLY ERROR:', e); } } module.exports = { handleDiscordReply };