diff --git a/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs b/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs
index e919d40a0..3a6cbc9b2 100644
--- a/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs
+++ b/src/Discord.Net.Core/Entities/Activities/GameSecrets.cs
@@ -1,6 +1,6 @@
namespace Discord
{
- public struct GameSecrets
+ public class GameSecrets
{
public string Match { get; }
public string Join { get; }
diff --git a/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs b/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs
index cf2dbb281..106f8ea49 100644
--- a/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs
+++ b/src/Discord.Net.Core/Entities/Activities/GameTimestamps.cs
@@ -2,7 +2,7 @@
namespace Discord
{
- public struct GameTimestamps
+ public class GameTimestamps
{
public DateTimeOffset Start { get; }
public DateTimeOffset End { get; }
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/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.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/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index 6c2a0f3b9..af7f38f6f 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 IActivity Activity { get => _shards[0].Activity; protected set { } }
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
public override IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount());
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index e5564ba3f..55a943ca1 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; }
+ internal IActivity Activity { get; protected set; }
//From DiscordSocketConfig
internal int TotalShards { get; private set; }
@@ -328,77 +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 (name != null && 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 async Task SetGameAsync(Game game)
+ public async Task SetActivityAsync(IActivity activity)
{
- Game = game;
+ 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)
{
- var assets = game.Value.Assets.HasValue
- ? new API.GameAssets()
- {
- SmallText = game.Value.Assets.Value.SmallText,
- SmallImage = game.Value.Assets.Value.SmallImage,
- LargeText = game.Value.Assets.Value.LargeText,
- LargeImage = game.Value.Assets.Value.LargeImage,
- }
- : Optional.Create();
- var party = game.Value.Party.HasValue
- ? new API.GameParty
- {
- Id = game.Value.Party.Value.Id,
- Size = game.Value.Party.Value.Size
- }
- : Optional.Create();
- var secrets = game.Value.Secrets.HasValue
- ? new API.GameSecrets()
- {
- Join = game.Value.Secrets.Value.Join,
- Match = game.Value.Secrets.Value.Match,
- Spectate = game.Value.Secrets.Value.Spectate
- }
- : Optional.Create();
- var timestamps = game.Value.Timestamps.HasValue
- ? new API.GameTimestamps
- {
- Start = game.Value.Timestamps.Value.Start,
- End = game.Value.Timestamps.Value.End
- }
- : Optional.Create();
- gameModel = new API.Game
- {
- Name = game.Value.Name,
- StreamType = game.Value.StreamType,
- StreamUrl = game.Value.StreamUrl,
- Details = game.Value.Details,
- State = game.Value.State,
- ApplicationId = game.Value.ApplicationId ?? Optional.Create(),
- Assets = assets,
- Party = party,
- Secrets = secrets,
- Timestamps = timestamps,
- };
+ 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 be62fec5a..35efef269 100644
--- a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs
+++ b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs
@@ -2,32 +2,64 @@
{
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,
- model.Details.GetValueOrDefault(),
- model.State.GetValueOrDefault(),
- model.ApplicationId.ToNullable(),
- model.Assets.GetValueOrDefault(null)?.ToEntity(),
- model.Party.GetValueOrDefault(null)?.ToEntity(),
- model.Secrets.GetValueOrDefault(null)?.ToEntity(),
- model.Timestamps.GetValueOrDefault(null)?.ToEntity()
- );
+ // Rich Game
+ if (model.Details.IsSpecified)
+ {
+ var appId = model.ApplicationId.ToNullable();
+ return new RichGame
+ {
+ ApplicationId = appId,
+ Name = model.Name,
+ Details = model.Details.GetValueOrDefault(),
+ State = model.State.GetValueOrDefault(),
+
+ Assets = model.Assets.GetValueOrDefault()?.ToEntity(appId ?? 0),
+ 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);
}
- public static GameAssets ToEntity(this API.GameAssets model)
+ public static GameAssets ToEntity(this API.GameAssets model, ulong appId)
{
- return new GameAssets(model.SmallText.GetValueOrDefault(),
- model.SmallImage.GetValueOrDefault(),
- model.LargeText.GetValueOrDefault(),
- model.LargeImage.GetValueOrDefault());
+ return new GameAssets
+ {
+ Large = new GameAsset
+ {
+ ApplicationId = appId,
+ ImageId = model.LargeImage.GetValueOrDefault(),
+ Text = model.LargeText.GetValueOrDefault()
+ },
+ Small = new GameAsset
+ {
+ ApplicationId = appId,
+ ImageId = model.LargeImage.GetValueOrDefault(),
+ Text = model.LargeText.GetValueOrDefault()
+ },
+ };
}
public static GameParty ToEntity(this API.GameParty model)
{
- return new GameParty(model.Size, model.Id);
+ return new GameParty
+ {
+ Id = model.Id,
+ Size = model.Size
+ };
}
public static GameSecrets ToEntity(this API.GameSecrets model)