+}
+
+/// Generate a unique ID for an activity, in the format:
+/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
+fn generate_activity_id<T>(kind: T, protocol_and_hostname: &str) -> Result<Url, ParseError>
+where
+ T: ToString,
+{
+ let id = format!(
+ "{}/activities/{}/{}",
+ protocol_and_hostname,
+ kind.to_string().to_lowercase(),
+ Uuid::new_v4()
+ );
+ Url::parse(&id)
+}
+
+#[tracing::instrument(skip_all)]
+async fn send_lemmy_activity<Activity, ActorT>(
+ data: &Data<LemmyContext>,
+ activity: Activity,
+ actor: &ActorT,
+ mut inbox: Vec<Url>,
+ sensitive: bool,
+) -> Result<(), LemmyError>
+where
+ Activity: ActivityHandler + Serialize + Send + Sync + Clone,
+ ActorT: Actor,
+ Activity: ActivityHandler<Error = LemmyError>,
+{
+ static CACHE: Lazy<Cache<(), Arc<Vec<String>>>> = Lazy::new(|| {
+ Cache::builder()
+ .max_capacity(1)
+ .time_to_live(DEAD_INSTANCE_LIST_CACHE_DURATION)
+ .build()
+ });
+ let dead_instances = CACHE
+ .try_get_with((), async {
+ Ok::<_, diesel::result::Error>(Arc::new(Instance::dead_instances(&mut data.pool()).await?))
+ })
+ .await?;
+
+ inbox.retain(|i| {
+ let domain = i.domain().expect("has domain").to_string();
+ !dead_instances.contains(&domain)
+ });
+ info!("Sending activity {}", activity.id().to_string());
+ let activity = WithContext::new(activity, CONTEXT.deref().clone());
+
+ let form = SentActivityForm {
+ ap_id: activity.id().clone().into(),
+ data: serde_json::to_value(activity.clone())?,
+ sensitive,
+ };
+ SentActivity::create(&mut data.pool(), form).await?;
+ send_activity(activity, actor, inbox, data).await?;
+