@@ -175,6 +175,9 @@ | |||||
<Compile Include="..\Discord.Net\Format.cs"> | <Compile Include="..\Discord.Net\Format.cs"> | ||||
<Link>Format.cs</Link> | <Link>Format.cs</Link> | ||||
</Compile> | </Compile> | ||||
<Compile Include="..\Discord.Net\Helpers\EpochTime.cs"> | |||||
<Link>Helpers\EpochTime.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Helpers\Extensions.cs"> | <Compile Include="..\Discord.Net\Helpers\Extensions.cs"> | ||||
<Link>Helpers\Extensions.cs</Link> | <Link>Helpers\Extensions.cs</Link> | ||||
</Compile> | </Compile> | ||||
@@ -1,4 +1,5 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Discord.Helpers; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
@@ -638,6 +639,34 @@ namespace Discord | |||||
return _api.EditProfile(currentPassword: currentPassword, username: username, email: email ?? _currentUser?.Email, password: password, | return _api.EditProfile(currentPassword: currentPassword, username: username, email: email ?? _currentUser?.Email, password: password, | ||||
avatarType: avatarType, avatar: avatar); | 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 | //Roles | ||||
/// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | ||||
@@ -24,6 +24,8 @@ namespace Discord | |||||
private readonly ConcurrentDictionary<string, DiscordSimpleClient> _voiceClients; | private readonly ConcurrentDictionary<string, DiscordSimpleClient> _voiceClients; | ||||
private bool _sentInitialLog; | private bool _sentInitialLog; | ||||
private uint _nextVoiceClientId; | private uint _nextVoiceClientId; | ||||
private string _status; | |||||
private int? _gameId; | |||||
public new DiscordClientConfig Config => _config as DiscordClientConfig; | public new DiscordClientConfig Config => _config as DiscordClientConfig; | ||||
@@ -69,8 +71,13 @@ namespace Discord | |||||
_roles = new Roles(this, cacheLock); | _roles = new Roles(this, cacheLock); | ||||
_servers = new Servers(this, cacheLock); | _servers = new Servers(this, cacheLock); | ||||
_users = new Users(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) => | VoiceDisconnected += (s, e) => | ||||
{ | { | ||||
@@ -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; | |||||
} | |||||
} |
@@ -2,17 +2,15 @@ | |||||
#pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
#pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
using Discord.Helpers; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
namespace Discord.WebSockets.Data | namespace Discord.WebSockets.Data | ||||
{ | { | ||||
internal sealed class KeepAliveCommand : WebSocketMessage<ulong> | internal sealed class KeepAliveCommand : WebSocketMessage<ulong> | ||||
{ | { | ||||
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<LoginCommand.Data> | internal sealed class LoginCommand : WebSocketMessage<LoginCommand.Data> | ||||
{ | { | ||||
@@ -33,9 +31,9 @@ namespace Discord.WebSockets.Data | |||||
public class Data | public class Data | ||||
{ | { | ||||
[JsonProperty("idle_since")] | [JsonProperty("idle_since")] | ||||
public string IdleSince; | |||||
public ulong? IdleSince; | |||||
[JsonProperty("game_id")] | [JsonProperty("game_id")] | ||||
public string GameId; | |||||
public int? GameId; | |||||
} | } | ||||
} | } | ||||
internal sealed class JoinVoiceCommand : WebSocketMessage<JoinVoiceCommand.Data> | internal sealed class JoinVoiceCommand : WebSocketMessage<JoinVoiceCommand.Data> | ||||
@@ -80,13 +80,11 @@ namespace Discord.WebSockets.Data | |||||
var payload = token.ToObject<ReadyEvent>(); | var payload = token.ToObject<ReadyEvent>(); | ||||
_sessionId = payload.SessionId; | _sessionId = payload.SessionId; | ||||
_heartbeatInterval = payload.HeartbeatInterval; | _heartbeatInterval = payload.HeartbeatInterval; | ||||
QueueMessage(new UpdateStatusCommand()); | |||||
} | } | ||||
else if (msg.Type == "RESUMED") | else if (msg.Type == "RESUMED") | ||||
{ | { | ||||
var payload = token.ToObject<ResumedEvent>(); | var payload = token.ToObject<ResumedEvent>(); | ||||
_heartbeatInterval = payload.HeartbeatInterval; | _heartbeatInterval = payload.HeartbeatInterval; | ||||
QueueMessage(new UpdateStatusCommand()); | |||||
} | } | ||||
RaiseReceivedEvent(msg.Type, token); | RaiseReceivedEvent(msg.Type, token); | ||||
if (msg.Type == "READY" || msg.Type == "RESUMED") | if (msg.Type == "READY" || msg.Type == "RESUMED") | ||||
@@ -114,6 +112,14 @@ namespace Discord.WebSockets.Data | |||||
return new KeepAliveCommand(); | 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) | public void SendJoinVoice(string serverId, string channelId) | ||||
{ | { | ||||
var joinVoice = new JoinVoiceCommand(); | var joinVoice = new JoinVoiceCommand(); | ||||