Files
hexerade/src/utils/ticket-events.ts
T
2026-06-03 15:22:25 -04:00

228 lines
7.3 KiB
TypeScript

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<EmbedBuilder> {
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<ActionRowBuilder<ButtonBuilder>> {
const row = new ActionRowBuilder<ButtonBuilder>();
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
}
});
}