diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index e1e21fecb..24844af8f 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -175,6 +175,9 @@ Format.cs + + Helpers\EpochTime.cs + Helpers\Extensions.cs diff --git a/src/Discord.Net/DiscordClient.API.cs b/src/Discord.Net/DiscordClient.API.cs index f69307126..8957088e6 100644 --- a/src/Discord.Net/DiscordClient.API.cs +++ b/src/Discord.Net/DiscordClient.API.cs @@ -1,4 +1,5 @@ using Discord.API; +using Discord.Helpers; using System; using System.Collections.Generic; using System.Linq; @@ -638,6 +639,34 @@ namespace Discord return _api.EditProfile(currentPassword: currentPassword, username: username, email: email ?? _currentUser?.Email, password: password, avatarType: avatarType, avatar: avatar); } + public Task SetStatus(string status = null, int? gameId = null) + { + if (status == null && gameId == null) + throw new ArgumentNullException("Either status or gameId must be non-null"); + + if (status != null) + { + switch (status) + { + case UserStatus.Online: + case UserStatus.Away: + _status = status; + break; + default: + throw new ArgumentException($"Invalid status, must be {UserStatus.Online} or {UserStatus.Away}"); + } + } + + if (gameId != null) + _gameId = gameId; + + return SendStatus(); + } + private Task SendStatus() + { + _dataSocket.SendStatus(_status == UserStatus.Away ? EpochTime.GetMilliseconds() : (ulong?)null, _gameId); + return TaskHelper.CompletedTask; + } //Roles /// Note: due to current API limitations, the created role cannot be returned. diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 3166803d1..734b1a59c 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -24,6 +24,8 @@ namespace Discord private readonly ConcurrentDictionary _voiceClients; private bool _sentInitialLog; private uint _nextVoiceClientId; + private string _status; + private int? _gameId; public new DiscordClientConfig Config => _config as DiscordClientConfig; @@ -69,8 +71,13 @@ namespace Discord _roles = new Roles(this, cacheLock); _servers = new Servers(this, cacheLock); _users = new Users(this, cacheLock); + _status = UserStatus.Online; - this.Connected += (s, e) => _api.CancelToken = CancelToken; + this.Connected += async (s, e) => + { + _api.CancelToken = CancelToken; + await SendStatus(); + }; VoiceDisconnected += (s, e) => { diff --git a/src/Discord.Net/Helpers/EpochTime.cs b/src/Discord.Net/Helpers/EpochTime.cs new file mode 100644 index 000000000..8ada91c67 --- /dev/null +++ b/src/Discord.Net/Helpers/EpochTime.cs @@ -0,0 +1,10 @@ +using System; + +namespace Discord.Helpers +{ + public class EpochTime + { + private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public static ulong GetMilliseconds() => (ulong)(DateTime.UtcNow - epoch).TotalMilliseconds; + } +} diff --git a/src/Discord.Net/WebSockets/Data/Commands.cs b/src/Discord.Net/WebSockets/Data/Commands.cs index e0d3d86c3..a8ba16bed 100644 --- a/src/Discord.Net/WebSockets/Data/Commands.cs +++ b/src/Discord.Net/WebSockets/Data/Commands.cs @@ -2,17 +2,15 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.Helpers; using Newtonsoft.Json; -using System; using System.Collections.Generic; namespace Discord.WebSockets.Data { internal sealed class KeepAliveCommand : WebSocketMessage { - public KeepAliveCommand() : base(1, GetTimestamp()) { } - private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - private static ulong GetTimestamp() => (ulong)(DateTime.UtcNow - epoch).TotalMilliseconds; + public KeepAliveCommand() : base(1, EpochTime.GetMilliseconds()) { } } internal sealed class LoginCommand : WebSocketMessage { @@ -33,9 +31,9 @@ namespace Discord.WebSockets.Data public class Data { [JsonProperty("idle_since")] - public string IdleSince; + public ulong? IdleSince; [JsonProperty("game_id")] - public string GameId; + public int? GameId; } } internal sealed class JoinVoiceCommand : WebSocketMessage diff --git a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs index f4d8af7dd..e989acea0 100644 --- a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs +++ b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs @@ -80,13 +80,11 @@ namespace Discord.WebSockets.Data var payload = token.ToObject(); _sessionId = payload.SessionId; _heartbeatInterval = payload.HeartbeatInterval; - QueueMessage(new UpdateStatusCommand()); } else if (msg.Type == "RESUMED") { var payload = token.ToObject(); _heartbeatInterval = payload.HeartbeatInterval; - QueueMessage(new UpdateStatusCommand()); } RaiseReceivedEvent(msg.Type, token); if (msg.Type == "READY" || msg.Type == "RESUMED") @@ -114,6 +112,14 @@ namespace Discord.WebSockets.Data return new KeepAliveCommand(); } + public void SendStatus(ulong? idleSince, int? gameId) + { + var updateStatus = new UpdateStatusCommand(); + updateStatus.Payload.IdleSince = idleSince; + updateStatus.Payload.GameId = gameId; + QueueMessage(updateStatus); + } + public void SendJoinVoice(string serverId, string channelId) { var joinVoice = new JoinVoiceCommand();