2025-01-06 11:51:57 +00:00
|
|
|
use std::vec;
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
use poise::ChoiceParameter;
|
2025-01-06 11:51:57 +00:00
|
|
|
use reqwest::Client;
|
|
|
|
|
|
|
|
use songbird::input::Input;
|
|
|
|
use songbird::input::HttpRequest;
|
|
|
|
|
2025-01-24 20:52:13 +00:00
|
|
|
use super::super::connect;
|
2025-01-26 20:45:24 +00:00
|
|
|
use super::link_or_string;
|
|
|
|
use super::LinkString;
|
2025-01-26 21:47:06 +00:00
|
|
|
use super::parse_radio_autocomplete;
|
2025-01-26 20:45:24 +00:00
|
|
|
use super::radio_utils::paginate_search_stations;
|
2025-01-24 20:52:13 +00:00
|
|
|
use super::super::voice_utils::MAX_ENTRIES;
|
|
|
|
|
2025-01-26 21:47:06 +00:00
|
|
|
use crate::commands::voice_types::{NumberOfEntries, PlayingInfoType};
|
|
|
|
use crate::types::{Context, Error, ContextExt};
|
|
|
|
use crate::commands::voice::voice_utils::autocomplete_channel;
|
|
|
|
|
2025-01-24 20:52:13 +00:00
|
|
|
#[poise::command(
|
|
|
|
slash_command,
|
|
|
|
description_localized("en-US", "Plays music from URL source"),
|
|
|
|
subcommands("search", "play")
|
|
|
|
)]
|
|
|
|
pub async fn radio(_ctx: Context<'_>) -> Result<(), Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2025-01-06 11:51:57 +00:00
|
|
|
|
|
|
|
#[poise::command(
|
|
|
|
slash_command,
|
|
|
|
description_localized("en-US", "Plays music from URL source")
|
|
|
|
)]
|
2025-01-24 20:52:13 +00:00
|
|
|
pub async fn play(ctx: Context<'_>,
|
2025-01-06 11:51:57 +00:00
|
|
|
#[autocomplete = "autocomplete_channel"]
|
|
|
|
#[description = "Voice channel name: "]
|
|
|
|
channel: Option<String>,
|
2025-01-24 20:52:13 +00:00
|
|
|
#[autocomplete = "autocomplete_radio"]
|
|
|
|
#[description = "Radio station: "]
|
|
|
|
name: String,
|
2025-01-06 11:51:57 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
|
|
|
if ctx.guild().is_none() {
|
|
|
|
ctx.reply_ephemeral("Can't use this outside of guild").await?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
let api = &ctx.data().radio_browser;
|
|
|
|
|
|
|
|
let stations_result = match link_or_string(&name) {
|
|
|
|
LinkString::Link => {
|
|
|
|
if name.ends_with(".m3u") {
|
|
|
|
ctx.reply_ephemeral("Sorry, currently I don't support m3u files").await?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
api.get_stations().byurl(name).await
|
|
|
|
},
|
|
|
|
LinkString::String => {
|
|
|
|
let (name, country, language) = match parse_radio_autocomplete(&name) {
|
|
|
|
Some(tuple) => tuple,
|
|
|
|
None => {
|
|
|
|
ctx.reply_ephemeral("Please use either direct URL or provided autocomplete").await?;
|
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
api.get_stations()
|
|
|
|
.name_exact(true)
|
|
|
|
.name(name)
|
|
|
|
.country_exact(true)
|
|
|
|
.country(country)
|
|
|
|
.language_exact(true)
|
|
|
|
.language(language)
|
|
|
|
.send().await
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let Ok(stations) = stations_result else {
|
|
|
|
ctx.reply_ephemeral("There was an error with radio API!").await?;
|
|
|
|
return Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
let Some(station) = stations.first() else {
|
|
|
|
ctx.reply_ephemeral("Radio station not found!").await?;
|
|
|
|
return Ok(())
|
|
|
|
};
|
|
|
|
|
2025-01-06 11:51:57 +00:00
|
|
|
let manager = songbird::get(ctx.serenity_context())
|
|
|
|
.await
|
|
|
|
.expect("Songbird Voice client placed in at initialisation.")
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
let Some(guild_id) = ctx.guild_id() else {
|
|
|
|
ctx.reply_ephemeral("Guild id not found").await?;
|
|
|
|
return Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
if manager.get(guild_id).is_none() {
|
2025-01-26 20:45:24 +00:00
|
|
|
if let Err(e) = connect(&ctx, guild_id, channel).await {
|
2025-01-24 20:52:13 +00:00
|
|
|
ctx.reply_ephemeral(&e.to_string()).await?;
|
2025-01-26 20:45:24 +00:00
|
|
|
println!("SONGBIRD MANAGER ERROR: {}", e.to_string());
|
2025-01-24 20:52:13 +00:00
|
|
|
return Ok(())
|
2025-01-06 11:51:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(handler_lock) = manager.get(guild_id) {
|
|
|
|
let mut handler = handler_lock.lock().await;
|
|
|
|
|
|
|
|
let client = Client::new();
|
2025-01-26 20:45:24 +00:00
|
|
|
let request = HttpRequest::new(client, station.url.clone());
|
2025-01-06 11:51:57 +00:00
|
|
|
let input = Input::from(request);
|
|
|
|
handler.play_only_input(input);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ctx.reply_ephemeral("Not in a voice channel").await?;
|
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
{
|
|
|
|
let mut hash_map = ctx.data().playing_info.lock().await;
|
|
|
|
hash_map.insert(guild_id, PlayingInfoType::RadioInfo(station.clone()));
|
|
|
|
}
|
|
|
|
|
2025-01-06 11:51:57 +00:00
|
|
|
ctx.reply_ephemeral("Done!").await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2025-01-24 20:52:13 +00:00
|
|
|
|
|
|
|
async fn autocomplete_radio(
|
|
|
|
ctx: Context<'_>,
|
|
|
|
partial: &str
|
|
|
|
) -> Vec<String> {
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
let api = &ctx.data().radio_browser;
|
2025-01-24 20:52:13 +00:00
|
|
|
|
|
|
|
let stations: Vec<String> = match api.get_stations()
|
|
|
|
.limit(MAX_ENTRIES)
|
|
|
|
.name(partial)
|
|
|
|
.hidebroken(true)
|
|
|
|
.send().await {
|
|
|
|
Ok(stations) => {
|
2025-01-26 20:45:24 +00:00
|
|
|
stations.iter().map(|station: &radiobrowser::ApiStation|
|
|
|
|
format!("Name: {} Country: {} Language: {}", station.name, station.country, station.language)
|
2025-01-24 20:52:13 +00:00
|
|
|
).collect()
|
|
|
|
},
|
|
|
|
Err(_) => return vec!["".to_owned()]
|
|
|
|
};
|
|
|
|
|
|
|
|
return stations
|
|
|
|
}
|
|
|
|
|
|
|
|
#[poise::command(
|
|
|
|
slash_command,
|
|
|
|
description_localized("en-US", "Search for a radio")
|
|
|
|
)]
|
|
|
|
pub async fn search(ctx: Context<'_>,
|
|
|
|
#[description = "Radio station: "]
|
|
|
|
name: String,
|
|
|
|
#[description = "Country: "]
|
|
|
|
country: Option<String>,
|
|
|
|
#[description = "Tag: "]
|
2025-01-26 20:45:24 +00:00
|
|
|
tag: Option<String>,
|
|
|
|
#[description = "Number of entries on page: "]
|
|
|
|
entries: Option<NumberOfEntries>
|
2025-01-24 20:52:13 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
let api = &ctx.data().radio_browser;
|
|
|
|
|
|
|
|
let entries = entries.unwrap_or(NumberOfEntries::Ten);
|
2025-01-24 20:52:13 +00:00
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
let search_builder = api.get_stations()
|
|
|
|
.limit(entries.name())
|
2025-01-24 20:52:13 +00:00
|
|
|
.name(name)
|
|
|
|
.tag(tag.unwrap_or("".to_owned()))
|
|
|
|
.country(country.unwrap_or("".to_owned()))
|
|
|
|
.hidebroken(true);
|
|
|
|
|
2025-01-26 20:45:24 +00:00
|
|
|
paginate_search_stations(&ctx, &search_builder, entries).await?;
|
2025-01-24 20:52:13 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|