// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using DiscordRPC; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; using static osu.Game.Users.UserActivity; using User = osu.Game.Users.User; namespace osu.Desktop { internal class DiscordRichPresenceClient : Component { private const string client_id = "563024054391537674"; private Bindable user; private readonly DiscordRpcClient client = new DiscordRpcClient(client_id); [BackgroundDependencyLoader] private void load(IAPIProvider provider) { user = provider.LocalUser.GetBoundCopy(); user.ValueChanged += usr => { usr.NewValue.Activity.ValueChanged += activity => updateStatus(user.Value.Status.Value, activity.NewValue); usr.NewValue.Status.ValueChanged += status => updateStatus(status.NewValue, user.Value.Activity.Value); }; user.TriggerChange(); enableLogging(); client.Initialize(); } private void enableLogging() { client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); } private void updateStatus(UserStatus st, UserActivity a) { var presence = defaultPresence(st is UserStatusOnline ? a?.Status : st.Message); if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence { client.SetPresence(presence); return; } switch (a) { case UserActivitySoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; case UserActivityEditing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; break; } client.SetPresence(presence); } private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) { switch (ruleset.ID) { case 0: presence.Assets.SmallImageKey = "osu"; break; case 1: presence.Assets.SmallImageKey = "taiko"; break; case 2: presence.Assets.SmallImageKey = "fruits"; break; case 3: presence.Assets.SmallImageKey = "mania"; break; default: presence.Assets.SmallImageKey = "unknown"; break; } presence.Assets.SmallImageText = ruleset.ShortName; } private RichPresence defaultPresence(string status) => new RichPresence { Details = status, Assets = new Assets { LargeImageKey = "lazer", LargeImageText = user.Value.Username } }; } }