public DefaultGatewayClient( IOptions <DefaultGatewayClientConfiguration> options, ILogger <DefaultGatewayClient> logger, IGatewayCacheProvider cacheProvider, IGatewayChunker chunker, IGatewayDispatcher dispatcher, IGatewayApiClient apiClient) { Logger = logger; CacheProvider = cacheProvider; CacheProvider.Bind(this); Chunker = chunker; Chunker.Bind(this); Dispatcher = dispatcher; if (apiClient != null) { _apiClient = apiClient; Shards = new Dictionary <ShardId, IGatewayApiClient>(1) { [new ShardId(0, 1)] = apiClient }.ReadOnly(); apiClient.DispatchReceived += Dispatcher.HandleDispatchAsync; } else { Shards = new SynchronizedDictionary <ShardId, IGatewayApiClient>(); } Dispatcher.Bind(this); }
public override ValueTask <BanDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildBanRemoveJsonModel model) { var user = Dispatcher.GetSharedUserTransient(model.User); var e = new BanDeletedEventArgs(model.GuildId, user); return(new(e)); }
public override ValueTask <IntegrationCreatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, IntegrationCreateJsonModel model) { var integration = new TransientIntegration(Client, model.GuildId, model); var e = new IntegrationCreatedEventArgs(integration); return(new(e)); }
public override ValueTask <ThreadCreatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, ChannelJsonModel model) { IThreadChannel thread; if (CacheProvider.TryGetChannels(model.GuildId.Value, out var channelCache)) { thread = channelCache.AddOrUpdate(model.Id, (_, tuple) => { var(client, model) = tuple; return(new CachedThreadChannel(client, model)); }, (_, tuple, oldThread) => { var(_, model) = tuple; oldThread.Update(model); return(oldThread); }, (Client, model)) as IThreadChannel; } else { thread = new TransientThreadChannel(Client, model); } var e = new ThreadCreatedEventArgs(thread); return(new(e)); }
public override ValueTask <MessagesDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, MessageDeleteBulkJsonModel model) { if (!model.GuildId.HasValue) { return(new(result : null)); } var messages = new Dictionary <Snowflake, CachedUserMessage>(); if (model.GuildId.HasValue && CacheProvider.TryGetMessages(model.ChannelId, out var cache)) { for (var i = 0; i < model.Ids.Length; i++) { var id = model.Ids[i]; if (cache.TryRemove(id, out var message)) { messages.Add(id, message); } } } var e = new MessagesDeletedEventArgs(model.GuildId.Value, model.ChannelId, model.Ids, messages); return(new(e)); }
public override ValueTask <ChannelDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, ChannelJsonModel model) { if (!model.GuildId.HasValue) { return(new(result : null)); } IGuildChannel channel; if (CacheProvider.TryGetChannels(model.GuildId.Value, out var cache) && cache.TryRemove(model.Id, out var cachedChannel)) { channel = cachedChannel; } else { channel = TransientGuildChannel.Create(Client, model); } // TODO: Pass removed messages to e? CacheProvider.TryRemoveCache <CachedUserMessage>(model.Id, out _); var e = new ChannelDeletedEventArgs(channel); return(new(e)); }
public void Bind(IGatewayApiClient apiClient) { _binder.Bind(apiClient); // TODO: identify concurrency _buckets[GatewayPayloadOperation.Identify] = GetSharedBucket(_loggerFactory.CreateLogger("Identify Bucket"), ApiClient.Token as BotToken, GatewayPayloadOperation.Identify, 1, TimeSpan.FromSeconds(5.5)); _buckets[GatewayPayloadOperation.UpdatePresence] = new Bucket(_loggerFactory.CreateLogger("Presence Bucket"), 5, TimeSpan.FromSeconds(60)); }
public override ValueTask <ThreadsSynchronizedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, ThreadListSyncJsonModel model) { var threadModelDictionary = new Dictionary <Snowflake, List <ChannelJsonModel> >(); if (model.ChannelIds.HasValue) { threadModelDictionary = model.ChannelIds.Value.ToDictionary(x => x, _ => new List <ChannelJsonModel>()); } foreach (var threadModel in model.Threads) { var memberModel = Array.Find(model.Members, x => x.Id == threadModel.Id); if (memberModel != null) { threadModel.Member = memberModel; } if (!model.ChannelIds.HasValue || !threadModelDictionary.TryGetValue(threadModel.ParentId.Value.Value, out var threadModels)) { threadModelDictionary.Add(threadModel.ParentId.Value.Value, threadModels = new List <ChannelJsonModel>()); } threadModels.Add(threadModel); } var threads = threadModelDictionary.ToDictionary(x => x.Key, x => new List <IThreadChannel>(x.Value.Count) as IReadOnlyList <IThreadChannel>); var uncachedThreads = new Dictionary <Snowflake, IReadOnlyList <CachedThreadChannel> >();
public override ValueTask <MemberUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildMemberUpdateJsonModel model) { CachedMember oldMember = null; IMember newMember = null; if (CacheProvider.TryGetMembers(model.GuildId, out var memberCache)) { if (memberCache.TryGetValue(model.User.Value.Id, out var member)) { newMember = member; var oldUser = member.SharedUser.Clone() as CachedSharedUser; oldMember = member.Clone() as CachedMember; oldMember.SharedUser = oldUser; newMember.Update(model); } else if (CacheProvider.TryGetUsers(out var userCache)) { newMember = Dispatcher.GetOrAddMember(userCache, memberCache, model.GuildId, model); } } newMember ??= new TransientMember(Client, model.GuildId, model); var e = new MemberUpdatedEventArgs(oldMember, newMember); return(new(e)); }
public override ValueTask <ChannelUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, ChannelJsonModel model) { if (!model.GuildId.HasValue) { return(new(result : null)); } CachedGuildChannel oldChannel; IGuildChannel newChannel; if (CacheProvider.TryGetChannels(model.GuildId.Value, out var cache) && cache.TryGetValue(model.Id, out var channel)) { newChannel = channel; oldChannel = channel.Clone() as CachedGuildChannel; newChannel.Update(model); } else { oldChannel = null; newChannel = TransientGuildChannel.Create(Client, model); } var e = new ChannelUpdatedEventArgs(oldChannel, newChannel); return(new(e)); }
public override ValueTask <ReactionAddedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, MessageReactionAddJsonModel model) { CachedUserMessage message; IMember member = null; if (CacheProvider.TryGetMessages(model.ChannelId, out var messageCache)) { message = messageCache.GetValueOrDefault(model.MessageId); message?.Update(model); } else { message = null; } if (model.GuildId.HasValue) { member = Dispatcher.GetOrAddMember(model.GuildId.Value, model.Member.Value); if (member == null) { member = new TransientMember(Client, model.GuildId.Value, model.Member.Value); } } var e = new ReactionAddedEventArgs(model.UserId, model.ChannelId, model.MessageId, message, model.GuildId.GetValueOrNullable(), member, Emoji.Create(model.Emoji)); return(new(e)); }
public override async ValueTask <EventArgs> HandleDispatchAsync(IGatewayApiClient shard, UnavailableGuildJsonModel model) { CachedGuild guild = null; var isPending = _readyHandler.IsPendingGuild(shard.Id, model.Id); if (model.Unavailable.HasValue || isPending) // Note: apparently `model.Unavailable` is provided for pending GUILD_CREATEs but not GUILD_DELETEs. { try { if (CacheProvider.TryGetGuilds(out var cache)) { guild = cache.GetValueOrDefault(model.Id); guild?.Update(model); } if (isPending) { shard.Logger.LogInformation("Pending guild {0} is unavailable.", model.Id.RawValue); } else { if (guild != null) { shard.Logger.LogInformation("Guild {0} ({1}) became unavailable.", guild.Name, guild.Id.RawValue); } else { shard.Logger.LogInformation("Uncached guild {0} became unavailable.", model.Id.RawValue); } } // Invoke the event and possibly invoke ready afterwards. await InvokeEventAsync(new GuildUnavailableEventArgs(model.Id, guild)).ConfigureAwait(false); } finally { if (isPending) { _readyHandler.PopPendingGuild(shard.Id, model.Id); } } return(null); } CacheProvider.Reset(model.Id, out guild); if (guild != null) { shard.Logger.LogInformation("Left guild '{0}' ({1}).", guild.Name, guild.Id.RawValue); } else { shard.Logger.LogInformation("Left uncached guild {0}.", model.Id.RawValue); } return(new LeftGuildEventArgs(model.Id, guild)); }
public override ValueTask <InviteCreatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, InviteCreateJsonModel model) { var inviter = Optional.ConvertOrDefault(model.Inviter, x => new TransientUser(Client, x)) as IUser; var targetUser = Optional.ConvertOrDefault(model.TargetUser, x => new TransientUser(Client, x)) as IUser; var targetApplication = Optional.ConvertOrDefault(model.TargetApplication, x => new TransientApplication(Client, x)) as IApplication; var e = new InviteCreatedEventArgs(model.GuildId.GetValueOrNullable(), model.ChannelId, model.Code, model.CreatedAt, inviter, model.MaxAge, model.MaxUses, model.TargetType.GetValueOrNullable(), targetUser, targetApplication, model.Temporary, model.Uses); return(new(e)); }
public override ValueTask <CurrentUserUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, UserJsonModel model) { var newCurrentUser = _readyHandler.CurrentUser; var oldCurrentUser = newCurrentUser.Clone() as CachedCurrentUser; newCurrentUser.Update(model); var e = new CurrentUserUpdatedEventArgs(oldCurrentUser, newCurrentUser); return(new(e)); }
public override ValueTask <MemberJoinedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildMemberAddJsonModel model) { var guild = Client.GetGuild(model.GuildId); if (_lastMemberIds != null) { if (_lastMemberIds.TryGetValue(model.GuildId, out var lastMemberId) && lastMemberId == model.User.Value.Id) { // If the event is a duplicate, we don't handle it nor trigger event handlers. return(default);
public override ValueTask <GuildUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildJsonModel model) { CachedGuild oldGuild; IGuild newGuild; if (CacheProvider.TryGetGuilds(out var cache) && cache.TryGetValue(model.Id, out var guild)) { newGuild = guild; oldGuild = guild.Clone() as CachedGuild; newGuild.Update(model); }
public override ValueTask <StickersUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildStickersUpdateJsonModel model) { IReadOnlyDictionary <Snowflake, IGuildSticker> oldStickers; IReadOnlyDictionary <Snowflake, IGuildSticker> newStickers; if (CacheProvider.TryGetGuilds(out var cache) && cache.TryGetValue(model.GuildId, out var guild)) { oldStickers = guild.Stickers; guild.Update(model); newStickers = guild.Stickers; }
public override ValueTask <MessageReceivedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, MessageJsonModel model) { CachedMember author = null; IGatewayMessage message = null; if (model.GuildId.HasValue && !model.WebhookId.HasValue && model.Member.HasValue && Client.CacheProvider.TryGetUsers(out var userCache) && Client.CacheProvider.TryGetMembers(model.GuildId.Value, out var memberCache)) { model.Member.Value.User = model.Author; author = Dispatcher.GetOrAddMember(userCache, memberCache, model.GuildId.Value, model.Member.Value); foreach (var memberModel in model.Mentions.Select(static x =>
public override async ValueTask HandleDispatchAsync(IGatewayApiClient shard, IJsonNode data) { var model = data.ToType <TModel>(); var eventArgs = await HandleDispatchAsync(shard, model).ConfigureAwait(false); if (eventArgs == null || eventArgs == EventArgs.Empty) { return; } await InvokeEventAsync(eventArgs).ConfigureAwait(false); }
public override ValueTask <TypingStartedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, TypingStartJsonModel model) { CachedMember member = null; if (model.GuildId.HasValue) { member = Dispatcher.GetOrAddMember(model.GuildId.Value, model.Member.Value); } var e = new TypingStartedEventArgs(model.GuildId.GetValueOrNullable(), model.ChannelId, model.UserId, DateTimeOffset.FromUnixTimeSeconds(model.Timestamp), member); return(new(e)); }
public override ValueTask <RoleDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildRoleDeleteJsonModel model) { CachedRole role = null; if (CacheProvider.TryGetRoles(model.GuildId, out var cache)) { cache.TryRemove(model.RoleId, out role); } var e = new RoleDeletedEventArgs(model.GuildId, model.RoleId, role); return(new(e)); }
public override ValueTask <MessageDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, MessageDeleteJsonModel model) { CachedUserMessage message = null; if (model.GuildId.HasValue && CacheProvider.TryGetMessages(model.ChannelId, out var cache)) { cache.TryRemove(model.Id, out message); } var e = new MessageDeletedEventArgs(model.GuildId.GetValueOrNullable(), model.ChannelId, model.Id, message); return(new(e)); }
public override ValueTask <InteractionReceivedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, InteractionJsonModel model) { CachedMember member = null; if (model.GuildId.HasValue) { member = Dispatcher.GetOrAddMember(model.GuildId.Value, model.Member.Value); } var interaction = TransientInteraction.Create(Client, model); var e = new InteractionReceivedEventArgs(interaction, member); return(new(e)); }
public override ValueTask <VoiceStateUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, VoiceStateJsonModel model) { if (!model.GuildId.HasValue) { return(new(result : null)); } CachedVoiceState oldVoiceState = null; IVoiceState newVoiceState = null; if (CacheProvider.TryGetVoiceStates(model.GuildId.Value, out var cache)) { if (model.ChannelId != null) { if (cache.TryGetValue(model.UserId, out var voiceState)) { newVoiceState = voiceState; oldVoiceState = voiceState.Clone() as CachedVoiceState; newVoiceState.Update(model); } else { newVoiceState = new CachedVoiceState(Client, model.GuildId.Value, model); cache.Add(model.UserId, newVoiceState as CachedVoiceState); } } else { cache.TryRemove(model.UserId, out oldVoiceState); } } newVoiceState ??= new TransientVoiceState(Client, model); var isLurker = false; if (model.Member.Value.TryGetValue("joined_at", out var joinedAt) && joinedAt is IJsonValue jsonValue && jsonValue.Value == null) { isLurker = true; jsonValue.Value = DateTimeOffset.UtcNow; } var memberModel = model.Member.Value.ToType <MemberJsonModel>(); var member = Dispatcher.GetOrAddMember(model.GuildId.Value, memberModel) ?? new TransientMember(Client, model.GuildId.Value, memberModel) as IMember; var e = new VoiceStateUpdatedEventArgs(member, isLurker, oldVoiceState, newVoiceState); return(new(e)); }
public override ValueTask <MessageReceivedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, MessageJsonModel model) { CachedMember author = null; IGatewayMessage message = null; if (model.GuildId.HasValue && !model.WebhookId.HasValue && model.Member.HasValue && Client.CacheProvider.TryGetUsers(out var userCache) && Client.CacheProvider.TryGetMembers(model.GuildId.Value, out var memberCache)) { model.Member.Value.User = model.Author; author = Dispatcher.GetOrAddMember(userCache, memberCache, model.GuildId.Value, model.Member.Value); foreach (var userModel in model.Mentions) { var memberModel = userModel["member"]?.ToType <MemberJsonModel>(); if (memberModel == null) { continue; } memberModel.User = userModel; Dispatcher.GetOrAddMember(userCache, memberCache, model.GuildId.Value, memberModel); } if (CacheProvider.TryGetMessages(model.ChannelId, out var messageCache) && model.Type is UserMessageType.Default or UserMessageType.Reply or UserMessageType.SlashCommand or UserMessageType.ThreadStarterMessage or UserMessageType.ContextMenuCommand) { message = new CachedUserMessage(Client, author, model); messageCache.Add(model.Id, message as CachedUserMessage); } } message ??= TransientGatewayMessage.Create(Client, model); CachedMessageGuildChannel channel = null; if (model.GuildId.HasValue && CacheProvider.TryGetChannels(model.GuildId.Value, out var channelCache)) { channel = channelCache.GetValueOrDefault(model.ChannelId) as CachedMessageGuildChannel; if (channel != null) { channel.LastMessageId = model.Id; } } var e = new MessageReceivedEventArgs(message, channel, author); return(new(e)); }
public override ValueTask <GuildEventDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildScheduledEventJsonModel model) { IGuildEvent @event; if (CacheProvider.TryGetGuildEvents(model.GuildId, out var cache) && cache.TryRemove(model.Id, out var cachedEvent)) { @event = cachedEvent; } else { @event = new TransientGuildEvent(Client, model); } var e = new GuildEventDeletedEventArgs(@event); return(new(e)); }
public override ValueTask <StageDeletedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, StageInstanceJsonModel model) { IStage stage; if (CacheProvider.TryGetStages(model.GuildId, out var cache) && cache.TryRemove(model.Id, out var cachedStage)) { stage = cachedStage; } else { stage = new TransientStage(Client, model); } var e = new StageDeletedEventArgs(stage); return(new(e)); }
public override ValueTask <MemberLeftEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildMemberRemoveJsonModel model) { IUser user; if (CacheProvider.TryGetMembers(model.GuildId, out var cache) && cache.TryRemove(model.User.Id, out var cachedMember)) { user = cachedMember; } else { user = new TransientUser(Client, model.User); } var e = new MemberLeftEventArgs(model.GuildId, user); return(new(e)); }
public override ValueTask <MemberJoinedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, GuildMemberAddJsonModel model) { IMember member = null; var sharedUser = Dispatcher.GetOrAddSharedUser(model.User.Value); if (sharedUser != null && CacheProvider.TryGetMembers(model.GuildId, out var cache)) { member = new CachedMember(sharedUser, model.GuildId, model); cache.Add(member.Id, member as CachedMember); } member ??= new TransientMember(Client, model.GuildId, model); var e = new MemberJoinedEventArgs(member); return(new(e)); }
public override ValueTask <ChannelPinsUpdatedEventArgs> HandleDispatchAsync(IGatewayApiClient shard, ChannelPinsUpdateJsonModel model) { CachedMessageGuildChannel channel = null; if (model.GuildId.HasValue) { if (CacheProvider.TryGetChannels(model.GuildId.Value, out var cache)) { channel = cache.GetValueOrDefault(model.ChannelId) as CachedMessageGuildChannel; channel?.Update(model); } } var e = new ChannelPinsUpdatedEventArgs(model.GuildId.GetValueOrNullable(), model.ChannelId, channel); return(new(e)); }