blob: 5c482f16a20d85ac4f66767f2bca10ce08c7bd7d [file] [log] [blame]
pineafane23c4ec2022-07-27 21:56:27 +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
12 .setName("all")
13 .setDescription("Gives or removes a role from everyone")
14
15class Filter {
16 name: string;
17 data: object;
18 checkFunction: (member) => boolean;
19 inverted: boolean = false;
20 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) {
28 if (this.inverted) return !this.checkFunction(member)
29 else return this.checkFunction(member)
30 }
31}
32
33const filterList = {
34 member: {
35 render: "Member",
36 has: {
37 render: "has",
38 role: (role) => ( new Filter((data) => `Member has role <@&${data.role}>`, {role: role, type: Discord.Role, render: "role"}, (member) => { return member.roles.cache.has(role)}))
39 },
40 joined: {
41 render: "joined",
42 before: (date) => ( new Filter((data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
43 return member.joinedTimestamp < date.getTime()
44 }))
45 },
46 nickname: {
47 render: "Nickname",
48 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) => {
50 return member.displayName.includes(name)})),
51 startsWith: (name) => ( new Filter((data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
52 return member.displayName.startsWith(name)})),
53 endsWith: (name) => ( new Filter((data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
54 return member.displayName.endsWith(name)}))
55 }
56 },
57 account: {
58 render: "Account",
59 created: {
60 render: "created",
61 before: (date) => ( new Filter((data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
62 return member.user.createdTimestamp < date.getTime()
63 }))
64 },
65 is: {
66 render: "is",
67 human: () => ( new Filter((data) => `Member is a human`, {human: true, render: "human"}, (member) => { return !member.bot })),
68 },
69 username: {
70 render: "Username",
71 includes: (name) => ( new Filter((data) => `Nickname includes "${name}"`, {nickname: name, type: String, render: "includes"}, (member) => {
72 return member.user.name.includes(name)})),
73 startsWith: (name) => ( new Filter((data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
74 return member.user.name.startsWith(name)})),
75 endsWith: (name) => ( new Filter((data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
76 return member.user.name.endsWith(name)}))
77 }
78 }
79}
80
81const callback = async (interaction: CommandInteraction) => {
pineafane23c4ec2022-07-27 21:56:27 +010082 await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true})
pineafand5a8e6a2022-07-25 17:38:14 +010083 let filters: Filter[] = [
84 filterList.member.has.role("959901346000154674"),
85 filterList.member.nickname.startsWith("Pinea"),
86 filterList.member.joined.before(new Date(2022, 1)).flip()
87 ]
88 let all = true;
89 while (true) {
90 let count = 0;
91 let affected = []
92 let members = interaction.guild.members.cache
93 if (all) {
94 members.forEach(member => {
95 let applies = true;
96 filters.forEach(filter => { if (!filter.check(member)) { applies = false } })
97 if (applies) { affected.push(member) }
98 })
99 } else {
100 members.forEach(member => {
101 let applies = false;
102 filters.forEach(filter => { if (filter.check(member)) { applies = true } })
103 if (applies) { affected.push(member) }
104 })
105 }
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** ")) +
pineafand5a8e6a2022-07-25 17:38:14 +0100112 (f.inverted ? "**not** " : "") + `${f.name}`
113 }).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 ])
135 ]})
136 break
137 }
138}
139
140const check = async (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
141 let member = (interaction.member as GuildMember)
142 let me = (interaction.guild.me as GuildMember)
pineafane23c4ec2022-07-27 21:56:27 +0100143 if (!me.permissions.has("MANAGE_ROLES")) throw "I do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100144 // Allow the owner to role anyone
pineafane23c4ec2022-07-27 21:56:27 +0100145 if (member.id === interaction.guild.ownerId) return true
pineafand5a8e6a2022-07-25 17:38:14 +0100146 // Check if the user has manage_roles permission
pineafane23c4ec2022-07-27 21:56:27 +0100147 if (! member.permissions.has("MANAGE_ROLES")) throw "You do not have the *Manage Roles* permission";
pineafand5a8e6a2022-07-25 17:38:14 +0100148 // Allow role
149 return true;
150}
151
152export { command };
153export { callback };
154export { check };