diff --git a/locale/Messages.resx b/locale/Messages.resx
index 84d7fa7..3d05722 100644
--- a/locale/Messages.resx
+++ b/locale/Messages.resx
@@ -600,4 +600,46 @@
     <data name="AllSettingsReset" xml:space="preserve">
         <value>All settings have been reset</value>
     </data>
+    <data name="ShowInfoDisplayName" xml:space="preserve">
+        <value>Display name</value>
+    </data>
+    <data name="ShowInfoTitle" xml:space="preserve">
+        <value>Information about {0}</value>
+    </data>
+    <data name="ShowInfoMuted" xml:space="preserve">
+        <value>Muted</value>
+    </data>
+    <data name="ShowInfoDiscordUserSince" xml:space="preserve">
+        <value>Discord user since</value>
+    </data>
+    <data name="ShowInfoBanned" xml:space="preserve">
+        <value>Banned</value>
+    </data>
+    <data name="ShowInfoPunishments" xml:space="preserve">
+        <value>Punishments</value>
+    </data>
+    <data name="ShowInfoBannedPermanently" xml:space="preserve">
+        <value>Banned permanently</value>
+    </data>
+    <data name="ShowInfoNotOnGuild" xml:space="preserve">
+        <value>Not in the guild</value>
+    </data>
+    <data name="ShowInfoMutedByTimeout" xml:space="preserve">
+        <value>Muted by timeout</value>
+    </data>
+    <data name="ShowInfoMutedByMuteRole" xml:space="preserve">
+        <value>Muted by mute role</value>
+    </data>
+    <data name="ShowInfoGuildMemberSince" xml:space="preserve">
+        <value>Guild member since</value>
+    </data>
+    <data name="ShowInfoGuildNickname" xml:space="preserve">
+        <value>Nickname</value>
+    </data>
+    <data name="ShowInfoGuildRoles" xml:space="preserve">
+        <value>Roles</value>
+    </data>
+    <data name="ShowInfoGuildMemberPremiumSince" xml:space="preserve">
+        <value>Nitro booster since</value>
+    </data>
 </root>
diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx
index abeaf88..3f20dd0 100644
--- a/locale/Messages.ru.resx
+++ b/locale/Messages.ru.resx
@@ -600,4 +600,46 @@
     <data name="AllSettingsReset" xml:space="preserve">
         <value>Все настройки были сброшены</value>
     </data>
+    <data name="ShowInfoDisplayName" xml:space="preserve">
+        <value>Отображаемое имя</value>
+    </data>
+    <data name="ShowInfoTitle" xml:space="preserve">
+        <value>Информация о {0}</value>
+    </data>
+    <data name="ShowInfoMuted" xml:space="preserve">
+        <value>Заглушен</value>
+    </data>
+    <data name="ShowInfoDiscordUserSince" xml:space="preserve">
+        <value>Вступил в Discord</value>
+    </data>
+    <data name="ShowInfoBanned" xml:space="preserve">
+        <value>Забанен</value>
+    </data>
+    <data name="ShowInfoPunishments" xml:space="preserve">
+        <value>Наказания</value>
+    </data>
+    <data name="ShowInfoBannedPermanently" xml:space="preserve">
+        <value>Забанен навсегда</value>
+    </data>
+    <data name="ShowInfoNotOnGuild" xml:space="preserve">
+        <value>Не на сервере</value>
+    </data>
+    <data name="ShowInfoMutedByTimeout" xml:space="preserve">
+        <value>Заглушен с помощью тайм-аута</value>
+    </data>
+    <data name="ShowInfoMutedByMuteRole" xml:space="preserve">
+        <value>Заглушен с помощью роли мута</value>
+    </data>
+    <data name="ShowInfoGuildMemberSince" xml:space="preserve">
+        <value>Вступил на сервер</value>
+    </data>
+    <data name="ShowInfoGuildNickname" xml:space="preserve">
+        <value>Никнейм</value>
+    </data>
+    <data name="ShowInfoGuildRoles" xml:space="preserve">
+        <value>Роли</value>
+    </data>
+    <data name="ShowInfoGuildMemberPremiumSince" xml:space="preserve">
+        <value>Начал бустить сервер</value>
+    </data>
 </root>
diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx
index fb01e72..a564e1c 100644
--- a/locale/Messages.tt-ru.resx
+++ b/locale/Messages.tt-ru.resx
@@ -600,4 +600,46 @@
     <data name="AllSettingsReset" xml:space="preserve">
         <value>откатываемся к заводским...</value>
     </data>
+    <data name="ShowInfoDisplayName" xml:space="preserve">
+        <value>дисплейнейм</value>
+    </data>
+    <data name="ShowInfoTitle" xml:space="preserve">
+        <value>деанон {0}</value>
+    </data>
+    <data name="ShowInfoMuted" xml:space="preserve">
+        <value>замучен</value>
+    </data>
+    <data name="ShowInfoDiscordUserSince" xml:space="preserve">
+        <value>юзер Discord со времен</value>
+    </data>
+    <data name="ShowInfoBanned" xml:space="preserve">
+        <value>забанен</value>
+    </data>
+    <data name="ShowInfoPunishments" xml:space="preserve">
+        <value>приколы полученные по заслугам</value>
+    </data>
+    <data name="ShowInfoBannedPermanently" xml:space="preserve">
+        <value>забанен</value>
+    </data>
+    <data name="ShowInfoNotOnGuild" xml:space="preserve">
+        <value>вышел из сервера</value>
+    </data>
+    <data name="ShowInfoMutedByTimeout" xml:space="preserve">
+        <value>замучен таймаутом</value>
+    </data>
+    <data name="ShowInfoMutedByMuteRole" xml:space="preserve">
+        <value>замучен ролькой</value>
+    </data>
+    <data name="ShowInfoGuildMemberSince" xml:space="preserve">
+        <value>участник сервера со времен</value>
+    </data>
+    <data name="ShowInfoGuildNickname" xml:space="preserve">
+        <value>сервернейм</value>
+    </data>
+    <data name="ShowInfoGuildRoles" xml:space="preserve">
+        <value>рольки</value>
+    </data>
+    <data name="ShowInfoGuildMemberPremiumSince" xml:space="preserve">
+        <value>бустит сервер со времен</value>
+    </data>
 </root>
diff --git a/src/Boyfriend.cs b/src/Boyfriend.cs
index 246869e..4ba43f8 100644
--- a/src/Boyfriend.cs
+++ b/src/Boyfriend.cs
@@ -102,7 +102,8 @@ public sealed class Boyfriend
                         .WithCommandGroup<MuteCommandGroup>()
                         .WithCommandGroup<PingCommandGroup>()
                         .WithCommandGroup<RemindCommandGroup>()
-                        .WithCommandGroup<SettingsCommandGroup>();
+                        .WithCommandGroup<SettingsCommandGroup>()
+                        .WithCommandGroup<ToolsCommandGroup>();
                     var responderTypes = typeof(Boyfriend).Assembly
                         .GetExportedTypes()
                         .Where(t => t.IsResponder());
diff --git a/src/Commands/ToolsCommandGroup.cs b/src/Commands/ToolsCommandGroup.cs
new file mode 100644
index 0000000..3ac8b70
--- /dev/null
+++ b/src/Commands/ToolsCommandGroup.cs
@@ -0,0 +1,231 @@
+using System.ComponentModel;
+using System.Drawing;
+using System.Text;
+using Boyfriend.Data;
+using Boyfriend.Services;
+using JetBrains.Annotations;
+using Remora.Commands.Attributes;
+using Remora.Commands.Groups;
+using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Abstractions.Rest;
+using Remora.Discord.Commands.Attributes;
+using Remora.Discord.Commands.Contexts;
+using Remora.Discord.Commands.Feedback.Services;
+using Remora.Discord.Extensions.Embeds;
+using Remora.Discord.Extensions.Formatting;
+using Remora.Rest.Core;
+using Remora.Results;
+
+namespace Boyfriend.Commands;
+
+/// <summary>
+///     Handles commands related to tools: /showinfo.
+/// </summary>
+[UsedImplicitly]
+public class ToolsCommandGroup : CommandGroup
+{
+    private readonly ICommandContext _context;
+    private readonly FeedbackService _feedback;
+    private readonly IDiscordRestGuildAPI _guildApi;
+    private readonly GuildDataService _guildData;
+    private readonly IDiscordRestUserAPI _userApi;
+
+    public ToolsCommandGroup(
+        ICommandContext context, FeedbackService feedback,
+        GuildDataService guildData, IDiscordRestGuildAPI guildApi,
+        IDiscordRestUserAPI userApi, IDiscordRestChannelAPI channelApi)
+    {
+        _context = context;
+        _guildData = guildData;
+        _feedback = feedback;
+        _guildApi = guildApi;
+        _userApi = userApi;
+    }
+
+    /// <summary>
+    ///     A slash command that shows information about user.
+    /// </summary>
+    /// <remarks>
+    ///     Information in the output:
+    ///     <list type="bullet">
+    ///         <item>Display name</item>
+    ///         <item>Discord user since</item>
+    ///         <item>Guild nickname</item>
+    ///         <item>Guild member since</item>
+    ///         <item>Nitro booster since</item>
+    ///         <item>Guild roles</item>
+    ///         <item>Active mute information</item>
+    ///         <item>Active ban information</item>
+    ///         <item>Is on guild status</item>
+    ///     </list>
+    /// </remarks>
+    /// <param name="target">The user to show info about.</param>
+    /// <returns>
+    ///     A feedback sending result which may or may not have succeeded.
+    /// </returns>
+    [Command("showinfo")]
+    [DiscordDefaultDMPermission(false)]
+    [Description("Shows info about user")]
+    [UsedImplicitly]
+    public async Task<Result> ExecuteShowInfoAsync(
+        [Description("User to show info about")]
+        IUser? target = null)
+    {
+        if (!_context.TryGetContextIDs(out var guildId, out _, out var userId))
+        {
+            return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
+        }
+
+        var userResult = await _userApi.GetUserAsync(userId, CancellationToken);
+        if (!userResult.IsDefined(out var user))
+        {
+            return Result.FromError(userResult);
+        }
+
+        var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken);
+        if (!currentUserResult.IsDefined(out var currentUser))
+        {
+            return Result.FromError(currentUserResult);
+        }
+
+        var data = await _guildData.GetData(guildId, CancellationToken);
+        Messages.Culture = GuildSettings.Language.Get(data.Settings);
+
+        return await ShowUserInfoAsync(target ?? user, currentUser, data, guildId, CancellationToken);
+    }
+
+    private async Task<Result> ShowUserInfoAsync(
+        IUser user, IUser currentUser, GuildData data, Snowflake guildId, CancellationToken ct = default)
+    {
+        var builder = new StringBuilder().AppendLine($"### <@{user.ID}>");
+
+        if (user.GlobalName is not null)
+        {
+            builder.Append("- ").AppendLine(Messages.ShowInfoDisplayName)
+                .AppendLine(Markdown.InlineCode(user.GlobalName));
+        }
+
+        builder.Append("- ").AppendLine(Messages.ShowInfoDiscordUserSince)
+            .AppendLine(Markdown.Timestamp(user.ID.Timestamp));
+
+        var memberData = data.GetOrCreateMemberData(user.ID);
+
+        var embedColor = ColorsList.Cyan;
+
+        var guildMemberResult = await _guildApi.GetGuildMemberAsync(guildId, user.ID, ct);
+        DateTimeOffset? communicationDisabledUntil = null;
+        if (guildMemberResult.IsDefined(out var guildMember))
+        {
+            communicationDisabledUntil = guildMember.CommunicationDisabledUntil.OrDefault(null);
+
+            embedColor = AppendGuildInformation(embedColor, guildMember, builder);
+        }
+
+        var isMuted = (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) ||
+                      communicationDisabledUntil is not null;
+
+        var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, user.ID, ct);
+
+        if (isMuted || existingBanResult.IsDefined())
+        {
+            builder.Append("### ")
+                .AppendLine(Markdown.Bold(Messages.ShowInfoPunishments));
+        }
+
+        if (isMuted)
+        {
+            AppendMuteInformation(memberData, communicationDisabledUntil, builder);
+
+            embedColor = ColorsList.Red;
+        }
+
+        if (existingBanResult.IsDefined())
+        {
+            AppendBanInformation(memberData, builder);
+
+            embedColor = ColorsList.Black;
+        }
+
+        if (!guildMemberResult.IsSuccess && !existingBanResult.IsDefined())
+        {
+            builder.Append("### ")
+                .AppendLine(Markdown.Bold(Messages.ShowInfoNotOnGuild));
+
+            embedColor = ColorsList.Default;
+        }
+
+        var embed = new EmbedBuilder().WithSmallTitle(
+                string.Format(Messages.ShowInfoTitle, user.GetTag()), currentUser)
+            .WithDescription(builder.ToString())
+            .WithColour(embedColor)
+            .WithLargeAvatar(user)
+            .WithFooter($"ID: {user.ID.ToString()}")
+            .Build();
+
+        return await _feedback.SendContextualEmbedResultAsync(embed, ct);
+    }
+
+    private static Color AppendGuildInformation(Color color, IGuildMember guildMember, StringBuilder builder)
+    {
+        if (guildMember.Nickname.IsDefined(out var nickname))
+        {
+            builder.Append("- ").AppendLine(Messages.ShowInfoGuildNickname)
+                .AppendLine(Markdown.InlineCode(nickname));
+        }
+
+        builder.Append("- ").AppendLine(Messages.ShowInfoGuildMemberSince)
+            .AppendLine(Markdown.Timestamp(guildMember.JoinedAt));
+
+        if (guildMember.PremiumSince.IsDefined(out var premiumSince))
+        {
+            builder.Append("- ").AppendLine(Messages.ShowInfoGuildMemberPremiumSince)
+                .AppendLine(Markdown.Timestamp(premiumSince.Value));
+            color = ColorsList.Magenta;
+        }
+
+        if (guildMember.Roles.Count > 0)
+        {
+            builder.Append("- ").AppendLine(Messages.ShowInfoGuildRoles);
+            for (var i = 0; i < guildMember.Roles.Count - 1; i++)
+            {
+                builder.Append($"<@&{guildMember.Roles[i]}>, ");
+            }
+
+            builder.AppendLine($"<@&{guildMember.Roles[^1]}>");
+        }
+
+        return color;
+    }
+
+    private static void AppendBanInformation(MemberData memberData, StringBuilder builder)
+    {
+        if (memberData.BannedUntil < DateTimeOffset.MaxValue)
+        {
+            builder.Append("- ").AppendLine(Messages.ShowInfoBanned)
+                .Append(" - ").AppendLine(string.Format(
+                    Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.BannedUntil.Value)));
+            return;
+        }
+
+        builder.Append("- ").AppendLine(Messages.ShowInfoBannedPermanently);
+    }
+
+    private static void AppendMuteInformation(
+        MemberData memberData, DateTimeOffset? communicationDisabledUntil, StringBuilder builder)
+    {
+        builder.Append("- ").AppendLine(Messages.ShowInfoMuted);
+        if (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil)
+        {
+            builder.Append(" - ").AppendLine(Messages.ShowInfoMutedByMuteRole)
+                .Append(" - ").AppendLine(string.Format(
+                    Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.MutedUntil.Value)));
+        }
+
+        if (communicationDisabledUntil is not null)
+        {
+            builder.Append(" - ").AppendLine(Messages.ShowInfoMutedByTimeout)
+                .Append(" - ").AppendLine(string.Format(
+                    Messages.DescriptionActionExpiresAt, Markdown.Timestamp(communicationDisabledUntil.Value)));
+        }
+    }
+}
diff --git a/src/Extensions.cs b/src/Extensions.cs
index 9a05e80..1e30cf4 100644
--- a/src/Extensions.cs
+++ b/src/Extensions.cs
@@ -74,6 +74,23 @@ public static class Extensions
         return builder;
     }
 
+    /// <summary>
+    ///     Adds a user avatar in the thumbnail field.
+    /// </summary>
+    /// <param name="builder">The builder to add the thumbnail to.</param>
+    /// <param name="avatarSource">The user whose avatar to use in the thumbnail field.</param>
+    /// <returns>The builder with the added avatar in the thumbnail field.</returns>
+    public static EmbedBuilder WithLargeAvatar(
+        this EmbedBuilder builder, IUser avatarSource)
+    {
+        var avatarUrlResult = CDN.GetUserAvatarUrl(avatarSource, imageSize: 256);
+        var avatarUrl = avatarUrlResult.IsSuccess
+            ? avatarUrlResult.Entity
+            : CDN.GetDefaultUserAvatarUrl(avatarSource, imageSize: 256).Entity;
+
+        return builder.WithThumbnailUrl(avatarUrl.AbsoluteUri);
+    }
+
     /// <summary>
     ///     Adds a footer representing that the action was performed in the <paramref name="guild" />.
     /// </summary>
diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs
index b45ae70..884cfd4 100644
--- a/src/Messages.Designer.cs
+++ b/src/Messages.Designer.cs
@@ -1013,5 +1013,89 @@ namespace Boyfriend {
                 return ResourceManager.GetString("AllSettingsReset", resourceCulture);
             }
         }
+
+        internal static string ShowInfoTitle {
+            get {
+                return ResourceManager.GetString("ShowInfoTitle", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoDisplayName {
+            get {
+                return ResourceManager.GetString("ShowInfoDisplayName", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoDiscordUserSince {
+            get {
+                return ResourceManager.GetString("ShowInfoDiscordUserSince", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoMuted {
+            get {
+                return ResourceManager.GetString("ShowInfoMuted", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoBanned {
+            get {
+                return ResourceManager.GetString("ShowInfoBanned", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoPunishments {
+            get {
+                return ResourceManager.GetString("ShowInfoPunishments", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoBannedPermanently {
+            get {
+                return ResourceManager.GetString("ShowInfoBannedPermanently", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoNotOnGuild {
+            get {
+                return ResourceManager.GetString("ShowInfoNotOnGuild", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoMutedByTimeout {
+            get {
+                return ResourceManager.GetString("ShowInfoMutedByTimeout", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoMutedByMuteRole {
+            get {
+                return ResourceManager.GetString("ShowInfoMutedByMuteRole", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoGuildMemberSince {
+            get {
+                return ResourceManager.GetString("ShowInfoGuildMemberSince", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoGuildNickname {
+            get {
+                return ResourceManager.GetString("ShowInfoGuildNickname", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoGuildRoles {
+            get {
+                return ResourceManager.GetString("ShowInfoGuildRoles", resourceCulture);
+            }
+        }
+
+        internal static string ShowInfoGuildMemberPremiumSince {
+            get {
+                return ResourceManager.GetString("ShowInfoGuildMemberPremiumSince", resourceCulture);
+            }
+        }
     }
 }