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); - } - } }