Files
scootaloo/src/mastodon.rs
2022-04-24 09:39:29 +02:00

169 lines
5.0 KiB
Rust

// auto imports
use crate::config::MastodonConfig;
// std
use std::{
borrow::Cow,
error::Error,
collections::HashMap,
io::stdin,
};
// htmlescape
use htmlescape::decode_html;
// egg-mode
use egg_mode::{
tweet::Tweet,
entities::{UrlEntity, MentionEntity},
};
// elefren
use elefren::{
prelude::*,
apps::App,
scopes::Scopes,
};
/// Decodes the Twitter mention to something that will make sense once Twitter has joined the
/// Fediverse
fn twitter_mentions(ums: &Vec<MentionEntity>) -> HashMap<String, String> {
let mut decoded_mentions = HashMap::new();
for um in ums {
decoded_mentions.insert(format!("@{}", um.screen_name), format!("@{}@twitter.com", um.screen_name));
}
decoded_mentions
}
/// Decodes urls from UrlEntities
fn decode_urls(urls: &Vec<UrlEntity>) -> HashMap<String, String> {
let mut decoded_urls = HashMap::new();
for url in urls {
if url.expanded_url.is_some() {
// unwrap is safe here as we just verified that there is something inside expanded_url
decoded_urls.insert(String::from(&url.url), String::from(url.expanded_url.as_deref().unwrap()));
}
}
decoded_urls
}
/// Gets Mastodon Data
pub fn get_mastodon_token(masto: &MastodonConfig) -> Mastodon {
let data = Data {
base: Cow::from(String::from(&masto.base)),
client_id: Cow::from(String::from(&masto.client_id)),
client_secret: Cow::from(String::from(&masto.client_secret)),
redirect: Cow::from(String::from(&masto.redirect)),
token: Cow::from(String::from(&masto.token)),
};
Mastodon::from(data)
}
/// Builds toot text from tweet
pub fn build_basic_status(tweet: &Tweet) -> Result<String, Box<dyn Error>> {
let mut toot = String::from(&tweet.text);
let decoded_urls = decode_urls(&tweet.entities.urls);
for decoded_url in decoded_urls {
toot = toot.replace(&decoded_url.0, &decoded_url.1);
}
let decoded_mentions = twitter_mentions(&tweet.entities.user_mentions);
for decoded_mention in decoded_mentions {
toot = toot.replace(&decoded_mention.0, &decoded_mention.1);
}
if let Ok(t) = decode_html(&toot) {
toot = t;
}
Ok(toot)
}
/// Generic register function
/// As this function is supposed to be run only once, it will panic for every error it encounters
/// Most of this function is a direct copy/paste of the official `elefren` crate
pub fn register(host: &str) {
let mut builder = App::builder();
builder.client_name(Cow::from(String::from(env!("CARGO_PKG_NAME"))))
.redirect_uris(Cow::from(String::from("urn:ietf:wg:oauth:2.0:oob")))
.scopes(Scopes::write_all())
.website(Cow::from(String::from("https://framagit.org/veretcle/scootaloo")));
let app = builder.build().expect("Cannot build the app");
let registration = Registration::new(host).register(app).expect("Cannot build registration object");
let url = registration.authorize_url().expect("Cannot generate registration URI!");
println!("Click this link to authorize on Mastodon: {}", url);
println!("Paste the returned authorization code: ");
let mut input = String::new();
stdin().read_line(&mut input).expect("Unable to read back registration code!");
let code = input.trim();
let mastodon = registration.complete(code).expect("Unable to create access token!");
let toml = toml::to_string(&*mastodon).unwrap();
println!("Please insert the following block at the end of your configuration file:\n[mastodon]\n{}", toml);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_twitter_mentions() {
let mention_entity = MentionEntity {
id: 12345,
range: (1, 3),
name: String::from("Ta Mere l0l"),
screen_name: String::from("tamerelol"),
};
let twitter_ums = vec![mention_entity];
let mut expected_mentions = HashMap::new();
expected_mentions.insert(String::from("@tamerelol"), String::from("@tamerelol@twitter.com"));
let decoded_mentions = twitter_mentions(&twitter_ums);
assert_eq!(expected_mentions, decoded_mentions);
}
#[test]
fn test_decode_urls() {
let url_entity1 = UrlEntity {
display_url: String::from("tamerelol"),
expanded_url: Some(String::from("https://www.nintendojo.fr/dojobar")),
range: (1, 3),
url: String::from("https://t.me/tamerelol"),
};
let url_entity2 = UrlEntity {
display_url: String::from("tamerelol"),
expanded_url: None,
range: (1, 3),
url: String::from("https://t.me/tamerelol"),
};
let twitter_urls = vec![url_entity1, url_entity2];
let mut expected_urls = HashMap::new();
expected_urls.insert(String::from("https://t.me/tamerelol"), String::from("https://www.nintendojo.fr/dojobar"));
let decoded_urls = decode_urls(&twitter_urls);
assert_eq!(expected_urls, decoded_urls);
}
}