diff --git a/src/Commands/MuteCommandGroup.cs b/src/Commands/MuteCommandGroup.cs
index ce70d68..bc64a64 100644
--- a/src/Commands/MuteCommandGroup.cs
+++ b/src/Commands/MuteCommandGroup.cs
@@ -101,11 +101,17 @@ public class MuteCommandGroup : CommandGroup
             return await _feedback.SendContextualEmbedResultAsync(embed, CancellationToken);
         }
 
-        return await MuteUserAsync(
+        if (GuildSettings.MuteRole.Get(data.Settings) != 0)
+        {
+            return await RoleMuteUserAsync(
+                target, reason, duration, guildId, data, channelId, user, currentUser, CancellationToken);
+        }
+
+        return await TimeoutUserAsync(
             target, reason, duration, guildId, data, channelId, user, currentUser, CancellationToken);
     }
 
-    private async Task<Result> MuteUserAsync(
+    private async Task<Result> RoleMuteUserAsync(
         IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId,
         IUser user, IUser currentUser, CancellationToken ct = default)
     {
@@ -125,10 +131,80 @@ public class MuteCommandGroup : CommandGroup
             return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct);
         }
 
+        var until = DateTimeOffset.UtcNow.Add(duration); // >:)
+        var memberData = data.GetOrCreateMemberData(target.ID);
+        memberData.MutedUntil = until;
+        var assignRoles = new List<Snowflake>
+        {
+            GuildSettings.MuteRole.Get(data.Settings)
+        };
+        if (!GuildSettings.RemoveRolesOnMute.Get(data.Settings))
+        {
+            assignRoles.AddRange(memberData.Roles.ConvertAll(r => r.ToSnowflake()));
+        }
+
+        var muteResult = await _guildApi.ModifyGuildMemberAsync(
+            guildId, target.ID, roles: assignRoles,
+            reason: $"({user.GetTag()}) {reason}".EncodeHeader(), ct: ct);
+        if (!muteResult.IsSuccess)
+        {
+            return Result.FromError(muteResult.Error);
+        }
+
+        var title = string.Format(Messages.UserMuted, target.GetTag());
+        var description = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason))
+            .Append(
+                string.Format(
+                    Messages.DescriptionActionExpiresAt, Markdown.Timestamp(until))).ToString();
+
+        var logResult = _utility.LogActionAsync(
+            data.Settings, channelId, user, title, description, target, ColorsList.Red, ct: ct);
+        if (!logResult.IsSuccess)
+        {
+            return Result.FromError(logResult.Error);
+        }
+
+        var embed = new EmbedBuilder().WithSmallTitle(
+                string.Format(Messages.UserMuted, target.GetTag()), target)
+            .WithColour(ColorsList.Green).Build();
+
+        return await _feedback.SendContextualEmbedResultAsync(embed, ct);
+    }
+
+    private async Task<Result> TimeoutUserAsync(
+        IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId,
+        IUser user, IUser currentUser, CancellationToken ct = default)
+    {
+        if (duration.TotalDays >= 28)
+        {
+            var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.BotCannotMuteTarget, currentUser)
+                .WithDescription(Messages.DurationRequiredForTimeOuts)
+                .WithColour(ColorsList.Red).Build();
+
+            return await _feedback.SendContextualEmbedResultAsync(failedEmbed, CancellationToken);
+        }
+
+        var interactionResult
+            = await _utility.CheckInteractionsAsync(
+                guildId, user.ID, target.ID, "Mute", ct);
+        if (!interactionResult.IsSuccess)
+        {
+            return Result.FromError(interactionResult);
+        }
+
+        if (interactionResult.Entity is not null)
+        {
+            var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
+                .WithColour(ColorsList.Red).Build();
+
+            return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct);
+        }
+
         var until = DateTimeOffset.UtcNow.Add(duration); // >:)
         var muteResult = await _guildApi.ModifyGuildMemberAsync(
             guildId, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(),
             communicationDisabledUntil: until, ct: ct);
+
         if (!muteResult.IsSuccess)
         {
             return Result.FromError(muteResult.Error);
@@ -211,11 +287,63 @@ public class MuteCommandGroup : CommandGroup
             return await _feedback.SendContextualEmbedResultAsync(embed, CancellationToken);
         }
 
-        return await UnmuteUserAsync(
+        if (data.GetOrCreateMemberData(target.ID).MutedUntil is not null)
+        {
+            return await RemoveMuteRoleUserAsync(
+                target, reason, guildId, data, channelId, user, currentUser, CancellationToken);
+        }
+
+        return await RemoveTimeoutUserAsync(
             target, reason, guildId, data, channelId, user, currentUser, CancellationToken);
     }
 
-    private async Task<Result> UnmuteUserAsync(
+    private async Task<Result> RemoveMuteRoleUserAsync(
+        IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user,
+        IUser currentUser, CancellationToken ct = default)
+    {
+        var interactionResult
+            = await _utility.CheckInteractionsAsync(
+                guildId, user.ID, target.ID, "Unmute", ct);
+        if (!interactionResult.IsSuccess)
+        {
+            return Result.FromError(interactionResult);
+        }
+
+        if (interactionResult.Entity is not null)
+        {
+            var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
+                .WithColour(ColorsList.Red).Build();
+
+            return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct);
+        }
+
+        var memberData = data.GetOrCreateMemberData(target.ID);
+        var unmuteResult = await _guildApi.ModifyGuildMemberAsync(
+            guildId, target.ID, roles: memberData.Roles.ConvertAll(r => r.ToSnowflake()),
+            reason: $"({user.GetTag()}) {reason}".EncodeHeader(), ct: ct);
+        memberData.MutedUntil = null;
+        if (!unmuteResult.IsSuccess)
+        {
+            return Result.FromError(unmuteResult.Error);
+        }
+
+        var title = string.Format(Messages.UserUnmuted, target.GetTag());
+        var description = string.Format(Messages.DescriptionActionReason, reason);
+        var logResult = _utility.LogActionAsync(
+            data.Settings, channelId, user, title, description, target, ColorsList.Green, ct: ct);
+        if (!logResult.IsSuccess)
+        {
+            return Result.FromError(logResult.Error);
+        }
+
+        var embed = new EmbedBuilder().WithSmallTitle(
+                string.Format(Messages.UserUnmuted, target.GetTag()), target)
+            .WithColour(ColorsList.Green).Build();
+
+        return await _feedback.SendContextualEmbedResultAsync(embed, ct);
+    }
+
+    private async Task<Result> RemoveTimeoutUserAsync(
         IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user,
         IUser currentUser, CancellationToken ct = default)
     {
@@ -236,7 +364,7 @@ public class MuteCommandGroup : CommandGroup
         }
 
         var unmuteResult = await _guildApi.ModifyGuildMemberAsync(
-            guildId, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
+            guildId, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(),
             communicationDisabledUntil: null, ct: ct);
         if (!unmuteResult.IsSuccess)
         {
diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs
index 1ff2bc0..72a9ee1 100644
--- a/src/Data/MemberData.cs
+++ b/src/Data/MemberData.cs
@@ -13,6 +13,7 @@ public sealed class MemberData
 
     public ulong Id { get; }
     public DateTimeOffset? BannedUntil { get; set; }
+    public DateTimeOffset? MutedUntil { get; set; }
     public List<ulong> Roles { get; set; } = new();
     public List<Reminder> Reminders { get; } = new();
 }
diff --git a/src/Responders/GuildMemberRolesUpdatedResponder.cs b/src/Responders/GuildMemberRolesUpdatedResponder.cs
index dbd8b3a..eade781 100644
--- a/src/Responders/GuildMemberRolesUpdatedResponder.cs
+++ b/src/Responders/GuildMemberRolesUpdatedResponder.cs
@@ -23,7 +23,11 @@ public class GuildMemberUpdateResponder : IResponder<IGuildMemberUpdate>
     public async Task<Result> RespondAsync(IGuildMemberUpdate gatewayEvent, CancellationToken ct = default)
     {
         var memberData = await _guildData.GetMemberData(gatewayEvent.GuildID, gatewayEvent.User.ID, ct);
-        memberData.Roles = gatewayEvent.Roles.ToList().ConvertAll(r => r.Value);
+        if (memberData.MutedUntil is null)
+        {
+            memberData.Roles = gatewayEvent.Roles.ToList().ConvertAll(r => r.Value);
+        }
+
         return Result.FromSuccess();
     }
 }
diff --git a/src/Services/Update/MemberUpdateService.cs b/src/Services/Update/MemberUpdateService.cs
index 631cc50..d4424ec 100644
--- a/src/Services/Update/MemberUpdateService.cs
+++ b/src/Services/Update/MemberUpdateService.cs
@@ -79,17 +79,9 @@ public sealed partial class MemberUpdateService : BackgroundService
     {
         var failedResults = new List<Result>();
         var id = data.Id.ToSnowflake();
-        if (DateTimeOffset.UtcNow > data.BannedUntil)
-        {
-            var unbanResult = await _guildApi.RemoveGuildBanAsync(
-                guildId, id, Messages.PunishmentExpired.EncodeHeader(), ct);
-            if (unbanResult.IsSuccess)
-            {
-                data.BannedUntil = null;
-            }
 
-            return unbanResult;
-        }
+        var punishmentsResult = await CheckMemberPunishmentsAsync(guildId, id, data, ct);
+        failedResults.AddIfFailed(punishmentsResult);
 
         if (defaultRole.Value is not 0 && !data.Roles.Contains(defaultRole.Value))
         {
@@ -125,6 +117,37 @@ public sealed partial class MemberUpdateService : BackgroundService
         return failedResults.AggregateErrors();
     }
 
+    private async Task<Result> CheckMemberPunishmentsAsync(
+        Snowflake guildId, Snowflake id, MemberData data, CancellationToken ct)
+    {
+        if (DateTimeOffset.UtcNow > data.BannedUntil)
+        {
+            var unbanResult = await _guildApi.RemoveGuildBanAsync(
+                guildId, id, Messages.PunishmentExpired.EncodeHeader(), ct);
+            if (unbanResult.IsSuccess)
+            {
+                data.BannedUntil = null;
+            }
+
+            return unbanResult;
+        }
+
+        if (DateTimeOffset.UtcNow > data.MutedUntil)
+        {
+            var unmuteResult = await _guildApi.ModifyGuildMemberAsync(
+                guildId, id, roles: data.Roles.ConvertAll(r => r.ToSnowflake()),
+                reason: Messages.PunishmentExpired.EncodeHeader(), ct: ct);
+            if (unmuteResult.IsSuccess)
+            {
+                data.MutedUntil = null;
+            }
+
+            return unmuteResult;
+        }
+
+        return Result.FromSuccess();
+    }
+
     private async Task<Result> FilterNicknameAsync(Snowflake guildId, IUser user, IGuildMember member,
         CancellationToken ct)
     {