@@ -41,6 +41,9 @@ namespace Discord
private readonly ConcurrentQueue<ulong> _largeGuilds;
private readonly Logger _gatewayLogger;
#if BENCHMARK
private readonly Logger _benchmarkLogger;
#endif
private readonly DataStoreProvider _dataStoreProvider;
private readonly JsonSerializer _serializer;
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
@@ -106,7 +109,10 @@ namespace Discord
_largeThreshold = config.LargeThreshold;
_gatewayLogger = _log.CreateLogger("Gateway");
#if BENCHMARK
_benchmarkLogger = _log.CreateLogger("Benchmark");
#endif
_serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.Debug($"Sent {(GatewayOpCode)opCode}");
@@ -207,7 +213,7 @@ namespace Discord
{
dataStore = dataStore ?? DataStore;
var guild = new CachedGuild(this, model);
var guild = new CachedGuild(this, model, dataStore );
if (model.Unavailable != true)
{
for (int i = 0; i < model.Channels.Length; i++)
@@ -247,7 +253,7 @@ namespace Discord
{
dataStore = dataStore ?? DataStore;
var recipient = AddCachedUser(model.Recipient, dataStore);
var recipient = GetOr AddCachedUser(model.Recipient, dataStore);
var channel = recipient.AddDMChannel(model);
dataStore.AddChannel(channel);
return channel;
@@ -287,7 +293,7 @@ namespace Discord
{
return Task.FromResult<IUser>(DataStore.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault());
}
internal CachedPublicUser AddCachedUser(API.User model, DataStore dataStore = null)
internal CachedPublicUser GetOr AddCachedUser(API.User model, DataStore dataStore = null)
{
dataStore = dataStore ?? DataStore;
@@ -299,8 +305,7 @@ namespace Discord
{
dataStore = dataStore ?? DataStore;
var user = dataStore.GetUser(id);
user.RemoveRef();
var user = dataStore.RemoveUser(id);
return user;
}
@@ -336,7 +341,7 @@ namespace Discord
batchTasks[j] = guild.DownloaderPromise;
}
ApiClient.SendRequestMembers(batchIds);
await ApiClient.SendRequestMembers(batchIds).ConfigureAwait(false );
if (isLast && batchCount > 1)
await Task.WhenAll(batchTasks.Take(count)).ConfigureAwait(false);
@@ -347,474 +352,511 @@ namespace Discord
private async Task ProcessMessage(GatewayOpCode opCode, int? seq, string type, object payload)
{
if (seq != null)
_lastSeq = seq.Value ;
#if BENCHMARK
Stopwatch stopwatch = Stopwatch.StartNew() ;
try
{
switch (opCode)
#endif
if (seq != null)
_lastSeq = seq.Value;
try
{
case GatewayOpCode.Hello:
{
var data = (payload as JToken).ToObject<HelloEvent>(_serializer);
await ApiClient.SendIdentify().ConfigureAwait(false);
_heartbeatTask = RunHeartbeat(data.HeartbeatInterval, _heartbeatCancelToken.Token);
}
break;
case GatewayOpCode.HeartbeatAck:
{
var latency = (int)(Environment.TickCount - _heartbeatTime);
await _gatewayLogger.Debug($"Latency: {latency} ms").ConfigureAwait(false);
Latency = latency;
await LatencyUpdated.Raise(latency).ConfigureAwait(false);
}
break;
case GatewayOpCode.Dispatch:
switch (type)
{
//Global
case "READY":
{
//TODO: Make downloading large guilds optional
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
var dataStore = _dataStoreProvider(ShardId, _totalShards, data.Guilds.Length, data.PrivateChannels.Length);
_currentUser = new CachedSelfUser(this, data.User);
for (int i = 0; i < data.Guilds.Length; i++)
AddCachedGuild(data.Guilds[i], dataStore);
for (int i = 0; i < data.PrivateChannels.Length; i++)
AddCachedDMChannel(data.PrivateChannels[i], dataStore);
_sessionId = data.SessionId;
DataStore = dataStore;
await Ready.Raise().ConfigureAwait(false);
_connectTask.TrySetResult(true); //Signal the .Connect() call to complete
}
break;
//Guilds
case "GUILD_CREATE":
{
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
var guild = new CachedGuild(this, data);
DataStore.AddGuild(guild);
if (data.Unavailable == false)
type = "GUILD_AVAILABLE";
else
await JoinedGuild.Raise(guild).ConfigureAwait(false);
await GuildAvailable.Raise(guild);
}
break;
case "GUILD_UPDATE":
{
var data = (payload as JToken).ToObject<API.Guild>(_serializer);
var guild = DataStore.GetGuild(data.Id);
if (guild != null)
switch (opCode)
{
case GatewayOpCode.Hello:
{
await _gatewayLogger.Debug($"Received Hello").ConfigureAwait(false);
var data = (payload as JToken).ToObject<HelloEvent>(_serializer);
await ApiClient.SendIdentify().ConfigureAwait(false);
_heartbeatTask = RunHeartbeat(data.HeartbeatInterval, _heartbeatCancelToken.Token);
}
break;
case GatewayOpCode.HeartbeatAck:
{
await _gatewayLogger.Debug($"Received HeartbeatAck").ConfigureAwait(false);
var latency = (int)(Environment.TickCount - _heartbeatTime);
await _gatewayLogger.Debug($"Latency = {latency} ms").ConfigureAwait(false);
Latency = latency;
await LatencyUpdated.Raise(latency).ConfigureAwait(false);
}
break;
case GatewayOpCode.Dispatch:
switch (type)
{
//Global
case "READY":
{
var before = _enablePreUpdateEvents ? guild.Clone() : null;
guild.Update(data, UpdateSource.WebSocket);
await GuildUpdated.Raise(before, guild);
await _gatewayLogger.Debug($"Received Dispatch (READY)").ConfigureAwait(false);
//TODO: Make downloading large guilds optional
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
var dataStore = _dataStoreProvider(ShardId, _totalShards, data.Guilds.Length, data.PrivateChannels.Length);
_currentUser = new CachedSelfUser(this, data.User);
for (int i = 0; i < data.Guilds.Length; i++)
AddCachedGuild(data.Guilds[i], dataStore);
for (int i = 0; i < data.PrivateChannels.Length; i++)
AddCachedDMChannel(data.PrivateChannels[i], dataStore);
_sessionId = data.SessionId;
DataStore = dataStore;
await Ready.Raise().ConfigureAwait(false);
_connectTask.TrySetResult(true); //Signal the .Connect() call to complete
}
else
await _gatewayLogger.Warning("GUILD_UPDATE referenced an unknown guild.");
}
break;
case "GUILD_DELETE":
{
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
var guild = DataStore.RemoveGuild(data.Id);
if (guild != null)
break;
//Guilds
case "GUILD_CREATE":
{
if (data.Unavailable == true)
type = "GUILD_UNAVAILABLE";
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
var guild = new CachedGuild(this, data, DataStore);
DataStore.AddGuild(guild);
if (data.Unavailable == false)
type = "GUILD_AVAILABLE";
await _gatewayLogger.Debug($"Received Dispatch ({type})").ConfigureAwait(false);
await GuildUnavailable.Raise(guild);
if (data.Unavailable != true)
await LeftGuild.Raise(guild);
if (data.Unavailable != false)
await JoinedGuild.Raise(guild).ConfigureAwait(false);
await GuildAvailable.Raise(guild).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("GUILD_DELETE referenced an unknown guild.");
}
break;
//Channels
case "CHANNEL_CREATE":
{
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
ICachedChannel channel = null;
if (data.GuildId != null)
break;
case "GUILD_UPDATE":
{
var guild = DataStore.GetGuild(data.GuildId.Value);
await _gatewayLogger.Debug($"Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Guild>(_serializer);
var guild = DataStore.GetGuild(data.Id);
if (guild != null)
{
channel = guild.AddCachedChannel(data);
DataStore.AddChannel(channel);
var before = _enablePreUpdateEvents ? guild.Clone() : null;
guild.Update(data, UpdateSource.WebSocket);
await GuildUpdated.Raise(before, guild).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("CHANNEL_CRE ATE referenced an unknown guild.");
await _gatewayLogger.Warning("GUILD_UPD ATE referenced an unknown guild.");
}
else
channel = AddCachedDMChannel(data);
if (channel != null)
await ChannelCreated.Raise(channel);
}
break;
case "CHANNEL_UPDATE":
{
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
var channel = DataStore.GetChannel(data.Id);
if (channel != null)
break;
case "GUILD_DELETE":
{
var before = _enablePreUpdateEvents ? channel.Clone() : null;
channel.Update(data, UpdateSource.WebSocket);
await ChannelUpdated.Raise(before, channel);
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
if (data.Unavailable == true)
type = "GUILD_UNAVAILABLE";
await _gatewayLogger.Debug($"Received Dispatch ({type})").ConfigureAwait(false);
var guild = DataStore.RemoveGuild(data.Id);
if (guild != null)
{
await GuildUnavailable.Raise(guild).ConfigureAwait(false);
if (data.Unavailable != true)
await LeftGuild.Raise(guild).ConfigureAwait(false);
foreach (var member in guild.Members)
member.User.RemoveRef();
}
else
await _gatewayLogger.Warning($"{type} referenced an unknown guild.").ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("CHANNEL_UPDATE referenced an unknown channel.");
}
break;
case "CHANNEL_DELETE":
{
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
var channel = RemoveCachedChannel(data.Id);
if (channel != null)
await ChannelDestroyed.Raise(channel);
else
await _gatewayLogger.Warning("CHANNEL_DELETE referenced an unknown channel.");
}
break;
//Members
/*case "GUILD_MEMBER_ADD":
{
var data = (payload as JToken).ToObject<API.GuildMember>(_serializer);
var guild = GetGuild(data.GuildId.Value);
if (guild != null)
break;
//Channels
case "CHANNEL_CREATE":
{
var user = guild.AddCachedUser(data.User.Id, true, true);
user.Update(data);
user.UpdateActivity();
UserJoined.Raise(user);
await _gatewayLogger.Debug($"Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
ICachedChannel channel = null;
if (data.GuildId != null)
{
var guild = DataStore.GetGuild(data.GuildId.Value);
if (guild != null)
{
channel = guild.AddCachedChannel(data);
DataStore.AddChannel(channel);
}
else
await _gatewayLogger.Warning("CHANNEL_CREATE referenced an unknown guild.");
}
else
channel = AddCachedDMChannel(data);
if (channel != null)
await ChannelCreated.Raise(channel);
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_ADD referenced an unknown guild.");
}
break;
case "GUILD_MEMBER_UPDATE":
{
var data = (payload as JToken).ToObject<API.GuildMember>(_serializer);
var guild = GetGuild(data.GuildId.Value);
if (guild != null)
break;
case "CHANNEL_UPDATE":
{
var user = guild.GetCachedUser(data.User.Id);
if (user != null)
await _gatewayLogger.Debug($"Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
var channel = DataStore.GetChannel(data.Id);
if (channel != null)
{
var before = _enablePreUpdateEvents ? user.Clone() : null;
user.Update(data);
await UserUpdated.Raise(before, user );
var before = _enablePreUpdateEvents ? channel .Clone() : null;
channel.Update(data, UpdateSource.WebSocket );
await ChannelUpdated.Raise(before, channel );
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_UPDATE referenced an unknown user.");
await _gatewayLogger.Warning("CHANNEL_UPDATE referenced an unknown channel.");
}
break;
case "CHANNEL_DELETE":
{
await _gatewayLogger.Debug($"Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
var channel = RemoveCachedChannel(data.Id);
if (channel != null)
await ChannelDestroyed.Raise(channel);
else
await _gatewayLogger.Warning("CHANNEL_DELETE referenced an unknown channel.");
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_UPDATE referenced an unknown guild.");
}
break;
case "GUILD_MEMBER_REMOVE":
{
var data = (payload as JToken).ToObject<API.GuildMember>(_serializer);
var guild = GetGuild(data.GuildId.Value);
if (guild != null)
break;
//Members
case "GUILD_MEMBER_ADD":
{
var user = guild.RemoveCachedUser(data.User.Id);
if (user != null)
await _gatewayLogger.Debug($"Received Dispatch (GUILD_MEMBER_ADD)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildMemberAddEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
user.GlobalUser.RemoveGuild();
if (user.GuildCount == 0 && user.DMChannel == null)
DataStore.RemoveUser(user.Id);
await UserLeft.Raise(user);
var user = guild.AddCachedUser(data);
await UserJoined.Raise(user).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_REMOVE referenced an unknown user .");
await _gatewayLogger.Warning("GUILD_MEMBER_ADD referenced an unknown guild .");
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_REMOVE referenced an unknown guild.");
}
break;
case "GUILD_MEMBERS_CHUNK":
{
var data = (payload as JToken).ToObject<GuildMembersChunkEvent>(_serializer);
var guild = GetCachedGuild(data.GuildId);
if (guild != null)
break;
case "GUILD_MEMBER_UPDATE":
{
foreach (var memberData in data.Members)
await _gatewayLogger.Debug($"Received Dispatch (GUILD_MEMBER_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildMemberUpdateEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var user = guild.AddCachedUser(memberData.User.Id, true, false);
user.Update(memberData);
var user = guild.GetCachedUser(data.User.Id);
if (user != null)
{
var before = _enablePreUpdateEvents ? user.Clone() : null;
user.Update(data, UpdateSource.WebSocket);
await UserUpdated.Raise(before, user);
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_UPDATE referenced an unknown user.");
}
if (guild.CurrentUserCount >= guild.UserCount) //Finished downloading for there
await GuildAvailable.Raise(guild);
else
await _gatewayLogger.Warning("GUILD_MEMBER_UPDATE referenced an unknown guild.");
}
else
await _gatewayLogger.Warning("GUILD_MEMBERS_CHUNK referenced an unknown guild.");
}
break;
//Roles
/*case "GUILD_ROLE_CREATE":
{
var data = (payload as JToken).ToObject<GuildRoleCreateEvent>(_serializer);
var guild = GetCachedGuild(data.GuildId);
if (guild != null)
break;
case "GUILD_MEMBER_REMOVE":
{
var role = guild.AddCachedRole(data.Data.Id);
role.Update(data.Data, false);
RoleCreated.Raise(role);
await _gatewayLogger.Debug($"Received Dispatch (GUILD_MEMBER_REMOVE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildMemberRemoveEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var user = guild.RemoveCachedUser(data.User.Id);
if (user != null)
{
user.User.RemoveRef();
await UserLeft.Raise(user);
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_REMOVE referenced an unknown user.");
}
else
await _gatewayLogger.Warning("GUILD_MEMBER_REMOVE referenced an unknown guild.");
}
else
await _gatewayLogger.Warning("GUILD_ROLE_CREATE referenced an unknown guild.");
}
break;
case "GUILD_ROLE_UPDATE":
{
var data = (payload as JToken).ToObject<GuildRoleUpdateEvent>(_serializer);
var guild = GetCachedGuild(data.GuildId);
if (guild != null)
break;
case "GUILD_MEMBERS_CHUNK":
{
var role = guild.GetRole(data.Data.Id);
if (role != null)
await _gatewayLogger.Debug($"Received Dispatch (GUILD_MEMBERS_CHUNK)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildMembersChunkEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var before = _enablePreUpdateEvents ? role.Clone() : null;
role.Update(data.Data, true);
RoleUpdated.Raise(before, role);
foreach (var memberModel in data.Members)
guild.AddCachedUser(memberModel);
if (guild.DownloadedMemberCount >= guild.MemberCount) //Finished downloading for there
{
guild.CompleteDownloadMembers();
await GuildDownloadedMembers.Raise(guild).ConfigureAwait(false);
}
}
else
await _gatewayLogger.Warning("GUILD_ROLE_UPDATE referenced an unknown role.");
await _gatewayLogger.Warning("GUILD_MEMBERS_CHUNK referenced an unknown guild .");
}
else
await _gatewayLogger.Warning("GUILD_ROLE_UPDATE referenced an unknown guild.");
}
break;
case "GUILD_ROLE_DELETE":
{
var data = (payload as JToken).ToObject<GuildRoleDeleteEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
break;
//Roles
case "GUILD_ROLE_CREATE":
{
var role = guild.RemoveRole(data.RoleId);
if (role != null)
RoleDeleted.Raise(role);
await _gatewayLogger.Debug($"Received Dispatch (GUILD_ROLE_CREATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildRoleCreateEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var role = guild.AddCachedRole(data.Role);
await RoleCreated.Raise(role).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("GUILD_ROLE_DELETE referenced an unknown role.");
await _gatewayLogger.Warning("GUILD_ROLE_CREATE referenced an unknown guild .");
}
else
await _gatewayLogger.Warning("GUILD_ROLE_DELETE referenced an unknown guild.");
}
break;
//Bans
case "GUILD_BAN_ADD":
{
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = GetCachedGuild(data.GuildId);
if (guild != null)
await UserBanned.Raise(new User(this, data));
else
await _gatewayLogger.Warning("GUILD_BAN_ADD referenced an unknown guild.");
}
break;
case "GUILD_BAN_REMOVE":
{
var data = payload.ToObject<GuildBanEvent>(_serializer);
var guild = GetCachedGuild(data.GuildId);
if (guild != null)
await UserUnbanned.Raise(new User(this, data));
else
await _gatewayLogger.Warning("GUILD_BAN_REMOVE referenced an unknown guild.");
}
break;
//Messages
case "MESSAGE_CREATE":
{
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = DataStore.GetChannel(data.ChannelId);
if (channel != null)
break;
case "GUILD_ROLE_UPDATE":
{
var user = channel.GetUser(data.Author.Id );
await _gatewayLogger.Debug($"Received Dispatch (GUILD_ROLE_UPDATE)").ConfigureAwait(false);
if (user != null)
var data = (payload as JToken).ToObject<GuildRoleUpdateEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
bool isAuthor = data.Author.Id == CurrentUser.Id;
var msg = channel.AddMessage(data.Id, user, data.Timestamp.Value);
msg.Update(data);
MessageReceived.Raise(msg);
var role = guild.GetRole(data.Role.Id);
if (role != null)
{
var before = _enablePreUpdateEvents ? role.Clone() : null;
role.Update(data.Role, UpdateSource.WebSocket);
await RoleUpdated.Raise(before, role).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("GUILD_ROLE_UPDATE referenced an unknown role.");
}
else
await _gatewayLogger.Warning("MESSAGE_CREATE referenced an unknown user.");
await _gatewayLogger.Warning("GUILD_ROLE_UPDATE referenced an unknown guild.");
}
else
await _gatewayLogger.Warning("MESSAGE_CREATE referenced an unknown channel.");
}
break;
case "MESSAGE_UPDATE":
{
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = GetCachedChannel(data.ChannelId);
if (channel != null)
break;
case "GUILD_ROLE_DELETE":
{
var msg = channel.GetMessage(data.Id, data.Author?.Id);
var before = _enablePreUpdateEvents ? msg.Clone() : null;
msg.Update(data);
MessageUpdated.Raise(before, msg);
await _gatewayLogger.Debug($"Received Dispatch (GUILD_ROLE_DELETE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildRoleDeleteEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var role = guild.RemoveCachedRole(data.RoleId);
if (role != null)
await RoleDeleted.Raise(role).ConfigureAwait(false);
else
await _gatewayLogger.Warning("GUILD_ROLE_DELETE referenced an unknown role.");
}
else
await _gatewayLogger.Warning("GUILD_ROLE_DELETE referenced an unknown guild.");
}
else
await _gatewayLogger.Warning("MESSAGE_UPDATE referenced an unknown channel.");
}
break;
case "MESSAGE_DELETE":
{
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = GetCachedChannel(data.ChannelId);
if (channel != null)
break;
//Bans
case "GUILD_BAN_ADD":
{
var msg = channel.RemoveMessage(data.Id);
MessageDeleted.Raise(msg);
await _gatewayLogger.Debug($"Received Dispatch (GUILD_BAN_ADD)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
await UserBanned.Raise(new User(this, data));
else
await _gatewayLogger.Warning("GUILD_BAN_ADD referenced an unknown guild.");
}
else
await _gatewayLogger.Warning("MESSAGE_DELETE referenced an unknown channel.");
}
break;
//Statuses
case "PRESENCE_UPDATE":
{
var data = (payload as JToken).ToObject<API.Presence>(_serializer);
User user;
Guild guild;
if (data.GuildId == null)
break;
case "GUILD_BAN_REMOVE":
{
guild = null;
user = GetPrivateChannel(data.User.Id)?.Recipient;
await _gatewayLogger.Debug($"Received Dispatch (GUILD_BAN_REMOVE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
await UserUnbanned.Raise(new User(this, data));
else
await _gatewayLogger.Warning("GUILD_BAN_REMOVE referenced an unknown guild.");
}
else
break;
//Messages
case "MESSAGE_CREATE":
{
guild = GetGuild(data.GuildId.Value);
if (guild == null)
await _gatewayLogger.Debug($"Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel;
if (channel != null)
{
await _gatewayLogger.Warning("PRESENCE_UPDATE referenced an unknown guild.");
break;
var author = channel.GetCachedUser(data.Author.Id);
if (author != null)
{
var msg = channel.AddCachedMessage(author, data);
await MessageReceived.Raise(msg).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("MESSAGE_CREATE referenced an unknown user.");
}
else
user = guild.GetUser(data.User.Id);
await _gatewayLogger.Warning("MESSAGE_CREATE referenced an unknown channel." );
}
if (user != null)
break;
case "MESSAGE_UPDATE":
{
var before = _enablePreUpdateEvents ? user.Clone() : null;
user.Update(data);
UserUpdated.Raise(before, user);
await _gatewayLogger.Debug($"Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel;
if (channel != null)
{
var msg = channel.GetCachedMessage(data.Id);
var before = _enablePreUpdateEvents ? msg.Clone() : null;
msg.Update(data, UpdateSource.WebSocket);
await MessageUpdated.Raise(before, msg).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("MESSAGE_UPDATE referenced an unknown channel.");
}
else
break;
case "MESSAGE_DELETE":
{
//Occurs when a user leaves a guild
//await _gatewayLogger.Warning("PRESENCE_UPDATE referenced an unknown user.");
await _gatewayLogger.Debug($"Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Message>(_serializer);
var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel;
if (channel != null)
{
var msg = channel.RemoveCachedMessage(data.Id);
await MessageDeleted.Raise(msg).ConfigureAwait(false);
}
else
await _gatewayLogger.Warning("MESSAGE_DELETE referenced an unknown channel.");
}
}
break;
case "TYPING_START":
{
var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer);
var channel = GetCachedChannel(data.ChannelId);
if (channel != null)
break;
//Statuses
case "PRESENCE_UPDATE":
{
var user = channel.GetUser(data.UserId);
if (user != null)
await _gatewayLogger.Debug($"Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.Presence>(_serializer);
if (data.GuildId == null)
{
var user = DataStore.GetUser(data.User.Id);
if (user == null)
user.Update(data, UpdateSource.WebSocket);
}
else
{
await UserIsTyping.Raise(channel, user);
user.UpdateActivity();
var guild = DataStore.GetGuild(data.GuildId.Value);
if (guild == null)
{
await _gatewayLogger.Warning("PRESENCE_UPDATE referenced an unknown guild.");
break;
}
if (data.Status == UserStatus.Offline)
guild.RemoveCachedPresence(data.User.Id);
else
guild.AddOrUpdateCachedPresence(data);
}
}
else
await _gatewayLogger.Warning("TYPING_START referenced an unknown channel.");
}
break;
//Voice
case "VOICE_STATE_UPDATE":
{
var data = (payload as JToken).ToObject<API.VoiceState>(_serializer);
var guild = GetGuild(data.GuildId);
if (guild != null)
break;
case "TYPING_START":
{
var user = guild.GetUser(data.UserId);
if (user != null)
await _gatewayLogger.Debug($"Received Dispatch (TYPING_START)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer);
var channel = DataStore.GetChannel(data.ChannelId) as ICachedMessageChannel;
if (channel != null)
{
var before = _enablePreUpdateEvents ? user.Clone() : null;
user.Update(data);
UserUpdated.Raise(before, user );
var user = channel.GetCachedUser(data.UserId);
if (user != null)
await UserIsTyping.Raise(channel, user).ConfigureAwait(false );
}
else
await _gatewayLogger.Warning("TYPING_START referenced an unknown channel.").ConfigureAwait(false);
}
break;
//Voice
case "VOICE_STATE_UPDATE":
{
await _gatewayLogger.Debug($"Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.VoiceState>(_serializer);
if (data.GuildId.HasValue)
{
//Occurs when a user leaves a guild
//await _gatewayLogger.Warning("VOICE_STATE_UPDATE referenced an unknown user.");
var guild = DataStore.GetGuild(data.GuildId.Value);
if (guild != null)
{
if (data.ChannelId == null)
guild.RemoveCachedVoiceState(data.UserId);
else
guild.AddOrUpdateCachedVoiceState(data);
var user = guild.GetCachedUser(data.UserId);
user.Update(data, UpdateSource.WebSocket);
}
else
await _gatewayLogger.Warning("VOICE_STATE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
}
}
else
await _gatewayLogger.Warning("VOICE_STATE_UPDATE referenced an unknown guild.");
}
break;
//Settings
case "USER_UPDATE":
{
var data = (payload as JToken).ToObject<SelfUser>(_serializer);
if (data.Id == CurrentUser.Id)
break;
//Settings
case "USER_UPDATE":
{
var before = _enablePreUpdateEvents ? CurrentUser.Clone() : null;
CurrentUser.Update(data);
await CurrentUserUpdated.Raise(before, CurrentUser).ConfigureAwait(false);
await _gatewayLogger.Debug($"Received Dispatch (USER_UPDATE)").ConfigureAwait(false);
var data = (payload as JToken).ToObject<API.User>(_serializer);
if (data.Id == CurrentUser.Id)
{
var before = _enablePreUpdateEvents ? CurrentUser.Clone() : null;
CurrentUser.Update(data, UpdateSource.WebSocket);
await CurrentUserUpdated.Raise(before, CurrentUser).ConfigureAwait(false);
}
}
}
break;*/
//Ignored
case "USER_SETTINGS_UPDATE":
case "MESSAGE_ACK": //TODO: Add (User only)
case "GUILD_EMOJI S_UPDATE": //TODO: Add
case "GUILD_INTEGRATIONS _UPDATE": //TODO: Add
case "VOICE_SERVER_UPDATE ": //TODO: Add
case "RESUMED": //TODO: Add
await _gatewayLogger.Debug($"Ignored Dispatch ({type})").ConfigureAwait(false) ;
return;
//Others
default:
await _gatewayLogger.Warning($"Unknown Dispatch ({type})").ConfigureAwait(false) ;
return;
}
break;
default:
await _gatewayLogger.Warning($"Unknown OpCode ({opCode})").ConfigureAwait(false) ;
return;
break;
//Ignored
case "USER_SETTINGS_UPDATE":
case "MESSAGE_ACK": //TODO: Add (User only)
case "GUILD_EMOJIS_UPDATE": //TODO: Add
case "GUILD_INTEGRATION S_UPDATE": //TODO: Add
case "VOICE_SERVER _UPDATE": //TODO: Add
case "RESUMED ": //TODO: Add
await _gatewayLogger.Debug($"Ignored Dispatch ({type})").ConfigureAwait(false);
return ;
//Others
default:
await _gatewayLogger.Warning($"Unknown Dispatch ({type})").ConfigureAwait(false);
return ;
}
break;
default:
await _gatewayLogger.Warning($"Unknown OpCode ({opCode})").ConfigureAwait(false);
return ;
}
}
catch (Exception ex)
{
await _gatewayLogger.Error($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false);
return;
}
#if BENCHMARK
}
catch (Exception ex)
finally
{
await _gatewayLogger.Error($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false);
return;
stopwatch.Stop();
double millis = Math.Round(stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
await _benchmarkLogger.Debug($"{millis} ms").ConfigureAwait(false);
}
await _gatewayLogger.Debug($"Received {opCode}{(type != null ? $" ({type})" : "")}").ConfigureAwait(false);
#endif
}
private async Task RunHeartbeat(int intervalMillis, CancellationToken cancelToken)
{