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, 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 = 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() }