Пример #1
0
        public async Task AcquireLockAsyncTest()
        {
            var cacheClient = new StackExchangeCacheClient(
                new ProtobufSerializer(), await ConnectionMultiplexer.ConnectAsync("localhost"));

            var tokenSource = new CancellationTokenSource();

            var testLock = await cacheClient.AcquireLockAsync("test:acquire", tokenSource.Token);

            Assert.Equal("1", await cacheClient.Client.GetDatabase().StringGetAsync("test:acquire"));

            await testLock.ReleaseAsync();

            Assert.Null(await cacheClient.GetAsync <string>("test:acquire"));
        }
Пример #2
0
        public async Task AcquireLockAsyncTwiceThrowsTimeoutTest()
        {
            var cacheClient = new StackExchangeCacheClient(
                new ProtobufSerializer(),
                await ConnectionMultiplexer.ConnectAsync("localhost"));

            var tokenSource = new CancellationTokenSource();

            var testLock = await cacheClient.AcquireLockAsync(
                "test:redis:acquire:multiple", tokenSource.Token);

            tokenSource.CancelAfter(1000);
            await Assert.ThrowsAnyAsync <OperationCanceledException>(
                () => cacheClient.AcquireLockAsync(
                    "test:redis:acquire:multiple", tokenSource.Token)
                .AsTask());

            await testLock.ReleaseAsync();

            Assert.Null(await cacheClient.GetAsync <string>("test:redis:acquire:multiple"));
        }
Пример #3
0
        public async Task LoadServicesAsync(MikiAppBuilder app)
        {
            new LogBuilder()
            .AddLogEvent((msg, lvl) =>
            {
                if (lvl >= Global.Config.LogLevel)
                {
                    Console.WriteLine(msg);
                }
            })
            .SetLogHeader((msg) => $"[{msg}]: ")
            .SetTheme(new LogTheme())
            .Apply();

            var cache = new StackExchangeCacheClient(
                new ProtobufSerializer(),
                await ConnectionMultiplexer.ConnectAsync(Global.Config.RedisConnectionString)
                );

            // Setup Redis
            {
                app.AddSingletonService <ICacheClient>(cache);
                app.AddSingletonService <IExtendedCacheClient>(cache);
            }

            // Setup Entity Framework
            {
                app.Services.AddDbContext <MikiDbContext>(x
                                                          => x.UseNpgsql(Global.Config.ConnString, b => b.MigrationsAssembly("Miki.Bot.Models")));
                app.Services.AddDbContext <DbContext, MikiDbContext>(x
                                                                     => x.UseNpgsql(Global.Config.ConnString, b => b.MigrationsAssembly("Miki.Bot.Models")));
            }

            // Setup Miki API
            {
                if (!string.IsNullOrWhiteSpace(Global.Config.MikiApiBaseUrl) && !string.IsNullOrWhiteSpace(Global.Config.MikiApiKey))
                {
                    app.AddSingletonService(new MikiApiClient(Global.Config.MikiApiKey));
                }
                else
                {
                    Log.Warning("No Miki API parameters were supplied, ignoring Miki API.");
                }
            }

            // Setup Discord
            {
                app.AddSingletonService <IApiClient>(new DiscordApiClient(Global.Config.Token, cache));
                if (Global.Config.SelfHosted)
                {
                    var gatewayConfig = new GatewayProperties();
                    gatewayConfig.ShardCount             = 1;
                    gatewayConfig.ShardId                = 0;
                    gatewayConfig.Token                  = Global.Config.Token;
                    gatewayConfig.Compressed             = true;
                    gatewayConfig.AllowNonDispatchEvents = true;
                    app.AddSingletonService <IGateway>(new GatewayCluster(gatewayConfig));
                }
                else
                {
                    app.AddSingletonService <IGateway>(new DistributedGateway(new MessageClientConfiguration
                    {
                        ConnectionString = new Uri(Global.Config.RabbitUrl.ToString()),
                        QueueName        = "gateway",
                        ExchangeName     = "consumer",
                        ConsumerAutoAck  = false,
                        PrefetchCount    = 25,
                    }));
                }
            }

            // Setup web services
            {
                app.AddSingletonService(new UrbanDictionaryAPI());
                app.AddSingletonService(new BunnyCDNClient(Global.Config.BunnyCdnKey));
            }

            // Setup miscellanious services
            {
                app.AddSingletonService(new ConfigurationManager());
                app.AddSingletonService(new EventSystem());
                app.AddSingletonService(new BackgroundStore());

                if (!string.IsNullOrWhiteSpace(Global.Config.SharpRavenKey))
                {
                    app.AddSingletonService(new RavenClient(Global.Config.SharpRavenKey));
                }
                else
                {
                    Log.Warning("Sentry.io key not provided, ignoring distributed error logging...");
                }
            }
        }
Пример #4
0
        public async Task LoadServicesAsync(MikiAppBuilder app)
        {
            var cache = new StackExchangeCacheClient(
                new ProtobufSerializer(),
                await ConnectionMultiplexer.ConnectAsync(Global.Config.RedisConnectionString)
                );

            app.AddSingletonService <ICacheClient>(cache);
            app.AddSingletonService <IExtendedCacheClient>(cache);
            app.Services.AddDbContext <DbContext, MikiContext>(x => x.UseNpgsql(Global.Config.ConnString));

            app.AddSingletonService(new LoggingService());

            if (!string.IsNullOrWhiteSpace(Global.Config.MikiApiBaseUrl) && !string.IsNullOrWhiteSpace(Global.Config.MikiApiKey))
            {
                app.AddSingletonService(new MikiApiClient(Global.Config.MikiApiKey));
            }
            else
            {
                Log.Warning("No Miki API parameters were supplied, ignoring Miki API.");
            }

            app.AddSingletonService <IApiClient>(new DiscordApiClient(Global.Config.Token, cache));

            if (Global.Config.SelfHosted)
            {
                var gatewayConfig = GatewayConfiguration.Default();
                gatewayConfig.ShardCount      = 1;
                gatewayConfig.ShardId         = 0;
                gatewayConfig.Token           = Global.Config.Token;
                gatewayConfig.WebSocketClient = new BasicWebSocketClient();
                app.AddSingletonService <IGateway>(new CentralizedGatewayShard(gatewayConfig));
            }
            else
            {
                app.AddSingletonService <IGateway>(new DistributedGateway(new MessageClientConfiguration
                {
                    ConnectionString = new Uri(Global.Config.RabbitUrl.ToString()),
                    QueueName        = "gateway",
                    ExchangeName     = "consumer",
                    ConsumerAutoAck  = false,
                    PrefetchCount    = 25
                }));
            }

            app.AddSingletonService(new BunnyCDNClient(Global.Config.BunnyCdnKey));
            app.AddSingletonService(new ConfigurationManager());
            app.AddSingletonService(new EventSystem(new EventSystemConfig()
            {
                Developers = Global.Config.DeveloperIds,
            }));

            app.AddSingletonService(new BackgroundStore());

            if (!string.IsNullOrWhiteSpace(Global.Config.SharpRavenKey))
            {
                app.AddSingletonService(new RavenClient(Global.Config.SharpRavenKey));
            }
            else
            {
                Log.Warning("Sentry.io key not provided, ignoring distributed error logging...");
            }
        }
Пример #5
0
        private static async Task MainAsync()
        {
            new LogBuilder()
            .AddLogEvent((msg, lvl) => { if (lvl >= config.LogLevel)
                                         {
                                             Console.WriteLine(msg);
                                         }
                         })
            .AddExceptionEvent((ex, lvl) => SentrySdk.CaptureException(ex))
            .Apply();

            await LoadConfigAsync();

            Log.Message("Config loaded.");

            using (SentrySdk.Init(config.SentryUrl))
            {
                Log.Message("Error handler setup.");

                var redis = await ConnectionMultiplexer.ConnectAsync(config.RedisUrl);

                var cache = new StackExchangeCacheClient(new ProtobufSerializer(), redis);

                Log.Message("Cache connected");
                List <int> allShardIds = new List <int>();
                for (var i = config.Discord.ShardIndex;
                     i < config.Discord.ShardIndex + config.Discord.ShardAmount;
                     i++)
                {
                    allShardIds.Add(i);
                }

                options = new JsonSerializerOptions
                {
                    Converters =
                    {
                        new StringToUlongConverter(),
                        new StringToShortConverter(),
                        new StringToEnumConverter <GuildPermission>(),
                        new UserAvatarConverter()
                    }
                };

                cluster = new GatewayConnectionCluster(new GatewayProperties
                {
                    Encoding               = GatewayEncoding.Json,
                    Ratelimiter            = new CacheBasedRatelimiter(cache),
                    SerializerOptions      = options,
                    ShardCount             = config.Discord.ShardCount,
                    ShardId                = 0,
                    Token                  = config.Discord.Token,
                    Version                = GatewayConstants.DefaultVersion,
                    AllowNonDispatchEvents = false,
                    Compressed             = false,
                    Intents                = config.Discord.Intents,
                }, allShardIds);

                var conn = new ConnectionFactory
                {
                    Uri = new Uri(config.MessageQueue.Url),
                };

                using var connection   = conn.CreateConnection();
                using var model        = pusherModel = connection.CreateModel();
                using var commandModel = connection.CreateModel();

                pusherModel.ExchangeDeclare("gateway", "direct", true);
                cluster.OnPacketReceived.SubscribeTask(OnPacketReceivedAsync);

                commandModel.ExchangeDeclare("gateway-command", "fanout", true);

                var queue = commandModel.QueueDeclare();
                commandModel.QueueBind(queue.QueueName, "gateway-command", "");

                var consumer = new AsyncEventingBasicConsumer(commandModel);
                consumer.Received += OnCommandReceivedAsync;
                commandModel.BasicConsume(queue.QueueName, false, consumer);
                Log.Message("Set up RabbitMQ");

                await cluster.StartAsync();

                Log.Message("Discord gateway running");

                await Task.Delay(-1);
            }
        }