@@ -94,9 +94,6 @@ | |||
<Compile Include="..\Discord.Net\API\Enums\PermissionTarget.cs"> | |||
<Link>API\Enums\PermissionTarget.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\API\Enums\Region.cs"> | |||
<Link>API\Enums\Region.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\API\Enums\StringEnum.cs"> | |||
<Link>API\Enums\StringEnum.cs</Link> | |||
</Compile> | |||
@@ -259,6 +256,9 @@ | |||
<Compile Include="..\Discord.Net\Models\Permissions.cs"> | |||
<Link>Models\Permissions.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Models\Region.cs"> | |||
<Link>Models\Region.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Models\Role.cs"> | |||
<Link>Models\Role.cs</Link> | |||
</Compile> | |||
@@ -0,0 +1,11 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Discord.API.Converters | |||
{ | |||
public class StringEnumConverter | |||
{ | |||
} | |||
} |
@@ -6,6 +6,8 @@ namespace Discord | |||
{ | |||
public class DiscordAPIClientConfig | |||
{ | |||
internal static readonly string UserAgent = $"Discord.Net/{DiscordClient.Version} (https://github.com/RogueException/Discord.Net)"; | |||
/// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary> | |||
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } } | |||
private LogMessageSeverity _logLevel = LogMessageSeverity.Info; | |||
@@ -21,15 +23,6 @@ namespace Discord | |||
public NetworkCredential ProxyCredentials { get { return _proxyCredentials; } set { SetValue(ref _proxyCredentials, value); } } | |||
private NetworkCredential _proxyCredentials = null; | |||
internal string UserAgent | |||
{ | |||
get | |||
{ | |||
string version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||
return $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"; | |||
} | |||
} | |||
//Lock | |||
protected bool _isLocked; | |||
internal void Lock() { _isLocked = true; } | |||
@@ -261,7 +261,7 @@ namespace Discord | |||
} | |||
/// <summary> Deserializes messages from JSON format and imports them into the message cache.</summary> | |||
public IEnumerable<Message> ImportMessages(string json) | |||
public IEnumerable<Message> ImportMessages(Channel channel, string json) | |||
{ | |||
if (json == null) throw new ArgumentNullException(nameof(json)); | |||
@@ -269,8 +269,8 @@ namespace Discord | |||
.Select(x => | |||
{ | |||
var msg = new Message(this, | |||
x["Id"].Value<long>(), | |||
x["ChannelId"].Value<long>(), | |||
x["Id"].Value<long>(), | |||
channel.Id, | |||
x["UserId"].Value<long>()); | |||
var reader = x.CreateReader(); | |||
@@ -86,19 +86,19 @@ namespace Discord | |||
if (region == null) throw new ArgumentNullException(nameof(region)); | |||
CheckReady(); | |||
var response = await _api.CreateServer(name, region.Value).ConfigureAwait(false); | |||
var response = await _api.CreateServer(name, region.Id).ConfigureAwait(false); | |||
var server = _servers.GetOrAdd(response.Id); | |||
server.Update(response); | |||
return server; | |||
} | |||
/// <summary> Edits the provided server, changing only non-null attributes. </summary> | |||
public async Task EditServer(Server server, string name = null, Region region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||
public async Task EditServer(Server server, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||
{ | |||
if (server == null) throw new ArgumentNullException(nameof(server)); | |||
CheckReady(); | |||
var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region.Value, iconType: iconType, icon: icon).ConfigureAwait(false); | |||
var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region, iconType: iconType, icon: icon).ConfigureAwait(false); | |||
server.Update(response); | |||
} | |||
@@ -112,5 +112,12 @@ namespace Discord | |||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
//return _servers.TryRemove(server.Id); | |||
} | |||
public async Task<IEnumerable<Region>> GetVoiceRegions() | |||
{ | |||
CheckReady(); | |||
return (await _api.GetVoiceRegions()).Select(x => new Region { Id = x.Id, Name = x.Name, Hostname = x.Hostname, Port = x.Port }); | |||
} | |||
} | |||
} |
@@ -5,6 +5,7 @@ using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
@@ -12,12 +13,14 @@ namespace Discord | |||
/// <summary> Provides a connection to the DiscordApp service. </summary> | |||
public sealed partial class DiscordClient : DiscordWSClient | |||
{ | |||
public static readonly string Version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3); | |||
private readonly DiscordAPIClient _api; | |||
private readonly Random _rand; | |||
private readonly JsonSerializer _socketSerializer, _messageImporter; | |||
private readonly JsonSerializer _messageImporter; | |||
private readonly ConcurrentQueue<Message> _pendingMessages; | |||
private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients; | |||
private readonly Dictionary<Type, IService> _services; | |||
private readonly Dictionary<Type, object> _singletons; | |||
private bool _sentInitialLog; | |||
private uint _nextVoiceClientId; | |||
private UserStatus _status; | |||
@@ -47,7 +50,7 @@ namespace Discord | |||
_roles = new Roles(this, cacheLock); | |||
_servers = new Servers(this, cacheLock); | |||
_globalUsers = new GlobalUsers(this, cacheLock); | |||
_services = new Dictionary<Type, IService>(); | |||
_singletons = new Dictionary<Type, object>(); | |||
_status = UserStatus.Online; | |||
@@ -162,16 +165,9 @@ namespace Discord | |||
if (Config.UseMessageQueue) | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
_socketSerializer = new JsonSerializer(); | |||
_socketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc; | |||
#if TEST_RESPONSES | |||
_serializer.CheckAdditionalContent = true; | |||
_serializer.MissingMemberHandling = MissingMemberHandling.Error; | |||
#endif | |||
_messageImporter = new JsonSerializer(); | |||
_messageImporter.ContractResolver = new MessageImporterResolver(); | |||
_messageImporter.ContractResolver = new Message.ImportResolver(); | |||
} | |||
internal override VoiceWebSocket CreateVoiceSocket() | |||
{ | |||
@@ -276,25 +272,34 @@ namespace Discord | |||
_privateUser = null; | |||
} | |||
public T AddSingleton<T>(T obj) | |||
where T : class | |||
{ | |||
_singletons.Add(typeof(T), obj); | |||
return obj; | |||
} | |||
public T GetSingleton<T>(bool required = true) | |||
where T : class | |||
{ | |||
object singleton; | |||
T singletonT = null; | |||
if (_singletons.TryGetValue(typeof(T), out singleton)) | |||
singletonT = singleton as T; | |||
if (singletonT == null && required) | |||
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}."); | |||
return singletonT; | |||
} | |||
public T AddService<T>(T obj) | |||
where T : class, IService | |||
{ | |||
_services.Add(typeof(T), obj); | |||
AddSingleton(obj); | |||
obj.Install(this); | |||
return obj; | |||
} | |||
public T GetService<T>(bool required = true) | |||
where T : class, IService | |||
{ | |||
IService service; | |||
T serviceT = null; | |||
if (_services.TryGetValue(typeof(T), out service)) | |||
serviceT = service as T; | |||
if (serviceT == null && required) | |||
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}."); | |||
return serviceT; | |||
} | |||
=> GetSingleton<T>(required); | |||
protected override IEnumerable<Task> GetTasks() | |||
{ | |||
@@ -314,7 +319,7 @@ namespace Discord | |||
case "READY": //Resync | |||
{ | |||
base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready | |||
var data = e.Payload.ToObject<ReadyEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<ReadyEvent>(_dataSocketSerializer); | |||
_privateUser = _users.GetOrAdd(data.User.Id, null); | |||
_privateUser.Update(data.User); | |||
_privateUser.Global.Update(data.User); | |||
@@ -339,7 +344,7 @@ namespace Discord | |||
//Servers | |||
case "GUILD_CREATE": | |||
{ | |||
var data = e.Payload.ToObject<GuildCreateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<GuildCreateEvent>(_dataSocketSerializer); | |||
if (data.Unavailable != true) | |||
{ | |||
var server = _servers.GetOrAdd(data.Id); | |||
@@ -353,7 +358,7 @@ namespace Discord | |||
break; | |||
case "GUILD_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<GuildUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<GuildUpdateEvent>(_dataSocketSerializer); | |||
var server = _servers[data.Id]; | |||
if (server != null) | |||
{ | |||
@@ -364,7 +369,7 @@ namespace Discord | |||
break; | |||
case "GUILD_DELETE": | |||
{ | |||
var data = e.Payload.ToObject<GuildDeleteEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<GuildDeleteEvent>(_dataSocketSerializer); | |||
var server = _servers.TryRemove(data.Id); | |||
if (server != null) | |||
{ | |||
@@ -379,7 +384,7 @@ namespace Discord | |||
//Channels | |||
case "CHANNEL_CREATE": | |||
{ | |||
var data = e.Payload.ToObject<ChannelCreateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<ChannelCreateEvent>(_dataSocketSerializer); | |||
Channel channel; | |||
if (data.IsPrivate) | |||
{ | |||
@@ -395,7 +400,7 @@ namespace Discord | |||
break; | |||
case "CHANNEL_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<ChannelUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<ChannelUpdateEvent>(_dataSocketSerializer); | |||
var channel = _channels[data.Id]; | |||
if (channel != null) | |||
{ | |||
@@ -406,7 +411,7 @@ namespace Discord | |||
break; | |||
case "CHANNEL_DELETE": | |||
{ | |||
var data = e.Payload.ToObject<ChannelDeleteEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<ChannelDeleteEvent>(_dataSocketSerializer); | |||
var channel = _channels.TryRemove(data.Id); | |||
if (channel != null) | |||
RaiseChannelDestroyed(channel); | |||
@@ -416,7 +421,7 @@ namespace Discord | |||
//Members | |||
case "GUILD_MEMBER_ADD": | |||
{ | |||
var data = e.Payload.ToObject<MemberAddEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MemberAddEvent>(_dataSocketSerializer); | |||
var user = _users.GetOrAdd(data.User.Id, data.GuildId); | |||
user.Update(data); | |||
if (Config.TrackActivity) | |||
@@ -426,7 +431,7 @@ namespace Discord | |||
break; | |||
case "GUILD_MEMBER_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<MemberUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MemberUpdateEvent>(_dataSocketSerializer); | |||
var user = _users[data.User.Id, data.GuildId]; | |||
if (user != null) | |||
{ | |||
@@ -437,7 +442,7 @@ namespace Discord | |||
break; | |||
case "GUILD_MEMBER_REMOVE": | |||
{ | |||
var data = e.Payload.ToObject<MemberRemoveEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MemberRemoveEvent>(_dataSocketSerializer); | |||
var user = _users.TryRemove(data.UserId, data.GuildId); | |||
if (user != null) | |||
RaiseUserLeft(user); | |||
@@ -445,7 +450,7 @@ namespace Discord | |||
break; | |||
case "GUILD_MEMBERS_CHUNK": | |||
{ | |||
var data = e.Payload.ToObject<MembersChunkEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MembersChunkEvent>(_dataSocketSerializer); | |||
foreach (var memberData in data.Members) | |||
{ | |||
var user = _users.GetOrAdd(memberData.User.Id, memberData.GuildId); | |||
@@ -458,7 +463,7 @@ namespace Discord | |||
//Roles | |||
case "GUILD_ROLE_CREATE": | |||
{ | |||
var data = e.Payload.ToObject<RoleCreateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<RoleCreateEvent>(_dataSocketSerializer); | |||
var role = _roles.GetOrAdd(data.Data.Id, data.GuildId); | |||
role.Update(data.Data); | |||
var server = _servers[data.GuildId]; | |||
@@ -469,7 +474,7 @@ namespace Discord | |||
break; | |||
case "GUILD_ROLE_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<RoleUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<RoleUpdateEvent>(_dataSocketSerializer); | |||
var role = _roles[data.Data.Id]; | |||
if (role != null) | |||
{ | |||
@@ -480,7 +485,7 @@ namespace Discord | |||
break; | |||
case "GUILD_ROLE_DELETE": | |||
{ | |||
var data = e.Payload.ToObject<RoleDeleteEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<RoleDeleteEvent>(_dataSocketSerializer); | |||
var role = _roles.TryRemove(data.RoleId); | |||
if (role != null) | |||
{ | |||
@@ -495,7 +500,7 @@ namespace Discord | |||
//Bans | |||
case "GUILD_BAN_ADD": | |||
{ | |||
var data = e.Payload.ToObject<BanAddEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<BanAddEvent>(_dataSocketSerializer); | |||
var server = _servers[data.GuildId]; | |||
if (server != null) | |||
{ | |||
@@ -507,7 +512,7 @@ namespace Discord | |||
break; | |||
case "GUILD_BAN_REMOVE": | |||
{ | |||
var data = e.Payload.ToObject<BanRemoveEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<BanRemoveEvent>(_dataSocketSerializer); | |||
var server = _servers[data.GuildId]; | |||
if (server != null) | |||
{ | |||
@@ -521,7 +526,7 @@ namespace Discord | |||
//Messages | |||
case "MESSAGE_CREATE": | |||
{ | |||
var data = e.Payload.ToObject<MessageCreateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MessageCreateEvent>(_dataSocketSerializer); | |||
Message msg = null; | |||
bool isAuthor = data.Author.Id == _userId; | |||
@@ -548,7 +553,7 @@ namespace Discord | |||
break; | |||
case "MESSAGE_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<MessageUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MessageUpdateEvent>(_dataSocketSerializer); | |||
var msg = _messages[data.Id]; | |||
if (msg != null) | |||
{ | |||
@@ -559,7 +564,7 @@ namespace Discord | |||
break; | |||
case "MESSAGE_DELETE": | |||
{ | |||
var data = e.Payload.ToObject<MessageDeleteEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MessageDeleteEvent>(_dataSocketSerializer); | |||
var msg = _messages.TryRemove(data.Id); | |||
if (msg != null) | |||
RaiseMessageDeleted(msg); | |||
@@ -567,7 +572,7 @@ namespace Discord | |||
break; | |||
case "MESSAGE_ACK": | |||
{ | |||
var data = e.Payload.ToObject<MessageAckEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MessageAckEvent>(_dataSocketSerializer); | |||
var msg = GetMessage(data.MessageId); | |||
if (msg != null) | |||
RaiseMessageReadRemotely(msg); | |||
@@ -577,7 +582,7 @@ namespace Discord | |||
//Statuses | |||
case "PRESENCE_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<PresenceUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<PresenceUpdateEvent>(_dataSocketSerializer); | |||
var user = _users.GetOrAdd(data.User.Id, data.GuildId); | |||
if (user != null) | |||
{ | |||
@@ -588,7 +593,7 @@ namespace Discord | |||
break; | |||
case "TYPING_START": | |||
{ | |||
var data = e.Payload.ToObject<TypingStartEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<TypingStartEvent>(_dataSocketSerializer); | |||
var channel = _channels[data.ChannelId]; | |||
if (channel != null) | |||
{ | |||
@@ -614,7 +619,7 @@ namespace Discord | |||
//Voice | |||
case "VOICE_STATE_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_dataSocketSerializer); | |||
var user = _users[data.UserId, data.GuildId]; | |||
if (user != null) | |||
{ | |||
@@ -633,7 +638,7 @@ namespace Discord | |||
//Settings | |||
case "USER_UPDATE": | |||
{ | |||
var data = e.Payload.ToObject<UserUpdateEvent>(_socketSerializer); | |||
var data = e.Payload.ToObject<UserUpdateEvent>(_dataSocketSerializer); | |||
var user = _globalUsers[data.Id]; | |||
if (user != null) | |||
{ | |||
@@ -1,5 +1,6 @@ | |||
using Discord.Net; | |||
using Discord.Net.WebSockets; | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
@@ -23,9 +24,9 @@ namespace Discord | |||
protected readonly DiscordWSClientConfig _config; | |||
protected readonly ManualResetEvent _disconnectedEvent; | |||
protected readonly ManualResetEventSlim _connectedEvent; | |||
protected ExceptionDispatchInfo _disconnectReason; | |||
internal readonly DataWebSocket _dataSocket; | |||
internal readonly VoiceWebSocket _voiceSocket; | |||
protected ExceptionDispatchInfo _disconnectReason; | |||
protected string _gateway, _token; | |||
protected long? _userId, _voiceServerId; | |||
private Task _runTask; | |||
@@ -44,6 +45,10 @@ namespace Discord | |||
private CancellationTokenSource _cancelTokenSource; | |||
protected CancellationToken _cancelToken; | |||
internal JsonSerializer DataSocketSerializer => _dataSocketSerializer; | |||
internal JsonSerializer VoiceSocketSerializer => _voiceSocketSerializer; | |||
protected readonly JsonSerializer _dataSocketSerializer, _voiceSocketSerializer; | |||
/// <summary> Initializes a new instance of the DiscordClient class. </summary> | |||
public DiscordWSClient(DiscordWSClientConfig config = null) | |||
{ | |||
@@ -55,6 +60,32 @@ namespace Discord | |||
_disconnectedEvent = new ManualResetEvent(true); | |||
_connectedEvent = new ManualResetEventSlim(false); | |||
_dataSocketSerializer = new JsonSerializer(); | |||
_dataSocketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc; | |||
#if TEST_RESPONSES | |||
_dataSocketSerializer.CheckAdditionalContent = true; | |||
_dataSocketSerializer.MissingMemberHandling = MissingMemberHandling.Error; | |||
#else | |||
_dataSocketSerializer.Error += (s, e) => | |||
{ | |||
e.ErrorContext.Handled = true; | |||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.DataWebSocket, "Serialization Failed", e.ErrorContext.Error); | |||
}; | |||
#endif | |||
_voiceSocketSerializer = new JsonSerializer(); | |||
_voiceSocketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc; | |||
#if TEST_RESPONSES | |||
_voiceSocketSerializer.CheckAdditionalContent = true; | |||
_voiceSocketSerializer.MissingMemberHandling = MissingMemberHandling.Error; | |||
#else | |||
_voiceSocketSerializer.Error += (s, e) => | |||
{ | |||
e.ErrorContext.Handled = true; | |||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.VoiceWebSocket, "Serialization Failed", e.ErrorContext.Error); | |||
}; | |||
#endif | |||
_dataSocket = CreateDataSocket(); | |||
if (_config.EnableVoice) | |||
_voiceSocket = CreateVoiceSocket(); | |||
@@ -84,15 +84,21 @@ namespace Discord | |||
lock (_writerLock) | |||
{ | |||
TValue newItem = createFunc(); | |||
result = _dictionary.GetOrAdd(key, newItem); | |||
if (result == newItem) | |||
if (!_dictionary.ContainsKey(key)) | |||
{ | |||
result.Cache(); | |||
RaiseItemCreated(result); | |||
result = createFunc(); | |||
if (result.Cache()) | |||
{ | |||
_dictionary.TryAdd(key, result); | |||
RaiseItemCreated(result); | |||
} | |||
else | |||
result.Uncache(); | |||
return result; | |||
} | |||
else | |||
return _dictionary[key]; | |||
} | |||
return result; | |||
} | |||
protected void Import(IEnumerable<KeyValuePair<TKey, TValue>> items) | |||
{ | |||
@@ -101,9 +107,13 @@ namespace Discord | |||
foreach (var pair in items) | |||
{ | |||
var value = pair.Value; | |||
_dictionary.TryAdd(pair.Key, value); | |||
value.Cache(); | |||
RaiseItemCreated(value); | |||
if (value.Cache()) | |||
{ | |||
_dictionary.TryAdd(pair.Key, value); | |||
RaiseItemCreated(value); | |||
} | |||
else | |||
value.Uncache(); | |||
} | |||
} | |||
} | |||
@@ -31,10 +31,14 @@ namespace Discord | |||
_client = client; | |||
} | |||
internal void Cache() | |||
internal bool Cache() | |||
{ | |||
LoadReferences(); | |||
_isCached = true; | |||
if (LoadReferences()) | |||
{ | |||
_isCached = true; | |||
return true; | |||
} | |||
return false; | |||
} | |||
internal void Uncache() | |||
{ | |||
@@ -44,7 +48,7 @@ namespace Discord | |||
_isCached = false; | |||
} | |||
} | |||
internal abstract void LoadReferences(); | |||
internal abstract bool LoadReferences(); | |||
internal abstract void UnloadReferences(); | |||
} | |||
} |
@@ -41,9 +41,9 @@ namespace Discord | |||
} | |||
} | |||
public T Load() | |||
public bool Load() | |||
{ | |||
return Value; //Used for precaching | |||
return Value != null; //Used for precaching | |||
} | |||
public void Unload() | |||
@@ -124,12 +124,12 @@ namespace Discord | |||
if (client.Config.MessageCacheLength > 0) | |||
_messages = new ConcurrentDictionary<long, Message>(); | |||
} | |||
internal override void LoadReferences() | |||
internal override bool LoadReferences() | |||
{ | |||
if (IsPrivate) | |||
_recipient.Load(); | |||
return _recipient.Load(); | |||
else | |||
_server.Load(); | |||
return _server.Load(); | |||
} | |||
internal override void UnloadReferences() | |||
{ | |||
@@ -44,7 +44,7 @@ namespace Discord | |||
{ | |||
_users = new ConcurrentDictionary<long, User>(); | |||
} | |||
internal override void LoadReferences() { } | |||
internal override bool LoadReferences() { return true; } | |||
internal override void UnloadReferences() | |||
{ | |||
//Don't need to clean _users - they're considered owned by server | |||
@@ -82,7 +82,7 @@ namespace Discord | |||
{ | |||
XkcdCode = xkcdPass; | |||
} | |||
internal override void LoadReferences() { } | |||
internal override bool LoadReferences() { return true; } | |||
internal override void UnloadReferences() { } | |||
internal void Update(InviteReference model) | |||
@@ -15,24 +15,25 @@ namespace Discord | |||
Queued, | |||
Failed | |||
} | |||
internal class MessageImporterResolver : DefaultContractResolver | |||
public sealed class Message : CachedObject<long> | |||
{ | |||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | |||
internal class ImportResolver : DefaultContractResolver | |||
{ | |||
var property = base.CreateProperty(member, memberSerialization); | |||
if (member is PropertyInfo) | |||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | |||
{ | |||
if (!(member as PropertyInfo).CanWrite) | |||
return null; | |||
var property = base.CreateProperty(member, memberSerialization); | |||
if (member is PropertyInfo) | |||
{ | |||
if (member.Name == nameof(ChannelId) || !(member as PropertyInfo).CanWrite) | |||
return null; | |||
property.Writable = true; //Handles private setters | |||
property.Writable = true; //Handles private setters | |||
} | |||
return property; | |||
} | |||
return property; | |||
} | |||
} | |||
public sealed class Message : CachedObject<long> | |||
{ | |||
public sealed class Attachment : File | |||
{ | |||
/// <summary> Unique identifier for this file. </summary> | |||
@@ -173,6 +174,8 @@ namespace Discord | |||
x => | |||
{ | |||
var channel = Channel; | |||
if (channel == null) return null; | |||
if (!channel.IsPrivate) | |||
return _client.Users[x, channel.Server.Id]; | |||
else | |||
@@ -181,10 +184,9 @@ namespace Discord | |||
Attachments = _initialAttachments; | |||
Embeds = _initialEmbeds; | |||
} | |||
internal override void LoadReferences() | |||
internal override bool LoadReferences() | |||
{ | |||
_channel.Load(); | |||
_user.Load(); | |||
return _channel.Load() && _user.Load(); | |||
} | |||
internal override void UnloadReferences() | |||
{ | |||
@@ -0,0 +1,10 @@ | |||
namespace Discord | |||
{ | |||
public sealed class Region | |||
{ | |||
public string Hostname; | |||
public int Port; | |||
public string Id; | |||
public string Name; | |||
} | |||
} |
@@ -47,9 +47,9 @@ namespace Discord | |||
Color = new Color(0); | |||
Color.Lock(); | |||
} | |||
internal override void LoadReferences() | |||
internal override bool LoadReferences() | |||
{ | |||
_server.Load(); | |||
return _server.Load(); | |||
} | |||
internal override void UnloadReferences() | |||
{ | |||
@@ -103,9 +103,10 @@ namespace Discord | |||
//Local Cache | |||
_bans = new ConcurrentDictionary<long, bool>(); | |||
} | |||
internal override void LoadReferences() | |||
internal override bool LoadReferences() | |||
{ | |||
_afkChannel.Load(); | |||
return true; | |||
} | |||
internal override void UnloadReferences() | |||
{ | |||
@@ -148,10 +148,10 @@ namespace Discord | |||
if (serverId == null) | |||
UpdateRoles(null); | |||
} | |||
internal override void LoadReferences() | |||
internal override bool LoadReferences() | |||
{ | |||
_globalUser.Load(); | |||
_server.Load(); | |||
return _globalUser.Load() && | |||
(IsPrivate || _server.Load()); | |||
} | |||
internal override void UnloadReferences() | |||
{ | |||
@@ -20,7 +20,7 @@ namespace Discord.Net.Rest | |||
{ | |||
PreAuthenticate = false, | |||
ReadWriteTimeout = _config.APITimeout, | |||
UserAgent = _config.UserAgent | |||
UserAgent = DiscordAPIClientConfig.UserAgent | |||
}; | |||
if (_config.ProxyUrl != null) | |||
_client.Proxy = new WebProxy(_config.ProxyUrl, true, new string[0], _config.ProxyCredentials); | |||
@@ -82,13 +82,13 @@ namespace Discord.Net.WebSockets | |||
JToken token = msg.Payload as JToken; | |||
if (msg.Type == "READY") | |||
{ | |||
var payload = token.ToObject<ReadyEvent>(); | |||
var payload = token.ToObject<ReadyEvent>(_client.DataSocketSerializer); | |||
_sessionId = payload.SessionId; | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
} | |||
else if (msg.Type == "RESUMED") | |||
{ | |||
var payload = token.ToObject<ResumedEvent>(); | |||
var payload = token.ToObject<ResumedEvent>(_client.DataSocketSerializer); | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
} | |||
RaiseReceivedEvent(msg.Type, token); | |||
@@ -98,7 +98,7 @@ namespace Discord.Net.WebSockets | |||
break; | |||
case 7: //Redirect | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<RedirectEvent>(); | |||
var payload = (msg.Payload as JToken).ToObject<RedirectEvent>(_client.DataSocketSerializer); | |||
if (payload.Url != null) | |||
{ | |||
Host = payload.Url; | |||
@@ -443,7 +443,7 @@ namespace Discord.Net.WebSockets | |||
{ | |||
if (_state != (int)WebSocketState.Connected) | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(); | |||
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_client.VoiceSocketSerializer); | |||
_heartbeatInterval = payload.HeartbeatInterval; | |||
_ssrc = payload.SSRC; | |||
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port); | |||
@@ -486,7 +486,7 @@ namespace Discord.Net.WebSockets | |||
break; | |||
case 4: //SESSION_DESCRIPTION | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<JoinServerEvent>(); | |||
var payload = (msg.Payload as JToken).ToObject<JoinServerEvent>(_client.VoiceSocketSerializer); | |||
_secretKey = payload.SecretKey; | |||
SendIsTalking(true); | |||
EndConnect(); | |||
@@ -494,7 +494,7 @@ namespace Discord.Net.WebSockets | |||
break; | |||
case 5: | |||
{ | |||
var payload = (msg.Payload as JToken).ToObject<IsTalkingEvent>(); | |||
var payload = (msg.Payload as JToken).ToObject<IsTalkingEvent>(_client.VoiceSocketSerializer); | |||
RaiseIsSpeaking(payload.UserId, payload.IsSpeaking); | |||
} | |||
break; | |||