147 lines
4.4 KiB
Rust
147 lines
4.4 KiB
Rust
pub mod digest;
|
|
pub mod send;
|
|
pub mod fetch;
|
|
mod error;
|
|
pub use error::Error;
|
|
|
|
use std::collections::HashMap;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
const MIME_TYPE: &str = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Actor {
|
|
#[serde(rename = "@context")]
|
|
pub jsonld_context: Option<serde_json::Value>,
|
|
#[serde(rename = "type")]
|
|
pub actor_type: String,
|
|
pub id: String,
|
|
pub url: Option<String>,
|
|
pub name: String,
|
|
pub icon: Option<Media>,
|
|
pub inbox: String,
|
|
pub outbox: String,
|
|
#[serde(rename = "publicKey")]
|
|
pub public_key: ActorPublicKey,
|
|
#[serde(rename = "preferredUsername")]
|
|
pub preferred_username: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ActorPublicKey {
|
|
pub id: String,
|
|
pub owner: Option<String>,
|
|
#[serde(rename = "publicKeyPem")]
|
|
pub pem: String,
|
|
}
|
|
|
|
/// `ActivityPub` "activity"
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Action<O> {
|
|
#[serde(rename = "@context")]
|
|
pub jsonld_context: Option<serde_json::Value>,
|
|
#[serde(rename = "type")]
|
|
pub action_type: String,
|
|
pub id: String,
|
|
pub actor: String,
|
|
pub to: Option<serde_json::Value>,
|
|
pub object: O,
|
|
}
|
|
|
|
impl Action<serde_json::Value> {
|
|
#[must_use] pub fn object_id(&self) -> Option<&str> {
|
|
if let Some(id) = self.object.as_str() {
|
|
Some(id)
|
|
} else if let Some(object) = self.object.as_object() {
|
|
object.get("id").and_then(|id| id.as_str())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
// hack for misskey
|
|
fn media_default_content_type() -> String {
|
|
"image/jpeg".to_string()
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Media {
|
|
#[serde(rename = "type")]
|
|
pub media_type: String,
|
|
#[serde(rename = "mediaType", default = "media_default_content_type")]
|
|
pub content_type: String,
|
|
pub url: String,
|
|
}
|
|
|
|
/// This is unfortunately strictly separate from the Client/Server
|
|
/// data schema in `cave::feed::Post`
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
pub struct Post {
|
|
pub published: String,
|
|
pub id: String,
|
|
pub url: Option<String>,
|
|
#[serde(rename = "attributedTo")]
|
|
pub attributed_to: Option<String>,
|
|
#[serde(default = "String::new")]
|
|
pub content: String,
|
|
#[serde(rename = "contentMap", default = "HashMap::new")]
|
|
pub content_map: HashMap<String, String>,
|
|
#[serde(default)]
|
|
pub tag: Vec<super::feed::Tag>,
|
|
pub sensitive: Option<bool>,
|
|
#[serde(rename = "attachment", default = "Vec::new")]
|
|
pub attachments: Vec<Media>,
|
|
}
|
|
|
|
impl Post {
|
|
#[must_use] pub fn language(&self) -> Option<&str> {
|
|
self.content_map.keys()
|
|
.next().map(std::string::String::as_str)
|
|
}
|
|
|
|
/// Translate `ActivityPub` post to Mastodon client API post format
|
|
#[must_use] pub fn to_feed_post(self, actor: Actor) -> super::feed::Post {
|
|
let language = self.language()
|
|
.map(std::string::ToString::to_string);
|
|
super::feed::Post {
|
|
created_at: self.published,
|
|
url: self.url.unwrap_or(self.id),
|
|
content: self.content,
|
|
account: super::feed::Account {
|
|
username: actor.preferred_username,
|
|
display_name: actor.name,
|
|
url: actor.url.unwrap_or(actor.id),
|
|
bot: actor.actor_type != "Person",
|
|
},
|
|
tags: self.tag.into_iter().filter_map(|mut tag| {
|
|
while tag.name.starts_with('#') {
|
|
tag.name.remove(0);
|
|
}
|
|
if let Some(c) = tag.name.chars().next() {
|
|
// check the first letter
|
|
if ['@', ':'].contains(&c) {
|
|
// these are weird
|
|
None
|
|
} else {
|
|
Some(tag)
|
|
}
|
|
} else {
|
|
// no first letter
|
|
None
|
|
}
|
|
}).collect(),
|
|
sensitive: self.sensitive,
|
|
mentions: vec![],
|
|
language,
|
|
media_attachments: self.attachments.into_iter().map(|media| {
|
|
super::feed::MediaAttachment {
|
|
media_type: media.media_type,
|
|
remote_url: Some(media.url),
|
|
}
|
|
}).collect(),
|
|
reblog: None,
|
|
}
|
|
}
|
|
}
|