UNPKG

@elara-services/tickets

Version:
379 lines (363 loc) 16.3 kB
const { AES, Interactions: { modal }, } = require("@elara-services/packages"), { Collection, version } = require("discord.js"), { DiscordWebhook: Webhook, Webhook: BotHook } = require("@elara-services/webhooks"), defLang = require("../languages/en-US"), pack = require("../package.json"); const { discordView } = require("./html"); exports.getString = (name, lang = "en-US") => { if (!lang) { lang = "en-US"; } let str = "[UNKNOWN STRING]"; if (!pack.languages.includes(lang)) { lang = "en-US"; } try { let file = require(`../languages/${lang}.js`); if (file[name]) { str = file[name]; } } catch { if (defLang[name]) { str = defLang[name]; } } return str; }; exports.code = (message, type = "d", token) => { if (!message || !type) { return ""; } if (!token) { return message; } try { const aes = new AES(token); return aes[type === "e" ? "encrypt" : "decrypt"](message); } catch { return message; } }; exports.de = { user: "<:Members:860931214232125450>", channel: "<:Channel:841654412509839390>", transcript: "<:Log:792290922749624320>", }; exports.perms = { manage: { guild: 32n, }, members: { ban: 4n, }, basic: 117760n, allows: { support: 379969n, ticketUser: 379968n, client: 511040n, }, denied: { all: 1024n, // @everyone role ticketUser: 131072n, }, }; exports.fetchMessages = async (channel, limit = 50, before, after, around) => { if (limit && limit > 100) { let logs = []; const get = async (_before, _after) => { const messages = [ ...( await channel.messages .fetch({ limit: 100, before: _before || undefined, after: _after || undefined, }) .catch(() => new Collection()) ).values(), ]; if (limit <= messages.length) { return _after ? messages .slice(messages.length - limit, messages.length) .map((message) => message) .concat(logs) : logs.concat(messages.slice(0, limit).map((message) => message)); } limit -= messages.length; logs = _after ? messages.map((message) => message).concat(logs) : logs.concat(messages.map((message) => message)); if (messages.length < 100) { return logs; } return get((_before || !_after) && messages[messages.length - 1].id, _after && messages[0].id); }; return get(before, after); } return [...(await channel.messages.fetch({ limit, before, after, around }).catch(() => new Collection())).values()]; }; exports.displayMessages = (channel, messages = [], ticketID, type, str) => { let users = []; for (const i of messages.values()) { let f = users.find((c) => c.user.id === i.author.id); if (f) { f.count++; } else { users.push({ user: i.author, count: 1 }); } } return [ `<discord-messages>`, `<discord-message author="${type} ${str("TICKET")}: ${ticketID}" bot="true" verified avatar="https://cdn.discordapp.com/emojis/847397714677334066.png?v=1" role-color="#1da1f2">${str("TOTAL_MESSAGES")}: ${users .map((c) => c.count) .reduce((a, b) => a + b, 0) .toLocaleString()}<br>${users.map((c) => `<discord-mention type="role" color="${channel.guild?.members?.resolve?.(c.user.id)?.displayColor ? channel.guild?.members?.resolve?.(c.user.id)?.displayHexColor : `#ffffff`}">${c.user.tag}</discord-mention> (${c.user.id})`).join("<br>")}</discord-message></discord-messages><discord-messages>`, ...messages.map((message) => { let str = [ `<discord-message${message.author.bot ? ` bot="true" ${message.author.flags?.has?.(version?.includes?.("14") ? 1 << 16 : "VERIFIED_BOT") ? `verified="true"` : ""}` : ""} new_timestamp="${message.createdAt.toISOString()}" author="${message.author.username}" avatar=${message.author.displayAvatarURL({ dynamic: true, format: "png", })} role-color="${channel.guild?.members?.resolve?.(message.author.id)?.displayColor ? channel.guild?.members?.resolve?.(message.author.id)?.displayHexColor : `#ffffff`}">`, ]; if (message.content) { let content = message.content; if (message.mentions.users.size) { for (const user of message.mentions.users.values()) { content = content.replace(new RegExp(`<@!?${user.id}>`, "g"), `<discord-mention type="role" color="${message.guild?.members?.resolve?.(user?.id)?.displayHexColor ?? "#ffffff"}">${user.tag}</discord-mention>`); } } if (message.mentions.channels.size) { for (const channel of message.mentions.channels.values()) { content = content.replace(new RegExp(channel.toString(), "g"), `<discord-mention type="channel">${channel.name}</discord-mention>`); } } if (message.mentions.roles.size) { for (const role of message.mentions.roles.values()) { content = content.replace(new RegExp(role.toString(), "g"), `<discord-mention type="role" color="${role.hexColor}">${role.name}</discord-mention>`); } } str.push(content); } if (message.embeds?.length) { let arr = [`<discord-embeds slot="embeds">`]; for (const embed of message.embeds) { let emb = [ `<discord-embed slot="embed" ${embed.thumbnail?.url ? `thumbnail="${embed.thumbnail.url}"` : ""} ${embed.image?.url ? `image="${embed.image.url}"` : ""} ${embed.author ? `${embed.author.name ? `author-name="${embed.author.name}"` : ""} ${embed.author.iconURL ? `author-image="${embed.author.iconURL}"` : ""} ${embed.author.url ? `author-url="${embed.author.url}"` : ""}` : ""} ${embed.title ? `embed-title="${embed.title}"` : ""}${ embed.color ? `color="${embed.hexColor}"` : "" }>`, ]; if (embed.description) { emb.push(`<discord-embed-description slot="description">${embed.description}</discord-embed-description>`); } if (embed.fields?.length) { emb.push(`<discord-embed-fields slot="fields">`); for (const field of embed.fields) { emb.push(`<discord-embed-field field-title="${field.name}" ${field.inline ? `inline` : ""}>${field.value}</discord-embed-field>`); } } arr.push(...emb, `</discord-embed>`); } str.push(...arr, "</discord-embeds>"); } if (message.interaction?.user) { str.push( `<discord-command slot="reply" command="${message.interaction.type === "APPLICATION_COMMAND" ? "/" : ""}${message.interaction.commandName}" profile="${message.interaction.user.id}" role-color="${channel.guild?.members?.resolve?.(message.interaction.user?.id)?.displayHexColor || "#fffff"}" author="${message.interaction.user.tag}" avatar="${message.interaction.user.displayAvatarURL({ dynamic: true, })}"></discord-command>`, ); } if (message.components?.length) { let row = [`<discord-attachments slot="components">`], styles = { PRIMARY: "primary", SECONDARY: "secondary", SUCCESS: "success", DANGER: "destructive", LINK: "secondary", }; for (const components of message.components) { row.push(`<discord-action-row>`); for (const c of components.components) { if (c.type === "BUTTON") { row.push(`<discord-button ${c.disabled ? `disabled=true` : ""} type="${styles[c.style]}" ${c.emoji?.name ? `emoji-name="${c.emoji.name}"` : ""} ${c.emoji?.id ? `emoji="https://cdn.discordapp.com/emojis/${c.emoji.id}.${c.emoji.animated ? "gif" : "png"}"` : ""} ${c.url ? `url="${c.url}"` : ""}>${c.label || ""}</discord-button>`); } } row.push(`</discord-action-row>`); } if (row.length >= 2) { str.push(...row, `</discord-attachments>`); } } if (message.attachments.size) { str.push(message.attachments.map((c) => `<a href="${c.proxyURL}">${c.name}</a>`).join("<br>")); } str.push(`${message.content?.length ? `<br><br>` : ""}<code style="background-color: #36393e; color: white;">ID: ${message.id}</code>`); return [...str, `</discord-message>`].join(" "); }), "</discord-messages>", ].join(" "); }; /** * @description Generates an HTML page string to use. */ exports.generateHTMLPage = (channel, messages = [], ticketID, type, str) => { return discordView(exports.displayMessages(channel, messages, ticketID, type, str)); }; exports.defs = { modals: { reason: (customId, str) => modal({ id: `${customId.split(":modal_submit")[0]}:modal_complete`, title: str("REASON"), components: [ { type: 1, components: [ { type: 4, custom_id: "reason", label: str("REASON"), style: 2, min_length: 1, max_length: 1024, required: true, placeholder: str("TICKET_CLOSE_PLACEHOLDER"), }, ], }, ], }), unbanReason: (customId, server, str, member, prefix) => modal({ id: `${prefix}:unban_modal:${customId.split(":")[1]}`, title: `${str("UNBAN_FROM")} ${server.name}`, components: [ { type: 1, components: [ { type: 4, label: str("REASON"), custom_id: "reason", style: 2, min_length: 1, max_length: 512, required: true, value: `${str("NO_REASON")} | ${str("BY")}: ${member.user.tag} (${member.id})`, }, ], }, ], }), custom: (options = { title: "", components: [] }, { prefix, str } = {}) => modal({ id: `${prefix}:modal_submit`, title: options?.title || str("CREATE_TICKET"), components: options?.components?.length >= 1 ? options.components : [ { type: 1, components: [ { type: 4, min_length: 10, max_length: 4000, custom_id: "message", label: str("MODAL_CONTENT"), style: 2, placeholder: str("MODAL_CONTENT_PLACEHOLDER"), required: true, }, ], }, ], }), }, getModalComponents: (questions) => { return questions.length >= 1 ? questions.slice(0, 5).map((c) => ({ type: 1, components: [ { min_length: c.min_length || 10, max_length: c.max_length || 4000, type: 4, style: c.style || 2, label: c.label, value: c.value, placeholder: c.placeholder, required: c.required, custom_id: c.label || `random_${Math.floor(Math.random() * 10000)}`, }, ], })) : []; }, }; /** * @param {object} options * @param {string} [options.userId] * @param {string} [options.token] * @param {import("discord.js").Guild} [options.guild] * @returns {boolean} */ exports.hasTicket = ({ userId, guild, token, prefix } = {}) => { if (!userId || !guild || !token || !prefix || !guild?.channels?.cache?.size) { return false; } return guild.channels.cache.filter((c) => [0, "GUILD_TEXT"].includes(c.type) && c.topic && exports.code(c.topic.split("ID: ")[1], "d", token) === userId && c.name?.match?.(new RegExp(prefix, "gi"))).size ? true : false; }; exports.embed = (content, { color, title, guild, footer, author, str, description } = {}) => ({ title: title || str("INFO"), description: content || description, color: color || 0xff0000, timestamp: new Date(), footer, author: author || { name: guild?.name ?? str("TICKETS"), icon_url: guild?.iconURL?.({ dynamic: true }) ?? "https://cdn.discordapp.com/emojis/818757771310792704.png?v=1", }, }); exports.webhook = (options) => { const { id, token, username, avatar, threadId } = options; return new Webhook(`https://discord.com/api/webhooks/${id}/${token}`, { username, avatar_url: avatar, threadId, }); }; /** * @param {import("@elara-services/tickets").TicketOptions} options * @param {string} username */ exports.getWebhookInfo = async (options, username = "Tickets") => { if (options.webhook?.channelId) { const channel = options.client.channels.resolve(options.webhook.channelId); if (channel) { const hook = await new BotHook(options.client.token).fetch(channel.isThread() ? channel.parentId : channel.id); if (hook) { options.webhook.id = hook.id; options.webhook.token = hook.token; options.webhook.threadId = channel.isThread() ? channel.id : undefined; } } } return { id: options.webhook?.id, token: options.webhook?.token, threadId: options.webhook?.threadId, username: options.webhook?.username || username, avatar: options.webhook?.avatar || "https://cdn.discordapp.com/emojis/818757771310792704.png?v=1", }; }; exports.getAppealServer = ({ appeals, client } = {}) => { if (!appeals?.enabled || !appeals?.mainserver?.checkIfBanned) { return null; } let server = client.guilds.resolve(appeals.mainserver?.id); return server?.available === true ? server : null; };