moover_rust/src/commands/voice/radio/radio_utils.rs

144 lines
4.7 KiB
Rust
Raw Normal View History

2025-01-26 20:45:24 +00:00
use poise::CreateReply;
use radiobrowser::{ApiStation, StationSearchBuilder};
use regex::Regex;
use serenity::all::{CreateActionRow, CreateButton, CreateEmbed, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage};
2025-01-24 20:52:13 +00:00
2025-01-26 20:45:24 +00:00
use crate::{commands::voice_types::NumberOfEntries, types::Context};
2025-01-24 20:52:13 +00:00
2025-01-26 20:45:24 +00:00
pub async fn paginate_search_stations(
ctx: &Context<'_>,
search_builder: &StationSearchBuilder,
limit: NumberOfEntries
2025-01-24 20:52:13 +00:00
) -> Result<(), serenity::Error> {
// Define some unique identifiers for the navigation buttons
let ctx_id = ctx.id();
let prev_button_id = format!("{}prev", ctx_id);
let next_button_id = format!("{}next", ctx_id);
2025-01-26 20:45:24 +00:00
let search_builder = search_builder;
let Ok(stations) = search_builder.clone().send().await else {
ctx.reply("Something went wrong, try searching again").await?;
2025-01-24 20:52:13 +00:00
return Ok(())
};
2025-01-26 20:45:24 +00:00
let mut page = 0;
let embed = create_station_list_embed(&stations, page);
2025-01-24 20:52:13 +00:00
// Send the embed with the first page as content
let reply = {
let components = CreateActionRow::Buttons(vec![
CreateButton::new(&prev_button_id).emoji('◀'),
CreateButton::new(&next_button_id).emoji('▶'),
2025-01-26 20:45:24 +00:00
]);
CreateReply::default()
.embed(embed)
2025-01-24 20:52:13 +00:00
.components(vec![components])
2025-01-26 20:45:24 +00:00
};
ctx.send(reply).await?;
// Loop through incoming interactions with the navigation buttons
let mut offset = 0;
let limit_int = limit as u32;
while let Some(press) = serenity::collector::ComponentInteractionCollector::new(ctx)
2025-01-24 20:52:13 +00:00
// We defined our button IDs to start with `ctx_id`. If they don't, some other command's
// button was pressed
.filter(move |press| press.data.custom_id.starts_with(&ctx_id.to_string()))
// Timeout when no navigation button has been pressed for 24 hours
.timeout(std::time::Duration::from_secs(3600 * 24))
.await
2025-01-26 20:45:24 +00:00
{
// Depending on which button was pressed, go to next or previous page
2025-01-24 20:52:13 +00:00
if press.data.custom_id == next_button_id {
2025-01-26 20:45:24 +00:00
offset += limit_int;
page += 1;
2025-01-24 20:52:13 +00:00
} else if press.data.custom_id == prev_button_id {
2025-01-26 20:45:24 +00:00
offset = if offset < limit_int { offset } else { offset - limit_int };
page = if page == 0 { 0 } else { page - 1 };
2025-01-24 20:52:13 +00:00
} else {
// This is an unrelated button interaction
continue;
}
2025-01-26 20:45:24 +00:00
let Ok(mut stations) = search_builder.clone().offset(offset.to_string()).send().await else {
ctx.reply("Something went wrong, try searching again").await?;
return Ok(())
};
if stations.is_empty() {
offset = 0;
page = 0;
let Ok(new_stations) = search_builder.clone().offset(offset.to_string()).send().await else {
ctx.reply("Something went wrong, try searching again").await?;
return Ok(())
};
stations = new_stations;
}
let embed = create_station_list_embed(&stations, page);
2025-01-24 20:52:13 +00:00
// Update the message with the new page contents
press
2025-01-26 20:45:24 +00:00
.create_response(
ctx.serenity_context(),
CreateInteractionResponse::UpdateMessage(
CreateInteractionResponseMessage::new()
.embed(embed),
),
)
.await?;
}
Ok(())
}
fn create_station_list_embed(stations: &Vec<ApiStation>, page: u32) -> CreateEmbed {
let result = CreateEmbed::new()
.fields(
stations.iter().map(|station| {
(
station.name.clone(),
format!("Country: {} Stream: {}",
station.country, station.url),
false
)
})
)
.footer(CreateEmbedFooter::new(format!("Page: {}", page + 1)));
result
}
pub enum LinkString {
Link,
String
}
pub fn link_or_string(haystack: &str) -> LinkString {
let Ok(re) = Regex::new(r"^https?://([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$") else {
panic!("Wrong regex expression!");
};
return if re.is_match(haystack) { LinkString::Link } else { LinkString::String }
}
pub fn parse_radio_autocomplete(haystack: &str) -> Option<(String, String, String)> {
let Ok(re) = Regex::new(r"^Name: (.*) Country: (.*) Language: (.*)") else {
panic!("Wrong regex expression!");
};
let Some(captures) = re.captures(haystack) else {
return None
};
2025-01-24 20:52:13 +00:00
2025-01-26 20:45:24 +00:00
return Some((
String::from(&captures[1]),
String::from(&captures[2]),
String::from(&captures[3])
))
2025-01-24 20:52:13 +00:00
}