1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 20:33:35 +08:00

Implement /savelog chat command (#37093)

Addresses https://github.com/ppy/osu/discussions/37090.

Filename and format almost match stable with the exception of:

- adding `chat-` prefix to export filename to distinguish from
everything else in the `exports` folder
- adding date to message timestamps as stable writes time only
This commit is contained in:
Bartłomiej Dach
2026-03-25 09:49:55 +01:00
committed by GitHub
Unverified
parent 86ef145f56
commit 31b81fdbbd
+73 -1
View File
@@ -6,7 +6,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.SignalR;
using osu.Framework.Allocation;
@@ -15,13 +17,19 @@ using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Localisation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays;
using osu.Game.Overlays.Chat.Listing;
using osu.Game.Overlays.Notifications;
namespace osu.Game.Online.Chat
{
@@ -77,6 +85,14 @@ namespace osu.Game.Online.Chat
[CanBeNull]
private MultiplayerClient multiplayerClient { get; set; }
[Resolved(CanBeNull = true)]
[CanBeNull]
private Storage storage { get; set; }
[Resolved(CanBeNull = true)]
[CanBeNull]
private INotificationOverlay notifications { get; set; }
private readonly IBindable<APIUser> localUser = new Bindable<APIUser>();
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
private readonly IBindableList<APIRelation> localUserBlocks = new BindableList<APIRelation>();
@@ -358,8 +374,44 @@ namespace osu.Game.Online.Chat
});
break;
case @"savelog":
ProgressNotification notification = new ProgressNotification
{
State = ProgressNotificationState.Active,
Text = NotificationsStrings.LogsExportOngoing,
};
notifications?.Post(notification);
exportChannelLog(target).ContinueWith(t =>
{
if (t.Exception != null)
{
Logger.Log($@"Failed to export channel log: {t.Exception}");
notification.State = ProgressNotificationState.Cancelled;
return;
}
string result = t.GetResultSafely();
if (result == null)
{
Logger.Log("Failed to export channel log due to missing storage.");
notification.State = ProgressNotificationState.Cancelled;
return;
}
notification.CompletionText = NotificationsStrings.FileExportFinished(result);
notification.CompletionClickAction = () =>
{
(storage as OsuStorage)?.GetExportStorage().PresentFileExternally(result);
return true;
};
notification.State = ProgressNotificationState.Completed;
});
break;
case @"help":
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /chat [user], /np, /roll [2-100] (multiplayer only)"));
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /chat [user], /np, /savelog, /roll [2-100] (multiplayer only)"));
break;
default:
@@ -714,6 +766,26 @@ namespace osu.Game.Online.Chat
}
}
[ItemCanBeNull]
private async Task<string> exportChannelLog(Channel channel)
{
if (storage is not OsuStorage osuStorage)
return null;
string filename = string.Format($@"chat-{channel.Name}-{DateTimeOffset.Now:yyyyMMdd-hhmmss}.txt").GetValidFilename();
var exportStorage = osuStorage.GetExportStorage();
using (var file = exportStorage.CreateFileSafely(filename))
{
using var textWriter = new StreamWriter(file);
foreach (var message in channel.Messages)
await textWriter.WriteLineAsync($@"{message.Timestamp:yyyy-MM-dd HH:mm} {message.Sender.Username}: {message.Content}").ConfigureAwait(false);
}
return filename;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);