Skip to content

Commit

Permalink
work on eurovision integration
Browse files Browse the repository at this point in the history
  • Loading branch information
th0mk committed Apr 18, 2024
1 parent 920e0de commit 9231bd7
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 18 deletions.
107 changes: 107 additions & 0 deletions src/FMBot.Bot/Builders/EurovisionBuilders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using FMBot.Bot.Models;
using System.Threading.Tasks;
using Fergun.Interactive;
using FMBot.Bot.Extensions;
using FMBot.Bot.Services;
using FMBot.Domain;
using FMBot.Domain.Models;
using Microsoft.Extensions.Primitives;

namespace FMBot.Bot.Builders;

public class EurovisionBuilders
{
private readonly UserService _userService;
private readonly EurovisionService _eurovisionService;

public EurovisionBuilders(UserService userService, EurovisionService eurovisionService)
{
this._userService = userService;
this._eurovisionService = eurovisionService;
}

public async Task<ResponseModel> GetEurovisionOverview(ContextModel context, int year, string country)
{
var response = new ResponseModel
{
ResponseType = ResponseType.Embed,
};

await this._eurovisionService.UpdateEurovisionData();

PublicProperties.EurovisionYears.TryGetValue(year, out var contestants);

var pages = new List<PageBuilder>();
var pageCounter = 1;
var eurovisionPages = contestants.OrderByDescending(o => o.PointsFinal).ThenByDescending(o => o.SfNum.HasValue).ThenBy(o => o.SfNum).Chunk(8);

foreach (var page in eurovisionPages)
{
var pageDescription = new StringBuilder();

foreach (var eurovisionEntry in page)
{

if (eurovisionEntry.PlaceContest.HasValue)
{
pageDescription.Append(
$"#{eurovisionEntry.PlaceContest}{eurovisionEntry.ToCountry}");
}
else
{
pageDescription.Append(
$"{eurovisionEntry.ToCountry}");
}

if (eurovisionEntry.SfNum.HasValue && !eurovisionEntry.PointsFinal.HasValue)
{
pageDescription.Append(
$"{eurovisionEntry.SfNum}{StringExtensions.GetAmountEnd((int)eurovisionEntry.SfNum.Value)} semi-final - {eurovisionEntry.RunningSf}{StringExtensions.GetAmountEnd((int)eurovisionEntry.RunningSf.GetValueOrDefault())} running");
}
else if (!eurovisionEntry.PointsFinal.HasValue && eurovisionEntry.RunningFinal.HasValue)
{
pageDescription.Append($" — Finals");
if (eurovisionEntry.RunningFinal.HasValue)
{
pageDescription.Append($"{eurovisionEntry.RunningFinal}{StringExtensions.GetAmountEnd((int)eurovisionEntry.RunningFinal.GetValueOrDefault())} running");
}
}
else if (eurovisionEntry.PointsFinal.HasValue)
{
pageDescription.Append($"{eurovisionEntry.PointsFinal}");
}

pageDescription.AppendLine();
pageDescription.Append($":flag_{eurovisionEntry.ToCountryId}: ");
if (!string.IsNullOrWhiteSpace(eurovisionEntry.YoutubeUrl))
{
pageDescription.Append($"**[{eurovisionEntry.Performer} - {eurovisionEntry.Song}]({eurovisionEntry.YoutubeUrl})**");
}
else
{
pageDescription.Append($"**{eurovisionEntry.Performer} - {eurovisionEntry.Song}**");
}
pageDescription.AppendLine();
pageDescription.AppendLine();
}

var footer = new StringBuilder();
footer.Append($"Page {pageCounter}/{eurovisionPages.Count()}");
footer.Append($" - {contestants.Count} total entries");

pages.Add(new PageBuilder()
.WithTitle($"Eurovision {year} <:eurovision:1084971471610323035>")
.WithDescription(pageDescription.ToString())
.WithFooter(footer.ToString()));
pageCounter++;
}

response.StaticPaginator = StringService.BuildStaticPaginator(pages);
response.ResponseType = ResponseType.Paginator;
return response;
}
}
20 changes: 18 additions & 2 deletions src/FMBot.Bot/Builders/TrackBuilders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class TrackBuilders
private readonly DiscogsService _discogsService;
private readonly ArtistsService _artistsService;
private readonly FeaturedService _featuredService;
private readonly EurovisionService _eurovisionService;

public TrackBuilders(UserService userService,
GuildService guildService,
Expand All @@ -66,7 +67,8 @@ public class TrackBuilders
WhoKnowsPlayService whoKnowsPlayService,
DiscogsService discogsService,
ArtistsService artistsService,
FeaturedService featuredService)
FeaturedService featuredService,
EurovisionService eurovisionService)
{
this._userService = userService;
this._guildService = guildService;
Expand All @@ -87,6 +89,7 @@ public class TrackBuilders
this._discogsService = discogsService;
this._artistsService = artistsService;
this._featuredService = featuredService;
this._eurovisionService = eurovisionService;
}

public async Task<ResponseModel> TrackAsync(
Expand Down Expand Up @@ -154,7 +157,6 @@ public class TrackBuilders

var audioFeatures = new StringBuilder();


if (spotifyTrack != null && !string.IsNullOrEmpty(spotifyTrack.SpotifyId))
{
var pitch = StringExtensions.KeyIntToPitchString(spotifyTrack.Key.GetValueOrDefault());
Expand Down Expand Up @@ -239,6 +241,20 @@ public class TrackBuilders
response.Embed.AddField("Info", info.ToString(), true);
}

var eurovisionEntry =
EurovisionService.GetEurovisionEntry(trackSearch.Track.ArtistName, trackSearch.Track.TrackName);

if (eurovisionEntry != null)
{
var eurovisionDescription = EurovisionService.GetEurovisionDescription(eurovisionEntry);
response.Embed.AddField($"Eurovision <:eurovision:1084971471610323035> ", eurovisionDescription.full);
if (eurovisionEntry.YoutubeUrl != null)
{
response.Components = new ComponentBuilder().WithButton(style: ButtonStyle.Link,
emote: Emote.Parse(DiscordConstants.YouTube), url: eurovisionEntry.YoutubeUrl);
}
}

if (!string.IsNullOrWhiteSpace(trackSearch.Track.Description))
{
response.Embed.AddField("Summary", trackSearch.Track.Description);
Expand Down
1 change: 1 addition & 0 deletions src/FMBot.Bot/Resources/DiscordConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static class DiscordConstants
public const string Bandcamp = "<:social_bandcamp:1183838619270643823>";
public const string Spotify = "<:spotify:882221219334725662>";
public const string RateYourMusic = "<:rym:1183851241151930399>";
public const string YouTube = "<:youtube:1230496939355934730>";

public const string Loading = "<a:loading:821676038102056991>";
}
148 changes: 148 additions & 0 deletions src/FMBot.Bot/Services/EurovisionService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using CsvHelper;
using CsvHelper.Configuration;
using FMBot.Bot.Extensions;
using FMBot.Domain;
using FMBot.Domain.Models;

namespace FMBot.Bot.Services;

public class EurovisionService
{
private readonly HttpClient _httpClient;

public EurovisionService(HttpClient httpClient)
{
this._httpClient = httpClient;
}

private class DotToNullIntConverter : CsvHelper.TypeConversion.ITypeConverter
{
public object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
if (string.IsNullOrWhiteSpace(text) || text.Contains('.'))
{
return null;
}

if (int.TryParse(text, out var intValue))
{
return intValue;
}

throw new Exception($"Cannot convert {text} to int.");
}

public string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
return value?.ToString() ?? string.Empty;
}
}

public async Task UpdateEurovisionData()
{
var allContestants = new List<EurovisionContestantModel>();

allContestants.AddRange(await EurovisionCsvToModel("1956-2022"));
allContestants.AddRange(await EurovisionCsvToModel("2023-2024"));

foreach (var contestant in allContestants.OrderBy(o => o.Performer))
{
var key = GetEurovisionContestantEntryKey(contestant.Performer, contestant.Song);
PublicProperties.EurovisionContestants.TryRemove(key, out _);
PublicProperties.EurovisionContestants.TryAdd(key, contestant);
}
foreach (var year in allContestants.GroupBy(g => g.Year))
{
var countries = year.GroupBy(g => g.ToCountryId);
var contestants = countries.Select(s => s.OrderByDescending(o => o.RunningSf.HasValue).First());

PublicProperties.EurovisionYears.TryRemove(year.Key, out _);
PublicProperties.EurovisionYears.TryAdd(year.Key, contestants.ToList());
}
}

public static EurovisionContestantModel GetEurovisionEntry(string artistName, string trackName)
{
var key = GetEurovisionContestantEntryKey(artistName, trackName);
return PublicProperties.EurovisionContestants.GetValueOrDefault(key);
}

public static (string full, string oneline) GetEurovisionDescription(EurovisionContestantModel eurovisionEntry)
{
var full = new StringBuilder();
var oneLine = new StringBuilder();

full.Append(
$"- **{eurovisionEntry.Year}** entry for **{eurovisionEntry.ToCountry}** :flag_{eurovisionEntry.ToCountryId}:");
full.AppendLine();

oneLine.Append($"Eurovision {eurovisionEntry.Year} for {eurovisionEntry.ToCountry} {IsoCountryCodeToFlagEmoji(eurovisionEntry.ToCountryId)}");

if (eurovisionEntry.SfNum.HasValue && !eurovisionEntry.PointsFinal.HasValue)
{
full.Append(
$"- Playing in the **{eurovisionEntry.SfNum}{StringExtensions.GetAmountEnd((int)eurovisionEntry.SfNum.Value)} semi-final** - **{eurovisionEntry.RunningSf}{StringExtensions.GetAmountEnd((int)eurovisionEntry.RunningSf.GetValueOrDefault())} running position**");
oneLine.Append($" - {eurovisionEntry.SfNum}{StringExtensions.GetAmountEnd((int)eurovisionEntry.SfNum.Value)} semi-final");
}
else if (!eurovisionEntry.PointsFinal.HasValue && eurovisionEntry.RunningFinal.HasValue)
{
full.Append($"- Playing in the finals - **{eurovisionEntry.RunningFinal}{StringExtensions.GetAmountEnd((int)eurovisionEntry.RunningFinal.GetValueOrDefault())} running position**");
oneLine.Append($" - Finals");
}
else if (eurovisionEntry.PlaceContest.HasValue)
{
full.Append(
$"- Got **{eurovisionEntry.PlaceContest}{StringExtensions.GetAmountEnd(eurovisionEntry.PlaceContest.GetValueOrDefault())} place**");
if (eurovisionEntry.PointsFinal.HasValue)
{
full.Append($" with **{eurovisionEntry.PointsFinal} points**");
}
oneLine.Append($" - #{eurovisionEntry.PlaceContest}");
}

return (full.ToString(), oneLine.ToString());
}

public static string IsoCountryCodeToFlagEmoji(string country)
{
return string.Concat(country.ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5)));
}

private static string GetEurovisionContestantEntryKey(string artistName, string trackName)
{
// todo: no hardcoded redirects pls
if (artistName.Contains("joost", StringComparison.OrdinalIgnoreCase))
{
artistName = "joost";
}
return $"{artistName.ToLower()}--{trackName.ToLower()}";
}


public async Task<List<EurovisionContestantModel>> EurovisionCsvToModel(string years)
{
await using var stream = await this._httpClient.GetStreamAsync($"https://fmbot.xyz/bot-assets/eurovision/contestants-{years}.csv");

var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
BadDataFound = null,
};

using var innerCsvStreamReader = new StreamReader(stream);

var csv = new CsvReader(innerCsvStreamReader, csvConfig);
csv.Context.TypeConverterCache.AddConverter<int?>(new DotToNullIntConverter());

var records = csv.GetRecords<EurovisionContestantModel>();

return records.ToList();
}
}
1 change: 0 additions & 1 deletion src/FMBot.Bot/Services/SettingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,6 @@ private async Task<User> DiscordIdToUser(string value)
.Replace("r:", "", StringComparison.OrdinalIgnoreCase)
.Replace("released:", "", StringComparison.OrdinalIgnoreCase);


if (cleaned.Length == 4 && int.TryParse(cleaned, out var result))
{
if (result >= 1900 && result <= DateTime.Today.AddDays(1).Year)
Expand Down
9 changes: 8 additions & 1 deletion src/FMBot.Bot/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,13 @@ public async Task<string> GetUserTitleAsync(IGuild guild, IUser user)
}
}

var eurovision = EurovisionService.GetEurovisionEntry(artistName, trackName);
if (eurovision != null)
{
var description = EurovisionService.GetEurovisionDescription(eurovision);
options.Add(description.oneline);
}

return CreateFooter(options, genres);
}

Expand Down Expand Up @@ -1165,7 +1172,7 @@ public async Task<User> SetDataSource(User userToUpdate, DataSource dataSource)
var user = await db.Users.FirstAsync(f => f.UserId == userToUpdate.UserId);

userToUpdate.DataSource = dataSource;

user.DataSource = dataSource;
db.Entry(user).State = EntityState.Modified;

Expand Down
2 changes: 2 additions & 0 deletions src/FMBot.Bot/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ private void ConfigureServices(IServiceCollection services)
.AddSingleton<CommandHandler>()
.AddSingleton<DiscogsBuilder>()
.AddSingleton<DiscogsService>()
.AddSingleton<EurovisionBuilders>()
.AddSingleton<FeaturedService>()
.AddSingleton<FriendsService>()
.AddSingleton<FriendBuilders>()
Expand Down Expand Up @@ -247,6 +248,7 @@ private void ConfigureServices(IServiceCollection services)
services.AddHttpClient<DiscordSkuService>();
services.AddHttpClient<OpenCollectiveService>();
services.AddHttpClient<OpenAiService>();
services.AddHttpClient<EurovisionService>();

services.AddHttpClient<SpotifyService>(client =>
{
Expand Down
Loading

0 comments on commit 9231bd7

Please sign in to comment.