blob: d09ca7b03f7b3c381f8e8f9fb510c3b24a8d4826 [file] [log] [blame]
PineaFan0d06edc2023-01-17 22:10:31 +00001import { LoadingEmbed } from "../utils/defaults.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,
PineaFan5d98a4b2023-01-19 16:15:47 +00008 ButtonStyle,
9 StringSelectMenuBuilder,
10 APIMessageComponentEmoji
Skyler Grey75ea9172022-08-06 10:22:23 +010011} from "discord.js";
pineafand5a8e6a2022-07-25 17:38:14 +010012import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafand5a8e6a2022-07-25 17:38:14 +010013import EmojiEmbed from "../utils/generateEmojiEmbed.js";
14import getEmojiByName from "../utils/getEmojiByName.js";
15import addPlural from "../utils/plurals.js";
16import client from "../utils/client.js";
17
18const command = (builder: SlashCommandSubcommandBuilder) =>
19 builder // TODO: DON'T RELEASE THIS
pineafan63fc5e22022-08-04 22:04:10 +010020 .setName("all")
21 .setDescription("Gives or removes a role from everyone");
pineafand5a8e6a2022-07-25 17:38:14 +010022
23class Filter {
24 name: string;
25 data: object;
26 checkFunction: (member) => boolean;
pineafan63fc5e22022-08-04 22:04:10 +010027 inverted = false;
Skyler Grey11236ba2022-08-08 21:13:33 +010028 constructor(name: (data) => string | string, data: object, check: (member) => boolean) {
Skyler Grey75ea9172022-08-06 10:22:23 +010029 if (typeof name === "function") {
30 this.name = name(data);
31 } else {
32 this.name = name;
33 }
pineafand5a8e6a2022-07-25 17:38:14 +010034 this.data = data;
35 this.checkFunction = check;
36 }
Skyler Grey75ea9172022-08-06 10:22:23 +010037 flip() {
38 this.inverted = true;
39 return this;
40 }
pineafand5a8e6a2022-07-25 17:38:14 +010041 check(member) {
pineafan63fc5e22022-08-04 22:04:10 +010042 if (this.inverted) return !this.checkFunction(member);
43 else return this.checkFunction(member);
pineafand5a8e6a2022-07-25 17:38:14 +010044 }
45}
46
47const filterList = {
48 member: {
49 render: "Member",
50 has: {
51 render: "has",
Skyler Grey75ea9172022-08-06 10:22:23 +010052 role: (role) =>
53 new Filter(
54 (data) => `Member has role <@&${data.role}>`,
55 { role: role, type: Discord.Role, render: "role" },
56 (member) => {
57 return member.roles.cache.has(role);
58 }
59 )
pineafand5a8e6a2022-07-25 17:38:14 +010060 },
61 joined: {
62 render: "joined",
Skyler Grey75ea9172022-08-06 10:22:23 +010063 before: (date) =>
64 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +010065 (_data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +010066 { date: date, type: Date, render: "before" },
67 (member) => {
68 return member.joinedTimestamp < date.getTime();
69 }
70 )
pineafand5a8e6a2022-07-25 17:38:14 +010071 },
72 nickname: {
73 render: "Nickname",
Skyler Grey75ea9172022-08-06 10:22:23 +010074 set: () =>
75 new Filter(
76 (_data) => 'Member has a nickname set"',
77 { render: "set" },
78 (member) => {
79 return member.nickname !== null;
80 }
81 ),
82 includes: (name) =>
83 new Filter(
84 (_data) => `Nickname includes "${name}"`,
85 { nickname: name, type: String, render: "includes" },
86 (member) => {
87 return member.displayName.includes(name);
88 }
89 ),
90 startsWith: (name) =>
91 new Filter(
92 (_data) => `Nickname starts with "${name}"`,
93 { nickname: name, type: String, render: "starts with" },
94 (member) => {
95 return member.displayName.startsWith(name);
96 }
97 ),
98 endsWith: (name) =>
99 new Filter(
100 (_data) => `Nickname ends with "${name}"`,
101 { nickname: name, type: String, render: "ends with" },
102 (member) => {
103 return member.displayName.endsWith(name);
104 }
105 )
pineafand5a8e6a2022-07-25 17:38:14 +0100106 }
107 },
108 account: {
109 render: "Account",
110 created: {
111 render: "created",
Skyler Grey75ea9172022-08-06 10:22:23 +0100112 before: (date) =>
113 new Filter(
Skyler Grey11236ba2022-08-08 21:13:33 +0100114 (_data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`,
Skyler Grey75ea9172022-08-06 10:22:23 +0100115 { date: date, type: Date, render: "before" },
116 (member) => {
117 return member.user.createdTimestamp < date.getTime();
118 }
119 )
pineafand5a8e6a2022-07-25 17:38:14 +0100120 },
121 is: {
122 render: "is",
Skyler Grey75ea9172022-08-06 10:22:23 +0100123 human: () =>
124 new Filter(
125 (_data) => "Member is a human",
126 { human: true, render: "human" },
127 (member) => {
128 return !member.bot;
129 }
130 )
pineafand5a8e6a2022-07-25 17:38:14 +0100131 },
132 username: {
133 render: "Username",
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 includes: (name) =>
135 new Filter(
136 (_data) => `Nickname includes "${name}"`,
137 { nickname: name, type: String, render: "includes" },
138 (member) => {
139 return member.user.name.includes(name);
140 }
141 ),
142 startsWith: (name) =>
143 new Filter(
144 (_data) => `Nickname starts with "${name}"`,
145 { nickname: name, type: String, render: "starts with" },
146 (member) => {
147 return member.user.name.startsWith(name);
148 }
149 ),
150 endsWith: (name) =>
151 new Filter(
152 (_data) => `Nickname ends with "${name}"`,
153 { nickname: name, type: String, render: "ends with" },
154 (member) => {
155 return member.user.name.endsWith(name);
156 }
157 )
pineafand5a8e6a2022-07-25 17:38:14 +0100158 }
159 }
pineafan63fc5e22022-08-04 22:04:10 +0100160};
pineafand5a8e6a2022-07-25 17:38:14 +0100161
pineafan3a02ea32022-08-11 21:35:04 +0100162const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100163 await interaction.reply({
164 embeds: LoadingEmbed,
165 ephemeral: true,
166 fetchReply: true
167 });
pineafan63fc5e22022-08-04 22:04:10 +0100168 const filters: Filter[] = [
pineafand5a8e6a2022-07-25 17:38:14 +0100169 filterList.member.has.role("959901346000154674"),
170 filterList.member.nickname.startsWith("Pinea"),
171 filterList.member.joined.before(new Date(2022, 1)).flip()
pineafan63fc5e22022-08-04 22:04:10 +0100172 ];
173 const all = true;
pineafand5a8e6a2022-07-25 17:38:14 +0100174 while (true) {
175 let count = 0;
PineaFan5d98a4b2023-01-19 16:15:47 +0000176 const affected: GuildMember[] = [];
177 const members = interaction.guild!.members.cache;
pineafand5a8e6a2022-07-25 17:38:14 +0100178 if (all) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100179 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100180 let applies = true;
Skyler Greyf21323a2022-08-13 23:58:22 +0100181 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100182 if (!filter.check(member)) {
183 applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100184 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100185 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100186 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100187 if (applies) {
188 affected.push(member);
189 }
pineafan63fc5e22022-08-04 22:04:10 +0100190 });
pineafand5a8e6a2022-07-25 17:38:14 +0100191 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100192 members.forEach((member) => {
pineafand5a8e6a2022-07-25 17:38:14 +0100193 let applies = false;
Skyler Greyf21323a2022-08-13 23:58:22 +0100194 for (const filter of filters) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100195 if (filter.check(member)) {
196 applies = true;
Skyler Greyf21323a2022-08-13 23:58:22 +0100197 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100198 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100199 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100200 if (applies) {
201 affected.push(member);
202 }
pineafan63fc5e22022-08-04 22:04:10 +0100203 });
pineafand5a8e6a2022-07-25 17:38:14 +0100204 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100205 await interaction.editReply({
206 embeds: [
207 new EmojiEmbed()
208 .setTitle("Role all")
209 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100210 (all ? "All of the following must be true:" : "Any of the following must be true") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100211 "\n" +
212 filters
213 .map((f) => {
214 count++;
215 return (
Skyler Grey11236ba2022-08-08 21:13:33 +0100216 (count === 1 ? getEmojiByName("ICONS.FILTER") : all ? "**and** " : "**or** ") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100217 (f.inverted ? "**not** " : "") +
218 `${f.name}`
219 );
220 })
221 .join("\n") +
222 "\n\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100223 `This will affect ${addPlural(affected.length, "member")}`
Skyler Grey75ea9172022-08-06 10:22:23 +0100224 )
225 .setEmoji("GUILD.ROLES.CREATE")
226 .setStatus("Success")
227 ],
228 components: [
PineaFan5d98a4b2023-01-19 16:15:47 +0000229 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([
230 new StringSelectMenuBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100231 .setOptions(
232 filters.map((f, index) => ({
233 label: (f.inverted ? "(Not) " : "") + f.name,
234 value: index.toString()
235 }))
236 )
237 .setMinValues(1)
238 .setMaxValues(filters.length)
239 .setCustomId("select")
240 .setPlaceholder("Remove a filter")
241 ]),
PineaFan5d98a4b2023-01-19 16:15:47 +0000242 new ActionRowBuilder<ButtonBuilder>().addComponents([
TheCodedProf21c08592022-09-13 14:14:43 -0400243 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100244 .setLabel("Apply")
TheCodedProf21c08592022-09-13 14:14:43 -0400245 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100246 .setCustomId("apply")
PineaFan5d98a4b2023-01-19 16:15:47 +0000247 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id"))! as APIMessageComponentEmoji)
Skyler Grey75ea9172022-08-06 10:22:23 +0100248 .setDisabled(affected.length === 0),
TheCodedProf21c08592022-09-13 14:14:43 -0400249 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100250 .setLabel("Add filter")
TheCodedProf21c08592022-09-13 14:14:43 -0400251 .setStyle(ButtonStyle.Primary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100252 .setCustomId("add")
PineaFan5d98a4b2023-01-19 16:15:47 +0000253 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id"))! as APIMessageComponentEmoji)
Skyler Grey75ea9172022-08-06 10:22:23 +0100254 .setDisabled(filters.length >= 25)
255 ])
256 ]
257 });
pineafan63fc5e22022-08-04 22:04:10 +0100258 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100259 }
pineafanbd02b4a2022-08-05 22:01:38 +0100260 return;
pineafan63fc5e22022-08-04 22:04:10 +0100261};
pineafand5a8e6a2022-07-25 17:38:14 +0100262
PineaFan64486c42022-12-28 09:21:04 +0000263const check = async (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100264 const member = interaction.member as GuildMember;
PineaFan5d98a4b2023-01-19 16:15:47 +0000265 const me = interaction.guild!.members.me!;
266 if (!me.permissions.has("ManageRoles")) return "I do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100267 // Allow the owner to role anyone
PineaFan5d98a4b2023-01-19 16:15:47 +0000268 if (member.id === interaction.guild!.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100269 // Check if the user has manage_roles permission
PineaFan5d98a4b2023-01-19 16:15:47 +0000270 if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100271 // Allow role
272 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100273};
pineafand5a8e6a2022-07-25 17:38:14 +0100274
275export { command };
276export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100277export { check };