| @@ -4,7 +4,7 @@ namespace Discord.Commands.Permissions.Userlist | |||||
| { | { | ||||
| public class BlacklistService : UserlistService | public class BlacklistService : UserlistService | ||||
| { | { | ||||
| public BlacklistService(IEnumerable<string> initialList = null) | |||||
| public BlacklistService(IEnumerable<long> initialList = null) | |||||
| : base(initialList) | : base(initialList) | ||||
| { | { | ||||
| } | } | ||||
| @@ -7,18 +7,18 @@ namespace Discord.Commands.Permissions.Userlist | |||||
| { | { | ||||
| public class UserlistService : IService | public class UserlistService : IService | ||||
| { | { | ||||
| protected readonly ConcurrentDictionary<string, bool> _userList; | |||||
| protected readonly ConcurrentDictionary<long, bool> _userList; | |||||
| private DiscordClient _client; | private DiscordClient _client; | ||||
| public DiscordClient Client => _client; | public DiscordClient Client => _client; | ||||
| public IEnumerable<string> UserIds => _userList.Select(x => x.Key); | |||||
| public IEnumerable<long> UserIds => _userList.Select(x => x.Key); | |||||
| public UserlistService(IEnumerable<string> initialList = null) | |||||
| public UserlistService(IEnumerable<long> initialList = null) | |||||
| { | { | ||||
| if (initialList != null) | if (initialList != null) | ||||
| _userList = new ConcurrentDictionary<string, bool>(initialList.Select(x => new KeyValuePair<string, bool>(x, true))); | |||||
| _userList = new ConcurrentDictionary<long, bool>(initialList.Select(x => new KeyValuePair<long, bool>(x, true))); | |||||
| else | else | ||||
| _userList = new ConcurrentDictionary<string, bool>(); | |||||
| _userList = new ConcurrentDictionary<long, bool>(); | |||||
| } | } | ||||
| public void Add(User user) | public void Add(User user) | ||||
| @@ -26,9 +26,9 @@ namespace Discord.Commands.Permissions.Userlist | |||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| _userList[user.Id] = true; | _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; | _userList[userId] = true; | ||||
| } | } | ||||
| public bool Remove(User user) | public bool Remove(User user) | ||||
| @@ -37,9 +37,9 @@ namespace Discord.Commands.Permissions.Userlist | |||||
| bool ignored; | bool ignored; | ||||
| return _userList.TryRemove(user.Id, out 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; | bool ignored; | ||||
| return _userList.TryRemove(userId, out ignored); | return _userList.TryRemove(userId, out ignored); | ||||
| } | } | ||||
| @@ -4,7 +4,7 @@ namespace Discord.Commands.Permissions.Userlist | |||||
| { | { | ||||
| public class WhitelistService : UserlistService | public class WhitelistService : UserlistService | ||||
| { | { | ||||
| public WhitelistService(IEnumerable<string> initialList = null) | |||||
| public WhitelistService(IEnumerable<long> initialList = null) | |||||
| : base(initialList) | : base(initialList) | ||||
| { | { | ||||
| } | } | ||||
| @@ -47,9 +47,9 @@ namespace Discord.Modules | |||||
| private readonly string _name, _id; | private readonly string _name, _id; | ||||
| private readonly FilterType _filterType; | private readonly FilterType _filterType; | ||||
| private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate; | private readonly bool _useServerWhitelist, _useChannelWhitelist, _allowAll, _allowPrivate; | ||||
| private readonly ConcurrentDictionary<string, Server> _enabledServers; | |||||
| private readonly ConcurrentDictionary<string, Channel> _enabledChannels; | |||||
| private readonly ConcurrentDictionary<string, int> _indirectServers; | |||||
| private readonly ConcurrentDictionary<long, Server> _enabledServers; | |||||
| private readonly ConcurrentDictionary<long, Channel> _enabledChannels; | |||||
| private readonly ConcurrentDictionary<long, int> _indirectServers; | |||||
| public DiscordClient Client => _client; | public DiscordClient Client => _client; | ||||
| public string Name => _name; | public string Name => _name; | ||||
| @@ -70,9 +70,9 @@ namespace Discord.Modules | |||||
| _useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist); | _useChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist); | ||||
| _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); | _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); | ||||
| _enabledServers = new ConcurrentDictionary<string, Server>(); | |||||
| _enabledChannels = new ConcurrentDictionary<string, Channel>(); | |||||
| _indirectServers = new ConcurrentDictionary<string, int>(); | |||||
| _enabledServers = new ConcurrentDictionary<long, Server>(); | |||||
| _enabledChannels = new ConcurrentDictionary<long, Channel>(); | |||||
| _indirectServers = new ConcurrentDictionary<long, int>(); | |||||
| if (_allowAll || _useServerWhitelist) //Server-only events | if (_allowAll || _useServerWhitelist) //Server-only events | ||||
| { | { | ||||
| @@ -76,6 +76,15 @@ | |||||
| <Compile Include="..\Discord.Net\API\Channels.cs"> | <Compile Include="..\Discord.Net\API\Channels.cs"> | ||||
| <Link>API\Channels.cs</Link> | <Link>API\Channels.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\API\Converters\LongCollectionConverter.cs"> | |||||
| <Link>API\Converters\LongCollectionConverter.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\API\Converters\LongStringConverter.cs"> | |||||
| <Link>API\Converters\LongStringConverter.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\API\Converters\ShortStringConverter.cs"> | |||||
| <Link>API\Converters\ShortStringConverter.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\API\Endpoints.cs"> | <Compile Include="..\Discord.Net\API\Endpoints.cs"> | ||||
| <Link>API\Endpoints.cs</Link> | <Link>API\Endpoints.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -220,6 +229,9 @@ | |||||
| <Compile Include="..\Discord.Net\Helpers\Format.cs"> | <Compile Include="..\Discord.Net\Helpers\Format.cs"> | ||||
| <Link>Helpers\Format.cs</Link> | <Link>Helpers\Format.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\Helpers\IdConvert.cs"> | |||||
| <Link>Helpers\IdConvert.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\Helpers\Mention.cs"> | <Compile Include="..\Discord.Net\Helpers\Mention.cs"> | ||||
| <Link>Helpers\Mention.cs</Link> | <Link>Helpers\Mention.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections; | using System.Collections; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -12,9 +13,11 @@ namespace Discord.API | |||||
| public class ChannelReference | public class ChannelReference | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long GuildId; | |||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name; | public string Name; | ||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| @@ -27,7 +30,8 @@ namespace Discord.API | |||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public string Type; | public string Type; | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("deny")] | [JsonProperty("deny")] | ||||
| public uint Deny; | public uint Deny; | ||||
| [JsonProperty("allow")] | [JsonProperty("allow")] | ||||
| @@ -35,7 +39,8 @@ namespace Discord.API | |||||
| } | } | ||||
| [JsonProperty("last_message_id")] | [JsonProperty("last_message_id")] | ||||
| public string LastMessageId; | |||||
| [JsonConverter(typeof(NullableLongStringConverter))] | |||||
| public long? LastMessageId; | |||||
| [JsonProperty("is_private")] | [JsonProperty("is_private")] | ||||
| public bool IsPrivate; | public bool IsPrivate; | ||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| @@ -59,7 +64,8 @@ namespace Discord.API | |||||
| public class CreatePMChannelRequest | public class CreatePMChannelRequest | ||||
| { | { | ||||
| [JsonProperty("recipient_id")] | [JsonProperty("recipient_id")] | ||||
| public string RecipientId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long RecipientId; | |||||
| } | } | ||||
| public class CreateChannelResponse : ChannelInfo { } | public class CreateChannelResponse : ChannelInfo { } | ||||
| @@ -82,7 +88,8 @@ namespace Discord.API | |||||
| public sealed class Channel | public sealed class Channel | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public uint Position; | public uint Position; | ||||
| } | } | ||||
| @@ -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<long>); | |||||
| } | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
| { | |||||
| List<long> result = new List<long>(); | |||||
| 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<long>)value) | |||||
| writer.WriteValue(IdConvert.ToString(v)); | |||||
| writer.WriteEndArray(); | |||||
| } | |||||
| } | |||||
| } | |||||
| internal class LongArrayStringConverter : JsonConverter | |||||
| { | |||||
| public override bool CanConvert(Type objectType) | |||||
| { | |||||
| return objectType == typeof(IEnumerable<long[]>); | |||||
| } | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
| { | |||||
| List<long> result = new List<long>(); | |||||
| 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(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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)); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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)); | |||||
| } | |||||
| }*/ | |||||
| } | |||||
| @@ -12,33 +12,35 @@ | |||||
| public const string AuthLogout = "auth/logout"; | public const string AuthLogout = "auth/logout"; | ||||
| public const string Channels = "channels"; | 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 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 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 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 static string UserMe => $"users/@me"; | ||||
| public const string Voice = "voice"; | public const string Voice = "voice"; | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| @@ -17,7 +18,8 @@ namespace Discord.API | |||||
| public sealed class PageData | public sealed class PageData | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name; | public string Name; | ||||
| [JsonProperty("url")] | [JsonProperty("url")] | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -12,9 +13,11 @@ namespace Discord.API | |||||
| public class MemberReference | public class MemberReference | ||||
| { | { | ||||
| [JsonProperty("user_id")] | [JsonProperty("user_id")] | ||||
| public string UserId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long UserId; | |||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long GuildId; | |||||
| [JsonProperty("user")] | [JsonProperty("user")] | ||||
| private UserReference _user; | private UserReference _user; | ||||
| @@ -33,7 +36,8 @@ namespace Discord.API | |||||
| [JsonProperty("joined_at")] | [JsonProperty("joined_at")] | ||||
| public DateTime? JoinedAt; | public DateTime? JoinedAt; | ||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public string[] Roles; | |||||
| [JsonConverter(typeof(LongArrayStringConverter))] | |||||
| public long[] Roles; | |||||
| } | } | ||||
| public class ExtendedMemberInfo : MemberInfo | public class ExtendedMemberInfo : MemberInfo | ||||
| { | { | ||||
| @@ -49,12 +53,14 @@ namespace Discord.API | |||||
| [JsonProperty("status")] | [JsonProperty("status")] | ||||
| public string Status; | public string Status; | ||||
| [JsonProperty("roles")] //TODO: Might be temporary | [JsonProperty("roles")] //TODO: Might be temporary | ||||
| public string[] Roles; | |||||
| [JsonConverter(typeof(LongArrayStringConverter))] | |||||
| public long[] Roles; | |||||
| } | } | ||||
| public class VoiceMemberInfo : MemberReference | public class VoiceMemberInfo : MemberReference | ||||
| { | { | ||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public string ChannelId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ChannelId; | |||||
| [JsonProperty("session_id")] | [JsonProperty("session_id")] | ||||
| public string SessionId; | public string SessionId; | ||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| @@ -79,7 +85,8 @@ namespace Discord.API | |||||
| [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public bool? Deaf; | public bool? Deaf; | ||||
| [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public IEnumerable<string> Roles; | |||||
| [JsonConverter(typeof(EnumerableLongStringConverter))] | |||||
| public IEnumerable<long> Roles; | |||||
| } | } | ||||
| public class PruneUsersResponse | public class PruneUsersResponse | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -12,11 +13,14 @@ namespace Discord.API | |||||
| public class MessageReference | public class MessageReference | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public string ChannelId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ChannelId; | |||||
| [JsonProperty("message_id")] | [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 | public class MessageInfo : MessageReference | ||||
| { | { | ||||
| @@ -104,7 +108,8 @@ namespace Discord.API | |||||
| [JsonProperty("content")] | [JsonProperty("content")] | ||||
| public string Content; | public string Content; | ||||
| [JsonProperty("mentions")] | [JsonProperty("mentions")] | ||||
| public IEnumerable<string> Mentions; | |||||
| [JsonConverter(typeof(EnumerableLongStringConverter))] | |||||
| public IEnumerable<long> Mentions; | |||||
| [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public string Nonce; | public string Nonce; | ||||
| [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)] | ||||
| @@ -118,7 +123,8 @@ namespace Discord.API | |||||
| [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public string Content; | public string Content; | ||||
| [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public IEnumerable<string> Mentions; | |||||
| [JsonConverter(typeof(EnumerableLongStringConverter))] | |||||
| public IEnumerable<long> Mentions; | |||||
| } | } | ||||
| public sealed class EditMessageResponse : MessageInfo { } | public sealed class EditMessageResponse : MessageInfo { } | ||||
| @@ -132,7 +138,8 @@ namespace Discord.API | |||||
| public class Data | public class Data | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string ServerId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ServerId; | |||||
| [JsonProperty("query")] | [JsonProperty("query")] | ||||
| public string Query; | public string Query; | ||||
| [JsonProperty("limit")] | [JsonProperty("limit")] | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API | namespace Discord.API | ||||
| @@ -10,7 +11,8 @@ namespace Discord.API | |||||
| internal sealed class SetChannelPermissionsRequest | internal sealed class SetChannelPermissionsRequest | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public string Type; | public string Type; | ||||
| [JsonProperty("allow")] | [JsonProperty("allow")] | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API | namespace Discord.API | ||||
| @@ -23,9 +24,11 @@ namespace Discord.API | |||||
| internal sealed class TypingStartEvent | internal sealed class TypingStartEvent | ||||
| { | { | ||||
| [JsonProperty("user_id")] | [JsonProperty("user_id")] | ||||
| public string UserId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long UserId; | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public string ChannelId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ChannelId; | |||||
| [JsonProperty("timestamp")] | [JsonProperty("timestamp")] | ||||
| public int Timestamp; | public int Timestamp; | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections; | using System.Collections; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -12,12 +13,17 @@ namespace Discord.API | |||||
| public class RoleReference | public class RoleReference | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long GuildId; | |||||
| [JsonProperty("role_id")] | [JsonProperty("role_id")] | ||||
| public string RoleId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long RoleId; | |||||
| } | } | ||||
| public class RoleInfo | public class RoleInfo | ||||
| { | { | ||||
| [JsonProperty("id")] | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("permissions")] | [JsonProperty("permissions")] | ||||
| public uint? Permissions; | public uint? Permissions; | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| @@ -28,8 +34,6 @@ namespace Discord.API | |||||
| public bool? Hoist; | public bool? Hoist; | ||||
| [JsonProperty("color")] | [JsonProperty("color")] | ||||
| public uint? Color; | public uint? Color; | ||||
| [JsonProperty("id")] | |||||
| public string Id; | |||||
| [JsonProperty("managed")] | [JsonProperty("managed")] | ||||
| public bool? Managed; | public bool? Managed; | ||||
| } | } | ||||
| @@ -57,7 +61,8 @@ namespace Discord.API | |||||
| public sealed class Role | public sealed class Role | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public uint Position; | public uint Position; | ||||
| } | } | ||||
| @@ -72,14 +77,16 @@ namespace Discord.API | |||||
| internal sealed class RoleCreateEvent | internal sealed class RoleCreateEvent | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long GuildId; | |||||
| [JsonProperty("role")] | [JsonProperty("role")] | ||||
| public RoleInfo Data; | public RoleInfo Data; | ||||
| } | } | ||||
| internal sealed class RoleUpdateEvent | internal sealed class RoleUpdateEvent | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long GuildId; | |||||
| [JsonProperty("role")] | [JsonProperty("role")] | ||||
| public RoleInfo Data; | public RoleInfo Data; | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| @@ -11,18 +12,21 @@ namespace Discord.API | |||||
| public class GuildReference | public class GuildReference | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name; | public string Name; | ||||
| } | } | ||||
| public class GuildInfo : GuildReference | public class GuildInfo : GuildReference | ||||
| { | { | ||||
| [JsonProperty("afk_channel_id")] | [JsonProperty("afk_channel_id")] | ||||
| public string AFKChannelId; | |||||
| [JsonConverter(typeof(NullableLongStringConverter))] | |||||
| public long? AFKChannelId; | |||||
| [JsonProperty("afk_timeout")] | [JsonProperty("afk_timeout")] | ||||
| public int? AFKTimeout; | public int? AFKTimeout; | ||||
| [JsonProperty("embed_channel_id")] | [JsonProperty("embed_channel_id")] | ||||
| public string EmbedChannelId; | |||||
| [JsonConverter(typeof(NullableLongStringConverter))] | |||||
| public long? EmbedChannelId; | |||||
| [JsonProperty("embed_enabled")] | [JsonProperty("embed_enabled")] | ||||
| public bool EmbedEnabled; | public bool EmbedEnabled; | ||||
| [JsonProperty("icon")] | [JsonProperty("icon")] | ||||
| @@ -30,7 +34,8 @@ namespace Discord.API | |||||
| [JsonProperty("joined_at")] | [JsonProperty("joined_at")] | ||||
| public DateTime? JoinedAt; | public DateTime? JoinedAt; | ||||
| [JsonProperty("owner_id")] | [JsonProperty("owner_id")] | ||||
| public string OwnerId; | |||||
| [JsonConverter(typeof(NullableLongStringConverter))] | |||||
| public long? OwnerId; | |||||
| [JsonProperty("region")] | [JsonProperty("region")] | ||||
| public string Region; | public string Region; | ||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| @@ -70,7 +75,8 @@ namespace Discord.API | |||||
| [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public string Icon; | public string Icon; | ||||
| [JsonProperty("afk_channel_id", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("afk_channel_id", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public string AFKChannelId; | |||||
| [JsonConverter(typeof(NullableLongStringConverter))] | |||||
| public long? AFKChannelId; | |||||
| [JsonProperty("afk_timeout", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("afk_timeout", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public int AFKTimeout; | public int AFKTimeout; | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| namespace Discord.API | namespace Discord.API | ||||
| @@ -12,9 +13,10 @@ namespace Discord.API | |||||
| [JsonProperty("username")] | [JsonProperty("username")] | ||||
| public string Username; | public string Username; | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public string Id; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long Id; | |||||
| [JsonProperty("discriminator")] | [JsonProperty("discriminator")] | ||||
| public string Discriminator; | |||||
| public short? Discriminator; | |||||
| [JsonProperty("avatar")] | [JsonProperty("avatar")] | ||||
| public string Avatar; | public string Avatar; | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| #pragma warning disable CS0649 | #pragma warning disable CS0649 | ||||
| #pragma warning disable CS0169 | #pragma warning disable CS0169 | ||||
| using Discord.API.Converters; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -22,7 +23,7 @@ namespace Discord.API | |||||
| } | } | ||||
| } | } | ||||
| public class GetIceResponse | |||||
| /*public class GetIceResponse | |||||
| { | { | ||||
| [JsonProperty("ttl")] | [JsonProperty("ttl")] | ||||
| public string TTL; | public string TTL; | ||||
| @@ -38,7 +39,7 @@ namespace Discord.API | |||||
| [JsonProperty("credential")] | [JsonProperty("credential")] | ||||
| public string Credential; | public string Credential; | ||||
| } | } | ||||
| } | |||||
| }*/ | |||||
| //Commands | //Commands | ||||
| internal sealed class JoinVoiceCommand : WebSocketMessage<JoinVoiceCommand.Data> | internal sealed class JoinVoiceCommand : WebSocketMessage<JoinVoiceCommand.Data> | ||||
| @@ -47,9 +48,11 @@ namespace Discord.API | |||||
| public class Data | public class Data | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string ServerId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ServerId; | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public string ChannelId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ChannelId; | |||||
| [JsonProperty("self_mute")] | [JsonProperty("self_mute")] | ||||
| public string SelfMute; | public string SelfMute; | ||||
| [JsonProperty("self_deaf")] | [JsonProperty("self_deaf")] | ||||
| @@ -61,7 +64,8 @@ namespace Discord.API | |||||
| internal sealed class VoiceServerUpdateEvent | internal sealed class VoiceServerUpdateEvent | ||||
| { | { | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public string GuildId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ServerId; | |||||
| [JsonProperty("endpoint")] | [JsonProperty("endpoint")] | ||||
| public string Endpoint; | public string Endpoint; | ||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| @@ -75,50 +79,52 @@ namespace Discord.API | |||||
| public class Data | public class Data | ||||
| { | { | ||||
| [JsonProperty("server_id")] | [JsonProperty("server_id")] | ||||
| public string ServerId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long ServerId; | |||||
| [JsonProperty("user_id")] | [JsonProperty("user_id")] | ||||
| public string UserId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long UserId; | |||||
| [JsonProperty("session_id")] | [JsonProperty("session_id")] | ||||
| public string SessionId; | public string SessionId; | ||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| public string Token; | public string Token; | ||||
| } | } | ||||
| } | } | ||||
| internal sealed class VoiceLogin2Command : WebSocketMessage<VoiceLogin2Command.Data> | |||||
| internal sealed class VoiceLogin2Command : WebSocketMessage<VoiceLogin2Command.Data> | |||||
| { | |||||
| 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<object> | |||||
| { | |||||
| public VoiceKeepAliveCommand() : base(3, null) { } | |||||
| } | |||||
| internal sealed class IsTalkingCommand : WebSocketMessage<IsTalkingCommand.Data> | |||||
| } | |||||
| internal sealed class VoiceKeepAliveCommand : WebSocketMessage<object> | |||||
| { | |||||
| public VoiceKeepAliveCommand() : base(3, null) { } | |||||
| } | |||||
| internal sealed class IsTalkingCommand : WebSocketMessage<IsTalkingCommand.Data> | |||||
| { | |||||
| 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) | //Events (Voice) | ||||
| public class VoiceReadyEvent | public class VoiceReadyEvent | ||||
| @@ -144,7 +150,8 @@ namespace Discord.API | |||||
| public class IsTalkingEvent | public class IsTalkingEvent | ||||
| { | { | ||||
| [JsonProperty("user_id")] | [JsonProperty("user_id")] | ||||
| public string UserId; | |||||
| [JsonConverter(typeof(LongStringConverter))] | |||||
| public long UserId; | |||||
| [JsonProperty("ssrc")] | [JsonProperty("ssrc")] | ||||
| public uint SSRC; | public uint SSRC; | ||||
| [JsonProperty("speaking")] | [JsonProperty("speaking")] | ||||
| @@ -6,7 +6,7 @@ namespace Discord.Audio | |||||
| { | { | ||||
| IDiscordVoiceBuffer OutputBuffer { get; } | IDiscordVoiceBuffer OutputBuffer { get; } | ||||
| Task JoinChannel(string channelId); | |||||
| Task JoinChannel(long channelId); | |||||
| void SendVoicePCM(byte[] data, int count); | void SendVoicePCM(byte[] data, int count); | ||||
| void ClearVoicePCM(); | void ClearVoicePCM(); | ||||
| @@ -52,39 +52,39 @@ namespace Discord | |||||
| => _rest.Post(Endpoints.AuthLogout); | => _rest.Post(Endpoints.AuthLogout); | ||||
| //Channels | //Channels | ||||
| public Task<CreateChannelResponse> CreateChannel(string serverId, string name, string channelType) | |||||
| public Task<CreateChannelResponse> 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 (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| if (channelType == null) throw new ArgumentNullException(nameof(channelType)); | if (channelType == null) throw new ArgumentNullException(nameof(channelType)); | ||||
| var request = new CreateChannelRequest { Name = name, Type = channelType }; | var request = new CreateChannelRequest { Name = name, Type = channelType }; | ||||
| return _rest.Post<CreateChannelResponse>(Endpoints.ServerChannels(serverId), request); | return _rest.Post<CreateChannelResponse>(Endpoints.ServerChannels(serverId), request); | ||||
| } | } | ||||
| public Task<CreateChannelResponse> CreatePMChannel(string myId, string recipientId) | |||||
| public Task<CreateChannelResponse> 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 }; | var request = new CreatePMChannelRequest { RecipientId = recipientId }; | ||||
| return _rest.Post<CreateChannelResponse>(Endpoints.UserChannels(myId), request); | return _rest.Post<CreateChannelResponse>(Endpoints.UserChannels(myId), request); | ||||
| } | } | ||||
| public Task<DestroyChannelResponse> DestroyChannel(string channelId) | |||||
| public Task<DestroyChannelResponse> DestroyChannel(long channelId) | |||||
| { | { | ||||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||||
| if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); | |||||
| return _rest.Delete<DestroyChannelResponse>(Endpoints.Channel(channelId)); | return _rest.Delete<DestroyChannelResponse>(Endpoints.Channel(channelId)); | ||||
| } | } | ||||
| public Task<EditChannelResponse> EditChannel(string channelId, string name = null, string topic = null) | |||||
| public Task<EditChannelResponse> 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 }; | var request = new EditChannelRequest { Name = name, Topic = topic }; | ||||
| return _rest.Patch<EditChannelResponse>(Endpoints.Channel(channelId), request); | return _rest.Patch<EditChannelResponse>(Endpoints.Channel(channelId), request); | ||||
| } | } | ||||
| public Task ReorderChannels(string serverId, IEnumerable<string> channelIds, int startPos = 0) | |||||
| public Task ReorderChannels(long serverId, IEnumerable<long> 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 (channelIds == null) throw new ArgumentNullException(nameof(channelIds)); | ||||
| if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer."); | 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); | var request = new ReorderChannelsRequest(channels); | ||||
| return _rest.Patch(Endpoints.ServerChannels(serverId), request); | return _rest.Patch(Endpoints.ServerChannels(serverId), request); | ||||
| } | } | ||||
| public Task<GetMessagesResponse> GetMessages(string channelId, int count, string relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before) | |||||
| public Task<GetMessagesResponse> 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) | if (relativeMessageId != null) | ||||
| return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count, relativeMessageId, relativeDir == RelativeDirection.Before ? "before" : "after")); | |||||
| return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count, relativeMessageId.Value, relativeDir == RelativeDirection.Before ? "before" : "after")); | |||||
| else | else | ||||
| return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count)); | return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count)); | ||||
| } | } | ||||
| @@ -114,9 +114,9 @@ namespace Discord | |||||
| } | } | ||||
| //Invites | //Invites | ||||
| public Task<CreateInviteResponse> CreateInvite(string channelId, int maxAge, int maxUses, bool tempMembership, bool hasXkcd) | |||||
| public Task<CreateInviteResponse> 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 }; | var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = tempMembership, WithXkcdPass = hasXkcd }; | ||||
| return _rest.Post<CreateInviteResponse>(Endpoints.ChannelInvites(channelId), request); | return _rest.Post<CreateInviteResponse>(Endpoints.ChannelInvites(channelId), request); | ||||
| @@ -127,9 +127,9 @@ namespace Discord | |||||
| return _rest.Get<GetInviteResponse>(Endpoints.Invite(inviteIdOrXkcd)); | return _rest.Get<GetInviteResponse>(Endpoints.Invite(inviteIdOrXkcd)); | ||||
| } | } | ||||
| public Task<GetInvitesResponse> GetInvites(string serverId) | |||||
| public Task<GetInvitesResponse> GetInvites(long serverId) | |||||
| { | { | ||||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||||
| if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); | |||||
| return _rest.Get<GetInvitesResponse>(Endpoints.ServerInvites(serverId)); | return _rest.Get<GetInvitesResponse>(Endpoints.ServerInvites(serverId)); | ||||
| } | } | ||||
| @@ -147,38 +147,38 @@ namespace Discord | |||||
| } | } | ||||
| //Users | //Users | ||||
| public Task EditUser(string serverId, string userId, bool? mute = null, bool? deaf = null, IEnumerable<string> roles = null) | |||||
| public Task EditUser(long serverId, long userId, bool? mute = null, bool? deaf = null, IEnumerable<long> 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); | 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)); | 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)); | 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)); | return _rest.Delete(Endpoints.ServerBan(serverId, userId)); | ||||
| } | } | ||||
| public Task<PruneUsersResponse> PruneUsers(string serverId, int days, bool simulate) | |||||
| public Task<PruneUsersResponse> 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 (days <= 0) throw new ArgumentOutOfRangeException(nameof(days)); | ||||
| if (simulate) | if (simulate) | ||||
| @@ -188,93 +188,93 @@ namespace Discord | |||||
| } | } | ||||
| //Messages | //Messages | ||||
| public Task<SendMessageResponse> SendMessage(string channelId, string message, IEnumerable<string> mentionedUserIds = null, string nonce = null, bool isTTS = false) | |||||
| public Task<SendMessageResponse> SendMessage(long channelId, string message, IEnumerable<long> 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)); | 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<SendMessageResponse>(Endpoints.ChannelMessages(channelId), request); | return _rest.Post<SendMessageResponse>(Endpoints.ChannelMessages(channelId), request); | ||||
| } | } | ||||
| public Task<SendMessageResponse> SendFile(string channelId, string filePath) | |||||
| public Task<SendMessageResponse> 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)); | if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | ||||
| return _rest.PostFile<SendMessageResponse>(Endpoints.ChannelMessages(channelId), filePath); | return _rest.PostFile<SendMessageResponse>(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)); | return _rest.Delete(Endpoints.ChannelMessage(channelId, messageId)); | ||||
| } | } | ||||
| public Task<EditMessageResponse> EditMessage(string messageId, string channelId, string message = null, IEnumerable<string> mentionedUserIds = null) | |||||
| public Task<EditMessageResponse> EditMessage(long messageId, long channelId, string message = null, IEnumerable<long> 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 }; | var request = new EditMessageRequest { Content = message, Mentions = mentionedUserIds }; | ||||
| return _rest.Patch<EditMessageResponse>(Endpoints.ChannelMessage(channelId, messageId), request); | return _rest.Patch<EditMessageResponse>(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)); | 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)); | return _rest.Post(Endpoints.ChannelTyping(channelId)); | ||||
| } | } | ||||
| //Permissions | //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)); | if (idType == null) throw new ArgumentNullException(nameof(idType)); | ||||
| var request = new SetChannelPermissionsRequest { Id = userOrRoleId, Type = idType, Allow = allow, Deny = deny }; | var request = new SetChannelPermissionsRequest { Id = userOrRoleId, Type = idType, Allow = allow, Deny = deny }; | ||||
| return _rest.Put(Endpoints.ChannelPermission(channelId, userOrRoleId), request); | 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); | return _rest.Delete(Endpoints.ChannelPermission(channelId, userOrRoleId), null); | ||||
| } | } | ||||
| //Roles | //Roles | ||||
| public Task<RoleInfo> CreateRole(string serverId) | |||||
| public Task<RoleInfo> CreateRole(long serverId) | |||||
| { | { | ||||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||||
| if (serverId <= 0) throw new ArgumentOutOfRangeException(nameof(serverId)); | |||||
| return _rest.Post<RoleInfo>(Endpoints.ServerRoles(serverId)); | return _rest.Post<RoleInfo>(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)); | return _rest.Delete(Endpoints.ServerRole(serverId, roleId)); | ||||
| } | } | ||||
| public Task<RoleInfo> EditRole(string serverId, string roleId, string name = null, uint? permissions = null, uint? color = null, bool? hoist = null) | |||||
| public Task<RoleInfo> 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 }; | var request = new EditRoleRequest { Name = name, Permissions = permissions, Hoist = hoist, Color = color }; | ||||
| return _rest.Patch<RoleInfo>(Endpoints.ServerRole(serverId, roleId), request); | return _rest.Patch<RoleInfo>(Endpoints.ServerRole(serverId, roleId), request); | ||||
| } | } | ||||
| public Task ReorderRoles(string serverId, IEnumerable<string> roleIds, int startPos = 0) | |||||
| public Task ReorderRoles(long serverId, IEnumerable<long> 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 (roleIds == null) throw new ArgumentNullException(nameof(roleIds)); | ||||
| if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer."); | 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 }; | var request = new CreateServerRequest { Name = name, Region = region }; | ||||
| return _rest.Post<CreateServerResponse>(Endpoints.Servers, request); | return _rest.Post<CreateServerResponse>(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<DeleteServerResponse>(Endpoints.Server(serverId)); | return _rest.Delete<DeleteServerResponse>(Endpoints.Server(serverId)); | ||||
| } | } | ||||
| public Task<EditServerResponse> EditServer(string serverId, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| public Task<EditServerResponse> 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) }; | var request = new EditServerRequest { Name = name, Region = region, Icon = Base64Picture(iconType, icon) }; | ||||
| return _rest.Patch<EditServerResponse>(Endpoints.Server(serverId), request); | return _rest.Patch<EditServerResponse>(Endpoints.Server(serverId), request); | ||||
| @@ -6,10 +6,10 @@ namespace Discord | |||||
| { | { | ||||
| public class BanEventArgs : EventArgs | public class BanEventArgs : EventArgs | ||||
| { | { | ||||
| public string UserId { get; } | |||||
| public long UserId { get; } | |||||
| public Server Server { get; } | public Server Server { get; } | ||||
| public BanEventArgs(string userId, Server server) | |||||
| public BanEventArgs(long userId, Server server) | |||||
| { | { | ||||
| UserId = userId; | UserId = userId; | ||||
| Server = server; | Server = server; | ||||
| @@ -19,13 +19,13 @@ namespace Discord | |||||
| public partial class DiscordClient | public partial class DiscordClient | ||||
| { | { | ||||
| public event EventHandler<BanEventArgs> UserBanned; | public event EventHandler<BanEventArgs> UserBanned; | ||||
| private void RaiseUserBanned(string userId, Server server) | |||||
| private void RaiseUserBanned(long userId, Server server) | |||||
| { | { | ||||
| if (UserBanned != null) | if (UserBanned != null) | ||||
| RaiseEvent(nameof(UserBanned), () => UserBanned(this, new BanEventArgs(userId, server))); | RaiseEvent(nameof(UserBanned), () => UserBanned(this, new BanEventArgs(userId, server))); | ||||
| } | } | ||||
| public event EventHandler<BanEventArgs> UserUnbanned; | public event EventHandler<BanEventArgs> UserUnbanned; | ||||
| private void RaiseUserUnbanned(string userId, Server server) | |||||
| private void RaiseUserUnbanned(long userId, Server server) | |||||
| { | { | ||||
| if (UserUnbanned != null) | if (UserUnbanned != null) | ||||
| RaiseEvent(nameof(UserUnbanned), () => UserUnbanned(this, new BanEventArgs(userId, server))); | RaiseEvent(nameof(UserUnbanned), () => UserUnbanned(this, new BanEventArgs(userId, server))); | ||||
| @@ -42,10 +42,10 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Unbans a user from the provided server. </summary> | /// <summary> Unbans a user from the provided server. </summary> | ||||
| 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 (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||||
| if (userId <= 0) throw new ArgumentOutOfRangeException(nameof(userId)); | |||||
| CheckReady(); | CheckReady(); | ||||
| try { await _api.UnbanUser(server.Id, userId).ConfigureAwait(false); } | try { await _api.UnbanUser(server.Id, userId).ConfigureAwait(false); } | ||||
| @@ -7,15 +7,15 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal sealed class Channels : AsyncCollection<Channel> | |||||
| internal sealed class Channels : AsyncCollection<long, Channel> | |||||
| { | { | ||||
| public IEnumerable<Channel> PrivateChannels => _privateChannels.Select(x => x.Value); | public IEnumerable<Channel> PrivateChannels => _privateChannels.Select(x => x.Value); | ||||
| private ConcurrentDictionary<string, Channel> _privateChannels; | |||||
| private ConcurrentDictionary<long, Channel> _privateChannels; | |||||
| public Channels(DiscordClient client, object writerLock) | public Channels(DiscordClient client, object writerLock) | ||||
| : base(client, writerLock) | : base(client, writerLock) | ||||
| { | { | ||||
| _privateChannels = new ConcurrentDictionary<string, Channel>(); | |||||
| _privateChannels = new ConcurrentDictionary<long, Channel>(); | |||||
| ItemCreated += (s, e) => | ItemCreated += (s, e) => | ||||
| { | { | ||||
| if (e.Item.IsPrivate) | if (e.Item.IsPrivate) | ||||
| @@ -31,8 +31,8 @@ namespace Discord | |||||
| }; | }; | ||||
| Cleared += (s, e) => _privateChannels.Clear(); | 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)); | => GetOrAdd(id, () => new Channel(_client, id, serverId, recipientId)); | ||||
| } | } | ||||
| @@ -71,9 +71,9 @@ namespace Discord | |||||
| private readonly Channels _channels; | private readonly Channels _channels; | ||||
| /// <summary> Returns the channel with the specified id, or null if none was found. </summary> | /// <summary> Returns the channel with the specified id, or null if none was found. </summary> | ||||
| 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(); | CheckReady(); | ||||
| return _channels[id]; | return _channels[id]; | ||||
| @@ -93,7 +93,7 @@ namespace Discord | |||||
| { | { | ||||
| if (name[0] == '<' && name[1] == '#' && name[name.Length - 1] == '>') //Parse mention | 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]; | var channel = _channels[id]; | ||||
| if (channel != null) | if (channel != null) | ||||
| query = query.Concat(new Channel[] { channel }); | query = query.Concat(new Channel[] { channel }); | ||||
| @@ -135,10 +135,10 @@ namespace Discord | |||||
| channel = user.GlobalUser.PrivateChannel; | channel = user.GlobalUser.PrivateChannel; | ||||
| if (channel == null) | 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); | 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); | channel.Update(response); | ||||
| } | } | ||||
| return channel; | return channel; | ||||
| @@ -23,7 +23,7 @@ namespace Discord | |||||
| inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); | inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); | ||||
| var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); | 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.Cache(); //Builds references | ||||
| invite.Update(response); | invite.Update(response); | ||||
| return invite; | return invite; | ||||
| @@ -38,7 +38,7 @@ namespace Discord | |||||
| var response = await _api.GetInvites(server.Id).ConfigureAwait(false); | var response = await _api.GetInvites(server.Id).ConfigureAwait(false); | ||||
| return response.Select(x => | 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.Cache(); //Builds references | ||||
| invite.Update(x); | invite.Update(x); | ||||
| return invite; | return invite; | ||||
| @@ -71,7 +71,7 @@ namespace Discord | |||||
| var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, | var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, | ||||
| tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); | 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 | invite.Cache(); //Builds references | ||||
| return invite; | return invite; | ||||
| } | } | ||||
| @@ -1,5 +1,4 @@ | |||||
| using Discord.API; | using Discord.API; | ||||
| using Discord.Net; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| @@ -8,7 +7,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal sealed class Messages : AsyncCollection<Message> | |||||
| internal sealed class Messages : AsyncCollection<long, Message> | |||||
| { | { | ||||
| private bool _isEnabled; | private bool _isEnabled; | ||||
| @@ -17,8 +16,8 @@ namespace Discord | |||||
| { | { | ||||
| _isEnabled = isEnabled; | _isEnabled = isEnabled; | ||||
| } | } | ||||
| public Message GetOrAdd(string id, string channelId, string userId) | |||||
| public Message GetOrAdd(long id, long channelId, long userId) | |||||
| { | { | ||||
| if (_isEnabled) | if (_isEnabled) | ||||
| return GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | return GetOrAdd(id, () => new Message(_client, id, channelId, userId)); | ||||
| @@ -80,9 +79,9 @@ namespace Discord | |||||
| private readonly Messages _messages; | private readonly Messages _messages; | ||||
| /// <summary> Returns the message with the specified id, or null if none was found. </summary> | /// <summary> Returns the message with the specified id, or null if none was found. </summary> | ||||
| 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(); | CheckReady(); | ||||
| return _messages[id]; | return _messages[id]; | ||||
| @@ -124,17 +123,16 @@ namespace Discord | |||||
| if (Config.UseMessageQueue) | if (Config.UseMessageQueue) | ||||
| { | { | ||||
| var nonce = GenerateNonce(); | var nonce = GenerateNonce(); | ||||
| msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _userId); | |||||
| msg = _messages.GetOrAdd(nonce, channel.Id, _userId.Value); | |||||
| var currentUser = msg.User; | var currentUser = msg.User; | ||||
| msg.Update(new MessageInfo | msg.Update(new MessageInfo | ||||
| { | { | ||||
| Content = text, | Content = text, | ||||
| Timestamp = DateTime.UtcNow, | 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, | ChannelId = channel.Id, | ||||
| IsTextToSpeech = isTextToSpeech | IsTextToSpeech = isTextToSpeech | ||||
| }); | }); | ||||
| msg.Nonce = nonce; | |||||
| msg.IsQueued = true; | msg.IsQueued = true; | ||||
| if (text.Length > MaxMessageSize) | if (text.Length > MaxMessageSize) | ||||
| @@ -217,7 +215,7 @@ namespace Discord | |||||
| /// <summary> Downloads last count messages from the server, returning all messages before or after relativeMessageId, if it's provided. </summary> | /// <summary> Downloads last count messages from the server, returning all messages before or after relativeMessageId, if it's provided. </summary> | ||||
| public async Task<Message[]> DownloadMessages(Channel channel, int count, string relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | |||||
| public async Task<Message[]> DownloadMessages(Channel channel, int count, long? relativeMessageId = null, RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true) | |||||
| { | { | ||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| if (count < 0) throw new ArgumentNullException(nameof(count)); | if (count < 0) throw new ArgumentNullException(nameof(count)); | ||||
| @@ -274,7 +272,7 @@ namespace Discord | |||||
| SendMessageResponse response = null; | SendMessageResponse response = null; | ||||
| try | 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 (WebException) { break; } | ||||
| catch (HttpException) { hasFailed = true; } | catch (HttpException) { hasFailed = true; } | ||||
| @@ -293,10 +291,10 @@ namespace Discord | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| private string GenerateNonce() | |||||
| private long GenerateNonce() | |||||
| { | { | ||||
| lock (_rand) | lock (_rand) | ||||
| return _rand.Next().ToString(); | |||||
| return -_rand.Next(1, int.MaxValue - 1); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -36,7 +36,7 @@ namespace Discord | |||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| CheckReady(); | 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) | public Task SetChannelPermissions(Channel channel, User user, DualChannelPermissions permissions = null) | ||||
| { | { | ||||
| @@ -44,7 +44,7 @@ namespace Discord | |||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| CheckReady(); | 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) | 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)); | if (role == null) throw new ArgumentNullException(nameof(role)); | ||||
| CheckReady(); | 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) | public Task SetChannelPermissions(Channel channel, Role role, DualChannelPermissions permissions = null) | ||||
| { | { | ||||
| @@ -60,9 +60,9 @@ namespace Discord | |||||
| if (role == null) throw new ArgumentNullException(nameof(role)); | if (role == null) throw new ArgumentNullException(nameof(role)); | ||||
| CheckReady(); | 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); | => _api.SetChannelPermissions(channel.Id, targetId, targetType.Value, allow?.RawValue ?? 0, deny?.RawValue ?? 0); | ||||
| public Task RemoveChannelPermissions(Channel channel, User user) | public Task RemoveChannelPermissions(Channel channel, User user) | ||||
| @@ -71,7 +71,7 @@ namespace Discord | |||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| CheckReady(); | CheckReady(); | ||||
| return RemoveChannelPermissions(channel, user?.Id, PermissionTarget.User); | |||||
| return RemoveChannelPermissions(channel, user.Id, PermissionTarget.User); | |||||
| } | } | ||||
| public Task RemoveChannelPermissions(Channel channel, Role role) | public Task RemoveChannelPermissions(Channel channel, Role role) | ||||
| { | { | ||||
| @@ -79,9 +79,9 @@ namespace Discord | |||||
| if (role == null) throw new ArgumentNullException(nameof(role)); | if (role == null) throw new ArgumentNullException(nameof(role)); | ||||
| CheckReady(); | 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 | try | ||||
| { | { | ||||
| @@ -6,12 +6,12 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal sealed class Roles : AsyncCollection<Role> | |||||
| internal sealed class Roles : AsyncCollection<long, Role> | |||||
| { | { | ||||
| public Roles(DiscordClient client, object writerLock) | public Roles(DiscordClient client, object writerLock) | ||||
| : base(client, 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)); | => GetOrAdd(id, () => new Role(_client, id, serverId)); | ||||
| } | } | ||||
| @@ -48,9 +48,9 @@ namespace Discord | |||||
| private readonly Roles _roles; | private readonly Roles _roles; | ||||
| /// <summary> Returns the role with the specified id, or null if none was found. </summary> | /// <summary> Returns the role with the specified id, or null if none was found. </summary> | ||||
| 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(); | CheckReady(); | ||||
| return _roles[id]; | return _roles[id]; | ||||
| @@ -6,19 +6,18 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal sealed class Servers : AsyncCollection<Server> | |||||
| internal sealed class Servers : AsyncCollection<long, Server> | |||||
| { | { | ||||
| public Servers(DiscordClient client, object writerLock) | public Servers(DiscordClient client, object writerLock) | ||||
| : base(client, writerLock) { } | : base(client, writerLock) { } | ||||
| public Server GetOrAdd(string id) | |||||
| public Server GetOrAdd(long id) | |||||
| => GetOrAdd(id, () => new Server(_client, id)); | => GetOrAdd(id, () => new Server(_client, id)); | ||||
| } | } | ||||
| public class ServerEventArgs : EventArgs | public class ServerEventArgs : EventArgs | ||||
| { | { | ||||
| public Server Server { get; } | public Server Server { get; } | ||||
| public string ServerId => Server.Id; | |||||
| public ServerEventArgs(Server server) { Server = server; } | public ServerEventArgs(Server server) { Server = server; } | ||||
| } | } | ||||
| @@ -62,9 +61,9 @@ namespace Discord | |||||
| private readonly Servers _servers; | private readonly Servers _servers; | ||||
| /// <summary> Returns the server with the specified id, or null if none was found. </summary> | /// <summary> Returns the server with the specified id, or null if none was found. </summary> | ||||
| 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(); | CheckReady(); | ||||
| return _servers[id]; | return _servers[id]; | ||||
| @@ -5,27 +5,25 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal sealed class GlobalUsers : AsyncCollection<GlobalUser> | |||||
| internal sealed class GlobalUsers : AsyncCollection<long, GlobalUser> | |||||
| { | { | ||||
| public GlobalUsers(DiscordClient client, object writerLock) | public GlobalUsers(DiscordClient client, object writerLock) | ||||
| : base(client, 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<User> | |||||
| internal sealed class Users : AsyncCollection<User.CompositeKey, User> | |||||
| { | { | ||||
| public Users(DiscordClient client, object writerLock) | public Users(DiscordClient client, object writerLock) | ||||
| : base(client, 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 | public class UserEventArgs : EventArgs | ||||
| @@ -38,7 +36,6 @@ namespace Discord | |||||
| public class UserChannelEventArgs : UserEventArgs | public class UserChannelEventArgs : UserEventArgs | ||||
| { | { | ||||
| public Channel Channel { get; } | public Channel Channel { get; } | ||||
| public string ChannelId => Channel.Id; | |||||
| public UserChannelEventArgs(User user, Channel channel) | public UserChannelEventArgs(User user, Channel channel) | ||||
| : base(user) | : base(user) | ||||
| @@ -123,30 +120,29 @@ namespace Discord | |||||
| private readonly Users _users; | private readonly Users _users; | ||||
| /// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary> | /// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary> | ||||
| public User GetUser(Server server, string userId) | |||||
| public User GetUser(Server server, long userId) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | 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(); | CheckReady(); | ||||
| return _users[userId, server.Id]; | return _users[userId, server.Id]; | ||||
| } | } | ||||
| /// <summary> Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. </summary> | /// <summary> Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. </summary> | ||||
| /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks> | /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks> | ||||
| 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 (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (username == null) throw new ArgumentNullException(nameof(username)); | 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(); | CheckReady(); | ||||
| User user = FindUsers(server, username, discriminator, true).FirstOrDefault(); | |||||
| return _users[user?.Id, server.Id]; | |||||
| return FindUsers(server, username, discriminator, true).FirstOrDefault(); | |||||
| } | } | ||||
| /// <summary> Returns all users with the specified server and name, along with their server-specific data. </summary> | /// <summary> Returns all users with the specified server and name, along with their server-specific data. </summary> | ||||
| /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks> | /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks> | ||||
| public IEnumerable<User> FindUsers(Server server, string name, string discriminator = null, bool exactMatch = false) | |||||
| public IEnumerable<User> FindUsers(Server server, string name, short? discriminator = null, bool exactMatch = false) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| @@ -156,16 +152,16 @@ namespace Discord | |||||
| } | } | ||||
| /// <summary> Returns all users with the specified channel and name, along with their server-specific data. </summary> | /// <summary> Returns all users with the specified channel and name, along with their server-specific data. </summary> | ||||
| /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks> | /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks> | ||||
| public IEnumerable<User> FindUsers(Channel channel, string name, string discriminator = null, bool exactMatch = false) | |||||
| public IEnumerable<User> FindUsers(Channel channel, string name, short? discriminator = null, bool exactMatch = false) | |||||
| { | { | ||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| CheckReady(); | 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<User> FindUsers(IEnumerable<User> users, string serverId, string name, string discriminator = null, bool exactMatch = false) | |||||
| private IEnumerable<User> FindUsers(IEnumerable<User> users, long? serverId, string name, short? discriminator = null, bool exactMatch = false) | |||||
| { | { | ||||
| var query = users.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | 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 | 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]; | var channel = _users[id, serverId]; | ||||
| if (channel != null) | if (channel != null) | ||||
| query = query.Concat(new User[] { channel }); | query = query.Concat(new User[] { channel }); | ||||
| @@ -186,57 +182,60 @@ namespace Discord | |||||
| } | } | ||||
| if (discriminator != null) | if (discriminator != null) | ||||
| query = query.Where(x => x.Discriminator == discriminator); | |||||
| query = query.Where(x => x.Discriminator == discriminator.Value); | |||||
| return query; | return query; | ||||
| } | } | ||||
| public Task EditUser(User user, bool? mute = null, bool? deaf = null, IEnumerable<Role> roles = null) | public Task EditUser(User user, bool? mute = null, bool? deaf = null, IEnumerable<Role> roles = null) | ||||
| { | { | ||||
| if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
| if (user.IsPrivate) throw new InvalidOperationException("Unable to edit users in a private channel"); | |||||
| CheckReady(); | CheckReady(); | ||||
| var serverId = user.Server?.Id; | |||||
| var serverId = user.Server.Id; | |||||
| return _api.EditUser(serverId, user.Id, | return _api.EditUser(serverId, user.Id, | ||||
| mute: mute, deaf: deaf, | 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) | public Task KickUser(User user) | ||||
| { | { | ||||
| if (user == null) throw new ArgumentNullException(nameof(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) | public Task BanUser(User user) | ||||
| { | { | ||||
| if (user == null) throw new ArgumentNullException(nameof(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 (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); | return _api.UnbanUser(server.Id, userId); | ||||
| } | } | ||||
| public async Task<int> PruneUsers(string serverId, int days, bool simulate = false) | |||||
| public async Task<int> 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)); | if (days <= 0) throw new ArgumentOutOfRangeException(nameof(days)); | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await _api.PruneUsers(serverId, days, simulate); | |||||
| var response = await _api.PruneUsers(server.Id, days, simulate); | |||||
| return response.Pruned ?? 0; | return response.Pruned ?? 0; | ||||
| } | } | ||||
| /// <summary>When Config.UseLargeThreshold is enabled, running this command will request the Discord server to provide you with all offline users for a particular server.</summary> | /// <summary>When Config.UseLargeThreshold is enabled, running this command will request the Discord server to provide you with all offline users for a particular server.</summary> | ||||
| 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 = "", | public Task EditProfile(string currentPassword = "", | ||||
| @@ -8,7 +8,7 @@ namespace Discord | |||||
| { | { | ||||
| public IDiscordVoiceClient GetVoiceClient(Server server) | 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) | if (!Config.EnableVoiceMultiserver) | ||||
| { | { | ||||
| @@ -16,7 +16,7 @@ namespace Discord | |||||
| return this; | return this; | ||||
| else | else | ||||
| return null; | return null; | ||||
| } | |||||
| } | |||||
| DiscordWSClient client; | DiscordWSClient client; | ||||
| if (_voiceClients.TryGetValue(server.Id, out client)) | if (_voiceClients.TryGetValue(server.Id, out client)) | ||||
| @@ -16,7 +16,7 @@ namespace Discord | |||||
| private readonly Random _rand; | private readonly Random _rand; | ||||
| private readonly JsonSerializer _serializer; | private readonly JsonSerializer _serializer; | ||||
| private readonly ConcurrentQueue<Message> _pendingMessages; | private readonly ConcurrentQueue<Message> _pendingMessages; | ||||
| private readonly ConcurrentDictionary<string, DiscordWSClient> _voiceClients; | |||||
| private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients; | |||||
| private readonly Dictionary<Type, IService> _services; | private readonly Dictionary<Type, IService> _services; | ||||
| private bool _sentInitialLog; | private bool _sentInitialLog; | ||||
| private uint _nextVoiceClientId; | private uint _nextVoiceClientId; | ||||
| @@ -38,7 +38,7 @@ namespace Discord | |||||
| if (Config.UseMessageQueue) | if (Config.UseMessageQueue) | ||||
| _pendingMessages = new ConcurrentQueue<Message>(); | _pendingMessages = new ConcurrentQueue<Message>(); | ||||
| if (Config.EnableVoiceMultiserver) | if (Config.EnableVoiceMultiserver) | ||||
| _voiceClients = new ConcurrentDictionary<string, DiscordWSClient>(); | |||||
| _voiceClients = new ConcurrentDictionary<long, DiscordWSClient>(); | |||||
| object cacheLock = new object(); | object cacheLock = new object(); | ||||
| _channels = new Channels(this, cacheLock); | _channels = new Channels(this, cacheLock); | ||||
| @@ -139,18 +139,18 @@ namespace Discord | |||||
| } | } | ||||
| if (_config.LogLevel >= LogMessageSeverity.Debug) | 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"); | _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"); | _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"); | _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"); | _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.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}"); | _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]; | var server = _servers[data.GuildId]; | ||||
| if (server != null) | 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; | break; | ||||
| @@ -507,8 +508,12 @@ namespace Discord | |||||
| { | { | ||||
| var data = e.Payload.ToObject<BanRemoveEvent>(_serializer); | var data = e.Payload.ToObject<BanRemoveEvent>(_serializer); | ||||
| var server = _servers[data.GuildId]; | 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; | break; | ||||
| @@ -34,9 +34,9 @@ namespace Discord | |||||
| } | } | ||||
| public class VoiceDisconnectedEventArgs : DisconnectedEventArgs | 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) | : base(e.WasUnexpected, e.Error) | ||||
| { | { | ||||
| ServerId = serverId; | ServerId = serverId; | ||||
| @@ -60,13 +60,13 @@ namespace Discord | |||||
| public sealed class VoicePacketEventArgs | 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 byte[] Buffer { get; } | ||||
| public int Offset { get; } | public int Offset { get; } | ||||
| public int Count { 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; | UserId = userId; | ||||
| Buffer = buffer; | Buffer = buffer; | ||||
| @@ -103,7 +103,7 @@ namespace Discord | |||||
| RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); | RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); | ||||
| } | } | ||||
| public event EventHandler<VoiceDisconnectedEventArgs> VoiceDisconnected; | public event EventHandler<VoiceDisconnectedEventArgs> VoiceDisconnected; | ||||
| private void RaiseVoiceDisconnected(string serverId, DisconnectedEventArgs e) | |||||
| private void RaiseVoiceDisconnected(long serverId, DisconnectedEventArgs e) | |||||
| { | { | ||||
| if (VoiceDisconnected != null) | if (VoiceDisconnected != null) | ||||
| RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e))); | RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e))); | ||||
| @@ -8,15 +8,15 @@ namespace Discord | |||||
| { | { | ||||
| IDiscordVoiceBuffer IDiscordVoiceClient.OutputBuffer => _voiceSocket.OutputBuffer; | IDiscordVoiceBuffer IDiscordVoiceClient.OutputBuffer => _voiceSocket.OutputBuffer; | ||||
| async Task IDiscordVoiceClient.JoinChannel(string channelId) | |||||
| async Task IDiscordVoiceClient.JoinChannel(long channelId) | |||||
| { | { | ||||
| CheckReady(checkVoice: true); | 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.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); | await _voiceSocket.WaitForConnection(_config.ConnectionTimeout).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -27,11 +27,11 @@ namespace Discord | |||||
| internal readonly VoiceWebSocket _voiceSocket; | internal readonly VoiceWebSocket _voiceSocket; | ||||
| protected ExceptionDispatchInfo _disconnectReason; | protected ExceptionDispatchInfo _disconnectReason; | ||||
| protected string _gateway, _token; | protected string _gateway, _token; | ||||
| protected string _userId, _voiceServerId; | |||||
| protected long? _userId, _voiceServerId; | |||||
| private Task _runTask; | private Task _runTask; | ||||
| private bool _wasDisconnectUnexpected; | private bool _wasDisconnectUnexpected; | ||||
| public string CurrentUserId => _userId; | |||||
| public long CurrentUserId => _userId.Value; | |||||
| /// <summary> 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. </summary> | /// <summary> 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. </summary> | ||||
| public DiscordWSClientConfig Config => _config; | public DiscordWSClientConfig Config => _config; | ||||
| @@ -59,7 +59,7 @@ namespace Discord | |||||
| if (_config.EnableVoice) | if (_config.EnableVoice) | ||||
| _voiceSocket = CreateVoiceSocket(); | _voiceSocket = CreateVoiceSocket(); | ||||
| } | } | ||||
| internal DiscordWSClient(DiscordWSClientConfig config = null, string voiceServerId = null) | |||||
| internal DiscordWSClient(DiscordWSClientConfig config = null, long? voiceServerId = null) | |||||
| : this(config) | : this(config) | ||||
| { | { | ||||
| _voiceServerId = voiceServerId; | _voiceServerId = voiceServerId; | ||||
| @@ -100,7 +100,7 @@ namespace Discord | |||||
| socket.Connected += (s, e) => RaiseVoiceConnected(); | socket.Connected += (s, e) => RaiseVoiceConnected(); | ||||
| socket.Disconnected += async (s, e) => | socket.Disconnected += async (s, e) => | ||||
| { | { | ||||
| RaiseVoiceDisconnected(socket.CurrentServerId, e); | |||||
| RaiseVoiceDisconnected(socket.CurrentServerId.Value, e); | |||||
| if (e.WasUnexpected) | if (e.WasUnexpected) | ||||
| await socket.Reconnect().ConfigureAwait(false); | await socket.Reconnect().ConfigureAwait(false); | ||||
| }; | }; | ||||
| @@ -241,9 +241,9 @@ namespace Discord | |||||
| { | { | ||||
| if (_config.EnableVoice) | if (_config.EnableVoice) | ||||
| { | { | ||||
| string voiceServerId = _voiceSocket.CurrentServerId; | |||||
| var voiceServerId = _voiceSocket.CurrentServerId; | |||||
| if (voiceServerId != null) | if (voiceServerId != null) | ||||
| _dataSocket.SendLeaveVoice(voiceServerId); | |||||
| _dataSocket.SendLeaveVoice(voiceServerId.Value); | |||||
| await _voiceSocket.Disconnect().ConfigureAwait(false); | await _voiceSocket.Disconnect().ConfigureAwait(false); | ||||
| } | } | ||||
| await _dataSocket.Disconnect().ConfigureAwait(false); | await _dataSocket.Disconnect().ConfigureAwait(false); | ||||
| @@ -303,17 +303,17 @@ namespace Discord | |||||
| switch (e.Type) | switch (e.Type) | ||||
| { | { | ||||
| case "READY": | case "READY": | ||||
| _userId = e.Payload["user"].Value<string>("id"); | |||||
| _userId = IdConvert.ToLong(e.Payload["user"].Value<string>("id")); | |||||
| break; | break; | ||||
| case "VOICE_SERVER_UPDATE": | case "VOICE_SERVER_UPDATE": | ||||
| { | { | ||||
| string guildId = e.Payload.Value<string>("guild_id"); | |||||
| long guildId = IdConvert.ToLong(e.Payload.Value<string>("guild_id")); | |||||
| if (_config.EnableVoice && guildId == _voiceSocket.CurrentServerId) | if (_config.EnableVoice && guildId == _voiceSocket.CurrentServerId) | ||||
| { | { | ||||
| string token = e.Payload.Value<string>("token"); | string token = e.Payload.Value<string>("token"); | ||||
| _voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; | _voiceSocket.Host = "wss://" + e.Payload.Value<string>("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; | break; | ||||
| @@ -6,7 +6,8 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal abstract class AsyncCollection<TValue> : IEnumerable<TValue> | |||||
| internal abstract class AsyncCollection<TKey, TValue> : IEnumerable<TValue> | |||||
| where TKey : struct, IEquatable<TKey> | |||||
| where TValue : CachedObject | where TValue : CachedObject | ||||
| { | { | ||||
| private readonly object _writerLock; | private readonly object _writerLock; | ||||
| @@ -19,9 +20,9 @@ namespace Discord | |||||
| public class CollectionItemRemappedEventArgs : EventArgs | public class CollectionItemRemappedEventArgs : EventArgs | ||||
| { | { | ||||
| public TValue Item { get; } | 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<CollectionItemEventArgs> ItemCreated; | public EventHandler<CollectionItemEventArgs> ItemCreated; | ||||
| @@ -37,7 +38,7 @@ namespace Discord | |||||
| ItemDestroyed(this, new CollectionItemEventArgs(item)); | ItemDestroyed(this, new CollectionItemEventArgs(item)); | ||||
| } | } | ||||
| public EventHandler<CollectionItemRemappedEventArgs> ItemRemapped; | public EventHandler<CollectionItemRemappedEventArgs> ItemRemapped; | ||||
| private void RaiseItemRemapped(TValue item, string oldId, string newId) | |||||
| private void RaiseItemRemapped(TValue item, TKey oldId, TKey newId) | |||||
| { | { | ||||
| if (ItemRemapped != null) | if (ItemRemapped != null) | ||||
| ItemRemapped(this, new CollectionItemRemappedEventArgs(item, oldId, newId)); | ItemRemapped(this, new CollectionItemRemappedEventArgs(item, oldId, newId)); | ||||
| @@ -51,20 +52,22 @@ namespace Discord | |||||
| } | } | ||||
| protected readonly DiscordClient _client; | protected readonly DiscordClient _client; | ||||
| protected readonly ConcurrentDictionary<string, TValue> _dictionary; | |||||
| protected readonly ConcurrentDictionary<TKey, TValue> _dictionary; | |||||
| protected AsyncCollection(DiscordClient client, object writerLock) | protected AsyncCollection(DiscordClient client, object writerLock) | ||||
| { | { | ||||
| _client = client; | _client = client; | ||||
| _writerLock = writerLock; | _writerLock = writerLock; | ||||
| _dictionary = new ConcurrentDictionary<string, TValue>(); | |||||
| _dictionary = new ConcurrentDictionary<TKey, TValue>(); | |||||
| } | } | ||||
| public TValue this[string key] | |||||
| public TValue this[TKey? key] | |||||
| => key == null ? null : this[key.Value]; | |||||
| public TValue this[TKey key] | |||||
| { | { | ||||
| get | get | ||||
| { | { | ||||
| if (key == null) | |||||
| if (key.Equals(default(TKey))) | |||||
| return null; | return null; | ||||
| TValue result; | TValue result; | ||||
| @@ -73,7 +76,7 @@ namespace Discord | |||||
| return result; | return result; | ||||
| } | } | ||||
| } | } | ||||
| protected TValue GetOrAdd(string key, Func<TValue> createFunc) | |||||
| protected TValue GetOrAdd(TKey key, Func<TValue> createFunc) | |||||
| { | { | ||||
| TValue result; | TValue result; | ||||
| if (_dictionary.TryGetValue(key, out result)) | if (_dictionary.TryGetValue(key, out result)) | ||||
| @@ -91,7 +94,7 @@ namespace Discord | |||||
| } | } | ||||
| return result; | return result; | ||||
| } | } | ||||
| public TValue TryRemove(string key) | |||||
| public TValue TryRemove(TKey key) | |||||
| { | { | ||||
| if (_dictionary.ContainsKey(key)) | if (_dictionary.ContainsKey(key)) | ||||
| { | { | ||||
| @@ -107,7 +110,7 @@ namespace Discord | |||||
| } | } | ||||
| return null; | return null; | ||||
| } | } | ||||
| public TValue Remap(string oldKey, string newKey) | |||||
| public TValue Remap(TKey oldKey, TKey newKey) | |||||
| { | { | ||||
| if (_dictionary.ContainsKey(oldKey)) | if (_dictionary.ContainsKey(oldKey)) | ||||
| { | { | ||||
| @@ -1,23 +1,35 @@ | |||||
| namespace Discord | |||||
| using System.Globalization; | |||||
| namespace Discord | |||||
| { | { | ||||
| public abstract class CachedObject | |||||
| public abstract class CachedObject<TKey> : CachedObject | |||||
| { | |||||
| private TKey _id; | |||||
| internal CachedObject(DiscordClient client, TKey id) | |||||
| : base(client) | |||||
| { | |||||
| _id = id; | |||||
| } | |||||
| /// <summary> Returns the unique identifier for this object. </summary> | |||||
| 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; | protected readonly DiscordClient _client; | ||||
| private bool _isCached; | private bool _isCached; | ||||
| internal bool IsCached => _isCached; | internal bool IsCached => _isCached; | ||||
| internal CachedObject(DiscordClient client, string id) | |||||
| internal CachedObject(DiscordClient client) | |||||
| { | { | ||||
| _client = client; | _client = client; | ||||
| Id = id; | |||||
| } | } | ||||
| /// <summary> Returns the unique identifier for this object. </summary> | |||||
| public string Id { get; internal set; } | |||||
| public override string ToString() => $"{this.GetType().Name} {Id}"; | |||||
| internal void Cache() | internal void Cache() | ||||
| { | { | ||||
| LoadReferences(); | LoadReferences(); | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -24,7 +24,7 @@ namespace Discord | |||||
| { | { | ||||
| return _userRegex.Replace(text, new MatchEvaluator(e => | 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]; | var user = client.Users[id, server?.Id]; | ||||
| if (user != null) | if (user != null) | ||||
| { | { | ||||
| @@ -40,7 +40,7 @@ namespace Discord | |||||
| { | { | ||||
| return _channelRegex.Replace(text, new MatchEvaluator(e => | 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]; | var channel = client.Channels[id]; | ||||
| if (channel != null && channel.Server.Id == server.Id) | if (channel != null && channel.Server.Id == server.Id) | ||||
| { | { | ||||
| @@ -52,7 +52,7 @@ namespace Discord | |||||
| return '#' + e.Value; | return '#' + e.Value; | ||||
| })); | })); | ||||
| } | } | ||||
| internal static string CleanRoleMentions(DiscordClient client, User user, Channel channel, string text, List<Role> roles = null) | |||||
| /*internal static string CleanRoleMentions(DiscordClient client, User user, Channel channel, string text, List<Role> roles = null) | |||||
| { | { | ||||
| return _roleRegex.Replace(text, new MatchEvaluator(e => | return _roleRegex.Replace(text, new MatchEvaluator(e => | ||||
| { | { | ||||
| @@ -60,6 +60,6 @@ namespace Discord | |||||
| roles.Add(channel.Server.EveryoneRole); | roles.Add(channel.Server.EveryoneRole); | ||||
| return e.Value; | return e.Value; | ||||
| })); | })); | ||||
| } | |||||
| }*/ | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,12 +3,12 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal struct Reference<T> | internal struct Reference<T> | ||||
| where T : CachedObject | |||||
| { | |||||
| where T : CachedObject<long> | |||||
| { | |||||
| private Action<T> _onCache, _onUncache; | private Action<T> _onCache, _onUncache; | ||||
| private Func<string, T> _getItem; | |||||
| private string _id; | |||||
| public string Id | |||||
| private Func<long, T> _getItem; | |||||
| private long? _id; | |||||
| public long? Id | |||||
| { | { | ||||
| get { return _id; } | get { return _id; } | ||||
| set | set | ||||
| @@ -24,14 +24,15 @@ namespace Discord | |||||
| get | get | ||||
| { | { | ||||
| var v = _value; //A little trickery to make this threadsafe | var v = _value; //A little trickery to make this threadsafe | ||||
| var id = _id; | |||||
| if (v != null && !_value.IsCached) | if (v != null && !_value.IsCached) | ||||
| { | { | ||||
| v = null; | v = null; | ||||
| _value = 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) | if (v != null && _onCache != null) | ||||
| _onCache(v); | _onCache(v); | ||||
| _value = v; | _value = v; | ||||
| @@ -55,9 +56,9 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| public Reference(Func<string, T> onUpdate, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| public Reference(Func<long, T> onUpdate, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| : this(null, onUpdate, onCache, onUncache) { } | : this(null, onUpdate, onCache, onUncache) { } | ||||
| public Reference(string id, Func<string, T> getItem, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| public Reference(long? id, Func<long, T> getItem, Action<T> onCache = null, Action<T> onUncache = null) | |||||
| { | { | ||||
| _id = id; | _id = id; | ||||
| _getItem = getItem; | _getItem = getItem; | ||||
| @@ -6,15 +6,15 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Channel : CachedObject | |||||
| public sealed class Channel : CachedObject<long> | |||||
| { | { | ||||
| public sealed class PermissionOverwrite | public sealed class PermissionOverwrite | ||||
| { | { | ||||
| public PermissionTarget TargetType { get; } | public PermissionTarget TargetType { get; } | ||||
| public string TargetId { get; } | |||||
| public long TargetId { get; } | |||||
| public DualChannelPermissions Permissions { 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; | TargetType = targetType; | ||||
| TargetId = targetId; | TargetId = targetId; | ||||
| @@ -55,20 +55,20 @@ namespace Discord | |||||
| return _members.Select(x => x.Value); | return _members.Select(x => x.Value); | ||||
| } | } | ||||
| } | } | ||||
| private Dictionary<string, User> _members; | |||||
| private Dictionary<long, User> _members; | |||||
| private bool _areMembersStale; | private bool _areMembersStale; | ||||
| /// <summary> Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. </summary> | /// <summary> Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Message> Messages => _messages.Values; | |||||
| private readonly ConcurrentDictionary<string, Message> _messages; | |||||
| public IEnumerable<Message> Messages => _messages?.Values ?? Enumerable.Empty<Message>(); | |||||
| private readonly ConcurrentDictionary<long, Message> _messages; | |||||
| /// <summary> Returns a collection of all custom permissions used for this channel. </summary> | /// <summary> Returns a collection of all custom permissions used for this channel. </summary> | ||||
| private static readonly PermissionOverwrite[] _initialPermissionsOverwrites = new PermissionOverwrite[0]; | private static readonly PermissionOverwrite[] _initialPermissionsOverwrites = new PermissionOverwrite[0]; | ||||
| private PermissionOverwrite[] _permissionOverwrites; | private PermissionOverwrite[] _permissionOverwrites; | ||||
| public IEnumerable<PermissionOverwrite> PermissionOverwrites { get { return _permissionOverwrites; } internal set { _permissionOverwrites = value.ToArray(); } } | public IEnumerable<PermissionOverwrite> 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) | : base(client, id) | ||||
| { | { | ||||
| _server = new Reference<Server>(serverId, | _server = new Reference<Server>(serverId, | ||||
| @@ -92,7 +92,8 @@ namespace Discord | |||||
| _areMembersStale = true; | _areMembersStale = true; | ||||
| //Local Cache | //Local Cache | ||||
| _messages = new ConcurrentDictionary<string, Message>(); | |||||
| if (client.Config.MessageCacheLength > 0) | |||||
| _messages = new ConcurrentDictionary<long, Message>(); | |||||
| } | } | ||||
| internal override void LoadReferences() | internal override void LoadReferences() | ||||
| { | { | ||||
| @@ -164,10 +165,10 @@ namespace Discord | |||||
| { | { | ||||
| if (IsPrivate) | if (IsPrivate) | ||||
| { | { | ||||
| _members = new Dictionary<string, User>() | |||||
| _members = new Dictionary<long, User>() | |||||
| { | { | ||||
| { _client.CurrentUserId, _client.PrivateUser }, | { _client.CurrentUserId, _client.PrivateUser }, | ||||
| { _recipient.Id, _recipient.Value } | |||||
| { _recipient.Id.Value, _recipient.Value } | |||||
| }; | }; | ||||
| } | } | ||||
| else if (Type == ChannelType.Text) | 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 bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 5658); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 5658); | ||||
| public override string ToString() => Name ?? Id; | |||||
| public override string ToString() => Name ?? IdConvert.ToString(Id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,9 +6,9 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class GlobalUser : CachedObject | |||||
| public sealed class GlobalUser : CachedObject<long> | |||||
| { | { | ||||
| private readonly ConcurrentDictionary<string, User> _users; | |||||
| private readonly ConcurrentDictionary<long, User> _users; | |||||
| /// <summary> Returns the email for this user. </summary> | /// <summary> Returns the email for this user. </summary> | ||||
| /// <remarks> This field is only ever populated for the current logged in user. </remarks> | /// <remarks> This field is only ever populated for the current logged in user. </remarks> | ||||
| @@ -35,12 +35,12 @@ namespace Discord | |||||
| /// <summary> Returns a collection of all server-specific data for every server this user is a member of. </summary> | /// <summary> Returns a collection of all server-specific data for every server this user is a member of. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| internal IEnumerable<User> Memberships => _users.Select(x => _client.Users[Id, x.Key]); | |||||
| internal IEnumerable<User> Memberships => _users.Select(x => x.Value); | |||||
| internal GlobalUser(DiscordClient client, string id) | |||||
| internal GlobalUser(DiscordClient client, long id) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| _users = new ConcurrentDictionary<string, User>(); | |||||
| _users = new ConcurrentDictionary<long, User>(); | |||||
| } | } | ||||
| internal override void LoadReferences() { } | internal override void LoadReferences() { } | ||||
| internal override void UnloadReferences() | internal override void UnloadReferences() | ||||
| @@ -56,10 +56,10 @@ namespace Discord | |||||
| IsVerified = model.IsVerified; | 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) | internal void RemoveUser(User user) | ||||
| { | { | ||||
| if (_users.TryRemove(user.UniqueId, out user)) | |||||
| if (_users.TryRemove(user.Server?.Id ?? 0, out user)) | |||||
| CheckUser(); | CheckUser(); | ||||
| } | } | ||||
| internal void 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 bool Equals(object obj) => obj is GlobalUser && (obj as GlobalUser).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 7891); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 7891); | ||||
| public override string ToString() => Id; | |||||
| public override string ToString() => IdConvert.ToString(Id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,19 +1,18 @@ | |||||
| using System; | using System; | ||||
| using Discord.API; | using Discord.API; | ||||
| using Newtonsoft.Json; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Invite : CachedObject | |||||
| public sealed class Invite : CachedObject<string> | |||||
| { | { | ||||
| public sealed class ServerInfo | public sealed class ServerInfo | ||||
| { | { | ||||
| /// <summary> Returns the unique identifier of this server. </summary> | /// <summary> Returns the unique identifier of this server. </summary> | ||||
| public string Id { get; } | |||||
| public long Id { get; } | |||||
| /// <summary> Returns the name of this server. </summary> | /// <summary> Returns the name of this server. </summary> | ||||
| public string Name { get; } | public string Name { get; } | ||||
| internal ServerInfo(string id, string name) | |||||
| internal ServerInfo(long id, string name) | |||||
| { | { | ||||
| Id = id; | Id = id; | ||||
| Name = name; | Name = name; | ||||
| @@ -22,11 +21,11 @@ namespace Discord | |||||
| public sealed class ChannelInfo | public sealed class ChannelInfo | ||||
| { | { | ||||
| /// <summary> Returns the unique identifier of this channel. </summary> | /// <summary> Returns the unique identifier of this channel. </summary> | ||||
| public string Id { get; } | |||||
| public long Id { get; } | |||||
| /// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
| public string Name { get; } | public string Name { get; } | ||||
| internal ChannelInfo(string id, string name) | |||||
| internal ChannelInfo(long id, string name) | |||||
| { | { | ||||
| Id = id; | Id = id; | ||||
| Name = name; | Name = name; | ||||
| @@ -35,17 +34,17 @@ namespace Discord | |||||
| public sealed class InviterInfo | public sealed class InviterInfo | ||||
| { | { | ||||
| /// <summary> Returns the unique identifier for this user. </summary> | /// <summary> Returns the unique identifier for this user. </summary> | ||||
| public string Id { get; } | |||||
| public long Id { get; } | |||||
| /// <summary> Returns the name of this user. </summary> | /// <summary> Returns the name of this user. </summary> | ||||
| public string Name { get; } | public string Name { get; } | ||||
| /// <summary> Returns the by-name unique identifier for this user. </summary> | /// <summary> Returns the by-name unique identifier for this user. </summary> | ||||
| public string Discriminator { get; } | |||||
| public int Discriminator { get; } | |||||
| /// <summary> Returns the unique identifier for this user's avatar. </summary> | /// <summary> Returns the unique identifier for this user's avatar. </summary> | ||||
| public string AvatarId { get; } | public string AvatarId { get; } | ||||
| /// <summary> Returns the full path to this user's avatar. </summary> | /// <summary> Returns the full path to this user's avatar. </summary> | ||||
| public string AvatarUrl => User.GetAvatarUrl(Id, AvatarId); | 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; | Id = id; | ||||
| Name = name; | Name = name; | ||||
| @@ -76,9 +75,9 @@ namespace Discord | |||||
| public DateTime CreatedAt { get; private set; } | public DateTime CreatedAt { get; private set; } | ||||
| /// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary> | /// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary> | ||||
| 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) | : base(client, code) | ||||
| { | { | ||||
| XkcdCode = xkcdPass; | XkcdCode = xkcdPass; | ||||
| @@ -93,7 +92,7 @@ namespace Discord | |||||
| if (model.Channel != null) | if (model.Channel != null) | ||||
| Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); | Channel = new ChannelInfo(model.Channel.Id, model.Channel.Name); | ||||
| if (model.Inviter != null) | 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) | internal void Update(InviteInfo model) | ||||
| { | { | ||||
| @@ -6,7 +6,7 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Message : CachedObject | |||||
| public sealed class Message : CachedObject<long> | |||||
| { | { | ||||
| public sealed class Attachment : File | public sealed class Attachment : File | ||||
| { | { | ||||
| @@ -90,9 +90,6 @@ namespace Discord | |||||
| Height = height; | Height = height; | ||||
| } | } | ||||
| } | } | ||||
| /// <summary> Returns the local unique identifier for this message. </summary> | |||||
| public string Nonce { get; internal set; } | |||||
| /// <summary> Returns true if the logged-in user was mentioned. </summary> | /// <summary> Returns true if the logged-in user was mentioned. </summary> | ||||
| /// <remarks> This is not set to true if the user was mentioned with @everyone (see IsMentioningEverone). </remarks> | /// <remarks> This is not set to true if the user was mentioned with @everyone (see IsMentioningEverone). </remarks> | ||||
| @@ -145,7 +142,7 @@ namespace Discord | |||||
| public User User => _user.Value; | public User User => _user.Value; | ||||
| private readonly Reference<User> _user; | private readonly Reference<User> _user; | ||||
| internal Message(DiscordClient client, string id, string channelId, string userId) | |||||
| internal Message(DiscordClient client, long id, long channelId, long userId) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| _channel = new Reference<Channel>(channelId, | _channel = new Reference<Channel>(channelId, | ||||
| @@ -5,7 +5,7 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Role : CachedObject | |||||
| public sealed class Role : CachedObject<long> | |||||
| { | { | ||||
| /// <summary> Returns the name of this role. </summary> | /// <summary> Returns the name of this role. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| @@ -33,7 +33,7 @@ namespace Discord | |||||
| public IEnumerable<User> Members => _server.Id != null ? (IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this))) : new User[0]; | public IEnumerable<User> Members => _server.Id != null ? (IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this))) : new User[0]; | ||||
| //TODO: Add local members cache | //TODO: Add local members cache | ||||
| internal Role(DiscordClient client, string id, string serverId) | |||||
| internal Role(DiscordClient client, long id, long serverId) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| _server = new Reference<Server>(serverId, x => _client.Servers[x], x => x.AddRole(this), x => x.RemoveRole(this)); | _server = new Reference<Server>(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 bool Equals(object obj) => obj is Role && (obj as Role).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 6653); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 6653); | ||||
| public override string ToString() => Name ?? Id; | |||||
| public override string ToString() => Name ?? IdConvert.ToString(Id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -7,7 +7,7 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Server : CachedObject | |||||
| public sealed class Server : CachedObject<long> | |||||
| { | { | ||||
| /// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| @@ -28,7 +28,7 @@ namespace Discord | |||||
| /// <summary> Returns the user that first created this server. </summary> | /// <summary> Returns the user that first created this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Owner { get; private set; } | public User Owner { get; private set; } | ||||
| private string _ownerId; | |||||
| private long _ownerId; | |||||
| /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -41,8 +41,8 @@ namespace Discord | |||||
| /// <summary> Returns a collection of the ids of all users banned on this server. </summary> | /// <summary> Returns a collection of the ids of all users banned on this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<string> Bans => _bans.Select(x => x.Key); | |||||
| private ConcurrentDictionary<string, bool> _bans; | |||||
| public IEnumerable<long> Bans => _bans.Select(x => x.Key); | |||||
| private ConcurrentDictionary<long, bool> _bans; | |||||
| /// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -53,12 +53,12 @@ namespace Discord | |||||
| /// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | ||||
| private ConcurrentDictionary<string, Channel> _channels; | |||||
| private ConcurrentDictionary<long, Channel> _channels; | |||||
| /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<User> Members => _members.Select(x => x.Value); | public IEnumerable<User> Members => _members.Select(x => x.Value); | ||||
| private ConcurrentDictionary<string, User> _members; | |||||
| private ConcurrentDictionary<long, User> _members; | |||||
| /// <summary> Return the the role representing all users in a server. </summary> | /// <summary> Return the the role representing all users in a server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -66,20 +66,20 @@ namespace Discord | |||||
| /// <summary> Returns a collection of all roles within this server. </summary> | /// <summary> Returns a collection of all roles within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | ||||
| private ConcurrentDictionary<string, Role> _roles; | |||||
| private ConcurrentDictionary<long, Role> _roles; | |||||
| internal Server(DiscordClient client, string id) | |||||
| internal Server(DiscordClient client, long id) | |||||
| : base(client, id) | : base(client, id) | ||||
| { | { | ||||
| _afkChannel = new Reference<Channel>(x => _client.Channels[x]); | _afkChannel = new Reference<Channel>(x => _client.Channels[x]); | ||||
| //Global Cache | //Global Cache | ||||
| _channels = new ConcurrentDictionary<string, Channel>(); | |||||
| _members = new ConcurrentDictionary<string, User>(); | |||||
| _roles = new ConcurrentDictionary<string, Role>(); | |||||
| _channels = new ConcurrentDictionary<long, Channel>(); | |||||
| _members = new ConcurrentDictionary<long, User>(); | |||||
| _roles = new ConcurrentDictionary<long, Role>(); | |||||
| //Local Cache | //Local Cache | ||||
| _bans = new ConcurrentDictionary<string, bool>(); | |||||
| _bans = new ConcurrentDictionary<long, bool>(); | |||||
| } | } | ||||
| internal override void LoadReferences() | internal override void LoadReferences() | ||||
| { | { | ||||
| @@ -129,7 +129,7 @@ namespace Discord | |||||
| JoinedAt = model.JoinedAt.Value; | JoinedAt = model.JoinedAt.Value; | ||||
| if (model.OwnerId != null && _ownerId != model.OwnerId) | if (model.OwnerId != null && _ownerId != model.OwnerId) | ||||
| { | { | ||||
| _ownerId = model.OwnerId; | |||||
| _ownerId = model.OwnerId.Value; | |||||
| Owner = _client.Users[_ownerId, Id]; | Owner = _client.Users[_ownerId, Id]; | ||||
| } | } | ||||
| if (model.Region != null) | if (model.Region != null) | ||||
| @@ -178,11 +178,11 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| internal void AddBan(string banId) | |||||
| internal void AddBan(long banId) | |||||
| { | { | ||||
| _bans.TryAdd(banId, true); | _bans.TryAdd(banId, true); | ||||
| } | } | ||||
| internal bool RemoveBan(string banId) | |||||
| internal bool RemoveBan(long banId) | |||||
| { | { | ||||
| bool ignored; | bool ignored; | ||||
| return _bans.TryRemove(banId, out 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 bool Equals(object obj) => obj is Server && (obj as Server).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175); | ||||
| public override string ToString() => Name ?? Id; | |||||
| public override string ToString() => Name ?? IdConvert.ToString(Id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -20,20 +20,34 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| public class User : CachedObject | |||||
| public class User : CachedObject<long> | |||||
| { | { | ||||
| 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<CompositeKey> | |||||
| { | |||||
| 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<string, ChannelPermissionsPair> _permissions; | |||||
| private ConcurrentDictionary<long, ChannelPermissionsPair> _permissions; | |||||
| private ServerPermissions _serverPermissions; | private ServerPermissions _serverPermissions; | ||||
| /// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | /// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | ||||
| internal string UniqueId => GetId(Id, _server.Id); | |||||
| internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); | |||||
| /// <summary> Returns the name of this user on this server. </summary> | /// <summary> Returns the name of this user on this server. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> | ||||
| public string Discriminator { get; private set; } | |||||
| public short Discriminator { get; private set; } | |||||
| /// <summary> Returns the unique identifier for this user's current avatar. </summary> | /// <summary> Returns the unique identifier for this user's current avatar. </summary> | ||||
| public string AvatarId { get; private set; } | public string AvatarId { get; private set; } | ||||
| /// <summary> Returns the URL to this user's current avatar. </summary> | /// <summary> Returns the URL to this user's current avatar. </summary> | ||||
| @@ -47,6 +61,7 @@ namespace Discord | |||||
| public bool IsServerDeafened { get; private set; } | public bool IsServerDeafened { get; private set; } | ||||
| public bool IsServerSuppressed { get; private set; } | public bool IsServerSuppressed { get; private set; } | ||||
| public bool IsSpeaking { get; internal set; } | public bool IsSpeaking { get; internal set; } | ||||
| public bool IsPrivate => _server.Id == null; | |||||
| public string SessionId { get; private set; } | public string SessionId { get; private set; } | ||||
| public string Token { get; private set; } | public string Token { get; private set; } | ||||
| @@ -79,7 +94,7 @@ namespace Discord | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | ||||
| private Dictionary<string, Role> _roles; | |||||
| private Dictionary<long, Role> _roles; | |||||
| /// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary> | /// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary> | ||||
| [JsonIgnore] | [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) | : base(client, id) | ||||
| { | { | ||||
| _globalUser = new Reference<GlobalUser>(id, | _globalUser = new Reference<GlobalUser>(id, | ||||
| @@ -140,13 +155,13 @@ namespace Discord | |||||
| x.CurrentUser = null; | x.CurrentUser = null; | ||||
| }); | }); | ||||
| _voiceChannel = new Reference<Channel>(x => _client.Channels[x]); | _voiceChannel = new Reference<Channel>(x => _client.Channels[x]); | ||||
| _roles = new Dictionary<string, Role>(); | |||||
| _roles = new Dictionary<long, Role>(); | |||||
| Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
| //_channels = new ConcurrentDictionary<string, Channel>(); | //_channels = new ConcurrentDictionary<string, Channel>(); | ||||
| if (serverId != null) | if (serverId != null) | ||||
| { | { | ||||
| _permissions = new ConcurrentDictionary<string, ChannelPermissionsPair>(); | |||||
| _permissions = new ConcurrentDictionary<long, ChannelPermissionsPair>(); | |||||
| _serverPermissions = new ServerPermissions(); | _serverPermissions = new ServerPermissions(); | ||||
| } | } | ||||
| @@ -169,7 +184,7 @@ namespace Discord | |||||
| if (model.Avatar != null) | if (model.Avatar != null) | ||||
| AvatarId = model.Avatar; | AvatarId = model.Avatar; | ||||
| if (model.Discriminator != null) | if (model.Discriminator != null) | ||||
| Discriminator = model.Discriminator; | |||||
| Discriminator = model.Discriminator.Value; | |||||
| if (model.Username != null) | if (model.Username != null) | ||||
| Name = model.Username; | Name = model.Username; | ||||
| } | } | ||||
| @@ -243,11 +258,11 @@ namespace Discord | |||||
| } | } | ||||
| private void UpdateRoles(IEnumerable<Role> roles) | private void UpdateRoles(IEnumerable<Role> roles) | ||||
| { | { | ||||
| Dictionary<string, Role> newRoles; | |||||
| Dictionary<long, Role> newRoles; | |||||
| if (roles != null) | if (roles != null) | ||||
| newRoles = roles.ToDictionary(x => x.Id, x => x); | newRoles = roles.ToDictionary(x => x.Id, x => x); | ||||
| else | else | ||||
| newRoles = new Dictionary<string, Role>(); | |||||
| newRoles = new Dictionary<long, Role>(); | |||||
| if (_server.Id != null) | 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 bool Equals(object obj) => obj is User && (obj as User).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 7230); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 7230); | ||||
| public override string ToString() => Name ?? Id; | |||||
| public override string ToString() => Name ?? IdConvert.ToString(Id); | |||||
| } | } | ||||
| } | } | ||||
| @@ -128,20 +128,20 @@ namespace Discord.Net.WebSockets | |||||
| QueueMessage(updateStatus); | QueueMessage(updateStatus); | ||||
| } | } | ||||
| public void SendJoinVoice(string serverId, string channelId) | |||||
| public void SendJoinVoice(long serverId, long channelId) | |||||
| { | { | ||||
| var joinVoice = new JoinVoiceCommand(); | var joinVoice = new JoinVoiceCommand(); | ||||
| joinVoice.Payload.ServerId = serverId; | joinVoice.Payload.ServerId = serverId; | ||||
| joinVoice.Payload.ChannelId = channelId; | joinVoice.Payload.ChannelId = channelId; | ||||
| QueueMessage(joinVoice); | QueueMessage(joinVoice); | ||||
| } | } | ||||
| public void SendLeaveVoice(string serverId) | |||||
| public void SendLeaveVoice(long serverId) | |||||
| { | { | ||||
| var leaveVoice = new JoinVoiceCommand(); | var leaveVoice = new JoinVoiceCommand(); | ||||
| leaveVoice.Payload.ServerId = serverId; | leaveVoice.Payload.ServerId = serverId; | ||||
| QueueMessage(leaveVoice); | 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(); | var getOfflineUsers = new GetUsersCommand(); | ||||
| getOfflineUsers.Payload.ServerId = serverId; | getOfflineUsers.Payload.ServerId = serverId; | ||||
| @@ -4,9 +4,9 @@ namespace Discord.Net.WebSockets | |||||
| { | { | ||||
| internal sealed class IsTalkingEventArgs : EventArgs | internal sealed class IsTalkingEventArgs : EventArgs | ||||
| { | { | ||||
| public readonly string UserId; | |||||
| public readonly long UserId; | |||||
| public readonly bool IsSpeaking; | public readonly bool IsSpeaking; | ||||
| internal IsTalkingEventArgs(string userId, bool isTalking) | |||||
| internal IsTalkingEventArgs(long userId, bool isTalking) | |||||
| { | { | ||||
| UserId = userId; | UserId = userId; | ||||
| IsSpeaking = isTalking; | IsSpeaking = isTalking; | ||||
| @@ -16,14 +16,14 @@ namespace Discord.Net.WebSockets | |||||
| internal partial class VoiceWebSocket | internal partial class VoiceWebSocket | ||||
| { | { | ||||
| public event EventHandler<IsTalkingEventArgs> IsSpeaking; | public event EventHandler<IsTalkingEventArgs> IsSpeaking; | ||||
| private void RaiseIsSpeaking(string userId, bool isSpeaking) | |||||
| private void RaiseIsSpeaking(long userId, bool isSpeaking) | |||||
| { | { | ||||
| if (IsSpeaking != null) | if (IsSpeaking != null) | ||||
| IsSpeaking(this, new IsTalkingEventArgs(userId, isSpeaking)); | IsSpeaking(this, new IsTalkingEventArgs(userId, isSpeaking)); | ||||
| } | } | ||||
| public event EventHandler<VoicePacketEventArgs> OnPacket; | public event EventHandler<VoicePacketEventArgs> 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) | if (OnPacket != null) | ||||
| OnPacket(this, new VoicePacketEventArgs(userId, channelId, buffer, offset, count)); | OnPacket(this, new VoicePacketEventArgs(userId, channelId, buffer, offset, count)); | ||||
| @@ -26,7 +26,7 @@ namespace Discord.Net.WebSockets | |||||
| private OpusEncoder _encoder; | private OpusEncoder _encoder; | ||||
| private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders; | private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders; | ||||
| private uint _ssrc; | private uint _ssrc; | ||||
| private ConcurrentDictionary<uint, string> _ssrcMapping; | |||||
| private ConcurrentDictionary<uint, long> _ssrcMapping; | |||||
| private VoiceBuffer _sendBuffer; | private VoiceBuffer _sendBuffer; | ||||
| private UdpClient _udp; | private UdpClient _udp; | ||||
| @@ -34,12 +34,13 @@ namespace Discord.Net.WebSockets | |||||
| private bool _isEncrypted; | private bool _isEncrypted; | ||||
| private byte[] _secretKey, _encodingBuffer; | private byte[] _secretKey, _encodingBuffer; | ||||
| private ushort _sequence; | 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; | 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 VoiceBuffer OutputBuffer => _sendBuffer; | ||||
| public VoiceWebSocket(DiscordWSClient client) | public VoiceWebSocket(DiscordWSClient client) | ||||
| @@ -49,19 +50,19 @@ namespace Discord.Net.WebSockets | |||||
| _decoders = new ConcurrentDictionary<uint, OpusDecoder>(); | _decoders = new ConcurrentDictionary<uint, OpusDecoder>(); | ||||
| _targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames | _targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames | ||||
| _encodingBuffer = new byte[MaxOpusSize]; | _encodingBuffer = new byte[MaxOpusSize]; | ||||
| _ssrcMapping = new ConcurrentDictionary<uint, string>(); | |||||
| _ssrcMapping = new ConcurrentDictionary<uint, long>(); | |||||
| _encoder = new OpusEncoder(48000, 1, 20, Opus.Application.Audio); | _encoder = new OpusEncoder(48000, 1, 20, Opus.Application.Audio); | ||||
| _sendBuffer = new VoiceBuffer((int)Math.Ceiling(client.Config.VoiceBufferLength / (double)_encoder.FrameLength), _encoder.FrameSize); | _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; | _serverId = serverId; | ||||
| _channelId = channelId; | _channelId = channelId; | ||||
| return base.BeginConnect(); | 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) | if ((WebSocketState)_state == WebSocketState.Connected) | ||||
| { | { | ||||
| @@ -108,10 +109,10 @@ namespace Discord.Net.WebSockets | |||||
| _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); | _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); | ||||
| VoiceLoginCommand msg = new VoiceLoginCommand(); | VoiceLoginCommand msg = new VoiceLoginCommand(); | ||||
| msg.Payload.ServerId = _serverId; | |||||
| msg.Payload.ServerId = _serverId.Value; | |||||
| msg.Payload.SessionId = _sessionId; | msg.Payload.SessionId = _sessionId; | ||||
| msg.Payload.Token = _token; | msg.Payload.Token = _token; | ||||
| msg.Payload.UserId = _userId; | |||||
| msg.Payload.UserId = _userId.Value; | |||||
| QueueMessage(msg); | QueueMessage(msg); | ||||
| List<Task> tasks = new List<Task>(); | List<Task> tasks = new List<Task>(); | ||||
| @@ -232,16 +233,9 @@ namespace Discord.Net.WebSockets | |||||
| else | else | ||||
| { | { | ||||
| //Parse RTP Data | //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) | | ushort sequenceNumber = (ushort)((packet[2] << 8) | | ||||
| packet[3] << 0); | packet[3] << 0); | ||||
| @@ -278,9 +272,9 @@ namespace Discord.Net.WebSockets | |||||
| /*if (_logLevel >= LogMessageSeverity.Debug) | /*if (_logLevel >= LogMessageSeverity.Debug) | ||||
| RaiseOnLog(LogMessageSeverity.Debug, $"Received {buffer.Length - 12} bytes.");*/ | RaiseOnLog(LogMessageSeverity.Debug, $"Received {buffer.Length - 12} bytes.");*/ | ||||
| string userId; | |||||
| long userId; | |||||
| if (_ssrcMapping.TryGetValue(ssrc, out 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[] frame = new byte[_encoder.FrameSize]; | ||||
| byte[] encodedFrame = new byte[MaxOpusSize]; | byte[] encodedFrame = new byte[MaxOpusSize]; | ||||
| byte[] udpPacket, nonce = null; | |||||
| byte[] voicePacket, pingPacket, nonce = null; | |||||
| uint timestamp = 0; | uint timestamp = 0; | ||||
| double nextTicks = 0.0; | |||||
| double nextTicks = 0.0, nextPingTicks = 0.0; | |||||
| double ticksPerMillisecond = Stopwatch.Frequency / 1000.0; | double ticksPerMillisecond = Stopwatch.Frequency / 1000.0; | ||||
| double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength; | double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength; | ||||
| double spinLockThreshold = 3 * ticksPerMillisecond; | double spinLockThreshold = 3 * ticksPerMillisecond; | ||||
| @@ -314,38 +308,58 @@ namespace Discord.Net.WebSockets | |||||
| if (_isEncrypted) | if (_isEncrypted) | ||||
| { | { | ||||
| nonce = new byte[24]; | nonce = new byte[24]; | ||||
| udpPacket = new byte[MaxOpusSize + 12 + 16]; | |||||
| voicePacket = new byte[MaxOpusSize + 12 + 16]; | |||||
| } | } | ||||
| else | 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; | 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) | if (_isEncrypted) | ||||
| Buffer.BlockCopy(udpPacket, 0, nonce, 0, 12); | |||||
| Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12); | |||||
| while (!cancelToken.IsCancellationRequested) | while (!cancelToken.IsCancellationRequested) | ||||
| { | { | ||||
| double ticksToNextFrame = nextTicks - sw.ElapsedTicks; | double ticksToNextFrame = nextTicks - sw.ElapsedTicks; | ||||
| if (ticksToNextFrame <= 0.0) | if (ticksToNextFrame <= 0.0) | ||||
| { | { | ||||
| while (sw.ElapsedTicks > nextTicks) | |||||
| long currentTicks = sw.ElapsedTicks; | |||||
| while (currentTicks > nextTicks) | |||||
| { | { | ||||
| if (_sendBuffer.Pop(frame)) | if (_sendBuffer.Pop(frame)) | ||||
| { | { | ||||
| ushort sequence = unchecked(_sequence++); | 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 | //Encode | ||||
| int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame); | int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame); | ||||
| @@ -353,23 +367,28 @@ namespace Discord.Net.WebSockets | |||||
| //Encrypt | //Encrypt | ||||
| if (_isEncrypted) | 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) | if (ret != 0) | ||||
| continue; | continue; | ||||
| rtpPacketLength = encodedLength + 12 + 16; | rtpPacketLength = encodedLength + 12 + 16; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| Buffer.BlockCopy(encodedFrame, 0, udpPacket, 12, encodedLength); | |||||
| Buffer.BlockCopy(encodedFrame, 0, voicePacket, 12, encodedLength); | |||||
| rtpPacketLength = encodedLength + 12; | rtpPacketLength = encodedLength + 12; | ||||
| } | } | ||||
| _udp.Send(udpPacket, rtpPacketLength); | |||||
| _udp.Send(voicePacket, rtpPacketLength); | |||||
| } | } | ||||
| timestamp = unchecked(timestamp + samplesPerFrame); | timestamp = unchecked(timestamp + samplesPerFrame); | ||||
| nextTicks += ticksPerFrame; | 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 | //Dont sleep if we need to output audio in the next spinLockThreshold | ||||
| else if (ticksToNextFrame > spinLockThreshold) | else if (ticksToNextFrame > spinLockThreshold) | ||||
| { | { | ||||