Add rate limiting, API response caching, and fix progress bar panic

- Rate limit !weather (5/day), !concerts (10/day), fix !translate default to 20/day
- Add generic api_cache table with CacheGet/CacheSet helpers
- Cache !weather (1h), !wiki, !define, !tv (24h), !upcoming movies, !anime season/upcoming (24h)
- Fix ProgressBar panic on negative Repeat count when XP exceeds level threshold
- All rate limits configurable via RATELIMIT_* env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-03-08 19:07:48 -07:00
parent ddb196aaad
commit 2c7c15b282
8 changed files with 145 additions and 21 deletions

View File

@@ -82,6 +82,34 @@ func MarkJobCompleted(jobName, dateKey string) {
}
}
// CacheGet returns cached data for the given key if it exists and is within ttlSeconds.
// Returns empty string if not cached or expired.
func CacheGet(key string, ttlSeconds int) string {
d := Get()
var data string
err := d.QueryRow(
`SELECT data FROM api_cache WHERE cache_key = ? AND cached_at > unixepoch() - ?`,
key, ttlSeconds,
).Scan(&data)
if err != nil {
return ""
}
return data
}
// CacheSet stores data in the generic API cache.
func CacheSet(key, data string) {
d := Get()
_, err := d.Exec(
`INSERT INTO api_cache (cache_key, data, cached_at) VALUES (?, ?, unixepoch())
ON CONFLICT(cache_key) DO UPDATE SET data = ?, cached_at = unixepoch()`,
key, data, data,
)
if err != nil {
slog.Error("cache set", "key", key, "err", err)
}
}
const schema = `
-- Users & XP
CREATE TABLE IF NOT EXISTS users (
@@ -507,6 +535,13 @@ CREATE TABLE IF NOT EXISTS rate_limits (
count INTEGER DEFAULT 0,
PRIMARY KEY (user_id, action, date)
);
-- Generic API response cache
CREATE TABLE IF NOT EXISTS api_cache (
cache_key TEXT PRIMARY KEY,
data TEXT NOT NULL,
cached_at INTEGER DEFAULT (unixepoch())
);
`
// SeedSchedulerDefaults inserts default scheduler jobs if they don't exist.