/**
* Representation of an activity, as received from the zot api streams.
*
* SPDX-FileCopyrightText: 2023 Eilertsens Kodeknekkeri
* SPDX-FileCopyrightText: 2023 Harald Eilertsen <haraldei@anduin.net>
*
* 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<Vec<ActivityFlag>>,
pub tags: Option<Vec<Tag>>,
}
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());
}
}