blob: 51be5cb082034bd53e783c347d1d3cc09ce35bcb [file] [log] [blame]
pineafan63fc5e22022-08-04 22:04:10 +01001import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
pineafand5a8e6a2022-07-25 17:38:14 +01002import Discord, { CommandInteraction, GuildMember, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
3import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
4import { WrappedCheck } from "jshaiku";
5import EmojiEmbed from "../utils/generateEmojiEmbed.js";
6import getEmojiByName from "../utils/getEmojiByName.js";
7import addPlural from "../utils/plurals.js";
8import client from "../utils/client.js";
9
10const command = (builder: SlashCommandSubcommandBuilder) =>
11 builder // TODO: DON'T RELEASE THIS
pineafan63fc5e22022-08-04 22:04:10 +010012 .setName("all")
13 .setDescription("Gives or removes a role from everyone");
pineafand5a8e6a2022-07-25 17:38:14 +010014
15class Filter {
16 name: string;
17 data: object;
18 checkFunction: (member) => boolean;
pineafan63fc5e22022-08-04 22:04:10 +010019 inverted = false;
pineafand5a8e6a2022-07-25 17:38:14 +010020 constructor(name: (data) => string | string, data: object, check: (member) => boolean) {
21 if (typeof name === "function") { this.name = name(data);
22 } else { this.name = name; }
23 this.data = data;
24 this.checkFunction = check;
25 }
26 flip() { this.inverted = true; return this; }
27 check(member) {
pineafan63fc5e22022-08-04 22:04:10 +010028 if (this.inverted) return !this.checkFunction(member);
29 else return this.checkFunction(member);
pineafand5a8e6a2022-07-25 17:38:14 +010030 }
31}
32
33const filterList = {
34 member: {
35 render: "Member",
36 has: {
37 render: "has",
pineafan63fc5e22022-08-04 22:04:10 +010038 role: (role) => ( new Filter((data) => `Member has role <@&${data.role}>`, {role: role, type: Discord.Role, render: "role"}, (member) => { return member.roles.cache.has(role);}))
pineafand5a8e6a2022-07-25 17:38:14 +010039 },
40 joined: {
41 render: "joined",
pineafanbd02b4a2022-08-05 22:01:38 +010042 before: (date) => ( new Filter((_data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010043 return member.joinedTimestamp < date.getTime();
pineafand5a8e6a2022-07-25 17:38:14 +010044 }))
45 },
46 nickname: {
47 render: "Nickname",
pineafanbd02b4a2022-08-05 22:01:38 +010048 set: () => ( new Filter((_data) => "Member has a nickname set\"", {render: "set"}, (member) => { return member.nickname !== null;})),
49 includes: (name) => ( new Filter((_data) => `Nickname includes "${name}"`, {nickname: name, type: String, render: "includes"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010050 return member.displayName.includes(name);})),
pineafanbd02b4a2022-08-05 22:01:38 +010051 startsWith: (name) => ( new Filter((_data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010052 return member.displayName.startsWith(name);})),
pineafanbd02b4a2022-08-05 22:01:38 +010053 endsWith: (name) => ( new Filter((_data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010054 return member.displayName.endsWith(name);}))
pineafand5a8e6a2022-07-25 17:38:14 +010055 }
56 },
57 account: {
58 render: "Account",
59 created: {
60 render: "created",
pineafanbd02b4a2022-08-05 22:01:38 +010061 before: (date) => ( new Filter((_data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010062 return member.user.createdTimestamp < date.getTime();
pineafand5a8e6a2022-07-25 17:38:14 +010063 }))
64 },
65 is: {
66 render: "is",
pineafanbd02b4a2022-08-05 22:01:38 +010067 human: () => ( new Filter((_data) => "Member is a human", {human: true, render: "human"}, (member) => { return !member.bot; }))
pineafand5a8e6a2022-07-25 17:38:14 +010068 },
69 username: {
70 render: "Username",
pineafanbd02b4a2022-08-05 22:01:38 +010071 includes: (name) => ( new Filter((_data) => `Nickname includes "${name}"`, {nickname: name, type: String, render: "includes"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010072 return member.user.name.includes(name);})),
pineafanbd02b4a2022-08-05 22:01:38 +010073 startsWith: (name) => ( new Filter((_data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010074 return member.user.name.startsWith(name);})),
pineafanbd02b4a2022-08-05 22:01:38 +010075 endsWith: (name) => ( new Filter((_data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
pineafan63fc5e22022-08-04 22:04:10 +010076 return member.user.name.endsWith(name);}))
pineafand5a8e6a2022-07-25 17:38:14 +010077 }
78 }
pineafan63fc5e22022-08-04 22:04:10 +010079};
pineafand5a8e6a2022-07-25 17:38:14 +010080
pineafanbd02b4a2022-08-05 22:01:38 +010081const callback = async (interaction: CommandInteraction): Promise<void | unknown> => {
pineafan63fc5e22022-08-04 22:04:10 +010082 await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
83 const filters: Filter[] = [
pineafand5a8e6a2022-07-25 17:38:14 +010084 filterList.member.has.role("959901346000154674"),
85 filterList.member.nickname.startsWith("Pinea"),
86 filterList.member.joined.before(new Date(2022, 1)).flip()
pineafan63fc5e22022-08-04 22:04:10 +010087 ];
88 const all = true;
pineafand5a8e6a2022-07-25 17:38:14 +010089 while (true) {
90 let count = 0;
pineafan63fc5e22022-08-04 22:04:10 +010091 const affected = [];
92 const members = interaction.guild.members.cache;
pineafand5a8e6a2022-07-25 17:38:14 +010093 if (all) {
94 members.forEach(member => {
95 let applies = true;
pineafan63fc5e22022-08-04 22:04:10 +010096 filters.forEach(filter => { if (!filter.check(member)) { applies = false; } });
97 if (applies) { affected.push(member); }
98 });
pineafand5a8e6a2022-07-25 17:38:14 +010099 } else {
100 members.forEach(member => {
101 let applies = false;
pineafan63fc5e22022-08-04 22:04:10 +0100102 filters.forEach(filter => { if (filter.check(member)) { applies = true; } });
103 if (applies) { affected.push(member); }
104 });
pineafand5a8e6a2022-07-25 17:38:14 +0100105 }
106 await interaction.editReply({embeds: [new EmojiEmbed()
107 .setTitle("Role all")
108 .setDescription((all ? "All of the following must be true:" : "Any of the following must be true") + "\n" +
109 filters.map((f) => {
110 count ++;
pineafane23c4ec2022-07-27 21:56:27 +0100111 return (count === 1 ? getEmojiByName("ICONS.FILTER") : (all ? "**and** " : "**or** ")) +
pineafan63fc5e22022-08-04 22:04:10 +0100112 (f.inverted ? "**not** " : "") + `${f.name}`;
pineafand5a8e6a2022-07-25 17:38:14 +0100113 }).join("\n") + "\n\n" + `This will affect ${addPlural(affected.length, "member")}`)
114 .setEmoji("GUILD.ROLES.CREATE")
115 .setStatus("Success")
116 ], components: [
117 new MessageActionRow().addComponents([new MessageSelectMenu().setOptions(filters.map((f, index) => ({
118 label: (f.inverted ? "(Not) " : "") + f.name,
119 value: index.toString()
120 }))).setMinValues(1).setMaxValues(filters.length).setCustomId("select").setPlaceholder("Remove a filter")]),
121 new MessageActionRow().addComponents([
122 new MessageButton()
123 .setLabel("Apply")
124 .setStyle("PRIMARY")
125 .setCustomId("apply")
126 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id")))
127 .setDisabled(affected.length === 0),
128 new MessageButton()
129 .setLabel("Add filter")
130 .setStyle("PRIMARY")
131 .setCustomId("add")
132 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id")))
133 .setDisabled(filters.length >= 25)
134 ])
pineafan63fc5e22022-08-04 22:04:10 +0100135 ]});
136 break;
pineafand5a8e6a2022-07-25 17:38:14 +0100137 }
pineafanbd02b4a2022-08-05 22:01:38 +0100138 return;
pineafan63fc5e22022-08-04 22:04:10 +0100139};
pineafand5a8e6a2022-07-25 17:38:14 +0100140
pineafanbd02b4a2022-08-05 22:01:38 +0100141const check = async (interaction: CommandInteraction, _defaultCheck: WrappedCheck) => {
pineafan63fc5e22022-08-04 22:04:10 +0100142 const member = (interaction.member as GuildMember);
143 const me = (interaction.guild.me as GuildMember);
pineafane23c4ec2022-07-27 21:56:27 +0100144 if (!me.permissions.has("MANAGE_ROLES")) throw "I do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100145 // Allow the owner to role anyone
pineafan63fc5e22022-08-04 22:04:10 +0100146 if (member.id === interaction.guild.ownerId) return true;
pineafand5a8e6a2022-07-25 17:38:14 +0100147 // Check if the user has manage_roles permission
pineafane23c4ec2022-07-27 21:56:27 +0100148 if (! member.permissions.has("MANAGE_ROLES")) throw "You do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100149 // Allow role
150 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100151};
pineafand5a8e6a2022-07-25 17:38:14 +0100152
153export { command };
154export { callback };
155export { check };