From 25d5d36772b5f518a40f8596ee36bb145b733ce2 Mon Sep 17 00:00:00 2001 From: Paulo Date: Sun, 22 Nov 2020 00:42:48 -0300 Subject: [PATCH] feature: Send presence on Identify payload (#1688) * Send presence on identify * Change CurrentUser presence --- .../API/Gateway/IdentifyParams.cs | 2 + .../API/Gateway/StatusUpdateParams.cs | 6 +-- .../DiscordSocketApiClient.cs | 18 +++++-- .../DiscordSocketClient.cs | 47 ++++++++++++------- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs index e3e24491d..92a494b71 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs @@ -15,6 +15,8 @@ namespace Discord.API.Gateway public int LargeThreshold { get; set; } [JsonProperty("shard")] public Optional ShardingParams { get; set; } + [JsonProperty("presence")] + public Optional Presence { get; set; } [JsonProperty("guild_subscriptions")] public Optional GuildSubscriptions { get; set; } [JsonProperty("intents")] diff --git a/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs b/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs index fc0964c17..e685e5e57 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Gateway @@ -12,7 +12,7 @@ namespace Discord.API.Gateway public long? IdleSince { get; set; } [JsonProperty("afk")] public bool IsAFK { get; set; } - [JsonProperty("game")] - public Game Game { get; set; } + [JsonProperty("activities")] + public Game[] Activities { get; set; } } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 07ebc87ec..5a926ba26 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -12,6 +12,7 @@ using System.IO.Compression; using System.Text; using System.Threading; using System.Threading.Tasks; +using GameModel = Discord.API.Game; namespace Discord.API { @@ -215,7 +216,7 @@ namespace Discord.API await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); } - public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, bool guildSubscriptions = true, GatewayIntents? gatewayIntents = null, RequestOptions options = null) + public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, int totalShards = 1, bool guildSubscriptions = true, GatewayIntents? gatewayIntents = null, (UserStatus, bool, long?, GameModel[])? presence = null, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); var props = new Dictionary @@ -238,6 +239,17 @@ namespace Discord.API else msg.GuildSubscriptions = guildSubscriptions; + if (presence.HasValue) + { + msg.Presence = new StatusUpdateParams + { + Status = presence.Value.Item1, + IsAFK = presence.Value.Item2, + IdleSince = presence.Value.Item3, + Activities = presence.Value.Item4 + }; + } + await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false); } public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) @@ -256,7 +268,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); } - public async Task SendStatusUpdateAsync(UserStatus status, bool isAFK, long? since, Game game, RequestOptions options = null) + public async Task SendStatusUpdateAsync(UserStatus status, bool isAFK, long? since, GameModel[] game, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); var args = new StatusUpdateParams @@ -264,7 +276,7 @@ namespace Discord.API Status = status, IdleSince = since, IsAFK = isAFK, - Game = game + Activities = game }; options.BucketId = GatewayBucket.Get(GatewayBucketType.PresenceUpdate).Id; await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index d53387afc..26a0d73bd 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -59,7 +59,8 @@ namespace Discord.WebSocket /// public override UserStatus Status { get; protected set; } = UserStatus.Online; /// - public override IActivity Activity { get; protected set; } + public override IActivity Activity { get => _activity.GetValueOrDefault(); protected set => _activity = Optional.Create(value); } + private Optional _activity; //From DiscordSocketConfig internal int TotalShards { get; private set; } @@ -248,14 +249,11 @@ namespace Discord.WebSocket else { await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); - await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents).ConfigureAwait(false); + await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); } //Wait for READY await _connection.WaitAsync().ConfigureAwait(false); - - await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); - await SendStatusAsync().ConfigureAwait(false); } finally { @@ -449,28 +447,44 @@ namespace Discord.WebSocket { if (CurrentUser == null) return; + CurrentUser.Presence = new SocketPresence(Status, Activity, null, null); + + var presence = BuildCurrentStatus(); + + await ApiClient.SendStatusUpdateAsync( + presence.Item1, + presence.Item2, + presence.Item3, + presence.Item4).ConfigureAwait(false); + } + + private (UserStatus, bool, long?, GameModel[]) BuildCurrentStatus() + { var status = Status; var statusSince = _statusSince; - CurrentUser.Presence = new SocketPresence(status, Activity, null, null); + var activity = _activity; - var gameModel = new GameModel(); + GameModel[] gameModels = null; // Discord only accepts rich presence over RPC, don't even bother building a payload - if (Activity is RichGame) - throw new NotSupportedException("Outgoing Rich Presences are not supported via WebSocket."); - if (Activity != null) + if (activity.GetValueOrDefault() != null) { + var gameModel = new GameModel(); + if (activity.Value is RichGame) + throw new NotSupportedException("Outgoing Rich Presences are not supported via WebSocket."); gameModel.Name = Activity.Name; gameModel.Type = Activity.Type; if (Activity is StreamingGame streamGame) gameModel.StreamUrl = streamGame.Url; + gameModels = new[] { gameModel }; } + else if (activity.IsSpecified) + gameModels = new GameModel[0]; - await ApiClient.SendStatusUpdateAsync( - status, - status == UserStatus.AFK, - statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, - gameModel).ConfigureAwait(false); + return (status, + status == UserStatus.AFK, + statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, + gameModels); } private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string type, object payload) @@ -523,7 +537,7 @@ namespace Discord.WebSocket await _shardedClient.AcquireIdentifyLockAsync(ShardId, _connection.CancelToken).ConfigureAwait(false); try { - await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents).ConfigureAwait(false); + await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); } finally { @@ -551,6 +565,7 @@ namespace Discord.WebSocket var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); var currentUser = SocketSelfUser.Create(this, state, data.User); + currentUser.Presence = new SocketPresence(Status, Activity, null, null); ApiClient.CurrentUserId = currentUser.Id; int unavailableGuilds = 0; for (int i = 0; i < data.Guilds.Length; i++)