mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-10-06 01:22:16 +00:00
fix: harden the debug command, link cache and rate limiter
- commands: /bot_dict dumped the whole chat state to any member of the chat and could exceed Telegram's 4096-char message limit (the send then failed and bubbled up as a handler error). It is now admin-only and capped at MAX_DEBUG_DUMP_CHARS; README, README.en and the /help description updated. - send: the edit-before-forward template buttons were built from a HashMap walk, so their order changed between prompts. Now sorted by name. - link_cache: an unparseable payload (older schema) was reported as a miss but left in place, re-failing the parse on every later hit; the row is dropped on read. - handlers: a link handed to the URL workers after the channel closed (shutdown) was discarded silently; it is now logged. - rate_limit: LIMITERS kept one bucket per chat that ever sent media, forever. The periodic sweep now drops buckets that are idle (refilled to capacity) and not held by an in-flight sender; acquire's refill was factored into a shared helper used by the idle check. Tests: +3 (corrupted row dropped, sorted markup, idle-bucket pruning); the cache one was verified to fail before the fix. fmt/clippy clean, 55 + 69.
This commit is contained in:
@@ -78,9 +78,15 @@ impl LinkCache {
|
||||
conn.execute("DELETE FROM link_cache WHERE url = ?1", params![key])?;
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(serde_json::from_str::<CachedPost>(&payload).map_err(
|
||||
|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)),
|
||||
)?))
|
||||
match serde_json::from_str::<CachedPost>(&payload) {
|
||||
Ok(post) => Ok(Some(post)),
|
||||
Err(e) => {
|
||||
// Unreadable payload (e.g. an older schema): drop it
|
||||
// instead of re-failing the parse on every later hit.
|
||||
conn.execute("DELETE FROM link_cache WHERE url = ?1", params![key])?;
|
||||
Err(rusqlite::Error::ToSqlConversionFailure(Box::new(e)))
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
match result {
|
||||
@@ -228,6 +234,32 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unreadable_entry_is_dropped_on_read() {
|
||||
// A payload from an older schema must not be re-parsed on every hit:
|
||||
// the row is removed and the read reports a miss.
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let db_path = dir.path().join("c.db");
|
||||
let cache = LinkCache::new(crate::db::open_store(db_path.to_str().unwrap()).unwrap());
|
||||
{
|
||||
let conn = rusqlite::Connection::open(&db_path).unwrap();
|
||||
conn.execute(
|
||||
"INSERT INTO link_cache (url, payload, created_at) VALUES (?1, ?2, ?3)",
|
||||
params!["twitter:1", "{not json", now_f64()],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert!(
|
||||
cache
|
||||
.get("twitter:1", Duration::from_secs(3600))
|
||||
.await
|
||||
.is_none()
|
||||
);
|
||||
// Dropped, not left behind for the next hit.
|
||||
assert_eq!(cache.clear(None).await, 0, "corrupted row still present");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_and_prune() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user