blob: 6d895f5451412290359e77fb998924ad770151a3 [file] [log] [blame]
pineafan8b4b17f2022-02-27 20:42:52 +00001import Discord, { CommandInteraction, GuildChannel, GuildMember, TextChannel } from "discord.js";
pineafan4f164f32022-02-26 22:07:12 +00002import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
3import { WrappedCheck } from "jshaiku";
pineafan8b4b17f2022-02-27 20:42:52 +00004import confirmationMessage from "../../utils/confirmationMessage.js";
5import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
6import keyValueList from "../../utils/generateKeyValueList.js";
7import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan4f164f32022-02-26 22:07:12 +00008
9const command = (builder: SlashCommandSubcommandBuilder) =>
10 builder
11 .setName("purge")
pineafan8b4b17f2022-02-27 20:42:52 +000012 .setDescription("Bulk deletes messages in a channel")
13 .addIntegerOption(option => option
14 .setName("amount")
15 .setDescription("The amount of messages to delete")
16 .setRequired(false)
17 .setMinValue(1)
18 .setMaxValue(50))
19 .addChannelOption(option => option.setName("channel").setDescription("The channel to purge messages from").setRequired(false))
20 .addUserOption(option => option.setName("user").setDescription("The user to purge messages from").setRequired(false))
21 .addStringOption(option => option.setName("reason").setDescription("The reason for the purge").setRequired(false))
pineafan4f164f32022-02-26 22:07:12 +000022
pineafan8b4b17f2022-02-27 20:42:52 +000023const callback = async (interaction: CommandInteraction) => {
24 let channel = (interaction.options.getChannel("channel") as GuildChannel) ?? interaction.channel
25 let thischannel
26 if ((interaction.options.getChannel("channel") as GuildChannel) == null) {
27 thischannel = true
28 } else {
29 thischannel = (interaction.options.getChannel("channel") as GuildChannel).id == interaction.channel.id
30 }
31 if (!(["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes(channel.type.toString()))) {
32 return await interaction.reply({
33 embeds: [
34 new EmojiEmbed()
35 .setEmoji("CHANNEL.PURGE.RED")
36 .setTitle("Purge")
37 .setDescription("You cannot purge this channel")
38 .setStatus("Danger")
39 ],
40 components: [],
41 ephemeral: true,
42 })
43 }
44 // TODO:[Modals] Replace this with a modal
45 if ( !interaction.options.getInteger("amount") ) {
46 await interaction.reply({
47 embeds: [
48 new EmojiEmbed()
49 .setEmoji("CHANNEL.PURGE.RED")
50 .setTitle("Purge")
51 .setDescription("Select how many messages to delete")
52 .setStatus("Danger")
53 ],
54 components: [],
55 ephemeral: true,
56 fetchReply: true
57 })
58 let deleted = []
59 while (true) {
60 let m = await interaction.editReply({
61 embeds: [
62 new EmojiEmbed()
63 .setEmoji("CHANNEL.PURGE.RED")
64 .setTitle("Purge")
65 .setDescription("Select how many messages to delete. You can continue clicking until all messages are cleared.")
66 .setStatus("Danger")
67 ],
68 components: [
69 new Discord.MessageActionRow().addComponents([
70 new Discord.MessageButton()
71 .setCustomId("1")
72 .setLabel("1")
73 .setStyle("SECONDARY"),
74 new Discord.MessageButton()
75 .setCustomId("3")
76 .setLabel("3")
77 .setStyle("SECONDARY"),
78 new Discord.MessageButton()
79 .setCustomId("5")
80 .setLabel("5")
81 .setStyle("SECONDARY")
82 ]),
83 new Discord.MessageActionRow().addComponents([
84 new Discord.MessageButton()
85 .setCustomId("10")
86 .setLabel("10")
87 .setStyle("SECONDARY"),
88 new Discord.MessageButton()
89 .setCustomId("25")
90 .setLabel("25")
91 .setStyle("SECONDARY"),
92 new Discord.MessageButton()
93 .setCustomId("50")
94 .setLabel("50")
95 .setStyle("SECONDARY")
96 ]),
97 new Discord.MessageActionRow().addComponents([
98 new Discord.MessageButton()
99 .setCustomId("done")
100 .setLabel("Done")
101 .setStyle("SUCCESS")
102 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
103 ])
104 ]
105 })
106 let component;
107 try {
108 component = await (m as Discord.Message).awaitMessageComponent({filter: (m) => m.user.id === interaction.user.id, time: 2.5 * 60 * 1000});
109 } catch (e) { break; }
110 component.deferUpdate();
111 if (component.customId === "done") break;
112 let amount;
113 try { amount = parseInt(component.customId); } catch { break; }
114 await (channel as TextChannel).bulkDelete(amount, true); // TODO: Add to deleted list | TODO: Support for users
115 }
116 if (deleted.length === 0) return await interaction.editReply({
117 embeds: [
118 new EmojiEmbed()
119 .setEmoji("CHANNEL.PURGE.RED")
120 .setTitle("Purge")
121 .setDescription("No messages were deleted")
122 .setStatus("Danger")
123 ],
124 components: []
125 })
126 return await interaction.editReply({
127 embeds: [
128 new EmojiEmbed()
129 .setEmoji("CHANNEL.PURGE.GREEN")
130 .setTitle("Purge")
131 .setDescription(`Deleted ${deleted.length} messages`)
132 .setStatus("Success")
133 ],
134 components: []
135 })
136 } else {
137 if (await new confirmationMessage(interaction)
138 .setEmoji("CHANNEL.PURGE.RED")
139 .setTitle("Purge")
140 .setDescription(keyValueList({
141 "channel": `<#${channel.id}> (${(channel as GuildChannel).name})` + (thischannel ? " [This channel]" : ""),
142 "amount": interaction.options.getInteger("amount").toString(),
143 "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
144 }))
145 .setColor("Danger")
146 // pluralize("day", interaction.options.getInteger("amount"))
147 // const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
148 .send()) {
149 try {
150 let messages = await (channel as TextChannel).bulkDelete(interaction.options.getInteger("amount"), true) // TODO: Support for users
151 let out = ""
152 messages.reverse().forEach(message => {
153 out += `${message.author.username}#${message.author.discriminator} (${message.author.id})\n`
154 let lines = message.content.split("\n")
155 lines.forEach(line => {out += `> ${line}\n`}) // TODO: Humanize timestamp
156 out += `\n\n`
157 }) // TODO: Upload as file
158 await interaction.editReply({embeds: [new EmojiEmbed()
159 .setEmoji(`CHANNEL.PURGE.GREEN`)
160 .setTitle(`Purge`)
161 .setDescription("Messages cleared")
162 .setStatus("Success")
163 ], components: []})
164 } catch {
165 await interaction.editReply({embeds: [new EmojiEmbed()
166 .setEmoji("CHANNEL.PURGE.RED")
167 .setTitle(`Purge`)
168 .setDescription("Something went wrong and no messages were deleted")
169 .setStatus("Danger")
170 ], components: []})
171 }
172 } else {
173 await interaction.editReply({embeds: [new EmojiEmbed()
174 .setEmoji("CHANNEL.PURGE.GREEN")
175 .setTitle(`Purge`)
176 .setDescription("No changes were made")
177 .setStatus("Success")
178 ], components: []})
179 }
180 }
pineafan4f164f32022-02-26 22:07:12 +0000181}
182
183const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
pineafan8b4b17f2022-02-27 20:42:52 +0000184 // Allow the owner to purge
185 if ((interaction.member as GuildMember).id == interaction.guild.ownerId) return true
186 // Check if the user has manage_messages permission
187 if (! (interaction.member as GuildMember).permissions.has("MANAGE_MESSAGES")) throw "You do not have the `manage_messages` permission";
188 // Check if nucleus has the manage_messages permission
189 if (! interaction.guild.me.permissions.has("MANAGE_MESSAGES")) throw "I do not have the `manage_messages` permission";
190 // Allow warn
191 return true
pineafan4f164f32022-02-26 22:07:12 +0000192}
193
pineafan8b4b17f2022-02-27 20:42:52 +0000194export { command, callback, check };