// Copyright (C) 2021-present sam (starshines.gay) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . using Catalogger.Backend.Bot.Commands; using Catalogger.Backend.Database; using Catalogger.Backend.Extensions; using Catalogger.Backend.Services; using Newtonsoft.Json.Serialization; using Prometheus; using Remora.Commands.Extensions; using Remora.Discord.API.Abstractions.Gateway.Commands; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Gateway.Commands; using Remora.Discord.API.Objects; using Remora.Discord.Caching.Extensions; using Remora.Discord.Commands.Extensions; using Remora.Discord.Extensions.Extensions; using Remora.Discord.Gateway; using Remora.Discord.Interactivity.Extensions; using Remora.Discord.Pagination.Extensions; using Serilog; using Metrics = Prometheus.Metrics; var builder = WebApplication.CreateBuilder(args); var config = builder.AddConfiguration(); builder.AddSerilog(config); builder .Services.AddControllers() .AddNewtonsoftJson(o => o.SerializerSettings.ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy(), } ); builder .Host.AddShardedDiscordService(_ => config.Discord.Token) .ConfigureServices(s => s.AddRespondersFromAssembly(typeof(Program).Assembly) .Configure(g => { g.Intents = GatewayIntents.Guilds // Actually GUILD_MODERATION | GatewayIntents.GuildBans | GatewayIntents.GuildInvites | GatewayIntents.GuildMembers | GatewayIntents.GuildMessages | GatewayIntents.GuildWebhooks | GatewayIntents.MessageContents // Actually GUILD_EXPRESSIONS | GatewayIntents.GuildEmojisAndStickers; // Set a default status for all shards. This is updated to a shard-specific one in StatusUpdateService. g.Presence = new UpdatePresence( Status: UserStatus.Online, IsAFK: false, Since: null, Activities: [ new Activity( Name: "Beep", Type: ActivityType.Custom, State: "/catalogger help" ), ] ); }) .AddDiscordCaching() .AddDiscordCommands(enableSlash: true, useDefaultCommandResponder: false) .AddCommandTree() // Start command tree .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() // End command tree .Finish() .AddPagination() .AddInteractivity() .AddInteractionGroup() .AddAutocompleteProvider() ); // Add metric server // If metrics are disabled (Logging.EnableMetrics = false), also add a background service that updates // metrics every minute, as some commands rely on them. builder.Services.AddMetricServer(o => o.Port = (ushort)config.Logging.MetricsPort); if (!config.Logging.EnableMetrics) builder.Services.AddHostedService(); builder .Services.AddDbContext() .MaybeAddRedisCaches(config) .AddCustomServices() .AddEndpointsApiExplorer() .AddSwaggerGen(); var app = builder.Build(); await app.Initialize(); app.UseSerilogRequestLogging(); app.UseRouting(); app.UseHttpMetrics(); app.UseSwagger(); app.UseSwaggerUI(); app.UseCors(); app.MapControllers(); app.Urls.Clear(); app.Urls.Add(config.Web.Address); // Make sure metrics are updated whenever Prometheus scrapes them Metrics.DefaultRegistry.AddBeforeCollectCallback(async ct => await app.Services.GetRequiredService().CollectMetricsAsync(ct) ); app.Run(); Log.CloseAndFlush();