diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs
index d3ade3722..415c0c30d 100644
--- a/src/Discord.Net.Core/CDN.cs
+++ b/src/Discord.Net.Core/CDN.cs
@@ -22,6 +22,12 @@ namespace Discord
public static string GetEmojiUrl(ulong emojiId)
=> $"{DiscordConfig.CDNUrl}emojis/{emojiId}.png";
+ public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format)
+ {
+ string extension = FormatToExtension(format, "");
+ return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}";
+ }
+
private static string FormatToExtension(ImageFormat format, string imageId)
{
if (format == ImageFormat.Auto)
diff --git a/src/Discord.Net.Core/Entities/Activities/Game.cs b/src/Discord.Net.Core/Entities/Activities/Game.cs
new file mode 100644
index 000000000..f2b7e8eb6
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/Game.cs
@@ -0,0 +1,19 @@
+using System.Diagnostics;
+
+namespace Discord
+{
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class Game : IActivity
+ {
+ public string Name { get; internal set; }
+
+ internal Game() { }
+ public Game(string name)
+ {
+ Name = name;
+ }
+
+ public override string ToString() => Name;
+ private string DebuggerDisplay => Name;
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Activities/GameAsset.cs b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs
new file mode 100644
index 000000000..385f37214
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/GameAsset.cs
@@ -0,0 +1,15 @@
+namespace Discord
+{
+ public class GameAsset
+ {
+ internal GameAsset() { }
+
+ internal ulong ApplicationId { get; set; }
+
+ public string Text { get; internal set; }
+ public string ImageId { get; internal set; }
+
+ public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
+ => CDN.GetRichAssetUrl(ApplicationId, ImageId, size, format);
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Activities/GameParty.cs b/src/Discord.Net.Core/Entities/Activities/GameParty.cs
new file mode 100644
index 000000000..dbfe5b6ce
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/GameParty.cs
@@ -0,0 +1,11 @@
+namespace Discord
+{
+ public class GameParty
+ {
+ internal GameParty() { }
+
+ public string Id { get; internal set; }
+ public int Members { get; internal set; }
+ public int Capacity { get; internal set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs b/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs
new file mode 100644
index 000000000..e9d988ba9
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs
@@ -0,0 +1,16 @@
+namespace Discord
+{
+ public class GameSecrets
+ {
+ public string Match { get; }
+ public string Join { get; }
+ public string Spectate { get; }
+
+ internal GameSecrets(string match, string join, string spectate)
+ {
+ Match = match;
+ Join = join;
+ Spectate = spectate;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs b/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs
new file mode 100644
index 000000000..8c8c992fa
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Discord
+{
+ public class GameTimestamps
+ {
+ public DateTimeOffset? Start { get; }
+ public DateTimeOffset? End { get; }
+
+ internal GameTimestamps(DateTimeOffset? start, DateTimeOffset? end)
+ {
+ Start = start;
+ End = end;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Activities/IActivity.cs b/src/Discord.Net.Core/Entities/Activities/IActivity.cs
new file mode 100644
index 000000000..0dcf34273
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/IActivity.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ public interface IActivity
+ {
+ string Name { get; }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Activities/RichGame.cs b/src/Discord.Net.Core/Entities/Activities/RichGame.cs
new file mode 100644
index 000000000..e66eac1d2
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/RichGame.cs
@@ -0,0 +1,22 @@
+using System.Diagnostics;
+
+namespace Discord
+{
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class RichGame : Game
+ {
+ internal RichGame() { }
+
+ public string Details { get; internal set;}
+ public string State { get; internal set;}
+ public ulong ApplicationId { get; internal set; }
+ public GameAsset SmallAsset { get; internal set; }
+ public GameAsset LargeAsset { get; internal set; }
+ public GameParty Party { get; internal set; }
+ public GameSecrets Secrets { get; internal set; }
+ public GameTimestamps Timestamps { get; internal set; }
+
+ public override string ToString() => Name;
+ private string DebuggerDisplay => $"{Name} (Rich)";
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Activities/StreamingGame.cs b/src/Discord.Net.Core/Entities/Activities/StreamingGame.cs
new file mode 100644
index 000000000..140024272
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Activities/StreamingGame.cs
@@ -0,0 +1,21 @@
+using System.Diagnostics;
+
+namespace Discord
+{
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class StreamingGame : Game
+ {
+ public string Url { get; internal set; }
+ public StreamType StreamType { get; internal set; }
+
+ public StreamingGame(string name, string url, StreamType streamType)
+ {
+ Name = name;
+ Url = url;
+ StreamType = streamType;
+ }
+
+ public override string ToString() => Name;
+ private string DebuggerDisplay => $"{Name} ({Url})";
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Core/Entities/Users/Game.cs b/src/Discord.Net.Core/Entities/Users/Game.cs
deleted file mode 100644
index 3405b0dd4..000000000
--- a/src/Discord.Net.Core/Entities/Users/Game.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Diagnostics;
-
-namespace Discord
-{
- [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
- public struct Game
- {
- public string Name { get; }
- public string StreamUrl { get; }
- public StreamType StreamType { get; }
-
- public Game(string name, string streamUrl, StreamType type)
- {
- Name = name;
- StreamUrl = streamUrl;
- StreamType = type;
- }
- private Game(string name)
- : this(name, null, StreamType.NotStreaming) { }
-
- public override string ToString() => Name;
- private string DebuggerDisplay => StreamUrl != null ? $"{Name} ({StreamUrl})" : Name;
- }
-}
diff --git a/src/Discord.Net.Core/Entities/Users/IPresence.cs b/src/Discord.Net.Core/Entities/Users/IPresence.cs
index 7f182241b..25adcc9c4 100644
--- a/src/Discord.Net.Core/Entities/Users/IPresence.cs
+++ b/src/Discord.Net.Core/Entities/Users/IPresence.cs
@@ -2,8 +2,8 @@
{
public interface IPresence
{
- /// Gets the game this user is currently playing, if any.
- Game? Game { get; }
+ /// Gets the activity this user is currently doing.
+ IActivity Activity { get; }
/// Gets the current status of this user.
UserStatus Status { get; }
}
diff --git a/src/Discord.Net.Rest/API/Common/Game.cs b/src/Discord.Net.Rest/API/Common/Game.cs
index a499d83b0..bfb861692 100644
--- a/src/Discord.Net.Rest/API/Common/Game.cs
+++ b/src/Discord.Net.Rest/API/Common/Game.cs
@@ -13,6 +13,22 @@ namespace Discord.API
public Optional StreamUrl { get; set; }
[JsonProperty("type")]
public Optional StreamType { get; set; }
+ [JsonProperty("details")]
+ public Optional Details { get; set; }
+ [JsonProperty("state")]
+ public Optional State { get; set; }
+ [JsonProperty("application_id")]
+ public Optional ApplicationId { get; set; }
+ [JsonProperty("assets")]
+ public Optional Assets { get; set; }
+ [JsonProperty("party")]
+ public Optional Party { get; set; }
+ [JsonProperty("secrets")]
+ public Optional Secrets { get; set; }
+ [JsonProperty("timestamps")]
+ public Optional Timestamps { get; set; }
+ [JsonProperty("instance")]
+ public Optional Instance { get; set; }
[OnError]
internal void OnError(StreamingContext context, ErrorContext errorContext)
diff --git a/src/Discord.Net.Rest/API/Common/GameAssets.cs b/src/Discord.Net.Rest/API/Common/GameAssets.cs
new file mode 100644
index 000000000..b5928a8ab
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/GameAssets.cs
@@ -0,0 +1,16 @@
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ internal class GameAssets
+ {
+ [JsonProperty("small_text")]
+ public Optional SmallText { get; set; }
+ [JsonProperty("small_image")]
+ public Optional SmallImage { get; set; }
+ [JsonProperty("large_image")]
+ public Optional LargeText { get; set; }
+ [JsonProperty("large_text")]
+ public Optional LargeImage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rest/API/Common/GameParty.cs b/src/Discord.Net.Rest/API/Common/GameParty.cs
new file mode 100644
index 000000000..e0da4a098
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/GameParty.cs
@@ -0,0 +1,12 @@
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ internal class GameParty
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+ [JsonProperty("size")]
+ public int[] Size { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rest/API/Common/GameSecrets.cs b/src/Discord.Net.Rest/API/Common/GameSecrets.cs
new file mode 100644
index 000000000..e70b48ff0
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/GameSecrets.cs
@@ -0,0 +1,14 @@
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ internal class GameSecrets
+ {
+ [JsonProperty("match")]
+ public string Match { get; set; }
+ [JsonProperty("join")]
+ public string Join { get; set; }
+ [JsonProperty("spectate")]
+ public string Spectate { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rest/API/Common/GameTimestamps.cs b/src/Discord.Net.Rest/API/Common/GameTimestamps.cs
new file mode 100644
index 000000000..5c6f10b86
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/GameTimestamps.cs
@@ -0,0 +1,15 @@
+using System;
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ internal class GameTimestamps
+ {
+ [JsonProperty("start")]
+ [UnixTimestamp]
+ public Optional Start { get; set; }
+ [JsonProperty("end")]
+ [UnixTimestamp]
+ public Optional End { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rest/API/UnixTimestampAttribute.cs b/src/Discord.Net.Rest/API/UnixTimestampAttribute.cs
new file mode 100644
index 000000000..3890ffc46
--- /dev/null
+++ b/src/Discord.Net.Rest/API/UnixTimestampAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Discord.API
+{
+ [AttributeUsage(AttributeTargets.Property)]
+ internal class UnixTimestampAttribute : Attribute { }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj
index 439b7bbb1..29f79e410 100644
--- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj
+++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj
@@ -10,7 +10,8 @@
-
+
+
diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
index d8ade3a6b..c6cf6103a 100644
--- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs
+++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs
@@ -16,7 +16,7 @@ namespace Discord.Rest
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
public string Discriminator => DiscriminatorValue.ToString("D4");
public string Mention => MentionUtils.MentionUser(Id);
- public virtual Game? Game => null;
+ public virtual IActivity Activity => null;
public virtual UserStatus Status => UserStatus.Offline;
public virtual bool IsWebhook => false;
diff --git a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
index b465fbed2..9213c5d75 100644
--- a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
+++ b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
@@ -66,6 +66,12 @@ namespace Discord.Net.Converters
if (type == typeof(ulong))
return UInt64Converter.Instance;
}
+ bool hasUnixStamp = propInfo.GetCustomAttribute() != null;
+ if (hasUnixStamp)
+ {
+ if (type == typeof(DateTimeOffset))
+ return UnixTimestampConverter.Instance;
+ }
//Enums
if (type == typeof(PermissionTarget))
diff --git a/src/Discord.Net.Rest/Net/Converters/UnixTimestampConverter.cs b/src/Discord.Net.Rest/Net/Converters/UnixTimestampConverter.cs
new file mode 100644
index 000000000..d4660dc44
--- /dev/null
+++ b/src/Discord.Net.Rest/Net/Converters/UnixTimestampConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using Newtonsoft.Json;
+
+namespace Discord.Net.Converters
+{
+ public class UnixTimestampConverter : JsonConverter
+ {
+ public static readonly UnixTimestampConverter Instance = new UnixTimestampConverter();
+
+ public override bool CanConvert(Type objectType) => true;
+ public override bool CanRead => true;
+ public override bool CanWrite => true;
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ // Discord doesn't validate if timestamps contain decimals or not
+ if (reader.Value is double d)
+ return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(d);
+ long offset = (long)reader.Value;
+ return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(offset);
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs
index c6b0b2fd8..f55c83b75 100644
--- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs
+++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs
@@ -18,7 +18,7 @@ namespace Discord.Rpc
public string Discriminator => DiscriminatorValue.ToString("D4");
public string Mention => MentionUtils.MentionUser(Id);
public virtual bool IsWebhook => false;
- public virtual Game? Game => null;
+ public virtual IActivity Activity => null;
public virtual UserStatus Status => UserStatus.Offline;
internal RpcUser(DiscordRpcClient discord, ulong id)
diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs
index d248285cd..2ab244aeb 100644
--- a/src/Discord.Net.WebSocket/BaseSocketClient.cs
+++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs
@@ -13,7 +13,7 @@ namespace Discord.WebSocket
/// Gets the estimated round-trip latency, in milliseconds, to the gateway server.
public abstract int Latency { get; protected set; }
public abstract UserStatus Status { get; protected set; }
- public abstract Game? Game { get; protected set; }
+ public abstract IActivity Activity { get; protected set; }
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
@@ -45,6 +45,7 @@ namespace Discord.WebSocket
public abstract Task StopAsync();
public abstract Task SetStatusAsync(UserStatus status);
public abstract Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming);
+ public abstract Task SetActivityAsync(IActivity activity);
public abstract Task DownloadUsersAsync(IEnumerable guilds);
///
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index 6c2a0f3b9..e827909d9 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -22,7 +22,7 @@ namespace Discord.WebSocket
/// Gets the estimated round-trip latency, in milliseconds, to the gateway server.
public override int Latency { get => GetLatency(); protected set { } }
public override UserStatus Status { get => _shards[0].Status; protected set { } }
- public override Game? Game { get => _shards[0].Game; protected set { } }
+ public override IActivity Activity { get => _shards[0].Activity; protected set { } }
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
public override IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount());
@@ -239,9 +239,18 @@ namespace Discord.WebSocket
await _shards[i].SetStatusAsync(status).ConfigureAwait(false);
}
public override async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming)
+ {
+ IActivity activity = null;
+ if (streamUrl != null)
+ activity = new StreamingGame(name, streamUrl, streamType);
+ else if (name != null)
+ activity = new Game(name);
+ await SetActivityAsync(activity).ConfigureAwait(false);
+ }
+ public override async Task SetActivityAsync(IActivity activity)
{
for (int i = 0; i < _shards.Length; i++)
- await _shards[i].SetGameAsync(name, streamUrl, streamType).ConfigureAwait(false);
+ await _shards[i].SetActivityAsync(activity).ConfigureAwait(false);
}
private void RegisterEvents(DiscordSocketClient client, bool isPrimary)
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index d152bbc03..35a22edc4 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -48,7 +48,7 @@ namespace Discord.WebSocket
///
public override int Latency { get; protected set; }
public override UserStatus Status { get; protected set; } = UserStatus.Online;
- public override Game? Game { get; protected set; }
+ public override IActivity Activity { get; protected set; }
//From DiscordSocketConfig
internal int TotalShards { get; private set; }
@@ -328,33 +328,39 @@ namespace Discord.WebSocket
}
public override async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming)
{
- if (name != null)
- Game = new Game(name, streamUrl, streamType);
+ if (streamUrl != null)
+ Activity = new StreamingGame(name, streamUrl, streamType);
+ else if (name != null)
+ Activity = new Game(name);
else
- Game = null;
+ Activity = null;
await SendStatusAsync().ConfigureAwait(false);
}
+ public override async Task SetActivityAsync(IActivity activity)
+ {
+ Activity = activity;
+ await SendStatusAsync().ConfigureAwait(false);
+ }
+
private async Task SendStatusAsync()
{
if (CurrentUser == null)
return;
- var game = Game;
+ var activity = Activity;
var status = Status;
var statusSince = _statusSince;
- CurrentUser.Presence = new SocketPresence(status, game);
+ CurrentUser.Presence = new SocketPresence(status, activity);
- GameModel gameModel;
- if (game != null)
+ var gameModel = new GameModel();
+ // Discord only accepts rich presence over RPC, don't even bother building a payload
+ if (activity is RichGame game) throw new NotSupportedException("Outgoing Rich Presences are not supported");
+ if (activity is StreamingGame stream)
{
- gameModel = new API.Game
- {
- Name = game.Value.Name,
- StreamType = game.Value.StreamType,
- StreamUrl = game.Value.StreamUrl
- };
+ gameModel.StreamUrl = stream.Url;
+ gameModel.StreamType = stream.StreamType;
}
- else
- gameModel = null;
+ else if (activity != null)
+ gameModel.Name = activity.Name;
await ApiClient.SendStatusUpdateAsync(
status,
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
index 00d4b4bbc..7d7ba16ce 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs
@@ -8,20 +8,20 @@ namespace Discord.WebSocket
public struct SocketPresence : IPresence
{
public UserStatus Status { get; }
- public Game? Game { get; }
+ public IActivity Activity { get; }
- internal SocketPresence(UserStatus status, Game? game)
+ internal SocketPresence(UserStatus status, IActivity activity)
{
Status = status;
- Game = game;
+ Activity= activity;
}
internal static SocketPresence Create(Model model)
{
- return new SocketPresence(model.Status, model.Game != null ? model.Game.ToEntity() : (Game?)null);
+ return new SocketPresence(model.Status, model.Game?.ToEntity());
}
public override string ToString() => Status.ToString();
- private string DebuggerDisplay => $"{Status}{(Game != null ? $", {Game.Value.Name} ({Game.Value.StreamType})" : "")}";
+ private string DebuggerDisplay => $"{Status}{(Activity != null ? $", {Activity.Name}": "")}";
internal SocketPresence Clone() => this;
}
diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
index a0c78b93f..58d5c62a1 100644
--- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
+++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs
@@ -18,7 +18,7 @@ namespace Discord.WebSocket
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
public string Discriminator => DiscriminatorValue.ToString("D4");
public string Mention => MentionUtils.MentionUser(Id);
- public Game? Game => Presence.Game;
+ public IActivity Activity => Presence.Activity;
public UserStatus Status => Presence.Status;
internal SocketUser(DiscordSocketClient discord, ulong id)
diff --git a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs
index 636ef68f4..4aff13753 100644
--- a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs
+++ b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs
@@ -2,11 +2,83 @@
{
internal static class EntityExtensions
{
- public static Game ToEntity(this API.Game model)
+ public static IActivity ToEntity(this API.Game model)
{
- return new Game(model.Name,
- model.StreamUrl.GetValueOrDefault(null),
- model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming);
+ // Rich Game
+ if (model.ApplicationId.IsSpecified)
+ {
+ ulong appId = model.ApplicationId.Value;
+ var assets = model.Assets.GetValueOrDefault()?.ToEntity(appId);
+ return new RichGame
+ {
+ ApplicationId = appId,
+ Name = model.Name,
+ Details = model.Details.GetValueOrDefault(),
+ State = model.State.GetValueOrDefault(),
+ SmallAsset = assets?[0],
+ LargeAsset = assets?[1],
+ Party = model.Party.GetValueOrDefault()?.ToEntity(),
+ Secrets = model.Secrets.GetValueOrDefault()?.ToEntity(),
+ Timestamps = model.Timestamps.GetValueOrDefault()?.ToEntity()
+ };
+ }
+ // Stream Game
+ if (model.StreamUrl.IsSpecified)
+ {
+ return new StreamingGame(
+ model.Name,
+ model.StreamUrl.Value,
+ model.StreamType.Value.GetValueOrDefault());
+ }
+ // Normal Game
+ return new Game(model.Name);
+ }
+
+ // (Small, Large)
+ public static GameAsset[] ToEntity(this API.GameAssets model, ulong appId)
+ {
+ return new GameAsset[]
+ {
+ model.SmallImage.IsSpecified ? new GameAsset
+ {
+ ApplicationId = appId,
+ ImageId = model.SmallImage.GetValueOrDefault(),
+ Text = model.SmallText.GetValueOrDefault()
+ } : null,
+ model.LargeImage.IsSpecified ? new GameAsset
+ {
+ ApplicationId = appId,
+ ImageId = model.LargeImage.GetValueOrDefault(),
+ Text = model.LargeText.GetValueOrDefault()
+ } : null,
+ };
+ }
+
+ public static GameParty ToEntity(this API.GameParty model)
+ {
+ // Discord will probably send bad data since they don't validate anything
+ int current = 0, cap = 0;
+ if (model.Size.Length == 2)
+ {
+ current = model.Size[0];
+ cap = model.Size[1];
+ }
+ return new GameParty
+ {
+ Id = model.Id,
+ Members = current,
+ Capacity = cap,
+ };
+ }
+
+ public static GameSecrets ToEntity(this API.GameSecrets model)
+ {
+ return new GameSecrets(model.Match, model.Join, model.Spectate);
+ }
+
+ public static GameTimestamps ToEntity(this API.GameTimestamps model)
+ {
+ return new GameTimestamps(model.Start.ToNullable(), model.End.ToNullable());
}
}
}