diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index cacea7636b..c5f075986a 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -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 localUser = new Bindable(); private readonly IBindable apiState = new Bindable(); private readonly IBindableList localUserBlocks = new BindableList(); @@ -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 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);