Merge pull request #408 from shaksternano/main
feat: add option to send message with missing replies
This commit is contained in:
@@ -8,7 +8,7 @@ use revolt_models::v0::{
|
|||||||
MessageWebhook, PushNotification, ReplyIntent, SendableEmbed, Text, RE_MENTION,
|
MessageWebhook, PushNotification, ReplyIntent, SendableEmbed, Text, RE_MENTION,
|
||||||
};
|
};
|
||||||
use revolt_permissions::{ChannelPermission, PermissionValue};
|
use revolt_permissions::{ChannelPermission, PermissionValue};
|
||||||
use revolt_result::Result;
|
use revolt_result::{ErrorType, Result};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
@@ -327,14 +327,31 @@ impl Message {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
for ReplyIntent { id, mention } in entries {
|
for ReplyIntent {
|
||||||
let message = db.fetch_message(&id).await?;
|
id,
|
||||||
|
mention,
|
||||||
|
fail_if_not_exists,
|
||||||
|
} in entries
|
||||||
|
{
|
||||||
|
match db.fetch_message(&id).await {
|
||||||
|
// Referenced message exists
|
||||||
|
Ok(message) => {
|
||||||
|
if mention && allow_mentions {
|
||||||
|
mentions.insert(message.author.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
if mention && allow_mentions {
|
replies.insert(message.id);
|
||||||
mentions.insert(message.author.to_owned());
|
}
|
||||||
|
// If the referenced message doesn't exist and fail_if_not_exists
|
||||||
|
// is set to false, send the message without the reply.
|
||||||
|
Err(e) => {
|
||||||
|
if !matches!(e.error_type, ErrorType::NotFound)
|
||||||
|
|| fail_if_not_exists.unwrap_or(true)
|
||||||
|
{
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replies.insert(message.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -239,6 +239,10 @@ auto_derived!(
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
/// Whether this reply should mention the message's author
|
/// Whether this reply should mention the message's author
|
||||||
pub mention: bool,
|
pub mention: bool,
|
||||||
|
/// Whether to error if the referenced message doesn't exist.
|
||||||
|
/// Otherwise, send a message without this reply.
|
||||||
|
/// Default is true.
|
||||||
|
pub fail_if_not_exists: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to send
|
/// Message to send
|
||||||
|
|||||||
@@ -323,4 +323,163 @@ mod test {
|
|||||||
"Mention was scrubbed when the user can see the channel"
|
"Mention was scrubbed when the user can see the channel"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rocket::async_test]
|
||||||
|
async fn message_reply() {
|
||||||
|
let harness = TestHarness::new().await;
|
||||||
|
let (_, _, user) = harness.new_user().await;
|
||||||
|
let (server, channels) = harness.new_server(&user).await;
|
||||||
|
let channel = harness.new_channel(&server).await;
|
||||||
|
let (_, member, message) = harness.new_message(&user, &server, channels).await;
|
||||||
|
|
||||||
|
// Send a message with a reply
|
||||||
|
// Should succeed
|
||||||
|
let message_with_reply = Message::create_from_api(
|
||||||
|
&harness.db,
|
||||||
|
Some(&harness.amqp),
|
||||||
|
channel.clone(),
|
||||||
|
v0::DataMessageSend {
|
||||||
|
content: Some("Message with reply".to_string()),
|
||||||
|
nonce: None,
|
||||||
|
attachments: None,
|
||||||
|
replies: Some(vec![v0::ReplyIntent {
|
||||||
|
id: message.id.clone(),
|
||||||
|
mention: false,
|
||||||
|
fail_if_not_exists: Some(true),
|
||||||
|
}]),
|
||||||
|
embeds: None,
|
||||||
|
masquerade: None,
|
||||||
|
interactions: None,
|
||||||
|
flags: None,
|
||||||
|
},
|
||||||
|
v0::MessageAuthor::User(&user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(member.clone().into()),
|
||||||
|
user.limits().await,
|
||||||
|
IdempotencyKey::unchecked_from_string("1".to_string()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create message with reply");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
message_with_reply.replies.is_some(),
|
||||||
|
"Message replies is None",
|
||||||
|
);
|
||||||
|
|
||||||
|
let replies = message_with_reply.replies.unwrap();
|
||||||
|
|
||||||
|
assert!(!replies.is_empty(), "Message replies is empty",);
|
||||||
|
|
||||||
|
assert_eq!(replies[0], message.id, "Message reply ID does not match",);
|
||||||
|
|
||||||
|
// Delete the message
|
||||||
|
message
|
||||||
|
.clone()
|
||||||
|
.delete(&harness.db)
|
||||||
|
.await
|
||||||
|
.expect("Failed to delete message");
|
||||||
|
|
||||||
|
// Attempt to create messages with a reply to a deleted message
|
||||||
|
|
||||||
|
// fail_if_not_exists is set to false
|
||||||
|
// Should send the message without a reply
|
||||||
|
let message_with_missing_reply = Message::create_from_api(
|
||||||
|
&harness.db,
|
||||||
|
Some(&harness.amqp),
|
||||||
|
channel.clone(),
|
||||||
|
v0::DataMessageSend {
|
||||||
|
content: Some("Message with missing reply".to_string()),
|
||||||
|
nonce: None,
|
||||||
|
attachments: None,
|
||||||
|
replies: Some(vec![v0::ReplyIntent {
|
||||||
|
id: message.id.clone(),
|
||||||
|
mention: false,
|
||||||
|
fail_if_not_exists: Some(false),
|
||||||
|
}]),
|
||||||
|
embeds: None,
|
||||||
|
masquerade: None,
|
||||||
|
interactions: None,
|
||||||
|
flags: None,
|
||||||
|
},
|
||||||
|
v0::MessageAuthor::User(&user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(member.clone().into()),
|
||||||
|
user.limits().await,
|
||||||
|
IdempotencyKey::unchecked_from_string("3".to_string()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create message with missing reply");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
message_with_missing_reply.replies.is_none()
|
||||||
|
|| message_with_missing_reply.replies.unwrap().is_empty(),
|
||||||
|
"Message replies exist when they shouldn't",
|
||||||
|
);
|
||||||
|
|
||||||
|
// fail_if_not_exists is set to true
|
||||||
|
// Should fail to send the message
|
||||||
|
Message::create_from_api(
|
||||||
|
&harness.db,
|
||||||
|
Some(&harness.amqp),
|
||||||
|
channel.clone(),
|
||||||
|
v0::DataMessageSend {
|
||||||
|
content: Some("Message with missing reply".to_string()),
|
||||||
|
nonce: None,
|
||||||
|
attachments: None,
|
||||||
|
replies: Some(vec![v0::ReplyIntent {
|
||||||
|
id: message.id.clone(),
|
||||||
|
mention: false,
|
||||||
|
fail_if_not_exists: Some(true),
|
||||||
|
}]),
|
||||||
|
embeds: None,
|
||||||
|
masquerade: None,
|
||||||
|
interactions: None,
|
||||||
|
flags: None,
|
||||||
|
},
|
||||||
|
v0::MessageAuthor::User(&user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(member.clone().into()),
|
||||||
|
user.limits().await,
|
||||||
|
IdempotencyKey::unchecked_from_string("4".to_string()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect_err("Created message with missing reply and true fail");
|
||||||
|
|
||||||
|
// fail_if_not_exists is not set
|
||||||
|
// Should fail to send the message
|
||||||
|
Message::create_from_api(
|
||||||
|
&harness.db,
|
||||||
|
Some(&harness.amqp),
|
||||||
|
channel.clone(),
|
||||||
|
v0::DataMessageSend {
|
||||||
|
content: Some("Message with missing reply".to_string()),
|
||||||
|
nonce: None,
|
||||||
|
attachments: None,
|
||||||
|
replies: Some(vec![v0::ReplyIntent {
|
||||||
|
id: message.id.clone(),
|
||||||
|
mention: false,
|
||||||
|
fail_if_not_exists: None,
|
||||||
|
}]),
|
||||||
|
embeds: None,
|
||||||
|
masquerade: None,
|
||||||
|
interactions: None,
|
||||||
|
flags: None,
|
||||||
|
},
|
||||||
|
v0::MessageAuthor::User(&user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(user.clone().into(&harness.db, Some(&user)).await),
|
||||||
|
Some(member.clone().into()),
|
||||||
|
user.limits().await,
|
||||||
|
IdempotencyKey::unchecked_from_string("4".to_string()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect_err("Created message with missing reply and none fail");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,22 @@ impl TestHarness {
|
|||||||
.expect("Failed to create test server")
|
.expect("Failed to create test server")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn new_channel(&self, server: &Server) -> Channel {
|
||||||
|
Channel::create_server_channel(
|
||||||
|
&self.db,
|
||||||
|
&mut server.clone(),
|
||||||
|
v0::DataCreateServerChannel {
|
||||||
|
channel_type: v0::LegacyServerChannelType::Text,
|
||||||
|
name: "Test Channel".to_string(),
|
||||||
|
description: None,
|
||||||
|
nsfw: Some(false),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to make test channel")
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn new_message(
|
pub async fn new_message(
|
||||||
&self,
|
&self,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
|||||||
Reference in New Issue
Block a user