From 037144311ca588e382a2853d8e261e4c621b2608 Mon Sep 17 00:00:00 2001
From: thomkaptein <thomkaptein@gmail.com>
Date: Sun, 12 Jan 2025 21:25:17 +0100
Subject: [PATCH] improve import manage flow

---
 src/FMBot.Bot/Builders/UserBuilder.cs         |   2 +-
 .../SlashCommands/ImportGroupSlashCommands.cs | 561 ++++++++++++++++++
 .../SlashCommands/ImportSlashCommands.cs      | 535 +++--------------
 .../SlashCommands/UserSlashCommands.cs        | 133 -----
 4 files changed, 643 insertions(+), 588 deletions(-)
 create mode 100644 src/FMBot.Bot/SlashCommands/ImportGroupSlashCommands.cs

diff --git a/src/FMBot.Bot/Builders/UserBuilder.cs b/src/FMBot.Bot/Builders/UserBuilder.cs
index fd9de8be..71aa34b5 100644
--- a/src/FMBot.Bot/Builders/UserBuilder.cs
+++ b/src/FMBot.Bot/Builders/UserBuilder.cs
@@ -1653,7 +1653,7 @@ public async Task<ResponseModel> ImportMode(ContextModel context, int userId)
         {
             embedDescription.AppendLine();
             embedDescription.AppendLine(
-                "Run the `/import spotify` command to see how to request your data and to get started with imports. " +
+                "Run the `.import` command to see how to request your data and to get started with imports. " +
                 "After importing you'll be able to change these settings.");
         }
         else
diff --git a/src/FMBot.Bot/SlashCommands/ImportGroupSlashCommands.cs b/src/FMBot.Bot/SlashCommands/ImportGroupSlashCommands.cs
new file mode 100644
index 00000000..0607b673
--- /dev/null
+++ b/src/FMBot.Bot/SlashCommands/ImportGroupSlashCommands.cs
@@ -0,0 +1,561 @@
+using Discord;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using System.Linq;
+using Discord.Interactions;
+using FMBot.Bot.Attributes;
+using FMBot.Bot.Extensions;
+using FMBot.Bot.Resources;
+using FMBot.Bot.Services;
+using FMBot.Domain.Enums;
+using FMBot.Domain.Models;
+using FMBot.Domain.Interfaces;
+using FMBot.Bot.Models;
+using FMBot.Bot.Builders;
+using Fergun.Interactive;
+
+namespace FMBot.Bot.SlashCommands;
+
+[Group("import", "Manage your data imports")]
+public class ImportGroupSlashCommands : InteractionModuleBase
+{
+    private readonly UserService _userService;
+    private readonly IDataSourceFactory _dataSourceFactory;
+    private readonly ImportService _importService;
+    private readonly PlayService _playService;
+    private readonly IndexService _indexService;
+    private readonly ImportBuilders _importBuilders;
+    private readonly SupporterService _supporterService;
+    private readonly UserBuilder _userBuilder;
+    private InteractiveService Interactivity { get; }
+
+    public ImportGroupSlashCommands(UserService userService,
+        IDataSourceFactory dataSourceFactory,
+        ImportService importService,
+        PlayService playService,
+        IndexService indexService,
+        InteractiveService interactivity,
+        ImportBuilders importBuilders,
+        SupporterService supporterService,
+        UserBuilder userBuilder)
+    {
+        this._userService = userService;
+        this._dataSourceFactory = dataSourceFactory;
+        this._importService = importService;
+        this._playService = playService;
+        this._indexService = indexService;
+        this.Interactivity = interactivity;
+        this._importBuilders = importBuilders;
+        this._supporterService = supporterService;
+        this._userBuilder = userBuilder;
+    }
+
+    private const string SpotifyFileDescription = "Spotify history package (.zip) or history files (.json) ";
+
+    [SlashCommand("spotify", "Import your Spotify history into .fmbot")]
+    [UsernameSetRequired]
+    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
+        InteractionContextType.Guild)]
+    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
+    public async Task SpotifyAsync(
+        [Summary("file-1", SpotifyFileDescription)]
+        IAttachment attachment1 = null,
+        [Summary("file-2", SpotifyFileDescription)]
+        IAttachment attachment2 = null,
+        [Summary("file-3", SpotifyFileDescription)]
+        IAttachment attachment3 = null,
+        [Summary("file-4", SpotifyFileDescription)]
+        IAttachment attachment4 = null,
+        [Summary("file-5", SpotifyFileDescription)]
+        IAttachment attachment5 = null,
+        [Summary("file-6", SpotifyFileDescription)]
+        IAttachment attachment6 = null,
+        [Summary("file-7", SpotifyFileDescription)]
+        IAttachment attachment7 = null,
+        [Summary("file-8", SpotifyFileDescription)]
+        IAttachment attachment8 = null,
+        [Summary("file-9", SpotifyFileDescription)]
+        IAttachment attachment9 = null,
+        [Summary("file-10", SpotifyFileDescription)]
+        IAttachment attachment10 = null,
+        [Summary("file-11", SpotifyFileDescription)]
+        IAttachment attachment11 = null,
+        [Summary("file-12", SpotifyFileDescription)]
+        IAttachment attachment12 = null,
+        [Summary("file-13", SpotifyFileDescription)]
+        IAttachment attachment13 = null,
+        [Summary("file-14", SpotifyFileDescription)]
+        IAttachment attachment14 = null,
+        [Summary("file-15", SpotifyFileDescription)]
+        IAttachment attachment15 = null)
+    {
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+
+        if (this.Context.Interaction.Entitlements.Any() && !SupporterService.IsSupporter(contextUser.UserType))
+        {
+            await this._supporterService.UpdateSingleDiscordSupporter(this.Context.User.Id);
+            this._userService.RemoveUserFromCache(contextUser);
+            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+        }
+
+        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
+
+        if (supporterRequired != null)
+        {
+            await this.Context.SendResponse(this.Interactivity, supporterRequired);
+            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
+            return;
+        }
+
+        var attachments = new List<IAttachment>
+        {
+            attachment1, attachment2, attachment3, attachment4, attachment5, attachment6,
+            attachment7, attachment8, attachment9, attachment10, attachment11, attachment12,
+            attachment13, attachment14, attachment15
+        };
+
+        var noAttachments = attachments.All(a => a == null);
+        attachments = attachments.Where(w => w != null).ToList();
+
+        var description = new StringBuilder();
+        var embed = new EmbedBuilder();
+        embed.WithColor(DiscordConstants.InformationColorBlue);
+
+        if (noAttachments)
+        {
+            var instructionResponse =
+                await this._importBuilders.GetSpotifyImportInstructions(new ContextModel(this.Context, contextUser));
+            await this.Context.SendResponse(this.Interactivity, instructionResponse);
+            this.Context.LogCommandUsed(instructionResponse.CommandResponse);
+            return;
+        }
+
+        await DeferAsync(ephemeral: noAttachments);
+
+        embed.AddField($"{DiscordConstants.Spotify} Importing history into .fmbot..",
+            $"- {DiscordConstants.Loading} Loading import files...");
+        var message = await FollowupAsync(embed: embed.Build());
+
+        try
+        {
+            var imports = await this._importService.HandleSpotifyFiles(contextUser, attachments);
+
+            if (imports.status == ImportStatus.UnknownFailure)
+            {
+                embed.WithColor(DiscordConstants.WarningColorOrange);
+                await UpdateSpotifyImportEmbed(message, embed, description,
+                    $"❌ Invalid Spotify import file. Make sure you select the right files, for example `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.",
+                    true);
+                this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                return;
+            }
+
+            if (imports.status == ImportStatus.WrongPackageFailure)
+            {
+                embed.WithColor(DiscordConstants.WarningColorOrange);
+                await UpdateSpotifyImportEmbed(message, embed, description,
+                    $"❌ Invalid Spotify import files. You have uploaded the wrong Spotify data package.\n\n" +
+                    $"We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package. Instead you have uploaded the 'Account data' package.",
+                    true,
+                    image: "https://fmbot.xyz/img/bot/import-spotify-instructions.png",
+                    components: new ComponentBuilder().WithButton("Spotify privacy page", style: ButtonStyle.Link,
+                        url: "https://www.spotify.com/us/account/privacy/"));
+                this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                return;
+            }
+
+            if (imports.result == null || imports.result.Count == 0 || imports.result.All(a => a.MsPlayed == 0))
+            {
+                if (attachments != null &&
+                    attachments.Any(a => a.Filename != null) &&
+                    attachments.Any(a => a.Filename.ToLower().Contains("streaminghistory")))
+                {
+                    embed.WithColor(DiscordConstants.WarningColorOrange);
+                    await UpdateSpotifyImportEmbed(message, embed, description,
+                        $"❌ Invalid Spotify import file. We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package.\n\n" +
+                        $"The files should have names like `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.\n\n" +
+                        $"The right files can take some more time to get, but actually contain your full Spotify history. Sorry for the inconvenience.",
+                        true);
+                    this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                    return;
+                }
+
+                embed.WithColor(DiscordConstants.WarningColorOrange);
+                await UpdateSpotifyImportEmbed(message, embed, description,
+                    $"❌ Invalid Spotify import file (contains no plays). Make sure you select the right files, for example `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.\n\n" +
+                    $"If your `.zip` contains files like `Userdata.json` or `Identity.json` its the wrong package. We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package. ",
+                    true);
+                this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                return;
+            }
+
+            await UpdateSpotifyImportEmbed(message, embed, description,
+                $"- **{imports.result.Count}** Spotify imports found");
+
+            var plays = await this._importService.SpotifyImportToUserPlays(contextUser, imports.result);
+            await UpdateSpotifyImportEmbed(message, embed, description, $"- **{plays.Count}** actual plays found");
+
+            var playsWithoutDuplicates =
+                await this._importService.RemoveDuplicateImports(contextUser.UserId, plays);
+            await UpdateSpotifyImportEmbed(message, embed, description,
+                $"- **{playsWithoutDuplicates.Count}** new plays found");
+
+            if (playsWithoutDuplicates.Count > 0)
+            {
+                await this._importService.InsertImportPlays(contextUser, playsWithoutDuplicates);
+                await UpdateSpotifyImportEmbed(message, embed, description, $"- Added plays to database");
+
+                if (contextUser.DataSource == DataSource.LastFm)
+                {
+                    var userHasImportedLastfm = await this._playService.UserHasImportedLastFm(contextUser.UserId);
+
+                    if (userHasImportedLastfm)
+                    {
+                        await this._userService.SetDataSource(contextUser, DataSource.FullImportThenLastFm);
+                    }
+                    else
+                    {
+                        await this._userService.SetDataSource(contextUser, DataSource.ImportThenFullLastFm);
+                    }
+
+                    await UpdateSpotifyImportEmbed(message, embed, description, $"- Updated import setting");
+                }
+            }
+
+            if (contextUser.DataSource != DataSource.LastFm)
+            {
+                await this._indexService.RecalculateTopLists(contextUser);
+                await UpdateSpotifyImportEmbed(message, embed, description, $"- Refreshed top list cache");
+            }
+
+            await this._importService.UpdateExistingScrobbleSource(contextUser);
+
+            var years = await this._importBuilders.GetImportedYears(contextUser.UserId, PlaySource.SpotifyImport);
+            if (years.Length > 0)
+            {
+                embed.AddField("<:fmbot_importing:1131511469096312914> All imported Spotify plays", years, true);
+            }
+
+            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+
+            var importActivated = new StringBuilder();
+            var importSetting = new StringBuilder();
+
+            switch (contextUser.DataSource)
+            {
+                case DataSource.LastFm:
+                    importActivated.AppendLine(
+                        "Your import setting is currently still set to just Last.fm, so imports will not be used. You can change this manually with the button below.");
+                    break;
+                case DataSource.FullImportThenLastFm:
+                    importActivated.AppendLine(
+                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
+
+                    importSetting.AppendLine(
+                        "Your import setting has been set to **Full imports, then Last.fm**. This uses your full Spotify history and adds your Last.fm scrobbles afterwards.");
+                    break;
+                case DataSource.ImportThenFullLastFm:
+                    importActivated.AppendLine(
+                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
+
+                    importSetting.AppendLine(
+                        "Your import setting has been set to **Imports, then full Last.fm**. This uses your Spotify history up until you started using Last.fm.");
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+
+            if (importActivated.Length > 0)
+            {
+                embed.AddField("✅ Importing service activated", importActivated);
+            }
+
+            if (importSetting.Length > 0)
+            {
+                embed.AddField("⚙️ Current import setting", importSetting);
+            }
+
+            var components = new ComponentBuilder()
+                .WithButton("View your stats", $"{InteractionConstants.RecapAlltime}-{contextUser.UserId}",
+                    style: ButtonStyle.Primary)
+                .WithButton("Manage import settings", InteractionConstants.ImportManage, style: ButtonStyle.Secondary);
+
+            embed.WithColor(DiscordConstants.SpotifyColorGreen);
+            await UpdateSpotifyImportEmbed(message, embed, description, $"- Import complete!", true, components);
+
+            this.Context.LogCommandUsed();
+        }
+        catch (Exception e)
+        {
+            await UpdateSpotifyImportEmbed(message, embed, description,
+                $"- ❌ Sorry, an internal error occured. Please try again later, or open a help thread on [our server](https://discord.gg/fmbot).",
+                true);
+            await this.Context.HandleCommandException(e, sendReply: false);
+        }
+    }
+
+    [SlashCommand("applemusic", "Import your Apple Music history into .fmbot")]
+    [UsernameSetRequired]
+    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
+        InteractionContextType.Guild)]
+    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
+    public async Task AppleMusicAsync(
+        [Summary("file", "'Apple Media Services information.zip' or 'Apple Music Play Activity.csv' file")]
+        IAttachment attachment = null)
+    {
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+
+        if (this.Context.Interaction.Entitlements.Any() && !SupporterService.IsSupporter(contextUser.UserType))
+        {
+            await this._supporterService.UpdateSingleDiscordSupporter(this.Context.User.Id);
+            this._userService.RemoveUserFromCache(contextUser);
+            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+        }
+
+        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
+
+        if (supporterRequired != null)
+        {
+            await this.Context.SendResponse(this.Interactivity, supporterRequired);
+            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
+            return;
+        }
+
+        var description = new StringBuilder();
+        var embed = new EmbedBuilder();
+        embed.WithColor(DiscordConstants.InformationColorBlue);
+
+        if (attachment == null)
+        {
+            var instructionResponse =
+                await this._importBuilders.GetAppleMusicImportInstructions(new ContextModel(this.Context, contextUser));
+            await this.Context.SendResponse(this.Interactivity, instructionResponse);
+            this.Context.LogCommandUsed(instructionResponse.CommandResponse);
+            return;
+        }
+
+        await DeferAsync(ephemeral: false);
+
+        embed.AddField($"{DiscordConstants.AppleMusic} Importing history into .fmbot..",
+            $"- {DiscordConstants.Loading} Loading import files...");
+
+        var message = await FollowupAsync(embed: embed.Build());
+
+        try
+        {
+            var imports = await this._importService.HandleAppleMusicFiles(contextUser, attachment);
+
+            if (imports.status == ImportStatus.UnknownFailure)
+            {
+                embed.WithColor(DiscordConstants.WarningColorOrange);
+                await UpdateAppleMusicImportEmbed(message, embed, description,
+                    $"❌ Invalid Apple Music import file, or something went wrong.\n\n" +
+                    $"If you've uploaded a `.zip` file you can also try to find the `Apple Music Play Activity.csv` inside the .zip and attach that instead.\n\n" +
+                    $"You can also open a help thread on [our server](https://discord.gg/fmbot).", true);
+                this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                return;
+            }
+
+            if (imports.status == ImportStatus.WrongCsvFailure)
+            {
+                embed.WithColor(DiscordConstants.WarningColorOrange);
+                await UpdateAppleMusicImportEmbed(message, embed, description,
+                    $"❌ We couldn't read the `.csv` file that was provided.\n\n" +
+                    $"We can only read a `Apple Music Play Activity.csv` file. Other files do not contain the data required for importing.\n\n" +
+                    $"Still having issues? You can also open a help thread on [our server](https://discord.gg/fmbot).",
+                    true);
+                this.Context.LogCommandUsed(CommandResponse.WrongInput);
+                return;
+            }
+
+            await UpdateAppleMusicImportEmbed(message, embed, description,
+                $"- **{imports.result.Count}** Apple Music imports found");
+
+            var importsWithArtist = await this._importService.AppleMusicImportAddArtists(contextUser, imports.result);
+            await UpdateAppleMusicImportEmbed(message, embed, description,
+                $"- **{importsWithArtist.matchFoundPercentage}** of artist names found for imports");
+            await UpdateAppleMusicImportEmbed(message, embed, description,
+                $"- **{importsWithArtist.userPlays.Count(c => !string.IsNullOrWhiteSpace(c.ArtistName))}** with artist names");
+
+            var plays = ImportService.AppleMusicImportsToValidUserPlays(contextUser, importsWithArtist.userPlays);
+            await UpdateAppleMusicImportEmbed(message, embed, description, $"- **{plays.Count}** actual plays found");
+
+            var playsWithoutDuplicates =
+                await this._importService.RemoveDuplicateImports(contextUser.UserId, plays);
+            await UpdateAppleMusicImportEmbed(message, embed, description,
+                $"- **{playsWithoutDuplicates.Count}** new plays found");
+
+            if (playsWithoutDuplicates.Count > 0)
+            {
+                await this._importService.InsertImportPlays(contextUser, playsWithoutDuplicates);
+                await UpdateAppleMusicImportEmbed(message, embed, description, $"- Added plays to database");
+
+                if (contextUser.DataSource == DataSource.LastFm)
+                {
+                    var userHasImportedLastfm = await this._playService.UserHasImportedLastFm(contextUser.UserId);
+
+                    if (userHasImportedLastfm)
+                    {
+                        await this._userService.SetDataSource(contextUser, DataSource.FullImportThenLastFm);
+                    }
+                    else
+                    {
+                        await this._userService.SetDataSource(contextUser, DataSource.ImportThenFullLastFm);
+                    }
+
+                    await UpdateAppleMusicImportEmbed(message, embed, description, $"- Updated import setting");
+                }
+            }
+
+            if (contextUser.DataSource != DataSource.LastFm)
+            {
+                await this._indexService.RecalculateTopLists(contextUser);
+                await UpdateAppleMusicImportEmbed(message, embed, description, $"- Refreshed top list cache");
+            }
+
+            await this._importService.UpdateExistingScrobbleSource(contextUser);
+
+            var years = await this._importBuilders.GetImportedYears(contextUser.UserId, PlaySource.AppleMusicImport);
+            if (years.Length > 0)
+            {
+                embed.AddField("<:fmbot_importing:1131511469096312914> All imported Apple Music plays", years, true);
+            }
+
+            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+
+            var importActivated = new StringBuilder();
+            var importSetting = new StringBuilder();
+
+            switch (contextUser.DataSource)
+            {
+                case DataSource.LastFm:
+                    importActivated.AppendLine(
+                        "Your import setting is currently still set to just Last.fm, so imports will not be used. You can change this manually with the button below.");
+                    break;
+                case DataSource.FullImportThenLastFm:
+                    importActivated.AppendLine(
+                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
+
+                    importSetting.AppendLine(
+                        "Your import setting has been set to **Full imports, then Last.fm**. This uses your full Apple Music history and adds your Last.fm scrobbles afterwards.");
+                    break;
+                case DataSource.ImportThenFullLastFm:
+                    importActivated.AppendLine(
+                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
+
+                    importSetting.AppendLine(
+                        "Your import setting has been set to **Imports, then full Last.fm**. This uses your Apple Music history up until you started using Last.fm.");
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+
+            if (importActivated.Length > 0)
+            {
+                embed.AddField("✅ Importing service activated", importActivated);
+            }
+
+            if (importSetting.Length > 0)
+            {
+                embed.AddField("⚙️ Current import setting", importSetting);
+            }
+
+            embed.WithColor(DiscordConstants.AppleMusicRed);
+
+            var components = new ComponentBuilder()
+                .WithButton("View your stats", $"{InteractionConstants.RecapAlltime}-{contextUser.UserId}",
+                    style: ButtonStyle.Primary)
+                .WithButton("Manage import settings", InteractionConstants.ImportManage, style: ButtonStyle.Secondary);
+
+            await UpdateAppleMusicImportEmbed(message, embed, description, $"- Import complete!", true, components);
+
+            this.Context.LogCommandUsed();
+        }
+        catch (Exception e)
+        {
+            await UpdateAppleMusicImportEmbed(message, embed, description,
+                $"- ❌ Sorry, an internal error occured. Please try again later, or open a help thread on [our server](https://discord.gg/fmbot).",
+                true);
+            await this.Context.HandleCommandException(e, sendReply: false);
+        }
+    }
+
+    private static async Task UpdateSpotifyImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
+        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null)
+    {
+        await UpdateImportEmbed(msg, embed, builder, lineToAdd, lastLine, components, image, PlaySource.SpotifyImport);
+    }
+
+    private static async Task UpdateAppleMusicImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
+        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null)
+    {
+        await UpdateImportEmbed(msg, embed, builder, lineToAdd, lastLine, components, image,
+            PlaySource.AppleMusicImport);
+    }
+
+    private static async Task UpdateImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
+        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null,
+        PlaySource playSource = PlaySource.SpotifyImport)
+    {
+        builder.AppendLine(lineToAdd);
+
+        const string loadingLine = $"- {DiscordConstants.Loading} Processing...";
+
+        var title = playSource == PlaySource.SpotifyImport
+            ? $"{DiscordConstants.Spotify} Importing history into .fmbot.."
+            : $"{DiscordConstants.AppleMusic} Importing history into .fmbot..";
+
+        var index = embed.Fields.FindIndex(f => f.Name == title);
+        embed.Fields[index] = new EmbedFieldBuilder()
+            .WithName(title)
+            .WithValue(builder + (lastLine ? null : loadingLine))
+            .WithIsInline(true);
+
+        if (image != null)
+        {
+            embed.WithImageUrl(image);
+        }
+
+        await msg.ModifyAsync(m =>
+        {
+            m.Embed = embed.Build();
+            m.Components = components?.Build();
+        });
+    }
+
+    [SlashCommand("manage", "Manage your imports and configure how they are used")]
+    [UsernameSetRequired]
+    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
+        InteractionContextType.Guild)]
+    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
+    public async Task ManageImportAsync()
+    {
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+
+        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
+
+        if (supporterRequired != null)
+        {
+            await this.Context.SendResponse(this.Interactivity, supporterRequired);
+            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
+            return;
+        }
+
+        await DeferAsync(ephemeral: true);
+
+        try
+        {
+            var response =
+                await this._userBuilder.ImportMode(new ContextModel(this.Context, contextUser), contextUser.UserId);
+
+            await this.Context.SendFollowUpResponse(this.Interactivity, response, ephemeral: true);
+            this.Context.LogCommandUsed(response.CommandResponse);
+        }
+        catch (Exception e)
+        {
+            await this.Context.HandleCommandException(e);
+        }
+    }
+}
diff --git a/src/FMBot.Bot/SlashCommands/ImportSlashCommands.cs b/src/FMBot.Bot/SlashCommands/ImportSlashCommands.cs
index 507dfddd..b04a8ea4 100644
--- a/src/FMBot.Bot/SlashCommands/ImportSlashCommands.cs
+++ b/src/FMBot.Bot/SlashCommands/ImportSlashCommands.cs
@@ -1,5 +1,4 @@
-using Discord;
-using System.Collections.Generic;
+using Discord;
 using System.Text;
 using System.Threading.Tasks;
 using System;
@@ -10,543 +9,171 @@
 using FMBot.Bot.Resources;
 using FMBot.Bot.Services;
 using FMBot.Domain.Enums;
-using FMBot.Domain.Models;
 using FMBot.Domain.Interfaces;
-using FMBot.Bot.Interfaces;
 using FMBot.Bot.Models;
 using FMBot.Bot.Builders;
 using Fergun.Interactive;
+using FMBot.Domain.Attributes;
 
 namespace FMBot.Bot.SlashCommands;
 
-[Group("import", "Manage your data imports")]
 public class ImportSlashCommands : InteractionModuleBase
 {
     private readonly UserService _userService;
-    private readonly IDataSourceFactory _dataSourceFactory;
     private readonly ImportService _importService;
-    private readonly PlayService _playService;
     private readonly IndexService _indexService;
     private readonly ImportBuilders _importBuilders;
-    private readonly SupporterService _supporterService;
     private readonly UserBuilder _userBuilder;
     private InteractiveService Interactivity { get; }
 
     public ImportSlashCommands(UserService userService,
-        IDataSourceFactory dataSourceFactory,
         ImportService importService,
-        PlayService playService,
         IndexService indexService,
         InteractiveService interactivity,
         ImportBuilders importBuilders,
-        SupporterService supporterService,
         UserBuilder userBuilder)
     {
         this._userService = userService;
-        this._dataSourceFactory = dataSourceFactory;
         this._importService = importService;
-        this._playService = playService;
         this._indexService = indexService;
         this.Interactivity = interactivity;
         this._importBuilders = importBuilders;
-        this._supporterService = supporterService;
         this._userBuilder = userBuilder;
     }
 
-    private const string SpotifyFileDescription = "Spotify history package (.zip) or history files (.json) ";
-
-    [SlashCommand("spotify", "Import your Spotify history into .fmbot")]
+    [ComponentInteraction(InteractionConstants.ImportSetting)]
     [UsernameSetRequired]
-    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
-        InteractionContextType.Guild)]
-    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
-    public async Task SpotifyAsync(
-        [Summary("file-1", SpotifyFileDescription)]
-        IAttachment attachment1 = null,
-        [Summary("file-2", SpotifyFileDescription)]
-        IAttachment attachment2 = null,
-        [Summary("file-3", SpotifyFileDescription)]
-        IAttachment attachment3 = null,
-        [Summary("file-4", SpotifyFileDescription)]
-        IAttachment attachment4 = null,
-        [Summary("file-5", SpotifyFileDescription)]
-        IAttachment attachment5 = null,
-        [Summary("file-6", SpotifyFileDescription)]
-        IAttachment attachment6 = null,
-        [Summary("file-7", SpotifyFileDescription)]
-        IAttachment attachment7 = null,
-        [Summary("file-8", SpotifyFileDescription)]
-        IAttachment attachment8 = null,
-        [Summary("file-9", SpotifyFileDescription)]
-        IAttachment attachment9 = null,
-        [Summary("file-10", SpotifyFileDescription)]
-        IAttachment attachment10 = null,
-        [Summary("file-11", SpotifyFileDescription)]
-        IAttachment attachment11 = null,
-        [Summary("file-12", SpotifyFileDescription)]
-        IAttachment attachment12 = null,
-        [Summary("file-13", SpotifyFileDescription)]
-        IAttachment attachment13 = null,
-        [Summary("file-14", SpotifyFileDescription)]
-        IAttachment attachment14 = null,
-        [Summary("file-15", SpotifyFileDescription)]
-        IAttachment attachment15 = null)
+    public async Task SetImport(string[] inputs)
     {
         var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
 
-        if (this.Context.Interaction.Entitlements.Any() && !SupporterService.IsSupporter(contextUser.UserType))
-        {
-            await this._supporterService.UpdateSingleDiscordSupporter(this.Context.User.Id);
-            this._userService.RemoveUserFromCache(contextUser);
-            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-        }
-
-        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
-
-        if (supporterRequired != null)
-        {
-            await this.Context.SendResponse(this.Interactivity, supporterRequired);
-            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
-            return;
-        }
-
-        var attachments = new List<IAttachment>
-        {
-            attachment1, attachment2, attachment3, attachment4, attachment5, attachment6,
-            attachment7, attachment8, attachment9, attachment10, attachment11, attachment12,
-            attachment13, attachment14, attachment15
-        };
-
-        var noAttachments = attachments.All(a => a == null);
-        attachments = attachments.Where(w => w != null).ToList();
-
-        var description = new StringBuilder();
-        var embed = new EmbedBuilder();
-        embed.WithColor(DiscordConstants.InformationColorBlue);
-
-        if (noAttachments)
-        {
-            var instructionResponse =
-                await this._importBuilders.GetSpotifyImportInstructions(new ContextModel(this.Context, contextUser));
-            await this.Context.SendResponse(this.Interactivity, instructionResponse);
-            this.Context.LogCommandUsed(instructionResponse.CommandResponse);
-            return;
-        }
-
-        await DeferAsync(ephemeral: noAttachments);
-
-        embed.AddField($"{DiscordConstants.Spotify} Importing history into .fmbot..",
-            $"- {DiscordConstants.Loading} Loading import files...");
-        var message = await FollowupAsync(embed: embed.Build());
-
-        try
+        if (Enum.TryParse(inputs.FirstOrDefault(), out DataSource dataSource))
         {
-            var imports = await this._importService.HandleSpotifyFiles(contextUser, attachments);
-
-            if (imports.status == ImportStatus.UnknownFailure)
-            {
-                embed.WithColor(DiscordConstants.WarningColorOrange);
-                await UpdateSpotifyImportEmbed(message, embed, description,
-                    $"❌ Invalid Spotify import file. Make sure you select the right files, for example `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.",
-                    true);
-                this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                return;
-            }
-
-            if (imports.status == ImportStatus.WrongPackageFailure)
-            {
-                embed.WithColor(DiscordConstants.WarningColorOrange);
-                await UpdateSpotifyImportEmbed(message, embed, description,
-                    $"❌ Invalid Spotify import files. You have uploaded the wrong Spotify data package.\n\n" +
-                    $"We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package. Instead you have uploaded the 'Account data' package.",
-                    true,
-                    image: "https://fmbot.xyz/img/bot/import-spotify-instructions.png",
-                    components: new ComponentBuilder().WithButton("Spotify privacy page", style: ButtonStyle.Link,
-                        url: "https://www.spotify.com/us/account/privacy/"));
-                this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                return;
-            }
-
-            if (imports.result == null || imports.result.Count == 0 || imports.result.All(a => a.MsPlayed == 0))
-            {
-                if (attachments != null &&
-                    attachments.Any(a => a.Filename != null) &&
-                    attachments.Any(a => a.Filename.ToLower().Contains("streaminghistory")))
-                {
-                    embed.WithColor(DiscordConstants.WarningColorOrange);
-                    await UpdateSpotifyImportEmbed(message, embed, description,
-                        $"❌ Invalid Spotify import file. We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package.\n\n" +
-                        $"The files should have names like `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.\n\n" +
-                        $"The right files can take some more time to get, but actually contain your full Spotify history. Sorry for the inconvenience.",
-                        true);
-                    this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                    return;
-                }
-
-                embed.WithColor(DiscordConstants.WarningColorOrange);
-                await UpdateSpotifyImportEmbed(message, embed, description,
-                    $"❌ Invalid Spotify import file (contains no plays). Make sure you select the right files, for example `my_spotify_data.zip` or `Streaming_History_Audio_x.json`.\n\n" +
-                    $"If your `.zip` contains files like `Userdata.json` or `Identity.json` its the wrong package. We can only process files that are from the ['Extended Streaming History'](https://www.spotify.com/us/account/privacy/) package. ",
-                    true);
-                this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                return;
-            }
-
-            await UpdateSpotifyImportEmbed(message, embed, description,
-                $"- **{imports.result.Count}** Spotify imports found");
-
-            var plays = await this._importService.SpotifyImportToUserPlays(contextUser, imports.result);
-            await UpdateSpotifyImportEmbed(message, embed, description, $"- **{plays.Count}** actual plays found");
-
-            var playsWithoutDuplicates =
-                await this._importService.RemoveDuplicateImports(contextUser.UserId, plays);
-            await UpdateSpotifyImportEmbed(message, embed, description,
-                $"- **{playsWithoutDuplicates.Count}** new plays found");
-
-            if (playsWithoutDuplicates.Count > 0)
-            {
-                await this._importService.InsertImportPlays(contextUser, playsWithoutDuplicates);
-                await UpdateSpotifyImportEmbed(message, embed, description, $"- Added plays to database");
-
-                if (contextUser.DataSource == DataSource.LastFm)
-                {
-                    var userHasImportedLastfm = await this._playService.UserHasImportedLastFm(contextUser.UserId);
+            var newUserSettings = await this._userService.SetDataSource(contextUser, dataSource);
 
-                    if (userHasImportedLastfm)
-                    {
-                        await this._userService.SetDataSource(contextUser, DataSource.FullImportThenLastFm);
-                    }
-                    else
-                    {
-                        await this._userService.SetDataSource(contextUser, DataSource.ImportThenFullLastFm);
-                    }
+            var name = newUserSettings.DataSource.GetAttribute<OptionAttribute>().Name;
 
-                    await UpdateSpotifyImportEmbed(message, embed, description, $"- Updated import setting");
-                }
-            }
+            var description = new StringBuilder();
+            description.AppendLine($"Import mode set to **{name}**");
+            description.AppendLine();
 
-            if (contextUser.DataSource != DataSource.LastFm)
-            {
-                await this._indexService.RecalculateTopLists(contextUser);
-                await UpdateSpotifyImportEmbed(message, embed, description, $"- Refreshed top list cache");
-            }
+            var embed = new EmbedBuilder();
+            embed.WithDescription(description +
+                                  $"{DiscordConstants.Loading} Your stored top artist/albums/tracks are being recalculated, please wait for this to complete...");
+            embed.WithColor(DiscordConstants.WarningColorOrange);
 
-            await this._importService.UpdateExistingScrobbleSource(contextUser);
-
-            var years = await this._importBuilders.GetImportedYears(contextUser.UserId, PlaySource.SpotifyImport);
-            if (years.Length > 0)
+            ComponentBuilder components = null;
+            if (dataSource == DataSource.LastFm)
             {
-                embed.AddField("<:fmbot_importing:1131511469096312914> All imported Spotify plays", years, true);
+                components = new ComponentBuilder()
+                    .WithButton("Delete imported Spotify history", InteractionConstants.ImportClearSpotify,
+                        style: ButtonStyle.Danger, row: 0)
+                    .WithButton("Delete imported Apple Music history", InteractionConstants.ImportClearAppleMusic,
+                        style: ButtonStyle.Danger, row: 0);
             }
 
-            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-            var importActivated = new StringBuilder();
-            var importSetting = new StringBuilder();
-
-            switch (contextUser.DataSource)
-            {
-                case DataSource.LastFm:
-                    importActivated.AppendLine(
-                        "Your import setting is currently still set to just Last.fm, so imports will not be used. You can change this manually with the button below.");
-                    break;
-                case DataSource.FullImportThenLastFm:
-                    importActivated.AppendLine(
-                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
-
-                    importSetting.AppendLine(
-                        "Your import setting has been set to **Full imports, then Last.fm**. This uses your full Spotify history and adds your Last.fm scrobbles afterwards.");
-                    break;
-                case DataSource.ImportThenFullLastFm:
-                    importActivated.AppendLine(
-                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
+            await this.Context.Interaction.RespondAsync(null, [embed.Build()], ephemeral: true, components: components?.Build());
+            this.Context.LogCommandUsed();
 
-                    importSetting.AppendLine(
-                        "Your import setting has been set to **Imports, then full Last.fm**. This uses your Spotify history up until you started using Last.fm.");
-                    break;
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
+            await this._indexService.RecalculateTopLists(newUserSettings);
 
-            if (importActivated.Length > 0)
+            embed.WithColor(DiscordConstants.SuccessColorGreen);
+            embed.WithDescription(description +
+                                  "✅ Your stored top artist/albums/tracks have successfully been recalculated.");
+            await this.Context.Interaction.ModifyOriginalResponseAsync(msg =>
             {
-                embed.AddField("✅ Importing service activated", importActivated);
-            }
-            if (importSetting.Length > 0)
-            {
-                embed.AddField("⚙️ Current import setting", importSetting);
-            }
-
-            var components = new ComponentBuilder()
-                .WithButton("View your stats", $"{InteractionConstants.RecapAlltime}-{contextUser.UserId}", style: ButtonStyle.Primary)
-                .WithButton("Manage import settings", InteractionConstants.ImportManage, style: ButtonStyle.Secondary);
-
-            embed.WithColor(DiscordConstants.SpotifyColorGreen);
-            await UpdateSpotifyImportEmbed(message, embed, description, $"- Import complete!", true, components);
-
-            this.Context.LogCommandUsed();
-        }
-        catch (Exception e)
-        {
-            await UpdateSpotifyImportEmbed(message, embed, description,
-                $"- ❌ Sorry, an internal error occured. Please try again later, or open a help thread on [our server](https://discord.gg/fmbot).",
-                true);
-            await this.Context.HandleCommandException(e, sendReply: false);
+                msg.Embed = embed.Build();
+            });
         }
     }
 
-    [SlashCommand("applemusic", "Import your Apple Music history into .fmbot")]
+    [ComponentInteraction(InteractionConstants.ImportClearSpotify)]
     [UsernameSetRequired]
-    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
-        InteractionContextType.Guild)]
-    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
-    public async Task AppleMusicAsync(
-        [Summary("file", "'Apple Media Services information.zip' or 'Apple Music Play Activity.csv' file")]
-        IAttachment attachment = null)
+    public async Task ClearImportSpotify()
     {
         var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
 
-        if (this.Context.Interaction.Entitlements.Any() && !SupporterService.IsSupporter(contextUser.UserType))
-        {
-            await this._supporterService.UpdateSingleDiscordSupporter(this.Context.User.Id);
-            this._userService.RemoveUserFromCache(contextUser);
-            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-        }
+        await this._importService.RemoveImportedSpotifyPlays(contextUser);
 
-        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
+        var embed = new EmbedBuilder();
+        embed.WithDescription($"All your imported Spotify history has been removed from .fmbot.");
+        embed.WithColor(DiscordConstants.SuccessColorGreen);
 
-        if (supporterRequired != null)
-        {
-            await this.Context.SendResponse(this.Interactivity, supporterRequired);
-            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
-            return;
-        }
+        await RespondAsync(null, new[] { embed.Build() }, ephemeral: true);
+        this.Context.LogCommandUsed();
+    }
 
-        var description = new StringBuilder();
-        var embed = new EmbedBuilder();
-        embed.WithColor(DiscordConstants.InformationColorBlue);
+    [ComponentInteraction(InteractionConstants.ImportClearAppleMusic)]
+    [UsernameSetRequired]
+    public async Task ClearImportAppleMusic()
+    {
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
 
-        if (attachment == null)
-        {
-            var instructionResponse =
-                await this._importBuilders.GetAppleMusicImportInstructions(new ContextModel(this.Context, contextUser));
-            await this.Context.SendResponse(this.Interactivity, instructionResponse);
-            this.Context.LogCommandUsed(instructionResponse.CommandResponse);
-            return;
-        }
+        await this._importService.RemoveImportedAppleMusicPlays(contextUser);
 
-        await DeferAsync(ephemeral: false);
+        var embed = new EmbedBuilder();
+        embed.WithDescription($"All your imported Apple Music history has been removed from .fmbot.");
+        embed.WithColor(DiscordConstants.SuccessColorGreen);
 
-        embed.AddField($"{DiscordConstants.AppleMusic} Importing history into .fmbot..",
-            $"- {DiscordConstants.Loading} Loading import files...");
+        await RespondAsync(null, new[] { embed.Build() }, ephemeral: true);
+        this.Context.LogCommandUsed();
+    }
 
-        var message = await FollowupAsync(embed: embed.Build());
+    [ComponentInteraction(InteractionConstants.ImportManage)]
+    [UsernameSetRequired]
+    public async Task ImportManage()
+    {
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
+        await DeferAsync(true);
 
         try
         {
-            var imports = await this._importService.HandleAppleMusicFiles(contextUser, attachment);
-
-            if (imports.status == ImportStatus.UnknownFailure)
-            {
-                embed.WithColor(DiscordConstants.WarningColorOrange);
-                await UpdateAppleMusicImportEmbed(message, embed, description,
-                    $"❌ Invalid Apple Music import file, or something went wrong.\n\n" +
-                    $"If you've uploaded a `.zip` file you can also try to find the `Apple Music Play Activity.csv` inside the .zip and attach that instead.\n\n" +
-                    $"You can also open a help thread on [our server](https://discord.gg/fmbot).", true);
-                this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                return;
-            }
-
-            if (imports.status == ImportStatus.WrongCsvFailure)
-            {
-                embed.WithColor(DiscordConstants.WarningColorOrange);
-                await UpdateAppleMusicImportEmbed(message, embed, description,
-                    $"❌ We couldn't read the `.csv` file that was provided.\n\n" +
-                    $"We can only read a `Apple Music Play Activity.csv` file. Other files do not contain the data required for importing.\n\n" +
-                    $"Still having issues? You can also open a help thread on [our server](https://discord.gg/fmbot).",
-                    true);
-                this.Context.LogCommandUsed(CommandResponse.WrongInput);
-                return;
-            }
-
-            await UpdateAppleMusicImportEmbed(message, embed, description,
-                $"- **{imports.result.Count}** Apple Music imports found");
-
-            var importsWithArtist = await this._importService.AppleMusicImportAddArtists(contextUser, imports.result);
-            await UpdateAppleMusicImportEmbed(message, embed, description,
-                $"- **{importsWithArtist.matchFoundPercentage}** of artist names found for imports");
-            await UpdateAppleMusicImportEmbed(message, embed, description,
-                $"- **{importsWithArtist.userPlays.Count(c => !string.IsNullOrWhiteSpace(c.ArtistName))}** with artist names");
-
-            var plays = ImportService.AppleMusicImportsToValidUserPlays(contextUser, importsWithArtist.userPlays);
-            await UpdateAppleMusicImportEmbed(message, embed, description, $"- **{plays.Count}** actual plays found");
-
-            var playsWithoutDuplicates =
-                await this._importService.RemoveDuplicateImports(contextUser.UserId, plays);
-            await UpdateAppleMusicImportEmbed(message, embed, description,
-                $"- **{playsWithoutDuplicates.Count}** new plays found");
-
-            if (playsWithoutDuplicates.Count > 0)
-            {
-                await this._importService.InsertImportPlays(contextUser, playsWithoutDuplicates);
-                await UpdateAppleMusicImportEmbed(message, embed, description, $"- Added plays to database");
-
-                if (contextUser.DataSource == DataSource.LastFm)
-                {
-                    var userHasImportedLastfm = await this._playService.UserHasImportedLastFm(contextUser.UserId);
-
-                    if (userHasImportedLastfm)
-                    {
-                        await this._userService.SetDataSource(contextUser, DataSource.FullImportThenLastFm);
-                    }
-                    else
-                    {
-                        await this._userService.SetDataSource(contextUser, DataSource.ImportThenFullLastFm);
-                    }
-
-                    await UpdateAppleMusicImportEmbed(message, embed, description, $"- Updated import setting");
-                }
-            }
-
-            if (contextUser.DataSource != DataSource.LastFm)
-            {
-                await this._indexService.RecalculateTopLists(contextUser);
-                await UpdateAppleMusicImportEmbed(message, embed, description, $"- Refreshed top list cache");
-            }
-
-            await this._importService.UpdateExistingScrobbleSource(contextUser);
-
-            var years = await this._importBuilders.GetImportedYears(contextUser.UserId, PlaySource.AppleMusicImport);
-            if (years.Length > 0)
-            {
-                embed.AddField("<:fmbot_importing:1131511469096312914> All imported Apple Music plays", years, true);
-            }
-
-            contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-            var importActivated = new StringBuilder();
-            var importSetting = new StringBuilder();
-
-            switch (contextUser.DataSource)
-            {
-                case DataSource.LastFm:
-                    importActivated.AppendLine(
-                        "Your import setting is currently still set to just Last.fm, so imports will not be used. You can change this manually with the button below.");
-                    break;
-                case DataSource.FullImportThenLastFm:
-                    importActivated.AppendLine(
-                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
-
-                    importSetting.AppendLine(
-                        "Your import setting has been set to **Full imports, then Last.fm**. This uses your full Apple Music history and adds your Last.fm scrobbles afterwards.");
-                    break;
-                case DataSource.ImportThenFullLastFm:
-                    importActivated.AppendLine(
-                        "With this service all playcounts and history in the bot will consist of your imports combined with your Last.fm history. The bot re-calculates this every time you run a command, all while still responding quickly.");
-
-                    importSetting.AppendLine(
-                        "Your import setting has been set to **Imports, then full Last.fm**. This uses your Apple Music history up until you started using Last.fm.");
-                    break;
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
-
-            if (importActivated.Length > 0)
-            {
-                embed.AddField("✅ Importing service activated", importActivated);
-            }
-            if (importSetting.Length > 0)
-            {
-                embed.AddField("⚙️ Current import setting", importSetting);
-            }
-
-            embed.WithColor(DiscordConstants.AppleMusicRed);
-
-            var components = new ComponentBuilder()
-                .WithButton("View your stats", $"{InteractionConstants.RecapAlltime}-{contextUser.UserId}", style: ButtonStyle.Primary)
-                .WithButton("Manage import settings", InteractionConstants.ImportManage, style: ButtonStyle.Secondary);
-
-            await UpdateAppleMusicImportEmbed(message, embed, description, $"- Import complete!", true, components);
+            var response =
+                await this._userBuilder.ImportMode(new ContextModel(this.Context, contextUser), contextUser.UserId);
 
-            this.Context.LogCommandUsed();
+            await this.Context.SendFollowUpResponse(this.Interactivity, response, ephemeral: true);
+            this.Context.LogCommandUsed(response.CommandResponse);
         }
         catch (Exception e)
         {
-            await UpdateAppleMusicImportEmbed(message, embed, description,
-                $"- ❌ Sorry, an internal error occured. Please try again later, or open a help thread on [our server](https://discord.gg/fmbot).",
-                true);
-            await this.Context.HandleCommandException(e, sendReply: false);
+            await this.Context.HandleCommandException(e);
         }
     }
 
-    private static async Task UpdateSpotifyImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
-        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null)
-    {
-        await UpdateImportEmbed(msg, embed, builder, lineToAdd, lastLine, components, image, PlaySource.SpotifyImport);
-    }
-
-    private static async Task UpdateAppleMusicImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
-        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null)
-    {
-        await UpdateImportEmbed(msg, embed, builder, lineToAdd, lastLine, components, image, PlaySource.AppleMusicImport);
-    }
-
-    private static async Task UpdateImportEmbed(IUserMessage msg, EmbedBuilder embed, StringBuilder builder,
-        string lineToAdd, bool lastLine = false, ComponentBuilder components = null, string image = null,
-        PlaySource playSource = PlaySource.SpotifyImport)
+    [ComponentInteraction(InteractionConstants.ImportInstructionsSpotify)]
+    [UsernameSetRequired]
+    public async Task ImportInstructionsSpotify()
     {
-        builder.AppendLine(lineToAdd);
-
-        const string loadingLine = $"- {DiscordConstants.Loading} Processing...";
-
-        var title = playSource == PlaySource.SpotifyImport
-            ? $"{DiscordConstants.Spotify} Importing history into .fmbot.."
-            : $"{DiscordConstants.AppleMusic} Importing history into .fmbot..";
-
-        var index = embed.Fields.FindIndex(f => f.Name == title);
-        embed.Fields[index] = new EmbedFieldBuilder()
-            .WithName(title)
-            .WithValue(builder + (lastLine ? null : loadingLine))
-            .WithIsInline(true);
+        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
 
-        if (image != null)
+        try
         {
-            embed.WithImageUrl(image);
-        }
+            var response =
+                await this._importBuilders.GetSpotifyImportInstructions(new ContextModel(this.Context, contextUser),
+                    true);
 
-        await msg.ModifyAsync(m =>
+            await this.Context.UpdateInteractionEmbed(response);
+            this.Context.LogCommandUsed(response.CommandResponse);
+        }
+        catch (Exception e)
         {
-            m.Embed = embed.Build();
-            m.Components = components?.Build();
-        });
+            await this.Context.HandleCommandException(e);
+        }
     }
 
-    [SlashCommand("manage", "Manage your imports and configure how they are used")]
+    [ComponentInteraction(InteractionConstants.ImportInstructionsAppleMusic)]
     [UsernameSetRequired]
-    [CommandContextType(InteractionContextType.BotDm, InteractionContextType.PrivateChannel,
-        InteractionContextType.Guild)]
-    [IntegrationType(ApplicationIntegrationType.UserInstall, ApplicationIntegrationType.GuildInstall)]
-    public async Task ManageImportAsync()
+    public async Task ImportInstructionsAppleMusic()
     {
         var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
 
-        var supporterRequired = ImportBuilders.ImportSupporterRequired(new ContextModel(this.Context, contextUser));
-
-        if (supporterRequired != null)
-        {
-            await this.Context.SendResponse(this.Interactivity, supporterRequired);
-            this.Context.LogCommandUsed(supporterRequired.CommandResponse);
-            return;
-        }
-
-        await DeferAsync(ephemeral: true);
-
         try
         {
             var response =
-                await this._userBuilder.ImportMode(new ContextModel(this.Context, contextUser), contextUser.UserId);
+                await this._importBuilders.GetAppleMusicImportInstructions(new ContextModel(this.Context, contextUser),
+                    true);
 
-            await this.Context.SendFollowUpResponse(this.Interactivity, response, ephemeral: true);
+            await this.Context.UpdateInteractionEmbed(response);
             this.Context.LogCommandUsed(response.CommandResponse);
         }
         catch (Exception e)
diff --git a/src/FMBot.Bot/SlashCommands/UserSlashCommands.cs b/src/FMBot.Bot/SlashCommands/UserSlashCommands.cs
index 557feec5..16f31140 100644
--- a/src/FMBot.Bot/SlashCommands/UserSlashCommands.cs
+++ b/src/FMBot.Bot/SlashCommands/UserSlashCommands.cs
@@ -1381,137 +1381,4 @@ public async Task DeleteAltConfirmed(string transferData, string targetUserId)
             await this.Context.HandleCommandException(e);
         }
     }
-
-    [ComponentInteraction(InteractionConstants.ImportSetting)]
-    [UsernameSetRequired]
-    public async Task SetImport(string[] inputs)
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-        if (Enum.TryParse(inputs.FirstOrDefault(), out DataSource dataSource))
-        {
-            var newUserSettings = await this._userService.SetDataSource(contextUser, dataSource);
-
-            var name = newUserSettings.DataSource.GetAttribute<OptionAttribute>().Name;
-
-            var embed = new EmbedBuilder();
-            embed.WithDescription($"Import mode set to **{name}**.\n\n" +
-                                  $"Your stored top artist/albums/tracks are being recalculated. \n\n" +
-                                  $"⚠️ **You must wait for this to complete before switching to a different mode.**");
-            embed.WithColor(DiscordConstants.SuccessColorGreen);
-
-            ComponentBuilder components = null;
-            if (dataSource == DataSource.LastFm)
-            {
-                components = new ComponentBuilder()
-                    .WithButton("Delete imported Spotify history", InteractionConstants.ImportClearSpotify,
-                        style: ButtonStyle.Danger, row: 0)
-                    .WithButton("Delete imported Apple Music history", InteractionConstants.ImportClearAppleMusic,
-                        style: ButtonStyle.Danger, row: 0);
-            }
-
-            await RespondAsync(null, new[] { embed.Build() }, ephemeral: true, components: components?.Build());
-            this.Context.LogCommandUsed();
-
-            await this._indexService.RecalculateTopLists(newUserSettings);
-
-            embed.WithDescription("✅ Your stored top artist/albums/tracks have successfully been recalculated.");
-            await FollowupAsync(null, new[] { embed.Build() }, ephemeral: true);
-        }
-    }
-
-    [ComponentInteraction(InteractionConstants.ImportClearSpotify)]
-    [UsernameSetRequired]
-    public async Task ClearImportSpotify()
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-        await this._importService.RemoveImportedSpotifyPlays(contextUser);
-
-        var embed = new EmbedBuilder();
-        embed.WithDescription($"All your imported Spotify history has been removed from .fmbot.");
-        embed.WithColor(DiscordConstants.SuccessColorGreen);
-
-        await RespondAsync(null, new[] { embed.Build() }, ephemeral: true);
-        this.Context.LogCommandUsed();
-    }
-
-    [ComponentInteraction(InteractionConstants.ImportClearAppleMusic)]
-    [UsernameSetRequired]
-    public async Task ClearImportAppleMusic()
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-        await this._importService.RemoveImportedAppleMusicPlays(contextUser);
-
-        var embed = new EmbedBuilder();
-        embed.WithDescription($"All your imported Apple Music history has been removed from .fmbot.");
-        embed.WithColor(DiscordConstants.SuccessColorGreen);
-
-        await RespondAsync(null, new[] { embed.Build() }, ephemeral: true);
-        this.Context.LogCommandUsed();
-    }
-
-    [ComponentInteraction(InteractionConstants.ImportManage)]
-    [UsernameSetRequired]
-    public async Task ImportManage()
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-        await DeferAsync(true);
-
-        try
-        {
-            var response =
-                await this._userBuilder.ImportMode(new ContextModel(this.Context, contextUser), contextUser.UserId);
-
-            await this.Context.SendFollowUpResponse(this.Interactivity, response, ephemeral: true);
-            this.Context.LogCommandUsed(response.CommandResponse);
-        }
-        catch (Exception e)
-        {
-            await this.Context.HandleCommandException(e);
-        }
-    }
-
-    [ComponentInteraction(InteractionConstants.ImportInstructionsSpotify)]
-    [UsernameSetRequired]
-    public async Task ImportInstructionsSpotify()
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-        try
-        {
-            var response =
-                await this._importBuilders.GetSpotifyImportInstructions(new ContextModel(this.Context, contextUser),
-                    true);
-
-            await this.Context.UpdateInteractionEmbed(response);
-            this.Context.LogCommandUsed(response.CommandResponse);
-        }
-        catch (Exception e)
-        {
-            await this.Context.HandleCommandException(e);
-        }
-    }
-
-    [ComponentInteraction(InteractionConstants.ImportInstructionsAppleMusic)]
-    [UsernameSetRequired]
-    public async Task ImportInstructionsAppleMusic()
-    {
-        var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);
-
-        try
-        {
-            var response =
-                await this._importBuilders.GetAppleMusicImportInstructions(new ContextModel(this.Context, contextUser),
-                    true);
-
-            await this.Context.UpdateInteractionEmbed(response);
-            this.Context.LogCommandUsed(response.CommandResponse);
-        }
-        catch (Exception e)
-        {
-            await this.Context.HandleCommandException(e);
-        }
-    }
 }