blob: 4fd1202e4efb115c925d9f96ff92887b1b981b44 [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,
TheCodedProf21c08592022-09-13 14:14:43 -04005 ActionRowBuilder,
6 ButtonBuilder,
7 SelectMenuBuilder,
8 ButtonStyle
Skyler Grey75ea9172022-08-06 10:22:23 +01009} from "discord.js";
pineafand5a8e6a2022-07-25 17:38:14 +010010import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafand5a8e6a2022-07-25 17:38:14 +010011import 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 Greyf21323a2022-08-13 23:58:22 +0100179 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100180 if (!filter.check(member)) {
181 applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100182 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100183 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100184 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100185 if (applies) {
186 affected.push(member);
187 }
pineafan63fc5e22022-08-04 22:04:10 +0100188 });
pineafand5a8e6a2022-07-25 17:38:14 +0100189 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100190 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100191 let applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100192 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100193 if (filter.check(member)) {
194 applies = true;
Skyler Greyf21323a2022-08-13 23:58:22 +0100195 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100196 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100197 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100198 if (applies) {
199 affected.push(member);
200 }
pineafan63fc5e22022-08-04 22:04:10 +0100201 });
pineafand5a8e6a2022-07-25 17:38:14 +0100202 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100203 await interaction.editReply({
204 embeds: [
205 new EmojiEmbed()
206 .setTitle("Role all")
207 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100208 (all ? "All of the following must be true:" : "Any of the following must be true") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100209 "\n" +
210 filters
211 .map((f) => {
212 count++;
213 return (
Skyler Grey11236ba2022-08-08 21:13:33 +0100214 (count === 1 ? getEmojiByName("ICONS.FILTER") : all ? "**and** " : "**or** ") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100215 (f.inverted ? "**not** " : "") +
216 `${f.name}`
217 );
218 })
219 .join("\n") +
220 "\n\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100221 `This will affect ${addPlural(affected.length, "member")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100222 )
223 .setEmoji("GUILD.ROLES.CREATE")
224 .setStatus("Success")
225 ],
226 components: [
TheCodedProf21c08592022-09-13 14:14:43 -0400227 new ActionRowBuilder().addComponents([
228 new SelectMenuBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100229 .setOptions(
230 filters.map((f, index) => ({
231 label: (f.inverted ? "(Not) " : "") + f.name,
232 value: index.toString()
233 }))
234 )
235 .setMinValues(1)
236 .setMaxValues(filters.length)
237 .setCustomId("select")
238 .setPlaceholder("Remove a filter")
239 ]),
TheCodedProf21c08592022-09-13 14:14:43 -0400240 new ActionRowBuilder().addComponents([
241 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100242 .setLabel("Apply")
TheCodedProf21c08592022-09-13 14:14:43 -0400243 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100244 .setCustomId("apply")
Skyler Grey11236ba2022-08-08 21:13:33 +0100245 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100246 .setDisabled(affected.length === 0),
TheCodedProf21c08592022-09-13 14:14:43 -0400247 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100248 .setLabel("Add filter")
TheCodedProf21c08592022-09-13 14:14:43 -0400249 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100250 .setCustomId("add")
Skyler Grey11236ba2022-08-08 21:13:33 +0100251 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100252 .setDisabled(filters.length >= 25)
253 ])
254 ]
255 });
pineafan63fc5e22022-08-04 22:04:10 +0100256 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100257 }
pineafanbd02b4a2022-08-05 22:01:38 +0100258 return;
pineafan63fc5e22022-08-04 22:04:10 +0100259};
pineafand5a8e6a2022-07-25 17:38:14 +0100260
PineaFan64486c42022-12-28 09:21:04 +0000261const check = async (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100262 const member = interaction.member as GuildMember;
263 const me = interaction.guild.me!;
pineafan3a02ea32022-08-11 21:35:04 +0100264 if (!me.permissions.has("MANAGE_ROLES")) throw new Error("I do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100265 // Allow the owner to role anyone
pineafan63fc5e22022-08-04 22:04:10 +0100266 if (member.id === interaction.guild.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100267 // Check if the user has manage_roles permission
pineafan3a02ea32022-08-11 21:35:04 +0100268 if (!member.permissions.has("MANAGE_ROLES")) throw new Error("You do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100269 // Allow role
270 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100271};
pineafand5a8e6a2022-07-25 17:38:14 +0100272
273export { command };
274export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100275export { check };