| @@ -2,7 +2,6 @@ | |||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Globalization; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Reflection; | using System.Reflection; | ||||
| using System.Threading; | using System.Threading; | ||||
| @@ -27,94 +26,56 @@ namespace Discord.Commands | |||||
| _map = new CommandMap(); | _map = new CommandMap(); | ||||
| _typeReaders = new ConcurrentDictionary<Type, TypeReader> | _typeReaders = new ConcurrentDictionary<Type, TypeReader> | ||||
| { | { | ||||
| [typeof(string)] = new GenericTypeReader((m, s) => Task.FromResult(TypeReaderResult.FromSuccess(s))), | |||||
| [typeof(byte)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| byte value; | |||||
| if (byte.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Byte")); | |||||
| }), | |||||
| [typeof(sbyte)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| sbyte value; | |||||
| if (sbyte.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse SByte")); | |||||
| }), | |||||
| [typeof(ushort)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| ushort value; | |||||
| if (ushort.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt16")); | |||||
| }), | |||||
| [typeof(short)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| short value; | |||||
| if (short.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int16")); | |||||
| }), | |||||
| [typeof(uint)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| uint value; | |||||
| if (uint.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt32")); | |||||
| }), | |||||
| [typeof(int)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| int value; | |||||
| if (int.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int32")); | |||||
| }), | |||||
| [typeof(ulong)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| ulong value; | |||||
| if (ulong.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse UInt64")); | |||||
| }), | |||||
| [typeof(long)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| long value; | |||||
| if (long.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Int64")); | |||||
| }), | |||||
| [typeof(float)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| float value; | |||||
| if (float.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Single")); | |||||
| }), | |||||
| [typeof(double)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| double value; | |||||
| if (double.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Double")); | |||||
| }), | |||||
| [typeof(decimal)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| decimal value; | |||||
| if (decimal.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse Decimal")); | |||||
| }), | |||||
| [typeof(DateTime)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| DateTime value; | |||||
| if (DateTime.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse DateTime")); | |||||
| }), | |||||
| [typeof(DateTimeOffset)] = new GenericTypeReader((m, s) => | |||||
| { | |||||
| DateTimeOffset value; | |||||
| if (DateTimeOffset.TryParse(s, out value)) return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse DateTimeOffset")); | |||||
| }), | |||||
| [typeof(string)] = new SimpleTypeReader<string>(), | |||||
| [typeof(byte)] = new SimpleTypeReader<byte>(), | |||||
| [typeof(sbyte)] = new SimpleTypeReader<sbyte>(), | |||||
| [typeof(ushort)] = new SimpleTypeReader<ushort>(), | |||||
| [typeof(short)] = new SimpleTypeReader<short>(), | |||||
| [typeof(uint)] = new SimpleTypeReader<uint>(), | |||||
| [typeof(int)] = new SimpleTypeReader<int>(), | |||||
| [typeof(ulong)] = new SimpleTypeReader<ulong>(), | |||||
| [typeof(long)] = new SimpleTypeReader<long>(), | |||||
| [typeof(float)] = new SimpleTypeReader<float>(), | |||||
| [typeof(double)] = new SimpleTypeReader<double>(), | |||||
| [typeof(decimal)] = new SimpleTypeReader<decimal>(), | |||||
| [typeof(DateTime)] = new SimpleTypeReader<DateTime>(), | |||||
| [typeof(DateTimeOffset)] = new SimpleTypeReader<DateTimeOffset>(), | |||||
| //TODO: Do we want to support any other interfaces? | |||||
| //[typeof(IMentionable)] = new GeneralTypeReader(), | |||||
| //[typeof(ISnowflakeEntity)] = new GeneralTypeReader(), | |||||
| //[typeof(IEntity<ulong>)] = new GeneralTypeReader(), | |||||
| [typeof(IMessage)] = new MessageTypeReader(), | [typeof(IMessage)] = new MessageTypeReader(), | ||||
| //[typeof(IAttachment)] = new xxx(), | |||||
| //[typeof(IEmbed)] = new xxx(), | |||||
| [typeof(IChannel)] = new ChannelTypeReader<IChannel>(), | [typeof(IChannel)] = new ChannelTypeReader<IChannel>(), | ||||
| [typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(), | |||||
| [typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(), | |||||
| [typeof(IGuildChannel)] = new ChannelTypeReader<IGuildChannel>(), | [typeof(IGuildChannel)] = new ChannelTypeReader<IGuildChannel>(), | ||||
| [typeof(IMessageChannel)] = new ChannelTypeReader<IMessageChannel>(), | |||||
| [typeof(IPrivateChannel)] = new ChannelTypeReader<IPrivateChannel>(), | |||||
| [typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(), | [typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(), | ||||
| [typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(), | [typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(), | ||||
| //[typeof(IGuild)] = new GuildTypeReader<IGuild>(), | |||||
| //[typeof(IUserGuild)] = new GuildTypeReader<IUserGuild>(), | |||||
| //[typeof(IGuildIntegration)] = new xxx(), | |||||
| [typeof(IRole)] = new RoleTypeReader(), | [typeof(IRole)] = new RoleTypeReader(), | ||||
| //[typeof(IInvite)] = new InviteTypeReader<IInvite>(), | |||||
| //[typeof(IInviteMetadata)] = new InviteTypeReader<IInviteMetadata>(), | |||||
| [typeof(IUser)] = new UserTypeReader<IUser>(), | [typeof(IUser)] = new UserTypeReader<IUser>(), | ||||
| [typeof(IGuildUser)] = new UserTypeReader<IGuildUser>() | |||||
| [typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(), | |||||
| [typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(), | |||||
| //[typeof(ISelfUser)] = new UserTypeReader<ISelfUser>(), | |||||
| //[typeof(IPresence)] = new UserTypeReader<IPresence>(), | |||||
| //[typeof(IVoiceState)] = new UserTypeReader<IVoiceState>(), | |||||
| //[typeof(IConnection)] = new xxx(), | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -1,6 +1,5 @@ | |||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | using System.Linq; | ||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| @@ -0,0 +1,36 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| internal delegate bool TryParseDelegate<T>(string str, out T value); | |||||
| internal static class PrimitiveParsers | |||||
| { | |||||
| private static readonly IReadOnlyDictionary<Type, Delegate> _parsers; | |||||
| static PrimitiveParsers() | |||||
| { | |||||
| var parserBuilder = ImmutableDictionary.CreateBuilder<Type, Delegate>(); | |||||
| parserBuilder[typeof(string)] = (TryParseDelegate<string>)delegate(string str, out string value) { value = str; return true; }; | |||||
| parserBuilder[typeof(sbyte)] = (TryParseDelegate<sbyte>)sbyte.TryParse; | |||||
| parserBuilder[typeof(byte)] = (TryParseDelegate<byte>)byte.TryParse; | |||||
| parserBuilder[typeof(short)] = (TryParseDelegate<short>)short.TryParse; | |||||
| parserBuilder[typeof(ushort)] = (TryParseDelegate<ushort>)ushort.TryParse; | |||||
| parserBuilder[typeof(int)] = (TryParseDelegate<int>)int.TryParse; | |||||
| parserBuilder[typeof(uint)] = (TryParseDelegate<uint>)uint.TryParse; | |||||
| parserBuilder[typeof(long)] = (TryParseDelegate<long>)long.TryParse; | |||||
| parserBuilder[typeof(ulong)] = (TryParseDelegate<ulong>)ulong.TryParse; | |||||
| parserBuilder[typeof(float)] = (TryParseDelegate<float>)float.TryParse; | |||||
| parserBuilder[typeof(double)] = (TryParseDelegate<double>)double.TryParse; | |||||
| parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse; | |||||
| parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse; | |||||
| parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse; | |||||
| _parsers = parserBuilder.ToImmutable(); | |||||
| } | |||||
| public static TryParseDelegate<T> Get<T>() => (TryParseDelegate<T>)_parsers[typeof(T)]; | |||||
| public static Delegate Get(Type type) => _parsers[type]; | |||||
| } | |||||
| } | |||||
| @@ -7,31 +7,13 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| delegate bool TryParseDelegate<T>(string str, out T value); | |||||
| internal static class EnumTypeReader | internal static class EnumTypeReader | ||||
| { | |||||
| private static readonly IReadOnlyDictionary<Type, object> _parsers; | |||||
| static EnumTypeReader() | |||||
| { | |||||
| var parserBuilder = ImmutableDictionary.CreateBuilder<Type, object>(); | |||||
| parserBuilder[typeof(sbyte)] = (TryParseDelegate<sbyte>)sbyte.TryParse; | |||||
| parserBuilder[typeof(byte)] = (TryParseDelegate<byte>)byte.TryParse; | |||||
| parserBuilder[typeof(short)] = (TryParseDelegate<short>)short.TryParse; | |||||
| parserBuilder[typeof(ushort)] = (TryParseDelegate<ushort>)ushort.TryParse; | |||||
| parserBuilder[typeof(int)] = (TryParseDelegate<int>)int.TryParse; | |||||
| parserBuilder[typeof(uint)] = (TryParseDelegate<uint>)uint.TryParse; | |||||
| parserBuilder[typeof(long)] = (TryParseDelegate<long>)long.TryParse; | |||||
| parserBuilder[typeof(ulong)] = (TryParseDelegate<ulong>)ulong.TryParse; | |||||
| _parsers = parserBuilder.ToImmutable(); | |||||
| } | |||||
| { | |||||
| public static TypeReader GetReader(Type type) | public static TypeReader GetReader(Type type) | ||||
| { | { | ||||
| Type baseType = Enum.GetUnderlyingType(type); | Type baseType = Enum.GetUnderlyingType(type); | ||||
| var constructor = typeof(EnumTypeReader<>).MakeGenericType(baseType).GetTypeInfo().DeclaredConstructors.First(); | var constructor = typeof(EnumTypeReader<>).MakeGenericType(baseType).GetTypeInfo().DeclaredConstructors.First(); | ||||
| return (TypeReader)constructor.Invoke(new object[] { type, _parsers[baseType] }); | |||||
| return (TypeReader)constructor.Invoke(new object[] { type, PrimitiveParsers.Get(baseType) }); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,17 +0,0 @@ | |||||
| using System; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| internal class GenericTypeReader : TypeReader | |||||
| { | |||||
| private readonly Func<IMessage, string, Task<TypeReaderResult>> _action; | |||||
| public GenericTypeReader(Func<IMessage, string, Task<TypeReaderResult>> action) | |||||
| { | |||||
| _action = action; | |||||
| } | |||||
| public override Task<TypeReaderResult> Read(IMessage context, string input) => _action(context, input); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Commands | |||||
| { | |||||
| internal class SimpleTypeReader<T> : TypeReader | |||||
| { | |||||
| private readonly TryParseDelegate<T> _tryParse; | |||||
| public SimpleTypeReader() | |||||
| { | |||||
| _tryParse = PrimitiveParsers.Get<T>(); | |||||
| } | |||||
| public override Task<TypeReaderResult> Read(IMessage context, string input) | |||||
| { | |||||
| T value; | |||||
| if (_tryParse(input, out value)) | |||||
| return Task.FromResult(TypeReaderResult.FromSuccess(value)); | |||||
| else | |||||
| return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Failed to parse {typeof(T).Name}")); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -8,7 +8,7 @@ namespace Discord.Commands | |||||
| { | { | ||||
| internal static object CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) | internal static object CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) | ||||
| { | { | ||||
| var constructors = typeInfo.DeclaredConstructors.ToArray(); | |||||
| var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray(); | |||||
| if (constructors.Length == 0) | if (constructors.Length == 0) | ||||
| throw new InvalidOperationException($"No constructor found for \"{typeInfo.FullName}\""); | throw new InvalidOperationException($"No constructor found for \"{typeInfo.FullName}\""); | ||||
| else if (constructors.Length > 1) | else if (constructors.Length > 1) | ||||
| @@ -3,14 +3,14 @@ | |||||
| internal static class CDN | internal static class CDN | ||||
| { | { | ||||
| public static string GetApplicationIconUrl(ulong appId, string iconId) | public static string GetApplicationIconUrl(ulong appId, string iconId) | ||||
| => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; | |||||
| => iconId != null ? $"{DiscordRestConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; | |||||
| public static string GetUserAvatarUrl(ulong userId, string avatarId) | public static string GetUserAvatarUrl(ulong userId, string avatarId) | ||||
| => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.jpg" : null; | |||||
| => avatarId != null ? $"{DiscordRestConfig.CDNUrl}avatars/{userId}/{avatarId}.jpg" : null; | |||||
| public static string GetGuildIconUrl(ulong guildId, string iconId) | public static string GetGuildIconUrl(ulong guildId, string iconId) | ||||
| => iconId != null ? $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null; | |||||
| => iconId != null ? $"{DiscordRestConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null; | |||||
| public static string GetGuildSplashUrl(ulong guildId, string splashId) | public static string GetGuildSplashUrl(ulong guildId, string splashId) | ||||
| => splashId != null ? $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null; | |||||
| => splashId != null ? $"{DiscordRestConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null; | |||||
| public static string GetChannelIconUrl(ulong channelId, string iconId) | public static string GetChannelIconUrl(ulong channelId, string iconId) | ||||
| => iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null; | |||||
| => iconId != null ? $"{DiscordRestConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null; | |||||
| } | } | ||||
| } | } | ||||
| @@ -55,9 +55,9 @@ namespace Discord.API | |||||
| _requestQueue = requestQueue ?? new RequestQueue(); | _requestQueue = requestQueue ?? new RequestQueue(); | ||||
| _restClient = restClientProvider(DiscordConfig.ClientAPIUrl); | |||||
| _restClient = restClientProvider(DiscordRestConfig.ClientAPIUrl); | |||||
| _restClient.SetHeader("accept", "*/*"); | _restClient.SetHeader("accept", "*/*"); | ||||
| _restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | |||||
| _restClient.SetHeader("user-agent", DiscordRestConfig.UserAgent); | |||||
| if (webSocketProvider != null) | if (webSocketProvider != null) | ||||
| { | { | ||||
| _gatewayClient = webSocketProvider(); | _gatewayClient = webSocketProvider(); | ||||
| @@ -211,7 +211,7 @@ namespace Discord.API | |||||
| if (_gatewayUrl == null) | if (_gatewayUrl == null) | ||||
| { | { | ||||
| var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false); | var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false); | ||||
| _gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordConfig.GatewayEncoding}"; | |||||
| _gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}"; | |||||
| } | } | ||||
| await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false); | await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false); | ||||
| @@ -461,8 +461,8 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | |||||
| Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | |||||
| Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); | |||||
| Preconditions.NotNullOrWhitespace(args._name, nameof(args.Name)); | |||||
| return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("POST", $"guilds/{guildId}/channels", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -476,8 +476,8 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
| Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -485,8 +485,8 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
| Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -494,10 +494,10 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); | |||||
| Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); | |||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
| Preconditions.GreaterThan(args._bitrate, 0, nameof(args.Bitrate)); | |||||
| Preconditions.AtLeast(args._userLimit, 0, nameof(args.Bitrate)); | |||||
| Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
| Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
| return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("PATCH", $"channels/{channelId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -606,11 +606,11 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotEqual(args.AFKChannelId, 0, nameof(args.AFKChannelId)); | |||||
| Preconditions.AtLeast(args.AFKTimeout, 0, nameof(args.AFKTimeout)); | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
| Preconditions.GreaterThan(args.OwnerId, 0, nameof(args.OwnerId)); | |||||
| Preconditions.NotNull(args.Region, nameof(args.Region)); | |||||
| Preconditions.NotEqual(args._afkChannelId, 0, nameof(args.AFKChannelId)); | |||||
| Preconditions.AtLeast(args._afkTimeout, 0, nameof(args.AFKTimeout)); | |||||
| Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
| Preconditions.GreaterThan(args._ownerId, 0, nameof(args.OwnerId)); | |||||
| Preconditions.NotNull(args._region, nameof(args.Region)); | |||||
| return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Guild>("PATCH", $"guilds/{guildId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -643,7 +643,7 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(userId, 0, nameof(userId)); | Preconditions.NotEqual(userId, 0, nameof(userId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.PruneDays, 0, nameof(args.PruneDays)); | |||||
| Preconditions.AtLeast(args._deleteMessageDays, 0, nameof(args.DeleteMessageDays)); | |||||
| await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | await SendAsync("PUT", $"guilds/{guildId}/bans/{userId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -701,8 +701,8 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.ExpireBehavior, 0, nameof(args.ExpireBehavior)); | |||||
| Preconditions.AtLeast(args.ExpireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | |||||
| Preconditions.AtLeast(args._expireBehavior, 0, nameof(args.ExpireBehavior)); | |||||
| Preconditions.AtLeast(args._expireGracePeriod, 0, nameof(args.ExpireGracePeriod)); | |||||
| return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Integration>("PATCH", $"guilds/{guildId}/integrations/{integrationId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -749,8 +749,8 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.MaxAge, 0, nameof(args.MaxAge)); | |||||
| Preconditions.AtLeast(args.MaxUses, 0, nameof(args.MaxUses)); | |||||
| Preconditions.AtLeast(args._maxAge, 0, nameof(args.MaxAge)); | |||||
| Preconditions.AtLeast(args._maxUses, 0, nameof(args.MaxUses)); | |||||
| return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | return await SendAsync<InviteMetadata>("POST", $"channels/{channelId}/invites", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -783,21 +783,21 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); | |||||
| Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); | |||||
| Preconditions.GreaterThan(args._limit, 0, nameof(args.Limit)); | |||||
| Preconditions.GreaterThan(args._afterUserId, 0, nameof(args.AfterUserId)); | |||||
| int limit = args.Limit.GetValueOrDefault(int.MaxValue); | |||||
| ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); | |||||
| int limit = args._limit.GetValueOrDefault(int.MaxValue); | |||||
| ulong afterUserId = args._afterUserId.GetValueOrDefault(0); | |||||
| List<GuildMember[]> result; | List<GuildMember[]> result; | ||||
| if (args.Limit.IsSpecified) | |||||
| result = new List<GuildMember[]>((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch); | |||||
| if (args._limit.IsSpecified) | |||||
| result = new List<GuildMember[]>((limit + DiscordRestConfig.MaxUsersPerBatch - 1) / DiscordRestConfig.MaxUsersPerBatch); | |||||
| else | else | ||||
| result = new List<GuildMember[]>(); | result = new List<GuildMember[]>(); | ||||
| while (true) | while (true) | ||||
| { | { | ||||
| int runLimit = (limit >= DiscordConfig.MaxUsersPerBatch) ? DiscordConfig.MaxUsersPerBatch : limit; | |||||
| int runLimit = (limit >= DiscordRestConfig.MaxUsersPerBatch) ? DiscordRestConfig.MaxUsersPerBatch : limit; | |||||
| string endpoint = $"guilds/{guildId}/members?limit={runLimit}&after={afterUserId}"; | string endpoint = $"guilds/{guildId}/members?limit={runLimit}&after={afterUserId}"; | ||||
| var models = await SendAsync<GuildMember[]>("GET", endpoint, options: options).ConfigureAwait(false); | var models = await SendAsync<GuildMember[]>("GET", endpoint, options: options).ConfigureAwait(false); | ||||
| @@ -806,11 +806,11 @@ namespace Discord.API | |||||
| result.Add(models); | result.Add(models); | ||||
| limit -= DiscordConfig.MaxUsersPerBatch; | |||||
| limit -= DiscordRestConfig.MaxUsersPerBatch; | |||||
| afterUserId = models[models.Length - 1].User.Id; | afterUserId = models[models.Length - 1].User.Id; | ||||
| //Was this an incomplete (the last) batch? | //Was this an incomplete (the last) batch? | ||||
| if (models.Length != DiscordConfig.MaxUsersPerBatch) break; | |||||
| if (models.Length != DiscordRestConfig.MaxUsersPerBatch) break; | |||||
| } | } | ||||
| if (result.Count > 1) | if (result.Count > 1) | ||||
| @@ -861,9 +861,9 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | Preconditions.NotEqual(guildId, 0, nameof(guildId)); | ||||
| Preconditions.NotEqual(roleId, 0, nameof(roleId)); | Preconditions.NotEqual(roleId, 0, nameof(roleId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.AtLeast(args.Color, 0, nameof(args.Color)); | |||||
| Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||||
| Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); | |||||
| Preconditions.AtLeast(args._color, 0, nameof(args.Color)); | |||||
| Preconditions.NotNullOrEmpty(args._name, nameof(args.Name)); | |||||
| Preconditions.AtLeast(args._position, 0, nameof(args.Position)); | |||||
| return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | return await SendAsync<Role>("PATCH", $"guilds/{guildId}/roles/{roleId}", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -903,7 +903,7 @@ namespace Discord.API | |||||
| Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | Preconditions.AtLeast(args.Limit, 0, nameof(args.Limit)); | ||||
| int limit = args.Limit; | int limit = args.Limit; | ||||
| ulong? relativeId = args.RelativeMessageId.IsSpecified ? args.RelativeMessageId.Value : (ulong?)null; | |||||
| ulong? relativeId = args._relativeMessageId.IsSpecified ? args._relativeMessageId.Value : (ulong?)null; | |||||
| string relativeDir; | string relativeDir; | ||||
| switch (args.RelativeDirection) | switch (args.RelativeDirection) | ||||
| @@ -920,14 +920,14 @@ namespace Discord.API | |||||
| break; | break; | ||||
| } | } | ||||
| int runs = (limit + DiscordConfig.MaxMessagesPerBatch - 1) / DiscordConfig.MaxMessagesPerBatch; | |||||
| int lastRunCount = limit - (runs - 1) * DiscordConfig.MaxMessagesPerBatch; | |||||
| int runs = (limit + DiscordRestConfig.MaxMessagesPerBatch - 1) / DiscordRestConfig.MaxMessagesPerBatch; | |||||
| int lastRunCount = limit - (runs - 1) * DiscordRestConfig.MaxMessagesPerBatch; | |||||
| var result = new API.Message[runs][]; | var result = new API.Message[runs][]; | ||||
| int i = 0; | int i = 0; | ||||
| for (; i < runs; i++) | for (; i < runs; i++) | ||||
| { | { | ||||
| int runCount = i == (runs - 1) ? lastRunCount : DiscordConfig.MaxMessagesPerBatch; | |||||
| int runCount = i == (runs - 1) ? lastRunCount : DiscordRestConfig.MaxMessagesPerBatch; | |||||
| string endpoint; | string endpoint; | ||||
| if (relativeId != null) | if (relativeId != null) | ||||
| endpoint = $"channels/{channelId}/messages?limit={runCount}&{relativeDir}={relativeId}"; | endpoint = $"channels/{channelId}/messages?limit={runCount}&{relativeDir}={relativeId}"; | ||||
| @@ -966,7 +966,7 @@ namespace Discord.API | |||||
| } | } | ||||
| //Was this an incomplete (the last) batch? | //Was this an incomplete (the last) batch? | ||||
| if (models.Length != DiscordConfig.MaxMessagesPerBatch) { i++; break; } | |||||
| if (models.Length != DiscordRestConfig.MaxMessagesPerBatch) { i++; break; } | |||||
| } | } | ||||
| if (i > 1) | if (i > 1) | ||||
| @@ -1010,9 +1010,9 @@ namespace Discord.API | |||||
| { | { | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
| if (args.Content.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); | |||||
| if (args._content.Length > DiscordRestConfig.MaxMessageSize) | |||||
| throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordRestConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| if (guildId != 0) | if (guildId != 0) | ||||
| return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); | return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false); | ||||
| @@ -1034,14 +1034,14 @@ namespace Discord.API | |||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| if (args.Content.GetValueOrDefault(null) == null) | |||||
| args.Content = ""; | |||||
| else if (args.Content.IsSpecified) | |||||
| if (args._content.GetValueOrDefault(null) == null) | |||||
| args._content = ""; | |||||
| else if (args._content.IsSpecified) | |||||
| { | { | ||||
| if (args.Content.Value == null) | |||||
| args.Content = ""; | |||||
| if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| if (args._content.Value == null) | |||||
| args._content = ""; | |||||
| if (args._content.Value?.Length > DiscordRestConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordRestConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| } | } | ||||
| if (guildId != 0) | if (guildId != 0) | ||||
| @@ -1084,8 +1084,8 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| var messageIds = args.MessageIds?.ToArray(); | |||||
| Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); | |||||
| var messageIds = args._messages; | |||||
| Preconditions.NotNull(args._messages, nameof(args.MessageIds)); | |||||
| Preconditions.AtMost(messageIds.Length, 100, nameof(messageIds.Length)); | Preconditions.AtMost(messageIds.Length, 100, nameof(messageIds.Length)); | ||||
| switch (messageIds.Length) | switch (messageIds.Length) | ||||
| @@ -1118,11 +1118,11 @@ namespace Discord.API | |||||
| Preconditions.NotEqual(channelId, 0, nameof(channelId)); | Preconditions.NotEqual(channelId, 0, nameof(channelId)); | ||||
| Preconditions.NotEqual(messageId, 0, nameof(messageId)); | Preconditions.NotEqual(messageId, 0, nameof(messageId)); | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| if (args.Content.IsSpecified) | |||||
| if (args._content.IsSpecified) | |||||
| { | { | ||||
| Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); | |||||
| if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| Preconditions.NotNullOrEmpty(args._content, nameof(args.Content)); | |||||
| if (args._content.Value.Length > DiscordRestConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordRestConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| } | } | ||||
| if (guildId != 0) | if (guildId != 0) | ||||
| @@ -1195,7 +1195,7 @@ namespace Discord.API | |||||
| public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); | |||||
| Preconditions.NotNullOrEmpty(args._username, nameof(args.Username)); | |||||
| return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | return await SendAsync<User>("PATCH", "users/@me", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -1209,7 +1209,7 @@ namespace Discord.API | |||||
| public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | public async Task<Channel> CreateDMChannelAsync(CreateDMChannelParams args, RequestOptions options = null) | ||||
| { | { | ||||
| Preconditions.NotNull(args, nameof(args)); | Preconditions.NotNull(args, nameof(args)); | ||||
| Preconditions.GreaterThan(args.RecipientId, 0, nameof(args.Recipient)); | |||||
| Preconditions.GreaterThan(args._recipientId, 0, nameof(args.Recipient)); | |||||
| return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | return await SendAsync<Channel>("POST", $"users/@me/channels", args, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -3,6 +3,7 @@ using System.Collections.Generic; | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class IdentifyParams | public class IdentifyParams | ||||
| { | { | ||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| @@ -4,6 +4,7 @@ using System.Linq; | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class RequestMembersParams | public class RequestMembersParams | ||||
| { | { | ||||
| [JsonProperty("query")] | [JsonProperty("query")] | ||||
| @@ -12,8 +13,8 @@ namespace Discord.API.Gateway | |||||
| public int Limit { get; set; } | public int Limit { get; set; } | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public IEnumerable<ulong> GuildIds { get; set; } | |||||
| [JsonIgnore] | |||||
| public IEnumerable<IGuild> Guilds { set { GuildIds = value.Select(x => x.Id); } } | |||||
| private ulong[] _guildIds; | |||||
| public IEnumerable<ulong> GuildIds { set { _guildIds = value.ToArray(); } } | |||||
| public IEnumerable<IGuild> Guilds { set { _guildIds = value.Select(x => x.Id).ToArray(); } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ResumeParams | public class ResumeParams | ||||
| { | { | ||||
| [JsonProperty("token")] | [JsonProperty("token")] | ||||
| @@ -2,6 +2,7 @@ | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class StatusUpdateParams | public class StatusUpdateParams | ||||
| { | { | ||||
| [JsonProperty("idle_since"), Int53] | [JsonProperty("idle_since"), Int53] | ||||
| @@ -2,6 +2,7 @@ | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class UpdateStatusParams | public class UpdateStatusParams | ||||
| { | { | ||||
| [JsonProperty("idle_since")] | [JsonProperty("idle_since")] | ||||
| @@ -2,6 +2,7 @@ | |||||
| namespace Discord.API.Gateway | namespace Discord.API.Gateway | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class VoiceStateUpdateParams | public class VoiceStateUpdateParams | ||||
| { | { | ||||
| [JsonProperty("self_mute")] | [JsonProperty("self_mute")] | ||||
| @@ -10,12 +11,11 @@ namespace Discord.API.Gateway | |||||
| public bool SelfDeaf { get; set; } | public bool SelfDeaf { get; set; } | ||||
| [JsonProperty("guild_id")] | [JsonProperty("guild_id")] | ||||
| public ulong GuildId { get; set; } | |||||
| [JsonIgnore] | |||||
| public IGuild Guild { set { GuildId = value.Id; } } | |||||
| public ulong? GuildId { get; set; } | |||||
| public IGuild Guild { set { GuildId = value?.Id; } } | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public ulong? ChannelId { get; set; } | public ulong? ChannelId { get; set; } | ||||
| [JsonIgnore] | |||||
| public IChannel Channel { set { ChannelId = value?.Id; } } | public IChannel Channel { set { ChannelId = value?.Id; } } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,11 +0,0 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| public class ApplicationInfo | |||||
| { | |||||
| } | |||||
| } | |||||
| @@ -2,15 +2,23 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateChannelInviteParams | public class CreateChannelInviteParams | ||||
| { | { | ||||
| [JsonProperty("max_age")] | [JsonProperty("max_age")] | ||||
| public Optional<int> MaxAge { get; set; } | |||||
| internal Optional<int> _maxAge { get; set; } | |||||
| public int MaxAge { set { _maxAge = value; } } | |||||
| [JsonProperty("max_uses")] | [JsonProperty("max_uses")] | ||||
| public Optional<int> MaxUses { get; set; } | |||||
| internal Optional<int> _maxUses { get; set; } | |||||
| public int MaxUses { set { _maxUses = value; } } | |||||
| [JsonProperty("temporary")] | [JsonProperty("temporary")] | ||||
| public Optional<bool> Temporary { get; set; } | |||||
| internal Optional<bool> _temporary { get; set; } | |||||
| public bool Temporary { set { _temporary = value; } } | |||||
| [JsonProperty("xkcdpass")] | [JsonProperty("xkcdpass")] | ||||
| public Optional<bool> XkcdPass { get; set; } | |||||
| internal Optional<bool> _xkcdPass { get; set; } | |||||
| public bool XkcdPass { set { _xkcdPass = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,12 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateDMChannelParams | public class CreateDMChannelParams | ||||
| { | { | ||||
| [JsonProperty("recipient_id")] | [JsonProperty("recipient_id")] | ||||
| public ulong RecipientId { get; set; } | |||||
| [JsonIgnore] | |||||
| public IUser Recipient { set { RecipientId = value.Id; } } | |||||
| internal ulong _recipientId { get; set; } | |||||
| public ulong RecipientId { set { _recipientId = value; } } | |||||
| public IUser Recipient { set { _recipientId = value.Id; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,11 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateGuildBanParams | public class CreateGuildBanParams | ||||
| { | { | ||||
| [JsonProperty("delete-message-days")] | [JsonProperty("delete-message-days")] | ||||
| public Optional<int> PruneDays { get; set; } | |||||
| internal Optional<int> _deleteMessageDays { get; set; } | |||||
| public int DeleteMessageDays { set { _deleteMessageDays = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,14 +2,19 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateGuildChannelParams | public class CreateGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | |||||
| internal string _name { get; set; } | |||||
| public string Name { set { _name = value; } } | |||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public ChannelType Type { get; set; } | |||||
| internal ChannelType _type { get; set; } | |||||
| public ChannelType Type { set { _type = value; } } | |||||
| [JsonProperty("bitrate")] | [JsonProperty("bitrate")] | ||||
| public Optional<int> Bitrate { get; set; } | |||||
| internal Optional<int> _bitrate { get; set; } | |||||
| public int Bitrate { set { _bitrate = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,13 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateGuildIntegrationParams | public class CreateGuildIntegrationParams | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | |||||
| public ulong Id { internal get; set; } | |||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public string Type { get; set; } | |||||
| public string Type { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,20 +3,17 @@ using System.IO; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateGuildParams | public class CreateGuildParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | |||||
| public string Name { internal get; set; } | |||||
| [JsonProperty("region")] | [JsonProperty("region")] | ||||
| public string Region { get; set; } | |||||
| public string Region { internal get; set; } | |||||
| [JsonProperty("icon")] | [JsonProperty("icon")] | ||||
| private Optional<Image> _icon { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<Stream> Icon | |||||
| { | |||||
| get { return _icon.IsSpecified ? _icon.Value.Stream : null; } | |||||
| set { _icon = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| internal Optional<Image?> _icon { get; set; } | |||||
| public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,14 +2,19 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class CreateMessageParams | public class CreateMessageParams | ||||
| { | { | ||||
| [JsonProperty("content")] | [JsonProperty("content")] | ||||
| public string Content { get; set; } = ""; | |||||
| internal string _content { get; set; } | |||||
| public string Content { set { _content = value; } } | |||||
| [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | |||||
| public Optional<string> Nonce { get; set; } | |||||
| [JsonProperty("tts", DefaultValueHandling = DefaultValueHandling.Ignore)] | |||||
| public Optional<bool> IsTTS { get; set; } | |||||
| [JsonProperty("nonce")] | |||||
| internal Optional<string> _nonce { get; set; } | |||||
| public string Nonce { set { _nonce = value; } } | |||||
| [JsonProperty("tts")] | |||||
| internal Optional<bool> _tts { get; set; } | |||||
| public bool IsTTS { set { _tts = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,11 +4,12 @@ using System.Linq; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class DeleteMessagesParams | public class DeleteMessagesParams | ||||
| { | { | ||||
| [JsonProperty("messages")] | [JsonProperty("messages")] | ||||
| public IEnumerable<ulong> MessageIds { get; set; } | |||||
| [JsonIgnore] | |||||
| public IEnumerable<IMessage> Messages { set { MessageIds = value.Select(x => x.Id); } } | |||||
| internal ulong[] _messages { get; set; } | |||||
| public IEnumerable<ulong> MessageIds { set { _messages = value.ToArray(); } } | |||||
| public IEnumerable<IMessage> Messages { set { _messages = value.Select(x => x.Id).ToArray(); } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,10 +2,12 @@ | |||||
| { | { | ||||
| public class GetChannelMessagesParams | public class GetChannelMessagesParams | ||||
| { | { | ||||
| public int Limit { get; set; } = DiscordConfig.MaxMessagesPerBatch; | |||||
| public Direction RelativeDirection { get; set; } = Direction.Before; | |||||
| public int Limit { internal get; set; } = DiscordRestConfig.MaxMessagesPerBatch; | |||||
| public Optional<ulong> RelativeMessageId { get; set; } | |||||
| public Optional<IMessage> RelativeMessage { set { RelativeMessageId = value.IsSpecified ? value.Value.Id : Optional.Create<ulong>(); } } | |||||
| public Direction RelativeDirection { internal get; set; } = Direction.Before; | |||||
| internal Optional<ulong> _relativeMessageId { get; set; } | |||||
| public ulong RelativeMessageId { set { _relativeMessageId = value; } } | |||||
| public IMessage RelativeMessage { set { _relativeMessageId = value.Id; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,7 +2,10 @@ | |||||
| { | { | ||||
| public class GetGuildMembersParams | public class GetGuildMembersParams | ||||
| { | { | ||||
| public Optional<int> Limit { get; set; } | |||||
| public Optional<ulong> AfterUserId { get; set; } | |||||
| internal Optional<int> _limit { get; set; } | |||||
| public int Limit { set { _limit = value; } } | |||||
| internal Optional<ulong> _afterUserId { get; set; } | |||||
| public ulong AfterUserId { set { _afterUserId = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,10 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class GuildPruneParams | public class GuildPruneParams | ||||
| { | { | ||||
| [JsonProperty("days")] | [JsonProperty("days")] | ||||
| public int Days = 30; | |||||
| public int Days { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,13 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyChannelPermissionsParams | public class ModifyChannelPermissionsParams | ||||
| { | { | ||||
| [JsonProperty("allow")] | [JsonProperty("allow")] | ||||
| public Optional<ulong> Allow { get; set; } | |||||
| public ulong Allow { internal get; set; } | |||||
| [JsonProperty("deny")] | [JsonProperty("deny")] | ||||
| public Optional<ulong> Deny { get; set; } | |||||
| public ulong Deny { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,10 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyCurrentUserNickParams | public class ModifyCurrentUserNickParams | ||||
| { | { | ||||
| [JsonProperty("nick")] | [JsonProperty("nick")] | ||||
| public string Nickname { get; set; } | |||||
| public string Nickname { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,24 +3,15 @@ using System.IO; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyCurrentUserParams | public class ModifyCurrentUserParams | ||||
| { | { | ||||
| [JsonProperty("username")] | [JsonProperty("username")] | ||||
| public Optional<string> Username { get; set; } | |||||
| internal Optional<string> _username { get; set; } | |||||
| public string Username { set { _username = value; } } | |||||
| [JsonProperty("avatar")] | [JsonProperty("avatar")] | ||||
| private Optional<Image> _avatar { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<Stream> Avatar | |||||
| { | |||||
| get { return _avatar.IsSpecified ? _avatar.Value.Stream : null; } | |||||
| set { _avatar = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| [JsonIgnore] | |||||
| internal Optional<string> AvatarHash | |||||
| { | |||||
| get { return _avatar.IsSpecified ? _avatar.Value.Hash : null; } | |||||
| set { _avatar = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| internal Optional<Image> _avatar { get; set; } | |||||
| public Stream Avatar { set { _avatar = new Image(value); } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,15 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildChannelParams | public class ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public Optional<string> Name { get; set; } | |||||
| internal Optional<string> _name { get; set; } | |||||
| public string Name { set { _name = value; } } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public Optional<int> Position { get; set; } | |||||
| internal Optional<int> _position { get; set; } | |||||
| public int Position { set { _position = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,13 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildChannelsParams | public class ModifyGuildChannelsParams | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | |||||
| public ulong Id { internal get; set; } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public Optional<int> Position { get; set; } | |||||
| public int Position { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,14 +2,16 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildEmbedParams | public class ModifyGuildEmbedParams | ||||
| { | { | ||||
| [JsonProperty("enabled")] | [JsonProperty("enabled")] | ||||
| public Optional<bool> Enabled { get; set; } | |||||
| internal Optional<bool> _enabled { get; set; } | |||||
| public bool Enabled { set { _enabled = value; } } | |||||
| [JsonProperty("channel")] | [JsonProperty("channel")] | ||||
| public Optional<ulong> ChannelId { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<IVoiceChannel> Channel { set { ChannelId = value.IsSpecified ? value.Value.Id : Optional.Create<ulong>(); } } | |||||
| internal Optional<ulong?> _channelId { get; set; } | |||||
| public ulong? ChannelId { set { _channelId = value; } } | |||||
| public IVoiceChannel Channel { set { _channelId = value != null ? value.Id : (ulong?)null; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,13 +2,19 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildIntegrationParams | public class ModifyGuildIntegrationParams | ||||
| { | { | ||||
| [JsonProperty("expire_behavior")] | [JsonProperty("expire_behavior")] | ||||
| public Optional<int> ExpireBehavior { get; set; } | |||||
| internal Optional<int> _expireBehavior { get; set; } | |||||
| public int ExpireBehavior { set { _expireBehavior = value; } } | |||||
| [JsonProperty("expire_grace_period")] | [JsonProperty("expire_grace_period")] | ||||
| public Optional<int> ExpireGracePeriod { get; set; } | |||||
| internal Optional<int> _expireGracePeriod { get; set; } | |||||
| public int ExpireGracePeriod { set { _expireGracePeriod = value; } } | |||||
| [JsonProperty("enable_emoticons")] | [JsonProperty("enable_emoticons")] | ||||
| public Optional<bool> EnableEmoticons { get; set; } | |||||
| internal Optional<bool> _enableEmoticons { get; set; } | |||||
| public bool EnableEmoticons { set { _enableEmoticons = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,23 +4,29 @@ using System.Linq; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildMemberParams | public class ModifyGuildMemberParams | ||||
| { | { | ||||
| [JsonProperty("mute")] | [JsonProperty("mute")] | ||||
| public Optional<bool> Mute { get; set; } | |||||
| internal Optional<bool> _mute { get; set; } | |||||
| public bool Mute { set { _mute = value; } } | |||||
| [JsonProperty("deaf")] | [JsonProperty("deaf")] | ||||
| public Optional<bool> Deaf { get; set; } | |||||
| internal Optional<bool> _deaf { get; set; } | |||||
| public bool Deaf { set { _deaf = value; } } | |||||
| [JsonProperty("nick")] | [JsonProperty("nick")] | ||||
| public Optional<string> Nickname { get; set; } | |||||
| internal Optional<string> _nickname { get; set; } | |||||
| public string Nickname { set { _nickname = value; } } | |||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public Optional<IEnumerable<ulong>> RoleIds { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<IEnumerable<IRole>> Roles { set { RoleIds = value.IsSpecified ? Optional.Create(value.Value.Select(x => x.Id)) : Optional.Create<IEnumerable<ulong>>(); } } | |||||
| internal Optional<ulong[]> _roleIds { get; set; } | |||||
| public IEnumerable<ulong> RoleIds { set { _roleIds = value.ToArray(); } } | |||||
| public IEnumerable<IRole> Roles { set { _roleIds = value.Select(x => x.Id).ToArray(); } } | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public Optional<ulong> VoiceChannelId { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<IVoiceChannel> VoiceChannel { set { VoiceChannelId = value.IsSpecified ? value.Value.Id : Optional.Create<ulong>(); } } | |||||
| internal Optional<ulong> _channelId { get; set; } | |||||
| public ulong VoiceChannelId { set { _channelId = value; } } | |||||
| public IVoiceChannel VoiceChannel { set { _channelId = value.Id; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,60 +3,49 @@ using System.IO; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildParams | public class ModifyGuildParams | ||||
| { | { | ||||
| [JsonProperty("username")] | [JsonProperty("username")] | ||||
| public Optional<string> Username { get; set; } | |||||
| internal Optional<string> _username { get; set; } | |||||
| public string Username { set { _username = value; } } | |||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public Optional<string> Name { get; set; } | |||||
| internal Optional<string> _name { get; set; } | |||||
| public string Name { set { _name = value; } } | |||||
| [JsonProperty("region")] | [JsonProperty("region")] | ||||
| public Optional<IVoiceRegion> Region { get; set; } | |||||
| internal Optional<IVoiceRegion> _region { get; set; } | |||||
| public IVoiceRegion Region { set { _region = Optional.Create(value); } } | |||||
| [JsonProperty("verification_level")] | [JsonProperty("verification_level")] | ||||
| public Optional<VerificationLevel> VerificationLevel { get; set; } | |||||
| internal Optional<VerificationLevel> _verificationLevel { get; set; } | |||||
| public VerificationLevel VerificationLevel { set { _verificationLevel = value; } } | |||||
| [JsonProperty("default_message_notifications")] | [JsonProperty("default_message_notifications")] | ||||
| public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; } | |||||
| internal Optional<DefaultMessageNotifications> _defaultMessageNotifications { get; set; } | |||||
| public DefaultMessageNotifications DefaultMessageNotifications { set { _defaultMessageNotifications = value; } } | |||||
| [JsonProperty("afk_timeout")] | [JsonProperty("afk_timeout")] | ||||
| public Optional<int> AFKTimeout { get; set; } | |||||
| internal Optional<int> _afkTimeout { get; set; } | |||||
| public int AFKTimeout { set { _afkTimeout = value; } } | |||||
| [JsonProperty("icon")] | [JsonProperty("icon")] | ||||
| private Optional<Image> _icon { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<Stream> Icon | |||||
| { | |||||
| get { return _icon.IsSpecified ? _icon.Value.Stream : null; } | |||||
| set { _icon = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| [JsonIgnore] | |||||
| internal Optional<string> IconHash | |||||
| { | |||||
| get { return _icon.IsSpecified ? _icon.Value.Hash : null; } | |||||
| set { _icon = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| internal Optional<Image?> _icon { get; set; } | |||||
| public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } } | |||||
| [JsonProperty("splash")] | [JsonProperty("splash")] | ||||
| private Optional<Image> _splash { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<Stream> Splash | |||||
| { | |||||
| get { return _splash.IsSpecified ? _splash.Value.Stream : null; } | |||||
| set { _splash = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| [JsonIgnore] | |||||
| internal Optional<string> SplashHash | |||||
| { | |||||
| get { return _splash.IsSpecified ? _splash.Value.Hash : null; } | |||||
| set { _splash = value.IsSpecified ? new Image(value.Value) : Optional.Create<Image>(); } | |||||
| } | |||||
| internal Optional<Image?> _splash { get; set; } | |||||
| public Stream Splash { set { _splash = value != null ? new Image(value) : (Image?)null; } } | |||||
| [JsonProperty("afk_channel_id")] | [JsonProperty("afk_channel_id")] | ||||
| public Optional<ulong?> AFKChannelId { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<IVoiceChannel> AFKChannel { set { OwnerId = value.IsSpecified ? value.Value.Id : Optional.Create<ulong>(); } } | |||||
| internal Optional<ulong?> _afkChannelId { get; set; } | |||||
| public ulong? AFKChannelId { set { _afkChannelId = value; } } | |||||
| public IVoiceChannel AFKChannel { set { _afkChannelId = value?.Id; } } | |||||
| [JsonProperty("owner_id")] | [JsonProperty("owner_id")] | ||||
| public Optional<ulong> OwnerId { get; set; } | |||||
| [JsonIgnore] | |||||
| public Optional<IGuildUser> Owner { set { OwnerId = value.IsSpecified ? value.Value.Id : Optional.Create<ulong>(); } } | |||||
| internal Optional<ulong> _ownerId { get; set; } | |||||
| public ulong OwnerId { set { _ownerId = value; } } | |||||
| public IGuildUser Owner { set { _ownerId = value.Id; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,17 +2,27 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildRoleParams | public class ModifyGuildRoleParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public Optional<string> Name { get; set; } | |||||
| internal Optional<string> _name { get; set; } | |||||
| public string Name { set { _name = value; } } | |||||
| [JsonProperty("permissions")] | [JsonProperty("permissions")] | ||||
| public Optional<ulong> Permissions { get; set; } | |||||
| internal Optional<ulong> _permissions { get; set; } | |||||
| public ulong Permissions { set { _permissions = value; } } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public Optional<int> Position { get; set; } | |||||
| internal Optional<int> _position { get; set; } | |||||
| public int Position { set { _position = value; } } | |||||
| [JsonProperty("color")] | [JsonProperty("color")] | ||||
| public Optional<uint> Color { get; set; } | |||||
| internal Optional<uint> _color { get; set; } | |||||
| public uint Color { set { _color = value; } } | |||||
| [JsonProperty("hoist")] | [JsonProperty("hoist")] | ||||
| public Optional<bool> Hoist { get; set; } | |||||
| internal Optional<bool> _hoist { get; set; } | |||||
| public bool Hoist { set { _hoist = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,10 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyGuildRolesParams : ModifyGuildRoleParams | public class ModifyGuildRolesParams : ModifyGuildRoleParams | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | |||||
| public ulong Id { internal get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,11 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyMessageParams | public class ModifyMessageParams | ||||
| { | { | ||||
| [JsonProperty("content")] | [JsonProperty("content")] | ||||
| public Optional<string> Content { get; set; } = ""; | |||||
| internal Optional<string> _content { get; set; } | |||||
| public string Content { set { _content = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,7 +2,10 @@ | |||||
| { | { | ||||
| public class ModifyPresenceParams | public class ModifyPresenceParams | ||||
| { | { | ||||
| public Optional<UserStatus> Status { get; set; } | |||||
| public Optional<Discord.Game> Game { get; set; } | |||||
| internal Optional<UserStatus> _status { get; set; } | |||||
| public UserStatus Status { set { _status = value; } } | |||||
| internal Optional<Discord.Game> _game { get; set; } | |||||
| public Discord.Game Game { set { _game = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,9 +2,11 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyTextChannelParams : ModifyGuildChannelParams | public class ModifyTextChannelParams : ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("topic")] | [JsonProperty("topic")] | ||||
| public Optional<string> Topic { get; set; } | |||||
| internal Optional<string> _topic { get; set; } | |||||
| public string Topic { set { _topic = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,11 +2,15 @@ | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| [JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
| public class ModifyVoiceChannelParams : ModifyGuildChannelParams | public class ModifyVoiceChannelParams : ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("bitrate")] | [JsonProperty("bitrate")] | ||||
| public Optional<int> Bitrate { get; set; } | |||||
| internal Optional<int> _bitrate { get; set; } | |||||
| public int Bitrate { set { _bitrate = value; } } | |||||
| [JsonProperty("user_limit")] | [JsonProperty("user_limit")] | ||||
| public Optional<int> UserLimit { get; set; } | |||||
| internal Optional<int> _userLimit { get; set; } | |||||
| public int UserLimit { set { _userLimit = value; } } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,28 +6,35 @@ namespace Discord.API.Rest | |||||
| { | { | ||||
| public class UploadFileParams | public class UploadFileParams | ||||
| { | { | ||||
| public Stream File { get; set; } | |||||
| public string Filename { get; set; } = "unknown.dat"; | |||||
| public Stream File { internal get; set; } | |||||
| public Optional<string> Content { get; set; } | |||||
| public Optional<string> Nonce { get; set; } | |||||
| public Optional<bool> IsTTS { get; set; } | |||||
| internal Optional<string> _filename { get; set; } | |||||
| public string Filename { set { _filename = value; } } | |||||
| internal Optional<string> _content { get; set; } | |||||
| public string Content { set { _content = value; } } | |||||
| internal Optional<string> _nonce { get; set; } | |||||
| public string Nonce { set { _nonce = value; } } | |||||
| internal Optional<bool> _isTTS { get; set; } | |||||
| public bool IsTTS { set { _isTTS = value; } } | |||||
| public UploadFileParams(Stream file) | public UploadFileParams(Stream file) | ||||
| { | { | ||||
| File = file; | File = file; | ||||
| } | } | ||||
| public IReadOnlyDictionary<string, object> ToDictionary() | |||||
| internal IReadOnlyDictionary<string, object> ToDictionary() | |||||
| { | { | ||||
| var d = new Dictionary<string, object>(); | var d = new Dictionary<string, object>(); | ||||
| d["file"] = new MultipartFile(File, Filename); | |||||
| if (Content.IsSpecified) | |||||
| d["content"] = Content.Value; | |||||
| if (IsTTS.IsSpecified) | |||||
| d["tts"] = IsTTS.Value.ToString(); | |||||
| if (Nonce.IsSpecified) | |||||
| d["nonce"] = Nonce.Value; | |||||
| d["file"] = new MultipartFile(File, _filename.GetValueOrDefault("unknown.dat")); | |||||
| if (_content.IsSpecified) | |||||
| d["content"] = _content.Value; | |||||
| if (_isTTS.IsSpecified) | |||||
| d["tts"] = _isTTS.Value.ToString(); | |||||
| if (_nonce.IsSpecified) | |||||
| d["nonce"] = _nonce.Value; | |||||
| return d; | return d; | ||||
| } | } | ||||
| } | } | ||||
| @@ -49,7 +49,7 @@ namespace Discord.Audio | |||||
| private uint _ssrc; | private uint _ssrc; | ||||
| private byte[] _secretKey; | private byte[] _secretKey; | ||||
| public CachedGuild Guild { get; } | |||||
| public SocketGuild Guild { get; } | |||||
| public DiscordVoiceAPIClient ApiClient { get; private set; } | public DiscordVoiceAPIClient ApiClient { get; private set; } | ||||
| public ConnectionState ConnectionState { get; private set; } | public ConnectionState ConnectionState { get; private set; } | ||||
| public int Latency { get; private set; } | public int Latency { get; private set; } | ||||
| @@ -57,7 +57,7 @@ namespace Discord.Audio | |||||
| private DiscordSocketClient Discord => Guild.Discord; | private DiscordSocketClient Discord => Guild.Discord; | ||||
| /// <summary> Creates a new REST/WebSocket discord client. </summary> | /// <summary> Creates a new REST/WebSocket discord client. </summary> | ||||
| public AudioClient(CachedGuild guild, int id) | |||||
| public AudioClient(SocketGuild guild, int id) | |||||
| { | { | ||||
| Guild = guild; | Guild = guild; | ||||
| @@ -13,76 +13,76 @@ namespace Discord | |||||
| private const double AverageUsersPerGuild = 47.78; //Source: Googie2149 | private const double AverageUsersPerGuild = 47.78; //Source: Googie2149 | ||||
| private const double CollectionMultiplier = 1.05; //Add 5% buffer to handle growth | private const double CollectionMultiplier = 1.05; //Add 5% buffer to handle growth | ||||
| private readonly ConcurrentDictionary<ulong, ICachedChannel> _channels; | |||||
| private readonly ConcurrentDictionary<ulong, CachedDMChannel> _dmChannels; | |||||
| private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds; | |||||
| private readonly ConcurrentDictionary<ulong, CachedGlobalUser> _users; | |||||
| private readonly ConcurrentDictionary<ulong, ISocketChannel> _channels; | |||||
| private readonly ConcurrentDictionary<ulong, SocketDMChannel> _dmChannels; | |||||
| private readonly ConcurrentDictionary<ulong, SocketGuild> _guilds; | |||||
| private readonly ConcurrentDictionary<ulong, SocketGlobalUser> _users; | |||||
| private readonly ConcurrentHashSet<ulong> _groupChannels; | private readonly ConcurrentHashSet<ulong> _groupChannels; | ||||
| internal IReadOnlyCollection<ICachedChannel> Channels => _channels.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<CachedDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<CachedGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as CachedGroupChannel).ToReadOnlyCollection(_groupChannels); | |||||
| internal IReadOnlyCollection<CachedGuild> Guilds => _guilds.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<CachedGlobalUser> Users => _users.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<ISocketChannel> Channels => _channels.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<SocketDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<SocketGroupChannel> GroupChannels => _groupChannels.Select(x => GetChannel(x) as SocketGroupChannel).ToReadOnlyCollection(_groupChannels); | |||||
| internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection(); | |||||
| internal IReadOnlyCollection<ICachedPrivateChannel> PrivateChannels => | |||||
| _dmChannels.Select(x => x.Value as ICachedPrivateChannel).Concat( | |||||
| _groupChannels.Select(x => GetChannel(x) as ICachedPrivateChannel)) | |||||
| internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => | |||||
| _dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat( | |||||
| _groupChannels.Select(x => GetChannel(x) as ISocketPrivateChannel)) | |||||
| .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); | .ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count); | ||||
| public DataStore(int guildCount, int dmChannelCount) | public DataStore(int guildCount, int dmChannelCount) | ||||
| { | { | ||||
| double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | ||||
| double estimatedUsersCount = guildCount * AverageUsersPerGuild; | double estimatedUsersCount = guildCount * AverageUsersPerGuild; | ||||
| _channels = new ConcurrentDictionary<ulong, ICachedChannel>(CollectionConcurrencyLevel, (int)(estimatedChannelCount * CollectionMultiplier)); | |||||
| _dmChannels = new ConcurrentDictionary<ulong, CachedDMChannel>(CollectionConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier)); | |||||
| _guilds = new ConcurrentDictionary<ulong, CachedGuild>(CollectionConcurrencyLevel, (int)(guildCount * CollectionMultiplier)); | |||||
| _users = new ConcurrentDictionary<ulong, CachedGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); | |||||
| _channels = new ConcurrentDictionary<ulong, ISocketChannel>(CollectionConcurrencyLevel, (int)(estimatedChannelCount * CollectionMultiplier)); | |||||
| _dmChannels = new ConcurrentDictionary<ulong, SocketDMChannel>(CollectionConcurrencyLevel, (int)(dmChannelCount * CollectionMultiplier)); | |||||
| _guilds = new ConcurrentDictionary<ulong, SocketGuild>(CollectionConcurrencyLevel, (int)(guildCount * CollectionMultiplier)); | |||||
| _users = new ConcurrentDictionary<ulong, SocketGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); | |||||
| _groupChannels = new ConcurrentHashSet<ulong>(CollectionConcurrencyLevel, (int)(10 * CollectionMultiplier)); | _groupChannels = new ConcurrentHashSet<ulong>(CollectionConcurrencyLevel, (int)(10 * CollectionMultiplier)); | ||||
| } | } | ||||
| internal ICachedChannel GetChannel(ulong id) | |||||
| internal ISocketChannel GetChannel(ulong id) | |||||
| { | { | ||||
| ICachedChannel channel; | |||||
| ISocketChannel channel; | |||||
| if (_channels.TryGetValue(id, out channel)) | if (_channels.TryGetValue(id, out channel)) | ||||
| return channel; | return channel; | ||||
| return null; | return null; | ||||
| } | } | ||||
| internal CachedDMChannel GetDMChannel(ulong userId) | |||||
| internal SocketDMChannel GetDMChannel(ulong userId) | |||||
| { | { | ||||
| CachedDMChannel channel; | |||||
| SocketDMChannel channel; | |||||
| if (_dmChannels.TryGetValue(userId, out channel)) | if (_dmChannels.TryGetValue(userId, out channel)) | ||||
| return channel; | return channel; | ||||
| return null; | return null; | ||||
| } | } | ||||
| internal void AddChannel(ICachedChannel channel) | |||||
| internal void AddChannel(ISocketChannel channel) | |||||
| { | { | ||||
| _channels[channel.Id] = channel; | _channels[channel.Id] = channel; | ||||
| var dmChannel = channel as CachedDMChannel; | |||||
| var dmChannel = channel as SocketDMChannel; | |||||
| if (dmChannel != null) | if (dmChannel != null) | ||||
| _dmChannels[dmChannel.Recipient.Id] = dmChannel; | _dmChannels[dmChannel.Recipient.Id] = dmChannel; | ||||
| else | else | ||||
| { | { | ||||
| var groupChannel = channel as CachedGroupChannel; | |||||
| var groupChannel = channel as SocketGroupChannel; | |||||
| if (groupChannel != null) | if (groupChannel != null) | ||||
| _groupChannels.TryAdd(groupChannel.Id); | _groupChannels.TryAdd(groupChannel.Id); | ||||
| } | } | ||||
| } | } | ||||
| internal ICachedChannel RemoveChannel(ulong id) | |||||
| internal ISocketChannel RemoveChannel(ulong id) | |||||
| { | { | ||||
| ICachedChannel channel; | |||||
| ISocketChannel channel; | |||||
| if (_channels.TryRemove(id, out channel)) | if (_channels.TryRemove(id, out channel)) | ||||
| { | { | ||||
| var dmChannel = channel as CachedDMChannel; | |||||
| var dmChannel = channel as SocketDMChannel; | |||||
| if (dmChannel != null) | if (dmChannel != null) | ||||
| { | { | ||||
| CachedDMChannel ignored; | |||||
| SocketDMChannel ignored; | |||||
| _dmChannels.TryRemove(dmChannel.Recipient.Id, out ignored); | _dmChannels.TryRemove(dmChannel.Recipient.Id, out ignored); | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| var groupChannel = channel as CachedGroupChannel; | |||||
| var groupChannel = channel as SocketGroupChannel; | |||||
| if (groupChannel != null) | if (groupChannel != null) | ||||
| _groupChannels.TryRemove(id); | _groupChannels.TryRemove(id); | ||||
| } | } | ||||
| @@ -91,39 +91,39 @@ namespace Discord | |||||
| return null; | return null; | ||||
| } | } | ||||
| internal CachedGuild GetGuild(ulong id) | |||||
| internal SocketGuild GetGuild(ulong id) | |||||
| { | { | ||||
| CachedGuild guild; | |||||
| SocketGuild guild; | |||||
| if (_guilds.TryGetValue(id, out guild)) | if (_guilds.TryGetValue(id, out guild)) | ||||
| return guild; | return guild; | ||||
| return null; | return null; | ||||
| } | } | ||||
| internal void AddGuild(CachedGuild guild) | |||||
| internal void AddGuild(SocketGuild guild) | |||||
| { | { | ||||
| _guilds[guild.Id] = guild; | _guilds[guild.Id] = guild; | ||||
| } | } | ||||
| internal CachedGuild RemoveGuild(ulong id) | |||||
| internal SocketGuild RemoveGuild(ulong id) | |||||
| { | { | ||||
| CachedGuild guild; | |||||
| SocketGuild guild; | |||||
| if (_guilds.TryRemove(id, out guild)) | if (_guilds.TryRemove(id, out guild)) | ||||
| return guild; | return guild; | ||||
| return null; | return null; | ||||
| } | } | ||||
| internal CachedGlobalUser GetUser(ulong id) | |||||
| internal SocketGlobalUser GetUser(ulong id) | |||||
| { | { | ||||
| CachedGlobalUser user; | |||||
| SocketGlobalUser user; | |||||
| if (_users.TryGetValue(id, out user)) | if (_users.TryGetValue(id, out user)) | ||||
| return user; | return user; | ||||
| return null; | return null; | ||||
| } | } | ||||
| internal CachedGlobalUser GetOrAddUser(ulong id, Func<ulong, CachedGlobalUser> userFactory) | |||||
| internal SocketGlobalUser GetOrAddUser(ulong id, Func<ulong, SocketGlobalUser> userFactory) | |||||
| { | { | ||||
| return _users.GetOrAdd(id, userFactory); | return _users.GetOrAdd(id, userFactory); | ||||
| } | } | ||||
| internal CachedGlobalUser RemoveUser(ulong id) | |||||
| internal SocketGlobalUser RemoveUser(ulong id) | |||||
| { | { | ||||
| CachedGlobalUser user; | |||||
| SocketGlobalUser user; | |||||
| if (_users.TryRemove(id, out user)) | if (_users.TryRemove(id, out user)) | ||||
| return user; | return user; | ||||
| return null; | return null; | ||||
| @@ -1,33 +1,17 @@ | |||||
| using Discord.Net.Rest; | |||||
| using System.Reflection; | |||||
| using System.Reflection; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class DiscordConfig | public class DiscordConfig | ||||
| { | { | ||||
| public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown"; | |||||
| public static string FullVersion { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion ?? "Unknown"; | |||||
| public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; | |||||
| public const int APIVersion = 6; | |||||
| public const string GatewayEncoding = "json"; | |||||
| public const int APIVersion = 6; | |||||
| public static string Version { get; } = typeof(DiscordRestConfig).GetTypeInfo().Assembly?.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion ?? "Unknown"; | |||||
| public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/"; | public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/"; | ||||
| public const string CDNUrl = "https://cdn.discordapp.com/"; | public const string CDNUrl = "https://cdn.discordapp.com/"; | ||||
| public const string InviteUrl = "https://discord.gg/"; | public const string InviteUrl = "https://discord.gg/"; | ||||
| public const int MaxMessageSize = 2000; | |||||
| public const int MaxMessagesPerBatch = 100; | |||||
| public const int MaxUsersPerBatch = 1000; | |||||
| internal const int RestTimeout = 10000; | |||||
| internal const int MessageQueueInterval = 100; | |||||
| internal const int WebSocketQueueInterval = 100; | |||||
| /// <summary> Gets or sets the minimum log level severity that will be sent to the LogMessage event. </summary> | /// <summary> Gets or sets the minimum log level severity that will be sent to the LogMessage event. </summary> | ||||
| public LogSeverity LogLevel { get; set; } = LogSeverity.Info; | public LogSeverity LogLevel { get; set; } = LogSeverity.Info; | ||||
| /// <summary> Gets or sets the provider used to generate new REST connections. </summary> | |||||
| public RestClientProvider RestClientProvider { get; set; } = url => new DefaultRestClient(url); | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,11 +10,10 @@ using System.Linq; | |||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
| using System.Collections.Concurrent; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class DiscordClient : IDiscordClient | |||||
| public class DiscordRestClient : IDiscordClient | |||||
| { | { | ||||
| private readonly object _eventLock = new object(); | private readonly object _eventLock = new object(); | ||||
| @@ -38,9 +37,9 @@ namespace Discord | |||||
| public LoginState LoginState { get; private set; } | public LoginState LoginState { get; private set; } | ||||
| /// <summary> Creates a new REST-only discord client. </summary> | /// <summary> Creates a new REST-only discord client. </summary> | ||||
| public DiscordClient() : this(new DiscordConfig()) { } | |||||
| public DiscordRestClient() : this(new DiscordRestConfig()) { } | |||||
| /// <summary> Creates a new REST-only discord client. </summary> | /// <summary> Creates a new REST-only discord client. </summary> | ||||
| public DiscordClient(DiscordConfig config) | |||||
| public DiscordRestClient(DiscordRestConfig config) | |||||
| { | { | ||||
| LogManager = new LogManager(config.LogLevel); | LogManager = new LogManager(config.LogLevel); | ||||
| LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); | LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); | ||||
| @@ -305,9 +304,10 @@ namespace Discord | |||||
| private async Task WriteInitialLog() | private async Task WriteInitialLog() | ||||
| { | { | ||||
| if (this is DiscordSocketClient) | if (this is DiscordSocketClient) | ||||
| await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.FullVersion} (API v{DiscordConfig.APIVersion}, {DiscordConfig.GatewayEncoding})").ConfigureAwait(false); | |||||
| await _clientLogger.InfoAsync($"DiscordSocketClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion}, {DiscordSocketConfig.GatewayEncoding})").ConfigureAwait(false); | |||||
| else | else | ||||
| await _clientLogger.InfoAsync($"DiscordClient v{DiscordConfig.FullVersion} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); | |||||
| await _clientLogger.InfoAsync($"DiscordRestClient v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); | |||||
| await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false); | await _clientLogger.VerboseAsync($"Runtime: {RuntimeInformation.FrameworkDescription.Trim()} ({ToArchString(RuntimeInformation.ProcessArchitecture)})").ConfigureAwait(false); | ||||
| await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false); | await _clientLogger.VerboseAsync($"OS: {RuntimeInformation.OSDescription.Trim()} ({ToArchString(RuntimeInformation.OSArchitecture)})").ConfigureAwait(false); | ||||
| await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false); | await _clientLogger.VerboseAsync($"Processors: {Environment.ProcessorCount}").ConfigureAwait(false); | ||||
| @@ -0,0 +1,20 @@ | |||||
| using Discord.Net.Rest; | |||||
| namespace Discord | |||||
| { | |||||
| public class DiscordRestConfig : DiscordConfig | |||||
| { | |||||
| public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; | |||||
| public const int MaxMessageSize = 2000; | |||||
| public const int MaxMessagesPerBatch = 100; | |||||
| public const int MaxUsersPerBatch = 1000; | |||||
| internal const int RestTimeout = 10000; | |||||
| internal const int MessageQueueInterval = 100; | |||||
| internal const int WebSocketQueueInterval = 100; | |||||
| /// <summary> Gets or sets the provider used to generate new REST connections. </summary> | |||||
| public RestClientProvider RestClientProvider { get; set; } = url => new DefaultRestClient(url); | |||||
| } | |||||
| } | |||||
| @@ -16,7 +16,7 @@ using System.Threading.Tasks; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public partial class DiscordSocketClient : DiscordClient, IDiscordClient | |||||
| public partial class DiscordSocketClient : DiscordRestClient, IDiscordClient | |||||
| { | { | ||||
| private readonly ConcurrentQueue<ulong> _largeGuilds; | private readonly ConcurrentQueue<ulong> _largeGuilds; | ||||
| private readonly ILogger _gatewayLogger; | private readonly ILogger _gatewayLogger; | ||||
| @@ -44,19 +44,16 @@ namespace Discord | |||||
| /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> | /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> | ||||
| public int Latency { get; private set; } | public int Latency { get; private set; } | ||||
| //From DiscordConfig | |||||
| //From DiscordSocketConfig | |||||
| internal int TotalShards { get; private set; } | internal int TotalShards { get; private set; } | ||||
| internal int ConnectionTimeout { get; private set; } | |||||
| internal int ReconnectDelay { get; private set; } | |||||
| internal int FailedReconnectDelay { get; private set; } | |||||
| internal int MessageCacheSize { get; private set; } | internal int MessageCacheSize { get; private set; } | ||||
| internal int LargeThreshold { get; private set; } | internal int LargeThreshold { get; private set; } | ||||
| internal AudioMode AudioMode { get; private set; } | internal AudioMode AudioMode { get; private set; } | ||||
| internal DataStore DataStore { get; private set; } | internal DataStore DataStore { get; private set; } | ||||
| internal WebSocketProvider WebSocketProvider { get; private set; } | internal WebSocketProvider WebSocketProvider { get; private set; } | ||||
| internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser; | |||||
| internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds; | |||||
| internal SocketSelfUser CurrentUser => _currentUser as SocketSelfUser; | |||||
| internal IReadOnlyCollection<SocketGuild> Guilds => DataStore.Guilds; | |||||
| internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); | ||||
| /// <summary> Creates a new REST/WebSocket discord client. </summary> | /// <summary> Creates a new REST/WebSocket discord client. </summary> | ||||
| @@ -67,9 +64,6 @@ namespace Discord | |||||
| { | { | ||||
| ShardId = config.ShardId; | ShardId = config.ShardId; | ||||
| TotalShards = config.TotalShards; | TotalShards = config.TotalShards; | ||||
| ConnectionTimeout = config.ConnectionTimeout; | |||||
| ReconnectDelay = config.ReconnectDelay; | |||||
| FailedReconnectDelay = config.FailedReconnectDelay; | |||||
| MessageCacheSize = config.MessageCacheSize; | MessageCacheSize = config.MessageCacheSize; | ||||
| LargeThreshold = config.LargeThreshold; | LargeThreshold = config.LargeThreshold; | ||||
| AudioMode = config.AudioMode; | AudioMode = config.AudioMode; | ||||
| @@ -340,15 +334,15 @@ namespace Discord | |||||
| { | { | ||||
| return Task.FromResult<IReadOnlyCollection<IGuild>>(Guilds); | return Task.FromResult<IReadOnlyCollection<IGuild>>(Guilds); | ||||
| } | } | ||||
| internal CachedGuild AddGuild(ExtendedGuild model, DataStore dataStore) | |||||
| internal SocketGuild AddGuild(ExtendedGuild model, DataStore dataStore) | |||||
| { | { | ||||
| var guild = new CachedGuild(this, model, dataStore); | |||||
| var guild = new SocketGuild(this, model, dataStore); | |||||
| dataStore.AddGuild(guild); | dataStore.AddGuild(guild); | ||||
| if (model.Large) | if (model.Large) | ||||
| _largeGuilds.Enqueue(model.Id); | _largeGuilds.Enqueue(model.Id); | ||||
| return guild; | return guild; | ||||
| } | } | ||||
| internal CachedGuild RemoveGuild(ulong id) | |||||
| internal SocketGuild RemoveGuild(ulong id) | |||||
| { | { | ||||
| var guild = DataStore.RemoveGuild(id); | var guild = DataStore.RemoveGuild(id); | ||||
| foreach (var channel in guild.Channels) | foreach (var channel in guild.Channels) | ||||
| @@ -367,7 +361,7 @@ namespace Discord | |||||
| { | { | ||||
| return Task.FromResult<IReadOnlyCollection<IPrivateChannel>>(DataStore.PrivateChannels); | return Task.FromResult<IReadOnlyCollection<IPrivateChannel>>(DataStore.PrivateChannels); | ||||
| } | } | ||||
| internal ICachedChannel AddPrivateChannel(API.Channel model, DataStore dataStore) | |||||
| internal ISocketChannel AddPrivateChannel(API.Channel model, DataStore dataStore) | |||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| { | { | ||||
| @@ -375,13 +369,13 @@ namespace Discord | |||||
| { | { | ||||
| var recipients = model.Recipients.Value; | var recipients = model.Recipients.Value; | ||||
| var user = GetOrAddUser(recipients[0], dataStore); | var user = GetOrAddUser(recipients[0], dataStore); | ||||
| var channel = new CachedDMChannel(this, new CachedDMUser(user), model); | |||||
| var channel = new SocketDMChannel(this, new SocketDMUser(user), model); | |||||
| dataStore.AddChannel(channel); | dataStore.AddChannel(channel); | ||||
| return channel; | return channel; | ||||
| } | } | ||||
| case ChannelType.Group: | case ChannelType.Group: | ||||
| { | { | ||||
| var channel = new CachedGroupChannel(this, model); | |||||
| var channel = new SocketGroupChannel(this, model); | |||||
| channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation, dataStore); | channel.UpdateUsers(model.Recipients.Value, UpdateSource.Creation, dataStore); | ||||
| dataStore.AddChannel(channel); | dataStore.AddChannel(channel); | ||||
| return channel; | return channel; | ||||
| @@ -390,9 +384,9 @@ namespace Discord | |||||
| throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); | throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); | ||||
| } | } | ||||
| } | } | ||||
| internal ICachedChannel RemovePrivateChannel(ulong id) | |||||
| internal ISocketChannel RemovePrivateChannel(ulong id) | |||||
| { | { | ||||
| var channel = DataStore.RemoveChannel(id) as ICachedPrivateChannel; | |||||
| var channel = DataStore.RemoveChannel(id) as ISocketPrivateChannel; | |||||
| foreach (var recipient in channel.Recipients) | foreach (var recipient in channel.Recipients) | ||||
| recipient.User.RemoveRef(this); | recipient.User.RemoveRef(this); | ||||
| return channel; | return channel; | ||||
| @@ -413,13 +407,13 @@ namespace Discord | |||||
| { | { | ||||
| return Task.FromResult<ISelfUser>(_currentUser); | return Task.FromResult<ISelfUser>(_currentUser); | ||||
| } | } | ||||
| internal CachedGlobalUser GetOrAddUser(API.User model, DataStore dataStore) | |||||
| internal SocketGlobalUser GetOrAddUser(API.User model, DataStore dataStore) | |||||
| { | { | ||||
| var user = dataStore.GetOrAddUser(model.Id, _ => new CachedGlobalUser(model)); | |||||
| var user = dataStore.GetOrAddUser(model.Id, _ => new SocketGlobalUser(model)); | |||||
| user.AddRef(); | user.AddRef(); | ||||
| return user; | return user; | ||||
| } | } | ||||
| internal CachedGlobalUser RemoveUser(ulong id) | |||||
| internal SocketGlobalUser RemoveUser(ulong id) | |||||
| { | { | ||||
| return DataStore.RemoveUser(id); | return DataStore.RemoveUser(id); | ||||
| } | } | ||||
| @@ -429,10 +423,10 @@ namespace Discord | |||||
| => DownloadUsersAsync(DataStore.Guilds.Where(x => !x.HasAllMembers)); | => DownloadUsersAsync(DataStore.Guilds.Where(x => !x.HasAllMembers)); | ||||
| /// <summary> Downloads the users list for the provided guilds, if they don't have a complete list. </summary> | /// <summary> Downloads the users list for the provided guilds, if they don't have a complete list. </summary> | ||||
| public Task DownloadUsersAsync(IEnumerable<IGuild> guilds) | public Task DownloadUsersAsync(IEnumerable<IGuild> guilds) | ||||
| => DownloadUsersAsync(guilds.Select(x => x as CachedGuild).Where(x => x != null)); | |||||
| => DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null)); | |||||
| public Task DownloadUsersAsync(params IGuild[] guilds) | public Task DownloadUsersAsync(params IGuild[] guilds) | ||||
| => DownloadUsersAsync(guilds.Select(x => x as CachedGuild).Where(x => x != null)); | |||||
| private async Task DownloadUsersAsync(IEnumerable<CachedGuild> guilds) | |||||
| => DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null)); | |||||
| private async Task DownloadUsersAsync(IEnumerable<SocketGuild> guilds) | |||||
| { | { | ||||
| var cachedGuilds = guilds.ToArray(); | var cachedGuilds = guilds.ToArray(); | ||||
| if (cachedGuilds.Length == 0) return; | if (cachedGuilds.Length == 0) return; | ||||
| @@ -559,7 +553,7 @@ namespace Discord | |||||
| var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | ||||
| var dataStore = new DataStore(data.Guilds.Length, data.PrivateChannels.Length); | var dataStore = new DataStore(data.Guilds.Length, data.PrivateChannels.Length); | ||||
| var currentUser = new CachedSelfUser(this, data.User); | |||||
| var currentUser = new SocketSelfUser(this, data.User); | |||||
| int unavailableGuilds = 0; | int unavailableGuilds = 0; | ||||
| for (int i = 0; i < data.Guilds.Length; i++) | for (int i = 0; i < data.Guilds.Length; i++) | ||||
| { | { | ||||
| @@ -625,7 +619,7 @@ namespace Discord | |||||
| } | } | ||||
| await _gatewayLogger.DebugAsync($"Received Dispatch ({type})").ConfigureAwait(false); | await _gatewayLogger.DebugAsync($"Received Dispatch ({type})").ConfigureAwait(false); | ||||
| CachedGuild guild; | |||||
| SocketGuild guild; | |||||
| if (data.Unavailable != false) | if (data.Unavailable != false) | ||||
| { | { | ||||
| guild = AddGuild(data, DataStore); | guild = AddGuild(data, DataStore); | ||||
| @@ -751,7 +745,7 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<API.Channel>(_serializer); | var data = (payload as JToken).ToObject<API.Channel>(_serializer); | ||||
| ICachedChannel channel = null; | |||||
| ISocketChannel channel = null; | |||||
| if (data.GuildId.IsSpecified) | if (data.GuildId.IsSpecified) | ||||
| { | { | ||||
| var guild = DataStore.GetGuild(data.GuildId.Value); | var guild = DataStore.GetGuild(data.GuildId.Value); | ||||
| @@ -789,7 +783,7 @@ namespace Discord | |||||
| var before = channel.Clone(); | var before = channel.Clone(); | ||||
| channel.Update(data, UpdateSource.WebSocket); | channel.Update(data, UpdateSource.WebSocket); | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored CHANNEL_UPDATE, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored CHANNEL_UPDATE, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| @@ -808,7 +802,7 @@ namespace Discord | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false); | ||||
| ICachedChannel channel = null; | |||||
| ISocketChannel channel = null; | |||||
| var data = (payload as JToken).ToObject<API.Channel>(_serializer); | var data = (payload as JToken).ToObject<API.Channel>(_serializer); | ||||
| if (data.GuildId.IsSpecified) | if (data.GuildId.IsSpecified) | ||||
| { | { | ||||
| @@ -978,7 +972,7 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<RecipientEvent>(_serializer); | var data = (payload as JToken).ToObject<RecipientEvent>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as CachedGroupChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as SocketGroupChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| var user = channel.AddUser(data.User, DataStore); | var user = channel.AddUser(data.User, DataStore); | ||||
| @@ -996,7 +990,7 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<RecipientEvent>(_serializer); | var data = (payload as JToken).ToObject<RecipientEvent>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as CachedGroupChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as SocketGroupChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| var user = channel.RemoveUser(data.User.Id); | var user = channel.RemoveUser(data.User.Id); | ||||
| @@ -1166,10 +1160,10 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<API.Message>(_serializer); | var data = (payload as JToken).ToObject<API.Message>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored MESSAGE_CREATE, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored MESSAGE_CREATE, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| @@ -1200,17 +1194,17 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<API.Message>(_serializer); | var data = (payload as JToken).ToObject<API.Message>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| } | } | ||||
| IMessage before = null, after = null; | IMessage before = null, after = null; | ||||
| CachedMessage cachedMsg = channel.GetMessage(data.Id); | |||||
| SocketMessage cachedMsg = channel.GetMessage(data.Id); | |||||
| if (cachedMsg != null) | if (cachedMsg != null) | ||||
| { | { | ||||
| before = cachedMsg.Clone(); | before = cachedMsg.Clone(); | ||||
| @@ -1239,10 +1233,10 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<API.Message>(_serializer); | var data = (payload as JToken).ToObject<API.Message>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| @@ -1266,10 +1260,10 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<MessageDeleteBulkEvent>(_serializer); | var data = (payload as JToken).ToObject<MessageDeleteBulkEvent>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE_BULK, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE_BULK, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| @@ -1341,10 +1335,10 @@ namespace Discord | |||||
| await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer); | var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer); | ||||
| var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel; | |||||
| var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel; | |||||
| if (channel != null) | if (channel != null) | ||||
| { | { | ||||
| if (!((channel as ICachedGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true)) | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Ignored TYPING_START, guild is not synced yet.").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored TYPING_START, guild is not synced yet.").ConfigureAwait(false); | ||||
| return; | return; | ||||
| @@ -1385,7 +1379,7 @@ namespace Discord | |||||
| var data = (payload as JToken).ToObject<API.VoiceState>(_serializer); | var data = (payload as JToken).ToObject<API.VoiceState>(_serializer); | ||||
| if (data.GuildId.HasValue) | if (data.GuildId.HasValue) | ||||
| { | { | ||||
| ICachedUser user; | |||||
| ISocketUser user; | |||||
| VoiceState before, after; | VoiceState before, after; | ||||
| if (data.GuildId != null) | if (data.GuildId != null) | ||||
| { | { | ||||
| @@ -1418,7 +1412,7 @@ namespace Discord | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| var groupChannel = DataStore.GetChannel(data.ChannelId.Value) as CachedGroupChannel; | |||||
| var groupChannel = DataStore.GetChannel(data.ChannelId.Value) as SocketGroupChannel; | |||||
| if (groupChannel != null) | if (groupChannel != null) | ||||
| { | { | ||||
| if (data.ChannelId != null) | if (data.ChannelId != null) | ||||
| @@ -3,20 +3,15 @@ using Discord.Net.WebSockets; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public class DiscordSocketConfig : DiscordConfig | |||||
| public class DiscordSocketConfig : DiscordRestConfig | |||||
| { | { | ||||
| public const string GatewayEncoding = "json"; | |||||
| /// <summary> Gets or sets the id for this shard. Must be less than TotalShards. </summary> | /// <summary> Gets or sets the id for this shard. Must be less than TotalShards. </summary> | ||||
| public int ShardId { get; set; } = 0; | public int ShardId { get; set; } = 0; | ||||
| /// <summary> Gets or sets the total number of shards for this application. </summary> | /// <summary> Gets or sets the total number of shards for this application. </summary> | ||||
| public int TotalShards { get; set; } = 1; | public int TotalShards { get; set; } = 1; | ||||
| /// <summary> Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize. </summary> | |||||
| public int ConnectionTimeout { get; set; } = 30000; | |||||
| /// <summary> Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. </summary> | |||||
| public int ReconnectDelay { get; set; } = 1000; | |||||
| /// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary> | |||||
| public int FailedReconnectDelay { get; set; } = 15000; | |||||
| /// <summary> Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. </summary> | /// <summary> Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. </summary> | ||||
| public int MessageCacheSize { get; set; } = 0; | public int MessageCacheSize { get; set; } = 0; | ||||
| /*/// <summary> | /*/// <summary> | ||||
| @@ -13,12 +13,12 @@ namespace Discord | |||||
| public string[] RPCOrigins { get; private set; } | public string[] RPCOrigins { get; private set; } | ||||
| public ulong Flags { get; private set; } | public ulong Flags { get; private set; } | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public IUser Owner { get; private set; } | public IUser Owner { get; private set; } | ||||
| public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); | ||||
| public Application(DiscordClient discord, Model model) | |||||
| public Application(DiscordRestClient discord, Model model) | |||||
| : base(model.Id) | : base(model.Id) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -13,13 +13,13 @@ namespace Discord | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| internal class DMChannel : SnowflakeEntity, IDMChannel | internal class DMChannel : SnowflakeEntity, IDMChannel | ||||
| { | { | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public IUser Recipient { get; private set; } | public IUser Recipient { get; private set; } | ||||
| public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>(); | public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>(); | ||||
| IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create(Recipient); | IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create(Recipient); | ||||
| public DMChannel(DiscordClient discord, IUser recipient, Model model) | |||||
| public DMChannel(DiscordRestClient discord, IUser recipient, Model model) | |||||
| : base(model.Id) | : base(model.Id) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -18,14 +18,14 @@ namespace Discord | |||||
| protected ConcurrentDictionary<ulong, GroupUser> _users; | protected ConcurrentDictionary<ulong, GroupUser> _users; | ||||
| private string _iconId; | private string _iconId; | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| public IReadOnlyCollection<IUser> Recipients => _users.ToReadOnlyCollection(); | public IReadOnlyCollection<IUser> Recipients => _users.ToReadOnlyCollection(); | ||||
| public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>(); | public virtual IReadOnlyCollection<IMessage> CachedMessages => ImmutableArray.Create<IMessage>(); | ||||
| public string IconUrl => API.CDN.GetChannelIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetChannelIconUrl(Id, _iconId); | ||||
| public GroupChannel(DiscordClient discord, Model model) | |||||
| public GroupChannel(DiscordRestClient discord, Model model) | |||||
| : base(model.Id) | : base(model.Id) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -19,7 +19,7 @@ namespace Discord | |||||
| public Guild Guild { get; private set; } | public Guild Guild { get; private set; } | ||||
| public override DiscordClient Discord => Guild.Discord; | |||||
| public override DiscordRestClient Discord => Guild.Discord; | |||||
| public GuildChannel(Guild guild, Model model) | public GuildChannel(Guild guild, Model model) | ||||
| : base(model.Id) | : base(model.Id) | ||||
| @@ -56,8 +56,8 @@ namespace Discord | |||||
| var args = new ModifyGuildChannelParams(); | var args = new ModifyGuildChannelParams(); | ||||
| func(args); | func(args); | ||||
| if (!args.Name.IsSpecified) | |||||
| args.Name = Name; | |||||
| if (!args._name.IsSpecified) | |||||
| args._name = Name; | |||||
| var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | ||||
| Update(model, UpdateSource.Rest); | Update(model, UpdateSource.Rest); | ||||
| @@ -20,12 +20,11 @@ namespace Discord | |||||
| /// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | /// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary> | ||||
| IMessage GetCachedMessage(ulong id); | IMessage GetCachedMessage(ulong id); | ||||
| /// <summary> Gets the last N messages from this message channel. </summary> | /// <summary> Gets the last N messages from this message channel. </summary> | ||||
| Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
| Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordRestConfig.MaxMessagesPerBatch); | |||||
| /// <summary> Gets a collection of messages in this channel. </summary> | /// <summary> Gets a collection of messages in this channel. </summary> | ||||
| Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch); | |||||
| Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordRestConfig.MaxMessagesPerBatch); | |||||
| /// <summary> Bulk deletes multiple messages. </summary> | /// <summary> Bulk deletes multiple messages. </summary> | ||||
| Task DeleteMessagesAsync(IEnumerable<IMessage> messages); | |||||
| Task DeleteMessagesAsync(IEnumerable<IMessage> messages); | |||||
| /// <summary> Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.</summary> | /// <summary> Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.</summary> | ||||
| Task TriggerTypingAsync(); | Task TriggerTypingAsync(); | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public interface IPrivateChannel | |||||
| public interface IPrivateChannel : IChannel | |||||
| { | { | ||||
| IReadOnlyCollection<IUser> Recipients { get; } | IReadOnlyCollection<IUser> Recipients { get; } | ||||
| } | } | ||||
| @@ -37,8 +37,8 @@ namespace Discord | |||||
| var args = new ModifyTextChannelParams(); | var args = new ModifyTextChannelParams(); | ||||
| func(args); | func(args); | ||||
| if (!args.Name.IsSpecified) | |||||
| args.Name = Name; | |||||
| if (!args._name.IsSpecified) | |||||
| args._name = Name; | |||||
| var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | ||||
| Update(model, UpdateSource.Rest); | Update(model, UpdateSource.Rest); | ||||
| @@ -34,8 +34,8 @@ namespace Discord | |||||
| var args = new ModifyVoiceChannelParams(); | var args = new ModifyVoiceChannelParams(); | ||||
| func(args); | func(args); | ||||
| if (!args.Name.IsSpecified) | |||||
| args.Name = Name; | |||||
| if (!args._name.IsSpecified) | |||||
| args._name = Name; | |||||
| var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.ModifyGuildChannelAsync(Id, args).ConfigureAwait(false); | ||||
| Update(model, UpdateSource.Rest); | Update(model, UpdateSource.Rest); | ||||
| @@ -4,9 +4,10 @@ | |||||
| { | { | ||||
| public T Id { get; } | public T Id { get; } | ||||
| public abstract DiscordClient Discord { get; } | |||||
| public abstract DiscordRestClient Discord { get; } | |||||
| public bool IsAttached => this is ICachedEntity<T>; | |||||
| internal virtual bool IsAttached => false; | |||||
| bool IEntity<T>.IsAttached => IsAttached; | |||||
| public Entity(T id) | public Entity(T id) | ||||
| { | { | ||||
| @@ -27,7 +27,7 @@ namespace Discord | |||||
| public MfaLevel MfaLevel { get; private set; } | public MfaLevel MfaLevel { get; private set; } | ||||
| public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } | public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public ulong? AFKChannelId { get; private set; } | public ulong? AFKChannelId { get; private set; } | ||||
| public ulong? EmbedChannelId { get; private set; } | public ulong? EmbedChannelId { get; private set; } | ||||
| public ulong OwnerId { get; private set; } | public ulong OwnerId { get; private set; } | ||||
| @@ -42,7 +42,7 @@ namespace Discord | |||||
| public Role EveryoneRole => GetRole(Id); | public Role EveryoneRole => GetRole(Id); | ||||
| public IReadOnlyCollection<IRole> Roles => _roles.ToReadOnlyCollection(); | public IReadOnlyCollection<IRole> Roles => _roles.ToReadOnlyCollection(); | ||||
| public Guild(DiscordClient discord, Model model) | |||||
| public Guild(DiscordRestClient discord, Model model) | |||||
| : base(model.Id) | : base(model.Id) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -122,10 +122,10 @@ namespace Discord | |||||
| var args = new ModifyGuildParams(); | var args = new ModifyGuildParams(); | ||||
| func(args); | func(args); | ||||
| if (args.Splash.IsSpecified && _splashId != null) | |||||
| args.SplashHash = _splashId; | |||||
| if (args.Icon.IsSpecified && _iconId != null) | |||||
| args.IconHash = _iconId; | |||||
| if (args._splash.IsSpecified && _splashId != null) | |||||
| args._splash = new API.Image(_splashId); | |||||
| if (args._icon.IsSpecified && _iconId != null) | |||||
| args._icon = new API.Image(_iconId); | |||||
| var model = await Discord.ApiClient.ModifyGuildAsync(Id, args).ConfigureAwait(false); | var model = await Discord.ApiClient.ModifyGuildAsync(Id, args).ConfigureAwait(false); | ||||
| Update(model, UpdateSource.Rest); | Update(model, UpdateSource.Rest); | ||||
| @@ -165,7 +165,7 @@ namespace Discord | |||||
| public Task AddBanAsync(IUser user, int pruneDays = 0) => AddBanAsync(user, pruneDays); | public Task AddBanAsync(IUser user, int pruneDays = 0) => AddBanAsync(user, pruneDays); | ||||
| public async Task AddBanAsync(ulong userId, int pruneDays = 0) | public async Task AddBanAsync(ulong userId, int pruneDays = 0) | ||||
| { | { | ||||
| var args = new CreateGuildBanParams() { PruneDays = pruneDays }; | |||||
| var args = new CreateGuildBanParams() { DeleteMessageDays = pruneDays }; | |||||
| await Discord.ApiClient.CreateGuildBanAsync(Id, userId, args).ConfigureAwait(false); | await Discord.ApiClient.CreateGuildBanAsync(Id, userId, args).ConfigureAwait(false); | ||||
| } | } | ||||
| public Task RemoveBanAsync(IUser user) => RemoveBanAsync(user.Id); | public Task RemoveBanAsync(IUser user) => RemoveBanAsync(user.Id); | ||||
| @@ -23,7 +23,7 @@ namespace Discord | |||||
| public User User { get; private set; } | public User User { get; private set; } | ||||
| public IntegrationAccount Account { get; private set; } | public IntegrationAccount Account { get; private set; } | ||||
| public override DiscordClient Discord => Guild.Discord; | |||||
| public override DiscordRestClient Discord => Guild.Discord; | |||||
| public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks); | ||||
| public GuildIntegration(Guild guild, Model model) | public GuildIntegration(Guild guild, Model model) | ||||
| @@ -13,11 +13,11 @@ namespace Discord | |||||
| public bool IsOwner { get; private set; } | public bool IsOwner { get; private set; } | ||||
| public GuildPermissions Permissions { get; private set; } | public GuildPermissions Permissions { get; private set; } | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); | ||||
| public UserGuild(DiscordClient discord, Model model) | |||||
| public UserGuild(DiscordRestClient discord, Model model) | |||||
| : base(model.Id) | : base(model.Id) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -13,13 +13,13 @@ namespace Discord | |||||
| public ulong ChannelId { get; private set; } | public ulong ChannelId { get; private set; } | ||||
| public ulong GuildId { get; private set; } | public ulong GuildId { get; private set; } | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public string Code => Id; | public string Code => Id; | ||||
| public string Url => $"{DiscordConfig.InviteUrl}/{XkcdCode ?? Code}"; | |||||
| public string XkcdUrl => XkcdCode != null ? $"{DiscordConfig.InviteUrl}/{XkcdCode}" : null; | |||||
| public string Url => $"{DiscordRestConfig.InviteUrl}/{XkcdCode ?? Code}"; | |||||
| public string XkcdUrl => XkcdCode != null ? $"{DiscordRestConfig.InviteUrl}/{XkcdCode}" : null; | |||||
| public Invite(DiscordClient discord, Model model) | |||||
| public Invite(DiscordRestClient discord, Model model) | |||||
| : base(model.Code) | : base(model.Code) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -16,7 +16,7 @@ namespace Discord | |||||
| public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks); | ||||
| public InviteMetadata(DiscordClient client, Model model) | |||||
| public InviteMetadata(DiscordRestClient client, Model model) | |||||
| : base(client, model) | : base(client, model) | ||||
| { | { | ||||
| Update(model, UpdateSource.Creation); | Update(model, UpdateSource.Creation); | ||||
| @@ -29,7 +29,7 @@ namespace Discord | |||||
| public IReadOnlyCollection<IRole> MentionedRoles { get; private set; } | public IReadOnlyCollection<IRole> MentionedRoles { get; private set; } | ||||
| public IReadOnlyCollection<IUser> MentionedUsers { get; private set; } | public IReadOnlyCollection<IUser> MentionedUsers { get; private set; } | ||||
| public override DiscordClient Discord => (Channel as Entity<ulong>).Discord; | |||||
| public override DiscordRestClient Discord => (Channel as Entity<ulong>).Discord; | |||||
| public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks); | ||||
| public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); | ||||
| @@ -23,7 +23,7 @@ namespace Discord | |||||
| public bool IsEveryone => Id == Guild.Id; | public bool IsEveryone => Id == Guild.Id; | ||||
| public string Mention => MentionUtils.Mention(this); | public string Mention => MentionUtils.Mention(this); | ||||
| public override DiscordClient Discord => Guild.Discord; | |||||
| public override DiscordRestClient Discord => Guild.Discord; | |||||
| public Role(Guild guild, Model model) | public Role(Guild guild, Model model) | ||||
| : base(model.Id) | : base(model.Id) | ||||
| @@ -51,6 +51,7 @@ namespace Discord | |||||
| var args = new ModifyGuildRoleParams(); | var args = new ModifyGuildRoleParams(); | ||||
| func(args); | func(args); | ||||
| var response = await Discord.ApiClient.ModifyGuildRoleAsync(Guild.Id, Id, args).ConfigureAwait(false); | var response = await Discord.ApiClient.ModifyGuildRoleAsync(Guild.Id, Id, args).ConfigureAwait(false); | ||||
| Update(response, UpdateSource.Rest); | Update(response, UpdateSource.Rest); | ||||
| } | } | ||||
| public async Task DeleteAsync() | public async Task DeleteAsync() | ||||
| @@ -6,6 +6,9 @@ namespace Discord | |||||
| { | { | ||||
| internal class GroupUser : IGroupUser | internal class GroupUser : IGroupUser | ||||
| { | { | ||||
| internal virtual bool IsAttached => false; | |||||
| bool IEntity<ulong>.IsAttached => IsAttached; | |||||
| public GroupChannel Channel { get; private set; } | public GroupChannel Channel { get; private set; } | ||||
| public User User { get; private set; } | public User User { get; private set; } | ||||
| @@ -14,7 +17,6 @@ namespace Discord | |||||
| public DateTimeOffset CreatedAt => User.CreatedAt; | public DateTimeOffset CreatedAt => User.CreatedAt; | ||||
| public string Discriminator => User.Discriminator; | public string Discriminator => User.Discriminator; | ||||
| public ushort DiscriminatorValue => User.DiscriminatorValue; | public ushort DiscriminatorValue => User.DiscriminatorValue; | ||||
| public bool IsAttached => User.IsAttached; | |||||
| public bool IsBot => User.IsBot; | public bool IsBot => User.IsBot; | ||||
| public string Username => User.Username; | public string Username => User.Username; | ||||
| public string Mention => MentionUtils.Mention(this, false); | public string Mention => MentionUtils.Mention(this, false); | ||||
| @@ -22,7 +24,7 @@ namespace Discord | |||||
| public virtual UserStatus Status => UserStatus.Unknown; | public virtual UserStatus Status => UserStatus.Unknown; | ||||
| public virtual Game Game => null; | public virtual Game Game => null; | ||||
| public DiscordClient Discord => Channel.Discord; | |||||
| public DiscordRestClient Discord => Channel.Discord; | |||||
| public GroupUser(GroupChannel channel, User user) | public GroupUser(GroupChannel channel, User user) | ||||
| { | { | ||||
| @@ -13,6 +13,9 @@ namespace Discord | |||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | [DebuggerDisplay("{DebuggerDisplay,nq}")] | ||||
| internal class GuildUser : IGuildUser, ISnowflakeEntity | internal class GuildUser : IGuildUser, ISnowflakeEntity | ||||
| { | { | ||||
| internal virtual bool IsAttached => false; | |||||
| bool IEntity<ulong>.IsAttached => IsAttached; | |||||
| private long? _joinedAtTicks; | private long? _joinedAtTicks; | ||||
| public string Nickname { get; private set; } | public string Nickname { get; private set; } | ||||
| @@ -27,7 +30,6 @@ namespace Discord | |||||
| public DateTimeOffset CreatedAt => User.CreatedAt; | public DateTimeOffset CreatedAt => User.CreatedAt; | ||||
| public string Discriminator => User.Discriminator; | public string Discriminator => User.Discriminator; | ||||
| public ushort DiscriminatorValue => User.DiscriminatorValue; | public ushort DiscriminatorValue => User.DiscriminatorValue; | ||||
| public bool IsAttached => User.IsAttached; | |||||
| public bool IsBot => User.IsBot; | public bool IsBot => User.IsBot; | ||||
| public string Mention => MentionUtils.Mention(this, Nickname != null); | public string Mention => MentionUtils.Mention(this, Nickname != null); | ||||
| public string Username => User.Username; | public string Username => User.Username; | ||||
| @@ -35,7 +37,7 @@ namespace Discord | |||||
| public virtual UserStatus Status => UserStatus.Unknown; | public virtual UserStatus Status => UserStatus.Unknown; | ||||
| public virtual Game Game => null; | public virtual Game Game => null; | ||||
| public DiscordClient Discord => Guild.Discord; | |||||
| public DiscordRestClient Discord => Guild.Discord; | |||||
| public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); | ||||
| public GuildUser(Guild guild, User user) | public GuildUser(Guild guild, User user) | ||||
| @@ -75,6 +77,15 @@ namespace Discord | |||||
| if (model.Nick.IsSpecified) | if (model.Nick.IsSpecified) | ||||
| Nickname = model.Nick.Value; | Nickname = model.Nick.Value; | ||||
| } | } | ||||
| private void Update(ModifyGuildMemberParams args, UpdateSource source) | |||||
| { | |||||
| if (source == UpdateSource.Rest && IsAttached) return; | |||||
| if (args._roleIds.IsSpecified) | |||||
| Roles = args._roleIds.Value.Select(x => Guild.GetRole(x)).Where(x => x != null).ToImmutableArray(); | |||||
| if (args._nickname.IsSpecified) | |||||
| Nickname = args._nickname.Value ?? ""; | |||||
| } | |||||
| private void UpdateRoles(ulong[] roleIds) | private void UpdateRoles(ulong[] roleIds) | ||||
| { | { | ||||
| var roles = ImmutableArray.CreateBuilder<Role>(roleIds.Length + 1); | var roles = ImmutableArray.CreateBuilder<Role>(roleIds.Length + 1); | ||||
| @@ -104,20 +115,17 @@ namespace Discord | |||||
| func(args); | func(args); | ||||
| bool isCurrentUser = (await Discord.GetCurrentUserAsync().ConfigureAwait(false)).Id == Id; | bool isCurrentUser = (await Discord.GetCurrentUserAsync().ConfigureAwait(false)).Id == Id; | ||||
| if (isCurrentUser && args.Nickname.IsSpecified) | |||||
| if (isCurrentUser && args._nickname.IsSpecified) | |||||
| { | { | ||||
| var nickArgs = new ModifyCurrentUserNickParams { Nickname = args.Nickname.Value ?? "" }; | |||||
| var nickArgs = new ModifyCurrentUserNickParams { Nickname = args._nickname.Value ?? "" }; | |||||
| await Discord.ApiClient.ModifyMyNickAsync(Guild.Id, nickArgs).ConfigureAwait(false); | await Discord.ApiClient.ModifyMyNickAsync(Guild.Id, nickArgs).ConfigureAwait(false); | ||||
| args.Nickname = new Optional<string>(); //Remove | |||||
| args._nickname = Optional.Create<string>(); //Remove | |||||
| } | } | ||||
| if (!isCurrentUser || args.Deaf.IsSpecified || args.Mute.IsSpecified || args.RoleIds.IsSpecified) | |||||
| if (!isCurrentUser || args._deaf.IsSpecified || args._mute.IsSpecified || args._roleIds.IsSpecified) | |||||
| { | { | ||||
| await Discord.ApiClient.ModifyGuildMemberAsync(Guild.Id, Id, args).ConfigureAwait(false); | await Discord.ApiClient.ModifyGuildMemberAsync(Guild.Id, Id, args).ConfigureAwait(false); | ||||
| if (args.Nickname.IsSpecified) | |||||
| Nickname = args.Nickname.Value ?? ""; | |||||
| if (args.RoleIds.IsSpecified) | |||||
| Roles = args.RoleIds.Value.Select(x => Guild.GetRole(x)).Where(x => x != null).ToImmutableArray(); | |||||
| Update(args, UpdateSource.Rest); | |||||
| } | } | ||||
| } | } | ||||
| public async Task KickAsync() | public async Task KickAsync() | ||||
| @@ -18,9 +18,9 @@ namespace Discord | |||||
| public override UserStatus Status => _status; | public override UserStatus Status => _status; | ||||
| public override Game Game => _game; | public override Game Game => _game; | ||||
| public override DiscordClient Discord { get; } | |||||
| public override DiscordRestClient Discord { get; } | |||||
| public SelfUser(DiscordClient discord, Model model) | |||||
| public SelfUser(DiscordRestClient discord, Model model) | |||||
| : base(model) | : base(model) | ||||
| { | { | ||||
| Discord = discord; | Discord = discord; | ||||
| @@ -53,10 +53,10 @@ namespace Discord | |||||
| var args = new ModifyCurrentUserParams(); | var args = new ModifyCurrentUserParams(); | ||||
| func(args); | func(args); | ||||
| if (!args.Username.IsSpecified) | |||||
| args.Username = Username; | |||||
| if (args.Avatar.IsSpecified && _avatarId != null) | |||||
| args.AvatarHash = _avatarId; | |||||
| if (!args._username.IsSpecified) | |||||
| args._username = Username; | |||||
| if (!args._avatar.IsSpecified && _avatarId != null) | |||||
| args._avatar = new API.Image(_avatarId); | |||||
| var model = await Discord.ApiClient.ModifySelfAsync(args).ConfigureAwait(false); | var model = await Discord.ApiClient.ModifySelfAsync(args).ConfigureAwait(false); | ||||
| Update(model, UpdateSource.Rest); | Update(model, UpdateSource.Rest); | ||||
| @@ -68,13 +68,13 @@ namespace Discord | |||||
| var args = new ModifyPresenceParams(); | var args = new ModifyPresenceParams(); | ||||
| func(args); | func(args); | ||||
| var game = args.Game.GetValueOrDefault(_game); | |||||
| var status = args.Status.GetValueOrDefault(_status); | |||||
| var game = args._game.GetValueOrDefault(_game); | |||||
| var status = args._status.GetValueOrDefault(_status); | |||||
| long idleSince = _idleSince; | long idleSince = _idleSince; | ||||
| if (status == UserStatus.Idle && _status != UserStatus.Idle) | if (status == UserStatus.Idle && _status != UserStatus.Idle) | ||||
| idleSince = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | idleSince = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | ||||
| var apiGame = new API.Game { Name = game.Name, StreamType = game.StreamType, StreamUrl = game.StreamUrl }; | |||||
| var apiGame = game != null ? new API.Game { Name = game.Name, StreamType = game.StreamType, StreamUrl = game.StreamUrl } : null; | |||||
| await Discord.ApiClient.SendStatusUpdateAsync(status == UserStatus.Idle ? _idleSince : (long?)null, apiGame).ConfigureAwait(false); | await Discord.ApiClient.SendStatusUpdateAsync(status == UserStatus.Idle ? _idleSince : (long?)null, apiGame).ConfigureAwait(false); | ||||
| @@ -13,7 +13,7 @@ namespace Discord | |||||
| public string Username { get; private set; } | public string Username { get; private set; } | ||||
| public ushort DiscriminatorValue { get; private set; } | public ushort DiscriminatorValue { get; private set; } | ||||
| public override DiscordClient Discord { get { throw new NotSupportedException(); } } | |||||
| public override DiscordRestClient Discord { get { throw new NotSupportedException(); } } | |||||
| public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId); | public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId); | ||||
| public string Discriminator => DiscriminatorValue.ToString("D4"); | public string Discriminator => DiscriminatorValue.ToString("D4"); | ||||
| @@ -1,17 +0,0 @@ | |||||
| using Model = Discord.API.Message; | |||||
| namespace Discord | |||||
| { | |||||
| internal class CachedMessage : Message, ICachedEntity<ulong> | |||||
| { | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||||
| public new ICachedMessageChannel Channel => base.Channel as ICachedMessageChannel; | |||||
| public CachedMessage(ICachedMessageChannel channel, IUser author, Model model) | |||||
| : base(channel, author, model) | |||||
| { | |||||
| } | |||||
| public CachedMessage Clone() => MemberwiseClone() as CachedMessage; | |||||
| } | |||||
| } | |||||
| @@ -1,19 +0,0 @@ | |||||
| using System; | |||||
| using Model = Discord.API.User; | |||||
| namespace Discord | |||||
| { | |||||
| internal class CachedSelfUser : SelfUser, ICachedUser | |||||
| { | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||||
| CachedGlobalUser ICachedUser.User { get { throw new NotSupportedException(); } } | |||||
| public CachedSelfUser(DiscordSocketClient discord, Model model) | |||||
| : base(discord, model) | |||||
| { | |||||
| } | |||||
| public CachedSelfUser Clone() => MemberwiseClone() as CachedSelfUser; | |||||
| ICachedUser ICachedUser.Clone() => Clone(); | |||||
| } | |||||
| } | |||||
| @@ -2,10 +2,10 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal interface ICachedChannel : IChannel, ICachedEntity<ulong> | |||||
| internal interface ISocketChannel : IChannel | |||||
| { | { | ||||
| void Update(Model model, UpdateSource source); | void Update(Model model, UpdateSource source); | ||||
| ICachedChannel Clone(); | |||||
| ISocketChannel Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,7 @@ | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ISocketGuildChannel : ISocketChannel, IGuildChannel | |||||
| { | |||||
| new SocketGuild Guild { get; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| using System.Collections.Generic; | |||||
| using MessageModel = Discord.API.Message; | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ISocketMessageChannel : ISocketChannel, IMessageChannel | |||||
| { | |||||
| IReadOnlyCollection<ISocketUser> Users { get; } | |||||
| SocketMessage AddMessage(ISocketUser author, MessageModel model); | |||||
| SocketMessage GetMessage(ulong id); | |||||
| SocketMessage RemoveMessage(ulong id); | |||||
| ISocketUser GetUser(ulong id, bool skipCheck = false); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ISocketPrivateChannel : ISocketChannel, IPrivateChannel | |||||
| { | |||||
| new IReadOnlyCollection<ISocketUser> Recipients { get; } | |||||
| } | |||||
| } | |||||
| @@ -10,51 +10,51 @@ namespace Discord | |||||
| { | { | ||||
| internal class MessageCache : MessageManager | internal class MessageCache : MessageManager | ||||
| { | { | ||||
| private readonly ConcurrentDictionary<ulong, CachedMessage> _messages; | |||||
| private readonly ConcurrentDictionary<ulong, SocketMessage> _messages; | |||||
| private readonly ConcurrentQueue<ulong> _orderedMessages; | private readonly ConcurrentQueue<ulong> _orderedMessages; | ||||
| private readonly int _size; | private readonly int _size; | ||||
| public override IReadOnlyCollection<CachedMessage> Messages => _messages.ToReadOnlyCollection(); | |||||
| public override IReadOnlyCollection<SocketMessage> Messages => _messages.ToReadOnlyCollection(); | |||||
| public MessageCache(DiscordSocketClient discord, ICachedMessageChannel channel) | |||||
| public MessageCache(DiscordSocketClient discord, ISocketMessageChannel channel) | |||||
| : base(discord, channel) | : base(discord, channel) | ||||
| { | { | ||||
| _size = discord.MessageCacheSize; | _size = discord.MessageCacheSize; | ||||
| _messages = new ConcurrentDictionary<ulong, CachedMessage>(1, (int)(_size * 1.05)); | |||||
| _messages = new ConcurrentDictionary<ulong, SocketMessage>(1, (int)(_size * 1.05)); | |||||
| _orderedMessages = new ConcurrentQueue<ulong>(); | _orderedMessages = new ConcurrentQueue<ulong>(); | ||||
| } | } | ||||
| public override void Add(CachedMessage message) | |||||
| public override void Add(SocketMessage message) | |||||
| { | { | ||||
| if (_messages.TryAdd(message.Id, message)) | if (_messages.TryAdd(message.Id, message)) | ||||
| { | { | ||||
| _orderedMessages.Enqueue(message.Id); | _orderedMessages.Enqueue(message.Id); | ||||
| ulong msgId; | ulong msgId; | ||||
| CachedMessage msg; | |||||
| SocketMessage msg; | |||||
| while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) | while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out msgId)) | ||||
| _messages.TryRemove(msgId, out msg); | _messages.TryRemove(msgId, out msg); | ||||
| } | } | ||||
| } | } | ||||
| public override CachedMessage Remove(ulong id) | |||||
| public override SocketMessage Remove(ulong id) | |||||
| { | { | ||||
| CachedMessage msg; | |||||
| SocketMessage msg; | |||||
| _messages.TryRemove(id, out msg); | _messages.TryRemove(id, out msg); | ||||
| return msg; | return msg; | ||||
| } | } | ||||
| public override CachedMessage Get(ulong id) | |||||
| public override SocketMessage Get(ulong id) | |||||
| { | { | ||||
| CachedMessage result; | |||||
| SocketMessage result; | |||||
| if (_messages.TryGetValue(id, out result)) | if (_messages.TryGetValue(id, out result)) | ||||
| return result; | return result; | ||||
| return null; | return null; | ||||
| } | } | ||||
| public override IImmutableList<CachedMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
| public override IImmutableList<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordRestConfig.MaxMessagesPerBatch) | |||||
| { | { | ||||
| if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | ||||
| if (limit == 0) return ImmutableArray<CachedMessage>.Empty; | |||||
| if (limit == 0) return ImmutableArray<SocketMessage>.Empty; | |||||
| IEnumerable<ulong> cachedMessageIds; | IEnumerable<ulong> cachedMessageIds; | ||||
| if (fromMessageId == null) | if (fromMessageId == null) | ||||
| @@ -68,7 +68,7 @@ namespace Discord | |||||
| .Take(limit) | .Take(limit) | ||||
| .Select(x => | .Select(x => | ||||
| { | { | ||||
| CachedMessage msg; | |||||
| SocketMessage msg; | |||||
| if (_messages.TryGetValue(x, out msg)) | if (_messages.TryGetValue(x, out msg)) | ||||
| return msg; | return msg; | ||||
| return null; | return null; | ||||
| @@ -77,7 +77,7 @@ namespace Discord | |||||
| .ToImmutableArray(); | .ToImmutableArray(); | ||||
| } | } | ||||
| public override async Task<CachedMessage> DownloadAsync(ulong id) | |||||
| public override async Task<SocketMessage> DownloadAsync(ulong id) | |||||
| { | { | ||||
| var msg = Get(id); | var msg = Get(id); | ||||
| if (msg != null) | if (msg != null) | ||||
| @@ -10,36 +10,36 @@ namespace Discord | |||||
| internal class MessageManager | internal class MessageManager | ||||
| { | { | ||||
| private readonly DiscordSocketClient _discord; | private readonly DiscordSocketClient _discord; | ||||
| private readonly ICachedMessageChannel _channel; | |||||
| private readonly ISocketMessageChannel _channel; | |||||
| public virtual IReadOnlyCollection<CachedMessage> Messages | |||||
| => ImmutableArray.Create<CachedMessage>(); | |||||
| public virtual IReadOnlyCollection<SocketMessage> Messages | |||||
| => ImmutableArray.Create<SocketMessage>(); | |||||
| public MessageManager(DiscordSocketClient discord, ICachedMessageChannel channel) | |||||
| public MessageManager(DiscordSocketClient discord, ISocketMessageChannel channel) | |||||
| { | { | ||||
| _discord = discord; | _discord = discord; | ||||
| _channel = channel; | _channel = channel; | ||||
| } | } | ||||
| public virtual void Add(CachedMessage message) { } | |||||
| public virtual CachedMessage Remove(ulong id) => null; | |||||
| public virtual CachedMessage Get(ulong id) => null; | |||||
| public virtual void Add(SocketMessage message) { } | |||||
| public virtual SocketMessage Remove(ulong id) => null; | |||||
| public virtual SocketMessage Get(ulong id) => null; | |||||
| public virtual IImmutableList<CachedMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
| => ImmutableArray.Create<CachedMessage>(); | |||||
| public virtual IImmutableList<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordRestConfig.MaxMessagesPerBatch) | |||||
| => ImmutableArray.Create<SocketMessage>(); | |||||
| public virtual async Task<CachedMessage> DownloadAsync(ulong id) | |||||
| public virtual async Task<SocketMessage> DownloadAsync(ulong id) | |||||
| { | { | ||||
| var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); | var model = await _discord.ApiClient.GetChannelMessageAsync(_channel.Id, id).ConfigureAwait(false); | ||||
| if (model != null) | if (model != null) | ||||
| return new CachedMessage(_channel, new User(model.Author.Value), model); | |||||
| return new SocketMessage(_channel, new User(model.Author.Value), model); | |||||
| return null; | return null; | ||||
| } | } | ||||
| public async Task<IReadOnlyCollection<CachedMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit) | |||||
| public async Task<IReadOnlyCollection<SocketMessage>> DownloadAsync(ulong? fromId, Direction dir, int limit) | |||||
| { | { | ||||
| //TODO: Test heavily, especially the ordering of messages | //TODO: Test heavily, especially the ordering of messages | ||||
| if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit)); | ||||
| if (limit == 0) return ImmutableArray<CachedMessage>.Empty; | |||||
| if (limit == 0) return ImmutableArray<SocketMessage>.Empty; | |||||
| var cachedMessages = GetMany(fromId, dir, limit); | var cachedMessages = GetMany(fromId, dir, limit); | ||||
| if (cachedMessages.Count == limit) | if (cachedMessages.Count == limit) | ||||
| @@ -48,20 +48,21 @@ namespace Discord | |||||
| return cachedMessages.Skip(cachedMessages.Count - limit).ToImmutableArray(); | return cachedMessages.Skip(cachedMessages.Count - limit).ToImmutableArray(); | ||||
| else | else | ||||
| { | { | ||||
| Optional<ulong> relativeId; | |||||
| if (cachedMessages.Count == 0) | |||||
| relativeId = fromId ?? new Optional<ulong>(); | |||||
| else | |||||
| relativeId = dir == Direction.Before ? cachedMessages[0].Id : cachedMessages[cachedMessages.Count - 1].Id; | |||||
| var args = new GetChannelMessagesParams | var args = new GetChannelMessagesParams | ||||
| { | { | ||||
| Limit = limit - cachedMessages.Count, | Limit = limit - cachedMessages.Count, | ||||
| RelativeDirection = dir, | |||||
| RelativeMessageId = relativeId | |||||
| RelativeDirection = dir | |||||
| }; | }; | ||||
| if (cachedMessages.Count == 0) | |||||
| { | |||||
| if (fromId != null) | |||||
| args.RelativeMessageId = fromId.Value; | |||||
| } | |||||
| else | |||||
| args.RelativeMessageId = dir == Direction.Before ? cachedMessages[0].Id : cachedMessages[cachedMessages.Count - 1].Id; | |||||
| var downloadedMessages = await _discord.ApiClient.GetChannelMessagesAsync(_channel.Id, args).ConfigureAwait(false); | var downloadedMessages = await _discord.ApiClient.GetChannelMessagesAsync(_channel.Id, args).ConfigureAwait(false); | ||||
| var guild = (_channel as ICachedGuildChannel)?.Guild; | |||||
| var guild = (_channel as ISocketGuildChannel)?.Guild; | |||||
| return cachedMessages.Concat(downloadedMessages.Select(x => | return cachedMessages.Concat(downloadedMessages.Select(x => | ||||
| { | { | ||||
| IUser user = _channel.GetUser(x.Author.Value.Id, true); | IUser user = _channel.GetUser(x.Author.Value.Id, true); | ||||
| @@ -73,7 +74,7 @@ namespace Discord | |||||
| else | else | ||||
| user = newUser; | user = newUser; | ||||
| } | } | ||||
| return new CachedMessage(_channel, user, x); | |||||
| return new SocketMessage(_channel, user, x); | |||||
| })).ToImmutableArray(); | })).ToImmutableArray(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -6,16 +6,18 @@ using Model = Discord.API.Channel; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedDMChannel : DMChannel, IDMChannel, ICachedChannel, ICachedMessageChannel, ICachedPrivateChannel | |||||
| internal class SocketDMChannel : DMChannel, IDMChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| private readonly MessageManager _messages; | private readonly MessageManager _messages; | ||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public new CachedDMUser Recipient => base.Recipient as CachedDMUser; | |||||
| public IReadOnlyCollection<ICachedUser> Members => ImmutableArray.Create<ICachedUser>(Discord.CurrentUser, Recipient); | |||||
| IReadOnlyCollection<ICachedUser> ICachedPrivateChannel.Recipients => ImmutableArray.Create(Recipient); | |||||
| public new SocketDMUser Recipient => base.Recipient as SocketDMUser; | |||||
| public IReadOnlyCollection<ISocketUser> Users => ImmutableArray.Create<ISocketUser>(Discord.CurrentUser, Recipient); | |||||
| IReadOnlyCollection<ISocketUser> ISocketPrivateChannel.Recipients => ImmutableArray.Create(Recipient); | |||||
| public CachedDMChannel(DiscordSocketClient discord, CachedDMUser recipient, Model model) | |||||
| public SocketDMChannel(DiscordSocketClient discord, SocketDMUser recipient, Model model) | |||||
| : base(discord, recipient, model) | : base(discord, recipient, model) | ||||
| { | { | ||||
| if (Discord.MessageCacheSize > 0) | if (Discord.MessageCacheSize > 0) | ||||
| @@ -25,8 +27,8 @@ namespace Discord | |||||
| } | } | ||||
| public override Task<IUser> GetUserAsync(ulong id) => Task.FromResult<IUser>(GetUser(id)); | public override Task<IUser> GetUserAsync(ulong id) => Task.FromResult<IUser>(GetUser(id)); | ||||
| public override Task<IReadOnlyCollection<IUser>> GetUsersAsync() => Task.FromResult<IReadOnlyCollection<IUser>>(Members); | |||||
| public ICachedUser GetUser(ulong id) | |||||
| public override Task<IReadOnlyCollection<IUser>> GetUsersAsync() => Task.FromResult<IReadOnlyCollection<IUser>>(Users); | |||||
| public ISocketUser GetUser(ulong id) | |||||
| { | { | ||||
| var currentUser = Discord.CurrentUser; | var currentUser = Discord.CurrentUser; | ||||
| if (id == Recipient.Id) | if (id == Recipient.Id) | ||||
| @@ -49,25 +51,25 @@ namespace Discord | |||||
| { | { | ||||
| return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
| } | } | ||||
| public CachedMessage AddMessage(ICachedUser author, MessageModel model) | |||||
| public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
| { | { | ||||
| var msg = new CachedMessage(this, author, model); | |||||
| var msg = new SocketMessage(this, author, model); | |||||
| _messages.Add(msg); | _messages.Add(msg); | ||||
| return msg; | return msg; | ||||
| } | } | ||||
| public CachedMessage GetMessage(ulong id) | |||||
| public SocketMessage GetMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Get(id); | return _messages.Get(id); | ||||
| } | } | ||||
| public CachedMessage RemoveMessage(ulong id) | |||||
| public SocketMessage RemoveMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Remove(id); | return _messages.Remove(id); | ||||
| } | } | ||||
| public CachedDMChannel Clone() => MemberwiseClone() as CachedDMChannel; | |||||
| public SocketDMChannel Clone() => MemberwiseClone() as SocketDMChannel; | |||||
| IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | ||||
| ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id); | |||||
| ICachedChannel ICachedChannel.Clone() => Clone(); | |||||
| ISocketUser ISocketMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id); | |||||
| ISocketChannel ISocketChannel.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -11,17 +11,19 @@ using VoiceStateModel = Discord.API.VoiceState; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedGroupChannel : GroupChannel, IGroupChannel, ICachedChannel, ICachedMessageChannel, ICachedPrivateChannel | |||||
| internal class SocketGroupChannel : GroupChannel, IGroupChannel, ISocketChannel, ISocketMessageChannel, ISocketPrivateChannel | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| private readonly MessageManager _messages; | private readonly MessageManager _messages; | ||||
| private ConcurrentDictionary<ulong, VoiceState> _voiceStates; | private ConcurrentDictionary<ulong, VoiceState> _voiceStates; | ||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public IReadOnlyCollection<ICachedUser> Members | |||||
| => _users.Select(x => x.Value as ICachedUser).Concat(ImmutableArray.Create(Discord.CurrentUser)).ToReadOnlyCollection(() => _users.Count + 1); | |||||
| public new IReadOnlyCollection<ICachedUser> Recipients => _users.Select(x => x.Value as ICachedUser).ToReadOnlyCollection(_users); | |||||
| public IReadOnlyCollection<ISocketUser> Users | |||||
| => _users.Select(x => x.Value as ISocketUser).Concat(ImmutableArray.Create(Discord.CurrentUser)).ToReadOnlyCollection(() => _users.Count + 1); | |||||
| public new IReadOnlyCollection<ISocketUser> Recipients => _users.Select(x => x.Value as ISocketUser).ToReadOnlyCollection(_users); | |||||
| public CachedGroupChannel(DiscordSocketClient discord, Model model) | |||||
| public SocketGroupChannel(DiscordSocketClient discord, Model model) | |||||
| : base(discord, model) | : base(discord, model) | ||||
| { | { | ||||
| if (Discord.MessageCacheSize > 0) | if (Discord.MessageCacheSize > 0) | ||||
| @@ -43,46 +45,46 @@ namespace Discord | |||||
| for (int i = 0; i < models.Length; i++) | for (int i = 0; i < models.Length; i++) | ||||
| { | { | ||||
| var globalUser = Discord.GetOrAddUser(models[i], dataStore); | var globalUser = Discord.GetOrAddUser(models[i], dataStore); | ||||
| users[models[i].Id] = new CachedGroupUser(this, globalUser); | |||||
| users[models[i].Id] = new SocketGroupUser(this, globalUser); | |||||
| } | } | ||||
| _users = users; | _users = users; | ||||
| } | } | ||||
| internal override void UpdateUsers(UserModel[] models, UpdateSource source) | internal override void UpdateUsers(UserModel[] models, UpdateSource source) | ||||
| => UpdateUsers(models, source, Discord.DataStore); | => UpdateUsers(models, source, Discord.DataStore); | ||||
| public CachedGroupUser AddUser(UserModel model, DataStore dataStore) | |||||
| public SocketGroupUser AddUser(UserModel model, DataStore dataStore) | |||||
| { | { | ||||
| GroupUser user; | GroupUser user; | ||||
| if (_users.TryGetValue(model.Id, out user)) | if (_users.TryGetValue(model.Id, out user)) | ||||
| return user as CachedGroupUser; | |||||
| return user as SocketGroupUser; | |||||
| else | else | ||||
| { | { | ||||
| var globalUser = Discord.GetOrAddUser(model, dataStore); | var globalUser = Discord.GetOrAddUser(model, dataStore); | ||||
| var privateUser = new CachedGroupUser(this, globalUser); | |||||
| var privateUser = new SocketGroupUser(this, globalUser); | |||||
| _users[privateUser.Id] = privateUser; | _users[privateUser.Id] = privateUser; | ||||
| return privateUser; | return privateUser; | ||||
| } | } | ||||
| } | } | ||||
| public ICachedUser GetUser(ulong id) | |||||
| public ISocketUser GetUser(ulong id) | |||||
| { | { | ||||
| GroupUser user; | GroupUser user; | ||||
| if (_users.TryGetValue(id, out user)) | if (_users.TryGetValue(id, out user)) | ||||
| return user as CachedGroupUser; | |||||
| return user as SocketGroupUser; | |||||
| if (id == Discord.CurrentUser.Id) | if (id == Discord.CurrentUser.Id) | ||||
| return Discord.CurrentUser; | return Discord.CurrentUser; | ||||
| return null; | return null; | ||||
| } | } | ||||
| public CachedGroupUser RemoveUser(ulong id) | |||||
| public SocketGroupUser RemoveUser(ulong id) | |||||
| { | { | ||||
| GroupUser user; | GroupUser user; | ||||
| if (_users.TryRemove(id, out user)) | if (_users.TryRemove(id, out user)) | ||||
| return user as CachedGroupUser; | |||||
| return user as SocketGroupUser; | |||||
| return null; | return null; | ||||
| } | } | ||||
| public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary<ulong, VoiceState> voiceStates = null) | public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary<ulong, VoiceState> voiceStates = null) | ||||
| { | { | ||||
| var voiceChannel = dataStore.GetChannel(model.ChannelId.Value) as CachedVoiceChannel; | |||||
| var voiceChannel = dataStore.GetChannel(model.ChannelId.Value) as SocketVoiceChannel; | |||||
| var voiceState = new VoiceState(voiceChannel, model); | var voiceState = new VoiceState(voiceChannel, model); | ||||
| (voiceStates ?? _voiceStates)[model.UserId] = voiceState; | (voiceStates ?? _voiceStates)[model.UserId] = voiceState; | ||||
| return voiceState; | return voiceState; | ||||
| @@ -114,25 +116,25 @@ namespace Discord | |||||
| { | { | ||||
| return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
| } | } | ||||
| public CachedMessage AddMessage(ICachedUser author, MessageModel model) | |||||
| public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
| { | { | ||||
| var msg = new CachedMessage(this, author, model); | |||||
| var msg = new SocketMessage(this, author, model); | |||||
| _messages.Add(msg); | _messages.Add(msg); | ||||
| return msg; | return msg; | ||||
| } | } | ||||
| public CachedMessage GetMessage(ulong id) | |||||
| public SocketMessage GetMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Get(id); | return _messages.Get(id); | ||||
| } | } | ||||
| public CachedMessage RemoveMessage(ulong id) | |||||
| public SocketMessage RemoveMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Remove(id); | return _messages.Remove(id); | ||||
| } | } | ||||
| public CachedDMChannel Clone() => MemberwiseClone() as CachedDMChannel; | |||||
| public SocketDMChannel Clone() => MemberwiseClone() as SocketDMChannel; | |||||
| IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | ||||
| ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id); | |||||
| ICachedChannel ICachedChannel.Clone() => Clone(); | |||||
| ISocketUser ISocketMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id); | |||||
| ISocketChannel ISocketChannel.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -7,17 +7,19 @@ using Model = Discord.API.Channel; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedTextChannel : TextChannel, ICachedGuildChannel, ICachedMessageChannel | |||||
| internal class SocketTextChannel : TextChannel, ISocketGuildChannel, ISocketMessageChannel | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| private readonly MessageManager _messages; | private readonly MessageManager _messages; | ||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public new CachedGuild Guild => base.Guild as CachedGuild; | |||||
| public new SocketGuild Guild => base.Guild as SocketGuild; | |||||
| public IReadOnlyCollection<CachedGuildUser> Members | |||||
| public IReadOnlyCollection<SocketGuildUser> Members | |||||
| => Guild.Members.Where(x => Permissions.GetValue(Permissions.ResolveChannel(x, this, x.GuildPermissions.RawValue), ChannelPermission.ReadMessages)).ToImmutableArray(); | => Guild.Members.Where(x => Permissions.GetValue(Permissions.ResolveChannel(x, this, x.GuildPermissions.RawValue), ChannelPermission.ReadMessages)).ToImmutableArray(); | ||||
| public CachedTextChannel(CachedGuild guild, Model model) | |||||
| public SocketTextChannel(SocketGuild guild, Model model) | |||||
| : base(guild, model) | : base(guild, model) | ||||
| { | { | ||||
| if (Discord.MessageCacheSize > 0) | if (Discord.MessageCacheSize > 0) | ||||
| @@ -28,7 +30,7 @@ namespace Discord | |||||
| public override Task<IGuildUser> GetUserAsync(ulong id) => Task.FromResult<IGuildUser>(GetUser(id)); | public override Task<IGuildUser> GetUserAsync(ulong id) => Task.FromResult<IGuildUser>(GetUser(id)); | ||||
| public override Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync() => Task.FromResult<IReadOnlyCollection<IGuildUser>>(Members); | public override Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync() => Task.FromResult<IReadOnlyCollection<IGuildUser>>(Members); | ||||
| public CachedGuildUser GetUser(ulong id, bool skipCheck = false) | |||||
| public SocketGuildUser GetUser(ulong id, bool skipCheck = false) | |||||
| { | { | ||||
| var user = Guild.GetUser(id); | var user = Guild.GetUser(id); | ||||
| if (skipCheck) return user; | if (skipCheck) return user; | ||||
| @@ -46,36 +48,36 @@ namespace Discord | |||||
| { | { | ||||
| return await _messages.DownloadAsync(id).ConfigureAwait(false); | return await _messages.DownloadAsync(id).ConfigureAwait(false); | ||||
| } | } | ||||
| public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
| public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordRestConfig.MaxMessagesPerBatch) | |||||
| { | { | ||||
| return await _messages.DownloadAsync(null, Direction.Before, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(null, Direction.Before, limit).ConfigureAwait(false); | ||||
| } | } | ||||
| public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch) | |||||
| public override async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordRestConfig.MaxMessagesPerBatch) | |||||
| { | { | ||||
| return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | return await _messages.DownloadAsync(fromMessageId, dir, limit).ConfigureAwait(false); | ||||
| } | } | ||||
| public CachedMessage AddMessage(ICachedUser author, MessageModel model) | |||||
| public SocketMessage AddMessage(ISocketUser author, MessageModel model) | |||||
| { | { | ||||
| var msg = new CachedMessage(this, author, model); | |||||
| var msg = new SocketMessage(this, author, model); | |||||
| _messages.Add(msg); | _messages.Add(msg); | ||||
| return msg; | return msg; | ||||
| } | } | ||||
| public CachedMessage GetMessage(ulong id) | |||||
| public SocketMessage GetMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Get(id); | return _messages.Get(id); | ||||
| } | } | ||||
| public CachedMessage RemoveMessage(ulong id) | |||||
| public SocketMessage RemoveMessage(ulong id) | |||||
| { | { | ||||
| return _messages.Remove(id); | return _messages.Remove(id); | ||||
| } | } | ||||
| public CachedTextChannel Clone() => MemberwiseClone() as CachedTextChannel; | |||||
| public SocketTextChannel Clone() => MemberwiseClone() as SocketTextChannel; | |||||
| IReadOnlyCollection<ICachedUser> ICachedMessageChannel.Members => Members; | |||||
| IReadOnlyCollection<ISocketUser> ISocketMessageChannel.Users => Members; | |||||
| IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | IMessage IMessageChannel.GetCachedMessage(ulong id) => GetMessage(id); | ||||
| ICachedUser ICachedMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id, skipCheck); | |||||
| ICachedChannel ICachedChannel.Clone() => Clone(); | |||||
| ISocketUser ISocketMessageChannel.GetUser(ulong id, bool skipCheck) => GetUser(id, skipCheck); | |||||
| ISocketChannel ISocketChannel.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,15 +8,17 @@ using Model = Discord.API.Channel; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedVoiceChannel : VoiceChannel, ICachedGuildChannel | |||||
| internal class SocketVoiceChannel : VoiceChannel, ISocketGuildChannel | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public new CachedGuild Guild => base.Guild as CachedGuild; | |||||
| public new SocketGuild Guild => base.Guild as SocketGuild; | |||||
| public IReadOnlyCollection<IGuildUser> Members | public IReadOnlyCollection<IGuildUser> Members | ||||
| => Guild.VoiceStates.Where(x => x.Value.VoiceChannel.Id == Id).Select(x => Guild.GetUser(x.Key)).ToImmutableArray(); | => Guild.VoiceStates.Where(x => x.Value.VoiceChannel.Id == Id).Select(x => Guild.GetUser(x.Key)).ToImmutableArray(); | ||||
| public CachedVoiceChannel(CachedGuild guild, Model model) | |||||
| public SocketVoiceChannel(SocketGuild guild, Model model) | |||||
| : base(guild, model) | : base(guild, model) | ||||
| { | { | ||||
| } | } | ||||
| @@ -46,8 +48,8 @@ namespace Discord | |||||
| //TODO: Block and return | //TODO: Block and return | ||||
| } | } | ||||
| public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel; | |||||
| public SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel; | |||||
| ICachedChannel ICachedChannel.Clone() => Clone(); | |||||
| ISocketChannel ISocketChannel.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -19,12 +19,14 @@ using VoiceStateModel = Discord.API.VoiceState; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedGuild : Guild, ICachedEntity<ulong>, IGuild, IUserGuild | |||||
| internal class SocketGuild : Guild, IGuild, IUserGuild | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| private readonly SemaphoreSlim _audioLock; | private readonly SemaphoreSlim _audioLock; | ||||
| private TaskCompletionSource<bool> _syncPromise, _downloaderPromise; | private TaskCompletionSource<bool> _syncPromise, _downloaderPromise; | ||||
| private ConcurrentHashSet<ulong> _channels; | private ConcurrentHashSet<ulong> _channels; | ||||
| private ConcurrentDictionary<ulong, CachedGuildUser> _members; | |||||
| private ConcurrentDictionary<ulong, SocketGuildUser> _members; | |||||
| private ConcurrentDictionary<ulong, VoiceState> _voiceStates; | private ConcurrentDictionary<ulong, VoiceState> _voiceStates; | ||||
| internal bool _available; | internal bool _available; | ||||
| @@ -39,20 +41,20 @@ namespace Discord | |||||
| public Task DownloaderPromise => _downloaderPromise.Task; | public Task DownloaderPromise => _downloaderPromise.Task; | ||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public CachedGuildUser CurrentUser => GetUser(Discord.CurrentUser.Id); | |||||
| public IReadOnlyCollection<ICachedGuildChannel> Channels | |||||
| public SocketGuildUser CurrentUser => GetUser(Discord.CurrentUser.Id); | |||||
| public IReadOnlyCollection<ISocketGuildChannel> Channels | |||||
| { | { | ||||
| get | get | ||||
| { | { | ||||
| var channels = _channels; | var channels = _channels; | ||||
| var store = Discord.DataStore; | var store = Discord.DataStore; | ||||
| return channels.Select(x => store.GetChannel(x) as ICachedGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels); | |||||
| return channels.Select(x => store.GetChannel(x) as ISocketGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels); | |||||
| } | } | ||||
| } | } | ||||
| public IReadOnlyCollection<CachedGuildUser> Members => _members.ToReadOnlyCollection(); | |||||
| public IReadOnlyCollection<SocketGuildUser> Members => _members.ToReadOnlyCollection(); | |||||
| public IEnumerable<KeyValuePair<ulong, VoiceState>> VoiceStates => _voiceStates; | public IEnumerable<KeyValuePair<ulong, VoiceState>> VoiceStates => _voiceStates; | ||||
| public CachedGuild(DiscordSocketClient discord, ExtendedModel model, DataStore dataStore) : base(discord, model) | |||||
| public SocketGuild(DiscordSocketClient discord, ExtendedModel model, DataStore dataStore) : base(discord, model) | |||||
| { | { | ||||
| _audioLock = new SemaphoreSlim(1, 1); | _audioLock = new SemaphoreSlim(1, 1); | ||||
| _syncPromise = new TaskCompletionSource<bool>(); | _syncPromise = new TaskCompletionSource<bool>(); | ||||
| @@ -70,7 +72,7 @@ namespace Discord | |||||
| if (_channels == null) | if (_channels == null) | ||||
| _channels = new ConcurrentHashSet<ulong>(); | _channels = new ConcurrentHashSet<ulong>(); | ||||
| if (_members == null) | if (_members == null) | ||||
| _members = new ConcurrentDictionary<ulong, CachedGuildUser>(); | |||||
| _members = new ConcurrentDictionary<ulong, SocketGuildUser>(); | |||||
| if (_roles == null) | if (_roles == null) | ||||
| _roles = new ConcurrentDictionary<ulong, Role>(); | _roles = new ConcurrentDictionary<ulong, Role>(); | ||||
| if (Emojis == null) | if (Emojis == null) | ||||
| @@ -91,7 +93,7 @@ namespace Discord | |||||
| } | } | ||||
| _channels = channels; | _channels = channels; | ||||
| var members = new ConcurrentDictionary<ulong, CachedGuildUser>(1, (int)(model.Presences.Length * 1.05)); | |||||
| var members = new ConcurrentDictionary<ulong, SocketGuildUser>(1, (int)(model.Presences.Length * 1.05)); | |||||
| { | { | ||||
| DownloadedMemberCount = 0; | DownloadedMemberCount = 0; | ||||
| for (int i = 0; i < model.Members.Length; i++) | for (int i = 0; i < model.Members.Length; i++) | ||||
| @@ -119,7 +121,7 @@ namespace Discord | |||||
| { | { | ||||
| if (source == UpdateSource.Rest && IsAttached) return; | if (source == UpdateSource.Rest && IsAttached) return; | ||||
| var members = new ConcurrentDictionary<ulong, CachedGuildUser>(1, (int)(model.Presences.Length * 1.05)); | |||||
| var members = new ConcurrentDictionary<ulong, SocketGuildUser>(1, (int)(model.Presences.Length * 1.05)); | |||||
| { | { | ||||
| DownloadedMemberCount = 0; | DownloadedMemberCount = 0; | ||||
| for (int i = 0; i < model.Members.Length; i++) | for (int i = 0; i < model.Members.Length; i++) | ||||
| @@ -152,14 +154,14 @@ namespace Discord | |||||
| (channels ?? _channels).TryAdd(model.Id); | (channels ?? _channels).TryAdd(model.Id); | ||||
| dataStore.AddChannel(channel); | dataStore.AddChannel(channel); | ||||
| } | } | ||||
| public ICachedGuildChannel GetChannel(ulong id) | |||||
| public ISocketGuildChannel GetChannel(ulong id) | |||||
| { | { | ||||
| return Discord.DataStore.GetChannel(id) as ICachedGuildChannel; | |||||
| return Discord.DataStore.GetChannel(id) as ISocketGuildChannel; | |||||
| } | } | ||||
| public ICachedGuildChannel RemoveChannel(ulong id) | |||||
| public ISocketGuildChannel RemoveChannel(ulong id) | |||||
| { | { | ||||
| _channels.TryRemove(id); | _channels.TryRemove(id); | ||||
| return Discord.DataStore.RemoveChannel(id) as ICachedGuildChannel; | |||||
| return Discord.DataStore.RemoveChannel(id) as ISocketGuildChannel; | |||||
| } | } | ||||
| public Role AddRole(RoleModel model, ConcurrentDictionary<ulong, Role> roles = null) | public Role AddRole(RoleModel model, ConcurrentDictionary<ulong, Role> roles = null) | ||||
| @@ -181,48 +183,48 @@ namespace Discord | |||||
| => Task.FromResult<IGuildUser>(CurrentUser); | => Task.FromResult<IGuildUser>(CurrentUser); | ||||
| public override Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync() | public override Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync() | ||||
| => Task.FromResult<IReadOnlyCollection<IGuildUser>>(Members); | => Task.FromResult<IReadOnlyCollection<IGuildUser>>(Members); | ||||
| public CachedGuildUser AddUser(MemberModel model, DataStore dataStore, ConcurrentDictionary<ulong, CachedGuildUser> members = null) | |||||
| public SocketGuildUser AddUser(MemberModel model, DataStore dataStore, ConcurrentDictionary<ulong, SocketGuildUser> members = null) | |||||
| { | { | ||||
| members = members ?? _members; | members = members ?? _members; | ||||
| CachedGuildUser member; | |||||
| SocketGuildUser member; | |||||
| if (members.TryGetValue(model.User.Id, out member)) | if (members.TryGetValue(model.User.Id, out member)) | ||||
| member.Update(model, UpdateSource.WebSocket); | member.Update(model, UpdateSource.WebSocket); | ||||
| else | else | ||||
| { | { | ||||
| var user = Discord.GetOrAddUser(model.User, dataStore); | var user = Discord.GetOrAddUser(model.User, dataStore); | ||||
| member = new CachedGuildUser(this, user, model); | |||||
| member = new SocketGuildUser(this, user, model); | |||||
| members[user.Id] = member; | members[user.Id] = member; | ||||
| DownloadedMemberCount++; | DownloadedMemberCount++; | ||||
| } | } | ||||
| return member; | return member; | ||||
| } | } | ||||
| public CachedGuildUser AddOrUpdateUser(PresenceModel model, DataStore dataStore, ConcurrentDictionary<ulong, CachedGuildUser> members = null) | |||||
| public SocketGuildUser AddOrUpdateUser(PresenceModel model, DataStore dataStore, ConcurrentDictionary<ulong, SocketGuildUser> members = null) | |||||
| { | { | ||||
| members = members ?? _members; | members = members ?? _members; | ||||
| CachedGuildUser member; | |||||
| SocketGuildUser member; | |||||
| if (members.TryGetValue(model.User.Id, out member)) | if (members.TryGetValue(model.User.Id, out member)) | ||||
| member.Update(model, UpdateSource.WebSocket); | member.Update(model, UpdateSource.WebSocket); | ||||
| else | else | ||||
| { | { | ||||
| var user = Discord.GetOrAddUser(model.User, dataStore); | var user = Discord.GetOrAddUser(model.User, dataStore); | ||||
| member = new CachedGuildUser(this, user, model); | |||||
| member = new SocketGuildUser(this, user, model); | |||||
| members[user.Id] = member; | members[user.Id] = member; | ||||
| DownloadedMemberCount++; | DownloadedMemberCount++; | ||||
| } | } | ||||
| return member; | return member; | ||||
| } | } | ||||
| public CachedGuildUser GetUser(ulong id) | |||||
| public SocketGuildUser GetUser(ulong id) | |||||
| { | { | ||||
| CachedGuildUser member; | |||||
| SocketGuildUser member; | |||||
| if (_members.TryGetValue(id, out member)) | if (_members.TryGetValue(id, out member)) | ||||
| return member; | return member; | ||||
| return null; | return null; | ||||
| } | } | ||||
| public CachedGuildUser RemoveUser(ulong id) | |||||
| public SocketGuildUser RemoveUser(ulong id) | |||||
| { | { | ||||
| CachedGuildUser member; | |||||
| SocketGuildUser member; | |||||
| if (_members.TryRemove(id, out member)) | if (_members.TryRemove(id, out member)) | ||||
| return member; | return member; | ||||
| return null; | return null; | ||||
| @@ -238,7 +240,7 @@ namespace Discord | |||||
| public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary<ulong, VoiceState> voiceStates = null) | public VoiceState AddOrUpdateVoiceState(VoiceStateModel model, DataStore dataStore, ConcurrentDictionary<ulong, VoiceState> voiceStates = null) | ||||
| { | { | ||||
| var voiceChannel = dataStore.GetChannel(model.ChannelId.Value) as CachedVoiceChannel; | |||||
| var voiceChannel = dataStore.GetChannel(model.ChannelId.Value) as SocketVoiceChannel; | |||||
| var voiceState = new VoiceState(voiceChannel, model); | var voiceState = new VoiceState(voiceChannel, model); | ||||
| (voiceStates ?? _voiceStates)[model.UserId] = voiceState; | (voiceStates ?? _voiceStates)[model.UserId] = voiceState; | ||||
| return voiceState; | return voiceState; | ||||
| @@ -307,16 +309,16 @@ namespace Discord | |||||
| await audioClient.ConnectAsync(url, CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); | await audioClient.ConnectAsync(url, CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); | ||||
| } | } | ||||
| public CachedGuild Clone() => MemberwiseClone() as CachedGuild; | |||||
| public SocketGuild Clone() => MemberwiseClone() as SocketGuild; | |||||
| new internal ICachedGuildChannel ToChannel(ChannelModel model) | |||||
| new internal ISocketGuildChannel ToChannel(ChannelModel model) | |||||
| { | { | ||||
| switch (model.Type) | switch (model.Type) | ||||
| { | { | ||||
| case ChannelType.Text: | case ChannelType.Text: | ||||
| return new CachedTextChannel(this, model); | |||||
| return new SocketTextChannel(this, model); | |||||
| case ChannelType.Voice: | case ChannelType.Voice: | ||||
| return new CachedVoiceChannel(this, model); | |||||
| return new SocketVoiceChannel(this, model); | |||||
| default: | default: | ||||
| throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); | throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); | ||||
| } | } | ||||
| @@ -1,7 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ICachedEntity<T> : IEntity<T> | |||||
| { | |||||
| DiscordSocketClient Discord { get; } | |||||
| } | |||||
| } | |||||
| @@ -1,7 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ICachedGuildChannel : ICachedChannel, IGuildChannel | |||||
| { | |||||
| new CachedGuild Guild { get; } | |||||
| } | |||||
| } | |||||
| @@ -1,16 +0,0 @@ | |||||
| using System.Collections.Generic; | |||||
| using MessageModel = Discord.API.Message; | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ICachedMessageChannel : ICachedChannel, IMessageChannel | |||||
| { | |||||
| IReadOnlyCollection<ICachedUser> Members { get; } | |||||
| CachedMessage AddMessage(ICachedUser author, MessageModel model); | |||||
| CachedMessage GetMessage(ulong id); | |||||
| CachedMessage RemoveMessage(ulong id); | |||||
| ICachedUser GetUser(ulong id, bool skipCheck = false); | |||||
| } | |||||
| } | |||||
| @@ -1,9 +0,0 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ICachedPrivateChannel : ICachedChannel, IPrivateChannel | |||||
| { | |||||
| new IReadOnlyCollection<ICachedUser> Recipients { get; } | |||||
| } | |||||
| } | |||||
| @@ -1,9 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ICachedUser : IUser, ICachedEntity<ulong> | |||||
| { | |||||
| CachedGlobalUser User { get; } | |||||
| ICachedUser Clone(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,19 @@ | |||||
| using Model = Discord.API.Message; | |||||
| namespace Discord | |||||
| { | |||||
| internal class SocketMessage : Message | |||||
| { | |||||
| internal override bool IsAttached => true; | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||||
| public new ISocketMessageChannel Channel => base.Channel as ISocketMessageChannel; | |||||
| public SocketMessage(ISocketMessageChannel channel, IUser author, Model model) | |||||
| : base(channel, author, model) | |||||
| { | |||||
| } | |||||
| public SocketMessage Clone() => MemberwiseClone() as SocketMessage; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| namespace Discord | |||||
| { | |||||
| internal interface ISocketUser : IUser, IEntity<ulong> | |||||
| { | |||||
| SocketGlobalUser User { get; } | |||||
| ISocketUser Clone(); | |||||
| } | |||||
| } | |||||
| @@ -5,9 +5,12 @@ using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | [DebuggerDisplay("{DebuggerDisplay,nq}")] | ||||
| internal class CachedDMUser : ICachedUser | |||||
| internal class SocketDMUser : ISocketUser | |||||
| { | { | ||||
| public CachedGlobalUser User { get; } | |||||
| internal bool IsAttached => true; | |||||
| bool IEntity<ulong>.IsAttached => IsAttached; | |||||
| public SocketGlobalUser User { get; } | |||||
| public DiscordSocketClient Discord => User.Discord; | public DiscordSocketClient Discord => User.Discord; | ||||
| @@ -20,12 +23,11 @@ namespace Discord | |||||
| public DateTimeOffset CreatedAt => User.CreatedAt; | public DateTimeOffset CreatedAt => User.CreatedAt; | ||||
| public string Discriminator => User.Discriminator; | public string Discriminator => User.Discriminator; | ||||
| public ushort DiscriminatorValue => User.DiscriminatorValue; | public ushort DiscriminatorValue => User.DiscriminatorValue; | ||||
| public bool IsAttached => User.IsAttached; | |||||
| public bool IsBot => User.IsBot; | public bool IsBot => User.IsBot; | ||||
| public string Mention => MentionUtils.Mention(this); | public string Mention => MentionUtils.Mention(this); | ||||
| public string Username => User.Username; | public string Username => User.Username; | ||||
| public CachedDMUser(CachedGlobalUser user) | |||||
| public SocketDMUser(SocketGlobalUser user) | |||||
| { | { | ||||
| User = user; | User = user; | ||||
| } | } | ||||
| @@ -35,8 +37,8 @@ namespace Discord | |||||
| User.Update(model, source); | User.Update(model, source); | ||||
| } | } | ||||
| public CachedDMUser Clone() => MemberwiseClone() as CachedDMUser; | |||||
| ICachedUser ICachedUser.Clone() => Clone(); | |||||
| public SocketDMUser Clone() => MemberwiseClone() as SocketDMUser; | |||||
| ISocketUser ISocketUser.Clone() => Clone(); | |||||
| public override string ToString() => $"{Username}#{Discriminator}"; | public override string ToString() => $"{Username}#{Discriminator}"; | ||||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; | private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; | ||||
| @@ -4,16 +4,18 @@ using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedGlobalUser : User, ICachedUser | |||||
| internal class SocketGlobalUser : User, ISocketUser | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| private ushort _references; | private ushort _references; | ||||
| public Presence Presence { get; private set; } | public Presence Presence { get; private set; } | ||||
| public new DiscordSocketClient Discord { get { throw new NotSupportedException(); } } | public new DiscordSocketClient Discord { get { throw new NotSupportedException(); } } | ||||
| CachedGlobalUser ICachedUser.User => this; | |||||
| SocketGlobalUser ISocketUser.User => this; | |||||
| public CachedGlobalUser(Model model) | |||||
| public SocketGlobalUser(Model model) | |||||
| : base(model) | : base(model) | ||||
| { | { | ||||
| } | } | ||||
| @@ -51,7 +53,7 @@ namespace Discord | |||||
| //} | //} | ||||
| } | } | ||||
| public CachedGlobalUser Clone() => MemberwiseClone() as CachedGlobalUser; | |||||
| ICachedUser ICachedUser.Clone() => Clone(); | |||||
| public SocketGlobalUser Clone() => MemberwiseClone() as SocketGlobalUser; | |||||
| ISocketUser ISocketUser.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,11 +3,13 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| [DebuggerDisplay("{DebuggerDisplay,nq}")] | [DebuggerDisplay("{DebuggerDisplay,nq}")] | ||||
| internal class CachedGroupUser : GroupUser, ICachedUser | |||||
| internal class SocketGroupUser : GroupUser, ISocketUser | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public new CachedGroupChannel Channel => base.Channel as CachedGroupChannel; | |||||
| public new CachedGlobalUser User => base.User as CachedGlobalUser; | |||||
| public new SocketGroupChannel Channel => base.Channel as SocketGroupChannel; | |||||
| public new SocketGlobalUser User => base.User as SocketGlobalUser; | |||||
| public Presence Presence => User.Presence; //{ get; private set; } | public Presence Presence => User.Presence; //{ get; private set; } | ||||
| public override Game Game => Presence.Game; | public override Game Game => Presence.Game; | ||||
| @@ -17,15 +19,15 @@ namespace Discord | |||||
| public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | ||||
| public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; | public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; | ||||
| public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; | public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; | ||||
| public CachedVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; | |||||
| public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; | |||||
| public CachedGroupUser(CachedGroupChannel channel, CachedGlobalUser user) | |||||
| public SocketGroupUser(SocketGroupChannel channel, SocketGlobalUser user) | |||||
| : base(channel, user) | : base(channel, user) | ||||
| { | { | ||||
| } | } | ||||
| public CachedGroupUser Clone() => MemberwiseClone() as CachedGroupUser; | |||||
| ICachedUser ICachedUser.Clone() => Clone(); | |||||
| public SocketGroupUser Clone() => MemberwiseClone() as SocketGroupUser; | |||||
| ISocketUser ISocketUser.Clone() => Clone(); | |||||
| public override string ToString() => $"{Username}#{Discriminator}"; | public override string ToString() => $"{Username}#{Discriminator}"; | ||||
| private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; | private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})"; | ||||
| @@ -3,11 +3,13 @@ using PresenceModel = Discord.API.Presence; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| internal class CachedGuildUser : GuildUser, ICachedUser | |||||
| internal class SocketGuildUser : GuildUser, ISocketUser | |||||
| { | { | ||||
| internal override bool IsAttached => true; | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | ||||
| public new CachedGuild Guild => base.Guild as CachedGuild; | |||||
| public new CachedGlobalUser User => base.User as CachedGlobalUser; | |||||
| public new SocketGuild Guild => base.Guild as SocketGuild; | |||||
| public new SocketGlobalUser User => base.User as SocketGlobalUser; | |||||
| public Presence Presence => User.Presence; //{ get; private set; } | public Presence Presence => User.Presence; //{ get; private set; } | ||||
| public override Game Game => Presence.Game; | public override Game Game => Presence.Game; | ||||
| @@ -17,14 +19,14 @@ namespace Discord | |||||
| public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; | ||||
| public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; | public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; | ||||
| public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; | public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; | ||||
| public CachedVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; | |||||
| public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; | |||||
| public CachedGuildUser(CachedGuild guild, CachedGlobalUser user, Model model) | |||||
| public SocketGuildUser(SocketGuild guild, SocketGlobalUser user, Model model) | |||||
| : base(guild, user, model) | : base(guild, user, model) | ||||
| { | { | ||||
| //Presence = new Presence(null, UserStatus.Offline); | //Presence = new Presence(null, UserStatus.Offline); | ||||
| } | } | ||||
| public CachedGuildUser(CachedGuild guild, CachedGlobalUser user, PresenceModel model) | |||||
| public SocketGuildUser(SocketGuild guild, SocketGlobalUser user, PresenceModel model) | |||||
| : base(guild, user, model) | : base(guild, user, model) | ||||
| { | { | ||||
| } | } | ||||
| @@ -39,7 +41,7 @@ namespace Discord | |||||
| User.Update(model, source); | User.Update(model, source); | ||||
| } | } | ||||
| public CachedGuildUser Clone() => MemberwiseClone() as CachedGuildUser; | |||||
| ICachedUser ICachedUser.Clone() => Clone(); | |||||
| public SocketGuildUser Clone() => MemberwiseClone() as SocketGuildUser; | |||||
| ISocketUser ISocketUser.Clone() => Clone(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,21 @@ | |||||
| using System; | |||||
| using Model = Discord.API.User; | |||||
| namespace Discord | |||||
| { | |||||
| internal class SocketSelfUser : SelfUser, ISocketUser | |||||
| { | |||||
| internal override bool IsAttached => true; | |||||
| public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient; | |||||
| SocketGlobalUser ISocketUser.User { get { throw new NotSupportedException(); } } | |||||
| public SocketSelfUser(DiscordSocketClient discord, Model model) | |||||
| : base(discord, model) | |||||
| { | |||||
| } | |||||
| public SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser; | |||||
| ISocketUser ISocketUser.Clone() => Clone(); | |||||
| } | |||||
| } | |||||
| @@ -19,7 +19,7 @@ namespace Discord | |||||
| private readonly Flags _voiceStates; | private readonly Flags _voiceStates; | ||||
| public CachedVoiceChannel VoiceChannel { get; } | |||||
| public SocketVoiceChannel VoiceChannel { get; } | |||||
| public string VoiceSessionId { get; } | public string VoiceSessionId { get; } | ||||
| public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | public bool IsMuted => (_voiceStates & Flags.Muted) != 0; | ||||
| @@ -28,9 +28,9 @@ namespace Discord | |||||
| public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; | public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; | ||||
| public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; | ||||
| public VoiceState(CachedVoiceChannel voiceChannel, Model model) | |||||
| public VoiceState(SocketVoiceChannel voiceChannel, Model model) | |||||
| : this(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress) { } | : this(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress) { } | ||||
| public VoiceState(CachedVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isSuppressed) | |||||
| public VoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isSuppressed) | |||||
| { | { | ||||
| VoiceChannel = voiceChannel; | VoiceChannel = voiceChannel; | ||||
| VoiceSessionId = sessionId; | VoiceSessionId = sessionId; | ||||
| @@ -5,7 +5,7 @@ namespace Discord.Extensions | |||||
| { | { | ||||
| public static class DiscordClientExtensions | public static class DiscordClientExtensions | ||||
| { | { | ||||
| public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this DiscordClient discord) | |||||
| public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this DiscordRestClient discord) | |||||
| { | { | ||||
| var regions = await discord.GetVoiceRegionsAsync().ConfigureAwait(false); | var regions = await discord.GetVoiceRegionsAsync().ConfigureAwait(false); | ||||
| return regions.FirstOrDefault(x => x.IsOptimal); | return regions.FirstOrDefault(x => x.IsOptimal); | ||||
| @@ -9,11 +9,11 @@ namespace Discord.Extensions | |||||
| public static Task AddRolesAsync(this IGuildUser user, params IRole[] roles) | public static Task AddRolesAsync(this IGuildUser user, params IRole[] roles) | ||||
| => AddRolesAsync(user, (IEnumerable<IRole>)roles); | => AddRolesAsync(user, (IEnumerable<IRole>)roles); | ||||
| public static Task AddRolesAsync(this IGuildUser user, IEnumerable<IRole> roles) | public static Task AddRolesAsync(this IGuildUser user, IEnumerable<IRole> roles) | ||||
| => user.ModifyAsync(x => x.Roles = Optional.Create(user.Roles.Concat(roles))); | |||||
| => user.ModifyAsync(x => x.Roles = user.Roles.Concat(roles)); | |||||
| public static Task RemoveRolesAsync(this IGuildUser user, params IRole[] roles) | public static Task RemoveRolesAsync(this IGuildUser user, params IRole[] roles) | ||||
| => RemoveRolesAsync(user, (IEnumerable<IRole>)roles); | => RemoveRolesAsync(user, (IEnumerable<IRole>)roles); | ||||
| public static Task RemoveRolesAsync(this IGuildUser user, IEnumerable<IRole> roles) | public static Task RemoveRolesAsync(this IGuildUser user, IEnumerable<IRole> roles) | ||||
| => user.ModifyAsync(x => x.Roles = Optional.Create(user.Roles.Except(roles))); | |||||
| => user.ModifyAsync(x => x.Roles = user.Roles.Except(roles)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -11,14 +11,10 @@ namespace Discord | |||||
| //TODO: Docstrings should explain when REST requests are sent and how many | //TODO: Docstrings should explain when REST requests are sent and how many | ||||
| public interface IDiscordClient : IDisposable | public interface IDiscordClient : IDisposable | ||||
| { | { | ||||
| LoginState LoginState { get; } | |||||
| ConnectionState ConnectionState { get; } | ConnectionState ConnectionState { get; } | ||||
| DiscordApiClient ApiClient { get; } | DiscordApiClient ApiClient { get; } | ||||
| ILogManager LogManager { get; } | ILogManager LogManager { get; } | ||||
| Task LoginAsync(TokenType tokenType, string token, bool validateToken = true); | |||||
| Task LogoutAsync(); | |||||
| Task ConnectAsync(); | Task ConnectAsync(); | ||||
| Task DisconnectAsync(); | Task DisconnectAsync(); | ||||