blob: b2b8c525a48e4a9aef57d16a0d74faf5fe19a801 [file] [log] [blame]
pineafan4edb7762022-06-26 19:21:04 +01001import Discord, { CommandInteraction, GuildMember, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
pineafan4f164f32022-02-26 22:07:12 +00002import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
3import { WrappedCheck } from "jshaiku";
pineafan4edb7762022-06-26 19:21:04 +01004import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
5import getEmojiByName from "../../utils/getEmojiByName.js";
6import addPlural from "../../utils/plurals.js";
7import client from "../../utils/client.js";
pineafan4f164f32022-02-26 22:07:12 +00008
9const command = (builder: SlashCommandSubcommandBuilder) =>
10 builder
11 .setName("all")
12 .setDescription("Gives or removes a role from everyone")
13
pineafan4edb7762022-06-26 19:21:04 +010014class Filter {
15 name: string;
16 data: object;
17 checkFunction: (member) => boolean;
18 inverted: boolean = false;
19 constructor(name: (data) => string | string, data: object, check: (member) => boolean) {
20 if (typeof name === "function") { this.name = name(data);
21 } else { this.name = name; }
22 this.data = data;
23 this.checkFunction = check;
24 }
25 flip() { this.inverted = true; return this; }
26 check(member) {
27 if (this.inverted) return !this.checkFunction(member)
28 else return this.checkFunction(member)
29 }
pineafan4f164f32022-02-26 22:07:12 +000030}
31
pineafan4edb7762022-06-26 19:21:04 +010032const filterList = {
33 member: {
34 render: "Member",
35 has: {
36 render: "has",
37 role: (role) => ( new Filter((data) => `Member has role <@&${data.role}>`, {role: role, type: Discord.Role, render: "role"}, (member) => { return member.roles.cache.has(role)}))
38 },
39 joined: {
40 render: "joined",
pineafan412beec2022-06-29 21:46:26 +010041 before: (date) => ( new Filter((data) => `Joined server before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
pineafan4edb7762022-06-26 19:21:04 +010042 return member.joinedTimestamp < date.getTime()
pineafan412beec2022-06-29 21:46:26 +010043 }))
pineafan4edb7762022-06-26 19:21:04 +010044 },
45 nickname: {
46 render: "Nickname",
47 set: () => ( new Filter((data) => `Member has a nickname set"`, {render: "set"}, (member) => { return member.nickname !== null})),
pineafan412beec2022-06-29 21:46:26 +010048 includes: (name) => ( new Filter((data) => `Nickname includes "${name}"`, {nickname: name, type: String, render: "includes"}, (member) => {
pineafan4edb7762022-06-26 19:21:04 +010049 return member.displayName.includes(name)})),
50 startsWith: (name) => ( new Filter((data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
51 return member.displayName.startsWith(name)})),
52 endsWith: (name) => ( new Filter((data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
53 return member.displayName.endsWith(name)}))
54 }
55 },
56 account: {
57 render: "Account",
58 created: {
59 render: "created",
60 before: (date) => ( new Filter((data) => `Account created before <t:${Math.round(date.getTime() / 1000)}:D>`, {date: date, type: Date, render: "before"}, (member) => {
61 return member.user.createdTimestamp < date.getTime()
pineafan412beec2022-06-29 21:46:26 +010062 }))
pineafan4edb7762022-06-26 19:21:04 +010063 },
64 is: {
65 render: "is",
66 human: () => ( new Filter((data) => `Member is a human`, {human: true, render: "human"}, (member) => { return !member.bot })),
67 },
68 username: {
69 render: "Username",
pineafan412beec2022-06-29 21:46:26 +010070 includes: (name) => ( new Filter((data) => `Nickname includes "${name}"`, {nickname: name, type: String, render: "includes"}, (member) => {
pineafan4edb7762022-06-26 19:21:04 +010071 return member.user.name.includes(name)})),
72 startsWith: (name) => ( new Filter((data) => `Nickname starts with "${name}"`, {nickname: name, type: String, render: "starts with"}, (member) => {
73 return member.user.name.startsWith(name)})),
74 endsWith: (name) => ( new Filter((data) => `Nickname ends with "${name}"`, {nickname: name, type: String, render: "ends with"}, (member) => {
75 return member.user.name.endsWith(name)}))
76 }
77 }
78}
79
80const callback = async (interaction: CommandInteraction) => {
81 await interaction.reply({embeds: [new EmojiEmbed()
82 .setTitle("Role all")
83 .setDescription("Loading...")
84 .setStatus("Danger")
85 .setEmoji("NUCLEUS.LOADING")
86 ], ephemeral: true, fetchReply: true})
87 let filters: Filter[] = [
88 filterList.member.has.role("959901346000154674"),
89 filterList.member.nickname.startsWith("Pinea"),
90 filterList.member.joined.before(new Date(2022, 1)).flip()
91 ]
92 let all = true;
93 while (true) {
94 let count = 0;
95 let affected = []
96 let members = interaction.guild.members.cache
97 if (all) {
98 members.forEach(member => {
99 let applies = true;
100 filters.forEach(filter => { if (!filter.check(member)) { applies = false } })
101 if (applies) { affected.push(member) }
102 })
103 } else {
104 members.forEach(member => {
105 let applies = false;
106 filters.forEach(filter => { if (filter.check(member)) { applies = true } })
107 if (applies) { affected.push(member) }
108 })
109 }
110 await interaction.editReply({embeds: [new EmojiEmbed()
111 .setTitle("Role all")
112 .setDescription((all ? "All of the following must be true:" : "Any of the following must be true") + "\n" +
113 filters.map((f) => {
114 count ++;
115 return (count == 1 ? getEmojiByName("ICONS.FILTER") : (all ? "**and** " : "**or** ")) +
116 (f.inverted ? "**not** " : "") + `${f.name}`
117 }).join("\n") + "\n\n" + `This will affect ${addPlural(affected.length.toString(), "member")}`)
118 .setEmoji("GUILD.ROLES.CREATE")
119 .setStatus("Success")
120 ], components: [
121 new MessageActionRow().addComponents([new MessageSelectMenu().setOptions(filters.map((f, index) => ({
122 label: (f.inverted ? "(Not) " : "") + f.name,
123 value: index.toString()
124 }))).setMinValues(1).setMaxValues(filters.length).setCustomId("select").setPlaceholder("Remove a filter")]),
125 new MessageActionRow().addComponents([
126 new MessageButton()
127 .setLabel("Apply")
128 .setStyle("PRIMARY")
129 .setCustomId("apply")
130 .setEmoji(client.emojis.cache.get(getEmojiByName("CONTROL.TICK", "id")))
131 .setDisabled(affected.length === 0),
132 new MessageButton()
133 .setLabel("Add filter")
134 .setStyle("PRIMARY")
135 .setCustomId("add")
136 .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.FILTER", "id")))
137 .setDisabled(filters.length >= 25)
138 ])
139 ]})
140 break
141 }
142}
143
144const check = async (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
145 let member = (interaction.member as GuildMember)
146 let me = (interaction.guild.me as GuildMember)
147 if (!me.permissions.has("MANAGE_ROLES")) throw "I do not have the Manage roles permission";
148 // Allow the owner to role anyone
149 if (member.id == interaction.guild.ownerId) return true
150 // Check if the user has manage_roles permission
151 if (! member.permissions.has("MANAGE_ROLES")) throw "You do not have the Manage roles permission";
152 // Allow role
pineafan4f164f32022-02-26 22:07:12 +0000153 return true;
154}
155
156export { command };
157export { callback };
158export { check };