Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Send presence on Identify payload #1688

Merged
merged 2 commits into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal class IdentifyParams
public int LargeThreshold { get; set; }
[JsonProperty("shard")]
public Optional<int[]> ShardingParams { get; set; }
[JsonProperty("presence")]
public Optional<StatusUpdateParams> Presence { get; set; }
[JsonProperty("guild_subscriptions")]
public Optional<bool> GuildSubscriptions { get; set; }
[JsonProperty("intents")]
Expand Down
6 changes: 3 additions & 3 deletions src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json;

namespace Discord.API.Gateway
Expand All @@ -12,7 +12,7 @@ internal class StatusUpdateParams
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; }
}
}
18 changes: 15 additions & 3 deletions src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GameModel = Discord.API.Game;

namespace Discord.API
{
Expand Down Expand Up @@ -215,7 +216,7 @@ private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload
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<string, string>
Expand All @@ -238,6 +239,17 @@ public async Task SendIdentifyAsync(int largeThreshold = 100, int shardID = 0, i
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)
Expand All @@ -256,15 +268,15 @@ public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null)
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
{
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);
Expand Down
47 changes: 31 additions & 16 deletions src/Discord.Net.WebSocket/DiscordSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public partial class DiscordSocketClient : BaseSocketClient, IDiscordClient
/// <inheritdoc />
public override UserStatus Status { get; protected set; } = UserStatus.Online;
/// <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
internal int TotalShards { get; private set; }
Expand Down Expand Up @@ -248,14 +249,11 @@ private async Task OnConnectingAsync()
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
{
Expand Down Expand Up @@ -449,28 +447,44 @@ private async Task SendStatusAsync()
{
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)
Expand Down Expand Up @@ -523,7 +537,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty
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
{
Expand Down Expand Up @@ -551,6 +565,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty
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++)
Expand Down