예제 #1
0
        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);
        }
예제 #2
0
        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));
        }
예제 #3
0
        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));
        }
예제 #4
0
        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));
        }
예제 #5
0
        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));
        }
예제 #6
0
        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));
        }
예제 #7
0
 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));
 }
예제 #8
0
        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> >();
예제 #9
0
        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));
        }
예제 #10
0
        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));
        }
예제 #11
0
        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));
        }
예제 #12
0
        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));
        }
예제 #13
0
        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));
        }
예제 #14
0
        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));
        }
예제 #15
0
        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);
예제 #16
0
        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);
            }
예제 #17
0
        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;
            }
예제 #18
0
        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 =>
예제 #19
0
        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);
        }
예제 #20
0
        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));
        }
예제 #21
0
        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));
        }
예제 #22
0
        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));
        }
예제 #23
0
        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));
        }
예제 #24
0
        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));
        }
예제 #25
0
        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));
        }
예제 #27
0
        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));
        }
예제 #28
0
        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));
        }
예제 #29
0
        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));
        }
예제 #30
0
        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));
        }