diff --git a/LibMatrix b/LibMatrix
-Subproject 84e33226690beb65f747526714ac9aa4cc8ee15
+Subproject 4340b1899470c06f170817dbc1200040619fbf8
diff --git a/OsuFederatedBeatmapApi.sln.DotSettings.user b/OsuFederatedBeatmapApi.sln.DotSettings.user
index d5ce503..98ceabe 100644
--- a/OsuFederatedBeatmapApi.sln.DotSettings.user
+++ b/OsuFederatedBeatmapApi.sln.DotSettings.user
@@ -1,3 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
- <s:Int64 x:Key="/Default/Dpa/Thresholds/=AllocationClosure/@EntryIndexedValue">52428800</s:Int64>
+ <s:Int64 x:Key="/Default/Dpa/Thresholds/=AllocationClosure/@EntryIndexedValue">53477376</s:Int64>
<s:Int64 x:Key="/Default/Dpa/Thresholds/=AllocationLoh/@EntryIndexedValue">52428800</s:Int64></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/OsuFederatedBeatmapApi/Controllers/BeatmapRepositoryController.cs b/OsuFederatedBeatmapApi/Controllers/BeatmapRepositoryController.cs
index 6a1149d..b1fa4f1 100644
--- a/OsuFederatedBeatmapApi/Controllers/BeatmapRepositoryController.cs
+++ b/OsuFederatedBeatmapApi/Controllers/BeatmapRepositoryController.cs
@@ -6,7 +6,7 @@ namespace OsuFederatedBeatmapApi.Controllers;
[ApiController]
[Route("/")]
-public class BeatmapRepositoryController(ILogger<BeatmapRepositoryController> logger, FederatedBeatmapApiBotAccountDataService ads) : ControllerBase {
+public class BeatmapRepositoryController(ILogger<BeatmapRepositoryController> logger, FederatedBeatmapApiBotAccountDataService ads, BeatmapFetcherService bfs) : ControllerBase {
[HttpGet("/beatmapset/all/info")]
public async IAsyncEnumerable<BeatmapSetInfo> GetAllInfo() {
await ads.LoadAccountDataAsync();
@@ -24,8 +24,9 @@ public class BeatmapRepositoryController(ILogger<BeatmapRepositoryController> lo
await ads.LoadAccountDataAsync();
foreach (var repo in ads.ListedRepositories) {
- var states = await repo.GetStateAsync<BeatmapSetInfo>("gay.rory.beatmap_api.beatmap_set_info", id.ToString());
- return states as BeatmapSetInfo;
+ var state = await repo.GetStateOrNullAsync<BeatmapSetInfo>("gay.rory.beatmap_api.beatmap_set_info", id.ToString());
+ if (state is not null) return state;
+ return await bfs.FetchBeatmapSetInfo(id);
}
return null;
diff --git a/OsuFederatedBeatmapApi/Events/State/BeatmapSetInfo.cs b/OsuFederatedBeatmapApi/Events/State/BeatmapSetInfo.cs
index 8cfcf0d..7d6b455 100644
--- a/OsuFederatedBeatmapApi/Events/State/BeatmapSetInfo.cs
+++ b/OsuFederatedBeatmapApi/Events/State/BeatmapSetInfo.cs
@@ -1,12 +1,46 @@
+using System.Text.Json.Serialization;
using LibMatrix;
using LibMatrix.EventTypes;
using LibMatrix.Interfaces;
namespace OsuFederatedBeatmapApi.Events.State;
-[MatrixEvent(EventName = "gay.rory.beatmap_api.beatmap_set_info")]
+[MatrixEvent(EventName = EventName)]
public class BeatmapSetInfo : EventContent {
+ public const string EventName = "gay.rory.beatmap_api.beatmap_set_info";
+ [JsonPropertyName("title")]
+ public string? Title { get; set; }
+ [JsonPropertyName("artist")]
+ public string? Artist { get; set; }
+ [JsonPropertyName("creator")]
+ public string? Creator { get; set; }
+
+ [JsonPropertyName("beatmaps")]
+ public Dictionary<int, BeatmapInfo> Beatmaps { get; set; } = new();
+
+ public class BeatmapInfo {
+ [JsonPropertyName("difficulty")]
+ public string? Difficulty { get; set; }
+
+ [JsonPropertyName("mode")]
+ public int? Mode { get; set; }
+
+ [JsonPropertyName("difficulty_rating")]
+ public double? DifficultyRating { get; set; }
+
+ [JsonPropertyName("total_length")]
+ public int? TotalLength { get; set; }
+
+ [JsonPropertyName("bpm")]
+ public double? BPM { get; set; }
+
+ [JsonPropertyName("max_combo")]
+ public int? MaxCombo { get; set; }
+
+ // [JsonPropertyName("drain")]
+ // public double? Drain { get; set; }
+ }
}
diff --git a/OsuFederatedBeatmapApi/OsuFederatedBeatmapApi.csproj b/OsuFederatedBeatmapApi/OsuFederatedBeatmapApi.csproj
index 387808f..bc2a324 100644
--- a/OsuFederatedBeatmapApi/OsuFederatedBeatmapApi.csproj
+++ b/OsuFederatedBeatmapApi/OsuFederatedBeatmapApi.csproj
@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj" />
+
</ItemGroup>
diff --git a/OsuFederatedBeatmapApi/Program.cs b/OsuFederatedBeatmapApi/Program.cs
index 15f9b12..8f54865 100644
--- a/OsuFederatedBeatmapApi/Program.cs
+++ b/OsuFederatedBeatmapApi/Program.cs
@@ -1,3 +1,4 @@
+using System.Text.Json;
using LibMatrix.Services;
using LibMatrix.Utilities.Bot;
using OsuFederatedBeatmapApi.Services;
@@ -21,6 +22,8 @@ builder.Services.AddScoped<TieredStorageService>(x =>
builder.Services.AddRoryLibMatrixServices();
builder.Services.AddBot(withCommands: true);
+builder.Services.AddScoped<BeatmapFetcherService>();
+
builder.Services.AddSingleton<FederatedBeatmapApiBotConfiguration>();
builder.Services.AddScoped<FederatedBeatmapApiBotAccountDataService>();
builder.Services.AddHostedService<FederatedBeatmapApiBot>();
diff --git a/OsuFederatedBeatmapApi/Services/BeatmapFetcherService.cs b/OsuFederatedBeatmapApi/Services/BeatmapFetcherService.cs
new file mode 100644
index 0000000..ae108fb
--- /dev/null
+++ b/OsuFederatedBeatmapApi/Services/BeatmapFetcherService.cs
@@ -0,0 +1,34 @@
+using System.Net.Http.Headers;
+using OsuFederatedBeatmapApi.Events.State;
+using OsuFederatedBeatmapApi.UtilityClasses;
+
+namespace OsuFederatedBeatmapApi.Services;
+
+public class BeatmapFetcherService(FederatedBeatmapApiBotAccountDataService ads) {
+
+ public async Task<BeatmapSetInfo> FetchBeatmapSetInfo(int id) {
+ var hc = new HttpClient();
+ hc.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("OsuFederatedBeatmapApi", "1.0"));
+ var res = await hc.GetFromJsonAsync<OsuDirectApiResponses.BeatmapSet>($"https://osu.direct/api/s/{id}");
+ var info = new BeatmapSetInfo {
+ Title = res.Title,
+ Artist = res.Artist,
+ Creator = res.Creator,
+ Beatmaps = res.ChildrenBeatmaps.ToDictionary(b => b.BeatmapID, b => new BeatmapSetInfo.BeatmapInfo {
+ Difficulty = b.DiffName,
+ Mode = b.Mode,
+ DifficultyRating = b.DifficultyRating,
+ TotalLength = b.TotalLength,
+ BPM = b.BPM,
+ MaxCombo = b.MaxCombo
+ })
+ };
+
+ await ads.LoadAccountDataAsync();
+
+ await ads.OwnBeatmapRepositoryRoom.SendStateEventAsync(BeatmapSetInfo.EventName, id.ToString(), info);
+
+ return info;
+ }
+
+}
diff --git a/OsuFederatedBeatmapApi/Services/FederatedBeatmapApiBot.cs b/OsuFederatedBeatmapApi/Services/FederatedBeatmapApiBot.cs
index b8cc635..2aaf45c 100644
--- a/OsuFederatedBeatmapApi/Services/FederatedBeatmapApiBot.cs
+++ b/OsuFederatedBeatmapApi/Services/FederatedBeatmapApiBot.cs
@@ -40,7 +40,28 @@ public class FederatedBeatmapApiBot(AuthenticatedHomeserverGeneric hs,
var controlRoomMembers = accountDataService.ControlRoom.GetMembersAsync();
await foreach (var member in controlRoomMembers) {
if ((member.TypedContent as RoomMemberEventContent)?
- .Membership == "join") admins.Add(member.UserId);
+ .Membership == "join")
+ admins.Add(member.StateKey);
+ }
+
+ var pls = await accountDataService.OwnBeatmapRepositoryRoom.GetPowerLevelsAsync();
+ var ownPl = pls.GetUserPowerLevel(hs.UserId);
+
+ if (!pls.UserHasPermission(hs.UserId, RoomPowerLevelEventContent.EventId)) {
+ accountDataService.LogRoom.SendMessageEventAsync(
+ MessageFormatter.FormatError(
+ $"I don't have permission to send power level updates in " +
+ $"{MessageFormatter.HtmlFormatMention(accountDataService.OwnBeatmapRepositoryRoom.RoomId, "my own beatmap repository")}!"));
+ }
+ else {
+ foreach (var admin in admins) {
+ if (pls.GetUserPowerLevel(admin) < ownPl - 1) {
+ logger.LogInformation("Raising powerlevel of {} in {} to {}", admin, accountDataService.OwnBeatmapRepositoryRoom.RoomId, ownPl - 1);
+ pls.SetUserPowerLevel(admin, ownPl - 1);
+ await accountDataService.OwnBeatmapRepositoryRoom.SendStateEventAsync(
+ RoomPowerLevelEventContent.EventId, pls);
+ }
+ }
}
await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
diff --git a/OsuFederatedBeatmapApi/UtilityClasses/OsuDirectApiResponses.cs b/OsuFederatedBeatmapApi/UtilityClasses/OsuDirectApiResponses.cs
new file mode 100644
index 0000000..b7b67b1
--- /dev/null
+++ b/OsuFederatedBeatmapApi/UtilityClasses/OsuDirectApiResponses.cs
@@ -0,0 +1,107 @@
+using System.Text.Json.Serialization;
+
+namespace OsuFederatedBeatmapApi.UtilityClasses;
+
+public class OsuDirectApiResponses {
+ public class ChildrenBeatmap
+ {
+ [JsonPropertyName("ParentSetID")]
+ public int ParentSetID { get; set; }
+
+ [JsonPropertyName("BeatmapID")]
+ public int BeatmapID { get; set; }
+
+ [JsonPropertyName("TotalLength")]
+ public int TotalLength { get; set; }
+
+ [JsonPropertyName("HitLength")]
+ public int HitLength { get; set; }
+
+ [JsonPropertyName("DiffName")]
+ public string DiffName { get; set; }
+
+ [JsonPropertyName("FileMD5")]
+ public string FileMD5 { get; set; }
+
+ [JsonPropertyName("CS")]
+ public int CS { get; set; }
+
+ [JsonPropertyName("AR")]
+ public int AR { get; set; }
+
+ [JsonPropertyName("HP")]
+ public int HP { get; set; }
+
+ [JsonPropertyName("OD")]
+ public int OD { get; set; }
+
+ [JsonPropertyName("Mode")]
+ public int Mode { get; set; }
+
+ [JsonPropertyName("BPM")]
+ public int BPM { get; set; }
+
+ [JsonPropertyName("Playcount")]
+ public int Playcount { get; set; }
+
+ [JsonPropertyName("Passcount")]
+ public int Passcount { get; set; }
+
+ [JsonPropertyName("MaxCombo")]
+ public int MaxCombo { get; set; }
+
+ [JsonPropertyName("DifficultyRating")]
+ public double DifficultyRating { get; set; }
+ }
+
+ public class BeatmapSet
+ {
+ [JsonPropertyName("SetID")]
+ public int SetID { get; set; }
+
+ [JsonPropertyName("Title")]
+ public string Title { get; set; }
+
+ [JsonPropertyName("Artist")]
+ public string Artist { get; set; }
+
+ [JsonPropertyName("Creator")]
+ public string Creator { get; set; }
+
+ [JsonPropertyName("Source")]
+ public string Source { get; set; }
+
+ [JsonPropertyName("Tags")]
+ public string Tags { get; set; }
+
+ [JsonPropertyName("RankedStatus")]
+ public int RankedStatus { get; set; }
+
+ [JsonPropertyName("Genre")]
+ public int Genre { get; set; }
+
+ [JsonPropertyName("Language")]
+ public int Language { get; set; }
+
+ [JsonPropertyName("Favourites")]
+ public int Favourites { get; set; }
+
+ [JsonPropertyName("HasVideo")]
+ public int HasVideo { get; set; }
+
+ [JsonPropertyName("SubmittedDate")]
+ public DateTime SubmittedDate { get; set; }
+
+ [JsonPropertyName("ApprovedDate")]
+ public DateTime ApprovedDate { get; set; }
+
+ [JsonPropertyName("LastUpdate")]
+ public DateTime LastUpdate { get; set; }
+
+ [JsonPropertyName("LastChecked")]
+ public DateTime LastChecked { get; set; }
+
+ [JsonPropertyName("ChildrenBeatmaps")]
+ public List<ChildrenBeatmap> ChildrenBeatmaps { get; set; }
+ }
+}
|