blob: 141d061e6e4ca88ec054ef7bb67f5281fe901898 [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";
11import { WrappedCheck } from "jshaiku";
12import EmojiEmbed from "../utils/generateEmojiEmbed.js";
13import getEmojiByName from "../utils/getEmojiByName.js";
14import addPlural from "../utils/plurals.js";
15import client from "../utils/client.js";
16
17const command = (builder: SlashCommandSubcommandBuilder) =>
18 builder // TODO: DON'T RELEASE THIS
pineafan63fc5e22022-08-04 22:04:10 +010019 .setName("all")
20 .setDescription("Gives or removes a role from everyone");
pineafand5a8e6a2022-07-25 17:38:14 +010021
22class Filter {
23 name: string;
24 data: object;
25 checkFunction: (member) => boolean;
pineafan63fc5e22022-08-04 22:04:10 +010026 inverted = false;
Skyler Grey11236ba2022-08-08 21:13:33 +010027 constructor(name: (data) => string | string, data: object, check: (member) => boolean) {
Skyler Grey75ea9172022-08-06 10:22:23 +010028 if (typeof name === "function") {
29 this.name = name(data);
30 } else {
31 this.name = name;
32 }
pineafand5a8e6a2022-07-25 17:38:14 +010033 this.data = data;
34 this.checkFunction = check;
35 }
Skyler Grey75ea9172022-08-06 10:22:23 +010036 flip() {
37 this.inverted = true;
38 return this;
39 }
pineafand5a8e6a2022-07-25 17:38:14 +010040 check(member) {
pineafan63fc5e22022-08-04 22:04:10 +010041 if (this.inverted) return !this.checkFunction(member);
42 else return this.checkFunction(member);
pineafand5a8e6a2022-07-25 17:38:14 +010043 }
44}
45
46const filterList = {
47 member: {
48 render: "Member",
49 has: {
50 render: "has",
Skyler Grey75ea9172022-08-06 10:22:23 +010051 role: (role) =>
52 new Filter(
53 (data) => `Member has role <@&${data.role}>`,
54 { role: role, type: Discord.Role, render: "role" },
55 (member) => {
56 return member.roles.cache.has(role);
57 }
58 )
pineafand5a8e6a2022-07-25 17:38:14 +010059 },
60 joined: {
61 render: "joined",
Skyler Grey75ea9172022-08-06 10:22:23 +010062 before: (date) =>
63 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +010064 (_data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +010065 { date: date, type: Date, render: "before" },
66 (member) => {
67 return member.joinedTimestamp < date.getTime();
68 }
69 )
pineafand5a8e6a2022-07-25 17:38:14 +010070 },
71 nickname: {
72 render: "Nickname",
Skyler Grey75ea9172022-08-06 10:22:23 +010073 set: () =>
74 new Filter(
75 (_data) => 'Member has a nickname set"',
76 { render: "set" },
77 (member) => {
78 return member.nickname !== null;
79 }
80 ),
81 includes: (name) =>
82 new Filter(
83 (_data) => `Nickname includes "${name}"`,
84 { nickname: name, type: String, render: "includes" },
85 (member) => {
86 return member.displayName.includes(name);
87 }
88 ),
89 startsWith: (name) =>
90 new Filter(
91 (_data) => `Nickname starts with "${name}"`,
92 { nickname: name, type: String, render: "starts with" },
93 (member) => {
94 return member.displayName.startsWith(name);
95 }
96 ),
97 endsWith: (name) =>
98 new Filter(
99 (_data) => `Nickname ends with "${name}"`,
100 { nickname: name, type: String, render: "ends with" },
101 (member) => {
102 return member.displayName.endsWith(name);
103 }
104 )
pineafand5a8e6a2022-07-25 17:38:14 +0100105 }
106 },
107 account: {
108 render: "Account",
109 created: {
110 render: "created",
Skyler Grey75ea9172022-08-06 10:22:23 +0100111 before: (date) =>
112 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +0100113 (_data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +0100114 { date: date, type: Date, render: "before" },
115 (member) => {
116 return member.user.createdTimestamp < date.getTime();
117 }
118 )
pineafand5a8e6a2022-07-25 17:38:14 +0100119 },
120 is: {
121 render: "is",
Skyler Grey75ea9172022-08-06 10:22:23 +0100122 human: () =>
123 new Filter(
124 (_data) => "Member is a human",
125 { human: true, render: "human" },
126 (member) => {
127 return !member.bot;
128 }
129 )
pineafand5a8e6a2022-07-25 17:38:14 +0100130 },
131 username: {
132 render: "Username",
Skyler Grey75ea9172022-08-06 10:22:23 +0100133 includes: (name) =>
134 new Filter(
135 (_data) => `Nickname includes "${name}"`,
136 { nickname: name, type: String, render: "includes" },
137 (member) => {
138 return member.user.name.includes(name);
139 }
140 ),
141 startsWith: (name) =>
142 new Filter(
143 (_data) => `Nickname starts with "${name}"`,
144 { nickname: name, type: String, render: "starts with" },
145 (member) => {
146 return member.user.name.startsWith(name);
147 }
148 ),
149 endsWith: (name) =>
150 new Filter(
151 (_data) => `Nickname ends with "${name}"`,
152 { nickname: name, type: String, render: "ends with" },
153 (member) => {
154 return member.user.name.endsWith(name);
155 }
156 )
pineafand5a8e6a2022-07-25 17:38:14 +0100157 }
158 }
pineafan63fc5e22022-08-04 22:04:10 +0100159};
pineafand5a8e6a2022-07-25 17:38:14 +0100160
pineafan3a02ea32022-08-11 21:35:04 +0100161const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100162 await interaction.reply({
163 embeds: LoadingEmbed,
164 ephemeral: true,
165 fetchReply: true
166 });
pineafan63fc5e22022-08-04 22:04:10 +0100167 const filters: Filter[] = [
pineafand5a8e6a2022-07-25 17:38:14 +0100168 filterList.member.has.role("959901346000154674"),
169 filterList.member.nickname.startsWith("Pinea"),
170 filterList.member.joined.before(new Date(2022, 1)).flip()
pineafan63fc5e22022-08-04 22:04:10 +0100171 ];
172 const all = true;
pineafand5a8e6a2022-07-25 17:38:14 +0100173 while (true) {
174 let count = 0;
pineafan63fc5e22022-08-04 22:04:10 +0100175 const affected = [];
176 const members = interaction.guild.members.cache;
pineafand5a8e6a2022-07-25 17:38:14 +0100177 if (all) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100178 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100179 let applies = true;
Skyler Greyf21323a2022-08-13 23:58:22 +0100180 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100181 if (!filter.check(member)) {
182 applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100183 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100184 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100185 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100186 if (applies) {
187 affected.push(member);
188 }
pineafan63fc5e22022-08-04 22:04:10 +0100189 });
pineafand5a8e6a2022-07-25 17:38:14 +0100190 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100191 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100192 let applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100193 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100194 if (filter.check(member)) {
195 applies = true;
Skyler Greyf21323a2022-08-13 23:58:22 +0100196 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100197 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100198 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100199 if (applies) {
200 affected.push(member);
201 }
pineafan63fc5e22022-08-04 22:04:10 +0100202 });
pineafand5a8e6a2022-07-25 17:38:14 +0100203 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100204 await interaction.editReply({
205 embeds: [
206 new EmojiEmbed()
207 .setTitle("Role all")
208 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100209 (all ? "All of the following must be true:" : "Any of the following must be true") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100210 "\n" +
211 filters
212 .map((f) => {
213 count++;
214 return (
Skyler Grey11236ba2022-08-08 21:13:33 +0100215 (count === 1 ? getEmojiByName("ICONS.FILTER") : all ? "**and** " : "**or** ") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100216 (f.inverted ? "**not** " : "") +
217 `${f.name}`
218 );
219 })
220 .join("\n") +
221 "\n\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100222 `This will affect ${addPlural(affected.length, "member")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100223 )
224 .setEmoji("GUILD.ROLES.CREATE")
225 .setStatus("Success")
226 ],
227 components: [
TheCodedProf21c08592022-09-13 14:14:43 -0400228 new ActionRowBuilder().addComponents([
229 new SelectMenuBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100230 .setOptions(
231 filters.map((f, index) => ({
232 label: (f.inverted ? "(Not) " : "") + f.name,
233 value: index.toString()
234 }))
235 )
236 .setMinValues(1)
237 .setMaxValues(filters.length)
238 .setCustomId("select")
239 .setPlaceholder("Remove a filter")
240 ]),
TheCodedProf21c08592022-09-13 14:14:43 -0400241 new ActionRowBuilder().addComponents([
242 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100243 .setLabel("Apply")
TheCodedProf21c08592022-09-13 14:14:43 -0400244 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100245 .setCustomId("apply")
Skyler Grey11236ba2022-08-08 21:13:33 +0100246 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100247 .setDisabled(affected.length === 0),
TheCodedProf21c08592022-09-13 14:14:43 -0400248 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100249 .setLabel("Add filter")
TheCodedProf21c08592022-09-13 14:14:43 -0400250 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100251 .setCustomId("add")
Skyler Grey11236ba2022-08-08 21:13:33 +0100252 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id")))
Skyler Grey75ea9172022-08-06 10:22:23 +0100253 .setDisabled(filters.length >= 25)
254 ])
255 ]
256 });
pineafan63fc5e22022-08-04 22:04:10 +0100257 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100258 }
pineafanbd02b4a2022-08-05 22:01:38 +0100259 return;
pineafan63fc5e22022-08-04 22:04:10 +0100260};
pineafand5a8e6a2022-07-25 17:38:14 +0100261
Skyler Grey11236ba2022-08-08 21:13:33 +0100262const check = async (interaction: CommandInteraction, _defaultCheck: WrappedCheck) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100263 const member = interaction.member as GuildMember;
264 const me = interaction.guild.me!;
pineafan3a02ea32022-08-11 21:35:04 +0100265 if (!me.permissions.has("MANAGE_ROLES")) throw new Error("I do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100266 // Allow the owner to role anyone
pineafan63fc5e22022-08-04 22:04:10 +0100267 if (member.id === interaction.guild.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100268 // Check if the user has manage_roles permission
pineafan3a02ea32022-08-11 21:35:04 +0100269 if (!member.permissions.has("MANAGE_ROLES")) throw new Error("You do not have the *Manage Roles* permission");
pineafand5a8e6a2022-07-25 17:38:14 +0100270 // Allow role
271 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100272};
pineafand5a8e6a2022-07-25 17:38:14 +0100273
274export { command };
275export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100276export { check };