Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        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);
        }