From fbd93fcefee470f7696c28037913a96043cac348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mano=20S=C3=A9gransan?= Date: Fri, 21 Jun 2024 12:45:09 +0200 Subject: [PATCH] Correctly handle paginated response for twitch user emotes --- src/emotes/downloader.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/emotes/downloader.rs b/src/emotes/downloader.rs index a64cd3ca..dcfdc4e9 100644 --- a/src/emotes/downloader.rs +++ b/src/emotes/downloader.rs @@ -30,10 +30,16 @@ mod twitch { theme_mode: Vec, } + #[derive(Deserialize, Debug)] + struct Cursor { + cursor: Option, + } + #[derive(Deserialize, Debug)] struct EmoteList { data: Vec, template: String, + pagination: Cursor, } fn parse_emote_list(v: EmoteList) -> EmoteMap { @@ -76,28 +82,31 @@ mod twitch { .collect() } - pub async fn get_global_emotes(client: &Client) -> Result { - let global_emotes = client - .get("https://api.twitch.tv/helix/chat/emotes/global") + // Twitch will not send all the emotes in one response, we use the cursor they return to query further emotes. + pub async fn get_user_emotes(client: &Client, user_id: &str) -> Result { + let mut user_emotes = client + .get(format!( + "https://api.twitch.tv/helix/chat/emotes/user?user_id={user_id}", + )) .send() .await? - .error_for_status()? + .error_for_status().map_err(|e| { warn!("Unable to get user emotes, please verify that the access token includes the user:read:emotes scope."); e})? .json::() .await?; - Ok(parse_emote_list(global_emotes)) - } - - pub async fn get_user_emotes(client: &Client, user_id: &str) -> Result { - let user_emotes = client + while let Some(c) = user_emotes.pagination.cursor { + let emotes = client .get(format!( - "https://api.twitch.tv/helix/chat/emotes/user?user_id={user_id}", + "https://api.twitch.tv/helix/chat/emotes/user?user_id={user_id}&after={c}", )) .send() .await? .error_for_status().map_err(|e| { warn!("Unable to get user emotes, please verify that the access token includes the user:read:emotes scope."); e})? - .json::() - .await?; + .json::().await?; + + user_emotes.pagination = emotes.pagination; + user_emotes.data.extend(emotes.data); + } Ok(parse_emote_list(user_emotes)) } @@ -426,13 +435,11 @@ pub async fn get_emotes( HashMap::default() }; - let twitch_get_global_emotes = || twitch::get_global_emotes(&twitch_client); - // Concurrently get the list of emotes for each provider let global_emotes = futures::stream::iter(enabled_emotes.into_iter().map(|emote_provider| async move { match emote_provider { - EmoteProvider::Twitch => twitch_get_global_emotes().await, + EmoteProvider::Twitch => Ok(HashMap::new()), EmoteProvider::BetterTTV => betterttv::get_emotes(channel_id).await, EmoteProvider::SevenTV => seventv::get_emotes(channel_id).await, EmoteProvider::FrankerFaceZ => frankerfacez::get_emotes(channel_id).await,