mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-12 19:56:48 +00:00
136 lines
3.7 KiB
Rust
136 lines
3.7 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use axum::extract::State;
|
|
use conduwuit::{
|
|
Err, Result, err,
|
|
matrix::pdu::{PartialPdu, sticky},
|
|
utils,
|
|
};
|
|
use ruma::{
|
|
OwnedEventId, api::client::message::send_message_event, events::MessageLikeEventType,
|
|
};
|
|
use serde::Deserialize;
|
|
use serde_json::from_str;
|
|
|
|
use crate::{Ruma, client_ip::ClientIp};
|
|
|
|
/// # `PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`
|
|
///
|
|
/// Send a message event into the room.
|
|
///
|
|
/// - Is a NOOP if the txn id was already used before and returns the same event
|
|
/// id again
|
|
/// - The only requirement for the content is that it has to be valid json
|
|
/// - Tries to send the event into the room, auth rules will determine if it is
|
|
/// allowed
|
|
pub(crate) async fn send_message_event_route(
|
|
State(services): State<crate::State>,
|
|
ClientIp(client_ip): ClientIp, // NOTE: required for updating device metadata
|
|
body: Ruma<send_message_event::v3::Request>,
|
|
) -> Result<send_message_event::v3::Response> {
|
|
let sender_user = body.identity.expect_sender_user()?;
|
|
let sender_device = body.identity.sender_device();
|
|
|
|
if services.users.is_suspended(sender_user).await? {
|
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
|
}
|
|
|
|
services
|
|
.users
|
|
.update_device_last_seen(sender_user, sender_device, client_ip)
|
|
.await;
|
|
|
|
// Forbid m.room.encrypted if encryption is disabled
|
|
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.config.allow_encryption
|
|
{
|
|
return Err!(Request(Forbidden("Encryption has been disabled")));
|
|
}
|
|
|
|
if body.event_type == MessageLikeEventType::CallInvite
|
|
&& services.rooms.directory.is_public_room(&body.room_id).await
|
|
{
|
|
return Err!(Request(Forbidden("Room call invites are not allowed in public rooms")));
|
|
}
|
|
|
|
// Check if this is a new transaction id
|
|
if let Ok(response) = services
|
|
.transactions
|
|
.get_client_txn(sender_user, sender_device, &body.txn_id)
|
|
.await
|
|
{
|
|
// The client might have sent a txnid of the /sendToDevice endpoint
|
|
// This txnid has no response associated with it
|
|
if response.is_empty() {
|
|
return Err!(Request(InvalidParam(
|
|
"Tried to use txn id already used for an incompatible endpoint."
|
|
)));
|
|
}
|
|
|
|
let event_id = utils::string_from_bytes(&response)
|
|
.map(TryInto::try_into)
|
|
.map_err(|e| err!(Database("Invalid event_id in txnid data: {e:?}")))??;
|
|
|
|
return Ok(send_message_event::v3::Response::new(event_id));
|
|
}
|
|
|
|
let mut unsigned = BTreeMap::new();
|
|
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
|
|
|
let content = from_str(body.body.body.json().get())
|
|
.map_err(|e| err!(Request(BadJson("Invalid JSON body: {e}"))))?;
|
|
let redacts = if body.body.event_type == MessageLikeEventType::RoomRedaction {
|
|
from_str::<MaybeRedaction>(body.body.body.json().get())
|
|
.ok()
|
|
.and_then(|r| r.redacts)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let event_id = services
|
|
.rooms
|
|
.timeline
|
|
.build_and_append_pdu(
|
|
PartialPdu {
|
|
event_type: body.event_type.clone().into(),
|
|
content,
|
|
sticky: services
|
|
.config
|
|
.allow_sticky_events
|
|
.then_some(body.sticky_duration_ms)
|
|
.flatten()
|
|
.map(sticky::object),
|
|
unsigned: Some(unsigned),
|
|
timestamp: if body.identity.is_appservice()
|
|
|| services.admin.user_is_admin(sender_user).await
|
|
{
|
|
body.timestamp
|
|
} else {
|
|
None
|
|
},
|
|
redacts,
|
|
..Default::default()
|
|
},
|
|
sender_user,
|
|
Some(&body.room_id),
|
|
None,
|
|
)
|
|
.await?;
|
|
|
|
services.transactions.add_client_txnid(
|
|
sender_user,
|
|
sender_device,
|
|
&body.txn_id,
|
|
event_id.as_bytes(),
|
|
);
|
|
|
|
Ok(send_message_event::v3::Response::new(event_id))
|
|
}
|
|
|
|
/// Struct containing exclusively the `redacts` field for MSC4169 compatible
|
|
/// redaction moving.
|
|
#[derive(Deserialize)]
|
|
struct MaybeRedaction {
|
|
#[serde(default)]
|
|
redacts: Option<OwnedEventId>,
|
|
}
|