/** * Representation of an activity, as received from the zot api streams. * * SPDX-FileCopyrightText: 2023 Eilertsens Kodeknekkeri * SPDX-FileCopyrightText: 2023 Harald Eilertsen * * SPDX-License-Identifier: AGPL-3.0-or-later */ use super::actor::Actor; use super::datetime::DateTime; use super::Tag; use super::verb::Verb; use serde::Deserialize; use url::Url; use uuid::Uuid; #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all="snake_case")] pub enum ActivityType { Activity, } #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all="snake_case")] pub enum ActivityEncoding { Zot, } #[derive(Debug, Deserialize, PartialEq)] pub enum ObjectType { Article, Note, Question, } #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all="snake_case")] pub enum ActivityFlag { Direct, Notshown, Private, ThreadParent, } /** * Represents an item as returned by the stream API's. */ #[derive(Debug, Deserialize, PartialEq)] pub struct Activity { #[serde(rename="type")] pub item_type: ActivityType, pub encoding: ActivityEncoding, pub uuid: Uuid, pub message_id: Url, pub message_top: Url, pub message_parent: Url, pub permalink: Url, /* * Date and time fields for when the item was * created, last edited or commented, as well as * when/if it expires. */ pub created: DateTime, pub edited: DateTime, pub expires: DateTime, pub commented: DateTime, pub verb: Verb, pub object_type: ObjectType, pub title: String, pub summary: String, pub body: String, pub owner: Actor, pub author: Actor, pub signature: String, pub flags: Option>, pub tags: Option>, } impl Activity { pub fn is_post(&self) -> bool { self.verb == Verb::Create && self.object_type == ObjectType::Note && self.has_flag(ActivityFlag::ThreadParent) } pub fn has_flag(&self, flag: ActivityFlag) -> bool { self.flags .as_ref() .map_or( false, |flags| flags.iter().find( |f| *f == &flag ).is_some()) } } #[cfg(test)] mod test { use super::*; #[test] fn parse_stream_item_from_json() { let json = r#" { "type": "activity", "encoding": "zot", "uuid": "2c102ec3-676b-4d84-b2b4-9141467a254f", "message_id": "https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", "message_top": "https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", "message_parent": "https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", "created": "2023-12-12 17:00:42", "edited": "2023-12-12 17:00:42", "expires": "0000-00-00 00:00:00", "commented": "2023-12-19 09:01:15", "mimetype": "text/bbcode", "title": "The item title", "summary": "The summary of the post", "body": "The body of the post", "app": "", "verb": "Create", "object_type": "Note", "target_type": "", "permalink": "https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", "location": "", "longlat": "", "signature": "sha256.ADzsjr5uFUpcwVkUT9liHimr1n2TJmGpTtm0SGSody_6vTbfYhHK3OJJhGknImHYwMradSwVcA9dNAAXzhSoAY5hCmSbnThLun30HVIA3E0ZSDBbt1RyKNomqBCvCBmyyFKhxYoPk34UQisCH6gIQ3eZL-m5PxE9t2oMO_CnpPvLWESoezY3CAZaEIIRj3KKozwC8DxibQmsnCeA32C-2Ejzv8PdZCX1skbIdc5d8Jj_ykyUTqc2DZKMSl9osua3esEMwYZLbxlRQruDfgyjJExdvz57zpeqf1WKzqdIZ5kmguZxch6NLVMiRitooT-sIZOvo9JzgP1ogBQW34W-kG1hHzuGJQkDZKOAA1rLD8qrfwugbbg9wDpjtZY-WHv265KMMsFhctIIupNPVVkuSgs0jMKQmjJTeDE0IxBKx2dCsxiqMWlJll_LSu7b6BtTa7jcuhfMPaKqdMOCRRnGNwehj6qeOXPV7zEZsc4Mzym6N_jRTV556OmUwaCHouEFqTG1ARE5EquYTMk2wXIteC5lT2d0WpEGTnlL1WpqWLx9DCp76Z2JsxMBxjZicKvJSm1gX3ng2ENG2cvECww0IF_zPy2DmHcWOqm795-11uLBNt_60bpP9-sgjrNSj0q6MHgAsFGnpW42M4sFe-6gb-W-HjyHwY2B2yjErYC9KDo", "route": "", "owner": { "name": "Benjamin Franklin", "address": "ben@example.com", "url": "https://example.com/channel/ben", "network": "zot6", "photo": { "mimetype": "image/jpeg", "src": "https://example.com/photo/profile/m/2" }, "id": "rUpgk2qbvnWLoKIXOlZlwlqI5vk8C4NgudFNjbcmnOBjFSXU34TObkZEClaPSfKnpFZpg87tANtko7WGs7QRvA", "id_sig": "sha256.ZD8uwYmUEG_d02Y...", "key": "-----BEGIN PUBLIC KEY-----\n....\n-----END PUBLIC KEY-----\n" }, "author": { "name": "Benjamin Franklin", "address": "ben@example.com", "url": "https://example.com/channel/ben", "network": "zot6", "photo": { "mimetype": "image/jpeg", "src": "https://example.com/photo/profile/m/2" }, "id": "rUpgk2qbvnWLoKIXOlZlwlqI5vk8C4NgudFNjbcmnOBjFSXU34TObkZEClaPSfKnpFZpg87tANtko7WGs7QRvA", "id_sig": "sha256.ZD8uwYmUEG_d02Y...", "key": "-----BEGIN PUBLIC KEY-----\n....\n-----END PUBLIC KEY-----\n" }, "flags": [ "thread_parent" ], "public_scope": "", "comment_scope": "contacts", "tags": [ { "tag": "a-tag", "url": "https://example.com/search?tag=a-tag", "type": "hashtag" }, { "tag": "fediverse", "url": "https://example.com/search?tag=fediverse", "type": "hashtag" }, { "tag": "hubzilla", "url": "https://example.com/search?tag=hubzilla", "type": "hashtag" } ] }"#; let item: Activity = serde_json::from_str(&json).unwrap(); assert_eq!(ActivityType::Activity, item.item_type); assert_eq!(ActivityEncoding::Zot, item.encoding); assert_eq!("2c102ec3-676b-4d84-b2b4-9141467a254f", item.uuid.to_string()); assert_eq!("https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", item.message_id.to_string()); assert_eq!("https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", item.message_top.to_string()); assert_eq!("https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", item.message_parent.to_string()); assert_eq!("https://example.com/item/2c102ec3-676b-4d84-b2b4-9141467a254f", item.permalink.to_string()); assert_eq!(Verb::Create, item.verb); assert_eq!("The item title", &item.title); assert_eq!("The summary of the post", &item.summary); assert_eq!("The body of the post", &item.body); assert_eq!("2023-12-12 17:00:42", &item.created.to_string()); assert_eq!("2023-12-12 17:00:42", &item.edited.to_string()); assert_eq!("0000-00-00 00:00:00", &item.expires.to_string()); assert_eq!("2023-12-19 09:01:15", &item.commented.to_string()); assert_eq!(ObjectType::Note, item.object_type); assert_eq!(true, item.has_flag(ActivityFlag::ThreadParent)); assert_eq!(true, item.is_post()); } }