| @@ -1,4 +1,6 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| using System; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| public class ModifyPresenceParams | public class ModifyPresenceParams | ||||
| @@ -5,6 +5,7 @@ | |||||
| Unknown, | Unknown, | ||||
| Online, | Online, | ||||
| Idle, | Idle, | ||||
| AFK, | |||||
| DoNotDisturb, | DoNotDisturb, | ||||
| Invisible, | Invisible, | ||||
| Offline | Offline | ||||
| @@ -38,6 +38,7 @@ namespace Discord.Net.Converters | |||||
| writer.WriteValue("online"); | writer.WriteValue("online"); | ||||
| break; | break; | ||||
| case UserStatus.Idle: | case UserStatus.Idle: | ||||
| case UserStatus.AFK: | |||||
| writer.WriteValue("idle"); | writer.WriteValue("idle"); | ||||
| break; | break; | ||||
| case UserStatus.DoNotDisturb: | case UserStatus.DoNotDisturb: | ||||
| @@ -212,12 +212,14 @@ namespace Discord.API | |||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null) | |||||
| public async Task SendStatusUpdateAsync(UserStatus status, bool isAFK, long? since, Game game, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var args = new StatusUpdateParams | var args = new StatusUpdateParams | ||||
| { | { | ||||
| IdleSince = idleSince, | |||||
| Status = status, | |||||
| IdleSince = since, | |||||
| IsAFK = isAFK, | |||||
| Game = game | Game = game | ||||
| }; | }; | ||||
| await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false); | await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false); | ||||
| @@ -6,8 +6,12 @@ namespace Discord.API.Gateway | |||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | ||||
| public class StatusUpdateParams | public class StatusUpdateParams | ||||
| { | { | ||||
| [JsonProperty("idle_since"), Int53] | |||||
| [JsonProperty("status")] | |||||
| public UserStatus Status { get; set; } | |||||
| [JsonProperty("since"), Int53] | |||||
| public long? IdleSince { get; set; } | public long? IdleSince { get; set; } | ||||
| [JsonProperty("afk")] | |||||
| public bool IsAFK { get; set; } | |||||
| [JsonProperty("game")] | [JsonProperty("game")] | ||||
| public Game Game { get; set; } | public Game Game { get; set; } | ||||
| } | } | ||||
| @@ -376,7 +376,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| var user = SocketGlobalUser.Create(this, state, model); | var user = SocketGlobalUser.Create(this, state, model); | ||||
| user.GlobalUser.AddRef(); | user.GlobalUser.AddRef(); | ||||
| user.Presence = new SocketPresence(null, UserStatus.Online); | |||||
| user.Presence = new SocketPresence(UserStatus.Online, null); | |||||
| return user; | return user; | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -1311,7 +1311,7 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| before = new SocketPresence(null, UserStatus.Offline); | |||||
| before = new SocketPresence(UserStatus.Offline, null); | |||||
| user = guild.AddOrUpdateUser(data); | user = guild.AddOrUpdateUser(data); | ||||
| } | } | ||||
| @@ -3,6 +3,7 @@ using Discord.Rest; | |||||
| using System; | using System; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using GameEntity = Discord.Game; | |||||
| using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| @@ -10,6 +11,8 @@ namespace Discord.WebSocket | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class SocketSelfUser : SocketUser, ISelfUser | public class SocketSelfUser : SocketUser, ISelfUser | ||||
| { | { | ||||
| private DateTimeOffset? _statusSince; | |||||
| public string Email { get; private set; } | public string Email { get; private set; } | ||||
| public bool IsVerified { get; private set; } | public bool IsVerified { get; private set; } | ||||
| public bool IsMfaEnabled { get; private set; } | public bool IsMfaEnabled { get; private set; } | ||||
| @@ -44,12 +47,57 @@ namespace Discord.WebSocket | |||||
| IsMfaEnabled = model.MfaEnabled.Value; | IsMfaEnabled = model.MfaEnabled.Value; | ||||
| } | } | ||||
| public Task ModifyAsync(Action<ModifyCurrentUserParams> func) | |||||
| => UserHelper.ModifyAsync(this, Discord, func); | |||||
| public Task ModifyAsync(Action<ModifyCurrentUserParams> func, RequestOptions options = null) | |||||
| => UserHelper.ModifyAsync(this, Discord, func, options); | |||||
| public async Task ModifyStatusAsync(Action<ModifyPresenceParams> func, RequestOptions options = null) | |||||
| { | |||||
| var args = new ModifyPresenceParams(); | |||||
| func(args); | |||||
| internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser; | |||||
| UserStatus status; | |||||
| if (args.Status.IsSpecified) | |||||
| { | |||||
| status = args.Status.Value; | |||||
| if (status == UserStatus.AFK) | |||||
| _statusSince = DateTimeOffset.UtcNow; | |||||
| else | |||||
| _statusSince = null; | |||||
| } | |||||
| else | |||||
| status = Status; | |||||
| //ISelfUser | |||||
| Task ISelfUser.ModifyStatusAsync(Action<ModifyPresenceParams> func) { throw new NotSupportedException(); } | |||||
| GameEntity? game; | |||||
| if (args.Game.IsSpecified) | |||||
| { | |||||
| var model = args.Game.Value; | |||||
| if (model != null) | |||||
| game = GameEntity.Create(model); | |||||
| else | |||||
| game = null; | |||||
| } | |||||
| else | |||||
| game = Game; | |||||
| Presence = new SocketPresence(status, game); | |||||
| await SendStatus(status, game); | |||||
| } | |||||
| internal async Task SendStatus(UserStatus status, GameEntity? game) | |||||
| { | |||||
| var gameModel = game != null ? new API.Game | |||||
| { | |||||
| Name = game.Value.Name, | |||||
| StreamType = game.Value.StreamType, | |||||
| StreamUrl = game.Value.StreamUrl | |||||
| } : null; | |||||
| await Discord.ApiClient.SendStatusUpdateAsync( | |||||
| status, | |||||
| status == UserStatus.AFK, | |||||
| _statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, | |||||
| gameModel); | |||||
| } | |||||
| internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser; | |||||
| } | } | ||||
| } | } | ||||
| @@ -12,7 +12,7 @@ namespace Discord.WebSocket | |||||
| public override string Username { get; internal set; } | public override string Username { get; internal set; } | ||||
| public override ushort DiscriminatorValue { get; internal set; } | public override ushort DiscriminatorValue { get; internal set; } | ||||
| public override string AvatarId { get; internal set; } | public override string AvatarId { get; internal set; } | ||||
| internal override SocketPresence Presence { get { return new SocketPresence(null, UserStatus.Offline); } set { } } | |||||
| internal override SocketPresence Presence { get { return new SocketPresence(UserStatus.Offline, null); } set { } } | |||||
| internal override SocketGlobalUser GlobalUser { get { throw new NotSupportedException(); } } | internal override SocketGlobalUser GlobalUser { get { throw new NotSupportedException(); } } | ||||