blob: acd641b04c0c6e51f29c7a748df3295457a0926a [file] [log] [blame]
Skyler Greyf21323a2022-08-13 23:58:22 +01001import {
2 CommandInteraction,
3 GuildMember,
4 Message,
TheCodedProf21c08592022-09-13 14:14:43 -04005 ActionRowBuilder,
6 ButtonBuilder,
Skyler Greyf21323a2022-08-13 23:58:22 +01007 MessageComponentInteraction,
TheCodedProf21c08592022-09-13 14:14:43 -04008 TextChannel,
PineaFan538d3752023-01-12 21:48:23 +00009 ButtonStyle,
TheCodedProf2e54a772023-02-14 16:26:47 -050010 User,
TheCodedProf1f675042023-02-16 17:01:29 -050011 ComponentType
Skyler Greyf21323a2022-08-13 23:58:22 +010012} from "discord.js";
pineafan813bdf42022-07-24 10:39:10 +010013import EmojiEmbed from "../utils/generateEmojiEmbed.js";
14import getEmojiByName from "../utils/getEmojiByName.js";
15import { PasteClient, Publicity, ExpireDate } from "pastebin-api";
pineafan813bdf42022-07-24 10:39:10 +010016import client from "../utils/client.js";
TheCodedProf94ff6de2023-02-22 17:47:26 -050017import { messageException } from '../utils/createTemporaryStorage.js';
pineafan813bdf42022-07-24 10:39:10 +010018
PineaFan752af462022-12-31 21:59:38 +000019const pbClient = new PasteClient(client.config.pastebinApiKey);
pineafan813bdf42022-07-24 10:39:10 +010020
TheCodedProf2e54a772023-02-14 16:26:47 -050021interface TranscriptEmbed {
22 title?: string;
23 description?: string;
24 fields?: {
25 name: string;
26 value: string;
27 inline: boolean;
28 }[];
29 footer?: {
30 text: string;
31 iconURL?: string;
32 };
33}
34
35interface TranscriptComponent {
36 type: number;
37 style?: ButtonStyle;
38 label?: string;
39 description?: string;
40 placeholder?: string;
41 emojiURL?: string;
42}
43
44interface TranscriptAuthor {
45 username: string;
46 discriminator: number;
47 nickname?: string;
48 id: string;
49 iconURL?: string;
50 topRole: {
51 color: number;
52 badgeURL?: string;
53 }
54}
55
TheCodedProf1f675042023-02-16 17:01:29 -050056interface TranscriptAttachment {
57 url: string;
58 filename: string;
59 size: number;
60 log?: string;
61}
62
TheCodedProf2e54a772023-02-14 16:26:47 -050063interface TranscriptMessage {
64 id: string;
65 author: TranscriptAuthor;
66 content?: string;
67 embeds?: TranscriptEmbed[];
68 components?: TranscriptComponent[][];
69 editedTimestamp?: number;
70 createdTimestamp: number;
71 flags?: string[];
TheCodedProf1f675042023-02-16 17:01:29 -050072 attachments?: TranscriptAttachment[];
TheCodedProf2e54a772023-02-14 16:26:47 -050073 stickerURLs?: string[];
74 referencedMessage?: string | [string, string, string];
75}
76
77interface Transcript {
78 type: "ticket" | "purge"
79 guild: string;
80 channel: string;
81 messages: TranscriptMessage[];
82 createdTimestamp: number;
83 createdBy: TranscriptAuthor;
84}
85
pineafan0f5cc782022-08-12 21:55:42 +010086export default async function (interaction: CommandInteraction | MessageComponentInteraction) {
Skyler Grey11236ba2022-08-08 21:13:33 +010087 if (interaction.channel === null) return;
pineafan4e425942022-08-08 22:01:47 +010088 if (!(interaction.channel instanceof TextChannel)) return;
Skyler Grey11236ba2022-08-08 21:13:33 +010089 const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
pineafan813bdf42022-07-24 10:39:10 +010090
Skyler Grey11236ba2022-08-08 21:13:33 +010091 let messages: Message[] = [];
92 let deletedCount: number;
pineafan813bdf42022-07-24 10:39:10 +010093
Skyler Grey11236ba2022-08-08 21:13:33 +010094 do {
95 const fetched = await (interaction.channel as TextChannel).messages.fetch({ limit: 100 });
96 const deleted = await (interaction.channel as TextChannel).bulkDelete(fetched, true);
97 deletedCount = deleted.size;
TheCodedProfbc94a5c2023-01-18 18:59:31 -050098 messages = messages.concat(Array.from(deleted.values() as Iterable<Message>));
TheCodedProf94ff6de2023-02-22 17:47:26 -050099 if (messages.length === 1) messageException(interaction.guild!.id, interaction.channel.id, messages[0]!.id)
Skyler Grey11236ba2022-08-08 21:13:33 +0100100 } while (deletedCount === 100);
101
pineafan63fc5e22022-08-04 22:04:10 +0100102 let out = "";
Skyler Grey75ea9172022-08-06 10:22:23 +0100103 messages.reverse().forEach((message) => {
pineafan813bdf42022-07-24 10:39:10 +0100104 if (!message.author.bot) {
pineafan63fc5e22022-08-04 22:04:10 +0100105 const sentDate = new Date(message.createdTimestamp);
Skyler Grey11236ba2022-08-08 21:13:33 +0100106 out += `${message.author.username}#${message.author.discriminator} (${
107 message.author.id
108 }) [${sentDate.toUTCString()}]\n`;
pineafan63fc5e22022-08-04 22:04:10 +0100109 const lines = message.content.split("\n");
Skyler Grey75ea9172022-08-06 10:22:23 +0100110 lines.forEach((line) => {
111 out += `> ${line}\n`;
112 });
pineafan63fc5e22022-08-04 22:04:10 +0100113 out += "\n\n";
pineafan813bdf42022-07-24 10:39:10 +0100114 }
pineafan63fc5e22022-08-04 22:04:10 +0100115 });
TheCodedProf2e54a772023-02-14 16:26:47 -0500116
TheCodedProf1807fb32023-02-20 14:33:48 -0500117 const interactionMember = await interaction.guild?.members.fetch(interaction.user.id)
TheCodedProf2e54a772023-02-14 16:26:47 -0500118
TheCodedProf1807fb32023-02-20 14:33:48 -0500119 const newOut: Transcript = {
TheCodedProf2e54a772023-02-14 16:26:47 -0500120 type: "ticket",
121 guild: interaction.guild!.id,
122 channel: interaction.channel!.id,
123 messages: [],
124 createdTimestamp: Date.now(),
125 createdBy: {
126 username: interaction.user.username,
127 discriminator: parseInt(interaction.user.discriminator),
128 id: interaction.user.id,
129 topRole: {
130 color: interactionMember?.roles.highest.color ?? 0x000000
131 }
132 }
133 }
134 if(interactionMember?.roles.icon) newOut.createdBy.topRole.badgeURL = interactionMember.roles.icon.iconURL()!;
135 messages.reverse().forEach((message) => {
TheCodedProf1807fb32023-02-20 14:33:48 -0500136 const msg: TranscriptMessage = {
TheCodedProf2e54a772023-02-14 16:26:47 -0500137 id: message.id,
138 author: {
139 username: message.author.username,
140 discriminator: parseInt(message.author.discriminator),
141 id: message.author.id,
142 topRole: {
143 color: message.member!.roles.highest.color
144 }
145 },
146 createdTimestamp: message.createdTimestamp
147 };
148 if (message.member!.roles.icon) msg.author.topRole.badgeURL = message.member!.roles.icon.iconURL()!;
149 if (message.content) msg.content = message.content;
150 if (message.embeds.length > 0) msg.embeds = message.embeds.map(embed => {
TheCodedProf1807fb32023-02-20 14:33:48 -0500151 const obj: TranscriptEmbed = {};
TheCodedProf2e54a772023-02-14 16:26:47 -0500152 if (embed.title) obj.title = embed.title;
153 if (embed.description) obj.description = embed.description;
154 if (embed.fields.length > 0) obj.fields = embed.fields.map(field => {
155 return {
156 name: field.name,
157 value: field.value,
158 inline: field.inline ?? false
159 }
160 });
161 if (embed.footer) obj.footer = {
162 text: embed.footer.text,
163 };
164 if (embed.footer?.iconURL) obj.footer!.iconURL = embed.footer.iconURL;
165 return obj;
166 });
167 if (message.components.length > 0) msg.components = message.components.map(component => component.components.map(child => {
TheCodedProf1807fb32023-02-20 14:33:48 -0500168 const obj: TranscriptComponent = {
TheCodedProf2e54a772023-02-14 16:26:47 -0500169 type: child.type
170 }
171 if (child.type === ComponentType.Button) {
172 obj.style = child.style;
173 obj.label = child.label ?? "";
TheCodedProf362c51d2023-02-14 16:31:33 -0500174 } else if (child.type > 2) {
175 obj.placeholder = child.placeholder ?? "";
TheCodedProf2e54a772023-02-14 16:26:47 -0500176 }
177 return obj
178 }));
179 if (message.editedTimestamp) msg.editedTimestamp = message.editedTimestamp;
TheCodedProf1807fb32023-02-20 14:33:48 -0500180 msg.flags = message.flags.toArray();
TheCodedProf2e54a772023-02-14 16:26:47 -0500181
182 if (message.stickers.size > 0) msg.stickerURLs = message.stickers.map(sticker => sticker.url);
183 if (message.reference) msg.referencedMessage = [message.reference.guildId ?? "", message.reference.channelId, message.reference.messageId ?? ""];
TheCodedProfaa3fe992023-02-25 21:53:09 -0500184 newOut.messages.push(msg);
TheCodedProf2e54a772023-02-14 16:26:47 -0500185 });
186
TheCodedProfaa3fe992023-02-25 21:53:09 -0500187 console.log(newOut);
188
pineafan3a02ea32022-08-11 21:35:04 +0100189 const topic = interaction.channel.topic;
190 let member: GuildMember | null = null;
191 if (topic !== null) {
192 const part = topic.split(" ")[0] ?? null;
193 if (part !== null) member = interaction.guild!.members.cache.get(part) ?? null;
194 }
195 let m: Message;
pineafan813bdf42022-07-24 10:39:10 +0100196 if (out !== "") {
197 const url = await pbClient.createPaste({
198 code: out,
199 expireDate: ExpireDate.Never,
pineafan3a02ea32022-08-11 21:35:04 +0100200 name:
201 `Ticket Transcript ${
202 member ? "for " + member.user.username + "#" + member.user.discriminator + " " : ""
203 }` + `(Created at ${new Date(interaction.channel.createdTimestamp).toDateString()})`,
pineafan63fc5e22022-08-04 22:04:10 +0100204 publicity: Publicity.Unlisted
205 });
pineafan4e425942022-08-08 22:01:47 +0100206 const guildConfig = await client.database.guilds.read(interaction.guild!.id);
pineafan3a02ea32022-08-11 21:35:04 +0100207 m = (await interaction.reply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100208 embeds: [
209 new EmojiEmbed()
210 .setTitle("Transcript")
211 .setDescription(
212 "You can view the transcript using the link below. You can save the link for later" +
213 (guildConfig.logging.logs.channel
214 ? ` or find it in <#${guildConfig.logging.logs.channel}> once you press delete below. After this the channel will be deleted.`
215 : ".")
216 )
217 .setStatus("Success")
218 .setEmoji("CONTROL.DOWNLOAD")
219 ],
220 components: [
PineaFan538d3752023-01-12 21:48:23 +0000221 new ActionRowBuilder<ButtonBuilder>().addComponents([
TheCodedProf21c08592022-09-13 14:14:43 -0400222 new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(url),
223 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100224 .setLabel("Delete")
TheCodedProf21c08592022-09-13 14:14:43 -0400225 .setStyle(ButtonStyle.Danger)
Skyler Grey75ea9172022-08-06 10:22:23 +0100226 .setCustomId("close")
227 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
228 ])
229 ],
230 fetchReply: true
pineafan3a02ea32022-08-11 21:35:04 +0100231 })) as Message;
pineafan813bdf42022-07-24 10:39:10 +0100232 } else {
pineafan3a02ea32022-08-11 21:35:04 +0100233 m = (await interaction.reply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100234 embeds: [
235 new EmojiEmbed()
236 .setTitle("Transcript")
237 .setDescription(
238 "The transcript was empty, so no changes were made. To delete this ticket, press the delete button below."
239 )
240 .setStatus("Success")
241 .setEmoji("CONTROL.DOWNLOAD")
242 ],
243 components: [
PineaFan538d3752023-01-12 21:48:23 +0000244 new ActionRowBuilder<ButtonBuilder>().addComponents([
TheCodedProf21c08592022-09-13 14:14:43 -0400245 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100246 .setLabel("Delete")
TheCodedProf21c08592022-09-13 14:14:43 -0400247 .setStyle(ButtonStyle.Danger)
Skyler Grey75ea9172022-08-06 10:22:23 +0100248 .setCustomId("close")
249 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
250 ])
251 ],
252 fetchReply: true
pineafan3a02ea32022-08-11 21:35:04 +0100253 })) as Message;
pineafan813bdf42022-07-24 10:39:10 +0100254 }
255 let i;
256 try {
PineaFan0d06edc2023-01-17 22:10:31 +0000257 i = await m.awaitMessageComponent({
258 time: 300000,
TheCodedProf267563a2023-01-21 17:00:57 -0500259 filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
PineaFan0d06edc2023-01-17 22:10:31 +0000260 });
TheCodedProf267563a2023-01-21 17:00:57 -0500261 await i.deferUpdate();
Skyler Grey75ea9172022-08-06 10:22:23 +0100262 } catch {
263 return;
264 }
pineafan63fc5e22022-08-04 22:04:10 +0100265 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100266 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100267 type: "ticketDeleted",
268 displayName: "Ticket Deleted",
pineafan813bdf42022-07-24 10:39:10 +0100269 calculateType: "ticketUpdate",
270 color: NucleusColors.red,
pineafan63fc5e22022-08-04 22:04:10 +0100271 emoji: "GUILD.TICKET.CLOSE",
TheCodedProf6ec331b2023-02-20 12:13:06 -0500272 timestamp: Date.now()
pineafan813bdf42022-07-24 10:39:10 +0100273 },
274 list: {
pineafan3a02ea32022-08-11 21:35:04 +0100275 ticketFor: member ? entry(member.id, renderUser(member.user)) : entry(null, "*Unknown*"),
PineaFan538d3752023-01-12 21:48:23 +0000276 deletedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as User)),
TheCodedProf6ec331b2023-02-20 12:13:06 -0500277 deleted: entry(Date.now().toString(), renderDelta(Date.now()))
pineafan813bdf42022-07-24 10:39:10 +0100278 },
279 hidden: {
pineafan4e425942022-08-08 22:01:47 +0100280 guild: interaction.guild!.id
pineafan813bdf42022-07-24 10:39:10 +0100281 }
pineafan63fc5e22022-08-04 22:04:10 +0100282 };
pineafan813bdf42022-07-24 10:39:10 +0100283 log(data);
pineafan63fc5e22022-08-04 22:04:10 +0100284 await interaction.channel.delete();
285 return;
Skyler Grey75ea9172022-08-06 10:22:23 +0100286}