Skip to content

Commit

Permalink
add iceberg
Browse files Browse the repository at this point in the history
  • Loading branch information
th0mk committed Oct 21, 2024
1 parent 59dd26b commit 2baa78d
Show file tree
Hide file tree
Showing 9 changed files with 708 additions and 233 deletions.
570 changes: 403 additions & 167 deletions src/FMBot.Bot/Builders/ArtistBuilders.cs

Large diffs are not rendered by default.

95 changes: 74 additions & 21 deletions src/FMBot.Bot/Services/ArtistsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ public ArtistsService(IDbContextFactory<FMBotDbContext> contextFactory,
this._botSettings = botSettings.Value;
}

public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser discordUser, string artistValues, string lastFmUserName, string sessionKey = null, string otherUserUsername = null,
bool useCachedArtists = false, int? userId = null, bool redirectsEnabled = true, ulong? interactionId = null, IUserMessage referencedMessage = null)
public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser discordUser, string artistValues,
string lastFmUserName, string sessionKey = null, string otherUserUsername = null,
bool useCachedArtists = false, int? userId = null, bool redirectsEnabled = true, ulong? interactionId = null,
IUserMessage referencedMessage = null)
{
if (referencedMessage != null && string.IsNullOrWhiteSpace(artistValues))
{
Expand Down Expand Up @@ -118,7 +120,8 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco
}
else
{
artistCall = await this._dataSourceFactory.GetArtistInfoAsync(artistValues, lastFmUserName, redirectsEnabled);
artistCall =
await this._dataSourceFactory.GetArtistInfoAsync(artistValues, lastFmUserName, redirectsEnabled);
}

if (interactionId.HasValue)
Expand All @@ -132,11 +135,13 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco

if (!artistCall.Success && artistCall.Error == ResponseStatus.MissingParameters)
{
response.Embed.WithDescription($"Artist `{artistValues}` could not be found, please check your search values and try again.");
response.Embed.WithDescription(
$"Artist `{artistValues}` could not be found, please check your search values and try again.");
response.CommandResponse = CommandResponse.NotFound;
response.ResponseType = ResponseType.Embed;
return new ArtistSearch(null, response);
}

if (!artistCall.Success || artistCall.Content == null)
{
response.Embed.ErrorResponse(artistCall.Error, artistCall.Message, null, discordUser, "artist");
Expand All @@ -153,16 +158,20 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco

if (userId.HasValue && otherUserUsername == null)
{
recentScrobbles = await this._updateService.UpdateUser(new UpdateUserQueueItem(userId.Value, getAccurateTotalPlaycount: false));
recentScrobbles =
await this._updateService.UpdateUser(new UpdateUserQueueItem(userId.Value,
getAccurateTotalPlaycount: false));
}
else
{
recentScrobbles = await this._dataSourceFactory.GetRecentTracksAsync(lastFmUserName, 1, true, sessionKey);
recentScrobbles =
await this._dataSourceFactory.GetRecentTracksAsync(lastFmUserName, 1, true, sessionKey);
}

if (GenericEmbedService.RecentScrobbleCallFailed(recentScrobbles))
{
var errorResponse = GenericEmbedService.RecentScrobbleCallFailedResponse(recentScrobbles, lastFmUserName);
var errorResponse =
GenericEmbedService.RecentScrobbleCallFailedResponse(recentScrobbles, lastFmUserName);
return new ArtistSearch(null, errorResponse);
}

Expand All @@ -175,11 +184,13 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco
Response<ArtistInfo> artistCall;
if (useCachedArtists)
{
artistCall = await GetDatabaseArtist(lastPlayedTrack.ArtistName, lastFmUserName, userId, redirectsEnabled);
artistCall =
await GetDatabaseArtist(lastPlayedTrack.ArtistName, lastFmUserName, userId, redirectsEnabled);
}
else
{
artistCall = await this._dataSourceFactory.GetArtistInfoAsync(lastPlayedTrack.ArtistName, lastFmUserName, redirectsEnabled);
artistCall = await this._dataSourceFactory.GetArtistInfoAsync(lastPlayedTrack.ArtistName,
lastFmUserName, redirectsEnabled);
}

if (interactionId.HasValue)
Expand All @@ -193,7 +204,8 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco

if (artistCall.Content == null || !artistCall.Success)
{
response.Embed.WithDescription($"Last.fm did not return a result for **{lastPlayedTrack.ArtistName}**.");
response.Embed.WithDescription(
$"Last.fm did not return a result for **{lastPlayedTrack.ArtistName}**.");
response.CommandResponse = CommandResponse.NotFound;
response.ResponseType = ResponseType.Embed;
return new ArtistSearch(null, response);
Expand All @@ -203,7 +215,8 @@ public async Task<ArtistSearch> SearchArtist(ResponseModel response, IUser disco
}
}

private async Task<Response<ArtistInfo>> GetDatabaseArtist(string artistName, string lastFmUserName, int? userId = null, bool redirectsEnabled = true)
private async Task<Response<ArtistInfo>> GetDatabaseArtist(string artistName, string lastFmUserName,
int? userId = null, bool redirectsEnabled = true)
{
Response<ArtistInfo> artistInfo;
var cachedArtist = await GetArtistFromDatabase(artistName, redirectsEnabled);
Expand All @@ -217,7 +230,8 @@ private async Task<Response<ArtistInfo>> GetDatabaseArtist(string artistName, st

if (userId.HasValue)
{
var userPlaycount = await this._whoKnowsArtistService.GetArtistPlayCountForUser(cachedArtist.Name, userId.Value);
var userPlaycount =
await this._whoKnowsArtistService.GetArtistPlayCountForUser(cachedArtist.Name, userId.Value);
artistInfo.Content.UserPlaycount = userPlaycount;
}
}
Expand Down Expand Up @@ -258,7 +272,7 @@ public async Task<List<TopArtist>> FillArtistImages(List<TopArtist> topArtists)
foreach (var topArtist in topArtists.Where(w => w.ArtistImageUrl == null))
{
var artist = artists.Artists.FirstOrDefault(f => !string.IsNullOrWhiteSpace(f.ArtistImageUrl) &&
f.ArtistName == topArtist.ArtistName);
f.ArtistName == topArtist.ArtistName);

if (artist != null)
{
Expand Down Expand Up @@ -311,6 +325,7 @@ public TasteModels GetEmbedTaste(ICollection<TasteItem> leftUserArtists,
{
right += $"{otherPlaycount}";
}

right += $"\n";
}

Expand Down Expand Up @@ -352,8 +367,36 @@ public async Task<List<TopArtist>> GetUserAllTimeTopArtists(int userId, bool use
return freshTopArtists;
}

public async Task<List<ArtistPopularity>> GetArtistsPopularity(List<TopArtist> topArtists)
{
await using var connection = new NpgsqlConnection(this._botSettings.Database.ConnectionString);
await connection.OpenAsync();

var artists = topArtists
.GroupBy(g => g.ArtistName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(
d => d.Key,
d => d.OrderByDescending(o => o.UserPlaycount).First().UserPlaycount,
StringComparer.OrdinalIgnoreCase
);

var artistNames = artists.Select(s => s.Key).ToList();
var artistsWithPopularity = await ArtistRepository.GetArtistsPopularity(artistNames, connection);

foreach (var artist in artistsWithPopularity)
{
if (artists.TryGetValue(artist.Name, out var playcount))
{
artist.Playcount = playcount;
}
}

return artistsWithPopularity.ToList();
}

// Top artists for 2 users
public (string result, int matches) GetTableTaste(IReadOnlyCollection<TasteItem> leftUserArtists, IReadOnlyCollection<TasteItem> rightUserArtists,
public (string result, int matches) GetTableTaste(IReadOnlyCollection<TasteItem> leftUserArtists,
IReadOnlyCollection<TasteItem> rightUserArtists,
int amount, TimePeriod timePeriod, string mainUser, string userToCompare, string type)
{
var artistsToShow = ArtistsToShow(leftUserArtists, rightUserArtists);
Expand All @@ -365,7 +408,9 @@ public async Task<List<TopArtist>> GetUserAllTimeTopArtists(int userId, bool use
return new TasteTwoUserModel
{
Artist = !string.IsNullOrWhiteSpace(s.Name) && s.Name.Length > AllowedCharacterCount(s.Name) ? $"{s.Name.Substring(0, AllowedCharacterCount(s.Name) - 2)}.." : s.Name,
Artist = !string.IsNullOrWhiteSpace(s.Name) && s.Name.Length > AllowedCharacterCount(s.Name)
? $"{s.Name.Substring(0, AllowedCharacterCount(s.Name) - 2)}.."
: s.Name,
OwnPlaycount = ownPlaycount,
OtherPlaycount = otherPlaycount
};
Expand Down Expand Up @@ -413,7 +458,8 @@ static int AllowedCharacterCount(string name)
return (description.ToString(), artistsToShow.Count);
}

private static string Description(IEnumerable<TasteItem> mainUserArtists, TimePeriod chartTimePeriod, IReadOnlyCollection<TasteItem> matchedArtists)
private static string Description(IEnumerable<TasteItem> mainUserArtists, TimePeriod chartTimePeriod,
IReadOnlyCollection<TasteItem> matchedArtists)
{
decimal percentage;

Expand All @@ -437,7 +483,8 @@ private static string GetCompareChar(long ownPlaycount, long otherPlaycount)
return ownPlaycount == otherPlaycount ? "" : ownPlaycount > otherPlaycount ? " > " : " < ";
}

private static List<TasteItem> ArtistsToShow(IEnumerable<TasteItem> leftUserArtists, IEnumerable<TasteItem> rightUserArtists)
private static List<TasteItem> ArtistsToShow(IEnumerable<TasteItem> leftUserArtists,
IEnumerable<TasteItem> rightUserArtists)
{
var artistsToShow =
leftUserArtists
Expand All @@ -460,10 +507,13 @@ public TasteSettings SetTasteSettings(TasteSettings currentTasteSettings, string
{
tasteSettings.TasteType = TasteType.Table;
}
if (extraOptions.Contains("e") || extraOptions.Contains("embed") || extraOptions.Contains("embedfull") || extraOptions.Contains("fullembed"))

if (extraOptions.Contains("e") || extraOptions.Contains("embed") || extraOptions.Contains("embedfull") ||
extraOptions.Contains("fullembed"))
{
tasteSettings.TasteType = TasteType.FullEmbed;
}

if (extraOptions.Contains("xl") || extraOptions.Contains("xxl") || extraOptions.Contains("extralarge"))
{
tasteSettings.EmbedSize = EmbedSize.Large;
Expand Down Expand Up @@ -569,7 +619,8 @@ public async Task<List<string>> GetLatestArtists(ulong discordUserId, bool cache
await using var connection = new NpgsqlConnection(this._botSettings.Database.ConnectionString);
await connection.OpenAsync();

var plays = await PlayRepository.GetUserPlaysWithinTimeRange(user.UserId, connection, DateTime.UtcNow.AddDays(-2));
var plays = await PlayRepository.GetUserPlaysWithinTimeRange(user.UserId, connection,
DateTime.UtcNow.AddDays(-2));

var artists = plays
.OrderByDescending(o => o.TimePlayed)
Expand All @@ -588,7 +639,8 @@ public async Task<List<string>> GetLatestArtists(ulong discordUserId, bool cache
}
}

public async Task<List<string>> GetRecentTopArtists(ulong discordUserId, bool cacheEnabled = true, int daysToGoBack = 20)
public async Task<List<string>> GetRecentTopArtists(ulong discordUserId, bool cacheEnabled = true,
int daysToGoBack = 20)
{
try
{
Expand All @@ -610,7 +662,8 @@ public async Task<List<string>> GetRecentTopArtists(ulong discordUserId, bool ca
await using var connection = new NpgsqlConnection(this._botSettings.Database.ConnectionString);
await connection.OpenAsync();

var plays = await PlayRepository.GetUserPlaysWithinTimeRange(user.UserId, connection, DateTime.UtcNow.AddDays(-daysToGoBack));
var plays = await PlayRepository.GetUserPlaysWithinTimeRange(user.UserId, connection,
DateTime.UtcNow.AddDays(-daysToGoBack));

var artists = plays
.GroupBy(g => g.ArtistName)
Expand Down
10 changes: 10 additions & 0 deletions src/FMBot.Bot/Services/SettingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ public static TimeSettingsModel GetTimePeriod(string options,
settingsModel.StartDateTime = new DateTime(2000, 1, 1);
}
}
else if (defaultTimePeriod == TimePeriod.Yearly)
{
settingsModel.LastStatsTimeSpan = LastStatsTimeSpan.Year;
settingsModel.TimePeriod = TimePeriod.Yearly;
settingsModel.Description = "Yearly";
settingsModel.AltDescription = "last year";
settingsModel.UrlParameter = "date_preset=LAST_365_DAYS";
settingsModel.ApiParameter = "12month";
settingsModel.PlayDays = 365;
}
else if (defaultTimePeriod == TimePeriod.Monthly)
{
settingsModel.LastStatsTimeSpan = LastStatsTimeSpan.Month;
Expand Down
2 changes: 1 addition & 1 deletion src/FMBot.Bot/TextCommands/AdminCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2364,7 +2364,7 @@ public async Task GetSupporterTestLink([Remainder] string user = null)
embed.AddField("Monthly - $3.99",
"-# $3.99 per month", true);
embed.AddField("Yearly - $29.99",
"-# $2.49 per month", true);
"-# $2.49 per month - Saves 45%", true);
embed.WithColor(DiscordConstants.InformationColorBlue);

await ReplyAsync(embed: embed.Build(), components: components.Build());
Expand Down
41 changes: 40 additions & 1 deletion src/FMBot.Bot/TextCommands/LastFM/ArtistCommands.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
Expand Down Expand Up @@ -34,6 +35,7 @@ public class ArtistCommands : BaseCommandModule
private readonly SettingService _settingService;
private readonly UserService _userService;
private readonly DiscogsBuilder _discogsBuilders;
private readonly AdminService _adminService;

private InteractiveService Interactivity { get; }

Expand All @@ -50,7 +52,8 @@ public ArtistCommands(
InteractiveService interactivity,
IOptions<BotSettings> botSettings,
ArtistBuilders artistBuilders,
DiscogsBuilder discogsBuilders) : base(botSettings)
DiscogsBuilder discogsBuilders,
AdminService adminService) : base(botSettings)
{
this._artistsService = artistsService;
this._guildService = guildService;
Expand All @@ -64,6 +67,7 @@ public ArtistCommands(
this.Interactivity = interactivity;
this._artistBuilders = artistBuilders;
this._discogsBuilders = discogsBuilders;
this._adminService = adminService;
}

[Command("artist", RunMode = RunMode.Async)]
Expand Down Expand Up @@ -655,4 +659,39 @@ public async Task AffinityAsync([Remainder] string extraOptions = null)
await this.Context.HandleCommandException(e);
}
}

[Command("iceberg", RunMode = RunMode.Async)]
[Summary("Shows your iceberg.")]
[Options(Constants.CompactTimePeriodList, Constants.UserMentionExample)]
[Examples("receipt", "receipt 2022", "rcpt week")]
[Alias("ice", "icebergify")]
[UsernameSetRequired]
[CommandCategories(CommandCategory.Artists)]
[ExcludeFromHelp]
public async Task IcebergAsync([Remainder] string extraOptions = null)
{
if (!await this._adminService.HasCommandAccessAsync(this.Context.User, UserType.Admin))
{
return;
}

_ = this.Context.Channel.TriggerTypingAsync();
var contextUser = await this._userService.GetUserSettingsAsync(this.Context.User);

try
{
var userSettings = await this._settingService.GetUser(extraOptions, contextUser, this.Context);
var timeSettings = SettingService.GetTimePeriod(extraOptions, registeredLastFm: userSettings.RegisteredLastFm, timeZone: userSettings.TimeZone, defaultTimePeriod: TimePeriod.Yearly);
var prfx = this._prefixService.GetPrefix(this.Context.Guild?.Id);

var response = await this._artistBuilders.GetIceberg(new ContextModel(this.Context, prfx, contextUser), userSettings, timeSettings);

await this.Context.SendResponse(this.Interactivity, response);
this.Context.LogCommandUsed(response.CommandResponse);
}
catch (Exception e)
{
await this.Context.HandleCommandException(e);
}
}
}
1 change: 1 addition & 0 deletions src/FMBot.Domain/Models/ArtistPopularity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public class ArtistPopularity
{
public string Name { get; set; }
public int Popularity { get; set; }
public long Playcount { get; set; }
}
Loading

0 comments on commit 2baa78d

Please sign in to comment.