Skip to content

Add persisted quota/cost guards for X and Twilio#15

Merged
MikeWills merged 1 commit into
masterfrom
feature/rate-limit-guards
Jul 8, 2026
Merged

Add persisted quota/cost guards for X and Twilio#15
MikeWills merged 1 commit into
masterfrom
feature/rate-limit-guards

Conversation

@MikeWills

Copy link
Copy Markdown
Owner

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.

  • New Services/RateLimitTracker.cs: a small, file-persisted fixed-window counter.
  • XSettings.MaxPostsPerMonth (default 500, matching the free tier) — rolling 30-day window, x_post_count.txt.
  • 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 (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 to 0 to 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. RateLimitTracker persists window-start + count to a small state file instead, mirroring the existing posted_alerts.txt/confirmed_platforms.txt convention.

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 GetStatus not itself consuming the counter.

Test plan

  • dotnet build NwsAlertBot.sln — 0 warnings/errors
  • dotnet test NwsAlertBot.Tests/NwsAlertBot.Tests.csproj — 70/70 passing (64 existing + 6 new)
  • Confirm x_post_count.txt/twilio_sms_count.txt get created correctly in the deployed working directory and survive a real restart

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.
@MikeWills
MikeWills merged commit 3e96488 into master Jul 8, 2026
1 check passed
@MikeWills
MikeWills deleted the feature/rate-limit-guards branch July 8, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant