Class: Discordrb::Commands::Bucket

Inherits:
Object
  • Object
show all
Defined in:
lib/discordrb/commands/rate_limiter.rb

Overview

This class represents a bucket for rate limiting - it keeps track of how many requests have been made and when exactly the user should be rate limited.

Instance Method Summary collapse

Constructor Details

#initialize(limit, time_span, delay) ⇒ Bucket

Makes a new bucket

Raises:

  • (ArgumentError)

11
12
13
14
15
16
17
18
19
# File 'lib/discordrb/commands/rate_limiter.rb', line 11

def initialize(limit, time_span, delay)
  raise ArgumentError, '`limit` and `time_span` have to either both be set or both be nil!' if !limit != !time_span

  @limit = limit
  @time_span = time_span
  @delay = delay

  @bucket = {}
end

Instance Method Details

#clean(rate_limit_time = nil) ⇒ Object

Cleans the bucket, removing all elements that aren't necessary anymore


23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/discordrb/commands/rate_limiter.rb', line 23

def clean(rate_limit_time = nil)
  rate_limit_time ||= Time.now

  @bucket.delete_if do |_, limit_hash|
    # Time limit has not run out
    return false if @time_span && rate_limit_time < (limit_hash[:set_time] + @time_span)

    # Delay has not run out
    return false if @delay && rate_limit_time < (limit_hash[:last_time] + @delay)

    true
  end
end

#rate_limited?(thing, rate_limit_time = nil, increment: 1) ⇒ Integer, false

Performs a rate limiting request


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/discordrb/commands/rate_limiter.rb', line 42

def rate_limited?(thing, rate_limit_time = nil, increment: 1)
  key = resolve_key thing
  limit_hash = @bucket[key]

  # First case: limit_hash doesn't exist yet
  unless limit_hash
    @bucket[key] = {
      last_time: Time.now,
      set_time: Time.now,
      count: increment
    }

    return false
  end

  # Define the time at which we're being rate limited once so it doesn't get inaccurate
  rate_limit_time ||= Time.now

  if @limit && (limit_hash[:count] + increment) > @limit
    # Second case: Count is over the limit and the time has not run out yet
    return (limit_hash[:set_time] + @time_span) - rate_limit_time if @time_span && rate_limit_time < (limit_hash[:set_time] + @time_span)

    # Third case: Count is over the limit but the time has run out
    # Don't return anything here because there may still be delay-based limiting
    limit_hash[:set_time] = rate_limit_time
    limit_hash[:count] = 0
  end

  if @delay && rate_limit_time < (limit_hash[:last_time] + @delay)
    # Fourth case: we're being delayed
    (limit_hash[:last_time] + @delay) - rate_limit_time
  else
    # Fifth case: no rate limiting at all! Increment the count, set the last_time, and return false
    limit_hash[:last_time] = rate_limit_time
    limit_hash[:count] += increment
    false
  end
end