import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, DiscordAPIError, EmbedBuilder, Message, SendableChannels } from 'discord.js'; import { config } from '../config'; import { Database } from '../shared/Database'; import { Ticket, TicketPhrase, TicketUpdate } from '../types'; import { PostAction, getE621Post, getE621User, spoilerOrBlacklist } from './e621-utils'; import { getAuthor, getColor, getDescription, getFields } from './event-utils'; import { humanizeCapitalization } from './string-utils'; import { shouldAlert } from './ticket-utils'; export async function ticketUpdateHandler(client: Client, update: string) { const data: TicketUpdate = JSON.parse(update); if (data.action == 'create') { postTicket(client, data); } else { updateTicket(client, data); } } async function postTicket(client: Client, data: TicketUpdate) { const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!); if (!guildSettings || !guildSettings.tickets_channel_id) return; const channel = await client.channels.fetch(guildSettings.tickets_channel_id); if (!channel || !channel.isSendable()) return; const ticket = data.ticket; const embed = await createEmbedFromTicket(ticket); const row = await getButtons(ticket); const message = await channel.send({ embeds: [embed], components: [row] }); await Database.putTicketOrUpdate(ticket.id, message.id); sendTicketAlerts(ticket, channel); } async function updateTicket(client: Client, data: TicketUpdate) { const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!); if (!guildSettings || !guildSettings.tickets_channel_id) return; const channel = await client.channels.fetch(guildSettings.tickets_channel_id); if (!channel || !channel.isSendable()) return; const messageId = await Database.getTicketMessageId(data.ticket.id); if (!messageId) return postTicket(client, data); let message: Message | undefined; try { message = await channel.messages.fetch(messageId); } catch (e) { // Ignore unknown message errors if (!(e instanceof DiscordAPIError && e.code == 10008)) { throw e; } } if (!message || message.author.id != config.DISCORD_CLIENT_ID) return postTicket(client, data); const embed = await createEmbedFromTicket(data.ticket); await message.edit({ embeds: [embed] }); } function getTitle(ticket: Ticket): string { if (!ticket.target) return `${humanizeCapitalization(ticket.category)} report by ${ticket.user}`; switch (ticket.category) { case 'blip': return `Blip by ${ticket.target}`; case 'comment': return `Comment by ${ticket.target}`; case 'dmail': return `DMail sent by ${ticket.target}`; case 'forum': return `Forum post by ${ticket.target}`; case 'pool': return `Pool ${ticket.target}`; case 'post': return `Post uploaded by ${ticket.target}`; case 'set': return `Wow, a rare set report! ${ticket.target}`; case 'user': return `User ${ticket.target}`; case 'wiki': return `Wiki page ${ticket.target}`; default: return 'Uknown ticket category'; } } function getURL(ticket: Ticket): string { return `${config.E621_BASE_URL}/tickets/${ticket.id}`; } async function createEmbedFromTicket(ticket: Ticket): Promise { return new EmbedBuilder() .setTitle(getTitle(ticket)) .setURL(await getURL(ticket)) .setDescription(await getDescription(ticket)) .setAuthor(getAuthor(ticket)) .setColor(getColor(ticket)) .setFields(...getFields(ticket)) .setFooter({ text: `Ticket #${ticket.id}` }); } async function getButtons(ticket: Ticket): Promise> { const row = new ActionRowBuilder(); const primaryButton = new ButtonBuilder() .setStyle(ButtonStyle.Link); let skipPrimary = false; if (ticket.category == 'blip') { primaryButton .setLabel('Open Blip') .setURL(`${config.E621_BASE_URL}/blips/${ticket.target_id}`); } else if (ticket.category == 'comment') { primaryButton .setLabel('Open Comment') .setURL(`${config.E621_BASE_URL}/comments/${ticket.target_id}`); } else if (ticket.category == 'dmail') { primaryButton .setLabel('Open DMail') .setURL(`${config.E621_BASE_URL}/dmails/${ticket.target_id}`); } else if (ticket.category == 'forum') { primaryButton .setLabel('Open Forum Post') .setURL(`${config.E621_BASE_URL}/forum_posts/${ticket.target_id}`); } else if (ticket.category == 'pool') { primaryButton .setLabel('Open Pool') .setURL(`${config.E621_BASE_URL}/pools/${ticket.target_id}`); } else if (ticket.category == 'post') { const post = await getE621Post(ticket.target_id); if (post && spoilerOrBlacklist(post).action == PostAction.Blacklist) skipPrimary = true; else { primaryButton .setLabel('Open Post') .setURL(`${config.E621_BASE_URL}/posts/${ticket.target_id}`); } } else if (ticket.category == 'set') { primaryButton .setLabel('Open Set') .setURL(`${config.E621_BASE_URL}/post_sets/${ticket.target_id}`); } else if (ticket.category == 'user') { primaryButton .setLabel('Open User') .setURL(`${config.E621_BASE_URL}/users/${ticket.target_id}`); } else if (ticket.category == 'wiki') { primaryButton .setLabel('Open Wiki') .setURL(`${config.E621_BASE_URL}/wikis/${ticket.target_id}`); } else { console.error('Unknown ticket type:'); console.error(JSON.stringify(ticket, null, 2)); skipPrimary = true; } if (!skipPrimary) row.addComponents(primaryButton); if (ticket.category == 'blip' || ticket.category == 'comment' || ticket.category == 'dmail' || ticket.category == 'forum') { const button = new ButtonBuilder() .setLabel('Open Target User') .setStyle(ButtonStyle.Link) .setURL(`${config.E621_BASE_URL}/users/${ticket.accused_id}`); row.addComponents(button); } else if (ticket.category == 'post') { const user = await getE621User(ticket.target!); if (user) { const button = new ButtonBuilder() .setLabel('Open Target User') .setStyle(ButtonStyle.Link) .setURL(`${config.E621_BASE_URL}/users/${user.id}`); row.addComponents(button); } } return row; } async function sendTicketAlerts(ticket: Ticket, channel: SendableChannels) { const guildSettings = await Database.getGuildSettings(config.DISCORD_GUILD_ID!); if (!guildSettings || !guildSettings.admin_role_id) return; const usersToMention: string[] = []; const rolesToMention: string[] = []; let content = ''; await Database.getAllTicketPhrases((ticketPhrase: TicketPhrase) => { const { alert, match } = shouldAlert(ticketPhrase, ticket); if (alert) { const mention = ticketPhrase.user_id == 'admin' ? `<@&${guildSettings.admin_role_id!}>` : `<@${ticketPhrase.user_id}>`; if (ticketPhrase.user_id == 'admin' && !rolesToMention.includes(guildSettings.admin_role_id!)) { rolesToMention.push(guildSettings.admin_role_id!); } else if (!usersToMention.includes(ticketPhrase.user_id)) { usersToMention.push(ticketPhrase.user_id); } content += `${mention}: ${match}\n`; } }); if (content.length == 0) return; await channel.send({ content, allowedMentions: { users: usersToMention, roles: rolesToMention } }); }