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")); }
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")); }
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..."); } } }
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..."); } }
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); } }