Compare commits

..

No commits in common. "master" and "9c6b18af9368abbfbf0daf436010ded794dbf074" have entirely different histories.

3 changed files with 34 additions and 90 deletions

2
Cargo.lock generated
View file

@ -1463,7 +1463,7 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "titlebot"
version = "0.2.1"
version = "0.2.0"
dependencies = [
"anyhow",
"futures",

View file

@ -1,6 +1,6 @@
[package]
name = "titlebot"
version = "0.2.1"
version = "0.2.0"
authors = ["Yash Karandikar <nerdstep710@gmail.com>"]
edition = "2018"

View file

@ -1,27 +1,12 @@
use anyhow::anyhow;
use std::collections::HashMap;
use futures::prelude::*;
use htmlescape::decode_html;
use irc::client::prelude::*;
use onig::Regex;
use rspotify::{ClientCredsSpotify, Credentials};
use rspotify::model::{Id, PlayableItem};
use rspotify::prelude::BaseClient;
use rspotify::{ClientCredsSpotify, Credentials};
use std::sync::Arc;
#[allow(unused_macros)]
macro_rules! cloned {
($i:ident => $e:expr) => {{
let $i = $i.clone();
$e
}};
(($($i:ident),+) => $e:expr) => {{
$(
let $i = $i.clone();
)*
$e
}}
}
fn calculate_playtime(secs: u64) -> (u64, u64) {
let mut dur_sec = secs;
@ -30,11 +15,7 @@ fn calculate_playtime(secs: u64) -> (u64, u64) {
(dur_min, dur_sec)
}
async fn resolve_spotify(
spotify: &mut ClientCredsSpotify,
resource_type: &str,
resource_id: &str,
) -> anyhow::Result<String> {
async fn resolve_spotify(spotify: &mut ClientCredsSpotify, resource_type: &str, resource_id: &str) -> anyhow::Result<String> {
if spotify.token.as_ref().unwrap().is_expired() {
spotify.request_token().await?;
}
@ -47,27 +28,15 @@ async fn resolve_spotify(
}
"artist" => {
let artist = spotify.artist(Id::from_id(resource_id)?).await?;
Ok(format!(
"\x037[Spotify]\x03 Artist: \x039\"{}\" \x0311|\x03 Genres:\x039 {} \x0311|",
artist.name,
artist.genres.join(", ")
))
Ok(format!("\x037[Spotify]\x03 Artist: \x039\"{}\" \x0311|\x03 Genres:\x039 {} \x0311|", artist.name, artist.genres.join(", ")))
}
"album" => {
let album = spotify.album(Id::from_id(resource_id)?).await?;
let playtime = calculate_playtime(
album
.tracks
.items
.iter()
.fold(0, |acc, x| acc + x.duration.as_secs()),
);
let playtime = calculate_playtime(album.tracks.items.iter().fold(0, |acc, x| acc + x.duration.as_secs()));
Ok(format!("\x037[Spotify]\x03 Album: \x039\"{}\" \x0311|\x03 Tracks:\x0315 {} \x0311|\x03 Release date:\x039 {} \x0311|\x03 Length:\x0315 {}:{:02} \x0311|", album.name, album.tracks.total, album.release_date, playtime.0, playtime.1))
}
"playlist" => {
let playlist = spotify
.playlist(Id::from_id(resource_id)?, None, None)
.await?;
let playlist = spotify.playlist(Id::from_id(resource_id)?, None, None).await?;
let mut tracks = 0;
let playtime = calculate_playtime(playlist.tracks.items.iter().fold(0, |acc, x| {
x.track.as_ref().map_or(acc, |item| match item {
@ -83,7 +52,7 @@ async fn resolve_spotify(
}));
Ok(format!("\x037[Spotify]\x03 Playlist: \x039\"{}\" \x0311|\x03 Tracks/Episodes:\x0315 {} \x0311|\x03 Length:\x0315 {}:{:02} \x0311|\x03 Description: \x039\"{}\" \x0311|", playlist.name, tracks, playtime.0, playtime.1, playlist.description.unwrap_or_else(|| "<empty>".into())))
}
_ => Ok("\x037[Spotify]\x03 Error: Invalid resource type".into()),
_ => Ok("\x037[Spotify]\x03 Error: Invalid resource type".into())
}
}
@ -97,57 +66,57 @@ async fn main() -> anyhow::Result<()> {
}, config.nickname.as_ref().unwrap(), config.channels.join(", "), config.umodes.as_ref().unwrap());
let mut spotify = ClientCredsSpotify::new(Credentials::from_env().unwrap());
spotify.request_token().await.unwrap();
let mut client = Arc::new(Client::from_config(config).await?);
let mut client = Client::from_config(config).await?;
client.identify()?;
let vlog = match std::env::var("TITLEBOT_VERBOSE_LOG") {
Ok(o) => o.eq_ignore_ascii_case("true"),
Err(_) => false,
Err(_) => false
};
if vlog {
println!("Verbose logging enabled.");
}
println!("Connected!");
let mut stream = Arc::get_mut(&mut client).unwrap().stream()?;
let mut stream = client.stream()?;
let url_regex = Regex::new(r"https?://\w+\.\w+[/\S+]*").unwrap();
let title_regex = Arc::new(Regex::new(r"(?<=<title>)(.*)(?=</title>)").unwrap());
let title_regex = Regex::new(r"(?<=<title>)(.*)(?=</title>)").unwrap();
let spotify_regex = Regex::new(r"(?:https?|spotify):(?://open\.spotify\.com/)?(track|artist|album|playlist)[/:]([a-zA-Z0-9]*)").unwrap();
let hclient = reqwest::Client::new();
let mut cache: HashMap<String, String> = HashMap::new();
while let Some(message) = stream.next().await.transpose()? {
if vlog {
print!("[IRC] {}", message)
}
if vlog { print!("[IRC] {}", message) }
if let Command::PRIVMSG(channel, message) = message.command {
if let Some(m) = spotify_regex.captures(&message) {
let tp_group = m.pos(1).unwrap();
let id_group = m.pos(2).unwrap();
match resolve_spotify(
&mut spotify,
&message[tp_group.0..tp_group.1],
&message[id_group.0..id_group.1],
)
.await
{
match resolve_spotify(&mut spotify, &message[tp_group.0..tp_group.1], &message[id_group.0..id_group.1]).await {
Ok(o) => client.send_privmsg(&channel, o)?,
Err(e) => {
client.send_privmsg(
&channel,
"\x037[Spotify]\x03 An error has occurred.",
)?;
client.send_privmsg(&channel, "\x037[Spotify]\x03 An error has occurred.")?;
println!("[Spotify] Error! {}", e);
}
}
} else if let Some(m) = url_regex.find(&message) {
let url = message[m.0..m.1].to_string();
tokio::spawn(cloned!((client, hclient, title_regex, url) => async move {
if let Ok(title) = resolve_url(&hclient, &url, &title_regex).await {
client.send_privmsg(&channel, title).unwrap();
let url = &message[m.0..m.1];
if let Some(entry) = cache.get(&url.to_string()) {
client.send_privmsg(&channel, format!("\x039[Title]\x0311 {}", entry))?;
continue;
}
match reqwest::get(url).await {
Ok(o) => {
let body = o.text().await?;
if let Some(tm) = title_regex.find(&body) {
let title_match = &body[tm.0..tm.1];
let result = decode_html(title_match).unwrap_or_else(|_| title_match.to_string());
client.send_privmsg(&channel, format!("\x039[Title]\x0311 {}", result))?;
cache.insert(url.to_string(), result.to_string());
}
}
}));
Err(e) => println!("[Title] Error! {}", e)
}
}
}
}
@ -156,28 +125,3 @@ async fn main() -> anyhow::Result<()> {
}
Ok(())
}
async fn resolve_url(
hclient: &reqwest::Client,
url: &str,
title_regex: &Regex,
) -> anyhow::Result<String> {
let o = hclient.head(url).send().await?;
let headers = o.headers();
let ctype = headers
.get("Content-Type")
.ok_or(anyhow!("Content-Type not set"))?;
let ctype = ctype.to_str()?;
if !ctype.starts_with("text/html") {
return Err(anyhow!("Incorrect Content-Type"));
}
let o = hclient.get(url).send().await?;
let body = o.text().await?;
if let Some(tm) = title_regex.find(&body) {
let title_match = &body[tm.0..tm.1];
let result = decode_html(title_match).unwrap_or_else(|_| title_match.to_string());
return Ok(format!("\x039[Title]\x0311 {}", result));
} else {
return Ok(String::new());
}
}