2025-05-05 19:38:01 -04:00
use super ::* ;
use crate ::cache ::Cache ;
use crate ::model ::{
Error , Result , auth ::User , permissions ::FinePermission ,
communities_permissions ::CommunityPermission , uploads ::CustomEmoji ,
} ;
use crate ::{ auto_method , execute , get , query_row , query_rows , params } ;
#[ cfg(feature = " sqlite " ) ]
use rusqlite ::Row ;
#[ cfg(feature = " postgres " ) ]
use tokio_postgres ::Row ;
impl DataManager {
/// Get a [`CustomEmoji`] from an SQL row.
pub ( crate ) fn get_emoji_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> CustomEmoji {
CustomEmoji {
id : get ! ( x ->0 ( i64 ) ) as usize ,
owner : get ! ( x ->1 ( i64 ) ) as usize ,
created : get ! ( x ->2 ( i64 ) ) as usize ,
community : get ! ( x ->3 ( i64 ) ) as usize ,
upload_id : get ! ( x ->4 ( i64 ) ) as usize ,
name : get ! ( x ->5 ( String ) ) ,
2025-05-05 23:27:05 -04:00
is_animated : get ! ( x ->6 ( i32 ) ) as i8 = = 1 ,
2025-05-05 19:38:01 -04:00
}
}
auto_method! ( get_emoji_by_id ( usize as i64 ) @ get_emoji_from_row -> " SELECT * FROM emojis WHERE id = $1 " - - name = " emoji " - - returns = CustomEmoji - - cache - key - tmpl = " atto.emoji:{} " ) ;
/// Get all emojis by community.
///
/// # Arguments
/// * `community` - the ID of the community to fetch emojis for
pub async fn get_emojis_by_community ( & self , community : usize ) -> Result < Vec < CustomEmoji > > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM emojis WHERE community = $1 ORDER BY name ASC " ,
& [ & ( community as i64 ) ] ,
| x | { Self ::get_emoji_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " emoji " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-05-05 23:12:46 -04:00
/// Get an emoji by community and name.
2025-05-05 19:38:01 -04:00
///
/// # Arguments
2025-05-05 23:12:46 -04:00
/// * `community` - the ID of the community to fetch emoji from
/// * `name` - the name of the emoji
2025-05-05 19:38:01 -04:00
///
2025-05-05 23:12:46 -04:00
/// # Returns
/// `(custom emoji, emoji string)`
///
/// Custom emoji will be none if emoji string is some, and vice versa. Emoji string
/// will only be some if the community is 0 (no community ID in parsed string, or `0.emoji_name`)
///
/// Regular unicode emojis should have a community ID of 0, since they don't belong
/// to any community and can be used by anyone.
pub async fn get_emoji_by_community_name (
2025-05-05 19:38:01 -04:00
& self ,
2025-05-05 23:12:46 -04:00
community : usize ,
name : & str ,
) -> Result < ( Option < CustomEmoji > , Option < String > ) > {
if community = = 0 {
return Ok ( ( None , Some ( " 🐇 " . to_string ( ) ) ) ) ;
}
// ...
2025-05-05 19:38:01 -04:00
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_row! (
& conn ,
2025-05-05 23:12:46 -04:00
" SELECT * FROM emojis WHERE community = $1 AND name = $2 ORDER BY name ASC " ,
params! [ & ( community as i64 ) , & name ] ,
| x | { Ok ( ( Some ( Self ::get_emoji_from_row ( x ) ) , None ) ) }
2025-05-05 19:38:01 -04:00
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " emoji " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
/// Create a new emoji in the database.
///
/// # Arguments
/// * `data` - a mock [`CustomEmoji`] object to insert
pub async fn create_emoji ( & self , data : CustomEmoji ) -> Result < ( ) > {
let user = self . get_user_by_id ( data . owner ) . await ? ;
// check user permission in community
if data . community ! = 0 {
let membership = self
. get_membership_by_owner_community ( user . id , data . community )
. await ? ;
if ! membership . role . check ( CommunityPermission ::MANAGE_EMOJIS )
& & ! user . permissions . check ( FinePermission ::MANAGE_EMOJIS )
{
return Err ( Error ::NotAllowed ) ;
}
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
2025-05-05 23:27:05 -04:00
" INSERT INTO emojis VALUES ($1, $2, $3, $4, $5, $6, $7) " ,
2025-05-05 19:38:01 -04:00
params! [
& ( data . id as i64 ) ,
& ( data . owner as i64 ) ,
& ( data . created as i64 ) ,
& ( data . community as i64 ) ,
& ( data . upload_id as i64 ) ,
2025-05-05 23:27:05 -04:00
& data . name ,
& { if data . is_animated { 1 } else { 0 } } ,
2025-05-05 19:38:01 -04:00
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( ( ) )
}
pub async fn delete_emoji ( & self , id : usize , user : & User ) -> Result < ( ) > {
let emoji = self . get_emoji_by_id ( id ) . await ? ;
// check user permission in community
if user . id ! = emoji . owner {
let membership = self
. get_membership_by_owner_community ( user . id , emoji . community )
. await ? ;
if ! membership . role . check ( CommunityPermission ::MANAGE_EMOJIS ) {
return Err ( Error ::NotAllowed ) ;
}
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM emojis WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// delete uploads
self . delete_upload ( emoji . upload_id ) . await ? ;
// ...
self . 2. remove ( format! ( " atto.emoji: {} " , id ) ) . await ;
Ok ( ( ) )
}
auto_method! ( update_emoji_name ( & str ) @ get_emoji_by_id :MANAGE_EMOJIS -> " UPDATE emojis SET title = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.emoji:{} " ) ;
}