diff --git a/src/Discord.Net.Core/Entities/Users/ClientType.cs b/src/Discord.Net.Core/Entities/Users/ClientType.cs
new file mode 100644
index 000000000..d4afe39f3
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Users/ClientType.cs
@@ -0,0 +1,21 @@
+namespace Discord
+{
+ ///
+ /// Defines the types of clients a user can be active on.
+ ///
+ public enum ClientType
+ {
+ ///
+ /// The user is active using the mobile application.
+ ///
+ Mobile,
+ ///
+ /// The user is active using the desktop application.
+ ///
+ Desktop,
+ ///
+ /// The user is active using the web application.
+ ///
+ Web
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Users/IPresence.cs b/src/Discord.Net.Core/Entities/Users/IPresence.cs
index b300115f8..620eb907c 100644
--- a/src/Discord.Net.Core/Entities/Users/IPresence.cs
+++ b/src/Discord.Net.Core/Entities/Users/IPresence.cs
@@ -1,3 +1,5 @@
+using System.Collections.Immutable;
+
namespace Discord
{
///
@@ -13,5 +15,9 @@ namespace Discord
/// Gets the current status of this user.
///
UserStatus Status { get; }
+ ///
+ /// Gets the set of clients where this user is currently active.
+ ///
+ IImmutableSet ActiveClients { get; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/Presence.cs b/src/Discord.Net.Rest/API/Common/Presence.cs
index 2902b7ce3..22526e8ac 100644
--- a/src/Discord.Net.Rest/API/Common/Presence.cs
+++ b/src/Discord.Net.Rest/API/Common/Presence.cs
@@ -1,5 +1,6 @@
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using Newtonsoft.Json;
+using System.Collections.Generic;
namespace Discord.API
{
@@ -18,5 +19,12 @@ namespace Discord.API
public Optional Roles { get; set; }
[JsonProperty("nick")]
public Optional Nick { get; set; }
+ // This property is a Dictionary where each key is the ClientType
+ // and the values are the current client status.
+ // The client status values are all the same.
+ // Example:
+ // "client_status": { "desktop": "dnd", "mobile": "dnd" }
+ [JsonProperty("client_status")]
+ public Optional> ClientStatus { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
index 6af5b5c95..37385fb7e 100644
--- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs
+++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading.Tasks;
using Model = Discord.API.User;
@@ -31,6 +32,8 @@ namespace Discord.Rest
///
public virtual UserStatus Status => UserStatus.Offline;
///
+ public virtual IImmutableSet ActiveClients => ImmutableHashSet.Empty;
+ ///
public virtual bool IsWebhook => false;
internal RestUser(BaseDiscordClient discord, ulong id)
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index 48ce34d83..70a135c01 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -325,7 +325,7 @@ namespace Discord.WebSocket
{
var user = SocketGlobalUser.Create(this, state, model);
user.GlobalUser.AddRef();
- user.Presence = new SocketPresence(UserStatus.Online, null);
+ user.Presence = new SocketPresence(UserStatus.Online, null, null);
return user;
});
}
@@ -433,7 +433,7 @@ namespace Discord.WebSocket
return;
var status = Status;
var statusSince = _statusSince;
- CurrentUser.Presence = new SocketPresence(status, Activity);
+ CurrentUser.Presence = new SocketPresence(status, Activity, null);
var gameModel = new GameModel();
// Discord only accepts rich presence over RPC, don't even bother building a payload
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
index 4112fd273..52f111303 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using Model = Discord.API.Presence;
@@ -13,15 +16,42 @@ namespace Discord.WebSocket
public UserStatus Status { get; }
///
public IActivity Activity { get; }
-
- internal SocketPresence(UserStatus status, IActivity activity)
+ ///
+ public IImmutableSet ActiveClients { get; }
+ internal SocketPresence(UserStatus status, IActivity activity, IImmutableSet activeClients)
{
Status = status;
Activity= activity;
+ ActiveClients = activeClients;
}
internal static SocketPresence Create(Model model)
{
- return new SocketPresence(model.Status, model.Game?.ToEntity());
+ var clients = ConvertClientTypesDict(model.ClientStatus.GetValueOrDefault());
+ return new SocketPresence(model.Status, model.Game?.ToEntity(), clients);
+ }
+ ///
+ /// Creates a new containing all of the client types
+ /// where a user is active from the data supplied in the Presence update frame.
+ ///
+ ///
+ /// A dictionary keyed by the
+ /// and where the value is the .
+ ///
+ ///
+ /// A collection of all s that this user is active.
+ ///
+ private static IImmutableSet ConvertClientTypesDict(IDictionary clientTypesDict)
+ {
+ if (clientTypesDict == null || clientTypesDict.Count == 0)
+ return ImmutableHashSet.Empty;
+ var set = new HashSet();
+ foreach (var key in clientTypesDict.Keys)
+ {
+ if (Enum.TryParse(key, true, out ClientType type))
+ set.Add(type);
+ // quietly discard ClientTypes that do not match
+ }
+ return set.ToImmutableHashSet();
}
///
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs
index e6ce0137e..840a1c30b 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUnknownUser.cs
@@ -24,8 +24,8 @@ namespace Discord.WebSocket
///
public override bool IsWebhook => false;
-
- internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null); } set { } }
+ ///
+ internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null); } set { } }
///
/// This field is not supported for an unknown user.
internal override SocketGlobalUser GlobalUser =>
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
index 4832e7311..eceb071eb 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
@@ -38,6 +38,8 @@ namespace Discord.WebSocket
public IActivity Activity => Presence.Activity;
///
public UserStatus Status => Presence.Status;
+ ///
+ public IImmutableSet ActiveClients => Presence.ActiveClients;
///
/// Gets mutual guilds shared with this user.
///
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs
index 2d701ef64..8ff4ee48c 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs
@@ -29,8 +29,8 @@ namespace Discord.WebSocket
///
public override bool IsWebhook => true;
-
- internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null); } set { } }
+ ///
+ internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null, null); } set { } }
internal override SocketGlobalUser GlobalUser =>
throw new NotSupportedException();