corobel/src/syndication.rs

123 lines
4.2 KiB
Rust

use crate::cohost_account::CohostAccount;
use crate::cohost_posts::*;
use crate::ARGS;
use rss::Channel;
pub fn channel_for_posts_page(
project_name: impl AsRef<str>,
project: CohostAccount,
mut page: CohostPostsPage,
originals_only: bool,
) -> Channel {
let project_name = project_name.as_ref().clone();
let mut builder = rss::ChannelBuilder::default();
builder
.title(format!(
"{} Cohost Posts{}",
project.display_name,
if originals_only { "" } else { " and Shares" }
))
.description(project.description)
.generator(Some(format!(
"{} {}",
env!("CARGO_CRATE_NAME"),
env!("CARGO_PKG_VERSION")
)))
.link(format!("https://cohost.org/{}", project_name,));
page.items.sort_by_key(|item| item.published_at);
let mut items = Vec::with_capacity(page.number_items);
for item in page.items {
let mut item_builder = rss::ItemBuilder::default();
let mut categories: Vec<rss::Category> = Vec::with_capacity(item.tags.len());
for tag in item.tags.iter().cloned() {
categories.push(rss::Category {
name: tag,
domain: Some("https://cohost.org".into()),
});
}
item_builder
.link(item.url.clone())
.title(item.headline)
.author(item.poster.handle)
.guid(Some(rss::Guid {
value: item.url.clone(),
permalink: true,
}))
.categories(categories)
.pub_date(item.published_at.to_rfc2822())
.source(Some(rss::Source {
title: Some(format!("{} Cohost Posts", project.display_name)),
url: format!("https://{}/feed/{}.rss", ARGS.domain, project_name),
}));
let mut body_text = String::new();
if let Some(shared_post_id) = item.transparent_share_of_post_id {
if originals_only {
continue;
}
body_text.push_str(&format!(
"(share of post {} without any commentary)\n\n---\n\n",
shared_post_id
));
} else if item.share_tree.len() == 1 {
body_text.push_str("(in reply to another post)\n\n---\n\n")
} else if item.share_tree.len() > 1 {
body_text.push_str(&format!(
"(in reply to {} other posts)\n\n---\n\n",
item.share_tree.len()
));
}
if item.cws.is_empty() {
body_text.push_str(&item.plain_body);
} else {
body_text.push_str("Sensitive post, body text omitted. Content warnings:");
for cw in item.cws {
body_text.push_str(&format!(" {},", cw));
}
body_text.pop(); // Remove trailing comma
body_text.push_str("\n\n---\n\n")
};
if !item.tags.is_empty() {
body_text.push_str("\n\n Post tagged:");
for tag in item.tags {
body_text.push_str(&format!(" #{},", tag));
}
body_text.pop(); // Remove trailing comma
}
use pulldown_cmark::Options;
let options = Options::ENABLE_FOOTNOTES
& Options::ENABLE_STRIKETHROUGH
& Options::ENABLE_TABLES
& Options::ENABLE_TASKLISTS;
let parser = pulldown_cmark::Parser::new_ext(&body_text, options);
let mut html_output = String::new();
pulldown_cmark::html::push_html(&mut html_output, parser);
item_builder.content(html_output);
for attachment in item.blocks.into_iter().filter_map(|block| block.attachment) {
use mime_guess::from_path as guess_mime_from_path;
use rss::EnclosureBuilder;
let enclosure = EnclosureBuilder::default()
.mime_type(
guess_mime_from_path(&attachment.file_url)
.first_or_octet_stream()
.to_string(),
)
.url(attachment.file_url)
.build();
item_builder.enclosure(enclosure);
}
items.push(item_builder.build());
}
builder.items(items);
builder.build()
}