Rust AppView - highly experimental!
at experiments 3.5 kB view raw
1use crate::error::Result; 2use deadpool_postgres::GenericClient; 3 4pub mod actor; 5pub mod bulk_copy; 6pub mod bulk_resolve; 7pub mod composite_builders; 8pub mod gates; 9pub mod id_resolution; 10pub mod labels; 11pub mod operations; 12pub mod record_exists; 13pub mod workers; 14 15pub use actor::*; 16pub use gates::*; 17pub use labels::*; 18pub use operations::*; 19pub use record_exists::record_exists; 20 21/// Check if a recipient has muted a thread 22/// 23/// # Arguments 24/// 25/// * `recipient_actor_id` - The actor_id of the potential recipient 26/// * `root_post_actor_id` - The actor_id of the root post author 27/// * `root_post_rkey` - The rkey (as i64) of the root post 28/// 29/// # Returns 30/// 31/// `true` if the recipient has muted this thread, `false` otherwise 32pub async fn is_thread_muted<C: GenericClient>( 33 conn: &C, 34 recipient_actor_id: i32, 35 root_post_actor_id: i32, 36 root_post_rkey: i64, 37) -> Result<bool> { 38 let row = conn 39 .query_opt( 40 "SELECT 1 FROM actors, unnest(COALESCE(thread_mutes, ARRAY[]::thread_mute_record[])) AS tm 41 WHERE id = $1 42 AND (tm).root_post_actor_id = $2 43 AND (tm).root_post_rkey = $3", 44 &[&recipient_actor_id, &root_post_actor_id, &root_post_rkey], 45 ) 46 .await?; 47 Ok(row.is_some()) 48} 49 50/// Walk up the reply chain to get the parent post's author actor_id and parent URI 51/// 52/// This is used by NotifyReplyChain to traverse the reply chain and notify ancestors. 53/// Returns (author_actor_id, parent_uri) if the post exists, None if not found. 54pub async fn get_reply_chain_parent<C: GenericClient>( 55 conn: &C, 56 post_uri: &str, 57) -> Result<Option<(i32, Option<String>)>> { 58 let row_opt = conn 59 .query_opt( 60 "WITH post_parts AS ( 61 SELECT 62 SPLIT_PART(SUBSTRING($1 FROM 6), '/', 1) as did, 63 SPLIT_PART(SUBSTRING($1 FROM 6), '/', 3) as rkey 64 ), 65 post_lookup AS ( 66 SELECT p.actor_id, p.parent_post_actor_id, p.parent_post_rkey 67 FROM post_parts pp 68 INNER JOIN actors a ON a.did = pp.did 69 INNER JOIN posts p ON p.actor_id = a.id AND p.rkey = tid_to_i64(pp.rkey) 70 ) 71 SELECT pl.actor_id, 72 CASE WHEN pl.parent_post_actor_id IS NOT NULL THEN 73 'at://' || pa.did || '/app.bsky.feed.post/' || i64_to_tid(pl.parent_post_rkey) 74 ELSE NULL END as parent_uri 75 FROM post_lookup pl 76 LEFT JOIN actors pa ON pa.id = pl.parent_post_actor_id", 77 &[&post_uri], 78 ) 79 .await?; 80 81 Ok(row_opt.map(|row| { 82 let author_actor_id: i32 = row.get(0); 83 let parent_uri: Option<String> = row.get(1); 84 (author_actor_id, parent_uri) 85 })) 86} 87 88/// Check if a post exists 89/// 90/// # Arguments 91/// 92/// * `actor_id` - The actor_id of the post author 93/// * `rkey` - The rkey (as i64) of the post 94/// 95/// # Returns 96/// 97/// `true` if the post exists, `false` otherwise 98/// 99/// This is a simple lookup helper to consolidate the many inline EXISTS queries 100/// scattered throughout the codebase. 101pub async fn post_exists<C: GenericClient>( 102 conn: &C, 103 actor_id: i32, 104 rkey: i64, 105) -> Result<bool> { 106 let exists: bool = conn 107 .query_one( 108 "SELECT EXISTS(SELECT 1 FROM posts WHERE actor_id = $1 AND rkey = $2)", 109 &[&actor_id, &rkey], 110 ) 111 .await? 112 .get(0); 113 Ok(exists) 114}