mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
feat(messaging): implement fetch and send
This commit is contained in:
@@ -1,11 +1,23 @@
|
||||
use revolt_quark::Result;
|
||||
use revolt_quark::{models::{User, Message}, perms, Db, Error, Ref, Result};
|
||||
|
||||
use rocket::serde::json::Value;
|
||||
use rocket::serde::json::Json;
|
||||
|
||||
#[get("/<target>/messages/<msg>")]
|
||||
pub async fn req(
|
||||
/*user: UserRef, target: Ref, msg: Ref*/ target: String,
|
||||
msg: String,
|
||||
) -> Result<Value> {
|
||||
todo!()
|
||||
pub async fn req(db: &Db, user: User, target: Ref, msg: Ref) -> Result<Json<Message>> {
|
||||
let channel = target.as_channel(db).await?;
|
||||
if !perms(&user)
|
||||
.channel(&channel)
|
||||
.calc_channel(db)
|
||||
.await
|
||||
.get_view()
|
||||
{
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
let message = msg.as_message(db).await?;
|
||||
if message.channel != channel.as_id() {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
Ok(Json(message))
|
||||
}
|
||||
|
||||
@@ -1,40 +1,28 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use revolt_quark::{
|
||||
models::{message::Masquerade, User},
|
||||
Ref, Result,
|
||||
models::{
|
||||
message::{Content, Masquerade, Reply, SendableEmbed},
|
||||
Message, User,
|
||||
},
|
||||
perms, ChannelPermission, Db, Error, Ref, Result,
|
||||
};
|
||||
|
||||
use mongodb::bson::doc;
|
||||
use regex::Regex;
|
||||
use rocket::serde::json::{Json, Value};
|
||||
use rocket::serde::json::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ulid::Ulid;
|
||||
use validator::Validate;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Reply {
|
||||
id: String,
|
||||
mention: bool,
|
||||
}
|
||||
|
||||
#[derive(Validate, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SendableEmbed {
|
||||
icon_url: Option<String>,
|
||||
url: Option<String>,
|
||||
#[validate(length(min = 1, max = 100))]
|
||||
title: Option<String>,
|
||||
#[validate(length(min = 1, max = 2000))]
|
||||
description: Option<String>,
|
||||
media: Option<String>,
|
||||
colour: Option<String>,
|
||||
}
|
||||
#[derive(Validate, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
#[validate(length(min = 0, max = 2000))]
|
||||
content: String,
|
||||
#[validate(length(min = 1, max = 128))]
|
||||
attachments: Option<Vec<String>>,
|
||||
nonce: Option<String>,
|
||||
replies: Option<Vec<Reply>>,
|
||||
//#[validate]
|
||||
#[validate]
|
||||
masquerade: Option<Masquerade>,
|
||||
#[validate(length(min = 1, max = 10))]
|
||||
embeds: Option<Vec<SendableEmbed>>,
|
||||
@@ -45,7 +33,126 @@ lazy_static! {
|
||||
static ref RE_MENTION: Regex = Regex::new(r"<@([0-9A-HJKMNP-TV-Z]{26})>").unwrap();
|
||||
}
|
||||
|
||||
#[post("/<target>/messages", data = "<message>")]
|
||||
pub async fn message_send(user: User, target: Ref, message: Json<Data>) -> Result<Value> {
|
||||
todo!()
|
||||
#[post("/<target>/messages", data = "<data>")]
|
||||
pub async fn message_send(
|
||||
db: &Db,
|
||||
user: User,
|
||||
target: Ref,
|
||||
data: Json<Data>,
|
||||
) -> Result<Json<Message>> {
|
||||
let data = data.into_inner();
|
||||
data.validate()
|
||||
.map_err(|error| Error::FailedValidation { error })?;
|
||||
|
||||
let channel = target.as_channel(db).await?;
|
||||
let permissions = perms(&user).channel(&channel).calc_channel(db).await;
|
||||
|
||||
if !permissions.get_send_message() {
|
||||
return Err(Error::MissingPermission {
|
||||
permission: ChannelPermission::SendMessage as i32,
|
||||
});
|
||||
}
|
||||
|
||||
if data.content.is_empty()
|
||||
&& (data.attachments.is_none() || data.attachments.as_ref().unwrap().is_empty())
|
||||
{
|
||||
return Err(Error::EmptyMessage);
|
||||
}
|
||||
|
||||
let message_id = Ulid::new().to_string();
|
||||
let mut message = Message {
|
||||
id: message_id.clone(),
|
||||
channel: channel.as_id(),
|
||||
author: user.id,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// 1. Parse mentions in message.
|
||||
let mut mentions = HashSet::new();
|
||||
for capture in RE_MENTION.captures_iter(&data.content) {
|
||||
if let Some(mention) = capture.get(1) {
|
||||
mentions.insert(mention.as_str().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Verify permissions for masquerade.
|
||||
if data.masquerade.is_some() && !permissions.get_masquerade() {
|
||||
return Err(Error::MissingPermission {
|
||||
permission: ChannelPermission::Masquerade as i32,
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Verify replies are valid.
|
||||
let mut replies = HashSet::new();
|
||||
if let Some(entries) = data.replies {
|
||||
if entries.len() > 5 {
|
||||
return Err(Error::TooManyReplies);
|
||||
}
|
||||
|
||||
for Reply { id, mention } in entries {
|
||||
let message = Ref::from_unchecked(id).as_message(db).await?;
|
||||
|
||||
replies.insert(message.id);
|
||||
|
||||
if mention {
|
||||
mentions.insert(message.author);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !mentions.is_empty() {
|
||||
message
|
||||
.mentions
|
||||
.replace(mentions.into_iter().collect::<Vec<String>>());
|
||||
}
|
||||
|
||||
if !replies.is_empty() {
|
||||
message
|
||||
.replies
|
||||
.replace(replies.into_iter().collect::<Vec<String>>());
|
||||
}
|
||||
|
||||
// 4. Add attachments to message.
|
||||
let mut attachments = vec![];
|
||||
if let Some(ids) = &data.attachments {
|
||||
if !ids.is_empty() && !permissions.get_upload_files() {
|
||||
return Err(Error::MissingPermission {
|
||||
permission: ChannelPermission::UploadFiles as i32,
|
||||
});
|
||||
}
|
||||
|
||||
// ! FIXME: move this to app config
|
||||
if ids.len() > 5 {
|
||||
return Err(Error::TooManyAttachments);
|
||||
}
|
||||
|
||||
for attachment_id in ids {
|
||||
attachments.push(
|
||||
db.find_and_use_attachment(attachment_id, "attachments", "message", &message_id)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !attachments.is_empty() {
|
||||
message.attachments.replace(attachments);
|
||||
}
|
||||
|
||||
// 5. Process included embeds.
|
||||
let mut embeds = vec![];
|
||||
if let Some(sendable_embeds) = data.embeds {
|
||||
for sendable_embed in sendable_embeds {
|
||||
embeds.push(sendable_embed.into_embed(db, message_id.clone()).await?)
|
||||
}
|
||||
}
|
||||
|
||||
if !embeds.is_empty() {
|
||||
message.embeds.replace(embeds);
|
||||
}
|
||||
|
||||
// 6. Set content
|
||||
message.content = Content::Text(data.content);
|
||||
|
||||
db.insert_message(&message).await?;
|
||||
Ok(Json(message))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user