feat: Send push notifications for dm call start/end

This commit is contained in:
IAmTomahawkx
2025-09-06 15:14:48 -07:00
parent d03e6be2eb
commit 77a23239ea
21 changed files with 568 additions and 80 deletions

View File

@@ -25,6 +25,7 @@ mod message_unreact;
mod permissions_set;
mod permissions_set_default;
mod voice_join;
mod voice_stop_ring;
mod webhook_create;
mod webhook_fetch_all;
@@ -49,6 +50,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
group_add_member::add_member,
group_remove_member::remove_member,
voice_join::call,
voice_stop_ring::stop_ring,
permissions_set::set_role_permissions,
permissions_set_default::set_default_channel_permissions,
message_react::react_message,

View File

@@ -5,7 +5,7 @@ use revolt_database::{
delete_voice_state, get_channel_node, get_user_voice_channels, get_voice_channel_members,
raise_if_in_voice, VoiceClient,
},
Database, User,
Channel, Database, SystemMessage, User, AMQP,
};
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
@@ -20,6 +20,7 @@ use rocket::{serde::json::Json, State};
#[post("/<target>/join_call", data = "<data>")]
pub async fn call(
db: &State<Database>,
amqp: &State<AMQP>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference<'_>,
@@ -99,6 +100,42 @@ pub async fn call(
log::debug!("Created room {}", room.name);
match &channel {
Channel::DirectMessage { .. } | Channel::Group { .. } => {
let mut msg = SystemMessage::CallStarted {
by: user.id.clone(),
finished_at: None,
}
.into_message(channel.id().to_string());
msg.send(
db,
Some(amqp),
v0::MessageAuthor::System {
username: &user.username,
avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
},
None,
None,
&channel,
false,
)
.await?;
let now = iso8601_timestamp::Timestamp::now_utc()
.format_short()
.to_string();
if let Err(e) = amqp
.dm_call_updated(&user.id, channel.id(), Some(&now), false, None)
.await
{
revolt_config::capture_error(&e);
}
}
_ => {}
};
Ok(Json(v0::CreateVoiceUserResponse {
token,
url: node_host.clone(),

View File

@@ -0,0 +1,69 @@
use revolt_database::{
util::reference::Reference,
voice::{get_voice_state, VoiceClient},
Channel, Database, User, AMQP,
};
use revolt_result::{create_error, Result, ToRevoltError};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Stop Ring
/// Stops ringing a specific user in a dm call.
/// You must be in the call to use this endpoint, returns NotConnected otherwise.
/// Only valid in DM/Group channels, will return NoEffect in servers.
/// Returns NotFound if the user is not in the dm/group channel
#[openapi(tag = "Voice")]
#[put("/<target>/end_ring/<target_user>")]
pub async fn stop_ring(
db: &State<Database>,
amqp: &State<AMQP>,
voice: &State<VoiceClient>,
user: User,
target: Reference<'_>,
target_user: Reference<'_>,
) -> Result<EmptyResponse> {
if !voice.is_enabled() {
return Err(create_error!(LiveKitUnavailable));
}
let channel = target.as_channel(db).await?;
if channel.server().is_some() {
return Err(create_error!(NoEffect));
}
if get_voice_state(channel.id(), None, &user.id)
.await?
.is_none()
{
return Err(create_error!(NotConnected));
}
let members = match channel {
Channel::DirectMessage { ref recipients, .. } | Channel::Group { ref recipients, .. } => {
recipients
}
_ => return Err(create_error!(NoEffect)),
};
if members.iter().any(|m| &target_user.id == m) {
if let Err(e) = amqp
.dm_call_updated(
&user.id,
channel.id(),
None,
true,
Some(vec![target_user.id.to_string()]),
)
.await
.to_internal_error()
{
revolt_config::capture_internal_error!(&e);
return Err(e);
}
Ok(EmptyResponse)
} else {
Err(create_error!(NotFound))
}
}

View File

@@ -27,7 +27,7 @@ pub struct VoiceNode {
pub name: String,
pub lat: f64,
pub lon: f64,
pub public_url: String
pub public_url: String,
}
/// # Voice Server Configuration
@@ -115,18 +115,24 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
},
livekit: VoiceFeature {
enabled: !config.hosts.livekit.is_empty(),
nodes: config.api.livekit.nodes
nodes: config
.api
.livekit
.nodes
.iter()
.filter(|(_, node)| !node.private)
.map(|(name, value)| {
VoiceNode {
name: name.clone(),
lat: value.lat,
lon: value.lon,
public_url: config.hosts.livekit.get(name).expect("Missing corresponding host for voice node").clone()
}
.map(|(name, value)| VoiceNode {
name: name.clone(),
lat: value.lat,
lon: value.lon,
public_url: config
.hosts
.livekit
.get(name)
.expect("Missing corresponding host for voice node")
.clone(),
})
.collect()
.collect(),
},
},
ws: config.hosts.events,