Add persisted quota/cost guards for X and Twilio#15
Merged
Conversation
New RateLimitTracker: a small, file-persisted fixed-window counter backing two new settings, XSettings.MaxPostsPerMonth (default 500, matching X's free tier -- rolling 30-day window, x_post_count.txt) and TwilioSettings.MaxSmsPerDay (default 100, counted per recipient -- rolling 24-hour window, twilio_sms_count.txt). Once a limit is reached, further posts/sends are skipped and logged rather than burning requests X would reject anyway or racking up SMS costs during a busy severe weather outbreak. Set either to 0 to disable. Deliberately hand-rolled rather than wrapping System.Threading.RateLimiting's built-in limiters: this bot redeploys on every push to master (deploy.yml), and the BCL limiters are in-memory only with no way to reconstruct state from external storage -- an in-memory-only counter would reset on every deploy, making a monthly/daily cap nearly meaningless in this project's specific deployment pattern. 6 new unit tests covering limit enforcement, zero-means-unlimited, window rollover, persistence across new instances (the actual point of the design), and missing-state-file startup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements NuGet opportunity #3 from the earlier full codebase review: nothing currently caps posting volume against X's 500/month free-tier limit or Twilio's per-SMS cost during a busy severe-weather outbreak.
Services/RateLimitTracker.cs: a small, file-persisted fixed-window counter.XSettings.MaxPostsPerMonth(default500, matching the free tier) — rolling 30-day window,x_post_count.txt.TwilioSettings.MaxSmsPerDay(default100, counted per recipient) — rolling 24-hour window,twilio_sms_count.txt.X: Monthly post quota reached.../Twilio: Daily SMS cost guard reached...) instead of burning requests X would reject anyway or racking up SMS costs. Set either to0to disable.Why not
System.Threading.RateLimiting(the BCL library originally suggested): this bot redeploys on every push to master (deploy.yml), and the BCL's built-in limiters (e.g.FixedWindowRateLimiter) are in-memory only with no way to reconstruct their state from external storage. An in-memory-only counter would reset on every deploy, making a monthly/daily cap nearly meaningless in this project's specific deployment pattern — confirmed with the user before implementing.RateLimitTrackerpersists window-start + count to a small state file instead, mirroring the existingposted_alerts.txt/confirmed_platforms.txtconvention.6 new unit tests: limit enforcement, zero-means-unlimited, window rollover, persistence across new instances (the actual point of the design), missing-state-file startup, and
GetStatusnot itself consuming the counter.Test plan
dotnet build NwsAlertBot.sln— 0 warnings/errorsdotnet test NwsAlertBot.Tests/NwsAlertBot.Tests.csproj— 70/70 passing (64 existing + 6 new)x_post_count.txt/twilio_sms_count.txtget created correctly in the deployed working directory and survive a real restart