mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-10-06 01:22:16 +00:00
refactor(x-media): drop the per-site fns the Site defaults already cover
twitter, bsky, misskey and bilibili each carried enabled() -> true, media_headers(url) -> None and is_retryable(err) -> the trait's own default, with no caller outside their tests (the adapters never override those methods, so the default was already the production policy). The tests that only restated the default are gone; the two that pin site-specific classes (bsky's MediaPrep, bilibili's risk-control codes) now ask the Site impl, and site/mod.rs keeps one assertion of the shared retry policy. The live verification notes (no Referer needed for hdslb/twimg) survive as comments.
This commit is contained in:
@@ -30,8 +30,12 @@ pub static PATTERN: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"^(?:https?://)?(?:www\.|mobile\.)?(?:x|twitter|fixvx|vxtwitter|fixupx|fxtwitter)\.com/[^.]+/status/(\d+)").unwrap()
|
||||
});
|
||||
|
||||
pub fn enabled() -> bool {
|
||||
true
|
||||
/// Cache key for a twitter URL: `"twitter:<id>"`. The prefix is the site id
|
||||
/// used for caption-format lookup and link-cache keys.
|
||||
pub fn cache_key(url: &str) -> Option<String> {
|
||||
PATTERN
|
||||
.captures(url)
|
||||
.map(|caps| format!("twitter:{}", &caps[1]))
|
||||
}
|
||||
|
||||
pub async fn fetch_from_url(url: &str) -> Result<Fetched, FetchError> {
|
||||
@@ -68,26 +72,6 @@ pub async fn fetch_from_url(url: &str) -> Result<Fetched, FetchError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cache key for a twitter URL: `"twitter:<id>"`. The prefix is the site id
|
||||
/// used for caption-format lookup and link-cache keys.
|
||||
pub fn cache_key(url: &str) -> Option<String> {
|
||||
PATTERN
|
||||
.captures(url)
|
||||
.map(|caps| format!("twitter:{}", &caps[1]))
|
||||
}
|
||||
|
||||
/// Twitter's fetch-retry policy: transient classes only. Not-found, blocked,
|
||||
/// sensitive (NSFW withholding) and parse failures are permanent — retrying
|
||||
/// them only wastes attempts against the syndication endpoint.
|
||||
pub fn is_retryable(err: &FetchError) -> bool {
|
||||
matches!(err, FetchError::Http(_) | FetchError::Transient(_))
|
||||
}
|
||||
|
||||
/// twimg URLs need no extra headers (no hotlink protection).
|
||||
pub fn media_headers(_url: &str) -> Option<Vec<(&'static str, String)>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Fetches a tweet from the syndication endpoint. Deleted/blocked tweets
|
||||
/// surface as `FetchError::NotFound`; withheld content (empty tombstone,
|
||||
/// age-restricted) as `FetchError::Sensitive`.
|
||||
@@ -462,20 +446,6 @@ mod tests {
|
||||
assert_eq!(cache_key("https://example.com/1"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_retryable_classifies_transient_and_permanent() {
|
||||
// Transient: network errors and explicit transient statuses (the
|
||||
// `Http` arm shares this match arm with `Transient`).
|
||||
assert!(is_retryable(&FetchError::Transient("429".into())));
|
||||
// Permanent: gone, blocked, withheld, oversized, unparseable.
|
||||
assert!(!is_retryable(&FetchError::NotFound));
|
||||
assert!(!is_retryable(&FetchError::Blocked));
|
||||
assert!(!is_retryable(&FetchError::Sensitive));
|
||||
assert!(!is_retryable(&FetchError::TooLarge));
|
||||
let json_err = serde_json::from_str::<serde_json::Value>("x").unwrap_err();
|
||||
assert!(!is_retryable(&FetchError::Json(json_err)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syndication_json_converts_to_fetched() {
|
||||
let raw = fixture(serde_json::json!([
|
||||
|
||||
@@ -2,6 +2,4 @@ mod auth;
|
||||
mod interface;
|
||||
mod model;
|
||||
|
||||
pub use interface::{
|
||||
PATTERN, Tweet, TwitterSite, cache_key, enabled, fetch_from_url, is_retryable, media_headers,
|
||||
};
|
||||
pub use interface::{PATTERN, Tweet, TwitterSite, cache_key, fetch_from_url};
|
||||
|
||||
Reference in New Issue
Block a user