zobweyt
zobweyt
Explore posts from servers
CC#
Created by zobweyt on 4/12/2024 in #help
Attributes confusion
hey! I'm having trouble choosing an approach when using attributes. I'm building a wrapper around System.Threading.RateLimiting. I have an attribute RateLimitAttribute which is responsible for the basic logic and creation of PartitionedRateLimiter. here’s the problem: I want to be able to create custom ignore policies (to have no limit in custom conditions) and pass them directly to the RateLimitAttribute, but I don’t know how to do this correctly and what approach to choose. initially I wanted to make a base class RateLimitPolicyAttribute for all policies and later use it like this:
[RateLimit]
[IgnoreOwner, IgnoreAdmins, IgnorePremiumUsers]
public static void Foo() => throw NotImplementedException();
[RateLimit]
[IgnoreOwner, IgnoreAdmins, IgnorePremiumUsers]
public static void Foo() => throw NotImplementedException();
but the policies here can exist without the main RateLimitAttribute also, I thought about passing the policies directly to the main attribute, but this approach is not possible because the values in attribute parameters must be constant, so I can't just pass a list of policies. additionally, it'll be a very long line in length if I have many policies how can I implement this most conveniently?
21 replies
CC#
Created by zobweyt on 1/25/2024 in #help
Implementing rate limiting for discord bot interactions with absolute expiration of items
I’m trying to create a simple and customizable RateLimitAttribute for interactions in Discord.Net using their preconditions. I'm too confused about all the possibilities. So, how would you implement this? What decisions would you make and what data structures would you use? Expected usage
[RateLimit(max: 2, period: TimeSpan.FromMinutes(1), ignore: Ignore.Owner | Ignore.DMs)]
[SlashCommand("ping", "Pings the bot.")]
public async Task PingAsync() => await RespondAsync("Pong!");
[RateLimit(max: 2, period: TimeSpan.FromMinutes(1), ignore: Ignore.Owner | Ignore.DMs)]
[SlashCommand("ping", "Pings the bot.")]
public async Task PingAsync() => await RespondAsync("Pong!");
Expected behavior /ping — Pong! (count is now 1) /ping — Pong! (count is now 2) /ping — Wait 60 seconds for limit to expire! /ping (30 seconds later) — Wait 30 seconds for limit to expire! … (30 seconds later)Remove rate limit from store in background. /ping (any time later) — Pong! (count is now 1) Wanted features: - Users can define custom ignore policies: for instance, if you have a database with different roles like admins and regular users, you might want to apply rate limiting to all users but exclude admins. - Users can access the store of rate limits: for example, EPIC RPG’s /cd command shows the cooldowns for each command per user. Problems I’ve met - Attributes require constant values: TimeSpan cannot be used directly. Instead, people use parameters specifying periods in seconds. Alternatively, a converter could be used with double period and enum TimeMeasure. - Caching with absolute expiration: IMemoryCache's AbsoluteExpiration simplifies caching implementation because it removes the rate limits right when they expire, but the keys there are strings and not generic types. Using System.Threading.RateLimiting, it’s hard to display the exact remaining time until the limit expires.
5 replies
CC#
Created by zobweyt on 7/16/2023 in #help
✅ What is the best way to setup app configuration using IHostBuilder?
I want to make a good building of my host configuration and make it convenient to use. My goal is to get the as much as possible: - Convenience; - Strongly typing; - Won't compile against typos in keys; I've already tried those ways to setup configuration and found them inconvenient: 1. HostBuilderContext.Configuration["Key"] · hardcoding keys; 2. context.Configuration.Get<Configuration>() · calling the same method everywhere; 3. context.Configuration.Bind(configuration) · confusing where to bind it at the first time; Personally I'm using the third way, because it saves me from code duplication and hardcoding keys, but also brings a new problem — confusing where to bind the configuration. Currently, I'm binding it in the IHostBuilder.ConfigureAppConfiguration:
internal class Program
{
private static readonly Configuration _configuration = new();

private static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(b => b.Build().Bind(_configuration))
.ConfigureServices((context, services) =>
{
services.AddSingleton(_configuration);
})
.Build();

await host.RunAsync();
}
}
internal class Program
{
private static readonly Configuration _configuration = new();

private static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(b => b.Build().Bind(_configuration))
.ConfigureServices((context, services) =>
{
services.AddSingleton(_configuration);
})
.Build();

await host.RunAsync();
}
}
Ideally, I would like to redefine content.Configuration so that its type matches Configuration (my custom class), in order to directly get values without hardcoding, for example: context.Configuration.Token. But I understand that this may not be possible.
6 replies
CC#
Created by zobweyt on 12/23/2022 in #help
❔ Naming fields meaning numbers
6 replies