diff --git a/EllieBot.sln b/EllieBot.sln index c015691..b112a25 100644 --- a/EllieBot.sln +++ b/EllieBot.sln @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EllieBot.Coordinator", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EllieBot.Generators", "src\EllieBot.Generators\EllieBot.Generators.csproj", "{CB1A5307-DD85-4795-8A8A-A25D36DADC51}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EllieBot.VotesApi", "src\EllieBot.VotesApi\EllieBot.VotesApi.csproj", "{F1A77F56-71B0-430E-AE46-94CDD7D43874}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,10 @@ Global {CB1A5307-DD85-4795-8A8A-A25D36DADC51}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB1A5307-DD85-4795-8A8A-A25D36DADC51}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB1A5307-DD85-4795-8A8A-A25D36DADC51}.Release|Any CPU.Build.0 = Release|Any CPU + {F1A77F56-71B0-430E-AE46-94CDD7D43874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1A77F56-71B0-430E-AE46-94CDD7D43874}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1A77F56-71B0-430E-AE46-94CDD7D43874}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1A77F56-71B0-430E-AE46-94CDD7D43874}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -62,6 +68,7 @@ Global {179DF3B3-AD32-4335-8231-9818338DF3A2} = {B28FB883-9688-41EB-BF5A-945F4A4EB628} {A631DDF0-3AD1-4CB9-8458-314B1320868A} = {B28FB883-9688-41EB-BF5A-945F4A4EB628} {CB1A5307-DD85-4795-8A8A-A25D36DADC51} = {B28FB883-9688-41EB-BF5A-945F4A4EB628} + {F1A77F56-71B0-430E-AE46-94CDD7D43874} = {B28FB883-9688-41EB-BF5A-945F4A4EB628} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {79F61C2C-CDBB-4361-A234-91A0B334CFE4} diff --git a/src/EllieBot.VotesApi/.dockerignore b/src/EllieBot.VotesApi/.dockerignore new file mode 100644 index 0000000..4c2af91 --- /dev/null +++ b/src/EllieBot.VotesApi/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/src/EllieBot.VotesApi/.gitignore b/src/EllieBot.VotesApi/.gitignore new file mode 100644 index 0000000..9ae80d3 --- /dev/null +++ b/src/EllieBot.VotesApi/.gitignore @@ -0,0 +1 @@ +store/ \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Common/AuthHandler.cs b/src/EllieBot.VotesApi/Common/AuthHandler.cs new file mode 100644 index 0000000..9294153 --- /dev/null +++ b/src/EllieBot.VotesApi/Common/AuthHandler.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace EllieBot.VotesApi +{ + public class AuthHandler : AuthenticationHandler + { + public const string SchemeName = "AUTHORIZATION_SCHEME"; + public const string DiscordsClaim = "DISCORDS_CLAIM"; + public const string TopggClaim = "TOPGG_CLAIM"; + + private readonly IConfiguration _conf; + + public AuthHandler(IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + IConfiguration conf) + : base(options, logger, encoder, clock) + => _conf = conf; + + protected override Task HandleAuthenticateAsync() + { + var claims = new List(); + + if (_conf[ConfKeys.DISCORDS_KEY].Trim() == Request.Headers["Authorization"].ToString().Trim()) + claims.Add(new(DiscordsClaim, "true")); + + if (_conf[ConfKeys.TOPGG_KEY] == Request.Headers["Authorization"].ToString().Trim()) + claims.Add(new Claim(TopggClaim, "true")); + + return Task.FromResult(AuthenticateResult.Success(new(new(new ClaimsIdentity(claims)), SchemeName))); + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Common/ConfKeys.cs b/src/EllieBot.VotesApi/Common/ConfKeys.cs new file mode 100644 index 0000000..dd7de64 --- /dev/null +++ b/src/EllieBot.VotesApi/Common/ConfKeys.cs @@ -0,0 +1,8 @@ +namespace EllieBot.VotesApi +{ + public static class ConfKeys + { + public const string DISCORDS_KEY = "DiscordsKey"; + public const string TOPGG_KEY = "TopGGKey"; + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Common/DiscordsVoteWebhookModel.cs b/src/EllieBot.VotesApi/Common/DiscordsVoteWebhookModel.cs new file mode 100644 index 0000000..09522b7 --- /dev/null +++ b/src/EllieBot.VotesApi/Common/DiscordsVoteWebhookModel.cs @@ -0,0 +1,26 @@ +namespace EllieBot.VotesApi +{ + public class DiscordsVoteWebhookModel + { + /// + /// The ID of the user who voted + /// + public string User { get; set; } + + /// + /// The ID of the bot which recieved the vote + /// + public string Bot { get; set; } + + /// + /// Contains totalVotes, votesMonth, votes24, hasVoted - a list of IDs of users who have voted this month, and + /// Voted24 - a list of IDs of users who have voted today + /// + public string Votes { get; set; } + + /// + /// The type of event, whether it is a vote event or test event + /// + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Common/Policies.cs b/src/EllieBot.VotesApi/Common/Policies.cs new file mode 100644 index 0000000..d4c59d0 --- /dev/null +++ b/src/EllieBot.VotesApi/Common/Policies.cs @@ -0,0 +1,8 @@ +namespace EllieBot.VotesApi +{ + public static class Policies + { + public const string DiscordsAuth = "DiscordsAuth"; + public const string TopggAuth = "TopggAuth"; + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Common/TopggVoteWebhookModel.cs b/src/EllieBot.VotesApi/Common/TopggVoteWebhookModel.cs new file mode 100644 index 0000000..bdfd8a9 --- /dev/null +++ b/src/EllieBot.VotesApi/Common/TopggVoteWebhookModel.cs @@ -0,0 +1,30 @@ +namespace EllieBot.VotesApi +{ + public class TopggVoteWebhookModel + { + /// + /// Discord ID of the bot that received a vote. + /// + public string Bot { get; set; } + + /// + /// Discord ID of the user who voted. + /// + public string User { get; set; } + + /// + /// The type of the vote (should always be "upvote" except when using the test button it's "test"). + /// + public string Type { get; set; } + + /// + /// Whether the weekend multiplier is in effect, meaning users votes count as two. + /// + public bool Weekend { get; set; } + + /// + /// Query string params found on the /bot/:ID/vote page. Example: ?a=1&b=2. + /// + public string Query { get; set; } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Controllers/DiscordsController.cs b/src/EllieBot.VotesApi/Controllers/DiscordsController.cs new file mode 100644 index 0000000..183db84 --- /dev/null +++ b/src/EllieBot.VotesApi/Controllers/DiscordsController.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using EllieBot.VotesApi.Services; + +namespace EllieBot.VotesApi.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DiscordsController : ControllerBase + { + private readonly ILogger _logger; + private readonly IVotesCache _cache; + + public DiscordsController(ILogger logger, IVotesCache cache) + { + _logger = logger; + _cache = cache; + } + + [HttpGet("new")] + [Authorize(Policy = Policies.DiscordsAuth)] + public async Task> New() + { + var votes = await _cache.GetNewDiscordsVotesAsync(); + if (votes.Count > 0) + _logger.LogInformation("Sending {NewDiscordsVotes} new discords votes", votes.Count); + return votes; + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Controllers/TopGgController.cs b/src/EllieBot.VotesApi/Controllers/TopGgController.cs new file mode 100644 index 0000000..28fb5a7 --- /dev/null +++ b/src/EllieBot.VotesApi/Controllers/TopGgController.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using EllieBot.VotesApi.Services; + +namespace EllieBot.VotesApi.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TopGgController : ControllerBase + { + private readonly ILogger _logger; + private readonly IVotesCache _cache; + + public TopGgController(ILogger logger, IVotesCache cache) + { + _logger = logger; + _cache = cache; + } + + [HttpGet("new")] + [Authorize(Policy = Policies.TopggAuth)] + public async Task> New() + { + var votes = await _cache.GetNewTopGgVotesAsync(); + if (votes.Count > 0) + _logger.LogInformation("Sending {NewTopggVotes} new topgg votes", votes.Count); + + return votes; + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Controllers/WebhookController.cs b/src/EllieBot.VotesApi/Controllers/WebhookController.cs new file mode 100644 index 0000000..51dcfd6 --- /dev/null +++ b/src/EllieBot.VotesApi/Controllers/WebhookController.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using EllieBot.VotesApi.Services; + +namespace EllieBot.VotesApi.Controllers +{ + [ApiController] + public class WebhookController : ControllerBase + { + private readonly ILogger _logger; + private readonly IVotesCache _votesCache; + + public WebhookController(ILogger logger, IVotesCache votesCache) + { + _logger = logger; + _votesCache = votesCache; + } + + [HttpPost("/discordswebhook")] + [Authorize(Policy = Policies.DiscordsAuth)] + public async Task DiscordsWebhook([FromBody] DiscordsVoteWebhookModel data) + { + + _logger.LogInformation("User {UserId} has voted for Bot {BotId} on {Platform}", + data.User, + data.Bot, + "discords.com"); + + await _votesCache.AddNewDiscordsVote(data.User); + return Ok(); + } + + [HttpPost("/topggwebhook")] + [Authorize(Policy = Policies.TopggAuth)] + public async Task TopggWebhook([FromBody] TopggVoteWebhookModel data) + { + _logger.LogInformation("User {UserId} has voted for Bot {BotId} on {Platform}", + data.User, + data.Bot, + "top.gg"); + + await _votesCache.AddNewTopggVote(data.User); + return Ok(); + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Dockerfile b/src/EllieBot.VotesApi/Dockerfile new file mode 100644 index 0000000..4597cb2 --- /dev/null +++ b/src/EllieBot.VotesApi/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +WORKDIR /src +COPY ["src/EllieBot.VotesApi/EllieBot.VotesApi.csproj", "EllieBot.VotesApi/"] +RUN dotnet restore "src/EllieBot.VotesApi/EllieBot.VotesApi.csproj" +COPY . . +WORKDIR "/src/EllieBot.VotesApi" +RUN dotnet build "EllieBot.VotesApi.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "EllieBot.VotesApi.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "EllieBot.VotesApi.dll"] diff --git a/src/EllieBot.VotesApi/EllieBot.VotesApi.csproj b/src/EllieBot.VotesApi/EllieBot.VotesApi.csproj new file mode 100644 index 0000000..f42db3d --- /dev/null +++ b/src/EllieBot.VotesApi/EllieBot.VotesApi.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + Linux + + + + + + + + diff --git a/src/EllieBot.VotesApi/Program.cs b/src/EllieBot.VotesApi/Program.cs new file mode 100644 index 0000000..20b7d0a --- /dev/null +++ b/src/EllieBot.VotesApi/Program.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using EllieBot.VotesApi; + +CreateHostBuilder(args).Build().Run(); + +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Properties/launchSettings.json b/src/EllieBot.VotesApi/Properties/launchSettings.json new file mode 100644 index 0000000..5112c07 --- /dev/null +++ b/src/EllieBot.VotesApi/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:16451", + "sslPort": 44323 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "EllieBot.VotesApi": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/EllieBot.VotesApi/README.md b/src/EllieBot.VotesApi/README.md new file mode 100644 index 0000000..4c22299 --- /dev/null +++ b/src/EllieBot.VotesApi/README.md @@ -0,0 +1,46 @@ +## Votes Api + +This api is used if you want your bot to be able to reward users who vote for it on discords.com or top.gg + +#### [GET] `/discords/new` + Get the discords votes received after previous call to this endpoint. + Input full url of this endpoint in your creds.yml file under Discords url field. + For example "https://api.my.cool.bot/discords/new" +#### [GET] `/topgg/new` + Get the topgg votes received after previous call to this endpoint. + Input full url of this endpoint in your creds.yml file under Topgg url field. + For example "https://api.my.cool.bot/topgg/new" + +#### [POST] `/discordswebhook` + Input this endpoint as the webhook on discords.com bot edit page + model: https://docs.botsfordiscord.com/methods/receiving-votes + For example "https://api.my.cool.bot/topggwebhook" +#### [POST] `/topggwebhook` + Input this endpoint as the webhook https://top.gg/bot/:your-bot-id/webhooks (replace :your-bot-id with your bot's id) + model: https://docs.top.gg/resources/webhooks/#schema + For example "https://api.my.cool.bot/discordswebhook" + +Input your super-secret header value in appsettings.json's DiscordsKey and TopGGKey fields +They must match your DiscordsKey and TopGG key respectively, as well as your secrets in the discords.com and top.gg webhook setup pages + +Full Example: + +⚠ Change TopggKey and DiscordsKey to a secure long string +⚠ You can use https://www.random.org/strings/?num=1&len=20&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new to generate it + +`creds.yml` +```yml +votes: + TopggServiceUrl: "https://api.my.cool.bot/topgg" + TopggKey: "my_topgg_key" + DiscordsServiceUrl: "https://api.my.cool.bot/discords" + DiscordsKey: "my_discords_key" +``` + +`appsettings.json` +```json +... + "DiscordsKey": "my_discords_key", + "TopGGKey": "my_topgg_key", +... +``` \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Services/FileVotesCache.cs b/src/EllieBot.VotesApi/Services/FileVotesCache.cs new file mode 100644 index 0000000..77b963a --- /dev/null +++ b/src/EllieBot.VotesApi/Services/FileVotesCache.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading; +using MorseCode.ITask; + +namespace EllieBot.VotesApi.Services +{ + public class FileVotesCache : IVotesCache + { + // private const string STATS_FILE = "store/stats.json"; + private const string TOPGG_FILE = "store/topgg.json"; + private const string DISCORDS_FILE = "store/discords.json"; + + private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1); + + public FileVotesCache() + { + if (!Directory.Exists("store")) + Directory.CreateDirectory("store"); + + if (!File.Exists(TOPGG_FILE)) + File.WriteAllText(TOPGG_FILE, "[]"); + + if (!File.Exists(DISCORDS_FILE)) + File.WriteAllText(DISCORDS_FILE, "[]"); + } + + public ITask AddNewTopggVote(string userId) + => AddNewVote(TOPGG_FILE, userId); + + public ITask AddNewDiscordsVote(string userId) + => AddNewVote(DISCORDS_FILE, userId); + + private async ITask AddNewVote(string file, string userId) + { + await _locker.WaitAsync(); + try + { + var votes = await GetVotesAsync(file); + votes.Add(userId); + await File.WriteAllTextAsync(file, JsonSerializer.Serialize(votes)); + } + finally + { + _locker.Release(); + } + } + + public async ITask> GetNewTopGgVotesAsync() + { + var votes = await EvictTopggVotes(); + return votes; + } + + public async ITask> GetNewDiscordsVotesAsync() + { + var votes = await EvictDiscordsVotes(); + return votes; + } + + private ITask> EvictTopggVotes() + => EvictVotes(TOPGG_FILE); + + private ITask> EvictDiscordsVotes() + => EvictVotes(DISCORDS_FILE); + + private async ITask> EvictVotes(string file) + { + await _locker.WaitAsync(); + try + { + + var ids = await GetVotesAsync(file); + await File.WriteAllTextAsync(file, "[]"); + + return ids? + .Select(x => (Ok: ulong.TryParse(x, out var r), Id: r)) + .Where(x => x.Ok) + .Select(x => new Vote + { + UserId = x.Id + }) + .ToList(); + } + finally + { + _locker.Release(); + } + } + + private async ITask> GetVotesAsync(string file) + { + await using var fs = File.Open(file, FileMode.Open); + var votes = await JsonSerializer.DeserializeAsync>(fs); + return votes; + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Services/IVotesCache.cs b/src/EllieBot.VotesApi/Services/IVotesCache.cs new file mode 100644 index 0000000..0bc25de --- /dev/null +++ b/src/EllieBot.VotesApi/Services/IVotesCache.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using MorseCode.ITask; + +namespace EllieBot.VotesApi.Services +{ + public interface IVotesCache + { + ITask> GetNewTopGgVotesAsync(); + ITask> GetNewDiscordsVotesAsync(); + ITask AddNewTopggVote(string userId); + ITask AddNewDiscordsVote(string userId); + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/Startup.cs b/src/EllieBot.VotesApi/Startup.cs new file mode 100644 index 0000000..c1d850f --- /dev/null +++ b/src/EllieBot.VotesApi/Startup.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using EllieBot.VotesApi.Services; + +namespace EllieBot.VotesApi +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + => Configuration = configuration; + + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSingleton(); + services.AddSwaggerGen(static c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "EllieBot.VotesApi", Version = "v1" }); + }); + + services + .AddAuthentication(opts => + { + opts.DefaultScheme = AuthHandler.SchemeName; + opts.AddScheme(AuthHandler.SchemeName, AuthHandler.SchemeName); + }); + + services + .AddAuthorization(static opts => + { + opts.DefaultPolicy = new AuthorizationPolicyBuilder(AuthHandler.SchemeName) + .RequireAssertion(static _ => false) + .Build(); + opts.AddPolicy(Policies.DiscordsAuth, static policy => policy.RequireClaim(AuthHandler.DiscordsClaim)); + opts.AddPolicy(Policies.TopggAuth, static policy => policy.RequireClaim(AuthHandler.TopggClaim)); + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(static c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "EllieBot.VotesApi v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(static endpoints => { endpoints.MapControllers(); }); + } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/WeatherForecast.cs b/src/EllieBot.VotesApi/WeatherForecast.cs new file mode 100644 index 0000000..a1e8f62 --- /dev/null +++ b/src/EllieBot.VotesApi/WeatherForecast.cs @@ -0,0 +1,7 @@ +namespace EllieBot.VotesApi +{ + public class Vote + { + public ulong UserId { get; set; } + } +} \ No newline at end of file diff --git a/src/EllieBot.VotesApi/appsettings.Development.json b/src/EllieBot.VotesApi/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/EllieBot.VotesApi/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/EllieBot.VotesApi/appsettings.json b/src/EllieBot.VotesApi/appsettings.json new file mode 100644 index 0000000..7b5f330 --- /dev/null +++ b/src/EllieBot.VotesApi/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "DiscordsKey": "my_discords_key", + "TopGGKey": "my_topgg_key", + "AllowedHosts": "*" +}