Merge branch 'development' of 140.82.121.4:clicksminuteper/nucleus into development
diff --git a/src/actions/createModActionTicket.ts b/src/actions/createModActionTicket.ts
index 72ba011..7bd3d22 100644
--- a/src/actions/createModActionTicket.ts
+++ b/src/actions/createModActionTicket.ts
@@ -3,7 +3,7 @@
 import getEmojiByName from "../utils/getEmojiByName.js";
 import client from "../utils/client.js";
 
-export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, reason: string) {
+export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, reason: string, customReason?: string) {
     let config = await client.database.guilds.read(guild.id);
     const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
     let overwrites = [{
@@ -51,7 +51,7 @@
             .setTitle("New Ticket")
             .setDescription(
                 `Ticket created by a Moderator\n` +
-                `**Support type:** Appeal submission\n` + (reason !== null ? `**Reason:**\n> ${reason}\n` : "") +
+                `**Support type:** ${customReason ? customReason : "Appeal submission"}\n` + (reason !== null ? `**Reason:**\n> ${reason}\n` : "") +
                 `**Ticket ID:** \`${c.id}\`\n` +
                 `Type \`/ticket close\` to close this ticket.`,
             )
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 5da8693..8fc98d2 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -129,6 +129,8 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow banning the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot ban the owner of the server"
     // Check if Nucleus can ban the member
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to ban
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index a0b9a97..ef748e8 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -128,6 +128,8 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow kicking the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot kick the owner of the server"
     // Check if Nucleus can kick the member
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to kick
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index 8bb6854..808d22d 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -252,12 +252,12 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow muting the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot mute the owner of the server"
     // Check if Nucleus can mute the member
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to mute
     if (! me.permissions.has("MODERATE_MEMBERS")) throw "I do not have the *Moderate Members* permission";
-    // Do not allow the user to have admin or be the owner
-    if (apply.permissions.has("ADMINISTRATOR") || (interaction.options.getMember("user") as GuildMember).id === interaction.guild.ownerId) throw "You cannot mute an admin or the owner"
     // Do not allow muting Nucleus
     if (member.id === me.id) throw "I cannot mute myself"
     // Allow the owner to mute anyone
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index cf33109..d50becd 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -121,6 +121,8 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow any changing of the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot change the owner's nickname"
     // Check if Nucleus can change the nickname
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to change the nickname
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index e1e4de9..521d8a7 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -313,13 +313,13 @@
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let member = (interaction.member as GuildMember)
     let me = (interaction.guild.me as GuildMember)
+    // Check if nucleus has the manage_messages permission
+    if (! me.permissions.has("MANAGE_MESSAGES")) throw "I do not have the *Manage Messages* permission";
     // Allow the owner to purge
     if (member.id === interaction.guild.ownerId) return true
     // Check if the user has manage_messages permission
     if (! member.permissions.has("MANAGE_MESSAGES")) throw "You do not have the *Manage Messages* permission";
-    // Check if nucleus has the manage_messages permission
-    if (! me.permissions.has("MANAGE_MESSAGES")) throw "I do not have the *Manage Messages* permission";
-    // Allow warn
+    // Allow purge
     return true
 }
 
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index 5c7e8c4..43d03fd 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -106,13 +106,15 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow softbanning the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot softban the owner of the server"
     // Check if Nucleus can ban the member
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to ban
     if (!me.permissions.has("BAN_MEMBERS")) throw "I do not have the *Ban Members* permission";
     // Do not allow softbanning Nucleus
     if (member.id === me.id) throw "I cannot softban myself"
-    // Allow the owner to ban anyone
+    // Allow the owner to softban anyone
     if (member.id === interaction.guild.ownerId) return true
     // Check if the user has ban_members permission
     if (! member.permissions.has("BAN_MEMBERS")) throw "You do not have the *Ban Members* permission";
diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts
index 25aacfa..6451837 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -114,12 +114,12 @@
     let memberPos = member.roles ? member.roles.highest.position : 0
     let mePos = me.roles ? me.roles.highest.position : 0
     let applyPos = apply.roles ? apply.roles.highest.position : 0
+    // Do not allow unmuting the owner
+    if (member.id === interaction.guild.ownerId) throw "You cannot unmute the owner of the server"
     // Check if Nucleus can unmute the member
     if (! (mePos > applyPos)) throw "I do not have a role higher than that member"
     // Check if Nucleus has permission to unmute
     if (! me.permissions.has("MODERATE_MEMBERS")) throw "I do not have the *Moderate Members* permission";
-    // Do not allow the user to have admin or be the owner
-    if (apply.permissions.has("ADMINISTRATOR") || apply.id === interaction.guild.ownerId) throw "You cannot unmute an admin or the owner"
     // Allow the owner to unmute anyone
     if (member.id === interaction.guild.ownerId) return true
     // Check if the user has moderate_members permission
diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts
index 5a42911..b007b29 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -107,6 +107,7 @@
                 .setStatus("Success")
             ], components: []})
         } else {
+            let canSeeChannel = (interaction.options.getMember("user") as GuildMember).permissionsIn(interaction.channel as Discord.TextChannel).has("VIEW_CHANNEL")
             let m = await interaction.editReply({
                 embeds: [new EmojiEmbed()
                     .setEmoji(`PUNISH.WARN.RED`)
@@ -122,8 +123,12 @@
                         new Discord.MessageButton()
                             .setCustomId("here")
                             .setLabel("Warn here")
-                            .setStyle("SECONDARY")
-                            .setDisabled((interaction.options.getMember("user") as GuildMember).permissionsIn(interaction.channel as Discord.TextChannel).has("VIEW_CHANNEL") === false),
+                            .setStyle(canSeeChannel ? "PRIMARY" : "SECONDARY")
+                            .setDisabled(!canSeeChannel),
+                        new Discord.MessageButton()
+                            .setCustomId("ticket")
+                            .setLabel("Create ticket")
+                            .setStyle(canSeeChannel ? "SECONDARY" : "PRIMARY")
                     ])
                 ]
             })
@@ -156,13 +161,29 @@
                     .setDescription("The user was warned" + (confirmation.response ? ` and an appeal ticket was opened in <#${confirmation.response}>` : ``))
                     .setStatus("Success")
                 ], components: []})
-            } else {
+            } else if (component.customId === "log") {
                 await interaction.editReply({embeds: [new EmojiEmbed()
                     .setEmoji(`PUNISH.WARN.GREEN`)
                     .setTitle(`Warn`)
                     .setDescription("The warn was logged")
                     .setStatus("Success")
                 ], components: []})
+            } else if (component.customId === "ticket") {
+                let ticketChannel = await create(interaction.guild, interaction.options.getUser("user"), interaction.user, reason, "Warn Notification")
+                if (ticketChannel === null) {
+                    return await interaction.editReply({embeds: [new EmojiEmbed()
+                        .setEmoji(`PUNISH.WARN.RED`)
+                        .setTitle(`Warn`)
+                        .setDescription("A ticket could not be created")
+                        .setStatus("Danger")
+                    ], components: []})
+                }
+                await interaction.editReply({embeds: [new EmojiEmbed()
+                    .setEmoji(`PUNISH.WARN.GREEN`)
+                    .setTitle(`Warn`)
+                    .setDescription(`A ticket was created in <#${ticketChannel}>`)
+                    .setStatus("Success")
+                ], components: []})
             }
         }
     } else {
diff --git a/src/commands/role/user.ts b/src/commands/role/user.ts
index 274dd3d..d361cf1 100644
--- a/src/commands/role/user.ts
+++ b/src/commands/role/user.ts
@@ -59,7 +59,7 @@
         await interaction.editReply({embeds: [new EmojiEmbed()
             .setEmoji("GUILD.ROLES.CREATE")
             .setTitle("Role")
-            .setDescription(`You have cancelled the role change.`)
+            .setDescription(`No changes were made.`)
             .setStatus("Danger")
         ], components: []})
     }
diff --git a/src/commands/settings/filters.ts b/src/commands/settings/filters.ts
index 183b91c..dfcff99 100644
--- a/src/commands/settings/filters.ts
+++ b/src/commands/settings/filters.ts
@@ -20,7 +20,7 @@
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let member = (interaction.member as Discord.GuildMember)
-    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
+    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the *Manage Messages* permission to use this command"
     return true;
 }
 
diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts
index 6f95b46..891b6f1 100644
--- a/src/commands/settings/rolemenu.ts
+++ b/src/commands/settings/rolemenu.ts
@@ -8,9 +8,9 @@
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
-    .setName("role")
-    .setDescription("Sets or shows the role given to users after using /verify")
-    .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying"))
+    .setName("rolemenu")
+    .setDescription("rolemenu")// TODO
+    .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying")) // TODO
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
 }
diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts
index be15869..10fba6b 100644
--- a/src/commands/settings/stats.ts
+++ b/src/commands/settings/stats.ts
@@ -1,10 +1,8 @@
 import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
-import { ChannelType } from 'discord-api-types';
-import Discord, { AutocompleteInteraction, CommandInteraction, Message, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
+import Discord, { CommandInteraction, MessageActionRow, MessageSelectMenu } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import confirmationMessage from "../../utils/confirmationMessage.js";
-import getEmojiByName from "../../utils/getEmojiByName.js";
-import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import client from "../../utils/client.js";
 import convertCurlyBracketString from '../../utils/convertCurlyBracketString.js';
@@ -149,7 +147,7 @@
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let member = (interaction.member as Discord.GuildMember)
-    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
+    if (!member.permissions.has("MANAGE_CHANNELS")) throw "You must have the *Manage Channels* permission to use this command"
     return true;
 }
 
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index 44f974e..8ebcf30 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -22,13 +22,14 @@
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
     let m;
-    m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
+    m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true})
     let options = {
         enabled: interaction.options.getString("enabled") as string | boolean,
         category: interaction.options.getChannel("category"),
         maxtickets: interaction.options.getNumber("maxticketsperuser"),
         supportping: interaction.options.getRole("supportrole")
     }
+    console.log(m)
     if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
         options.enabled = options.enabled === "yes" ? true : false;
         if (options.category) {
diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts
index f6c1df1..c2e0986 100644
--- a/src/commands/user/track.ts
+++ b/src/commands/user/track.ts
@@ -166,10 +166,12 @@
     if (member.id === interaction.guild.ownerId) return true
     // Check if the user can manage any of the tracks
     let managed = false
-    tracks.forEach(element => {
-        if (!element.track.manageableBy) return
-        if (element.track.manageableBy.some(role => member.roles.cache.has(role))) managed = true
-    });
+    for (const element of tracks) {
+        if (!element.track.manageableBy) continue
+        if (!element.track.manageableBy.some(role => member.roles.cache.has(role))) continue
+        managed = true;
+        break;
+    };
     // Check if the user has manage_roles permission
     if (!managed && ! member.permissions.has("MANAGE_ROLES")) throw "You do not have the *Manage Roles* permission";
     // Allow track