Add support for masks
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS payphone.messages_by_author_id AS
|
||||||
|
SELECT call_id, message_id, author_id FROM payphone.messages
|
||||||
|
WHERE call_id IS NOT NULL AND message_id IS NOT NULL AND author_id IS NOT NULL
|
||||||
|
PRIMARY KEY (call_id, author_id, message_id);
|
||||||
+276
-111
@@ -2,7 +2,10 @@ use scylla::{
|
|||||||
DeserializeValue, SerializeValue,
|
DeserializeValue, SerializeValue,
|
||||||
client::session::Session,
|
client::session::Session,
|
||||||
deserialize::row::DeserializeRow,
|
deserialize::row::DeserializeRow,
|
||||||
errors::{ExecutionError, SingleRowError},
|
errors::{
|
||||||
|
DeserializationError, ExecutionError, FirstRowError, IntoRowsResultError, RowsError,
|
||||||
|
SingleRowError,
|
||||||
|
},
|
||||||
response::query_result::QueryRowsResult,
|
response::query_result::QueryRowsResult,
|
||||||
statement::{
|
statement::{
|
||||||
Statement,
|
Statement,
|
||||||
@@ -21,7 +24,7 @@ use twilight_model::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::DEFAULT_PREFIX,
|
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},
|
utils::{get_random_u64, i64_to_u64, u64_to_i64},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,13 +43,22 @@ pub enum DatabaseOrDiscordError {
|
|||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DatabaseError {
|
pub enum DatabaseError {
|
||||||
#[error("An error occurred when querying the database")]
|
#[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")]
|
#[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")]
|
#[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")]
|
#[error("An unknown error occurred")]
|
||||||
Unknown(&'static str),
|
Unknown(&'static str),
|
||||||
@@ -72,10 +84,8 @@ async fn get_single_row_from_result<'a, T: Object<'a>>(
|
|||||||
match result.single_row::<T::Row>() {
|
match result.single_row::<T::Row>() {
|
||||||
Ok(row) => Ok(Some(T::from_row(row))),
|
Ok(row) => Ok(Some(T::from_row(row))),
|
||||||
Err(error) => match error {
|
Err(error) => match error {
|
||||||
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => {
|
|
||||||
Err(DatabaseError::DeserializationFailed)
|
|
||||||
}
|
|
||||||
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
||||||
|
_ => Err(error.into()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,10 +180,8 @@ impl User {
|
|||||||
"#,
|
"#,
|
||||||
(id,),
|
(id,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
get_single_row_from_result(&result).await
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -301,10 +309,8 @@ impl User {
|
|||||||
"#,
|
"#,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
Ok(result.rows_num())
|
Ok(result.rows_num())
|
||||||
}
|
}
|
||||||
@@ -364,10 +370,8 @@ impl UserByName {
|
|||||||
"#,
|
"#,
|
||||||
(name,),
|
(name,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
get_single_row_from_result(&result).await
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -423,20 +427,10 @@ impl Server {
|
|||||||
"#,
|
"#,
|
||||||
(id,),
|
(id,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
match result.single_row::<<Server as Object>::Row>() {
|
get_single_row_from_result(&result).await
|
||||||
Ok(row) => Ok(Some(Self::from_row(row))),
|
|
||||||
Err(error) => match error {
|
|
||||||
SingleRowError::DeserializationFailed(_) | SingleRowError::TypeCheckFailed(_) => {
|
|
||||||
Err(DatabaseError::DeserializationFailed)
|
|
||||||
}
|
|
||||||
SingleRowError::UnexpectedRowCount(_) => Ok(None),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_or_create_by_id(
|
pub async fn get_or_create_by_id(
|
||||||
@@ -643,10 +637,8 @@ impl Channel {
|
|||||||
"#,
|
"#,
|
||||||
(id,),
|
(id,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
get_single_row_from_result(&result).await
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -789,16 +781,12 @@ impl SearchingChannel {
|
|||||||
"#,
|
"#,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
Ok(result
|
Ok(result
|
||||||
.rows::<<SearchingChannel as Object>::Row>()
|
.rows::<<SearchingChannel as Object>::Row>()?
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Self::from_row)
|
.map(Self::from_row)
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
@@ -865,8 +853,7 @@ impl Call {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(DatabaseError::QueryError)?
|
.map_err(DatabaseError::QueryError)?
|
||||||
.into_rows_result()
|
.into_rows_result()?;
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
get_single_row_from_result(&result).await
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -952,14 +939,11 @@ impl Call {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.inspect_err(|e| log::error!("Failed to add channel to the call! {e:?}"))
|
.inspect_err(|e| log::error!("Failed to add channel to the call! {e:?}"))?
|
||||||
.map_err(DatabaseError::QueryError)?
|
|
||||||
.into_rows_result()
|
.into_rows_result()
|
||||||
.inspect_err(|e| log::error!("Failed to get result of the query! {e:?}"))
|
.inspect_err(|e| log::error!("Failed to get result of the query! {e:?}"))?
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
|
||||||
.first_row::<(bool, Option<i64>)>()
|
.first_row::<(bool, Option<i64>)>()
|
||||||
.inspect_err(|e| log::error!("Failed to get first row of the result! {e:?}"))
|
.inspect_err(|e| log::error!("Failed to get first row of the result! {e:?}"))?;
|
||||||
.map_err(|_| DatabaseError::DeserializationFailed)?;
|
|
||||||
|
|
||||||
log::trace!("Is applied: {is_applied:?}");
|
log::trace!("Is applied: {is_applied:?}");
|
||||||
|
|
||||||
@@ -1075,10 +1059,8 @@ impl Call {
|
|||||||
"#,
|
"#,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
Ok(result.rows_num())
|
Ok(result.rows_num())
|
||||||
}
|
}
|
||||||
@@ -1115,7 +1097,7 @@ pub struct MessageBuilderError;
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MessageBuilder {
|
pub struct MessageBuilder {
|
||||||
message: Option<TwilightMessage>,
|
message: Option<TwilightMessage>,
|
||||||
profile: Option<DisplayProfile>,
|
participant: Option<Participant>,
|
||||||
call_id: Option<u64>,
|
call_id: Option<u64>,
|
||||||
is_censored: bool,
|
is_censored: bool,
|
||||||
propagation_message_id: Option<u64>,
|
propagation_message_id: Option<u64>,
|
||||||
@@ -1127,7 +1109,7 @@ impl MessageBuilder {
|
|||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
message: None,
|
message: None,
|
||||||
profile: None,
|
participant: None,
|
||||||
call_id: None,
|
call_id: None,
|
||||||
is_censored: false,
|
is_censored: false,
|
||||||
propagation_message_id: None,
|
propagation_message_id: None,
|
||||||
@@ -1141,8 +1123,8 @@ impl MessageBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn profile(mut self, profile: DisplayProfile) -> Self {
|
pub fn participant(mut self, participant: Participant) -> Self {
|
||||||
self.profile = Some(profile);
|
self.participant = Some(participant);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1176,16 +1158,16 @@ impl MessageBuilder {
|
|||||||
|
|
||||||
pub fn build(self) -> Result<Message, MessageBuilderError> {
|
pub fn build(self) -> Result<Message, MessageBuilderError> {
|
||||||
let message = self.message.ok_or(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 call_id = self.call_id.ok_or(MessageBuilderError)?;
|
||||||
|
|
||||||
let data = MessageData {
|
let data = MessageData {
|
||||||
id: u64_to_i64(message.id.get()),
|
id: u64_to_i64(message.id.get()),
|
||||||
call_id: u64_to_i64(call_id),
|
call_id: u64_to_i64(call_id),
|
||||||
author_id: u64_to_i64(profile.id),
|
author_id: u64_to_i64(participant.user_id),
|
||||||
author_name: profile.name.clone(),
|
author_name: participant.user_name,
|
||||||
author_avatar_url: profile.avatar_url.clone(),
|
author_avatar_url: participant.user_avatar_url,
|
||||||
author_mask: profile.mask,
|
author_mask: participant.is_masked.then_some(participant.user_mask),
|
||||||
content: message.content,
|
content: message.content,
|
||||||
attachments: message.attachments.iter().map(|a| a.url.clone()).collect(),
|
attachments: message.attachments.iter().map(|a| a.url.clone()).collect(),
|
||||||
sticker: message
|
sticker: message
|
||||||
@@ -1206,7 +1188,7 @@ impl MessageBuilder {
|
|||||||
Ok(Message {
|
Ok(Message {
|
||||||
call_id,
|
call_id,
|
||||||
message_id: message.id.get(),
|
message_id: message.id.get(),
|
||||||
author_id: profile.id,
|
author_id: participant.user_id,
|
||||||
propagation_message_id: self.propagation_message_id,
|
propagation_message_id: self.propagation_message_id,
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
@@ -1276,10 +1258,8 @@ impl Message {
|
|||||||
"#,
|
"#,
|
||||||
(call_id, message_id_i64),
|
(call_id, message_id_i64),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
if result.rows_num() == 0 {
|
if result.rows_num() == 0 {
|
||||||
Self::get_by_propagation_message_id(message_id, session).await
|
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),
|
u64_to_i64(message_by_propagation_id.message_id),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
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;
|
return get_single_row_from_result(&result).await;
|
||||||
}
|
}
|
||||||
@@ -1356,10 +1372,8 @@ impl Message {
|
|||||||
"#,
|
"#,
|
||||||
(),
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
Ok(result.rows_num())
|
Ok(result.rows_num())
|
||||||
}
|
}
|
||||||
@@ -1413,10 +1427,67 @@ impl MessageByPropagationId {
|
|||||||
"#,
|
"#,
|
||||||
(propagation_message_id,),
|
(propagation_message_id,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
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
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -1637,10 +1708,8 @@ impl UserIncident {
|
|||||||
"#,
|
"#,
|
||||||
(user_id, incident_id),
|
(user_id, incident_id),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
get_single_row_from_result(&result).await
|
get_single_row_from_result(&result).await
|
||||||
}
|
}
|
||||||
@@ -1669,16 +1738,12 @@ impl UserIncident {
|
|||||||
"#,
|
"#,
|
||||||
(user_id,),
|
(user_id,),
|
||||||
)
|
)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::QueryError)?
|
.into_rows_result()?;
|
||||||
.into_rows_result()
|
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?;
|
|
||||||
|
|
||||||
Ok(result
|
Ok(result
|
||||||
.rows::<<Self as Object>::Row>()
|
.rows::<<Self as Object>::Row>()?
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Self::from_row)
|
.map(Self::from_row)
|
||||||
.collect::<Vec<_>>())
|
.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 {
|
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> {
|
pub async fn get_for_call(call_id: u64, session: &Session) -> Result<Vec<Self>, DatabaseError> {
|
||||||
let call_id = u64_to_i64(call_id);
|
let call_id = u64_to_i64(call_id);
|
||||||
|
|
||||||
let rows = session
|
let rows = session
|
||||||
.query_unpaged(
|
.query_unpaged(
|
||||||
r#"
|
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 = ?;
|
WHERE call_id = ?;
|
||||||
"#,
|
"#,
|
||||||
(call_id,),
|
(call_id,),
|
||||||
@@ -1805,27 +1904,69 @@ impl Participant {
|
|||||||
.map_err(DatabaseError::QueryError)?;
|
.map_err(DatabaseError::QueryError)?;
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_rows_result()
|
.into_rows_result()?
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
.rows()?
|
||||||
.rows()
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.map_err(|_| DatabaseError::ResultNotRows)?
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|_| DatabaseError::DeserializationFailed)?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Self::from_row)
|
.map(Self::from_row)
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(
|
pub async fn get_for_user_and_channel_in_call(
|
||||||
user_id: u64,
|
user_id: u64,
|
||||||
channel_id: u64,
|
channel_id: u64,
|
||||||
call_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,
|
is_masked: bool,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<Self, DatabaseError> {
|
||||||
let masks_in_use = Self::get_for_call(call_id, session)
|
let participants = Self::get_for_call(call_id, session).await?;
|
||||||
.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()
|
.iter()
|
||||||
.filter_map(|p| {
|
.filter_map(|p| {
|
||||||
if p.channel_id == channel_id {
|
if p.channel_id == channel_id {
|
||||||
@@ -1844,13 +1985,15 @@ impl Participant {
|
|||||||
call_id,
|
call_id,
|
||||||
user_id,
|
user_id,
|
||||||
channel_id,
|
channel_id,
|
||||||
user_name: profile.name,
|
user_name: display_name,
|
||||||
user_avatar_url: profile.avatar_url,
|
user_avatar_url: display_avatar_url,
|
||||||
user_mask: mask,
|
user_mask: mask,
|
||||||
is_masked,
|
is_masked,
|
||||||
};
|
};
|
||||||
|
|
||||||
participant.insert(session).await
|
participant.insert(session).await?;
|
||||||
|
|
||||||
|
Ok(participant)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert(&self, session: &Session) -> Result<(), DatabaseError> {
|
pub async fn insert(&self, session: &Session) -> Result<(), DatabaseError> {
|
||||||
@@ -1865,7 +2008,29 @@ impl Participant {
|
|||||||
user_avatar_url,
|
user_avatar_url,
|
||||||
user_mask,
|
user_mask,
|
||||||
is_masked
|
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()),
|
Self::to_row(self.clone()),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ impl<S, E> FromArguments<S, E> for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match arg {
|
match arg.to_lowercase().as_str() {
|
||||||
"y" | "yes" | "on" | "true" | "t" | "1" => Ok((true, rest)),
|
"y" | "yes" | "on" | "true" | "t" | "1" => Ok((true, rest)),
|
||||||
"n" | "no" | "off" | "false" | "f" | "0" => Ok((false, rest)),
|
"n" | "no" | "off" | "false" | "f" | "0" => Ok((false, rest)),
|
||||||
_ => Err(ArgumentParsingError::InvalidArgument),
|
_ => Err(ArgumentParsingError::InvalidArgument),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use scc::HashSet;
|
|||||||
use twilight_model::id::Id;
|
use twilight_model::id::Id;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::models::{Call, Channel},
|
database::models::{Call, Channel, Participant},
|
||||||
discord::sanitizing::SanitizeDiscordMarkdown,
|
discord::sanitizing::SanitizeDiscordMarkdown,
|
||||||
extras::Extras,
|
extras::Extras,
|
||||||
framework::commands::{
|
framework::commands::{
|
||||||
@@ -13,7 +13,7 @@ use crate::{
|
|||||||
permissions::Permission,
|
permissions::Permission,
|
||||||
},
|
},
|
||||||
groups::calls::{
|
groups::calls::{
|
||||||
get_display_profile,
|
masks::{get_mask_display_name, get_mask_emoji},
|
||||||
permissions::{NonNewAccountPermission, NotBannedPermission},
|
permissions::{NonNewAccountPermission, NotBannedPermission},
|
||||||
strings::{
|
strings::{
|
||||||
get_string_call_answered, get_string_call_hangup_other_party,
|
get_string_call_answered, get_string_call_hangup_other_party,
|
||||||
@@ -414,7 +414,20 @@ impl Command<State, Extras> for FriendRequestCommand {
|
|||||||
|
|
||||||
let other_channel_id = call.other_party_channel_id(channel.id);
|
let other_channel_id = call.other_party_channel_id(channel.id);
|
||||||
|
|
||||||
let profile = get_display_profile(ctx.event);
|
let Some(participant) = Participant::get_for_user_and_channel_in_call(
|
||||||
|
ctx.event.author.id.get(),
|
||||||
|
ctx.event.channel_id.get(),
|
||||||
|
call.id,
|
||||||
|
&ctx.rt.state.db,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
ctx.send()
|
||||||
|
.content("❌ You haven't even said hi! Who are you trying to add?")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
ctx
|
ctx
|
||||||
.rt
|
.rt
|
||||||
@@ -425,7 +438,7 @@ impl Command<State, Extras> for FriendRequestCommand {
|
|||||||
.content(
|
.content(
|
||||||
&format!(
|
&format!(
|
||||||
"📬 **{}** wants you to send them a friend request on Discord! Their username is `{}`.",
|
"📬 **{}** wants you to send them a friend request on Discord! Their username is `{}`.",
|
||||||
profile.name.sanitize(false),
|
participant.display_name().sanitize(false),
|
||||||
ctx.event.author.name.sanitize(false)
|
ctx.event.author.name.sanitize(false)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -505,21 +518,50 @@ impl Command<State, Extras> for MaskCommand {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
log::trace!("User is masked, checking for call...");
|
||||||
if let Some(call_id) = channel.call_id {
|
if let Some(call_id) = channel.call_id {
|
||||||
} else {
|
log::trace!("Found call ID: {call_id}");
|
||||||
ctx
|
if let Some(mut participant) = Participant::get_for_user_and_channel_in_call(
|
||||||
.send()
|
ctx.extras.user.id,
|
||||||
.content(
|
ctx.event.channel_id.get(),
|
||||||
format!(
|
call_id,
|
||||||
"🎭 Your mask was set to `{}`. You can toggle your mask on or off using `{}mask on` and `{}mask off`.",
|
&ctx.rt.state.db,
|
||||||
if ctx.extras.user.is_masked { "on" } else { "off" },
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
log::trace!(
|
||||||
|
"Participant's is_masked is being set to {}",
|
||||||
|
ctx.extras.user.is_masked
|
||||||
|
);
|
||||||
|
|
||||||
|
participant.is_masked = ctx.extras.user.is_masked;
|
||||||
|
participant.upsert(&ctx.rt.state.db).await?;
|
||||||
|
|
||||||
|
if ctx.extras.user.is_masked {
|
||||||
|
ctx.send().content(format!("🎭 Your mask was set to on. You're currently being displayed as **{} {}**. You can toggle your mask on or off using `{}mask on` and `{}mask off`.",
|
||||||
|
get_mask_emoji(&participant.user_mask).expect("User's mask did not exist"),
|
||||||
|
get_mask_display_name(&participant.user_mask),
|
||||||
ctx.prefix,
|
ctx.prefix,
|
||||||
ctx.prefix
|
ctx.prefix
|
||||||
)
|
)).await?;
|
||||||
)
|
|
||||||
.await?;
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx
|
||||||
|
.send()
|
||||||
|
.content(
|
||||||
|
format!(
|
||||||
|
"🎭 Your mask was set to `{}`. You can toggle your mask on or off using `{}mask on` and `{}mask off`.",
|
||||||
|
if ctx.extras.user.is_masked { "on" } else { "off" },
|
||||||
|
ctx.prefix,
|
||||||
|
ctx.prefix
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-28
@@ -9,17 +9,17 @@ use twilight_model::{
|
|||||||
id::Id,
|
id::Id,
|
||||||
};
|
};
|
||||||
use twilight_util::{
|
use twilight_util::{
|
||||||
builder::embed::{EmbedAuthorBuilder, EmbedBuilder, ImageSource},
|
builder::embed::{EmbedAuthorBuilder, EmbedBuilder, EmbedFooterBuilder, ImageSource},
|
||||||
snowflake::Snowflake,
|
snowflake::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::COLOR_WHITE,
|
constants::{COLOR_WHITE, COLOR_YELLOW},
|
||||||
database::models::{Call, Channel, Message, User, UserIncident},
|
database::models::{Call, Channel, Message, Participant, User, UserIncident},
|
||||||
discord::sanitizing::SanitizeDiscordMarkdown,
|
discord::{images, sanitizing::SanitizeDiscordMarkdown},
|
||||||
extras::Extras,
|
extras::Extras,
|
||||||
framework::{
|
framework::{
|
||||||
commands::{extras::ExtrasGetter, permissions::Permission},
|
commands::extras::ExtrasGetter,
|
||||||
events::traits::{
|
events::traits::{
|
||||||
EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
|
EventResult, OnMessageDeleteHandler, OnMessageEditHandler, OnMessageHandler,
|
||||||
},
|
},
|
||||||
@@ -28,9 +28,9 @@ use crate::{
|
|||||||
},
|
},
|
||||||
groups::calls::{
|
groups::calls::{
|
||||||
components::{CodeOfConductButton, NeedHelpButton},
|
components::{CodeOfConductButton, NeedHelpButton},
|
||||||
embeds, get_display_profile,
|
embeds,
|
||||||
|
masks::{get_mask_avatar_url, get_mask_display_name},
|
||||||
moderation::{Action, Rule, escape_message},
|
moderation::{Action, Rule, escape_message},
|
||||||
permissions::NonNewAccountPermission,
|
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{ellipse, i64_to_u64},
|
utils::{ellipse, i64_to_u64},
|
||||||
@@ -195,8 +195,6 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let profile = get_display_profile(event);
|
|
||||||
|
|
||||||
let (content, rules) = escape_message(event);
|
let (content, rules) = escape_message(event);
|
||||||
|
|
||||||
let message: Message;
|
let message: Message;
|
||||||
@@ -217,6 +215,30 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
};
|
};
|
||||||
let replying_to_id = replying_to.as_ref().map(|i| i.message_id);
|
let replying_to_id = replying_to.as_ref().map(|i| i.message_id);
|
||||||
|
|
||||||
|
let participant = Participant::get_or_create(
|
||||||
|
user.id,
|
||||||
|
event.channel_id.get(),
|
||||||
|
call.id,
|
||||||
|
event
|
||||||
|
.member
|
||||||
|
.clone()
|
||||||
|
.and_then(|member| member.nick)
|
||||||
|
.or(event.author.global_name.clone())
|
||||||
|
.unwrap_or(event.author.name.clone()),
|
||||||
|
images::get_avatar_url(
|
||||||
|
event
|
||||||
|
.member
|
||||||
|
.clone()
|
||||||
|
.and_then(|inner| inner.avatar)
|
||||||
|
.or(event.author.avatar),
|
||||||
|
event.author.id.get(),
|
||||||
|
event.author.discriminator as u64,
|
||||||
|
),
|
||||||
|
user.is_masked,
|
||||||
|
&rt.state.db,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if rules
|
if rules
|
||||||
.iter()
|
.iter()
|
||||||
.any(|rule| rule.action() == &Action::BlockAndPenalty)
|
.any(|rule| rule.action() == &Action::BlockAndPenalty)
|
||||||
@@ -232,14 +254,14 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
.create_message(Id::new(opposite_channel.id))
|
.create_message(Id::new(opposite_channel.id))
|
||||||
.content(&format!(
|
.content(&format!(
|
||||||
"🚩 **{}** Sent a message that was blocked.\n-# If you see an inappropiate message, please report it using `Message > Apps > Report Message`.",
|
"🚩 **{}** Sent a message that was blocked.\n-# If you see an inappropiate message, please report it using `Message > Apps > Report Message`.",
|
||||||
profile.name.sanitize(false)
|
participant.display_name().sanitize(false)
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
message = Message::build()
|
message = Message::build()
|
||||||
.call_id(call.id)
|
.call_id(call.id)
|
||||||
.message(event.0.clone())
|
.message(event.0.clone())
|
||||||
.profile(profile.clone())
|
.participant(participant)
|
||||||
.is_censored(!rules.is_empty())
|
.is_censored(!rules.is_empty())
|
||||||
.replying_to_message_id(replying_to_id)
|
.replying_to_message_id(replying_to_id)
|
||||||
.build()
|
.build()
|
||||||
@@ -250,29 +272,72 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
let mut embeds = vec![];
|
let mut embeds = vec![];
|
||||||
|
|
||||||
if let Some(replying_to) = replying_to {
|
if let Some(replying_to) = replying_to {
|
||||||
embeds.push(
|
if let Some(propagation_channel_id) = replying_to.data.propagation_channel_id {
|
||||||
EmbedBuilder::new()
|
let (display_name, avatar_url) =
|
||||||
.author(
|
if let Some(mask) = replying_to.data.author_mask {
|
||||||
EmbedAuthorBuilder::new(format!(
|
(
|
||||||
"Replying to {}",
|
if i64_to_u64(propagation_channel_id) != event.channel_id.get() {
|
||||||
replying_to.data.author_name
|
get_mask_display_name(&mask)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{} ({})",
|
||||||
|
get_mask_display_name(&mask),
|
||||||
|
replying_to.data.author_name
|
||||||
|
)
|
||||||
|
},
|
||||||
|
get_mask_avatar_url(&mask).expect("Failed to get mask avatar URL"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
replying_to.data.author_name.clone(),
|
||||||
|
replying_to.data.author_avatar_url.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
embeds.push(
|
||||||
|
EmbedBuilder::new()
|
||||||
|
.author(
|
||||||
|
EmbedAuthorBuilder::new(format!("Replying to {display_name}",))
|
||||||
|
.icon_url(ImageSource::url(avatar_url).unwrap()),
|
||||||
|
)
|
||||||
|
.color(COLOR_WHITE)
|
||||||
|
.description(ellipse(replying_to.data.content, 100))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if participant.is_masked {
|
||||||
|
let last_message =
|
||||||
|
Message::get_last_by_author_id(call.id, event.author.id.get(), &rt.state.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if last_message
|
||||||
|
.map(|lm| lm.data.author_mask.is_none())
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
embeds.push(
|
||||||
|
EmbedBuilder::new()
|
||||||
|
.color(COLOR_YELLOW)
|
||||||
|
.description(format!(
|
||||||
|
"🎭 **{}** is wearing a mask! Their real identity is hidden.",
|
||||||
|
participant.display_name()
|
||||||
))
|
))
|
||||||
.icon_url(
|
.footer(EmbedFooterBuilder::new(format!(
|
||||||
ImageSource::url(replying_to.data.author_avatar_url).unwrap(),
|
"You can set your own mask using {}mask on.",
|
||||||
),
|
prefixes[0]
|
||||||
)
|
)))
|
||||||
.color(COLOR_WHITE)
|
.build(),
|
||||||
.description(ellipse(replying_to.data.content, 100))
|
);
|
||||||
.build(),
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let message_sent = match rt
|
let message_sent = match rt
|
||||||
.http
|
.http
|
||||||
.execute_webhook(Id::new(webhook_id), &webhook_token)
|
.execute_webhook(Id::new(webhook_id), &webhook_token)
|
||||||
.allowed_mentions(None)
|
.allowed_mentions(None)
|
||||||
.username(&profile.name)
|
.username(&participant.display_name())
|
||||||
.avatar_url(&profile.avatar_url)
|
.avatar_url(&participant.avatar_url())
|
||||||
.content(&content.sanitize_mentions())
|
.content(&content.sanitize_mentions())
|
||||||
.embeds(&embeds)
|
.embeds(&embeds)
|
||||||
.wait()
|
.wait()
|
||||||
@@ -307,7 +372,7 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
message = Message::build()
|
message = Message::build()
|
||||||
.call_id(call.id)
|
.call_id(call.id)
|
||||||
.message(event.0.clone())
|
.message(event.0.clone())
|
||||||
.profile(profile.clone())
|
.participant(participant)
|
||||||
.is_censored(!rules.is_empty())
|
.is_censored(!rules.is_empty())
|
||||||
.replying_to_message_id(replying_to_id)
|
.replying_to_message_id(replying_to_id)
|
||||||
.propagation_message_id(message_sent.id.get())
|
.propagation_message_id(message_sent.id.get())
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use once_cell::sync::Lazy;
|
|||||||
use rand::{SeedableRng, seq::IndexedRandom};
|
use rand::{SeedableRng, seq::IndexedRandom};
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
|
||||||
|
use crate::utils::ToTitleCase;
|
||||||
|
|
||||||
static MASKS_COMMON: Lazy<HashMap<String, u64>> = Lazy::new(|| {
|
static MASKS_COMMON: Lazy<HashMap<String, u64>> = Lazy::new(|| {
|
||||||
let (file_name, json) = if cfg!(debug_assertions) {
|
let (file_name, json) = if cfg!(debug_assertions) {
|
||||||
("dev/common.json", include_str!("dev/common.json"))
|
("dev/common.json", include_str!("dev/common.json"))
|
||||||
@@ -11,8 +13,8 @@ static MASKS_COMMON: Lazy<HashMap<String, u64>> = Lazy::new(|| {
|
|||||||
("prod/common.json", include_str!("prod/common.json"))
|
("prod/common.json", include_str!("prod/common.json"))
|
||||||
};
|
};
|
||||||
|
|
||||||
let masks: HashMap<String, u64> =
|
let masks: HashMap<String, u64> = serde_json::from_str(json)
|
||||||
serde_json::from_str(json).expect(&format!("Failed to parse masks/{file_name} masks"));
|
.unwrap_or_else(|_| panic!("Failed to parse masks/{file_name} masks"));
|
||||||
|
|
||||||
assert_ne!(masks.len(), 0, "No masks found in masks/{file_name}");
|
assert_ne!(masks.len(), 0, "No masks found in masks/{file_name}");
|
||||||
|
|
||||||
@@ -30,3 +32,22 @@ pub fn get_random_mask(not_in: &[String]) -> Option<(String, u64)> {
|
|||||||
|
|
||||||
mask_names.choose(&mut rng).cloned()
|
mask_names.choose(&mut rng).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mask_display_name(mask: impl ToString) -> String {
|
||||||
|
mask.to_title_case()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mask_emoji(mask: impl ToString) -> Option<String> {
|
||||||
|
let emoji_name = mask.to_string().replace(" ", "");
|
||||||
|
let emoji_id = MASKS_COMMON.get(&mask.to_string())?;
|
||||||
|
|
||||||
|
Some(format!("<:{emoji_name}:{emoji_id}>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mask_avatar_url(mask: impl ToString) -> Option<String> {
|
||||||
|
let mask_emoji_id = MASKS_COMMON.get(&mask.to_string())?;
|
||||||
|
|
||||||
|
Some(format!(
|
||||||
|
"https://cdn.discordapp.com/emojis/{mask_emoji_id}.png?size=128&quality=lossless"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
//! Call commands and events.
|
//! Call commands and events.
|
||||||
|
|
||||||
use twilight_model::gateway::payload::incoming::MessageCreate;
|
|
||||||
use twilight_validate::request::webhook_username;
|
|
||||||
|
|
||||||
use crate::discord::images;
|
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub(super) mod components;
|
pub(super) mod components;
|
||||||
pub(super) mod embeds;
|
pub(super) mod embeds;
|
||||||
@@ -13,62 +8,3 @@ pub mod masks;
|
|||||||
pub(super) mod moderation;
|
pub(super) mod moderation;
|
||||||
pub(super) mod permissions;
|
pub(super) mod permissions;
|
||||||
pub(super) mod strings;
|
pub(super) mod strings;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DisplayProfile {
|
|
||||||
pub id: u64,
|
|
||||||
pub name: String,
|
|
||||||
pub avatar_url: String,
|
|
||||||
pub mask: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_display_profile(event: &MessageCreate) -> DisplayProfile {
|
|
||||||
let member = event.member.clone();
|
|
||||||
|
|
||||||
let name = if let Some(nick) = &member
|
|
||||||
.clone()
|
|
||||||
.and_then(|member| member.nick.map(make_name_valid))
|
|
||||||
{
|
|
||||||
if webhook_username(nick).is_err() {
|
|
||||||
"Invalid Nickname".to_string()
|
|
||||||
} else {
|
|
||||||
nick.clone()
|
|
||||||
}
|
|
||||||
} else if let Some(name) = event.author.global_name.clone().map(make_name_valid) {
|
|
||||||
if webhook_username(&name).is_err() {
|
|
||||||
"Invalid Global Name".to_string()
|
|
||||||
} else {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
} else if webhook_username(make_name_valid(event.author.name.clone())).is_err() {
|
|
||||||
"Invalid Username".to_string()
|
|
||||||
} else {
|
|
||||||
make_name_valid(event.author.name.clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
let avatar_hash = member
|
|
||||||
.and_then(|inner| inner.avatar)
|
|
||||||
.or(event.author.avatar);
|
|
||||||
let avatar_url = images::get_avatar_url(
|
|
||||||
avatar_hash,
|
|
||||||
event.author.id.get(),
|
|
||||||
event.author.discriminator as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
DisplayProfile {
|
|
||||||
id: event.author.id.get(),
|
|
||||||
name,
|
|
||||||
avatar_url,
|
|
||||||
mask: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use time::{Duration, UtcDateTime};
|
use time::{Duration, UtcDateTime};
|
||||||
use twilight_util::snowflake::{self, Snowflake};
|
use twilight_util::snowflake::Snowflake;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::models::UserIncident,
|
database::models::UserIncident,
|
||||||
|
|||||||
Reference in New Issue
Block a user