Add support for masks
This commit is contained in:
+276
-111
@@ -2,7 +2,10 @@ use scylla::{
|
||||
DeserializeValue, SerializeValue,
|
||||
client::session::Session,
|
||||
deserialize::row::DeserializeRow,
|
||||
errors::{ExecutionError, SingleRowError},
|
||||
errors::{
|
||||
DeserializationError, ExecutionError, FirstRowError, IntoRowsResultError, RowsError,
|
||||
SingleRowError,
|
||||
},
|
||||
response::query_result::QueryRowsResult,
|
||||
statement::{
|
||||
Statement,
|
||||
@@ -21,7 +24,7 @@ use twilight_model::{
|
||||
|
||||
use crate::{
|
||||
constants::DEFAULT_PREFIX,
|
||||
groups::calls::{DisplayProfile, masks::get_random_mask},
|
||||
groups::calls::masks::{get_mask_avatar_url, get_mask_display_name, get_random_mask},
|
||||
utils::{get_random_u64, i64_to_u64, u64_to_i64},
|
||||
};
|
||||
|
||||
@@ -40,13 +43,22 @@ pub enum DatabaseOrDiscordError {
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DatabaseError {
|
||||
#[error("An error occurred when querying the database")]
|
||||
QueryError(ExecutionError),
|
||||
QueryError(#[from] ExecutionError),
|
||||
|
||||
#[error("The database responded but the result of the query weren't rows")]
|
||||
ResultNotRows,
|
||||
ResultNotRows(#[from] IntoRowsResultError),
|
||||
|
||||
#[error("The resulting row could not be deserialized into an object")]
|
||||
DeserializationFailed,
|
||||
DeserializationFailed(#[from] DeserializationError),
|
||||
|
||||
#[error("The database responded but the result of the query weren't rows")]
|
||||
RowsError(#[from] RowsError),
|
||||
|
||||
#[error("The resulting row could not be deserialized into an object")]
|
||||
SingleRowFailed(#[from] SingleRowError),
|
||||
|
||||
#[error("The database responded but the first row raised an error")]
|
||||
FirstRowError(#[from] FirstRowError),
|
||||
|
||||
#[error("An unknown error occurred")]
|
||||
Unknown(&'static str),
|
||||
@@ -72,10 +84,8 @@ async fn get_single_row_from_result<'a, T: Object<'a>>(
|
||||
match result.single_row::<T::Row>() {
|
||||
Ok(row) => Ok(Some(T::from_row(row))),
|
||||
Err(error) => match error {
|
||||
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => {
|
||||
Err(DatabaseError::DeserializationFailed)
|
||||
}
|
||||
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
||||
_ => Err(error.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -170,10 +180,8 @@ impl User {
|
||||
"#,
|
||||
(id,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -301,10 +309,8 @@ impl User {
|
||||
"#,
|
||||
(),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
Ok(result.rows_num())
|
||||
}
|
||||
@@ -364,10 +370,8 @@ impl UserByName {
|
||||
"#,
|
||||
(name,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -423,20 +427,10 @@ impl Server {
|
||||
"#,
|
||||
(id,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
match result.single_row::<<Server as Object>::Row>() {
|
||||
Ok(row) => Ok(Some(Self::from_row(row))),
|
||||
Err(error) => match error {
|
||||
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => {
|
||||
Err(DatabaseError::DeserializationFailed)
|
||||
}
|
||||
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
||||
},
|
||||
}
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
|
||||
pub async fn get_or_create_by_id(
|
||||
@@ -643,10 +637,8 @@ impl Channel {
|
||||
"#,
|
||||
(id,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -789,16 +781,12 @@ impl SearchingChannel {
|
||||
"#,
|
||||
(),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
Ok(result
|
||||
.rows::<<SearchingChannel as Object>::Row>()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
||||
.rows::<<SearchingChannel as Object>::Row>()?
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.map(Self::from_row)
|
||||
.collect::<Vec<_>>())
|
||||
@@ -865,8 +853,7 @@ impl Call {
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -952,14 +939,11 @@ impl Call {
|
||||
),
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| log::error!("Failed to add channel to the call! {e:?}"))
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.inspect_err(|e| log::error!("Failed to add channel to the call! {e:?}"))?
|
||||
.into_rows_result()
|
||||
.inspect_err(|e| log::error!("Failed to get result of the query! {e:?}"))
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
||||
.inspect_err(|e| log::error!("Failed to get result of the query! {e:?}"))?
|
||||
.first_row::<(bool, Option<i64>)>()
|
||||
.inspect_err(|e| log::error!("Failed to get first row of the result! {e:?}"))
|
||||
.map_err(|_| DatabaseError::DeserializationFailed)?;
|
||||
.inspect_err(|e| log::error!("Failed to get first row of the result! {e:?}"))?;
|
||||
|
||||
log::trace!("Is applied: {is_applied:?}");
|
||||
|
||||
@@ -1075,10 +1059,8 @@ impl Call {
|
||||
"#,
|
||||
(),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
Ok(result.rows_num())
|
||||
}
|
||||
@@ -1115,7 +1097,7 @@ pub struct MessageBuilderError;
|
||||
#[derive(Default)]
|
||||
pub struct MessageBuilder {
|
||||
message: Option<TwilightMessage>,
|
||||
profile: Option<DisplayProfile>,
|
||||
participant: Option<Participant>,
|
||||
call_id: Option<u64>,
|
||||
is_censored: bool,
|
||||
propagation_message_id: Option<u64>,
|
||||
@@ -1127,7 +1109,7 @@ impl MessageBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
message: None,
|
||||
profile: None,
|
||||
participant: None,
|
||||
call_id: None,
|
||||
is_censored: false,
|
||||
propagation_message_id: None,
|
||||
@@ -1141,8 +1123,8 @@ impl MessageBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn profile(mut self, profile: DisplayProfile) -> Self {
|
||||
self.profile = Some(profile);
|
||||
pub fn participant(mut self, participant: Participant) -> Self {
|
||||
self.participant = Some(participant);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1176,16 +1158,16 @@ impl MessageBuilder {
|
||||
|
||||
pub fn build(self) -> Result<Message, MessageBuilderError> {
|
||||
let message = self.message.ok_or(MessageBuilderError)?;
|
||||
let profile = self.profile.ok_or(MessageBuilderError)?;
|
||||
let participant = self.participant.ok_or(MessageBuilderError)?;
|
||||
let call_id = self.call_id.ok_or(MessageBuilderError)?;
|
||||
|
||||
let data = MessageData {
|
||||
id: u64_to_i64(message.id.get()),
|
||||
call_id: u64_to_i64(call_id),
|
||||
author_id: u64_to_i64(profile.id),
|
||||
author_name: profile.name.clone(),
|
||||
author_avatar_url: profile.avatar_url.clone(),
|
||||
author_mask: profile.mask,
|
||||
author_id: u64_to_i64(participant.user_id),
|
||||
author_name: participant.user_name,
|
||||
author_avatar_url: participant.user_avatar_url,
|
||||
author_mask: participant.is_masked.then_some(participant.user_mask),
|
||||
content: message.content,
|
||||
attachments: message.attachments.iter().map(|a| a.url.clone()).collect(),
|
||||
sticker: message
|
||||
@@ -1206,7 +1188,7 @@ impl MessageBuilder {
|
||||
Ok(Message {
|
||||
call_id,
|
||||
message_id: message.id.get(),
|
||||
author_id: profile.id,
|
||||
author_id: participant.user_id,
|
||||
propagation_message_id: self.propagation_message_id,
|
||||
data,
|
||||
})
|
||||
@@ -1276,10 +1258,8 @@ impl Message {
|
||||
"#,
|
||||
(call_id, message_id_i64),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
if result.rows_num() == 0 {
|
||||
Self::get_by_propagation_message_id(message_id, session).await
|
||||
@@ -1317,10 +1297,46 @@ impl Message {
|
||||
u64_to_i64(message_by_propagation_id.message_id),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
return get_single_row_from_result(&result).await;
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn get_last_by_author_id(
|
||||
call_id: u64,
|
||||
author_id: u64,
|
||||
session: &Session,
|
||||
) -> Result<Option<Self>, DatabaseError> {
|
||||
let message_by_author_id =
|
||||
MessageByAuthorId::get_last_by_author_id(call_id, author_id, session).await?;
|
||||
|
||||
if let Some(message_by_author_id) = message_by_author_id {
|
||||
let result = session
|
||||
.query_unpaged(
|
||||
r#"
|
||||
SELECT
|
||||
call_id,
|
||||
message_id,
|
||||
author_id,
|
||||
propagation_message_id,
|
||||
data
|
||||
FROM
|
||||
payphone.messages
|
||||
WHERE
|
||||
call_id = ? AND message_id = ?
|
||||
LIMIT 1;
|
||||
"#,
|
||||
(
|
||||
u64_to_i64(message_by_author_id.call_id),
|
||||
u64_to_i64(message_by_author_id.message_id),
|
||||
),
|
||||
)
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
return get_single_row_from_result(&result).await;
|
||||
}
|
||||
@@ -1356,10 +1372,8 @@ impl Message {
|
||||
"#,
|
||||
(),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
Ok(result.rows_num())
|
||||
}
|
||||
@@ -1413,10 +1427,67 @@ impl MessageByPropagationId {
|
||||
"#,
|
||||
(propagation_message_id,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MessageByAuthorId {
|
||||
pub call_id: u64,
|
||||
pub message_id: u64,
|
||||
pub author_id: u64,
|
||||
}
|
||||
|
||||
impl<'a> Object<'a> for MessageByAuthorId {
|
||||
type Row = (i64, i64, i64);
|
||||
|
||||
fn from_row(row: Self::Row) -> Self {
|
||||
Self {
|
||||
call_id: i64_to_u64(row.0),
|
||||
message_id: i64_to_u64(row.1),
|
||||
author_id: i64_to_u64(row.2),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_row(value: Self) -> Self::Row {
|
||||
(
|
||||
u64_to_i64(value.call_id),
|
||||
u64_to_i64(value.message_id),
|
||||
u64_to_i64(value.author_id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageByAuthorId {
|
||||
pub async fn get_last_by_author_id(
|
||||
call_id: u64,
|
||||
author_id: u64,
|
||||
session: &Session,
|
||||
) -> Result<Option<Self>, DatabaseError> {
|
||||
let call_id = u64_to_i64(call_id);
|
||||
let author_id = u64_to_i64(author_id);
|
||||
|
||||
let result = session
|
||||
.query_unpaged(
|
||||
r#"
|
||||
SELECT
|
||||
call_id,
|
||||
message_id,
|
||||
author_id
|
||||
FROM
|
||||
payphone.messages_by_author_id
|
||||
WHERE
|
||||
call_id = ? AND
|
||||
author_id = ?
|
||||
ORDER BY message_id DESC
|
||||
LIMIT 1;
|
||||
"#,
|
||||
(call_id, author_id),
|
||||
)
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -1637,10 +1708,8 @@ impl UserIncident {
|
||||
"#,
|
||||
(user_id, incident_id),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
@@ -1669,16 +1738,12 @@ impl UserIncident {
|
||||
"#,
|
||||
(user_id,),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
Ok(result
|
||||
.rows::<<Self as Object>::Row>()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
||||
.rows::<<Self as Object>::Row>()?
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.map(Self::from_row)
|
||||
.collect::<Vec<_>>())
|
||||
@@ -1789,14 +1854,48 @@ impl<'a> Object<'a> for Participant {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_name_valid(name: String) -> String {
|
||||
if name.len() == 1 {
|
||||
format!("{name}\u{2800}")
|
||||
} else if name.len() > 80 {
|
||||
format!("{name:.80}")
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
impl Participant {
|
||||
pub fn display_name(&self) -> String {
|
||||
if self.is_masked {
|
||||
get_mask_display_name(self.user_mask.clone())
|
||||
} else {
|
||||
make_name_valid(self.user_name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn avatar_url(&self) -> String {
|
||||
if self.is_masked {
|
||||
get_mask_avatar_url(self.user_mask.clone()).expect("Failed to get mask avatar URL")
|
||||
} else {
|
||||
self.user_avatar_url.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_for_call(call_id: u64, session: &Session) -> Result<Vec<Self>, DatabaseError> {
|
||||
let call_id = u64_to_i64(call_id);
|
||||
|
||||
let rows = session
|
||||
.query_unpaged(
|
||||
r#"
|
||||
SELECT * FROM payphone.participants
|
||||
SELECT
|
||||
call_id,
|
||||
user_id,
|
||||
channel_id,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
user_mask,
|
||||
is_masked
|
||||
FROM payphone.participants
|
||||
WHERE call_id = ?;
|
||||
"#,
|
||||
(call_id,),
|
||||
@@ -1805,27 +1904,69 @@ impl Participant {
|
||||
.map_err(DatabaseError::QueryError)?;
|
||||
|
||||
Ok(rows
|
||||
.into_rows_result()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
||||
.rows()
|
||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
||||
.into_rows_result()?
|
||||
.rows()?
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.map(Self::from_row)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
pub async fn get_for_user_and_channel_in_call(
|
||||
user_id: u64,
|
||||
channel_id: u64,
|
||||
call_id: u64,
|
||||
profile: DisplayProfile,
|
||||
session: &Session,
|
||||
) -> Result<Option<Self>, DatabaseError> {
|
||||
let user_id = u64_to_i64(user_id);
|
||||
let channel_id = u64_to_i64(channel_id);
|
||||
let call_id = u64_to_i64(call_id);
|
||||
|
||||
let result = session
|
||||
.query_unpaged(
|
||||
r#"
|
||||
SELECT
|
||||
call_id,
|
||||
user_id,
|
||||
channel_id,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
user_mask,
|
||||
is_masked
|
||||
FROM
|
||||
payphone.participants
|
||||
WHERE
|
||||
call_id = ? AND user_id = ? AND channel_id = ?
|
||||
LIMIT 1;
|
||||
"#,
|
||||
(call_id, user_id, channel_id),
|
||||
)
|
||||
.await?
|
||||
.into_rows_result()?;
|
||||
|
||||
get_single_row_from_result(&result).await
|
||||
}
|
||||
|
||||
pub async fn get_or_create(
|
||||
user_id: u64,
|
||||
channel_id: u64,
|
||||
call_id: u64,
|
||||
display_name: String,
|
||||
display_avatar_url: String,
|
||||
is_masked: bool,
|
||||
session: &Session,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let masks_in_use = Self::get_for_call(call_id, session)
|
||||
.await?
|
||||
) -> Result<Self, DatabaseError> {
|
||||
let participants = Self::get_for_call(call_id, session).await?;
|
||||
|
||||
if let Some(participant) = participants
|
||||
.iter()
|
||||
.find(|p| p.user_id == user_id && p.channel_id == channel_id)
|
||||
{
|
||||
// User is already a participant in the call
|
||||
return Ok(participant.clone());
|
||||
}
|
||||
|
||||
let masks_in_use = participants
|
||||
.iter()
|
||||
.filter_map(|p| {
|
||||
if p.channel_id == channel_id {
|
||||
@@ -1844,13 +1985,15 @@ impl Participant {
|
||||
call_id,
|
||||
user_id,
|
||||
channel_id,
|
||||
user_name: profile.name,
|
||||
user_avatar_url: profile.avatar_url,
|
||||
user_name: display_name,
|
||||
user_avatar_url: display_avatar_url,
|
||||
user_mask: mask,
|
||||
is_masked,
|
||||
};
|
||||
|
||||
participant.insert(session).await
|
||||
participant.insert(session).await?;
|
||||
|
||||
Ok(participant)
|
||||
}
|
||||
|
||||
pub async fn insert(&self, session: &Session) -> Result<(), DatabaseError> {
|
||||
@@ -1865,7 +2008,29 @@ impl Participant {
|
||||
user_avatar_url,
|
||||
user_mask,
|
||||
is_masked
|
||||
) IF NOT EXISTS VALUES (?, ?, ?, ?, ?, ?, ?);
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?) IF NOT EXISTS;
|
||||
"#,
|
||||
Self::to_row(self.clone()),
|
||||
)
|
||||
.await
|
||||
.map_err(DatabaseError::QueryError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn upsert(&self, session: &Session) -> Result<(), DatabaseError> {
|
||||
session
|
||||
.query_unpaged(
|
||||
r#"
|
||||
INSERT INTO payphone.participants (
|
||||
call_id,
|
||||
user_id,
|
||||
channel_id,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
user_mask,
|
||||
is_masked
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?);
|
||||
"#,
|
||||
Self::to_row(self.clone()),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user