few changes
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index e94035e..8da4f1c 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -4,7 +4,8 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js'
+import readConfig from '../../utils/readConfig.js';
+import addPlurals from "../../utils/plurals.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -27,7 +28,7 @@
             "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
         })
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
-        + `${interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0} day${interaction.options.getInteger("delete") === 1 || interaction.options.getInteger("delete") === null ? "s" : ""} of messages will be deleted\n\n` // TODO:[s addition]
+        + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
         + `Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
 //        pluralize("day", interaction.options.getInteger("delete"))
@@ -57,10 +58,37 @@
             }
         } catch {}
         try {
-            (interaction.options.getMember("user") as GuildMember).ban({
+            let member = (interaction.options.getMember("user") as GuildMember)
+            let reason = interaction.options.getString("reason") ?? "No reason provided"
+            member.ban({
                 days: Number(interaction.options.getInteger("delete") ?? 0),
-                reason: interaction.options.getString("reason") ?? "No reason provided"
+                reason: reason
             })
+            // @ts-ignore
+            const { log, NucleusColors, entry, renderUser, renderDelta } = interaction.user.client.logger
+            let data = {
+                meta: {
+                    type: 'memberBan',
+                    displayName: 'Member Banned',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.red,
+                    emoji: "PUNISH.BAN.RED",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    id: entry(member.user.id, `\`${member.user.id}\``),
+                    name: entry(member.user.id, renderUser(member.user)),
+                    banned: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    bannedBy: entry(interaction.user.id, renderUser(interaction.user)),
+                    reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
+                    accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)),
+                    serverMemberCount: interaction.guild.memberCount,
+                },
+                hidden: {
+                    guild: interaction.guild.id
+                }
+            }
+            log(data, member.user.client);
         } catch {
             await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setEmoji("PUNISH.BAN.RED")
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index 34a1571..b6ab653 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -1,4 +1,5 @@
 import { CommandInteraction, GuildMember, MessageActionRow, MessageButton } from "discord.js";
+import humanizeDuration from 'humanize-duration';
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -56,6 +57,35 @@
         } catch {}
         try {
             (interaction.options.getMember("user") as GuildMember).kick(interaction.options.getString("reason") ?? "No reason provided.")
+            let member = (interaction.options.getMember("user") as GuildMember)
+            let reason = interaction.options.getString("reason") ?? null
+            // @ts-ignore
+            const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
+            let data = {
+                meta: {
+                    type: 'memberKick',
+                    displayName: 'Member Kicked',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.red,
+                    emoji: "PUNISH.KICK.RED",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    id: entry(member.id, `\`${member.id}\``),
+                    name: entry(member.id, renderUser(member.user)),
+                    joined: entry(member.joinedAt, renderDelta(member.joinedAt)),
+                    kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    kickedBy: entry(interaction.user.id, renderUser(interaction.user)),
+                    reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
+                    timeInServer: entry(new Date().getTime() - member.joinedTimestamp, humanizeDuration(new Date().getTime() - member.joinedTimestamp, { round: true })),
+                    accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)),
+                    serverMemberCount: member.guild.memberCount,
+                },
+                hidden: {
+                    guild: member.guild.id
+                }
+            }
+            log(data, member.client);
         } catch {
             await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setEmoji("PUNISH.KICK.RED")
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index 165d906..3d6d912 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -21,7 +21,6 @@
     .addStringOption(option => option.setName("reason").setDescription("The reason for the mute").setRequired(false))
     .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are muted | Default yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]]))
-    // TODO: notify the user when the mute is lifted
 
 const callback = async (interaction: CommandInteraction) => {
     // @ts-ignore
@@ -34,6 +33,10 @@
         minutes: interaction.options.getInteger("minutes") || 0,
         seconds: interaction.options.getInteger("seconds") || 0
     }
+    let config = await readConfig(interaction.guild.id)
+    let serverSettingsDescription = (config.moderation.mute.timeout ? "given a timeout" : "")
+    if (config.moderation.mute.role) serverSettingsDescription += (serverSettingsDescription ? " and " : "") + `given the <@&${config.moderation.mute.role}> role`
+
     let muteTime = (time.days * 24 * 60 * 60) + (time.hours * 60 * 60) + (time.minutes * 60) + time.seconds
     if (muteTime == 0) {
         let m = await interaction.reply({embeds: [
@@ -126,6 +129,7 @@
             "time": `${humanizeDuration(muteTime * 1000, {round: true})}`,
             "reason": `\n> ${reason ? reason : "*No reason provided*"}`
         })
+        + `The user will be ` + serverSettingsDescription + "\n"
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
         + `Are you sure you want to mute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
@@ -157,7 +161,12 @@
             }
         } catch {}
         try {
-            (interaction.options.getMember("user") as GuildMember).timeout(muteTime * 1000, interaction.options.getString("reason") || "No reason provided")
+            if (config.moderation.mute.timeout) {
+                (interaction.options.getMember("user") as GuildMember).timeout(muteTime * 1000, interaction.options.getString("reason") || "No reason provided")
+            }
+            if (config.moderation.mute.role) {
+                (interaction.options.getMember("user") as GuildMember).roles.add(config.moderation.mute.role)
+            }
         } catch {
             await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setEmoji("PUNISH.MUTE.RED")
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index 9584055..72b2524 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -45,8 +45,8 @@
                     embeds: [new generateEmojiEmbed()
                         .setEmoji("PUNISH.NICKNAME.RED")
                         .setTitle("Nickname changed")
-                        .setDescription(`Your nickname was ${interaction.options.getString("name") ? "changed" : "cleared"} in ${interaction.guild.name}` +
-                                    (interaction.options.getString("name") ? ` it is now: ${interaction.options.getString("name")}` : ".") + "\n\n" +
+                        .setDescription(`Your nickname was ${interaction.options.getString("name") ? "changed" : "cleared"} in ${interaction.guild.name}.` +
+                                    (interaction.options.getString("name") ? ` it is now: ${interaction.options.getString("name")}` : "") + "\n\n" +
                                     (confirmation.buttonClicked ? `You can appeal this in this ticket: <#${confirmation.response}>` : ``))
                         .setStatus("Danger")
                     ]
@@ -55,7 +55,33 @@
             }
         } catch {}
         try {
-            (interaction.options.getMember("user") as GuildMember).setNickname(interaction.options.getString("name") ?? null, "Nucleus Nickname command")
+            let member = (interaction.options.getMember("user") as GuildMember)
+            let before = member.nickname
+            let nickname = interaction.options.getString("name")
+            member.setNickname(nickname ?? null, "Nucleus Nickname command")
+            // @ts-ignore
+            const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = interaction.client.logger
+            let data = {
+                meta: {
+                    type: 'memberUpdate',
+                    displayName: 'Member Updated',
+                    calculateType: 'guildMemberUpdate',
+                    color: NucleusColors.yellow,
+                    emoji: "PUNISH.NICKNAME.YELLOW",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    id: entry(member.id, `\`${member.id}\``),
+                    before: entry(before, before ? before : '*None*'),
+                    after: entry(nickname, nickname ? nickname : '*None*'),
+                    updated: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    updatedBy: entry(interaction.user.id, renderUser(interaction.user))
+                },
+                hidden: {
+                    guild: interaction.guild.id
+                }
+            }
+            log(data, interaction.client);
         } catch {
             await interaction.editReply({embeds: [new generateEmojiEmbed()
                 .setEmoji("PUNISH.NICKNAME.RED")
@@ -99,6 +125,8 @@
     if ((interaction.member as GuildMember).id == interaction.guild.ownerId) return true
     // Check if the user has manage_nicknames permission
     if (! (interaction.member as GuildMember).permissions.has("MANAGE_NICKNAMES")) throw "You do not have the `manage_nicknames` permission";
+    // Allow changing your own nickname
+    if (member == apply) return true
     // Check if the user is below on the role list
     if (! (memberPos > applyPos)) throw "You do not have a role higher than that member"
     // Allow change
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index 29c228f..a1c8fe3 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -107,13 +107,15 @@
             let amount;
             try { amount = parseInt(component.customId); } catch { break; }
             let messages;
-            (interaction.channel as TextChannel).messages.fetch({limit: amount}).then(async (ms) => {
+            await (interaction.channel as TextChannel).messages.fetch({limit: amount}).then(async (ms) => {
                 if (user) {
                     ms = ms.filter(m => m.author.id === user.id)
                 }
                 messages = await (channel as TextChannel).bulkDelete(ms, true);
             })
-            deleted = deleted.concat(messages.map(m => m))
+            if (messages) {
+                deleted = deleted.concat(messages.map(m => m))
+            }
         }
         if (deleted.length === 0) return await interaction.editReply({
             embeds: [
@@ -127,6 +129,28 @@
         })
         let attachmentObject;
         try {
+            // @ts-ignore
+            const { log, NucleusColors, entry, renderUser, renderChannel } = interaction.user.client.logger
+            let data = {
+                meta: {
+                    type: 'channelPurge',
+                    displayName: 'Channel Purged',
+                    calculateType: 'messageDelete',
+                    color: NucleusColors.red,
+                    emoji: "PUNISH.BAN.RED",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    id: entry(interaction.user.id, `\`${interaction.user.id}\``),
+                    purgedBy: entry(interaction.user.id, renderUser(interaction.user)),
+                    channel: entry(interaction.channel.id, renderChannel(interaction.channel)),
+                    messagesCleared: entry(deleted.length, deleted.length),
+                },
+                hidden: {
+                    guild: interaction.guild.id
+                }
+            }
+            log(data, interaction.user.client);
             let out = ""
             deleted.reverse().forEach(message => {
                 out += `${message.author.username}#${message.author.discriminator} (${message.author.id}) [${new Date(message.createdTimestamp).toISOString()}]\n`
@@ -177,7 +201,7 @@
             .setEmoji("CHANNEL.PURGE.RED")
             .setTitle("Purge")
             .setDescription(keyValueList({
-                "channel": `<#${channel.id}> (${(channel as GuildChannel).name})` + ("[This channel]"),
+                "channel": `<#${channel.id}>`,
                 "amount": interaction.options.getInteger("amount").toString(),
                 "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
             }))
@@ -206,6 +230,28 @@
             }
             let attachmentObject;
             try {
+                // @ts-ignore
+                const { log, NucleusColors, entry, renderUser, renderChannel } = interaction.user.client.logger
+                let data = {
+                    meta: {
+                        type: 'channelPurge',
+                        displayName: 'Channel Purged',
+                        calculateType: 'messageDelete',
+                        color: NucleusColors.red,
+                        emoji: "PUNISH.BAN.RED",
+                        timestamp: new Date().getTime()
+                    },
+                    list: {
+                        id: entry(interaction.user.id, `\`${interaction.user.id}\``),
+                        purgedBy: entry(interaction.user.id, renderUser(interaction.user)),
+                        channel: entry(interaction.channel.id, renderChannel(interaction.channel)),
+                        messagesCleared: entry(messages.size, messages.size),
+                    },
+                    hidden: {
+                        guild: interaction.guild.id
+                    }
+                }
+                log(data, interaction.user.client);
                 let out = ""
                 messages.reverse().forEach(message => {
                     out += `${message.author.username}#${message.author.discriminator} (${message.author.id}) [${new Date(message.createdTimestamp).toISOString()}]\n`
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index 44d425a..2b9643d 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -4,7 +4,8 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import readConfig from '../../utils/readConfig.js'
+import readConfig from '../../utils/readConfig.js';
+import addPlurals from '../../utils/plurals.js';
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -27,7 +28,7 @@
             "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
         })
         + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
-        + `${interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0} day${interaction.options.getInteger("delete") === 1 || interaction.options.getInteger("delete") === null ? "s" : ""} of messages will be deleted\n\n` // TODO:[s addition]
+        + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
         + `Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
         .setColor("Danger")
 //        pluralize("day", interaction.options.getInteger("delete"))
diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts
index 3ffc001..f96a7bd 100644
--- a/src/commands/mod/unban.ts
+++ b/src/commands/mod/unban.ts
@@ -1,18 +1,102 @@
-import { CommandInteraction } from "discord.js";
+import { CommandInteraction, GuildMember, User } from "discord.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
+import generateEmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+import confirmationMessage from "../../utils/confirmationMessage.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
     .setName("unban")
     .setDescription("Unbans a user")
+    .addStringOption(option => option.setName("user").setDescription("The user to unban (Username or ID)").setRequired(true))
 
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [mod/unban]");
+const callback = async (interaction: CommandInteraction) => { // TODO: User search
+    let bans = await interaction.guild.bans.fetch()
+    let user = interaction.options.getString("user")
+    let resolved = bans.find(ban => ban.user.id == user)
+    if (!resolved) resolved = bans.find(ban => ban.user.username.toLowerCase() == user.toLowerCase())
+    if (!resolved) resolved = bans.find(ban => ban.user.tag.toLowerCase() == user.toLowerCase())
+    if (!resolved) {
+        return interaction.reply({embeds: [new generateEmojiEmbed()
+            .setTitle("Unban")
+            .setDescription(`Could not find any user called \`${user}\``)
+            .setEmoji("PUNISH.UNBAN.RED")
+            .setStatus("Danger")
+        ], ephemeral: true})
+    }
+    // TODO:[Modals] Replace this with a modal
+    let confirmation = await new confirmationMessage(interaction)
+        .setEmoji("PUNISH.UNBAN.RED")
+        .setTitle("Unban")
+        .setDescription(keyValueList({
+            "user": `${resolved.user.username} [<@${resolved.user.id}>]`,
+        })
+        + `Are you sure you want to unban <@${resolved.user.id}>?`)
+        .setColor("Danger")
+    .send()
+    if (confirmation.success) {
+        try {
+            await interaction.guild.members.unban(resolved.user as User, "Unban");
+            let member = (resolved.user as User)
+            // @ts-ignore
+            const { log, NucleusColors, entry, renderUser, renderDelta } = interaction.user.client.logger
+            let data = {
+                meta: {
+                    type: 'memberUnban',
+                    displayName: 'Member Unbanned',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.green,
+                    emoji: "PUNISH.BAN.GREEN",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    id: entry(member.id, `\`${member.id}\``),
+                    name: entry(member.id, renderUser(member)),
+                    unbanned: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    unbannedBy: entry(interaction.user.id, renderUser(interaction.user)),
+                    accountCreated: entry(member.createdAt, renderDelta(member.createdAt)),
+                },
+                hidden: {
+                    guild: interaction.guild.id
+                }
+            }
+            log(data, member.client);
+        } catch {
+            await interaction.editReply({embeds: [new generateEmojiEmbed()
+                .setEmoji("PUNISH.UNBAN.RED")
+                .setTitle(`Unban`)
+                .setDescription("Something went wrong and the user was not unbanned")
+                .setStatus("Danger")
+            ], components: []})
+        }
+        await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setEmoji(`PUNISH.UNBAN.GREEN`)
+            .setTitle(`Unban`)
+            .setDescription("The member was unbanned")
+            .setStatus("Success")
+        ], components: []})
+    } else {
+        await interaction.editReply({embeds: [new generateEmojiEmbed()
+            .setEmoji("PUNISH.UNBAN.GREEN")
+            .setTitle(`Unban`)
+            .setDescription("No changes were made")
+            .setStatus("Success")
+        ], components: []})
+    }
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
+    let member = (interaction.member as GuildMember)
+    let me = (interaction.guild.me as GuildMember)
+    // Check if Nucleus can unban members
+    if (! interaction.guild.me.permissions.has("BAN_MEMBERS")) throw "I do not have the `ban_members` permission";
+    // Allow the owner to unban anyone
+    if ((interaction.member as GuildMember).id == interaction.guild.ownerId) return true
+    // Check if the user has ban_members permission
+    if (! (interaction.member as GuildMember).permissions.has("BAN_MEMBERS")) throw "You do not have the `ban_members` permission";
+    // Allow unban
+    return true
 }
 
 export { command, callback, check };
\ No newline at end of file
diff --git a/src/commands/nucleus/suggest.ts b/src/commands/nucleus/suggest.ts
index 36338dc..0512020 100644
--- a/src/commands/nucleus/suggest.ts
+++ b/src/commands/nucleus/suggest.ts
@@ -11,9 +11,9 @@
     .addStringOption(option => option.setName("suggestion").setDescription("The suggestion to send").setRequired(true))
 
 const callback = async (interaction: CommandInteraction) => {
-	// @ts-ignore
+    // @ts-ignore
     const { renderUser } = interaction.client.logger
-	let suggestion = interaction.options.getString("suggestion");
+    let suggestion = interaction.options.getString("suggestion");
     let confirmation = await new confirmationMessage(interaction)
         .setEmoji("ICONS.OPP.ADD")
         .setTitle("Suggest")
@@ -23,14 +23,14 @@
     .send()
     if (confirmation.success) {
         await (interaction.client.channels.cache.get('955161206459600976') as Discord.TextChannel).send({
-			embeds: [
-				new generateEmojiEmbed()
-					.setTitle(`Suggestion`)
-					.setDescription(`**From:** ${renderUser(interaction.member.user)}\n**Suggestion:**\n> ${suggestion}`)
-					.setStatus("Danger")
-					.setEmoji("NUCLEUS.LOGO")
-			]
-		})
+            embeds: [
+                new generateEmojiEmbed()
+                    .setTitle(`Suggestion`)
+                    .setDescription(`**From:** ${renderUser(interaction.member.user)}\n**Suggestion:**\n> ${suggestion}`)
+                    .setStatus("Danger")
+                    .setEmoji("NUCLEUS.LOGO")
+            ]
+        })
         await interaction.editReply({embeds: [new generateEmojiEmbed()
             .setEmoji("ICONS.ADD")
             .setTitle(`Suggest`)
diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts
index da6a37a..ebbef82 100644
--- a/src/commands/user/track.ts
+++ b/src/commands/user/track.ts
@@ -5,6 +5,7 @@
 import getEmojiByName from "../../utils/getEmojiByName.js";
 import generateKeyValueList from "../../utils/generateKeyValueList.js";
 import readConfig from "../../utils/readConfig.js";
+import addPlural from "../../utils/plurals.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -45,7 +46,7 @@
                 default: index == track,
                 label: option.name,
                 value: index.toString(),
-                description: option.track.length == 0 ? "No" : option.track.length + " role" + (option.track.length == 1 ? "" : "s"), // TODO[s addition]
+                description: option.track.length == 0 ? "No" : addPlural(option.track.length, "role"),
                 emoji: interaction.client.emojis.resolve(getEmojiByName("TRACKS.SINGLE." + (hasRoleInTrack ? "ACTIVE" : "INACTIVE"), "id"))
             })
         })).setCustomId("select").setMaxValues(1)