Initial commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-02-10 08:22:19 -06:00
commit 519788c633
39 changed files with 17121 additions and 0 deletions

94
handlers/messages.js Normal file
View File

@@ -0,0 +1,94 @@
/**
* Discord messageCreate handler forwards staff replies to Gmail and Zammad.
*/
const { mongoose } = require('../db-connection');
const { CONFIG } = require('../config');
const { ZAMMAD } = require('../config');
const { extractRawEmail } = require('../utils');
const { getGmailClient, sendGmailReply } = require('../services/gmail');
const { addZammadArticle } = require('../services/zammad');
const { updateTicketActivity } = require('../services/tickets');
const Ticket = mongoose.model('Ticket');
/**
* Handle a Discord message in a ticket channel → relay to Gmail (email tickets only) + Zammad.
*/
async function handleDiscordReply(m) {
if (m.author.bot || m.interaction) return;
const ticket = await Ticket.findOne({ discordThreadId: m.channel.id }).lean();
if (!ticket) return;
const discordUser = m.member?.displayName || m.author.username;
// Discord-originated tickets: no Gmail thread; only add reply to Zammad.
if (ticket.gmailThreadId.startsWith('discord-')) {
if (ticket.zammadTicketId && ZAMMAD.URL && ZAMMAD.TOKEN) {
try {
await addZammadArticle(ticket.zammadTicketId, m.content, { from: discordUser });
} catch (zErr) {
console.error('Zammad article (Discord ticket reply) failed:', zErr.response?.data || zErr.message);
}
}
return;
}
// Email tickets: send reply via Gmail and add to Zammad.
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
);
await updateTicketActivity(ticket.gmailThreadId);
if (ticket.zammadTicketId && ZAMMAD.URL && ZAMMAD.TOKEN) {
try {
await addZammadArticle(ticket.zammadTicketId, m.content, { from: discordUser });
} catch (zErr) {
console.error('Zammad article (Discord reply) failed:', zErr.response?.data || zErr.message);
}
}
} catch (e) {
console.error('REPLY ERROR:', e);
}
}
module.exports = { handleDiscordReply };