From 5be13b91aee73c937cdc8194ef9d340f8f4b9c2e Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 18 Nov 2015 20:33:49 -0400 Subject: [PATCH] Switched ids to use int64 rather than strings --- .../Permissions/Userlist/BlacklistService.cs | 2 +- .../Permissions/Userlist/UserlistService.cs | 18 +-- .../Permissions/Userlist/WhitelistService.cs | 2 +- src/Discord.Net.Modules/ModuleManager.cs | 12 +- src/Discord.Net.Net45/Discord.Net.csproj | 12 ++ src/Discord.Net/API/Channels.cs | 19 ++- .../API/Converters/LongCollectionConverter.cs | 75 ++++++++++ .../API/Converters/LongStringConverter.cs | 37 +++++ .../API/Converters/ShortStringConverter.cs | 37 +++++ src/Discord.Net/API/Endpoints.cs | 44 +++--- src/Discord.Net/API/Maintenance.cs | 4 +- src/Discord.Net/API/Members.cs | 19 ++- src/Discord.Net/API/Messages.cs | 19 ++- src/Discord.Net/API/Permissions.cs | 4 +- src/Discord.Net/API/Presence.cs | 7 +- src/Discord.Net/API/Roles.cs | 21 ++- src/Discord.Net/API/Servers.cs | 16 +- src/Discord.Net/API/Users.cs | 6 +- src/Discord.Net/API/Voice.cs | 81 +++++----- src/Discord.Net/Audio/IDiscordVoiceClient.cs | 2 +- src/Discord.Net/DiscordAPIClient.cs | 138 +++++++++--------- src/Discord.Net/DiscordClient.Bans.cs | 12 +- src/Discord.Net/DiscordClient.Channels.cs | 22 +-- src/Discord.Net/DiscordClient.Invites.cs | 6 +- src/Discord.Net/DiscordClient.Messages.cs | 24 ++- src/Discord.Net/DiscordClient.Permissions.cs | 16 +- src/Discord.Net/DiscordClient.Roles.cs | 8 +- src/Discord.Net/DiscordClient.Servers.cs | 11 +- src/Discord.Net/DiscordClient.Users.cs | 73 +++++---- src/Discord.Net/DiscordClient.Voice.cs | 4 +- src/Discord.Net/DiscordClient.cs | 35 +++-- src/Discord.Net/DiscordWSClient.Events.cs | 12 +- src/Discord.Net/DiscordWSClient.Voice.cs | 8 +- src/Discord.Net/DiscordWSClient.cs | 18 +-- src/Discord.Net/Helpers/AsyncCollection.cs | 27 ++-- src/Discord.Net/Helpers/CachedObject.cs | 30 ++-- src/Discord.Net/Helpers/IdConvert.cs | 28 ++++ src/Discord.Net/Helpers/Mention.cs | 8 +- src/Discord.Net/Helpers/Reference.cs | 19 +-- src/Discord.Net/Models/Channel.cs | 23 +-- src/Discord.Net/Models/GlobalUser.cs | 16 +- src/Discord.Net/Models/Invite.cs | 23 ++- src/Discord.Net/Models/Message.cs | 7 +- src/Discord.Net/Models/Role.cs | 6 +- src/Discord.Net/Models/Server.cs | 32 ++-- src/Discord.Net/Models/User.cs | 43 ++++-- .../Net/WebSockets/DataWebSocket.cs | 6 +- .../Net/WebSockets/VoiceWebSocket.Events.cs | 8 +- .../Net/WebSockets/VoiceWebSocket.cs | 107 ++++++++------ 49 files changed, 748 insertions(+), 459 deletions(-) create mode 100644 src/Discord.Net/API/Converters/LongCollectionConverter.cs create mode 100644 src/Discord.Net/API/Converters/LongStringConverter.cs create mode 100644 src/Discord.Net/API/Converters/ShortStringConverter.cs create mode 100644 src/Discord.Net/Helpers/IdConvert.cs diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs index 8e91ef8b6..df1868862 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs @@ -4,7 +4,7 @@ namespace Discord.Commands.Permissions.Userlist { public class BlacklistService : UserlistService { - public BlacklistService(IEnumerable initialList = null) + public BlacklistService(IEnumerable initialList = null) : base(initialList) { } diff --git a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs index 3c6e8bf6e..edfbb5e7d 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs @@ -7,18 +7,18 @@ namespace Discord.Commands.Permissions.Userlist { public class UserlistService : IService { - protected readonly ConcurrentDictionary _userList; + protected readonly ConcurrentDictionary _userList; private DiscordClient _client; public DiscordClient Client => _client; - public IEnumerable UserIds => _userList.Select(x => x.Key); + public IEnumerable UserIds => _userList.Select(x => x.Key); - public UserlistService(IEnumerable initialList = null) + public UserlistService(IEnumerable initialList = null) { if (initialList != null) - _userList = new ConcurrentDictionary(initialList.Select(x => new KeyValuePair(x, true))); + _userList = new ConcurrentDictionary(initialList.Select(x => new KeyValuePair(x, true))); else - _userList = new ConcurrentDictionary(); + _userList = new ConcurrentDictionary(); } public void Add(User user) @@ -26,9 +26,9 @@ namespace Discord.Commands.Permissions.Userlist if (user == null) throw new ArgumentNullException(nameof(user)); _userList[user.Id] = true; } - public void Add(string userId) + public void Add(long userId) { - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); _userList[userId] = true; } public bool Remove(User user) @@ -37,9 +37,9 @@ namespace Discord.Commands.Permissions.Userlist bool ignored; return _userList.TryRemove(user.Id, out ignored); } - public bool Remove(string userId) + public bool Remove(long userId) { - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); bool ignored; return _userList.TryRemove(userId, out ignored); } diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs index 4719ee004..7b078a915 100644 --- a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs @@ -4,7 +4,7 @@ namespace Discord.Commands.Permissions.Userlist { public class WhitelistService : UserlistService { - public WhitelistService(IEnumerable initialList = null) + public WhitelistService(IEnumerable initialList = null) : base(initialList) { } diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs index e850fe1e4..316a38e18 100644 --- a/src/Discord.Net.Modules/ModuleManager.cs +++ b/src/Discord.Net.Modules/ModuleManager.cs @@ -47,9 +47,9 @@ namespace Discord.Modules private readonly string _name, _id; private readonly FilterType _filterType; private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate; - private readonly ConcurrentDictionary _enabledServers; - private readonly ConcurrentDictionary _enabledChannels; - private readonly ConcurrentDictionary _indirectServers; + private readonly ConcurrentDictionary _enabledServers; + private readonly ConcurrentDictionary _enabledChannels; + private readonly ConcurrentDictionary _indirectServers; public DiscordClient Client => _client; public string Name => _name; @@ -70,9 +70,9 @@ namespace Discord.Modules _useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist); _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); - _enabledServers = new ConcurrentDictionary(); - _enabledChannels = new ConcurrentDictionary(); - _indirectServers = new ConcurrentDictionary(); + _enabledServers = new ConcurrentDictionary(); + _enabledChannels = new ConcurrentDictionary(); + _indirectServers = new ConcurrentDictionary(); if (_allowAll || _useServerWhitelist) //Server-only events { diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index 9447f3985..34fb669bd 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -76,6 +76,15 @@ API\Channels.cs + + API\Converters\LongCollectionConverter.cs + + + API\Converters\LongStringConverter.cs + + + API\Converters\ShortStringConverter.cs + API\Endpoints.cs @@ -220,6 +229,9 @@ Helpers\Format.cs + + Helpers\IdConvert.cs + Helpers\Mention.cs diff --git a/src/Discord.Net/API/Channels.cs b/src/Discord.Net/API/Channels.cs index ec5c2c5d5..383ee2f83 100644 --- a/src/Discord.Net/API/Channels.cs +++ b/src/Discord.Net/API/Channels.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System.Collections; using System.Collections.Generic; @@ -12,9 +13,11 @@ namespace Discord.API public class ChannelReference { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long GuildId; [JsonProperty("name")] public string Name; [JsonProperty("type")] @@ -27,7 +30,8 @@ namespace Discord.API [JsonProperty("type")] public string Type; [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("deny")] public uint Deny; [JsonProperty("allow")] @@ -35,7 +39,8 @@ namespace Discord.API } [JsonProperty("last_message_id")] - public string LastMessageId; + [JsonConverter(typeof(NullableLongStringConverter))] + public long? LastMessageId; [JsonProperty("is_private")] public bool IsPrivate; [JsonProperty("position")] @@ -59,7 +64,8 @@ namespace Discord.API public class CreatePMChannelRequest { [JsonProperty("recipient_id")] - public string RecipientId; + [JsonConverter(typeof(LongStringConverter))] + public long RecipientId; } public class CreateChannelResponse : ChannelInfo { } @@ -82,7 +88,8 @@ namespace Discord.API public sealed class Channel { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("position")] public uint Position; } diff --git a/src/Discord.Net/API/Converters/LongCollectionConverter.cs b/src/Discord.Net/API/Converters/LongCollectionConverter.cs new file mode 100644 index 000000000..81b2f0df9 --- /dev/null +++ b/src/Discord.Net/API/Converters/LongCollectionConverter.cs @@ -0,0 +1,75 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace Discord.API.Converters +{ + internal class EnumerableLongStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(IEnumerable); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + List result = new List(); + if (reader.TokenType == JsonToken.StartArray) + { + reader.Read(); + while (reader.TokenType != JsonToken.EndArray) + { + result.Add(IdConvert.ToLong((string)reader.Value)); + reader.Read(); + } + } + return result; + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + writer.WriteNull(); + else + { + writer.WriteStartArray(); + foreach (var v in (IEnumerable)value) + writer.WriteValue(IdConvert.ToString(v)); + writer.WriteEndArray(); + } + } + } + + internal class LongArrayStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(IEnumerable); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + List result = new List(); + if (reader.TokenType == JsonToken.StartArray) + { + reader.Read(); + while (reader.TokenType != JsonToken.EndArray) + { + result.Add(IdConvert.ToLong((string)reader.Value)); + reader.Read(); + } + } + return result.ToArray(); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + writer.WriteNull(); + else + { + writer.WriteStartArray(); + var a = (long[])value; + for (int i = 0; i < a.Length; i++) + writer.WriteValue(IdConvert.ToString(a[i])); + writer.WriteEndArray(); + } + } + } +} diff --git a/src/Discord.Net/API/Converters/LongStringConverter.cs b/src/Discord.Net/API/Converters/LongStringConverter.cs new file mode 100644 index 000000000..9b320c210 --- /dev/null +++ b/src/Discord.Net/API/Converters/LongStringConverter.cs @@ -0,0 +1,37 @@ +using System; +using Newtonsoft.Json; + +namespace Discord.API.Converters +{ + internal class LongStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(long); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IdConvert.ToLong((string)reader.Value); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(IdConvert.ToString((long)value)); + } + } + + internal class NullableLongStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(long?); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IdConvert.ToNullableLong((string)reader.Value); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(IdConvert.ToString((long?)value)); + } + } +} diff --git a/src/Discord.Net/API/Converters/ShortStringConverter.cs b/src/Discord.Net/API/Converters/ShortStringConverter.cs new file mode 100644 index 000000000..1cfb1a7a2 --- /dev/null +++ b/src/Discord.Net/API/Converters/ShortStringConverter.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using System; + +namespace Discord.API.Converters +{ + /*internal class ShortStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(short); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IdConvert.ToShort((string)reader.Value); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(IdConvert.ToString((short)value)); + } + } + + internal class NullableShortStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(short?); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return IdConvert.ToNullableShort((string)reader.Value); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(IdConvert.ToString((short?)value)); + } + }*/ +} diff --git a/src/Discord.Net/API/Endpoints.cs b/src/Discord.Net/API/Endpoints.cs index eae381cbd..3f18f5626 100644 --- a/src/Discord.Net/API/Endpoints.cs +++ b/src/Discord.Net/API/Endpoints.cs @@ -12,33 +12,35 @@ public const string AuthLogout = "auth/logout"; public const string Channels = "channels"; - public static string Channel(string channelId) => $"channels/{channelId}"; - public static string ChannelInvites(string channelId) => $"channels/{channelId}/invites"; - public static string ChannelMessages(string channelId) => $"channels/{channelId}/messages"; - public static string ChannelMessages(string channelId, int limit) => $"channels/{channelId}/messages?limit={limit}"; - public static string ChannelMessages(string channelId, int limit, string relativeId, string relativeDir) => $"channels/{channelId}/messages?limit={limit}&{relativeDir}={relativeId}"; - public static string ChannelMessage(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}"; - public static string ChannelMessageAck(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}/ack"; - public static string ChannelPermission(string channelId, string userOrRoleId) => $"channels/{channelId}/permissions/{userOrRoleId}"; - public static string ChannelTyping(string channelId) => $"channels/{channelId}/typing"; + public static string Channel(long channelId) => $"channels/{channelId}"; + public static string ChannelInvites(long channelId) => $"channels/{channelId}/invites"; + public static string ChannelMessages(long channelId) => $"channels/{channelId}/messages"; + public static string ChannelMessages(long channelId, int limit) => $"channels/{channelId}/messages?limit={limit}"; + public static string ChannelMessages(long channelId, int limit, long relativeId, string relativeDir) => $"channels/{channelId}/messages?limit={limit}&{relativeDir}={relativeId}"; + public static string ChannelMessage(long channelId, long msgId) => $"channels/{channelId}/messages/{msgId}"; + public static string ChannelMessageAck(long channelId, long msgId) => $"channels/{channelId}/messages/{msgId}/ack"; + public static string ChannelPermission(long channelId, long userOrRoleId) => $"channels/{channelId}/permissions/{userOrRoleId}"; + public static string ChannelTyping(long channelId) => $"channels/{channelId}/typing"; public const string Servers = "guilds"; - public static string Server(string serverId) => $"guilds/{serverId}"; - public static string ServerBan(string serverId, string userId) => $"guilds/{serverId}/bans/{userId}"; - public static string ServerChannels(string serverId) => $"guilds/{serverId}/channels"; - public static string ServerInvites(string serverId) => $"guilds/{serverId}/invites"; - public static string ServerMember(string serverId, string userId) => $"guilds/{serverId}/members/{userId}"; - public static string ServerPrune(string serverId, int days) => $"guilds/{serverId}/prune?days={days}"; - public static string ServerRoles(string serverId) => $"guilds/{serverId}/roles"; - public static string ServerRole(string serverId, string roleId) => $"guilds/{serverId}/roles/{roleId}"; + public static string Server(long serverId) => $"guilds/{serverId}"; + public static string ServerBan(long serverId, long userId) => $"guilds/{serverId}/bans/{userId}"; + public static string ServerChannels(long serverId) => $"guilds/{serverId}/channels"; + public static string ServerInvites(long serverId) => $"guilds/{serverId}/invites"; + public static string ServerMember(long serverId, long userId) => $"guilds/{serverId}/members/{userId}"; + public static string ServerPrune(long serverId, int days) => $"guilds/{serverId}/prune?days={days}"; + public static string ServerRoles(long serverId) => $"guilds/{serverId}/roles"; + public static string ServerRole(long serverId, long roleId) => $"guilds/{serverId}/roles/{roleId}"; public const string Invites = "invite"; - public static string Invite(string inviteId) => $"invite/{inviteId}"; - public static string InviteUrl(string inviteId) => $"https://discord.gg/{inviteId}"; + public static string Invite(long inviteId) => $"invite/{inviteId}"; + public static string Invite(string inviteIdOrXkcd) => $"invite/{inviteIdOrXkcd}"; + public static string InviteUrl(long inviteId) => $"https://discord.gg/{inviteId}"; + public static string InviteUrl(string inviteIdOrXkcd) => $"https://discord.gg/{inviteIdOrXkcd}"; public const string Users = "users"; - public static string UserAvatar(string userId, string avatarId) => $"users/{userId}/avatars/{avatarId}.jpg"; - public static string UserChannels(string userId) => $"users/{userId}/channels"; + public static string UserAvatar(long userId, string avatarId) => $"users/{userId}/avatars/{avatarId}.jpg"; + public static string UserChannels(long userId) => $"users/{userId}/channels"; public static string UserMe => $"users/@me"; public const string Voice = "voice"; diff --git a/src/Discord.Net/API/Maintenance.cs b/src/Discord.Net/API/Maintenance.cs index 337b89453..0fc91bb96 100644 --- a/src/Discord.Net/API/Maintenance.cs +++ b/src/Discord.Net/API/Maintenance.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System; @@ -17,7 +18,8 @@ namespace Discord.API public sealed class PageData { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("name")] public string Name; [JsonProperty("url")] diff --git a/src/Discord.Net/API/Members.cs b/src/Discord.Net/API/Members.cs index 1f702719e..70efd18d1 100644 --- a/src/Discord.Net/API/Members.cs +++ b/src/Discord.Net/API/Members.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -12,9 +13,11 @@ namespace Discord.API public class MemberReference { [JsonProperty("user_id")] - public string UserId; + [JsonConverter(typeof(LongStringConverter))] + public long UserId; [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long GuildId; [JsonProperty("user")] private UserReference _user; @@ -33,7 +36,8 @@ namespace Discord.API [JsonProperty("joined_at")] public DateTime? JoinedAt; [JsonProperty("roles")] - public string[] Roles; + [JsonConverter(typeof(LongArrayStringConverter))] + public long[] Roles; } public class ExtendedMemberInfo : MemberInfo { @@ -49,12 +53,14 @@ namespace Discord.API [JsonProperty("status")] public string Status; [JsonProperty("roles")] //TODO: Might be temporary - public string[] Roles; + [JsonConverter(typeof(LongArrayStringConverter))] + public long[] Roles; } public class VoiceMemberInfo : MemberReference { [JsonProperty("channel_id")] - public string ChannelId; + [JsonConverter(typeof(LongStringConverter))] + public long ChannelId; [JsonProperty("session_id")] public string SessionId; [JsonProperty("token")] @@ -79,7 +85,8 @@ namespace Discord.API [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)] public bool? Deaf; [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable Roles; + [JsonConverter(typeof(EnumerableLongStringConverter))] + public IEnumerable Roles; } public class PruneUsersResponse diff --git a/src/Discord.Net/API/Messages.cs b/src/Discord.Net/API/Messages.cs index a5edb27ec..928847aa5 100644 --- a/src/Discord.Net/API/Messages.cs +++ b/src/Discord.Net/API/Messages.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -12,11 +13,14 @@ namespace Discord.API public class MessageReference { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("channel_id")] - public string ChannelId; + [JsonConverter(typeof(LongStringConverter))] + public long ChannelId; [JsonProperty("message_id")] - public string MessageId { get { return Id; } set { Id = value; } } + [JsonConverter(typeof(LongStringConverter))] + public long MessageId { get { return Id; } set { Id = value; } } } public class MessageInfo : MessageReference { @@ -104,7 +108,8 @@ namespace Discord.API [JsonProperty("content")] public string Content; [JsonProperty("mentions")] - public IEnumerable Mentions; + [JsonConverter(typeof(EnumerableLongStringConverter))] + public IEnumerable Mentions; [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] public string Nonce; [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)] @@ -118,7 +123,8 @@ namespace Discord.API [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] public string Content; [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable Mentions; + [JsonConverter(typeof(EnumerableLongStringConverter))] + public IEnumerable Mentions; } public sealed class EditMessageResponse : MessageInfo { } @@ -132,7 +138,8 @@ namespace Discord.API public class Data { [JsonProperty("guild_id")] - public string ServerId; + [JsonConverter(typeof(LongStringConverter))] + public long ServerId; [JsonProperty("query")] public string Query; [JsonProperty("limit")] diff --git a/src/Discord.Net/API/Permissions.cs b/src/Discord.Net/API/Permissions.cs index df8a82353..73a7c6d5a 100644 --- a/src/Discord.Net/API/Permissions.cs +++ b/src/Discord.Net/API/Permissions.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; namespace Discord.API @@ -10,7 +11,8 @@ namespace Discord.API internal sealed class SetChannelPermissionsRequest { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("type")] public string Type; [JsonProperty("allow")] diff --git a/src/Discord.Net/API/Presence.cs b/src/Discord.Net/API/Presence.cs index 07a1d8f56..9a16c9fbc 100644 --- a/src/Discord.Net/API/Presence.cs +++ b/src/Discord.Net/API/Presence.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; namespace Discord.API @@ -23,9 +24,11 @@ namespace Discord.API internal sealed class TypingStartEvent { [JsonProperty("user_id")] - public string UserId; + [JsonConverter(typeof(LongStringConverter))] + public long UserId; [JsonProperty("channel_id")] - public string ChannelId; + [JsonConverter(typeof(LongStringConverter))] + public long ChannelId; [JsonProperty("timestamp")] public int Timestamp; } diff --git a/src/Discord.Net/API/Roles.cs b/src/Discord.Net/API/Roles.cs index ba1517513..e6d4572c4 100644 --- a/src/Discord.Net/API/Roles.cs +++ b/src/Discord.Net/API/Roles.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System.Collections; using System.Collections.Generic; @@ -12,12 +13,17 @@ namespace Discord.API public class RoleReference { [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long GuildId; [JsonProperty("role_id")] - public string RoleId; + [JsonConverter(typeof(LongStringConverter))] + public long RoleId; } public class RoleInfo { + [JsonProperty("id")] + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("permissions")] public uint? Permissions; [JsonProperty("name")] @@ -28,8 +34,6 @@ namespace Discord.API public bool? Hoist; [JsonProperty("color")] public uint? Color; - [JsonProperty("id")] - public string Id; [JsonProperty("managed")] public bool? Managed; } @@ -57,7 +61,8 @@ namespace Discord.API public sealed class Role { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("position")] public uint Position; } @@ -72,14 +77,16 @@ namespace Discord.API internal sealed class RoleCreateEvent { [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long GuildId; [JsonProperty("role")] public RoleInfo Data; } internal sealed class RoleUpdateEvent { [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long GuildId; [JsonProperty("role")] public RoleInfo Data; } diff --git a/src/Discord.Net/API/Servers.cs b/src/Discord.Net/API/Servers.cs index 16970b91e..73177ab6f 100644 --- a/src/Discord.Net/API/Servers.cs +++ b/src/Discord.Net/API/Servers.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System; @@ -11,18 +12,21 @@ namespace Discord.API public class GuildReference { [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("name")] public string Name; } public class GuildInfo : GuildReference { [JsonProperty("afk_channel_id")] - public string AFKChannelId; + [JsonConverter(typeof(NullableLongStringConverter))] + public long? AFKChannelId; [JsonProperty("afk_timeout")] public int? AFKTimeout; [JsonProperty("embed_channel_id")] - public string EmbedChannelId; + [JsonConverter(typeof(NullableLongStringConverter))] + public long? EmbedChannelId; [JsonProperty("embed_enabled")] public bool EmbedEnabled; [JsonProperty("icon")] @@ -30,7 +34,8 @@ namespace Discord.API [JsonProperty("joined_at")] public DateTime? JoinedAt; [JsonProperty("owner_id")] - public string OwnerId; + [JsonConverter(typeof(NullableLongStringConverter))] + public long? OwnerId; [JsonProperty("region")] public string Region; [JsonProperty("roles")] @@ -70,7 +75,8 @@ namespace Discord.API [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)] public string Icon; [JsonProperty("afk_channel_id", NullValueHandling = NullValueHandling.Ignore)] - public string AFKChannelId; + [JsonConverter(typeof(NullableLongStringConverter))] + public long? AFKChannelId; [JsonProperty("afk_timeout", NullValueHandling = NullValueHandling.Ignore)] public int AFKTimeout; } diff --git a/src/Discord.Net/API/Users.cs b/src/Discord.Net/API/Users.cs index a75363d2c..ee507ef77 100644 --- a/src/Discord.Net/API/Users.cs +++ b/src/Discord.Net/API/Users.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; namespace Discord.API @@ -12,9 +13,10 @@ namespace Discord.API [JsonProperty("username")] public string Username; [JsonProperty("id")] - public string Id; + [JsonConverter(typeof(LongStringConverter))] + public long Id; [JsonProperty("discriminator")] - public string Discriminator; + public short? Discriminator; [JsonProperty("avatar")] public string Avatar; } diff --git a/src/Discord.Net/API/Voice.cs b/src/Discord.Net/API/Voice.cs index 2bf30fe53..3e3b159c6 100644 --- a/src/Discord.Net/API/Voice.cs +++ b/src/Discord.Net/API/Voice.cs @@ -2,6 +2,7 @@ #pragma warning disable CS0649 #pragma warning disable CS0169 +using Discord.API.Converters; using Newtonsoft.Json; using System.Collections.Generic; @@ -22,7 +23,7 @@ namespace Discord.API } } - public class GetIceResponse + /*public class GetIceResponse { [JsonProperty("ttl")] public string TTL; @@ -38,7 +39,7 @@ namespace Discord.API [JsonProperty("credential")] public string Credential; } - } + }*/ //Commands internal sealed class JoinVoiceCommand : WebSocketMessage @@ -47,9 +48,11 @@ namespace Discord.API public class Data { [JsonProperty("guild_id")] - public string ServerId; + [JsonConverter(typeof(LongStringConverter))] + public long ServerId; [JsonProperty("channel_id")] - public string ChannelId; + [JsonConverter(typeof(LongStringConverter))] + public long ChannelId; [JsonProperty("self_mute")] public string SelfMute; [JsonProperty("self_deaf")] @@ -61,7 +64,8 @@ namespace Discord.API internal sealed class VoiceServerUpdateEvent { [JsonProperty("guild_id")] - public string GuildId; + [JsonConverter(typeof(LongStringConverter))] + public long ServerId; [JsonProperty("endpoint")] public string Endpoint; [JsonProperty("token")] @@ -75,50 +79,52 @@ namespace Discord.API public class Data { [JsonProperty("server_id")] - public string ServerId; + [JsonConverter(typeof(LongStringConverter))] + public long ServerId; [JsonProperty("user_id")] - public string UserId; + [JsonConverter(typeof(LongStringConverter))] + public long UserId; [JsonProperty("session_id")] public string SessionId; [JsonProperty("token")] public string Token; } } - internal sealed class VoiceLogin2Command : WebSocketMessage +internal sealed class VoiceLogin2Command : WebSocketMessage +{ + public VoiceLogin2Command() : base(1) { } + public class Data { - public VoiceLogin2Command() : base(1) { } - public class Data + public class SocketInfo { - public class SocketInfo - { - [JsonProperty("address")] - public string Address; - [JsonProperty("port")] - public int Port; - [JsonProperty("mode")] - public string Mode = "xsalsa20_poly1305"; - } - [JsonProperty("protocol")] - public string Protocol = "udp"; - [JsonProperty("data")] - public SocketInfo SocketData = new SocketInfo(); + [JsonProperty("address")] + public string Address; + [JsonProperty("port")] + public int Port; + [JsonProperty("mode")] + public string Mode = "xsalsa20_poly1305"; } + [JsonProperty("protocol")] + public string Protocol = "udp"; + [JsonProperty("data")] + public SocketInfo SocketData = new SocketInfo(); } - internal sealed class VoiceKeepAliveCommand : WebSocketMessage - { - public VoiceKeepAliveCommand() : base(3, null) { } - } - internal sealed class IsTalkingCommand : WebSocketMessage +} +internal sealed class VoiceKeepAliveCommand : WebSocketMessage +{ + public VoiceKeepAliveCommand() : base(3, null) { } +} +internal sealed class IsTalkingCommand : WebSocketMessage +{ + public IsTalkingCommand() : base(5) { } + public class Data { - public IsTalkingCommand() : base(5) { } - public class Data - { - [JsonProperty("delay")] - public int Delay; - [JsonProperty("speaking")] - public bool IsSpeaking; - } + [JsonProperty("delay")] + public int Delay; + [JsonProperty("speaking")] + public bool IsSpeaking; } +} //Events (Voice) public class VoiceReadyEvent @@ -144,7 +150,8 @@ namespace Discord.API public class IsTalkingEvent { [JsonProperty("user_id")] - public string UserId; + [JsonConverter(typeof(LongStringConverter))] + public long UserId; [JsonProperty("ssrc")] public uint SSRC; [JsonProperty("speaking")] diff --git a/src/Discord.Net/Audio/IDiscordVoiceClient.cs b/src/Discord.Net/Audio/IDiscordVoiceClient.cs index a6984242b..d04d730b5 100644 --- a/src/Discord.Net/Audio/IDiscordVoiceClient.cs +++ b/src/Discord.Net/Audio/IDiscordVoiceClient.cs @@ -6,7 +6,7 @@ namespace Discord.Audio { IDiscordVoiceBuffer OutputBuffer { get; } - Task JoinChannel(string channelId); + Task JoinChannel(long channelId); void SendVoicePCM(byte[] data, int count); void ClearVoicePCM(); diff --git a/src/Discord.Net/DiscordAPIClient.cs b/src/Discord.Net/DiscordAPIClient.cs index 9bcab8461..ed31f48bc 100644 --- a/src/Discord.Net/DiscordAPIClient.cs +++ b/src/Discord.Net/DiscordAPIClient.cs @@ -52,39 +52,39 @@ namespace Discord => _rest.Post(Endpoints.AuthLogout); //Channels - public Task CreateChannel(string serverId, string name, string channelType) + public Task CreateChannel(long serverId, string name, string channelType) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); if (name == null) throw new ArgumentNullException(nameof(name)); if (channelType == null) throw new ArgumentNullException(nameof(channelType)); var request = new CreateChannelRequest { Name = name, Type = channelType }; return _rest.Post(Endpoints.ServerChannels(serverId), request); } - public Task CreatePMChannel(string myId, string recipientId) + public Task CreatePMChannel(long myId, long recipientId) { - if (myId == null) throw new ArgumentNullException(nameof(myId)); - if (recipientId == null) throw new ArgumentNullException(nameof(recipientId)); + if (myId <= 0) throw new ArgumentOutOfRangeException(nameof(myId)); + if (recipientId <= 0) throw new ArgumentOutOfRangeException(nameof(recipientId)); var request = new CreatePMChannelRequest { RecipientId = recipientId }; return _rest.Post(Endpoints.UserChannels(myId), request); } - public Task DestroyChannel(string channelId) + public Task DestroyChannel(long channelId) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); return _rest.Delete(Endpoints.Channel(channelId)); } - public Task EditChannel(string channelId, string name = null, string topic = null) + public Task EditChannel(long channelId, string name = null, string topic = null) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); var request = new EditChannelRequest { Name = name, Topic = topic }; return _rest.Patch(Endpoints.Channel(channelId), request); } - public Task ReorderChannels(string serverId, IEnumerable channelIds, int startPos = 0) + public Task ReorderChannels(long serverId, IEnumerable channelIds, int startPos = 0) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); if (channelIds == null) throw new ArgumentNullException(nameof(channelIds)); if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer."); @@ -93,12 +93,12 @@ namespace Discord var request = new ReorderChannelsRequest(channels); return _rest.Patch(Endpoints.ServerChannels(serverId), request); } - public Task GetMessages(string channelId, int count, string relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before) + public Task GetMessages(long channelId, int count, long? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); if (relativeMessageId != null) - return _rest.Get(Endpoints.ChannelMessages(channelId, count, relativeMessageId, relativeDir == RelativeDirection.Before ? "before" : "after")); + return _rest.Get(Endpoints.ChannelMessages(channelId, count, relativeMessageId.Value, relativeDir == RelativeDirection.Before ? "before" : "after")); else return _rest.Get(Endpoints.ChannelMessages(channelId, count)); } @@ -114,9 +114,9 @@ namespace Discord } //Invites - public Task CreateInvite(string channelId, int maxAge, int maxUses, bool tempMembership, bool hasXkcd) + public Task CreateInvite(long channelId, int maxAge, int maxUses, bool tempMembership, bool hasXkcd) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = tempMembership, WithXkcdPass = hasXkcd }; return _rest.Post(Endpoints.ChannelInvites(channelId), request); @@ -127,9 +127,9 @@ namespace Discord return _rest.Get(Endpoints.Invite(inviteIdOrXkcd)); } - public Task GetInvites(string serverId) + public Task GetInvites(long serverId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); return _rest.Get(Endpoints.ServerInvites(serverId)); } @@ -147,38 +147,38 @@ namespace Discord } //Users - public Task EditUser(string serverId, string userId, bool? mute = null, bool? deaf = null, IEnumerable roles = null) + public Task EditUser(long serverId, long userId, bool? mute = null, bool? deaf = null, IEnumerable roleIds = null) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); - var request = new EditMemberRequest { Mute = mute, Deaf = deaf, Roles = roles }; + var request = new EditMemberRequest { Mute = mute, Deaf = deaf, Roles = roleIds }; return _rest.Patch(Endpoints.ServerMember(serverId, userId), request); } - public Task KickUser(string serverId, string userId) + public Task KickUser(long serverId, long userId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); return _rest.Delete(Endpoints.ServerMember(serverId, userId)); } - public Task BanUser(string serverId, string userId) + public Task BanUser(long serverId, long userId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); return _rest.Put(Endpoints.ServerBan(serverId, userId)); } - public Task UnbanUser(string serverId, string userId) + public Task UnbanUser(long serverId, long userId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); return _rest.Delete(Endpoints.ServerBan(serverId, userId)); } - public Task PruneUsers(string serverId, int days, bool simulate) + public Task PruneUsers(long serverId, int days, bool simulate) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); if (days <= 0) throw new ArgumentOutOfRangeException(nameof(days)); if (simulate) @@ -188,93 +188,93 @@ namespace Discord } //Messages - public Task SendMessage(string channelId, string message, IEnumerable mentionedUserIds = null, string nonce = null, bool isTTS = false) + public Task SendMessage(long channelId, string message, IEnumerable mentionedUserIds = null, string nonce = null, bool isTTS = false) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); if (message == null) throw new ArgumentNullException(nameof(message)); - var request = new SendMessageRequest { Content = message, Mentions = mentionedUserIds ?? new string[0], Nonce = nonce, IsTTS = isTTS ? true : false }; + var request = new SendMessageRequest { Content = message, Mentions = mentionedUserIds ?? new long[0], Nonce = nonce, IsTTS = isTTS ? true : false }; return _rest.Post(Endpoints.ChannelMessages(channelId), request); } - public Task SendFile(string channelId, string filePath) + public Task SendFile(long channelId, string filePath) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); if (filePath == null) throw new ArgumentNullException(nameof(filePath)); return _rest.PostFile(Endpoints.ChannelMessages(channelId), filePath); } - public Task DeleteMessage(string messageId, string channelId) + public Task DeleteMessage(long messageId, long channelId) { - if (messageId == null) throw new ArgumentNullException(nameof(messageId)); - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (messageId <= 0) throw new ArgumentOutOfRangeException(nameof(messageId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); return _rest.Delete(Endpoints.ChannelMessage(channelId, messageId)); } - public Task EditMessage(string messageId, string channelId, string message = null, IEnumerable mentionedUserIds = null) + public Task EditMessage(long messageId, long channelId, string message = null, IEnumerable mentionedUserIds = null) { - if (messageId == null) throw new ArgumentNullException(nameof(messageId)); - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (messageId <= 0) throw new ArgumentOutOfRangeException(nameof(messageId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); var request = new EditMessageRequest { Content = message, Mentions = mentionedUserIds }; return _rest.Patch(Endpoints.ChannelMessage(channelId, messageId), request); } - public Task AckMessage(string messageId, string channelId) + public Task AckMessage(long messageId, long channelId) { - if (messageId == null) throw new ArgumentNullException(nameof(messageId)); - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (messageId <= 0) throw new ArgumentOutOfRangeException(nameof(messageId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); return _rest.Post(Endpoints.ChannelMessageAck(channelId, messageId)); } - public Task SendIsTyping(string channelId) + public Task SendIsTyping(long channelId) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); return _rest.Post(Endpoints.ChannelTyping(channelId)); } //Permissions - public Task SetChannelPermissions(string channelId, string userOrRoleId, string idType, uint allow = 0, uint deny = 0) + public Task SetChannelPermissions(long channelId, long userOrRoleId, string idType, uint allow = 0, uint deny = 0) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); - if (userOrRoleId == null) throw new ArgumentNullException(nameof(userOrRoleId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); + if (userOrRoleId <= 0) throw new ArgumentOutOfRangeException(nameof(userOrRoleId)); if (idType == null) throw new ArgumentNullException(nameof(idType)); var request = new SetChannelPermissionsRequest { Id = userOrRoleId, Type = idType, Allow = allow, Deny = deny }; return _rest.Put(Endpoints.ChannelPermission(channelId, userOrRoleId), request); } - public Task DeleteChannelPermissions(string channelId, string userOrRoleId) + public Task DeleteChannelPermissions(long channelId, long userOrRoleId) { - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); - if (userOrRoleId == null) throw new ArgumentNullException(nameof(userOrRoleId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); + if (userOrRoleId <= 0) throw new ArgumentOutOfRangeException(nameof(userOrRoleId)); return _rest.Delete(Endpoints.ChannelPermission(channelId, userOrRoleId), null); } //Roles - public Task CreateRole(string serverId) + public Task CreateRole(long serverId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); return _rest.Post(Endpoints.ServerRoles(serverId)); } - public Task DeleteRole(string serverId, string roleId) + public Task DeleteRole(long serverId, long roleId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (roleId == null) throw new ArgumentNullException(nameof(roleId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (roleId <= 0) throw new ArgumentOutOfRangeException(nameof(roleId)); return _rest.Delete(Endpoints.ServerRole(serverId, roleId)); } - public Task EditRole(string serverId, string roleId, string name = null, uint? permissions = null, uint? color = null, bool? hoist = null) + public Task EditRole(long serverId, long roleId, string name = null, uint? permissions = null, uint? color = null, bool? hoist = null) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); - if (roleId == null) throw new ArgumentNullException(nameof(roleId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); + if (roleId <= 0) throw new ArgumentOutOfRangeException(nameof(roleId)); var request = new EditRoleRequest { Name = name, Permissions = permissions, Hoist = hoist, Color = color }; return _rest.Patch(Endpoints.ServerRole(serverId, roleId), request); } - public Task ReorderRoles(string serverId, IEnumerable roleIds, int startPos = 0) + public Task ReorderRoles(long serverId, IEnumerable roleIds, int startPos = 0) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); if (roleIds == null) throw new ArgumentNullException(nameof(roleIds)); if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer."); @@ -293,15 +293,15 @@ namespace Discord var request = new CreateServerRequest { Name = name, Region = region }; return _rest.Post(Endpoints.Servers, request); } - public Task LeaveServer(string serverId) + public Task LeaveServer(long serverId) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); return _rest.Delete(Endpoints.Server(serverId)); } - public Task EditServer(string serverId, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) + public Task EditServer(long serverId, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); var request = new EditServerRequest { Name = name, Region = region, Icon = Base64Picture(iconType, icon) }; return _rest.Patch(Endpoints.Server(serverId), request); diff --git a/src/Discord.Net/DiscordClient.Bans.cs b/src/Discord.Net/DiscordClient.Bans.cs index 87ce6581c..b66b76862 100644 --- a/src/Discord.Net/DiscordClient.Bans.cs +++ b/src/Discord.Net/DiscordClient.Bans.cs @@ -6,10 +6,10 @@ namespace Discord { public class BanEventArgs : EventArgs { - public string UserId { get; } + public long UserId { get; } public Server Server { get; } - public BanEventArgs(string userId, Server server) + public BanEventArgs(long userId, Server server) { UserId = userId; Server = server; @@ -19,13 +19,13 @@ namespace Discord public partial class DiscordClient { public event EventHandler UserBanned; - private void RaiseUserBanned(string userId, Server server) + private void RaiseUserBanned(long userId, Server server) { if (UserBanned != null) RaiseEvent(nameof(UserBanned), () => UserBanned(this, new BanEventArgs(userId, server))); } public event EventHandler UserUnbanned; - private void RaiseUserUnbanned(string userId, Server server) + private void RaiseUserUnbanned(long userId, Server server) { if (UserUnbanned != null) RaiseEvent(nameof(UserUnbanned), () => UserUnbanned(this, new BanEventArgs(userId, server))); @@ -42,10 +42,10 @@ namespace Discord } /// Unbans a user from the provided server. - public async Task Unban(Server server, string userId) + public async Task Unban(Server server, long userId) { if (server == null) throw new ArgumentNullException(nameof(server)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); CheckReady(); try { await _api.UnbanUser(server.Id, userId).ConfigureAwait(false); } diff --git a/src/Discord.Net/DiscordClient.Channels.cs b/src/Discord.Net/DiscordClient.Channels.cs index a7128a6a1..061dd565c 100644 --- a/src/Discord.Net/DiscordClient.Channels.cs +++ b/src/Discord.Net/DiscordClient.Channels.cs @@ -7,15 +7,15 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class Channels : AsyncCollection + internal sealed class Channels : AsyncCollection { public IEnumerable PrivateChannels => _privateChannels.Select(x => x.Value); - private ConcurrentDictionary _privateChannels; + private ConcurrentDictionary _privateChannels; public Channels(DiscordClient client, object writerLock) : base(client, writerLock) { - _privateChannels = new ConcurrentDictionary(); + _privateChannels = new ConcurrentDictionary(); ItemCreated += (s, e) => { if (e.Item.IsPrivate) @@ -31,8 +31,8 @@ namespace Discord }; Cleared += (s, e) => _privateChannels.Clear(); } - - public Channel GetOrAdd(string id, string serverId, string recipientId = null) + + public Channel GetOrAdd(long id, long? serverId, long? recipientId = null) => GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); } @@ -71,9 +71,9 @@ namespace Discord private readonly Channels _channels; /// Returns the channel with the specified id, or null if none was found. - public Channel GetChannel(string id) + public Channel GetChannel(long id) { - if (id == null) throw new ArgumentNullException(nameof(id)); + if (id <= 0) throw new ArgumentOutOfRangeException(nameof(id)); CheckReady(); return _channels[id]; @@ -93,7 +93,7 @@ namespace Discord { if (name[0] == '<' && name[1] == '#' && name[name.Length - 1] == '>') //Parse mention { - string id = name.Substring(2, name.Length - 3); + long id = IdConvert.ToLong(name.Substring(2, name.Length - 3)); var channel = _channels[id]; if (channel != null) query = query.Concat(new Channel[] { channel }); @@ -135,10 +135,10 @@ namespace Discord channel = user.GlobalUser.PrivateChannel; if (channel == null) { - var response = await _api.CreatePMChannel(_userId, user.Id).ConfigureAwait(false); - var recipient = _users.GetOrAdd(response.Recipient?.Id, null); + var response = await _api.CreatePMChannel(_userId.Value, user.Id).ConfigureAwait(false); + var recipient = _users.GetOrAdd(response.Recipient.Id, null); recipient.Update(response.Recipient); - channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id); + channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient.Id); channel.Update(response); } return channel; diff --git a/src/Discord.Net/DiscordClient.Invites.cs b/src/Discord.Net/DiscordClient.Invites.cs index 0d3b8804e..bfdb7325c 100644 --- a/src/Discord.Net/DiscordClient.Invites.cs +++ b/src/Discord.Net/DiscordClient.Invites.cs @@ -23,7 +23,7 @@ namespace Discord inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); - var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id); + var invite = new Invite(this, response.Code, response.XkcdPass); invite.Cache(); //Builds references invite.Update(response); return invite; @@ -38,7 +38,7 @@ namespace Discord var response = await _api.GetInvites(server.Id).ConfigureAwait(false); return response.Select(x => { - var invite = new Invite(this, x.Code, x.XkcdPass, x.Guild.Id, x.Inviter?.Id, x.Channel?.Id); + var invite = new Invite(this, x.Code, x.XkcdPass); invite.Cache(); //Builds references invite.Update(x); return invite; @@ -71,7 +71,7 @@ namespace Discord var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); - var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id); + var invite = new Invite(this, response.Code, response.XkcdPass); invite.Cache(); //Builds references return invite; } diff --git a/src/Discord.Net/DiscordClient.Messages.cs b/src/Discord.Net/DiscordClient.Messages.cs index 1572155d9..6de9c1542 100644 --- a/src/Discord.Net/DiscordClient.Messages.cs +++ b/src/Discord.Net/DiscordClient.Messages.cs @@ -1,5 +1,4 @@ using Discord.API; -using Discord.Net; using System; using System.Collections.Generic; using System.Linq; @@ -8,7 +7,7 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class Messages : AsyncCollection + internal sealed class Messages : AsyncCollection { private bool _isEnabled; @@ -17,8 +16,8 @@ namespace Discord { _isEnabled = isEnabled; } - - public Message GetOrAdd(string id, string channelId, string userId) + + public Message GetOrAdd(long id, long channelId, long userId) { if (_isEnabled) return GetOrAdd(id, () => new Message(_client, id, channelId, userId)); @@ -80,9 +79,9 @@ namespace Discord private readonly Messages _messages; /// Returns the message with the specified id, or null if none was found. - public Message GetMessage(string id) + public Message GetMessage(long id) { - if (id == null) throw new ArgumentNullException(nameof(id)); + if (id <= 0) throw new ArgumentOutOfRangeException(nameof(id)); CheckReady(); return _messages[id]; @@ -124,17 +123,16 @@ namespace Discord if (Config.UseMessageQueue) { var nonce = GenerateNonce(); - msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _userId); + msg = _messages.GetOrAdd(nonce, channel.Id, _userId.Value); var currentUser = msg.User; msg.Update(new MessageInfo { Content = text, Timestamp = DateTime.UtcNow, - Author = new UserReference { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = _userId, Username = currentUser.Name }, + Author = new UserReference { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = _userId.Value, Username = currentUser.Name }, ChannelId = channel.Id, IsTextToSpeech = isTextToSpeech }); - msg.Nonce = nonce; msg.IsQueued = true; if (text.Length > MaxMessageSize) @@ -217,7 +215,7 @@ namespace Discord /// Downloads last count messages from the server, returning all messages before or after relativeMessageId, if it's provided. - public async Task DownloadMessages(Channel channel, int count, string relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) + public async Task DownloadMessages(Channel channel, int count, long? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (count < 0) throw new ArgumentNullException(nameof(count)); @@ -274,7 +272,7 @@ namespace Discord SendMessageResponse response = null; try { - response = await _api.SendMessage(msg.Channel.Id, msg.RawText, msg.MentionedUsers.Select(x => x.Id), msg.Nonce, msg.IsTTS).ConfigureAwait(false); + response = await _api.SendMessage(msg.Channel.Id, msg.RawText, msg.MentionedUsers.Select(x => x.Id), IdConvert.ToString(msg.Id), msg.IsTTS).ConfigureAwait(false); } catch (WebException) { break; } catch (HttpException) { hasFailed = true; } @@ -293,10 +291,10 @@ namespace Discord } }); } - private string GenerateNonce() + private long GenerateNonce() { lock (_rand) - return _rand.Next().ToString(); + return -_rand.Next(1, int.MaxValue - 1); } } } \ No newline at end of file diff --git a/src/Discord.Net/DiscordClient.Permissions.cs b/src/Discord.Net/DiscordClient.Permissions.cs index 8353ffef3..6c8a3c46f 100644 --- a/src/Discord.Net/DiscordClient.Permissions.cs +++ b/src/Discord.Net/DiscordClient.Permissions.cs @@ -36,7 +36,7 @@ namespace Discord if (user == null) throw new ArgumentNullException(nameof(user)); CheckReady(); - return SetChannelPermissions(channel, user?.Id, PermissionTarget.User, allow, deny); + return SetChannelPermissions(channel, user.Id, PermissionTarget.User, allow, deny); } public Task SetChannelPermissions(Channel channel, User user, DualChannelPermissions permissions = null) { @@ -44,7 +44,7 @@ namespace Discord if (user == null) throw new ArgumentNullException(nameof(user)); CheckReady(); - return SetChannelPermissions(channel, user?.Id, PermissionTarget.User, permissions?.Allow, permissions?.Deny); + return SetChannelPermissions(channel, user.Id, PermissionTarget.User, permissions?.Allow, permissions?.Deny); } public Task SetChannelPermissions(Channel channel, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null) { @@ -52,7 +52,7 @@ namespace Discord if (role == null) throw new ArgumentNullException(nameof(role)); CheckReady(); - return SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, allow, deny); + return SetChannelPermissions(channel, role.Id, PermissionTarget.Role, allow, deny); } public Task SetChannelPermissions(Channel channel, Role role, DualChannelPermissions permissions = null) { @@ -60,9 +60,9 @@ namespace Discord if (role == null) throw new ArgumentNullException(nameof(role)); CheckReady(); - return SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); + return SetChannelPermissions(channel, role.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); } - private Task SetChannelPermissions(Channel channel, string targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) + private Task SetChannelPermissions(Channel channel, long targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) => _api.SetChannelPermissions(channel.Id, targetId, targetType.Value, allow?.RawValue ?? 0, deny?.RawValue ?? 0); public Task RemoveChannelPermissions(Channel channel, User user) @@ -71,7 +71,7 @@ namespace Discord if (user == null) throw new ArgumentNullException(nameof(user)); CheckReady(); - return RemoveChannelPermissions(channel, user?.Id, PermissionTarget.User); + return RemoveChannelPermissions(channel, user.Id, PermissionTarget.User); } public Task RemoveChannelPermissions(Channel channel, Role role) { @@ -79,9 +79,9 @@ namespace Discord if (role == null) throw new ArgumentNullException(nameof(role)); CheckReady(); - return RemoveChannelPermissions(channel, role?.Id, PermissionTarget.Role); + return RemoveChannelPermissions(channel, role.Id, PermissionTarget.Role); } - private async Task RemoveChannelPermissions(Channel channel, string userOrRoleId, PermissionTarget targetType) + private async Task RemoveChannelPermissions(Channel channel, long userOrRoleId, PermissionTarget targetType) { try { diff --git a/src/Discord.Net/DiscordClient.Roles.cs b/src/Discord.Net/DiscordClient.Roles.cs index bfa3c8aaf..028f38b66 100644 --- a/src/Discord.Net/DiscordClient.Roles.cs +++ b/src/Discord.Net/DiscordClient.Roles.cs @@ -6,12 +6,12 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class Roles : AsyncCollection + internal sealed class Roles : AsyncCollection { public Roles(DiscordClient client, object writerLock) : base(client, writerLock) { } - public Role GetOrAdd(string id, string serverId) + public Role GetOrAdd(long id, long serverId) => GetOrAdd(id, () => new Role(_client, id, serverId)); } @@ -48,9 +48,9 @@ namespace Discord private readonly Roles _roles; /// Returns the role with the specified id, or null if none was found. - public Role GetRole(string id) + public Role GetRole(long id) { - if (id == null) throw new ArgumentNullException(nameof(id)); + if (id <= 0) throw new ArgumentOutOfRangeException(nameof(id)); CheckReady(); return _roles[id]; diff --git a/src/Discord.Net/DiscordClient.Servers.cs b/src/Discord.Net/DiscordClient.Servers.cs index 8e113cdfa..889c5879e 100644 --- a/src/Discord.Net/DiscordClient.Servers.cs +++ b/src/Discord.Net/DiscordClient.Servers.cs @@ -6,19 +6,18 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class Servers : AsyncCollection + internal sealed class Servers : AsyncCollection { public Servers(DiscordClient client, object writerLock) : base(client, writerLock) { } - - public Server GetOrAdd(string id) + + public Server GetOrAdd(long id) => GetOrAdd(id, () => new Server(_client, id)); } public class ServerEventArgs : EventArgs { public Server Server { get; } - public string ServerId => Server.Id; public ServerEventArgs(Server server) { Server = server; } } @@ -62,9 +61,9 @@ namespace Discord private readonly Servers _servers; /// Returns the server with the specified id, or null if none was found. - public Server GetServer(string id) + public Server GetServer(long id) { - if (id == null) throw new ArgumentNullException(nameof(id)); + if (id <= 0) throw new ArgumentOutOfRangeException(nameof(id)); CheckReady(); return _servers[id]; diff --git a/src/Discord.Net/DiscordClient.Users.cs b/src/Discord.Net/DiscordClient.Users.cs index fae87ed90..c8717aca3 100644 --- a/src/Discord.Net/DiscordClient.Users.cs +++ b/src/Discord.Net/DiscordClient.Users.cs @@ -5,27 +5,25 @@ using System.Threading.Tasks; namespace Discord { - internal sealed class GlobalUsers : AsyncCollection + internal sealed class GlobalUsers : AsyncCollection { public GlobalUsers(DiscordClient client, object writerLock) : base(client, writerLock) { } - public GlobalUser GetOrAdd(string id) => GetOrAdd(id, () => new GlobalUser(_client, id)); + public GlobalUser GetOrAdd(long id) => GetOrAdd(id, () => new GlobalUser(_client, id)); } - internal sealed class Users : AsyncCollection + internal sealed class Users : AsyncCollection { public Users(DiscordClient client, object writerLock) : base(client, writerLock) { } - private string GetKey(string userId, string serverId) - => User.GetId(userId, serverId); - - public User this[string userId, string serverId] - => this[GetKey(userId, serverId)]; - public User GetOrAdd(string userId, string serverId) - => GetOrAdd(GetKey(userId, serverId), () => new User(_client, userId, serverId)); - public User TryRemove(string userId, string serverId) - => TryRemove(GetKey(userId, serverId)); + + public User this[long userId, long? serverId] + => base[new User.CompositeKey(userId, serverId)]; + public User GetOrAdd(long userId, long? serverId) + => GetOrAdd(new User.CompositeKey(userId, serverId), () => new User(_client, userId, serverId)); + public User TryRemove(long userId, long? serverId) + => TryRemove(new User.CompositeKey(userId, serverId)); } public class UserEventArgs : EventArgs @@ -38,7 +36,6 @@ namespace Discord public class UserChannelEventArgs : UserEventArgs { public Channel Channel { get; } - public string ChannelId => Channel.Id; public UserChannelEventArgs(User user, Channel channel) : base(user) @@ -123,30 +120,29 @@ namespace Discord private readonly Users _users; /// Returns the user with the specified id, along with their server-specific data, or null if none was found. - public User GetUser(Server server, string userId) + public User GetUser(Server server, long userId) { if (server == null) throw new ArgumentNullException(nameof(server)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); CheckReady(); return _users[userId, server.Id]; } /// Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. /// Name formats supported: Name and @Name. Search is case-insensitive. - public User GetUser(Server server, string username, string discriminator) + public User GetUser(Server server, string username, short discriminator) { if (server == null) throw new ArgumentNullException(nameof(server)); if (username == null) throw new ArgumentNullException(nameof(username)); - if (discriminator == null) throw new ArgumentNullException(nameof(discriminator)); + if (discriminator <= 0) throw new ArgumentOutOfRangeException(nameof(discriminator)); CheckReady(); - User user = FindUsers(server, username, discriminator, true).FirstOrDefault(); - return _users[user?.Id, server.Id]; + return FindUsers(server, username, discriminator, true).FirstOrDefault(); } /// Returns all users with the specified server and name, along with their server-specific data. /// Name formats supported: Name and @Name. Search is case-insensitive. - public IEnumerable FindUsers(Server server, string name, string discriminator = null, bool exactMatch = false) + public IEnumerable FindUsers(Server server, string name, short? discriminator = null, bool exactMatch = false) { if (server == null) throw new ArgumentNullException(nameof(server)); if (name == null) throw new ArgumentNullException(nameof(name)); @@ -156,16 +152,16 @@ namespace Discord } /// Returns all users with the specified channel and name, along with their server-specific data. /// Name formats supported: Name and @Name. Search is case-insensitive. - public IEnumerable FindUsers(Channel channel, string name, string discriminator = null, bool exactMatch = false) + public IEnumerable FindUsers(Channel channel, string name, short? discriminator = null, bool exactMatch = false) { if (channel == null) throw new ArgumentNullException(nameof(channel)); if (name == null) throw new ArgumentNullException(nameof(name)); CheckReady(); - return FindUsers(channel.Members, channel.IsPrivate ? null : channel.Server.Id, name, discriminator, exactMatch); + return FindUsers(channel.Members, channel.IsPrivate ? (long?)null : channel.Server.Id, name, discriminator, exactMatch); } - private IEnumerable FindUsers(IEnumerable users, string serverId, string name, string discriminator = null, bool exactMatch = false) + private IEnumerable FindUsers(IEnumerable users, long? serverId, string name, short? discriminator = null, bool exactMatch = false) { var query = users.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); @@ -173,7 +169,7 @@ namespace Discord { if (name[0] == '<' && name[1] == '@' && name[name.Length - 1] == '>') //Parse mention { - string id = name.Substring(2, name.Length - 3); + long id = IdConvert.ToLong(name.Substring(2, name.Length - 3)); var channel = _users[id, serverId]; if (channel != null) query = query.Concat(new User[] { channel }); @@ -186,57 +182,60 @@ namespace Discord } if (discriminator != null) - query = query.Where(x => x.Discriminator == discriminator); + query = query.Where(x => x.Discriminator == discriminator.Value); return query; } public Task EditUser(User user, bool? mute = null, bool? deaf = null, IEnumerable roles = null) { if (user == null) throw new ArgumentNullException(nameof(user)); + if (user.IsPrivate) throw new InvalidOperationException("Unable to edit users in a private channel"); CheckReady(); - var serverId = user.Server?.Id; + var serverId = user.Server.Id; return _api.EditUser(serverId, user.Id, mute: mute, deaf: deaf, - roles: roles.Select(x => x.Id).Where(x => x != serverId)); + roleIds: roles.Select(x => x.Id).Where(x => x != serverId)); } public Task KickUser(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); + if (user.IsPrivate) throw new InvalidOperationException("Unable to kick users from a private channel"); - return _api.KickUser(user.Server?.Id, user.Id); + return _api.KickUser(user.Server.Id, user.Id); } public Task BanUser(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); + if (user.IsPrivate) throw new InvalidOperationException("Unable to ban users from a private channel"); - return _api.BanUser(user.Server?.Id, user.Id); + return _api.BanUser(user.Server.Id, user.Id); } - public Task UnbanUser(Server server, string userId) + public Task UnbanUser(Server server, long userId) { if (server == null) throw new ArgumentNullException(nameof(server)); - if (userId == null) throw new ArgumentNullException(nameof(userId)); + if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); return _api.UnbanUser(server.Id, userId); } - public async Task PruneUsers(string serverId, int days, bool simulate = false) + public async Task PruneUsers(Server server, int days, bool simulate = false) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (server == null) throw new ArgumentNullException(nameof(server)); if (days <= 0) throw new ArgumentOutOfRangeException(nameof(days)); CheckReady(); - var response = await _api.PruneUsers(serverId, days, simulate); + var response = await _api.PruneUsers(server.Id, days, simulate); return response.Pruned ?? 0; } /// When Config.UseLargeThreshold is enabled, running this command will request the Discord server to provide you with all offline users for a particular server. - public void RequestOfflineUsers(string serverId) + public void RequestOfflineUsers(Server server) { - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (server == null) throw new ArgumentNullException(nameof(server)); - _dataSocket.SendGetUsers(serverId); + _dataSocket.SendGetUsers(server.Id); } public Task EditProfile(string currentPassword = "", diff --git a/src/Discord.Net/DiscordClient.Voice.cs b/src/Discord.Net/DiscordClient.Voice.cs index 795b4f75e..d923c2d7e 100644 --- a/src/Discord.Net/DiscordClient.Voice.cs +++ b/src/Discord.Net/DiscordClient.Voice.cs @@ -8,7 +8,7 @@ namespace Discord { public IDiscordVoiceClient GetVoiceClient(Server server) { - if (server.Id == null) throw new ArgumentNullException(nameof(server.Id)); + if (server.Id <= 0) throw new ArgumentOutOfRangeException(nameof(server.Id)); if (!Config.EnableVoiceMultiserver) { @@ -16,7 +16,7 @@ namespace Discord return this; else return null; - } + } DiscordWSClient client; if (_voiceClients.TryGetValue(server.Id, out client)) diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index c6f069cf7..d313f1ea4 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -16,7 +16,7 @@ namespace Discord private readonly Random _rand; private readonly JsonSerializer _serializer; private readonly ConcurrentQueue _pendingMessages; - private readonly ConcurrentDictionary _voiceClients; + private readonly ConcurrentDictionary _voiceClients; private readonly Dictionary _services; private bool _sentInitialLog; private uint _nextVoiceClientId; @@ -38,7 +38,7 @@ namespace Discord if (Config.UseMessageQueue) _pendingMessages = new ConcurrentQueue(); if (Config.EnableVoiceMultiserver) - _voiceClients = new ConcurrentDictionary(); + _voiceClients = new ConcurrentDictionary(); object cacheLock = new object(); _channels = new Channels(this, cacheLock); @@ -139,18 +139,18 @@ namespace Discord } if (_config.LogLevel >= LogMessageSeverity.Debug) { - _channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); - _channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); + _channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); + _channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); _channels.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Channels"); - _users.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); - _users.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); + _users.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created User {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); + _users.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed User {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); _users.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Users"); - _messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); - _messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); - _messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Channel.Id}/[{e.OldId} -> {e.NewId}]"); + _messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); + _messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Channel.Id}/{e.Item.Id}"); + _messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Channel.Id}/[{e.OldId} -> {e.NewId}]"); _messages.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Messages"); - _roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); - _roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.Server?.Id ?? "[Private]"}/{e.Item.Id}"); + _roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); + _roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {IdConvert.ToString(e.Item.Server?.Id) ?? "[Private]"}/{e.Item.Id}"); _roles.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Roles"); _servers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Server {e.Item.Id}"); _servers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Server {e.Item.Id}"); @@ -498,8 +498,9 @@ namespace Discord var server = _servers[data.GuildId]; if (server != null) { - server.AddBan(data.User?.Id); - RaiseUserBanned(data.User?.Id, server); + var id = data.User?.Id ?? data.UserId; + server.AddBan(id); + RaiseUserBanned(id, server); } } break; @@ -507,8 +508,12 @@ namespace Discord { var data = e.Payload.ToObject(_serializer); var server = _servers[data.GuildId]; - if (server != null && server.RemoveBan(data.User?.Id)) - RaiseUserUnbanned(data.User?.Id, server); + if (server != null) + { + var id = data.User?.Id ?? data.UserId; + if (server.RemoveBan(id)) + RaiseUserUnbanned(id, server); + } } break; diff --git a/src/Discord.Net/DiscordWSClient.Events.cs b/src/Discord.Net/DiscordWSClient.Events.cs index c665ddcfb..1a6bedaa0 100644 --- a/src/Discord.Net/DiscordWSClient.Events.cs +++ b/src/Discord.Net/DiscordWSClient.Events.cs @@ -34,9 +34,9 @@ namespace Discord } public class VoiceDisconnectedEventArgs : DisconnectedEventArgs { - public readonly string ServerId; + public readonly long ServerId; - internal VoiceDisconnectedEventArgs(string serverId, DisconnectedEventArgs e) + internal VoiceDisconnectedEventArgs(long serverId, DisconnectedEventArgs e) : base(e.WasUnexpected, e.Error) { ServerId = serverId; @@ -60,13 +60,13 @@ namespace Discord public sealed class VoicePacketEventArgs { - public string UserId { get; } - public string ChannelId { get; } + public long UserId { get; } + public long ChannelId { get; } public byte[] Buffer { get; } public int Offset { get; } public int Count { get; } - internal VoicePacketEventArgs(string userId, string channelId, byte[] buffer, int offset, int count) + internal VoicePacketEventArgs(long userId, long channelId, byte[] buffer, int offset, int count) { UserId = userId; Buffer = buffer; @@ -103,7 +103,7 @@ namespace Discord RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); } public event EventHandler VoiceDisconnected; - private void RaiseVoiceDisconnected(string serverId, DisconnectedEventArgs e) + private void RaiseVoiceDisconnected(long serverId, DisconnectedEventArgs e) { if (VoiceDisconnected != null) RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e))); diff --git a/src/Discord.Net/DiscordWSClient.Voice.cs b/src/Discord.Net/DiscordWSClient.Voice.cs index e04caa8a1..1ebbc0f10 100644 --- a/src/Discord.Net/DiscordWSClient.Voice.cs +++ b/src/Discord.Net/DiscordWSClient.Voice.cs @@ -8,15 +8,15 @@ namespace Discord { IDiscordVoiceBuffer IDiscordVoiceClient.OutputBuffer => _voiceSocket.OutputBuffer; - async Task IDiscordVoiceClient.JoinChannel(string channelId) + async Task IDiscordVoiceClient.JoinChannel(long channelId) { CheckReady(checkVoice: true); - if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); await _voiceSocket.Disconnect().ConfigureAwait(false); - await _voiceSocket.SetChannel(_voiceServerId, channelId).ConfigureAwait(false); - _dataSocket.SendJoinVoice(_voiceServerId, channelId); + await _voiceSocket.SetChannel(_voiceServerId.Value, channelId).ConfigureAwait(false); + _dataSocket.SendJoinVoice(_voiceServerId.Value, channelId); await _voiceSocket.WaitForConnection(_config.ConnectionTimeout).ConfigureAwait(false); } diff --git a/src/Discord.Net/DiscordWSClient.cs b/src/Discord.Net/DiscordWSClient.cs index 4b64200d4..f3613db09 100644 --- a/src/Discord.Net/DiscordWSClient.cs +++ b/src/Discord.Net/DiscordWSClient.cs @@ -27,11 +27,11 @@ namespace Discord internal readonly VoiceWebSocket _voiceSocket; protected ExceptionDispatchInfo _disconnectReason; protected string _gateway, _token; - protected string _userId, _voiceServerId; + protected long? _userId, _voiceServerId; private Task _runTask; private bool _wasDisconnectUnexpected; - public string CurrentUserId => _userId; + public long CurrentUserId => _userId.Value; /// Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. public DiscordWSClientConfig Config => _config; @@ -59,7 +59,7 @@ namespace Discord if (_config.EnableVoice) _voiceSocket = CreateVoiceSocket(); } - internal DiscordWSClient(DiscordWSClientConfig config = null, string voiceServerId = null) + internal DiscordWSClient(DiscordWSClientConfig config = null, long? voiceServerId = null) : this(config) { _voiceServerId = voiceServerId; @@ -100,7 +100,7 @@ namespace Discord socket.Connected += (s, e) => RaiseVoiceConnected(); socket.Disconnected += async (s, e) => { - RaiseVoiceDisconnected(socket.CurrentServerId, e); + RaiseVoiceDisconnected(socket.CurrentServerId.Value, e); if (e.WasUnexpected) await socket.Reconnect().ConfigureAwait(false); }; @@ -241,9 +241,9 @@ namespace Discord { if (_config.EnableVoice) { - string voiceServerId = _voiceSocket.CurrentServerId; + var voiceServerId = _voiceSocket.CurrentServerId; if (voiceServerId != null) - _dataSocket.SendLeaveVoice(voiceServerId); + _dataSocket.SendLeaveVoice(voiceServerId.Value); await _voiceSocket.Disconnect().ConfigureAwait(false); } await _dataSocket.Disconnect().ConfigureAwait(false); @@ -303,17 +303,17 @@ namespace Discord switch (e.Type) { case "READY": - _userId = e.Payload["user"].Value("id"); + _userId = IdConvert.ToLong(e.Payload["user"].Value("id")); break; case "VOICE_SERVER_UPDATE": { - string guildId = e.Payload.Value("guild_id"); + long guildId = IdConvert.ToLong(e.Payload.Value("guild_id")); if (_config.EnableVoice && guildId == _voiceSocket.CurrentServerId) { string token = e.Payload.Value("token"); _voiceSocket.Host = "wss://" + e.Payload.Value("endpoint").Split(':')[0]; - await _voiceSocket.Login(_userId, _dataSocket.SessionId, token, _cancelToken).ConfigureAwait(false); + await _voiceSocket.Login(_userId.Value, _dataSocket.SessionId, token, _cancelToken).ConfigureAwait(false); } } break; diff --git a/src/Discord.Net/Helpers/AsyncCollection.cs b/src/Discord.Net/Helpers/AsyncCollection.cs index ee47ce375..0e3252d13 100644 --- a/src/Discord.Net/Helpers/AsyncCollection.cs +++ b/src/Discord.Net/Helpers/AsyncCollection.cs @@ -6,7 +6,8 @@ using System.Linq; namespace Discord { - internal abstract class AsyncCollection : IEnumerable + internal abstract class AsyncCollection : IEnumerable + where TKey : struct, IEquatable where TValue : CachedObject { private readonly object _writerLock; @@ -19,9 +20,9 @@ namespace Discord public class CollectionItemRemappedEventArgs : EventArgs { public TValue Item { get; } - public string OldId { get; } - public string NewId { get; } - public CollectionItemRemappedEventArgs(TValue item, string oldId, string newId) { Item = item; OldId = oldId; NewId = newId; } + public TKey OldId { get; } + public TKey NewId { get; } + public CollectionItemRemappedEventArgs(TValue item, TKey oldId, TKey newId) { Item = item; OldId = oldId; NewId = newId; } } public EventHandler ItemCreated; @@ -37,7 +38,7 @@ namespace Discord ItemDestroyed(this, new CollectionItemEventArgs(item)); } public EventHandler ItemRemapped; - private void RaiseItemRemapped(TValue item, string oldId, string newId) + private void RaiseItemRemapped(TValue item, TKey oldId, TKey newId) { if (ItemRemapped != null) ItemRemapped(this, new CollectionItemRemappedEventArgs(item, oldId, newId)); @@ -51,20 +52,22 @@ namespace Discord } protected readonly DiscordClient _client; - protected readonly ConcurrentDictionary _dictionary; + protected readonly ConcurrentDictionary _dictionary; protected AsyncCollection(DiscordClient client, object writerLock) { _client = client; _writerLock = writerLock; - _dictionary = new ConcurrentDictionary(); + _dictionary = new ConcurrentDictionary(); } - public TValue this[string key] + public TValue this[TKey? key] + => key == null ? null : this[key.Value]; + public TValue this[TKey key] { get { - if (key == null) + if (key.Equals(default(TKey))) return null; TValue result; @@ -73,7 +76,7 @@ namespace Discord return result; } } - protected TValue GetOrAdd(string key, Func createFunc) + protected TValue GetOrAdd(TKey key, Func createFunc) { TValue result; if (_dictionary.TryGetValue(key, out result)) @@ -91,7 +94,7 @@ namespace Discord } return result; } - public TValue TryRemove(string key) + public TValue TryRemove(TKey key) { if (_dictionary.ContainsKey(key)) { @@ -107,7 +110,7 @@ namespace Discord } return null; } - public TValue Remap(string oldKey, string newKey) + public TValue Remap(TKey oldKey, TKey newKey) { if (_dictionary.ContainsKey(oldKey)) { diff --git a/src/Discord.Net/Helpers/CachedObject.cs b/src/Discord.Net/Helpers/CachedObject.cs index 0d0b3bf36..9dbd21b03 100644 --- a/src/Discord.Net/Helpers/CachedObject.cs +++ b/src/Discord.Net/Helpers/CachedObject.cs @@ -1,23 +1,35 @@ -namespace Discord +using System.Globalization; + +namespace Discord { - public abstract class CachedObject + public abstract class CachedObject : CachedObject + { + private TKey _id; + + internal CachedObject(DiscordClient client, TKey id) + : base(client) + { + _id = id; + } + + /// Returns the unique identifier for this object. + public TKey Id { get { return _id; } internal set { _id = value; } } + + public override string ToString() => $"{this.GetType().Name} {Id}"; + } + + public abstract class CachedObject { protected readonly DiscordClient _client; private bool _isCached; internal bool IsCached => _isCached; - internal CachedObject(DiscordClient client, string id) + internal CachedObject(DiscordClient client) { _client = client; - Id = id; } - /// Returns the unique identifier for this object. - public string Id { get; internal set; } - - public override string ToString() => $"{this.GetType().Name} {Id}"; - internal void Cache() { LoadReferences(); diff --git a/src/Discord.Net/Helpers/IdConvert.cs b/src/Discord.Net/Helpers/IdConvert.cs new file mode 100644 index 000000000..6721d7de9 --- /dev/null +++ b/src/Discord.Net/Helpers/IdConvert.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; + +namespace Discord +{ + internal static class IdConvert + { + internal static readonly IFormatProvider _format = CultureInfo.InvariantCulture; + + public static short ToShort(string value) + => short.Parse(value, NumberStyles.None, _format); + public static short? ToNullableShort(string value) + => value == null ? (short?)null : short.Parse(value, NumberStyles.None, _format); + public static long ToLong(string value) + => long.Parse(value, NumberStyles.None, _format); + public static long? ToNullableLong(string value) + => value == null ? (long?)null : long.Parse(value, NumberStyles.None, _format); + + public static string ToString(short value) + => value.ToString(_format); + public static string ToString(short? value) + => value?.ToString(_format); + public static string ToString(long value) + => value.ToString(_format); + public static string ToString(long? value) + => value?.ToString(_format); + } +} diff --git a/src/Discord.Net/Helpers/Mention.cs b/src/Discord.Net/Helpers/Mention.cs index b3c6ac649..4eef7992d 100644 --- a/src/Discord.Net/Helpers/Mention.cs +++ b/src/Discord.Net/Helpers/Mention.cs @@ -24,7 +24,7 @@ namespace Discord { return _userRegex.Replace(text, new MatchEvaluator(e => { - string id = e.Value.Substring(2, e.Value.Length - 3); + long id = IdConvert.ToLong(e.Value.Substring(2, e.Value.Length - 3)); var user = client.Users[id, server?.Id]; if (user != null) { @@ -40,7 +40,7 @@ namespace Discord { return _channelRegex.Replace(text, new MatchEvaluator(e => { - string id = e.Value.Substring(2, e.Value.Length - 3); + long id = IdConvert.ToLong(e.Value.Substring(2, e.Value.Length - 3)); var channel = client.Channels[id]; if (channel != null && channel.Server.Id == server.Id) { @@ -52,7 +52,7 @@ namespace Discord return '#' + e.Value; })); } - internal static string CleanRoleMentions(DiscordClient client, User user, Channel channel, string text, List roles = null) + /*internal static string CleanRoleMentions(DiscordClient client, User user, Channel channel, string text, List roles = null) { return _roleRegex.Replace(text, new MatchEvaluator(e => { @@ -60,6 +60,6 @@ namespace Discord roles.Add(channel.Server.EveryoneRole); return e.Value; })); - } + }*/ } } diff --git a/src/Discord.Net/Helpers/Reference.cs b/src/Discord.Net/Helpers/Reference.cs index 588ede92f..cdfe3a7dd 100644 --- a/src/Discord.Net/Helpers/Reference.cs +++ b/src/Discord.Net/Helpers/Reference.cs @@ -3,12 +3,12 @@ namespace Discord { internal struct Reference - where T : CachedObject - { + where T : CachedObject + { private Action _onCache, _onUncache; - private Func _getItem; - private string _id; - public string Id + private Func _getItem; + private long? _id; + public long? Id { get { return _id; } set @@ -24,14 +24,15 @@ namespace Discord get { var v = _value; //A little trickery to make this threadsafe + var id = _id; if (v != null && !_value.IsCached) { v = null; _value = null; } - if (v == null && _id != null) + if (v == null && id != null) { - v = _getItem(_id); + v = _getItem(id.Value); if (v != null && _onCache != null) _onCache(v); _value = v; @@ -55,9 +56,9 @@ namespace Discord } } - public Reference(Func onUpdate, Action onCache = null, Action onUncache = null) + public Reference(Func onUpdate, Action onCache = null, Action onUncache = null) : this(null, onUpdate, onCache, onUncache) { } - public Reference(string id, Func getItem, Action onCache = null, Action onUncache = null) + public Reference(long? id, Func getItem, Action onCache = null, Action onUncache = null) { _id = id; _getItem = getItem; diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 3c8ed467c..a0191750f 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -6,15 +6,15 @@ using System.Linq; namespace Discord { - public sealed class Channel : CachedObject + public sealed class Channel : CachedObject { public sealed class PermissionOverwrite { public PermissionTarget TargetType { get; } - public string TargetId { get; } + public long TargetId { get; } public DualChannelPermissions Permissions { get; } - internal PermissionOverwrite(PermissionTarget targetType, string targetId, uint allow, uint deny) + internal PermissionOverwrite(PermissionTarget targetType, long targetId, uint allow, uint deny) { TargetType = targetType; TargetId = targetId; @@ -55,20 +55,20 @@ namespace Discord return _members.Select(x => x.Value); } } - private Dictionary _members; + private Dictionary _members; private bool _areMembersStale; /// Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. [JsonIgnore] - public IEnumerable Messages => _messages.Values; - private readonly ConcurrentDictionary _messages; + public IEnumerable Messages => _messages?.Values ?? Enumerable.Empty(); + private readonly ConcurrentDictionary _messages; /// Returns a collection of all custom permissions used for this channel. private static readonly PermissionOverwrite[] _initialPermissionsOverwrites = new PermissionOverwrite[0]; private PermissionOverwrite[] _permissionOverwrites; public IEnumerable PermissionOverwrites { get { return _permissionOverwrites; } internal set { _permissionOverwrites = value.ToArray(); } } - internal Channel(DiscordClient client, string id, string serverId, string recipientId) + internal Channel(DiscordClient client, long id, long? serverId, long? recipientId) : base(client, id) { _server = new Reference(serverId, @@ -92,7 +92,8 @@ namespace Discord _areMembersStale = true; //Local Cache - _messages = new ConcurrentDictionary(); + if (client.Config.MessageCacheLength > 0) + _messages = new ConcurrentDictionary(); } internal override void LoadReferences() { @@ -164,10 +165,10 @@ namespace Discord { if (IsPrivate) { - _members = new Dictionary() + _members = new Dictionary() { { _client.CurrentUserId, _client.PrivateUser }, - { _recipient.Id, _recipient.Value } + { _recipient.Id.Value, _recipient.Value } }; } else if (Type == ChannelType.Text) @@ -205,6 +206,6 @@ namespace Discord public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; public override int GetHashCode() => unchecked(Id.GetHashCode() + 5658); - public override string ToString() => Name ?? Id; + public override string ToString() => Name ?? IdConvert.ToString(Id); } } diff --git a/src/Discord.Net/Models/GlobalUser.cs b/src/Discord.Net/Models/GlobalUser.cs index 956e8ad13..4325dd7d4 100644 --- a/src/Discord.Net/Models/GlobalUser.cs +++ b/src/Discord.Net/Models/GlobalUser.cs @@ -6,9 +6,9 @@ using System.Linq; namespace Discord { - public sealed class GlobalUser : CachedObject + public sealed class GlobalUser : CachedObject { - private readonly ConcurrentDictionary _users; + private readonly ConcurrentDictionary _users; /// Returns the email for this user. /// This field is only ever populated for the current logged in user. @@ -35,12 +35,12 @@ namespace Discord /// Returns a collection of all server-specific data for every server this user is a member of. [JsonIgnore] - internal IEnumerable Memberships => _users.Select(x => _client.Users[Id, x.Key]); + internal IEnumerable Memberships => _users.Select(x => x.Value); - internal GlobalUser(DiscordClient client, string id) + internal GlobalUser(DiscordClient client, long id) : base(client, id) { - _users = new ConcurrentDictionary(); + _users = new ConcurrentDictionary(); } internal override void LoadReferences() { } internal override void UnloadReferences() @@ -56,10 +56,10 @@ namespace Discord IsVerified = model.IsVerified; } - internal void AddUser(User user) => _users.TryAdd(user.UniqueId, user); + internal void AddUser(User user) => _users.TryAdd(user.Server?.Id ?? 0, user); internal void RemoveUser(User user) { - if (_users.TryRemove(user.UniqueId, out user)) + if (_users.TryRemove(user.Server?.Id ?? 0, out user)) CheckUser(); } internal void CheckUser() @@ -70,6 +70,6 @@ namespace Discord public override bool Equals(object obj) => obj is GlobalUser && (obj as GlobalUser).Id == Id; public override int GetHashCode() => unchecked(Id.GetHashCode() + 7891); - public override string ToString() => Id; + public override string ToString() => IdConvert.ToString(Id); } } diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs index 46660b566..4601dadf5 100644 --- a/src/Discord.Net/Models/Invite.cs +++ b/src/Discord.Net/Models/Invite.cs @@ -1,19 +1,18 @@ using System; using Discord.API; -using Newtonsoft.Json; namespace Discord { - public sealed class Invite : CachedObject + public sealed class Invite : CachedObject { public sealed class ServerInfo { /// Returns the unique identifier of this server. - public string Id { get; } + public long Id { get; } /// Returns the name of this server. public string Name { get; } - internal ServerInfo(string id, string name) + internal ServerInfo(long id, string name) { Id = id; Name = name; @@ -22,11 +21,11 @@ namespace Discord public sealed class ChannelInfo { /// Returns the unique identifier of this channel. - public string Id { get; } + public long Id { get; } /// Returns the name of this channel. public string Name { get; } - internal ChannelInfo(string id, string name) + internal ChannelInfo(long id, string name) { Id = id; Name = name; @@ -35,17 +34,17 @@ namespace Discord public sealed class InviterInfo { /// Returns the unique identifier for this user. - public string Id { get; } + public long Id { get; } /// Returns the name of this user. public string Name { get; } /// Returns the by-name unique identifier for this user. - public string Discriminator { get; } + public int Discriminator { get; } /// Returns the unique identifier for this user's avatar. public string AvatarId { get; } /// Returns the full path to this user's avatar. public string AvatarUrl => User.GetAvatarUrl(Id, AvatarId); - internal InviterInfo(string id, string name, string discriminator, string avatarId) + internal InviterInfo(long id, string name, int discriminator, string avatarId) { Id = id; Name = name; @@ -76,9 +75,9 @@ namespace Discord public DateTime CreatedAt { get; private set; } /// Returns a URL for this invite using XkcdCode if available or Id if not. - public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Id); + public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Id.ToString()); - internal Invite(DiscordClient client, string code, string xkcdPass, string serverId, string inviterId, string channelId) + internal Invite(DiscordClient client, string code, string xkcdPass) : base(client, code) { XkcdCode = xkcdPass; @@ -93,7 +92,7 @@ namespace Discord if (model.Channel != null) Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); if (model.Inviter != null) - Inviter = new InviterInfo(model.Inviter.Id, model.Inviter.Username, model.Inviter.Discriminator, model.Inviter.Avatar); + Inviter = new InviterInfo(model.Inviter.Id, model.Inviter.Username, model.Inviter.Discriminator.Value, model.Inviter.Avatar); } internal void Update(InviteInfo model) { diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs index a4c7e6864..5f23ecea6 100644 --- a/src/Discord.Net/Models/Message.cs +++ b/src/Discord.Net/Models/Message.cs @@ -6,7 +6,7 @@ using System.Linq; namespace Discord { - public sealed class Message : CachedObject + public sealed class Message : CachedObject { public sealed class Attachment : File { @@ -90,9 +90,6 @@ namespace Discord Height = height; } } - - /// Returns the local unique identifier for this message. - public string Nonce { get; internal set; } /// Returns true if the logged-in user was mentioned. /// This is not set to true if the user was mentioned with @everyone (see IsMentioningEverone). @@ -145,7 +142,7 @@ namespace Discord public User User => _user.Value; private readonly Reference _user; - internal Message(DiscordClient client, string id, string channelId, string userId) + internal Message(DiscordClient client, long id, long channelId, long userId) : base(client, id) { _channel = new Reference(channelId, diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index 8e4eda702..168ff5123 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -5,7 +5,7 @@ using System.Linq; namespace Discord { - public sealed class Role : CachedObject + public sealed class Role : CachedObject { /// Returns the name of this role. public string Name { get; private set; } @@ -33,7 +33,7 @@ namespace Discord public IEnumerable Members => _server.Id != null ? (IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this))) : new User[0]; //TODO: Add local members cache - internal Role(DiscordClient client, string id, string serverId) + internal Role(DiscordClient client, long id, long serverId) : base(client, id) { _server = new Reference(serverId, x => _client.Servers[x], x => x.AddRole(this), x => x.RemoveRole(this)); @@ -72,6 +72,6 @@ namespace Discord public override bool Equals(object obj) => obj is Role && (obj as Role).Id == Id; public override int GetHashCode() => unchecked(Id.GetHashCode() + 6653); - public override string ToString() => Name ?? Id; + public override string ToString() => Name ?? IdConvert.ToString(Id); } } diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index e3f1afbc7..99b85feb7 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -7,7 +7,7 @@ using System.Linq; namespace Discord { - public sealed class Server : CachedObject + public sealed class Server : CachedObject { /// Returns the name of this channel. public string Name { get; private set; } @@ -28,7 +28,7 @@ namespace Discord /// Returns the user that first created this server. [JsonIgnore] public User Owner { get; private set; } - private string _ownerId; + private long _ownerId; /// Returns the AFK voice channel for this server (see AFKTimeout). [JsonIgnore] @@ -41,8 +41,8 @@ namespace Discord /// Returns a collection of the ids of all users banned on this server. [JsonIgnore] - public IEnumerable Bans => _bans.Select(x => x.Key); - private ConcurrentDictionary _bans; + public IEnumerable Bans => _bans.Select(x => x.Key); + private ConcurrentDictionary _bans; /// Returns a collection of all channels within this server. [JsonIgnore] @@ -53,12 +53,12 @@ namespace Discord /// Returns a collection of all channels within this server. [JsonIgnore] public IEnumerable VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); - private ConcurrentDictionary _channels; + private ConcurrentDictionary _channels; /// Returns a collection of all users within this server with their server-specific data. [JsonIgnore] public IEnumerable Members => _members.Select(x => x.Value); - private ConcurrentDictionary _members; + private ConcurrentDictionary _members; /// Return the the role representing all users in a server. [JsonIgnore] @@ -66,20 +66,20 @@ namespace Discord /// Returns a collection of all roles within this server. [JsonIgnore] public IEnumerable Roles => _roles.Select(x => x.Value); - private ConcurrentDictionary _roles; + private ConcurrentDictionary _roles; - internal Server(DiscordClient client, string id) + internal Server(DiscordClient client, long id) : base(client, id) { _afkChannel = new Reference(x => _client.Channels[x]); //Global Cache - _channels = new ConcurrentDictionary(); - _members = new ConcurrentDictionary(); - _roles = new ConcurrentDictionary(); + _channels = new ConcurrentDictionary(); + _members = new ConcurrentDictionary(); + _roles = new ConcurrentDictionary(); //Local Cache - _bans = new ConcurrentDictionary(); + _bans = new ConcurrentDictionary(); } internal override void LoadReferences() { @@ -129,7 +129,7 @@ namespace Discord JoinedAt = model.JoinedAt.Value; if (model.OwnerId != null && _ownerId != model.OwnerId) { - _ownerId = model.OwnerId; + _ownerId = model.OwnerId.Value; Owner = _client.Users[_ownerId, Id]; } if (model.Region != null) @@ -178,11 +178,11 @@ namespace Discord } } - internal void AddBan(string banId) + internal void AddBan(long banId) { _bans.TryAdd(banId, true); } - internal bool RemoveBan(string banId) + internal bool RemoveBan(long banId) { bool ignored; return _bans.TryRemove(banId, out ignored); @@ -254,6 +254,6 @@ namespace Discord public override bool Equals(object obj) => obj is Server && (obj as Server).Id == Id; public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175); - public override string ToString() => Name ?? Id; + public override string ToString() => Name ?? IdConvert.ToString(Id); } } diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index 1e25eaf8e..e53dd7a76 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -20,20 +20,34 @@ namespace Discord } } - public class User : CachedObject + public class User : CachedObject { - internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId; - internal static string GetAvatarUrl(string userId, string avatarId) => avatarId != null ? Endpoints.UserAvatar(userId, avatarId) : null; + internal struct CompositeKey : IEquatable + { + public long ServerId, UserId; + public CompositeKey(long userId, long? serverId) + { + ServerId = serverId ?? 0; + UserId = userId; + } + + public bool Equals(CompositeKey other) + => UserId == other.UserId && ServerId == other.ServerId; + public override int GetHashCode() + => unchecked(ServerId.GetHashCode() + UserId.GetHashCode() + 23); + } + + internal static string GetAvatarUrl(long userId, string avatarId) => avatarId != null ? Endpoints.UserAvatar(userId, avatarId) : null; - private ConcurrentDictionary _permissions; + private ConcurrentDictionary _permissions; private ServerPermissions _serverPermissions; /// Returns a unique identifier combining this user's id with its server's. - internal string UniqueId => GetId(Id, _server.Id); + internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); /// Returns the name of this user on this server. public string Name { get; private set; } /// Returns a by-name unique identifier separating this user from others with the same name. - public string Discriminator { get; private set; } + public short Discriminator { get; private set; } /// Returns the unique identifier for this user's current avatar. public string AvatarId { get; private set; } /// Returns the URL to this user's current avatar. @@ -47,6 +61,7 @@ namespace Discord public bool IsServerDeafened { get; private set; } public bool IsServerSuppressed { get; private set; } public bool IsSpeaking { get; internal set; } + public bool IsPrivate => _server.Id == null; public string SessionId { get; private set; } public string Token { get; private set; } @@ -79,7 +94,7 @@ namespace Discord [JsonIgnore] public IEnumerable Roles => _roles.Select(x => x.Value); - private Dictionary _roles; + private Dictionary _roles; /// Returns a collection of all messages this user has sent on this server that are still in cache. [JsonIgnore] @@ -118,7 +133,7 @@ namespace Discord } } - internal User(DiscordClient client, string id, string serverId) + internal User(DiscordClient client, long id, long? serverId) : base(client, id) { _globalUser = new Reference(id, @@ -140,13 +155,13 @@ namespace Discord x.CurrentUser = null; }); _voiceChannel = new Reference(x => _client.Channels[x]); - _roles = new Dictionary(); + _roles = new Dictionary(); Status = UserStatus.Offline; //_channels = new ConcurrentDictionary(); if (serverId != null) { - _permissions = new ConcurrentDictionary(); + _permissions = new ConcurrentDictionary(); _serverPermissions = new ServerPermissions(); } @@ -169,7 +184,7 @@ namespace Discord if (model.Avatar != null) AvatarId = model.Avatar; if (model.Discriminator != null) - Discriminator = model.Discriminator; + Discriminator = model.Discriminator.Value; if (model.Username != null) Name = model.Username; } @@ -243,11 +258,11 @@ namespace Discord } private void UpdateRoles(IEnumerable roles) { - Dictionary newRoles; + Dictionary newRoles; if (roles != null) newRoles = roles.ToDictionary(x => x.Id, x => x); else - newRoles = new Dictionary(); + newRoles = new Dictionary(); if (_server.Id != null) { @@ -378,6 +393,6 @@ namespace Discord public override bool Equals(object obj) => obj is User && (obj as User).Id == Id; public override int GetHashCode() => unchecked(Id.GetHashCode() + 7230); - public override string ToString() => Name ?? Id; + public override string ToString() => Name ?? IdConvert.ToString(Id); } } \ No newline at end of file diff --git a/src/Discord.Net/Net/WebSockets/DataWebSocket.cs b/src/Discord.Net/Net/WebSockets/DataWebSocket.cs index 7e59565d7..450a9cd5c 100644 --- a/src/Discord.Net/Net/WebSockets/DataWebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/DataWebSocket.cs @@ -128,20 +128,20 @@ namespace Discord.Net.WebSockets QueueMessage(updateStatus); } - public void SendJoinVoice(string serverId, string channelId) + public void SendJoinVoice(long serverId, long channelId) { var joinVoice = new JoinVoiceCommand(); joinVoice.Payload.ServerId = serverId; joinVoice.Payload.ChannelId = channelId; QueueMessage(joinVoice); } - public void SendLeaveVoice(string serverId) + public void SendLeaveVoice(long serverId) { var leaveVoice = new JoinVoiceCommand(); leaveVoice.Payload.ServerId = serverId; QueueMessage(leaveVoice); } - public void SendGetUsers(string serverId, string query = "", int limit = 0) + public void SendGetUsers(long serverId, string query = "", int limit = 0) { var getOfflineUsers = new GetUsersCommand(); getOfflineUsers.Payload.ServerId = serverId; diff --git a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.Events.cs b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.Events.cs index 1146d6cc2..dee9ffb18 100644 --- a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.Events.cs +++ b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.Events.cs @@ -4,9 +4,9 @@ namespace Discord.Net.WebSockets { internal sealed class IsTalkingEventArgs : EventArgs { - public readonly string UserId; + public readonly long UserId; public readonly bool IsSpeaking; - internal IsTalkingEventArgs(string userId, bool isTalking) + internal IsTalkingEventArgs(long userId, bool isTalking) { UserId = userId; IsSpeaking = isTalking; @@ -16,14 +16,14 @@ namespace Discord.Net.WebSockets internal partial class VoiceWebSocket { public event EventHandler IsSpeaking; - private void RaiseIsSpeaking(string userId, bool isSpeaking) + private void RaiseIsSpeaking(long userId, bool isSpeaking) { if (IsSpeaking != null) IsSpeaking(this, new IsTalkingEventArgs(userId, isSpeaking)); } public event EventHandler OnPacket; - internal void RaiseOnPacket(string userId, string channelId, byte[] buffer, int offset, int count) + internal void RaiseOnPacket(long userId, long channelId, byte[] buffer, int offset, int count) { if (OnPacket != null) OnPacket(this, new VoicePacketEventArgs(userId, channelId, buffer, offset, count)); diff --git a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs index 50e04fbdb..b6a9a6446 100644 --- a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs @@ -26,7 +26,7 @@ namespace Discord.Net.WebSockets private OpusEncoder _encoder; private readonly ConcurrentDictionary _decoders; private uint _ssrc; - private ConcurrentDictionary _ssrcMapping; + private ConcurrentDictionary _ssrcMapping; private VoiceBuffer _sendBuffer; private UdpClient _udp; @@ -34,12 +34,13 @@ namespace Discord.Net.WebSockets private bool _isEncrypted; private byte[] _secretKey, _encodingBuffer; private ushort _sequence; - private string _serverId, _channelId, _userId, _sessionId, _token, _encryptionMode; + private long? _serverId, _channelId, _userId; + private string _sessionId, _token, _encryptionMode; private Thread _sendThread, _receiveThread; - public string CurrentServerId => _serverId; - public string CurrentChannelId => _channelId; + public long? CurrentServerId => _serverId; + public long? CurrentChannelId => _channelId; public VoiceBuffer OutputBuffer => _sendBuffer; public VoiceWebSocket(DiscordWSClient client) @@ -49,19 +50,19 @@ namespace Discord.Net.WebSockets _decoders = new ConcurrentDictionary(); _targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames _encodingBuffer = new byte[MaxOpusSize]; - _ssrcMapping = new ConcurrentDictionary(); + _ssrcMapping = new ConcurrentDictionary(); _encoder = new OpusEncoder(48000, 1, 20, Opus.Application.Audio); _sendBuffer = new VoiceBuffer((int)Math.Ceiling(client.Config.VoiceBufferLength / (double)_encoder.FrameLength), _encoder.FrameSize); } - public Task SetChannel(string serverId, string channelId) + public Task SetChannel(long serverId, long channelId) { _serverId = serverId; _channelId = channelId; return base.BeginConnect(); } - public async Task Login(string userId, string sessionId, string token, CancellationToken cancelToken) + public async Task Login(long userId, string sessionId, string token, CancellationToken cancelToken) { if ((WebSocketState)_state == WebSocketState.Connected) { @@ -108,10 +109,10 @@ namespace Discord.Net.WebSockets _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); VoiceLoginCommand msg = new VoiceLoginCommand(); - msg.Payload.ServerId = _serverId; + msg.Payload.ServerId = _serverId.Value; msg.Payload.SessionId = _sessionId; msg.Payload.Token = _token; - msg.Payload.UserId = _userId; + msg.Payload.UserId = _userId.Value; QueueMessage(msg); List tasks = new List(); @@ -232,16 +233,9 @@ namespace Discord.Net.WebSockets else { //Parse RTP Data - if (packetLength < 12) - return; - - byte flags = packet[0]; - if (flags != 0x80) - return; - - byte payloadType = packet[1]; - if (payloadType != 0x78) - return; + if (packetLength < 12) return; + if (packet[0] != 0x80) return; //Flags + if (packet[1] != 0x78) return; //Payload Type ushort sequenceNumber = (ushort)((packet[2] << 8) | packet[3] << 0); @@ -278,9 +272,9 @@ namespace Discord.Net.WebSockets /*if (_logLevel >= LogMessageSeverity.Debug) RaiseOnLog(LogMessageSeverity.Debug, $"Received {buffer.Length - 12} bytes.");*/ - string userId; + long userId; if (_ssrcMapping.TryGetValue(ssrc, out userId)) - RaiseOnPacket(userId, _channelId, result, resultOffset, resultLength); + RaiseOnPacket(userId, _channelId.Value, result, resultOffset, resultLength); } } } @@ -302,9 +296,9 @@ namespace Discord.Net.WebSockets byte[] frame = new byte[_encoder.FrameSize]; byte[] encodedFrame = new byte[MaxOpusSize]; - byte[] udpPacket, nonce = null; + byte[] voicePacket, pingPacket, nonce = null; uint timestamp = 0; - double nextTicks = 0.0; + double nextTicks = 0.0, nextPingTicks = 0.0; double ticksPerMillisecond = Stopwatch.Frequency / 1000.0; double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength; double spinLockThreshold = 3 * ticksPerMillisecond; @@ -314,38 +308,58 @@ namespace Discord.Net.WebSockets if (_isEncrypted) { nonce = new byte[24]; - udpPacket = new byte[MaxOpusSize + 12 + 16]; + voicePacket = new byte[MaxOpusSize + 12 + 16]; } else - udpPacket = new byte[MaxOpusSize + 12]; + voicePacket = new byte[MaxOpusSize + 12]; + + pingPacket = new byte[8]; + pingPacket[0] = 0x80; //Flags; + pingPacket[1] = 0x78; //Payload Type + pingPacket[3] = 0x00; //Length + pingPacket[4] = 0x01; //Length (1*8 bytes) + pingPacket[5] = (byte)((_ssrc >> 24) & 0xFF); + pingPacket[6] = (byte)((_ssrc >> 16) & 0xFF); + pingPacket[7] = (byte)((_ssrc >> 8) & 0xFF); + pingPacket[8] = (byte)((_ssrc >> 0) & 0xFF); + if (_isEncrypted) + { + Buffer.BlockCopy(pingPacket, 0, nonce, 0, 8); + int ret = Sodium.Encrypt(pingPacket, 8, encodedFrame, 0, nonce, _secretKey); + if (ret != 0) + throw new InvalidOperationException("Failed to encrypt ping packet"); + pingPacket = new byte[ret]; + Buffer.BlockCopy(encodedFrame, 0, pingPacket, 0, ret); + } int rtpPacketLength = 0; - udpPacket[0] = 0x80; //Flags; - udpPacket[1] = 0x78; //Payload Type - udpPacket[8] = (byte)((_ssrc >> 24) & 0xFF); - udpPacket[9] = (byte)((_ssrc >> 16) & 0xFF); - udpPacket[10] = (byte)((_ssrc >> 8) & 0xFF); - udpPacket[11] = (byte)((_ssrc >> 0) & 0xFF); + voicePacket[0] = 0x80; //Flags; + voicePacket[1] = 0xC9; //Payload Type + voicePacket[8] = (byte)((_ssrc >> 24) & 0xFF); + voicePacket[9] = (byte)((_ssrc >> 16) & 0xFF); + voicePacket[10] = (byte)((_ssrc >> 8) & 0xFF); + voicePacket[11] = (byte)((_ssrc >> 0) & 0xFF); if (_isEncrypted) - Buffer.BlockCopy(udpPacket, 0, nonce, 0, 12); + Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12); while (!cancelToken.IsCancellationRequested) { double ticksToNextFrame = nextTicks - sw.ElapsedTicks; if (ticksToNextFrame <= 0.0) { - while (sw.ElapsedTicks > nextTicks) + long currentTicks = sw.ElapsedTicks; + while (currentTicks > nextTicks) { if (_sendBuffer.Pop(frame)) { ushort sequence = unchecked(_sequence++); - udpPacket[2] = (byte)((sequence >> 8) & 0xFF); - udpPacket[3] = (byte)((sequence >> 0) & 0xFF); - udpPacket[4] = (byte)((timestamp >> 24) & 0xFF); - udpPacket[5] = (byte)((timestamp >> 16) & 0xFF); - udpPacket[6] = (byte)((timestamp >> 8) & 0xFF); - udpPacket[7] = (byte)((timestamp >> 0) & 0xFF); + voicePacket[2] = (byte)((sequence >> 8) & 0xFF); + voicePacket[3] = (byte)((sequence >> 0) & 0xFF); + voicePacket[4] = (byte)((timestamp >> 24) & 0xFF); + voicePacket[5] = (byte)((timestamp >> 16) & 0xFF); + voicePacket[6] = (byte)((timestamp >> 8) & 0xFF); + voicePacket[7] = (byte)((timestamp >> 0) & 0xFF); //Encode int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame); @@ -353,23 +367,28 @@ namespace Discord.Net.WebSockets //Encrypt if (_isEncrypted) { - Buffer.BlockCopy(udpPacket, 2, nonce, 2, 6); //Update nonce - int ret = Sodium.Encrypt(encodedFrame, encodedLength, udpPacket, 12, nonce, _secretKey); + Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce + int ret = Sodium.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey); if (ret != 0) continue; rtpPacketLength = encodedLength + 12 + 16; } else { - Buffer.BlockCopy(encodedFrame, 0, udpPacket, 12, encodedLength); + Buffer.BlockCopy(encodedFrame, 0, voicePacket, 12, encodedLength); rtpPacketLength = encodedLength + 12; } - _udp.Send(udpPacket, rtpPacketLength); + _udp.Send(voicePacket, rtpPacketLength); } timestamp = unchecked(timestamp + samplesPerFrame); nextTicks += ticksPerFrame; } - } + if (currentTicks > nextPingTicks) + { + _udp.Send(pingPacket, pingPacket.Length); + nextPingTicks = currentTicks + 5 * Stopwatch.Frequency; + } + } //Dont sleep if we need to output audio in the next spinLockThreshold else if (ticksToNextFrame > spinLockThreshold) {