Browse Source

feature: Send presence on Identify payload (#1688)

* Send presence on identify

* Change CurrentUser presence
tags/2.3.0
Paulo GitHub 4 years ago
parent
commit
25d5d36772
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 22 deletions
  1. +2
    -0
      src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
  2. +3
    -3
      src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs
  3. +15
    -3
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  4. +31
    -16
      src/Discord.Net.WebSocket/DiscordSocketClient.cs

+ 2
- 0
src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs View File

@@ -15,6 +15,8 @@ namespace Discord.API.Gateway
public int LargeThreshold { get; set; } public int LargeThreshold { get; set; }
[JsonProperty("shard")] [JsonProperty("shard")]
public Optional<int[]> ShardingParams { get; set; } public Optional<int[]> ShardingParams { get; set; }
[JsonProperty("presence")]
public Optional<StatusUpdateParams> Presence { get; set; }
[JsonProperty("guild_subscriptions")] [JsonProperty("guild_subscriptions")]
public Optional<bool> GuildSubscriptions { get; set; } public Optional<bool> GuildSubscriptions { get; set; }
[JsonProperty("intents")] [JsonProperty("intents")]


+ 3
- 3
src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API.Gateway namespace Discord.API.Gateway
@@ -12,7 +12,7 @@ namespace Discord.API.Gateway
public long? IdleSince { get; set; } public long? IdleSince { get; set; }
[JsonProperty("afk")] [JsonProperty("afk")]
public bool IsAFK { get; set; } public bool IsAFK { get; set; }
[JsonProperty("game")]
public Game Game { get; set; }
[JsonProperty("activities")]
public Game[] Activities { get; set; }
} }
} }

+ 15
- 3
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -12,6 +12,7 @@ using System.IO.Compression;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GameModel = Discord.API.Game;


namespace Discord.API namespace Discord.API
{ {
@@ -215,7 +216,7 @@ namespace Discord.API
await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); 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); options = RequestOptions.CreateOrClone(options);
var props = new Dictionary<string, string> var props = new Dictionary<string, string>
@@ -238,6 +239,17 @@ namespace Discord.API
else else
msg.GuildSubscriptions = guildSubscriptions; 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); await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false);
} }
public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null) public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null)
@@ -256,7 +268,7 @@ namespace Discord.API
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); 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); options = RequestOptions.CreateOrClone(options);
var args = new StatusUpdateParams var args = new StatusUpdateParams
@@ -264,7 +276,7 @@ namespace Discord.API
Status = status, Status = status,
IdleSince = since, IdleSince = since,
IsAFK = isAFK, IsAFK = isAFK,
Game = game
Activities = game
}; };
options.BucketId = GatewayBucket.Get(GatewayBucketType.PresenceUpdate).Id; options.BucketId = GatewayBucket.Get(GatewayBucketType.PresenceUpdate).Id;
await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false); await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false);


+ 31
- 16
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -59,7 +59,8 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
public override UserStatus Status { get; protected set; } = UserStatus.Online; public override UserStatus Status { get; protected set; } = UserStatus.Online;
/// <inheritdoc /> /// <inheritdoc />
public override IActivity Activity { get; protected set; }
public override IActivity Activity { get => _activity.GetValueOrDefault(); protected set => _activity = Optional.Create(value); }
private Optional<IActivity> _activity;


//From DiscordSocketConfig //From DiscordSocketConfig
internal int TotalShards { get; private set; } internal int TotalShards { get; private set; }
@@ -248,14 +249,11 @@ namespace Discord.WebSocket
else else
{ {
await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); 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 //Wait for READY
await _connection.WaitAsync().ConfigureAwait(false); await _connection.WaitAsync().ConfigureAwait(false);

await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false);
await SendStatusAsync().ConfigureAwait(false);
} }
finally finally
{ {
@@ -449,28 +447,44 @@ namespace Discord.WebSocket
{ {
if (CurrentUser == null) if (CurrentUser == null)
return; 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 status = Status;
var statusSince = _statusSince; 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 // 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.Name = Activity.Name;
gameModel.Type = Activity.Type; gameModel.Type = Activity.Type;
if (Activity is StreamingGame streamGame) if (Activity is StreamingGame streamGame)
gameModel.StreamUrl = streamGame.Url; 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) 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); await _shardedClient.AcquireIdentifyLockAsync(ShardId, _connection.CancelToken).ConfigureAwait(false);
try 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 finally
{ {
@@ -551,6 +565,7 @@ namespace Discord.WebSocket
var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);


var currentUser = SocketSelfUser.Create(this, state, data.User); var currentUser = SocketSelfUser.Create(this, state, data.User);
currentUser.Presence = new SocketPresence(Status, Activity, null, null);
ApiClient.CurrentUserId = currentUser.Id; ApiClient.CurrentUserId = currentUser.Id;
int unavailableGuilds = 0; int unavailableGuilds = 0;
for (int i = 0; i < data.Guilds.Length; i++) for (int i = 0; i < data.Guilds.Length; i++)


Loading…
Cancel
Save