add outgoing federation typing and conf items
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
4a57592378
commit
18c34434bc
|
@ -563,6 +563,22 @@ url_preview_check_root_domain = false
|
||||||
# Defaults to true.
|
# Defaults to true.
|
||||||
#allow_incoming_read_receipts = true
|
#allow_incoming_read_receipts = true
|
||||||
|
|
||||||
|
# Config option to control outgoing typing updates to federation. Defaults to true.
|
||||||
|
#allow_outgoing_typing = true
|
||||||
|
|
||||||
|
# Config option to control incoming typing updates from federation. Defaults to true.
|
||||||
|
#allow_incoming_typing = true
|
||||||
|
|
||||||
|
# Config option to control maximum time federation user can indicate typing.
|
||||||
|
#typing_federation_timeout_s = 30
|
||||||
|
|
||||||
|
# Config option to control minimum time local client can indicate typing. This does not override
|
||||||
|
# a client's request to stop typing. It only enforces a minimum value in case of no stop request.
|
||||||
|
#typing_client_timeout_min_s = 15
|
||||||
|
|
||||||
|
# Config option to control maximum time local client can indicate typing.
|
||||||
|
#typing_client_timeout_max_s = 45
|
||||||
|
|
||||||
|
|
||||||
# Other options not in [global]:
|
# Other options not in [global]:
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,15 +21,16 @@ pub async fn create_typing_event_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Typing::Yes(duration) = body.state {
|
if let Typing::Yes(duration) = body.state {
|
||||||
|
let duration = utils::clamp(
|
||||||
|
duration.as_millis() as u64,
|
||||||
|
services().globals.config.typing_client_timeout_min_s * 1000,
|
||||||
|
services().globals.config.typing_client_timeout_max_s * 1000,
|
||||||
|
);
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.edus
|
.edus
|
||||||
.typing
|
.typing
|
||||||
.typing_add(
|
.typing_add(sender_user, &body.room_id, utils::millis_since_unix_epoch() + duration)
|
||||||
sender_user,
|
|
||||||
&body.room_id,
|
|
||||||
duration.as_millis() as u64 + utils::millis_since_unix_epoch(),
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
services()
|
services()
|
||||||
|
|
|
@ -956,17 +956,23 @@ pub async fn send_transaction_message_route(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Edu::Typing(typing) => {
|
Edu::Typing(typing) => {
|
||||||
|
if !services().globals.config.allow_incoming_typing {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if services()
|
if services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.is_joined(&typing.user_id, &typing.room_id)?
|
.is_joined(&typing.user_id, &typing.room_id)?
|
||||||
{
|
{
|
||||||
if typing.typing {
|
if typing.typing {
|
||||||
|
let timeout = utils::millis_since_unix_epoch()
|
||||||
|
+ services().globals.config.typing_federation_timeout_s * 1000;
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.edus
|
.edus
|
||||||
.typing
|
.typing
|
||||||
.typing_add(&typing.user_id, &typing.room_id, 3000 + utils::millis_since_unix_epoch())
|
.typing_add(&typing.user_id, &typing.room_id, timeout)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
services()
|
services()
|
||||||
|
|
|
@ -195,6 +195,17 @@ pub struct Config {
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_incoming_read_receipts: bool,
|
pub allow_incoming_read_receipts: bool,
|
||||||
|
|
||||||
|
#[serde(default = "true_fn")]
|
||||||
|
pub allow_outgoing_typing: bool,
|
||||||
|
#[serde(default = "true_fn")]
|
||||||
|
pub allow_incoming_typing: bool,
|
||||||
|
#[serde(default = "default_typing_federation_timeout_s")]
|
||||||
|
pub typing_federation_timeout_s: u64,
|
||||||
|
#[serde(default = "default_typing_client_timeout_min_s")]
|
||||||
|
pub typing_client_timeout_min_s: u64,
|
||||||
|
#[serde(default = "default_typing_client_timeout_max_s")]
|
||||||
|
pub typing_client_timeout_max_s: u64,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub zstd_compression: bool,
|
pub zstd_compression: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -390,6 +401,14 @@ impl fmt::Display for Config {
|
||||||
"Block non-admin room invites (local and remote, admins can still send and receive invites)",
|
"Block non-admin room invites (local and remote, admins can still send and receive invites)",
|
||||||
&self.block_non_admin_invites.to_string(),
|
&self.block_non_admin_invites.to_string(),
|
||||||
),
|
),
|
||||||
|
("Allow outgoing federated typing", &self.allow_outgoing_typing.to_string()),
|
||||||
|
("Allow incoming federated typing", &self.allow_incoming_typing.to_string()),
|
||||||
|
(
|
||||||
|
"Incoming federated typing timeout",
|
||||||
|
&self.typing_federation_timeout_s.to_string(),
|
||||||
|
),
|
||||||
|
("Client typing timeout minimum", &self.typing_client_timeout_min_s.to_string()),
|
||||||
|
("Client typing timeout maxmimum", &self.typing_client_timeout_max_s.to_string()),
|
||||||
("Allow device name federation", &self.allow_device_name_federation.to_string()),
|
("Allow device name federation", &self.allow_device_name_federation.to_string()),
|
||||||
("Notification push path", &self.notification_push_path),
|
("Notification push path", &self.notification_push_path),
|
||||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||||
|
@ -639,6 +658,12 @@ fn default_presence_idle_timeout_s() -> u64 { 2 * 60 }
|
||||||
|
|
||||||
fn default_presence_offline_timeout_s() -> u64 { 15 * 60 }
|
fn default_presence_offline_timeout_s() -> u64 { 15 * 60 }
|
||||||
|
|
||||||
|
fn default_typing_federation_timeout_s() -> u64 { 30 }
|
||||||
|
|
||||||
|
fn default_typing_client_timeout_min_s() -> u64 { 15 }
|
||||||
|
|
||||||
|
fn default_typing_client_timeout_max_s() -> u64 { 45 }
|
||||||
|
|
||||||
fn default_rocksdb_recovery_mode() -> u8 { 1 }
|
fn default_rocksdb_recovery_mode() -> u8 { 1 }
|
||||||
|
|
||||||
fn default_rocksdb_log_level() -> String { "error".to_owned() }
|
fn default_rocksdb_log_level() -> String { "error".to_owned() }
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use ruma::{events::SyncEphemeralRoomEvent, OwnedRoomId, OwnedUserId, RoomId, UserId};
|
use ruma::{
|
||||||
|
api::federation::transactions::edu::{Edu, TypingContent},
|
||||||
|
events::SyncEphemeralRoomEvent,
|
||||||
|
OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||||
|
};
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{services, utils, Result};
|
use crate::{services, utils, Result};
|
||||||
|
|
||||||
|
@ -16,6 +21,8 @@ impl Service {
|
||||||
/// Sets a user as typing until the timeout timestamp is reached or
|
/// Sets a user as typing until the timeout timestamp is reached or
|
||||||
/// roomtyping_remove is called.
|
/// roomtyping_remove is called.
|
||||||
pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> {
|
pub async fn typing_add(&self, user_id: &UserId, room_id: &RoomId, timeout: u64) -> Result<()> {
|
||||||
|
debug!("typing add {:?} in {:?} timeout:{:?}", user_id, room_id, timeout);
|
||||||
|
// update clients
|
||||||
self.typing
|
self.typing
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
|
@ -27,11 +34,19 @@ impl Service {
|
||||||
.await
|
.await
|
||||||
.insert(room_id.to_owned(), services().globals.next_count()?);
|
.insert(room_id.to_owned(), services().globals.next_count()?);
|
||||||
_ = self.typing_update_sender.send(room_id.to_owned());
|
_ = self.typing_update_sender.send(room_id.to_owned());
|
||||||
|
|
||||||
|
// update federation
|
||||||
|
if user_id.server_name() == services().globals.server_name() {
|
||||||
|
self.federation_send(room_id, user_id, true)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a user from typing before the timeout is reached.
|
/// Removes a user from typing before the timeout is reached.
|
||||||
pub async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> {
|
pub async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> {
|
||||||
|
debug!("typing remove {:?} in {:?}", user_id, room_id);
|
||||||
|
// update clients
|
||||||
self.typing
|
self.typing
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
|
@ -43,6 +58,12 @@ impl Service {
|
||||||
.await
|
.await
|
||||||
.insert(room_id.to_owned(), services().globals.next_count()?);
|
.insert(room_id.to_owned(), services().globals.next_count()?);
|
||||||
_ = self.typing_update_sender.send(room_id.to_owned());
|
_ = self.typing_update_sender.send(room_id.to_owned());
|
||||||
|
|
||||||
|
// update federation
|
||||||
|
if user_id.server_name() == services().globals.server_name() {
|
||||||
|
self.federation_send(room_id, user_id, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,14 +101,23 @@ impl Service {
|
||||||
if !removable.is_empty() {
|
if !removable.is_empty() {
|
||||||
let typing = &mut self.typing.write().await;
|
let typing = &mut self.typing.write().await;
|
||||||
let room = typing.entry(room_id.to_owned()).or_default();
|
let room = typing.entry(room_id.to_owned()).or_default();
|
||||||
for user in removable {
|
for user in &removable {
|
||||||
room.remove(&user);
|
debug!("typing maintain remove {:?} in {:?}", &user, room_id);
|
||||||
|
room.remove(user);
|
||||||
}
|
}
|
||||||
|
// update clients
|
||||||
self.last_typing_update
|
self.last_typing_update
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.insert(room_id.to_owned(), services().globals.next_count()?);
|
.insert(room_id.to_owned(), services().globals.next_count()?);
|
||||||
_ = self.typing_update_sender.send(room_id.to_owned());
|
_ = self.typing_update_sender.send(room_id.to_owned());
|
||||||
|
|
||||||
|
// update federation
|
||||||
|
for user in removable {
|
||||||
|
if user.server_name() == services().globals.server_name() {
|
||||||
|
self.federation_send(room_id, &user, false)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -121,4 +151,22 @@ impl Service {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn federation_send(&self, room_id: &RoomId, user_id: &UserId, typing: bool) -> Result<()> {
|
||||||
|
debug_assert!(
|
||||||
|
user_id.server_name() == services().globals.server_name(),
|
||||||
|
"tried to broadcast typing status of remote user",
|
||||||
|
);
|
||||||
|
if !services().globals.config.allow_outgoing_typing {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let edu = Edu::Typing(TypingContent::new(room_id.to_owned(), user_id.to_owned(), typing));
|
||||||
|
|
||||||
|
services()
|
||||||
|
.sending
|
||||||
|
.send_edu_room(room_id, serde_json::to_vec(&edu).expect("Serialized Edu::Typing"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue