blob: 453a467fc2ca70885c94a289be823bd9108c9dd5 [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 Grey75ea9172022-08-06 10:22:23 +010026 constructor(
27 name: (data) => string | string,
28 data: object,
29 check: (member) => boolean
30 ) {
31 if (typeof name === "function") {
32 this.name = name(data);
33 } else {
34 this.name = name;
35 }
pineafand5a8e6a2022-07-25 17:38:14 +010036 this.data = data;
37 this.checkFunction = check;
38 }
Skyler Grey75ea9172022-08-06 10:22:23 +010039 flip() {
40 this.inverted = true;
41 return this;
42 }
pineafand5a8e6a2022-07-25 17:38:14 +010043 check(member) {
pineafan63fc5e22022-08-04 22:04:10 +010044 if (this.inverted) return !this.checkFunction(member);
45 else return this.checkFunction(member);
pineafand5a8e6a2022-07-25 17:38:14 +010046 }
47}
48
49const filterList = {
50 member: {
51 render: "Member",
52 has: {
53 render: "has",
Skyler Grey75ea9172022-08-06 10:22:23 +010054 role: (role) =>
55 new Filter(
56 (data) => `Member has role <@&${data.role}>`,
57 { role: role, type: Discord.Role, render: "role" },
58 (member) => {
59 return member.roles.cache.has(role);
60 }
61 )
pineafand5a8e6a2022-07-25 17:38:14 +010062 },
63 joined: {
64 render: "joined",
Skyler Grey75ea9172022-08-06 10:22:23 +010065 before: (date) =>
66 new Filter(
67 (_data) =>
68 `Joined server before <t:${Math.round(
69 date.getTime() / 1000
70 )}:D>`,
71 { date: date, type: Date, render: "before" },
72 (member) => {
73 return member.joinedTimestamp < date.getTime();
74 }
75 )
pineafand5a8e6a2022-07-25 17:38:14 +010076 },
77 nickname: {
78 render: "Nickname",
Skyler Grey75ea9172022-08-06 10:22:23 +010079 set: () =>
80 new Filter(
81 (_data) => 'Member has a nickname set"',
82 { render: "set" },
83 (member) => {
84 return member.nickname !== null;
85 }
86 ),
87 includes: (name) =>
88 new Filter(
89 (_data) => `Nickname includes "${name}"`,
90 { nickname: name, type: String, render: "includes" },
91 (member) => {
92 return member.displayName.includes(name);
93 }
94 ),
95 startsWith: (name) =>
96 new Filter(
97 (_data) => `Nickname starts with "${name}"`,
98 { nickname: name, type: String, render: "starts with" },
99 (member) => {
100 return member.displayName.startsWith(name);
101 }
102 ),
103 endsWith: (name) =>
104 new Filter(
105 (_data) => `Nickname ends with "${name}"`,
106 { nickname: name, type: String, render: "ends with" },
107 (member) => {
108 return member.displayName.endsWith(name);
109 }
110 )
pineafand5a8e6a2022-07-25 17:38:14 +0100111 }
112 },
113 account: {
114 render: "Account",
115 created: {
116 render: "created",
Skyler Grey75ea9172022-08-06 10:22:23 +0100117 before: (date) =>
118 new Filter(
119 (_data) =>
120 `Account created before <t:${Math.round(
121 date.getTime() / 1000
122 )}:D>`,
123 { date: date, type: Date, render: "before" },
124 (member) => {
125 return member.user.createdTimestamp < date.getTime();
126 }
127 )
pineafand5a8e6a2022-07-25 17:38:14 +0100128 },
129 is: {
130 render: "is",
Skyler Grey75ea9172022-08-06 10:22:23 +0100131 human: () =>
132 new Filter(
133 (_data) => "Member is a human",
134 { human: true, render: "human" },
135 (member) => {
136 return !member.bot;
137 }
138 )
pineafand5a8e6a2022-07-25 17:38:14 +0100139 },
140 username: {
141 render: "Username",
Skyler Grey75ea9172022-08-06 10:22:23 +0100142 includes: (name) =>
143 new Filter(
144 (_data) => `Nickname includes "${name}"`,
145 { nickname: name, type: String, render: "includes" },
146 (member) => {
147 return member.user.name.includes(name);
148 }
149 ),
150 startsWith: (name) =>
151 new Filter(
152 (_data) => `Nickname starts with "${name}"`,
153 { nickname: name, type: String, render: "starts with" },
154 (member) => {
155 return member.user.name.startsWith(name);
156 }
157 ),
158 endsWith: (name) =>
159 new Filter(
160 (_data) => `Nickname ends with "${name}"`,
161 { nickname: name, type: String, render: "ends with" },
162 (member) => {
163 return member.user.name.endsWith(name);
164 }
165 )
pineafand5a8e6a2022-07-25 17:38:14 +0100166 }
167 }
pineafan63fc5e22022-08-04 22:04:10 +0100168};
pineafand5a8e6a2022-07-25 17:38:14 +0100169
Skyler Grey75ea9172022-08-06 10:22:23 +0100170const callback = async (
171 interaction: CommandInteraction
172): Promise<void | unknown> => {
173 await interaction.reply({
174 embeds: LoadingEmbed,
175 ephemeral: true,
176 fetchReply: true
177 });
pineafan63fc5e22022-08-04 22:04:10 +0100178 const filters: Filter[] = [
pineafand5a8e6a2022-07-25 17:38:14 +0100179 filterList.member.has.role("959901346000154674"),
180 filterList.member.nickname.startsWith("Pinea"),
181 filterList.member.joined.before(new Date(2022, 1)).flip()
pineafan63fc5e22022-08-04 22:04:10 +0100182 ];
183 const all = true;
pineafand5a8e6a2022-07-25 17:38:14 +0100184 while (true) {
185 let count = 0;
pineafan63fc5e22022-08-04 22:04:10 +0100186 const affected = [];
187 const members = interaction.guild.members.cache;
pineafand5a8e6a2022-07-25 17:38:14 +0100188 if (all) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100189 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100190 let applies = true;
Skyler Grey75ea9172022-08-06 10:22:23 +0100191 filters.forEach((filter) => {
192 if (!filter.check(member)) {
193 applies = false;
194 }
195 });
196 if (applies) {
197 affected.push(member);
198 }
pineafan63fc5e22022-08-04 22:04:10 +0100199 });
pineafand5a8e6a2022-07-25 17:38:14 +0100200 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100201 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100202 let applies = false;
Skyler Grey75ea9172022-08-06 10:22:23 +0100203 filters.forEach((filter) => {
204 if (filter.check(member)) {
205 applies = true;
206 }
207 });
208 if (applies) {
209 affected.push(member);
210 }
pineafan63fc5e22022-08-04 22:04:10 +0100211 });
pineafand5a8e6a2022-07-25 17:38:14 +0100212 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100213 await interaction.editReply({
214 embeds: [
215 new EmojiEmbed()
216 .setTitle("Role all")
217 .setDescription(
218 (all
219 ? "All of the following must be true:"
220 : "Any of the following must be true") +
221 "\n" +
222 filters
223 .map((f) => {
224 count++;
225 return (
226 (count === 1
227 ? getEmojiByName("ICONS.FILTER")
228 : all
229 ? "**and** "
230 : "**or** ") +
231 (f.inverted ? "**not** " : "") +
232 `${f.name}`
233 );
234 })
235 .join("\n") +
236 "\n\n" +
237 `This will affect ${addPlural(
238 affected.length,
239 "member"
240 )}`
241 )
242 .setEmoji("GUILD.ROLES.CREATE")
243 .setStatus("Success")
244 ],
245 components: [
246 new MessageActionRow().addComponents([
247 new MessageSelectMenu()
248 .setOptions(
249 filters.map((f, index) => ({
250 label: (f.inverted ? "(Not) " : "") + f.name,
251 value: index.toString()
252 }))
253 )
254 .setMinValues(1)
255 .setMaxValues(filters.length)
256 .setCustomId("select")
257 .setPlaceholder("Remove a filter")
258 ]),
259 new MessageActionRow().addComponents([
260 new MessageButton()
261 .setLabel("Apply")
262 .setStyle("PRIMARY")
263 .setCustomId("apply")
264 .setEmoji(
265 client.emojis.cache.get(
266 getEmojiByName("CONTROL.TICK", "id")
267 )
268 )
269 .setDisabled(affected.length === 0),
270 new MessageButton()
271 .setLabel("Add filter")
272 .setStyle("PRIMARY")
273 .setCustomId("add")
274 .setEmoji(
275 client.emojis.cache.get(
276 getEmojiByName("ICONS.FILTER", "id")
277 )
278 )
279 .setDisabled(filters.length >= 25)
280 ])
281 ]
282 });
pineafan63fc5e22022-08-04 22:04:10 +0100283 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100284 }
pineafanbd02b4a2022-08-05 22:01:38 +0100285 return;
pineafan63fc5e22022-08-04 22:04:10 +0100286};
pineafand5a8e6a2022-07-25 17:38:14 +0100287
Skyler Grey75ea9172022-08-06 10:22:23 +0100288const check = async (
289 interaction: CommandInteraction,
290 _defaultCheck: WrappedCheck
291) => {
292 const member = interaction.member as GuildMember;
293 const me = interaction.guild.me!;
294 if (!me.permissions.has("MANAGE_ROLES"))
295 throw "I do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100296 // Allow the owner to role anyone
pineafan63fc5e22022-08-04 22:04:10 +0100297 if (member.id === interaction.guild.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100298 // Check if the user has manage_roles permission
Skyler Grey75ea9172022-08-06 10:22:23 +0100299 if (!member.permissions.has("MANAGE_ROLES"))
300 throw "You do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100301 // Allow role
302 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100303};
pineafand5a8e6a2022-07-25 17:38:14 +0100304
305export { command };
306export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100307export { check };