Add music link translator
This commit is contained in:
parent
68711f9ee6
commit
0639effde1
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -640,6 +640,15 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@ -1084,22 +1093,23 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fren"
|
||||
version = "2.6.1"
|
||||
version = "2.7.0"
|
||||
dependencies = [
|
||||
"axum 0.8.1",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"chrono-tz 0.10.1",
|
||||
"config",
|
||||
"convert_case 0.10.0",
|
||||
"cta-api",
|
||||
"emojis",
|
||||
"j_db",
|
||||
@ -1127,6 +1137,7 @@ dependencies = [
|
||||
"tonic",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1830,9 +1841,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
@ -2437,9 +2448,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
@ -4928,9 +4939,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "fren"
|
||||
version = "2.6.1"
|
||||
version = "2.7.0"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@ -35,6 +35,8 @@ thiserror = "2.0.12"
|
||||
tracing-core = "0.1.33"
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
thousands = "0.2.0"
|
||||
url = { version = "2.5.7", features = ["serde"] }
|
||||
convert_case = "0.10.0"
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.35.1"
|
||||
|
||||
@ -48,6 +48,8 @@ pub struct BotConfig {
|
||||
pub raas_server: String,
|
||||
pub cta_key: String,
|
||||
pub omdb_key: String,
|
||||
pub music_streaming_site_urls: Vec<String>,
|
||||
pub music_streaming_sites: Vec<String>,
|
||||
pub picox: PicOxConfig,
|
||||
}
|
||||
|
||||
|
||||
@ -75,11 +75,7 @@ pub async fn buy_improvement(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
User::try_take_funds(
|
||||
&ctx.data().db,
|
||||
ctx.author().id,
|
||||
amount,
|
||||
)?;
|
||||
User::try_take_funds(&ctx.data().db, ctx.author().id, amount)?;
|
||||
|
||||
let (has_improved, _) =
|
||||
Improvements::contribute_to_improvement(&ctx.data().db, improvement_type, amount as u64)?;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use rand::seq::IteratorRandom;
|
||||
use std::str::FromStr;
|
||||
mod admin;
|
||||
mod album;
|
||||
mod birthday;
|
||||
@ -24,14 +25,19 @@ use crate::error::Error;
|
||||
use crate::event_listener::{Listener, TriggerEvent, TriggerType};
|
||||
use crate::models::social_credit::SocialCreditPhrase;
|
||||
use crate::models::task::Task;
|
||||
use crate::music::songlink::get_song_urls;
|
||||
use crate::user::{User, UserRole};
|
||||
use log::{debug, error, info};
|
||||
use poise::serenity_prelude::{GuildId, Http, Message, MessageBuilder, ReactionType, RoleId};
|
||||
use poise::serenity_prelude::{
|
||||
CreateMessage, GuildId, Http, Message, MessageBuilder, ReactionType, RoleId,
|
||||
};
|
||||
use poise::{FrameworkOptions, find_command, serenity_prelude as serenity};
|
||||
use rand::{Rng, rng};
|
||||
use regex::Regex;
|
||||
use songbird::SerenityInit;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
pub type Context<'a> = poise::Context<'a, Arc<GlobalData>, Error>;
|
||||
|
||||
@ -75,6 +81,42 @@ async fn event_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_message_for_music_link(
|
||||
ctx: &serenity::Context,
|
||||
data: &Arc<GlobalData>,
|
||||
new_message: &Message,
|
||||
) -> Result<(), Error> {
|
||||
let re = Regex::new(r"(?:https?://.)?(?:www\.)?[-a-zA-Z0-9@%._+~#=]{2,256}\.[a-z]{2,6}\b[-a-zA-Z0-9@:%_+.~#?&/=]*").unwrap();
|
||||
|
||||
if let Some(url_match) = re.find(&new_message.content) {
|
||||
let url = url_match.as_str();
|
||||
if data
|
||||
.cfg
|
||||
.music_streaming_site_urls
|
||||
.iter()
|
||||
.any(|site| url.contains(site))
|
||||
{
|
||||
match get_song_urls(Url::from_str(url).unwrap()).await {
|
||||
Ok(resp) => {
|
||||
new_message
|
||||
.channel_id
|
||||
.send_message(
|
||||
&ctx.http,
|
||||
CreateMessage::default()
|
||||
.embed(resp.to_discord_message(&data.cfg.music_streaming_sites)),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to handle music link: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_message(
|
||||
ctx: &serenity::Context,
|
||||
data: &Arc<GlobalData>,
|
||||
@ -151,6 +193,8 @@ async fn handle_message(
|
||||
}
|
||||
}
|
||||
|
||||
check_message_for_music_link(ctx, data, new_message).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ mod image_manipulation;
|
||||
mod inventory;
|
||||
mod migrations;
|
||||
mod models;
|
||||
mod music;
|
||||
mod user;
|
||||
|
||||
use crate::config::{Args, BotConfig, GlobalData};
|
||||
|
||||
1
src/music/mod.rs
Normal file
1
src/music/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod songlink;
|
||||
93
src/music/songlink.rs
Normal file
93
src/music/songlink.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use poise::serenity_prelude::{Color, CreateEmbed, MessageBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
const API_URL: &str = "https://api.song.link/v1-alpha.1/links";
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct SonglinkRequest {
|
||||
url: Url,
|
||||
#[serde(rename = "songIfSingle")]
|
||||
song_if_single: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Hash, Debug)]
|
||||
pub struct Platform {
|
||||
url: String,
|
||||
#[serde(rename = "nativeAppUriMobile", default)]
|
||||
native_app_uri_mobile: Option<String>,
|
||||
#[serde(rename = "nativeAppUriDesktop", default)]
|
||||
native_app_uri_desktop: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Hash, Debug)]
|
||||
pub struct Entity {
|
||||
title: String,
|
||||
#[serde(rename = "artistName", default)]
|
||||
artist_name: String,
|
||||
#[serde(rename = "thumbnailUrl", default)]
|
||||
thumbnail_url: String,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct SonglinkResponse {
|
||||
#[serde(rename = "entityUniqueId")]
|
||||
pub entity_unique_id: String,
|
||||
#[serde(rename = "userCountry")]
|
||||
pub user_country: String,
|
||||
#[serde(rename = "linksByPlatform")]
|
||||
pub links_by_platform: HashMap<String, Platform>,
|
||||
#[serde(rename = "entitiesByUniqueId")]
|
||||
pub entities_by_unique_id: HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
impl SonglinkResponse {
|
||||
pub fn to_discord_message(&self, platforms: &[String]) -> CreateEmbed {
|
||||
let embed = if let Some((_id, entity)) = self.entities_by_unique_id.iter().next() {
|
||||
CreateEmbed::new()
|
||||
.title(entity.title.clone())
|
||||
.field("Artist:", entity.artist_name.clone(), true)
|
||||
.thumbnail(entity.thumbnail_url.clone())
|
||||
.color(Color::from_rgb(0xf2, 0x55, 0x43))
|
||||
} else {
|
||||
CreateEmbed::new()
|
||||
.title("Alternative Links")
|
||||
.color(Color::from_rgb(0xf2, 0x55, 0x43))
|
||||
};
|
||||
|
||||
let mut msg_builder = MessageBuilder::new();
|
||||
for (platform, link) in &self.links_by_platform {
|
||||
let platform_search_name = platform.to_lowercase();
|
||||
|
||||
if platforms.contains(&platform_search_name) {
|
||||
msg_builder.push_line(format!(
|
||||
"* [{}]({})",
|
||||
platform.to_case(Case::Title),
|
||||
link.url
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
embed.field("Available At", msg_builder.build(), false)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_song_urls(url: Url) -> Result<SonglinkResponse, reqwest::Error> {
|
||||
let request = SonglinkRequest {
|
||||
url,
|
||||
song_if_single: true,
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let resp = client
|
||||
.execute(client.get(API_URL).query(&request).build()?)
|
||||
.await?;
|
||||
|
||||
let resp: SonglinkResponse = resp.json().await?;
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user