Files
broccolini-bot/handlers/messages.js

121 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 { pingStaffChannel } = require('../services/staffChannel');
const { notifyStaffOfReply } = require('../services/staffNotifications');
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;
if (ticket.claimerId && m.author.id !== ticket.claimerId && ticket.staffChannelId) {
try {
const staffChan = await m.guild.channels.fetch(ticket.staffChannelId).catch(() => null);
if (staffChan) {
await pingStaffChannel(staffChan, ticket.claimerId, m);
}
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(() => {});
}
}
} catch (e) {
console.error('Staff ping error:', e);
}
}
// 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(() => {});
// Notify claiming staff if a non-staff user replied (works for both Discord and email tickets)
if (ticket.claimerId && !isStaffMember) {
const guild = m.guild;
const freshTicket = await Ticket.findOne({ discordThreadId: m.channel.id }).lean();
if (freshTicket) {
await notifyStaffOfReply(guild, freshTicket, m).catch(e => console.error('notifyStaffOfReply:', e));
}
}
const discordUser = m.member?.displayName || 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,
discordUser,
msgId,
m.author.id
);
await updateTicketActivity(ticket.gmailThreadId);
} catch (e) {
console.error('REPLY ERROR:', e);
}
}
module.exports = { handleDiscordReply };