blob: ae59e7ef9b1beb168e2e9bf313736e158185b50a [file] [log] [blame]
pineafan8b4b17f2022-02-27 20:42:52 +00001import Discord, { CommandInteraction, GuildMember, MessageActionRow } from "discord.js";
2import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
3import { WrappedCheck } from "jshaiku";
4import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
5import getEmojiByName from "../../utils/getEmojiByName.js";
6import confirmationMessage from "../../utils/confirmationMessage.js";
7import keyValueList from "../../utils/generateKeyValueList.js";
8import humanizeDuration from "humanize-duration";
9
10const command = (builder: SlashCommandSubcommandBuilder) =>
11 builder
12 .setName("mute")
13 .setDescription("Mutes a member using Discord's \"Timeout\" feature")
14 .addUserOption(option => option.setName("user").setDescription("The user to mute").setRequired(true))
15 .addIntegerOption(option => option.setName("days").setDescription("The number of days to mute the user for | Default 0").setMinValue(0).setMaxValue(27).setRequired(false))
16 .addIntegerOption(option => option.setName("hours").setDescription("The number of hours to mute the user for | Default 0").setMinValue(0).setMaxValue(23).setRequired(false))
17 .addIntegerOption(option => option.setName("minutes").setDescription("The number of minutes to mute the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
18 .addIntegerOption(option => option.setName("seconds").setDescription("The number of seconds to mute the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
19 .addStringOption(option => option.setName("reason").setDescription("The reason for the mute").setRequired(false))
20 .addStringOption(option => option.setName("notify").setDescription("The user to notify they have been muted").setRequired(false))
21 // TODO: notify the user when the mute is lifted
22
23const callback = async (interaction: CommandInteraction) => {
24 const user = interaction.options.getMember("user") as GuildMember
25 const reason = interaction.options.getString("reason")
26 const time = {
27 days: interaction.options.getInteger("days") || 0,
28 hours: interaction.options.getInteger("hours") || 0,
29 minutes: interaction.options.getInteger("minutes") || 0,
30 seconds: interaction.options.getInteger("seconds") || 0
31 }
32 let muteTime = (time.days * 24 * 60 * 60) + (time.hours * 60 * 60) + (time.minutes * 60) + time.seconds
33 if (muteTime == 0) {
34 let m = await interaction.reply({embeds: [
35 new EmojiEmbed()
36 .setEmoji("PUNISH.MUTE.GREEN")
37 .setTitle("Mute")
38 .setDescription("How long should the user be muted")
39 .setStatus("Success")
40 ], components: [
41 new MessageActionRow().addComponents([
42 new Discord.MessageButton()
43 .setCustomId("1m")
44 .setLabel("1 Minute")
45 .setStyle("SECONDARY"),
46 new Discord.MessageButton()
47 .setCustomId("10m")
48 .setLabel("10 Minutes")
49 .setStyle("SECONDARY"),
50 new Discord.MessageButton()
51 .setCustomId("30m")
52 .setLabel("30 Minutes")
53 .setStyle("SECONDARY"),
54 new Discord.MessageButton()
55 .setCustomId("1h")
56 .setLabel("1 Hour")
57 .setStyle("SECONDARY")
58 ]),
59 new MessageActionRow().addComponents([
60 new Discord.MessageButton()
61 .setCustomId("6h")
62 .setLabel("6 Hours")
63 .setStyle("SECONDARY"),
64 new Discord.MessageButton()
65 .setCustomId("12h")
66 .setLabel("12 Hours")
67 .setStyle("SECONDARY"),
68 new Discord.MessageButton()
69 .setCustomId("1d")
70 .setLabel("1 Day")
71 .setStyle("SECONDARY"),
72 new Discord.MessageButton()
73 .setCustomId("1w")
74 .setLabel("1 Week")
75 .setStyle("SECONDARY")
76 ]),
77 new MessageActionRow().addComponents([
78 new Discord.MessageButton()
79 .setCustomId("cancel")
80 .setLabel("Cancel")
81 .setStyle("DANGER")
82 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
83 ])
84 ], ephemeral: true, fetchReply: true})
85 let component;
86 try {
87 component = await (m as Discord.Message).awaitMessageComponent({filter: (m) => m.user.id === interaction.user.id, time: 2.5 * 60 * 1000});
88 } catch { return }
89 component.deferUpdate();
90 if (component.customId == "cancel") return interaction.editReply({embeds: [new EmojiEmbed()
91 .setEmoji("PUNISH.MUTE.RED")
92 .setTitle("Mute")
93 .setDescription("Mute cancelled")
94 .setStatus("Danger")
95 ]})
96 switch (component.customId) {
97 case "1m": { muteTime = 60; break; }
98 case "10m": { muteTime = 60 * 10; break; }
99 case "30m": { muteTime = 60 * 30; break; }
100 case "1h": { muteTime = 60 * 60; break; }
101 case "6h": { muteTime = 60 * 60 * 6; break; }
102 case "12h": { muteTime = 60 * 60 * 12; break; }
103 case "1d": { muteTime = 60 * 60 * 24; break; }
104 case "1w": { muteTime = 60 * 60 * 24 * 7; break; }
105 }
106 } else {
107 await interaction.reply({embeds: [
108 new EmojiEmbed()
109 .setEmoji("PUNISH.MUTE.GREEN")
110 .setTitle("Mute")
111 .setDescription("Loading...")
112 .setStatus("Success")
113 ], ephemeral: true, fetchReply: true})
114 }
115 if (await new confirmationMessage(interaction)
116 .setEmoji("PUNISH.MUTE.RED")
117 .setTitle("Mute")
118 .setDescription(keyValueList({
119 "user": `<@!${user.id}> (${user.user.username})`,
120 "time": `${humanizeDuration(muteTime * 1000, {round: true})}`,
121 "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
122 })
123 + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
124 + `Are you sure you want to mute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
125 .setColor("Danger")
126// pluralize("day", interaction.options.getInteger("delete"))
127// const pluralize = (word: string, count: number) => { return count === 1 ? word : word + "s" }
128 .send(true)) {
129 let dmd = false
130 try {
131 if (interaction.options.getString("notify") != "no") {
132 await (interaction.options.getMember("user") as GuildMember).send({
133 embeds: [new EmojiEmbed()
134 .setEmoji("PUNISH.MUTE.RED")
135 .setTitle("Muted")
136 .setDescription(`You have been muted in ${interaction.guild.name}` +
137 (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : " with no reason provided.\n\n" +
138 `You will be unmuted at: <t:${Math.round((new Date).getTime() / 1000) + muteTime}:D> at <t:${Math.round((new Date).getTime() / 1000) + muteTime}:T> (<t:${Math.round((new Date).getTime() / 1000) + muteTime}:R>)`))
139 .setStatus("Danger")
140 ]
141 })
142 dmd = true
143 }
144 } catch {}
145 try {
146 (interaction.options.getMember("user") as GuildMember).timeout(muteTime * 1000, interaction.options.getString("reason") || "No reason provided")
147 let failed = (dmd == false && interaction.options.getString("notify") != "no")
148 await interaction.editReply({embeds: [new EmojiEmbed()
149 .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
150 .setTitle(`Mute`)
151 .setDescription("The member was muted" + (failed ? ", but could not be notified" : ""))
152 .setStatus(failed ? "Warning" : "Success")
153 ], components: []})
154 } catch {
155 await interaction.editReply({embeds: [new EmojiEmbed()
156 .setEmoji("PUNISH.MUTE.RED")
157 .setTitle(`Mute`)
158 .setDescription("Something went wrong and the user was not kicked")
159 .setStatus("Danger")
160 ], components: []})
161 }
162 } else {
163 await interaction.editReply({embeds: [new EmojiEmbed()
164 .setEmoji("PUNISH.MUTE.GREEN")
165 .setTitle(`Mute`)
166 .setDescription("No changes were made")
167 .setStatus("Success")
168 ], components: []})
169 }
170}
171
172const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
173 // Check if Nucleus can mute the member
174 if (! (interaction.guild.me.roles.highest.position > (interaction.member as GuildMember).roles.highest.position)) throw "I do not have a role higher than that member"
175 // Check if Nucleus has permission to mute
176 if (! interaction.guild.me.permissions.has("MODERATE_MEMBERS")) throw "I do not have the `moderate_members` permission";
177 // Do not allow the user to have admin or be the owner
178 if ((interaction.options.getMember("user") as GuildMember).permissions.has("ADMINISTRATOR") || (interaction.options.getMember("user") as GuildMember).id == interaction.guild.ownerId) throw "You cannot mute an admin or the owner"
179 // Do not allow muting Nucleus
180 if ((interaction.member as GuildMember).id == interaction.guild.me.id) throw "I cannot mute myself"
181 // Allow the owner to mute anyone
182 if ((interaction.member as GuildMember).id == interaction.guild.ownerId) return true
183 // Check if the user has moderate_members permission
184 if (! (interaction.member as GuildMember).permissions.has("MODERATE_MEMBERS")) throw "You do not have the `moderate_members` permission";
185 // Check if the user is below on the role list
186 if (! ((interaction.member as GuildMember).roles.highest.position > (interaction.options.getMember("user") as GuildMember).roles.highest.position)) throw "You do not have a role higher than that member"
187 // Allow mute
188 return true
189}
190
191export { command, callback, check };