Skip to content

Commit

Permalink
Merge pull request #13 from minisbett/master
Browse files Browse the repository at this point in the history
release: version 2.2.0
  • Loading branch information
minisbett authored Jul 20, 2024
2 parents 7ebbeb3 + 31c7f25 commit 1cefab8
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 37 deletions.
14 changes: 14 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: minisbett
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
21 changes: 14 additions & 7 deletions huisbot/Models/Huis/HuisPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,47 +32,54 @@ public class HuisPlayer
public string? Name { get; private set; }

/// <summary>
/// The live pp of the player.
/// The live PP of the player.
/// </summary>
[JsonProperty("old_pp")]
public double OldPP { get; private set; }

/// <summary>
/// The pp of the player in the rework the player object is from.
/// The PP of the player in the rework the player object is from.
/// </summary>
[JsonProperty("new_pp_incl_bonus")]
public double NewPP { get; private set; }

/// <summary>
/// The bonus pp of the player.
/// The bonus PP of the player.
/// </summary>
[JsonProperty("bonus_pp")]
public double BonusPP { get; private set; }

/// <summary>
/// The weighted accuracy pp of the player in the rework the player object is from.
/// The weighted accuracy PP of the player in the rework the player object is from.
/// </summary>
[JsonProperty("weighted_acc_pp")]
public double AccPP { get; private set; }

/// <summary>
/// The weighted aim pp of the player in the rework the player object is from.
/// The weighted aim PP of the player in the rework the player object is from.
/// </summary>
[JsonProperty("weighted_aim_pp")]
public double AimPP { get; private set; }

/// <summary>
/// The weighted tapping pp of the player in the rework the player object is from.
/// The weighted tapping PP of the player in the rework the player object is from.
/// </summary>
[JsonProperty("weighted_tap_pp")]
public double TapPP { get; private set; }

/// <summary>
/// The weighted flashlight pp of the player in the rework the player object is from.
/// The weighted flashlight PP of the player in the rework the player object is from.
/// </summary>
[JsonProperty("weighted_fl_pp")]
public double FLPP { get; private set; }

/// <summary>
/// The weighted cognition PP of the player in the rework the player object is from.
/// This skill only exists in some reworks, and therefore may be null.
/// </summary>
[JsonProperty("weighted_cognition_pp")]
public double? CognitionPP { get; private set; }

/// <summary>
/// The last time the player got updated on Huismetbenen.
/// </summary>
Expand Down
11 changes: 8 additions & 3 deletions huisbot/Models/Huis/HuisScore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@ public class HuisScore
public double LocalPP { get; private set; }

/// <summary>
/// The mods of the score.
/// The string representation of the mods of the score. Null if no mods are present.
/// </summary>
[JsonProperty("mods")]
public Mods Mods { get; private set; } = null!;
public string? ModsStr { get; private set; }

/// <summary>
/// The mods of the score. Defaults to an empty <see cref="Utilities.Mods"/> object if <see cref="ModsStr"/> is null.
/// </summary>
public Mods Mods => Mods.Parse(ModsStr ?? "");

/// <summary>
/// The amount of 300s/greats of the score.
Expand Down Expand Up @@ -124,7 +129,7 @@ public class HuisScore

public override string ToString()
{
return $"{Username} | {Artist} - {Title} ({Mapper}) [{Version}]{Mods.PlusString} " +
return $"{Username} | {Artist} - {Title} ({Mapper}) [{Version}]{Mods!.PlusString} " +
$"{Accuracy}% {MaxCombo}x {LivePP} -> {LocalPP}pp [{Count300}/{Count100}/{Count50}/{Misses}]";
}
}
6 changes: 6 additions & 0 deletions huisbot/Models/Huis/HuisSimulatedScore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public class HuisSimulatedScore
[JsonProperty("fl_pp")]
public double FLPP { get; private set; }

/// <summary>
/// The PP for the congition skill. This skill only exists in some reworks, and therefore may be null.
/// </summary>
[JsonProperty("cognition_pp")]
public double? CognitionPP { get; private set; }

/// <summary>
/// The total PP of the score.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion huisbot/Models/Osu/OsuBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public double GetAdjustedAR(Mods mods)
// Ensure scaling of the pre-empt through the clock rate of the mods.
int ms = (int)(ar >= 5 ? ar == 5 ? 1200 : 1200 - 750 * (ar - 5) / 5d : 1200 + 600 * (5 - ar) / 5d);
ms = (int)(ms / mods.ClockRate);
return Math.Min(11.11, (ms == 1200) ? 5 : (ms > 1200) ? 5 - 5 * (1200 - ms) / 600d : 5 + 5 * (1200 - ms) / 750d);
return Math.Min(11.11, (ms == 1200) ? 5 : (ms > 1200) ? 5 - 5 * (ms - 1200) / 600d : 5 + 5 * (1200 - ms) / 750d);
}

/// <summary>
Expand Down
85 changes: 85 additions & 0 deletions huisbot/Modules/Huis/Feedback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using huisbot.Models.Huis;
using huisbot.Models.Osu;
using huisbot.Models.Persistence;
using huisbot.Services;
using huisbot.Utilities.Discord;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace huisbot.Modules.Huis;

public class FeedbackCommandModule : ModuleBase
{
public FeedbackCommandModule(HuisApiService huis) : base(huis) { }

[SlashCommand("feedback", "Feedback")]
public async Task HandleAsync(
[Summary("rework", "An identifier for the rework. This can be it's ID, internal code or autocompleted name.")]
[Autocomplete(typeof(ReworkAutocompleteHandler))] string reworkId)
{
// Get the specified rework.
HuisRework? rework = await GetReworkAsync(reworkId);
if (rework is null)
return;

// Make sure the rework is eligible.
if(rework.IsLive || !rework.IsActive || rework.IsHistoric || rework.IsConfirmed)
{
await RespondAsync(embed: Embeds.Error("The specified rework cannot receive feedback."));
return;
}

// Build the modal and respond.
await RespondWithModalAsync<FeedbackModal>($"pp_feedback_{rework.Id}");
}
}

/// <summary>
/// The interaction module for the "rework" select menu from the <see cref="ReworksCommandModule"/> command.
/// </summary>
public class FeedbackModalModule : ModuleBase
{
public FeedbackModalModule(HuisApiService huis) : base(huis) { }

[ModalInteraction("pp_feedback_.*", TreatAsRegex = true)]
public async Task HandleAsync(FeedbackModal modal)
{
await DeferAsync();

// Get the rework from it's ID.
HuisRework? rework = await GetReworkAsync(((SocketModal)Context.Interaction).Data.CustomId.Substring(12) /* pp_feedback_<rework ID> */);
if (rework is null)
return;

#if DEVELOPMENT || CUTTING_EDGE
// In development or cutting edge mode, always send the feedback into the same channel.
ISocketMessageChannel channel = Context.Channel;
#else
// Get the PP Discord feedback channel. (WIP, other temp channel for now)
ISocketMessageChannel channel = Context.Client.GetGuild(1166126757141827775).GetTextChannel(1264243048821293105);
#endif

// Respond to the user and send the feedback in the feedback channel.
await FollowupAsync(embed: Embeds.Success("Your feedback was submitted."));
await channel.SendMessageAsync(embed: Embeds.Feedback(Context.User, rework, modal.FeebackText));
}
}

public class FeedbackModal : IModal
{
public string Title => "PP Feedback Form";

/// <summary>
/// The text of the feedback.
/// </summary>
[InputLabel("Feedback")]
[ModalTextInput("text", TextInputStyle.Paragraph, "Type your feedback here... NOTE: The feedback is NOT anonymous, and publically visible.", 200)]
public required string FeebackText { get; init; }
}
2 changes: 1 addition & 1 deletion huisbot/Modules/Huis/Reworks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task HandleAsync()
}

/// <summary>
/// The interaction module for the "rework" select menu from the <see cref="ReworksCommandModule"/> command.
/// The interaction module for the rework select menu from the <see cref="ReworksCommandModule"/> command.
/// </summary>
public class ReworksComponentModule : ModuleBase
{
Expand Down
24 changes: 21 additions & 3 deletions huisbot/Modules/Huis/Simulate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,29 @@ public async Task HandleAsync(
{
await DeferAsync();

// Check if either a beatmap ID or a score ID was specified.
// Check if either a beatmap ID or a score ID was specified, or if a recent bot message with a beatmap URL can be found.
if (beatmapId is null && scoreId is null)
{
await FollowupAsync(embed: Embeds.Error("Either a beatmap ID or a score ID must be specified."));
return;
// Look for a message with a score in the last 100 messages.
foreach (IMessage message in (await Context.Channel.GetMessagesAsync(100).FlattenAsync()))
if (Utils.TryFindScore(message, out (int? beatmapId, int? score100, int? score50, int? scoreMiss, int? combo, string? mods) score))
{
beatmapId = score.beatmapId.ToString();
combo ??= score.combo;
count100 ??= score.score100;
count50 ??= score.score50;
misses ??= score.scoreMiss;
modsStr ??= score.mods;
break;
}


// If there was no beatmap ID found in the last 100 messages, respond with an error.
if (beatmapId is null)
{
await FollowupAsync(embed: Embeds.Error("Either a beatmap ID or a score ID must be specified."));
return;
}
}

// Get the matching rework for the specified rework identifier.
Expand Down
8 changes: 5 additions & 3 deletions huisbot/Modules/ModuleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public async Task<bool> QueuePlayerAsync(OsuUser player, int reworkId, ulong dis
else
await FollowupAsync(embed: Embeds.Neutral($"`{player.Name}` has been queued. You will be notified once it completed."));

return queued.HasValue ? queued.HasValue : false;
return queued.HasValue ? queued.Value : false;
}

/// <summary>
Expand Down Expand Up @@ -369,7 +369,8 @@ await ModifyOriginalResponseAsync(x =>
/// <param name="context">The Discord socket interaction context.</param>
public static async Task<bool> IsOnionAsync(SocketInteractionContext context)
{
#if DEBUG
#if DEVELOPMENT || CUTTING_EDGE
// In development mode, always grant the permissions.
return true;
#endif

Expand All @@ -391,7 +392,8 @@ public static async Task<bool> IsOnionAsync(SocketInteractionContext context)
/// <param name="context">The Discord socket interaction context.</param>
public static async Task<bool> IsPPTeamAsync(SocketInteractionContext context)
{
#if DEBUG
#if DEVELOPMENT
// In development mode, always grant the permissions.
return true;
#endif

Expand Down
6 changes: 3 additions & 3 deletions huisbot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class Program
/// <summary>
/// The version of the application.
/// </summary>
public const string VERSION = "2.1.1";
public const string VERSION = "2.2.0";

/// <summary>
/// The startup time of the application.
Expand Down Expand Up @@ -92,7 +92,7 @@ public static async Task MainAsync(string[] args)
LogLevel = LogSeverity.Verbose,
AlwaysDownloadUsers = true,
MessageCacheSize = 100,
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | GatewayIntents.MessageContent
};
config.Token = context.Configuration["BOT_TOKEN"]
Expand Down Expand Up @@ -172,4 +172,4 @@ public static async Task MainAsync(string[] args)
// Run the host.
await host.RunAsync();
}
}
}
7 changes: 4 additions & 3 deletions huisbot/Services/InteractionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Discord.Addons.Hosting;
using Discord;
using Discord.Addons.Hosting;
using Discord.Addons.Hosting.Util;
using Discord.Interactions;
using Discord.WebSocket;
Expand Down Expand Up @@ -53,7 +54,7 @@ private Task OnSlashCommandExecuted(SocketSlashCommand command)
string parse(IReadOnlyCollection<SocketSlashCommandDataOption> data, string str = "")
{
foreach (var i in data)
str += " " + (i.Type == Discord.ApplicationCommandOptionType.SubCommand ? $"{i.Name}{parse(i.Options, str)}" : $"{i.Name}:{i.Value}");
str += " " + (i.Type == ApplicationCommandOptionType.SubCommand ? $"{i.Name}{parse(i.Options, str)}" : $"{i.Name}:{i.Value}");

return str;
}
Expand All @@ -62,7 +63,7 @@ string parse(IReadOnlyCollection<SocketSlashCommandDataOption> data, string str
string guild = command.GuildId is null ? "Unknown" : $"{Client.GetGuild(command.GuildId.Value)} ({command.GuildId})";
string user = $"{command.User.Username} [{command.User.GlobalName}] ({command.User.Id})";
string cmd = $"/{command.CommandName}{parse(command.Data.Options)}";
Logger.LogInformation("Guild: {guild}\nUser: {user}\nCommand: {cmd}", guild, user, cmd);
Logger.LogInformation(" Guild: {guild}\n User: {user}\nCommand: {cmd}", guild, user, cmd);

return Task.CompletedTask;
}
Expand Down
10 changes: 5 additions & 5 deletions huisbot/Services/OsuApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class OsuApiService
/// <summary>
/// The API key for the osu! v1 API.
/// </summary>
private readonly string _apikey;
private readonly string _apiKey;

/// <summary>
/// The client ID for the osu! v2 API.
Expand All @@ -39,7 +39,7 @@ public class OsuApiService
public OsuApiService(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger<OsuApiService> logger)
{
_http = httpClientFactory.CreateClient("osuapi");
_apikey = config["OSU_API_KEY"] ?? throw new InvalidOperationException("The environment variable 'OSU_API_KEY' is not set.");
_apiKey = config["OSU_API_KEY"] ?? throw new InvalidOperationException("The environment variable 'OSU_API_KEY' is not set.");
_clientId = config["OSU_OAUTH_CLIENT_ID"] ?? throw new InvalidOperationException("The environment variable 'OSU_OAUTH_CLIENT_ID' is not set.");
_clientSecret = config["OSU_OAUTH_CLIENT_SECRET"] ?? throw new InvalidOperationException("The environment variable 'OSU_OAUTH_CLIENT_SECRET' is not set.");
_logger = logger;
Expand Down Expand Up @@ -155,7 +155,7 @@ public async Task<bool> EnsureAccessTokenAsync()
try
{
// Get the user from the API.
string json = await _http.GetStringAsync($"api/get_user?u={identifier}&k={_apikey}");
string json = await _http.GetStringAsync($"api/get_user?u={identifier}&k={_apiKey}");
OsuUser? user = JsonConvert.DeserializeObject<OsuUser[]>(json)?.FirstOrDefault();

// Check whether the deserialized json is null/an empty array. If so, the user could not be found. The API returns "[]" when the user could not be found.
Expand All @@ -180,7 +180,7 @@ public async Task<bool> EnsureAccessTokenAsync()
try
{
// Get the user from the API.
string json = await _http.GetStringAsync($"api/get_beatmaps?b={id}&k={_apikey}");
string json = await _http.GetStringAsync($"api/get_beatmaps?b={id}&k={_apiKey}");
OsuBeatmap? beatmap = JsonConvert.DeserializeObject<OsuBeatmap[]>(json)?.FirstOrDefault(x => x.Id == id);

// Check whether the deserialized json is null/an empty array. If so, the beatmap could not be found. The API returns "[]" when the beatmap could not be found.
Expand All @@ -207,7 +207,7 @@ public async Task<bool> EnsureAccessTokenAsync()
public async Task<double?> GetDifficultyRatingAsync(int rulesetId, int beatmapId, Mods mods)
{
// Serialize the JSON string for the request.
var requestJson = JsonConvert.SerializeObject(new
string requestJson = JsonConvert.SerializeObject(new
{
ruleset_id = rulesetId,
beatmap_id = beatmapId,
Expand Down
Loading

0 comments on commit 1cefab8

Please sign in to comment.