blob: 758947b08b45ff68b07cb3bc7dc1571a6bef5561 [file] [log] [blame]
pineafan63fc5e22022-08-04 22:04:10 +01001import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
Skyler Grey75ea9172022-08-06 10:22:23 +01002import Discord, {
3 CommandInteraction,
4 GuildMember,
5 MessageActionRow,
6 MessageButton,
7 MessageSelectMenu
8} from "discord.js";
pineafand5a8e6a2022-07-25 17:38:14 +01009import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
10import { WrappedCheck } from "jshaiku";
11import EmojiEmbed from "../utils/generateEmojiEmbed.js";
12import getEmojiByName from "../utils/getEmojiByName.js";
13import addPlural from "../utils/plurals.js";
14import client from "../utils/client.js";
15
16const command = (builder: SlashCommandSubcommandBuilder) =>
17 builder // TODO: DON'T RELEASE THIS
pineafan63fc5e22022-08-04 22:04:10 +010018 .setName("all")
19 .setDescription("Gives or removes a role from everyone");
pineafand5a8e6a2022-07-25 17:38:14 +010020
21class Filter {
22 name: string;
23 data: object;
24 checkFunction: (member) => boolean;
pineafan63fc5e22022-08-04 22:04:10 +010025 inverted = false;
Skyler Grey11236ba2022-08-08 21:13:33 +010026 constructor(name: (data) => string | string, data: object, check: (member) => boolean) {
Skyler Grey75ea9172022-08-06 10:22:23 +010027 if (typeof name === "function") {
28 this.name = name(data);
29 } else {
30 this.name = name;
31 }
pineafand5a8e6a2022-07-25 17:38:14 +010032 this.data = data;
33 this.checkFunction = check;
34 }
Skyler Grey75ea9172022-08-06 10:22:23 +010035 flip() {
36 this.inverted = true;
37 return this;
38 }
pineafand5a8e6a2022-07-25 17:38:14 +010039 check(member) {
pineafan63fc5e22022-08-04 22:04:10 +010040 if (this.inverted) return !this.checkFunction(member);
41 else return this.checkFunction(member);
pineafand5a8e6a2022-07-25 17:38:14 +010042 }
43}
44
45const filterList = {
46 member: {
47 render: "Member",
48 has: {
49 render: "has",
Skyler Grey75ea9172022-08-06 10:22:23 +010050 role: (role) =>
51 new Filter(
52 (data) => `Member has role <@&${data.role}>`,
53 { role: role, type: Discord.Role, render: "role" },
54 (member) => {
55 return member.roles.cache.has(role);
56 }
57 )
pineafand5a8e6a2022-07-25 17:38:14 +010058 },
59 joined: {
60 render: "joined",
Skyler Grey75ea9172022-08-06 10:22:23 +010061 before: (date) =>
62 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +010063 (_data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +010064 { date: date, type: Date, render: "before" },
65 (member) => {
66 return member.joinedTimestamp < date.getTime();
67 }
68 )
pineafand5a8e6a2022-07-25 17:38:14 +010069 },
70 nickname: {
71 render: "Nickname",
Skyler Grey75ea9172022-08-06 10:22:23 +010072 set: () =>
73 new Filter(
74 (_data) => 'Member has a nickname set"',
75 { render: "set" },
76 (member) => {
77 return member.nickname !== null;
78 }
79 ),
80 includes: (name) =>
81 new Filter(
82 (_data) => `Nickname includes "${name}"`,
83 { nickname: name, type: String, render: "includes" },
84 (member) => {
85 return member.displayName.includes(name);
86 }
87 ),
88 startsWith: (name) =>
89 new Filter(
90 (_data) => `Nickname starts with "${name}"`,
91 { nickname: name, type: String, render: "starts with" },
92 (member) => {
93 return member.displayName.startsWith(name);
94 }
95 ),
96 endsWith: (name) =>
97 new Filter(
98 (_data) => `Nickname ends with "${name}"`,
99 { nickname: name, type: String, render: "ends with" },
100 (member) => {
101 return member.displayName.endsWith(name);
102 }
103 )
pineafand5a8e6a2022-07-25 17:38:14 +0100104 }
105 },
106 account: {
107 render: "Account",
108 created: {
109 render: "created",
Skyler Grey75ea9172022-08-06 10:22:23 +0100110 before: (date) =>
111 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +0100112 (_data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +0100113 { date: date, type: Date, render: "before" },
114 (member) => {
115 return member.user.createdTimestamp < date.getTime();
116 }
117 )
pineafand5a8e6a2022-07-25 17:38:14 +0100118 },
119 is: {
120 render: "is",
Skyler Grey75ea9172022-08-06 10:22:23 +0100121 human: () =>
122 new Filter(
123 (_data) => "Member is a human",
124 { human: true, render: "human" },
125 (member) => {
126 return !member.bot;
127 }
128 )
pineafand5a8e6a2022-07-25 17:38:14 +0100129 },
130 username: {
131 render: "Username",
Skyler Grey75ea9172022-08-06 10:22:23 +0100132 includes: (name) =>
133 new Filter(
134 (_data) => `Nickname includes "${name}"`,
135 { nickname: name, type: String, render: "includes" },
136 (member) => {
137 return member.user.name.includes(name);
138 }
139 ),
140 startsWith: (name) =>
141 new Filter(
142 (_data) => `Nickname starts with "${name}"`,
143 { nickname: name, type: String, render: "starts with" },
144 (member) => {
145 return member.user.name.startsWith(name);
146 }
147 ),
148 endsWith: (name) =>
149 new Filter(
150 (_data) => `Nickname ends with "${name}"`,
151 { nickname: name, type: String, render: "ends with" },
152 (member) => {
153 return member.user.name.endsWith(name);
154 }
155 )
pineafand5a8e6a2022-07-25 17:38:14 +0100156 }
157 }
pineafan63fc5e22022-08-04 22:04:10 +0100158};
pineafand5a8e6a2022-07-25 17:38:14 +0100159
pineafan3a02ea32022-08-11 21:35:04 +0100160const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100161 await interaction.reply({
162 embeds: LoadingEmbed,
163 ephemeral: true,
164 fetchReply: true
165 });
pineafan63fc5e22022-08-04 22:04:10 +0100166 const filters: Filter[] = [
pineafand5a8e6a2022-07-25 17:38:14 +0100167 filterList.member.has.role("959901346000154674"),
168 filterList.member.nickname.startsWith("Pinea"),
169 filterList.member.joined.before(new Date(2022, 1)).flip()
pineafan63fc5e22022-08-04 22:04:10 +0100170 ];
171 const all = true;
pineafand5a8e6a2022-07-25 17:38:14 +0100172 while (true) {
173 let count = 0;
pineafan63fc5e22022-08-04 22:04:10 +0100174 const affected = [];
175 const members = interaction.guild.members.cache;
pineafand5a8e6a2022-07-25 17:38:14 +0100176 if (all) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100177 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100178 let applies = true;
Skyler Grey75ea9172022-08-06 10:22:23 +0100179 filters.forEach((filter) => {
180 if (!filter.check(member)) {
181 applies = false;
182 }
183 });
184 if (applies) {
185 affected.push(member);
186 }
pineafan63fc5e22022-08-04 22:04:10 +0100187 });
pineafand5a8e6a2022-07-25 17:38:14 +0100188 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100189 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100190 let applies = false;
Skyler Grey75ea9172022-08-06 10:22:23 +0100191 filters.forEach((filter) => {
192 if (filter.check(member)) {
193 applies = true;
194 }
195 });
196 if (applies) {
197 affected.push(member);
198 }
pineafan63fc5e22022-08-04 22:04:10 +0100199 });
pineafand5a8e6a2022-07-25 17:38:14 +0100200 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100201 await interaction.editReply({
202 embeds: [
203 new EmojiEmbed()
204 .setTitle("Role all")
205 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100206 (all ? "All of the following must be true:" : "Any of the following must be true") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100207 "\n" +
208 filters
209 .map((f) => {
210 count++;
211 return (
Skyler Grey11236ba2022-08-08 21:13:33 +0100212 (count === 1 ? getEmojiByName("ICONS.FILTER") : all ? "**and** " : "**or** ") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100213 (f.inverted ? "**not** " : "") +
214 `${f.name}`
215 );
216 })
217 .join("\n") +
218 "\n\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100219 `This will affect ${addPlural(affected.length, "member")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100220 )
221 .setEmoji("GUILD.ROLES.CREATE")
222 .setStatus("Success")
223 ],
224 components: [
225 new MessageActionRow().addComponents([
226 new MessageSelectMenu()
227 .setOptions(
228 filters.map((f, index) => ({
229 label: (f.inverted ? "(Not) " : "") + f.name,
230 value: index.toString()
231 }))
232 )
233 .setMinValues(1)
234 .setMaxValues(filters.length)
235 .setCustomId("select")
236 .setPlaceholder("Remove a filter")
237 ]),
238 new MessageActionRow().addComponents([
239 new MessageButton()
240 .setLabel("Apply")
241 .setStyle("PRIMARY")
242 .setCustomId("apply")
Skyler Grey11236ba2022-08-08 21:13:33 +0100243 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100244 .setDisabled(affected.length === 0),
245 new MessageButton()
246 .setLabel("Add filter")
247 .setStyle("PRIMARY")
248 .setCustomId("add")
Skyler Grey11236ba2022-08-08 21:13:33 +0100249 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100250 .setDisabled(filters.length >= 25)
251 ])
252 ]
253 });
pineafan63fc5e22022-08-04 22:04:10 +0100254 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100255 }
pineafanbd02b4a2022-08-05 22:01:38 +0100256 return;
pineafan63fc5e22022-08-04 22:04:10 +0100257};
pineafand5a8e6a2022-07-25 17:38:14 +0100258
Skyler Grey11236ba2022-08-08 21:13:33 +0100259const check = async (interaction: CommandInteraction, _defaultCheck: WrappedCheck) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100260 const member = interaction.member as GuildMember;
261 const me = interaction.guild.me!;
pineafan3a02ea32022-08-11 21:35:04 +0100262 if (!me.permissions.has("MANAGE_ROLES")) throw new Error("I do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100263 // Allow the owner to role anyone
pineafan63fc5e22022-08-04 22:04:10 +0100264 if (member.id === interaction.guild.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100265 // Check if the user has manage_roles permission
pineafan3a02ea32022-08-11 21:35:04 +0100266 if (!member.permissions.has("MANAGE_ROLES")) throw new Error("You do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100267 // Allow role
268 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100269};
pineafand5a8e6a2022-07-25 17:38:14 +0100270
271export { command };
272export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100273export { check };