class BBS::RateLimit

At most limit events per per seconds for each key (a caller, an address, a whole board), over a sliding window; thread-safe.

TIPS = BBS::RateLimit.new(limit: 3, per: 3600)
if TIPS.allow?(BBS::RateLimit.caller_key(session)) then save(tip)
else say("Again in #{TIPS.wait(key).ceil} seconds.")
end

Several limits at once (a caller’s and the whole board’s) are checked with ::allow_all?, which counts the event in every limit or in none. room? and record check and count apart, for an event counted only when it went wrong (a failed login):

return locked_out unless LOGINS.room?(key)
LOGINS.record(key) unless password_ok?

By default the events live in memory and a restart forgets them; with a BBS::SQLite store they are kept in its rate_limit_events table under the limit’s name, so a lockout outlasts a restart. Keys that have been quiet for per seconds are forgotten either way.