public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url) { var rparams_props = route_params.GetType() .GetTypeInfo() .DeclaredProperties; var rparams = new Dictionary <string, string>(); foreach (var xp in rparams_props) { var val = xp.GetValue(route_params); if (val is string xs) { rparams[xp.Name] = xs; } else if (val is DateTime dt) { rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else if (val is DateTimeOffset dto) { rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else if (val is IFormattable xf) { rparams[xp.Name] = xf.ToString(null, CultureInfo.InvariantCulture); } else { rparams[xp.Name] = val.ToString(); } } var guild_id = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : ""; var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : ""; var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : ""; var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id, webhook_id); RateLimitBucket bucket = null; bucket = this.Buckets.FirstOrDefault(xb => xb.BucketId == id); if (bucket == null) { bucket = new RateLimitBucket(method, route, guild_id, channel_id, webhook_id); this.Buckets.Add(bucket); } url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]); return(bucket); }
public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url) { var rparams_props = route_params.GetType() .GetTypeInfo() .DeclaredProperties; var rparams = new Dictionary <string, string>(); foreach (var xp in rparams_props) { var val = xp.GetValue(route_params); if (val is string xs) { rparams[xp.Name] = xs; } else if (val is DateTime dt) { rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else if (val is DateTimeOffset dto) { rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else if (val is IFormattable xf) { rparams[xp.Name] = xf.ToString(null, CultureInfo.InvariantCulture); } else { rparams[xp.Name] = val.ToString(); } } var guild_id = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : ""; var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : ""; var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : ""; var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id, webhook_id); // using the GetOrAdd version with the factory has no advantages as it will allocate the delegate, closure object and bucket (if needed) instead of just the bucket var bucket = this.Buckets.GetOrAdd(id, new RateLimitBucket(method, route, guild_id, channel_id, webhook_id)); url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]); return(bucket); }
public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url) { var rparams = route_params.GetType() .GetTypeInfo() .DeclaredProperties .ToDictionary(xp => xp.Name, xp => xp.GetValue(route_params) as string); var guild_id = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : ""; var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : ""; var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id); RateLimitBucket bucket = null; bucket = this.Buckets.FirstOrDefault(xb => xb.BucketId == id); if (bucket == null) { bucket = new RateLimitBucket(method, route, guild_id, channel_id); this.Buckets.Add(bucket); } url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]); return(bucket); }
public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url) { var rparams_props = route_params.GetType() .GetTypeInfo() .DeclaredProperties; var rparams = new Dictionary <string, string>(); foreach (var xp in rparams_props) { var val = xp.GetValue(route_params); if (val is string xs) { rparams[xp.Name] = xs; } else if (val is DateTime dt) { rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else if (val is DateTimeOffset dto) { rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture); } else { rparams[xp.Name] = val is IFormattable xf?xf.ToString(null, CultureInfo.InvariantCulture) : val.ToString(); } } var guild_id = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : ""; var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : ""; var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : ""; // Create a generic route (minus major params) key // ex: POST:/channels/channel_id/messages var hashKey = RateLimitBucket.GenerateHashKey(method, route); // We check if the hash is present, using our generic route (without major params) // ex: in POST:/channels/channel_id/messages, out 80c17d2f203122d936070c88c8d10f33 // If it doesn't exist, we create an unlimited hash as our initial key in the form of the hash key + the unlimited constant // and assign this to the route to hash cache // ex: this.RoutesToHashes[POST:/channels/channel_id/messages] = POST:/channels/channel_id/messages:unlimited var hash = this.RoutesToHashes.GetOrAdd(hashKey, RateLimitBucket.GenerateUnlimitedHash(method, route)); // Next we use the hash to generate the key to obtain the bucket. // ex: 80c17d2f203122d936070c88c8d10f33:guild_id:506128773926879242:webhook_id // or if unlimited: POST:/channels/channel_id/messages:unlimited:guild_id:506128773926879242:webhook_id var bucketId = RateLimitBucket.GenerateBucketId(hash, guild_id, channel_id, webhook_id); // If it's not in cache, create a new bucket and index it by its bucket id. var bucket = this.HashesToBuckets.GetOrAdd(bucketId, new RateLimitBucket(hash, guild_id, channel_id, webhook_id)); bucket.LastAttemptAt = DateTimeOffset.UtcNow; // Cache the routes for each bucket so it can be used for GC later. if (!bucket.RouteHashes.Contains(bucketId)) { bucket.RouteHashes.Add(bucketId); } // Add the current route to the request queue, which indexes the amount // of requests occurring to the bucket id. _ = this.RequestQueue.TryGetValue(bucketId, out var count); // Increment by one atomically due to concurrency this.RequestQueue[bucketId] = Interlocked.Increment(ref count); // Start bucket cleaner if not already running. if (!this._cleanerRunning) { this._cleanerRunning = true; this._bucketCleanerTokenSource = new CancellationTokenSource(); this._cleanerTask = Task.Run(this.CleanupBucketsAsync, this._bucketCleanerTokenSource.Token); this.Logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task started."); } url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]); return(bucket); }