Compare commits

..
Author SHA1 Message Date
İspik 79943efdf7 Merge branch 'main' into fix/adjust-bit-manipulation 2026-05-31 10:20:17 +03:00
İspik bd987bf72a chore: update unicode emoji list (#781)
Signed-off-by: ispik <ispik@ispik.dev>
2026-05-28 14:55:49 -07:00
ispik abe3cf87a1 fix: adjust bit manipulation
Signed-off-by: ispik <ispik@ispik.dev>
2026-05-28 22:55:13 +03:00
5 changed files with 160 additions and 157 deletions
@@ -1,4 +1,3 @@
*️⃣ *️⃣
0️⃣ 0️⃣
1️⃣ 1️⃣
@@ -242,6 +241,7 @@
🆘 🆘
🆙 🆙
🆚 🆚
🇦
🇦🇨 🇦🇨
🇦🇩 🇦🇩
🇦🇪 🇦🇪
@@ -259,6 +259,7 @@
🇦🇼 🇦🇼
🇦🇽 🇦🇽
🇦🇿 🇦🇿
🇧
🇧🇦 🇧🇦
🇧🇧 🇧🇧
🇧🇩 🇧🇩
@@ -280,6 +281,7 @@
🇧🇼 🇧🇼
🇧🇾 🇧🇾
🇧🇿 🇧🇿
🇨
🇨🇦 🇨🇦
🇨🇨 🇨🇨
🇨🇩 🇨🇩
@@ -301,6 +303,7 @@
🇨🇽 🇨🇽
🇨🇾 🇨🇾
🇨🇿 🇨🇿
🇩
🇩🇪 🇩🇪
🇩🇬 🇩🇬
🇩🇯 🇩🇯
@@ -308,6 +311,7 @@
🇩🇲 🇩🇲
🇩🇴 🇩🇴
🇩🇿 🇩🇿
🇪
🇪🇦 🇪🇦
🇪🇨 🇪🇨
🇪🇪 🇪🇪
@@ -317,12 +321,14 @@
🇪🇸 🇪🇸
🇪🇹 🇪🇹
🇪🇺 🇪🇺
🇫
🇫🇮 🇫🇮
🇫🇯 🇫🇯
🇫🇰 🇫🇰
🇫🇲 🇫🇲
🇫🇴 🇫🇴
🇫🇷 🇫🇷
🇬
🇬🇦 🇬🇦
🇬🇧 🇬🇧
🇬🇩 🇬🇩
@@ -342,12 +348,14 @@
🇬🇺 🇬🇺
🇬🇼 🇬🇼
🇬🇾 🇬🇾
🇭
🇭🇰 🇭🇰
🇭🇲 🇭🇲
🇭🇳 🇭🇳
🇭🇷 🇭🇷
🇭🇹 🇭🇹
🇭🇺 🇭🇺
🇮
🇮🇨 🇮🇨
🇮🇩 🇮🇩
🇮🇪 🇮🇪
@@ -359,10 +367,12 @@
🇮🇷 🇮🇷
🇮🇸 🇮🇸
🇮🇹 🇮🇹
🇯
🇯🇪 🇯🇪
🇯🇲 🇯🇲
🇯🇴 🇯🇴
🇯🇵 🇯🇵
🇰
🇰🇪 🇰🇪
🇰🇬 🇰🇬
🇰🇭 🇰🇭
@@ -374,6 +384,7 @@
🇰🇼 🇰🇼
🇰🇾 🇰🇾
🇰🇿 🇰🇿
🇱
🇱🇦 🇱🇦
🇱🇧 🇱🇧
🇱🇨 🇱🇨
@@ -385,6 +396,7 @@
🇱🇺 🇱🇺
🇱🇻 🇱🇻
🇱🇾 🇱🇾
🇲
🇲🇦 🇲🇦
🇲🇨 🇲🇨
🇲🇩 🇲🇩
@@ -408,6 +420,7 @@
🇲🇽 🇲🇽
🇲🇾 🇲🇾
🇲🇿 🇲🇿
🇳
🇳🇦 🇳🇦
🇳🇨 🇳🇨
🇳🇪 🇳🇪
@@ -420,7 +433,9 @@
🇳🇷 🇳🇷
🇳🇺 🇳🇺
🇳🇿 🇳🇿
🇴
🇴🇲 🇴🇲
🇵
🇵🇦 🇵🇦
🇵🇪 🇵🇪
🇵🇫 🇵🇫
@@ -435,12 +450,15 @@
🇵🇹 🇵🇹
🇵🇼 🇵🇼
🇵🇾 🇵🇾
🇶
🇶🇦 🇶🇦
🇷
🇷🇪 🇷🇪
🇷🇴 🇷🇴
🇷🇸 🇷🇸
🇷🇺 🇷🇺
🇷🇼 🇷🇼
🇸
🇸🇦 🇸🇦
🇸🇧 🇸🇧
🇸🇨 🇸🇨
@@ -462,6 +480,7 @@
🇸🇽 🇸🇽
🇸🇾 🇸🇾
🇸🇿 🇸🇿
🇹
🇹🇦 🇹🇦
🇹🇨 🇹🇨
🇹🇩 🇹🇩
@@ -479,6 +498,7 @@
🇹🇻 🇹🇻
🇹🇼 🇹🇼
🇹🇿 🇹🇿
🇺
🇺🇦 🇺🇦
🇺🇬 🇺🇬
🇺🇲 🇺🇲
@@ -486,6 +506,7 @@
🇺🇸 🇺🇸
🇺🇾 🇺🇾
🇺🇿 🇺🇿
🇻
🇻🇦 🇻🇦
🇻🇨 🇻🇨
🇻🇪 🇻🇪
@@ -493,11 +514,15 @@
🇻🇮 🇻🇮
🇻🇳 🇻🇳
🇻🇺 🇻🇺
🇼
🇼🇫 🇼🇫
🇼🇸 🇼🇸
🇽
🇽🇰 🇽🇰
🇾
🇾🇪 🇾🇪
🇾🇹 🇾🇹
🇿
🇿🇦 🇿🇦
🇿🇲 🇿🇲
🇿🇼 🇿🇼
@@ -219,7 +219,7 @@ impl MessageFlagsValue {
self.has_value(flag as u32) self.has_value(flag as u32)
} }
pub fn has_value(&self, bit: u32) -> bool { pub fn has_value(&self, bit: u32) -> bool {
let mask = 1 << bit; let mask = 1 << (bit - 1);
self.0 & mask == mask self.0 & mask == mask
} }
@@ -227,10 +227,11 @@ impl MessageFlagsValue {
self.set_value(flag as u32, toggle) self.set_value(flag as u32, toggle)
} }
pub fn set_value(&mut self, bit: u32, toggle: bool) -> &mut Self { pub fn set_value(&mut self, bit: u32, toggle: bool) -> &mut Self {
let mask = 1 << (bit - 1);
if toggle { if toggle {
self.0 |= 1 << bit; self.0 |= mask;
} else { } else {
self.0 &= !(1 << bit); self.0 &= !mask;
} }
self self
} }
@@ -159,17 +159,4 @@ impl VoiceClient {
.await .await
.to_internal_error() .to_internal_error()
} }
pub async fn get_room_participants(
&self,
node: &str,
channel_id: &str,
) -> Result<Vec<ParticipantInfo>> {
let room = self.get_node(node)?;
room.client
.list_participants(channel_id)
.await
.to_internal_error()
}
} }
+92 -91
View File
@@ -1,15 +1,19 @@
use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver}; use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver};
use livekit_protocol::TrackType; use livekit_protocol::TrackType;
use revolt_database::{ use revolt_database::{
AMQP, Database, PartialMessage, SystemMessage, events::client::EventV1, iso8601_timestamp::{Duration, Timestamp}, util::reference::Reference, voice::{ events::client::EventV1,
RoomMetadata, UserVoiceChannel, VoiceClient, create_voice_state, delete_channel_voice_state, delete_voice_state, get_call_notification_recipients, get_user_moved_from_voice, get_user_moved_to_voice, get_voice_channel_members, set_channel_call_started_system_message, take_channel_call_started_system_message, update_voice_state_tracks iso8601_timestamp::{Duration, Timestamp},
} util::reference::Reference,
voice::{
create_voice_state, delete_channel_voice_state, delete_voice_state,
get_user_moved_from_voice, get_user_moved_to_voice, update_voice_state_tracks,
RoomMetadata, UserVoiceChannel, VoiceClient,
},
Database, AMQP,
}; };
use revolt_models::v0;
use revolt_result::{Result, ToRevoltError}; use revolt_result::{Result, ToRevoltError};
use rocket::{post, State}; use rocket::{post, State};
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
use ulid::Ulid;
use crate::guard::AuthHeader; use crate::guard::AuthHeader;
@@ -17,12 +21,12 @@ use crate::guard::AuthHeader;
pub async fn ingress( pub async fn ingress(
db: &State<Database>, db: &State<Database>,
voice_client: &State<VoiceClient>, voice_client: &State<VoiceClient>,
amqp: &State<AMQP>, _amqp: &State<AMQP>,
node: &str, node: &str,
auth_header: AuthHeader<'_>, auth_header: AuthHeader<'_>,
body: &str, body: &str,
) -> Result<EmptyResponse> { ) -> Result<EmptyResponse> {
log::debug!("received event: {body}"); log::debug!("received event: {body:?}");
let config = revolt_config::config().await; let config = revolt_config::config().await;
@@ -59,18 +63,16 @@ pub async fn ingress(
let channel_id = channel_id.to_internal_error()?; let channel_id = channel_id.to_internal_error()?;
let user_id = user_id.to_internal_error()?; let user_id = user_id.to_internal_error()?;
let server_id = room_metadata.to_internal_error()?.server; let server_id = room_metadata.to_internal_error()?.server;
let voice_channel = UserVoiceChannel { let channel = UserVoiceChannel {
id: channel_id.clone(), id: channel_id.clone(),
server_id: server_id.clone(), server_id: server_id.clone(),
}; };
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
let joined_at = Timestamp::UNIX_EPOCH let joined_at = Timestamp::UNIX_EPOCH
.checked_add(Duration::seconds(event.created_at)) .checked_add(Duration::seconds(event.created_at))
.unwrap(); .unwrap();
let voice_state = create_voice_state(&voice_channel, user_id, joined_at).await?; let voice_state = create_voice_state(&channel, user_id, joined_at).await?;
// Only publish one event when a user is moved from one channel to another. // Only publish one event when a user is moved from one channel to another.
if let Some(moved_from) = get_user_moved_to_voice(channel_id, user_id).await? { if let Some(moved_from) = get_user_moved_to_voice(channel_id, user_id).await? {
@@ -91,66 +93,63 @@ pub async fn ingress(
.await; .await;
}; };
let participants = voice_client.get_room_participants(node, channel_id).await?; // TODO: fix `num_participants` being incorrect sometimes see (#457)
// First user who joined - send call started system message.
// if event.room.as_ref().unwrap().num_participants == 1 {
// let user = Reference::from_unchecked(user_id).as_user(db).await?;
if participants.len() == 1 { // let message_id =
let user = Reference::from_unchecked(user_id).as_user(db).await?; // Ulid::from_datetime(DateTime::from_timestamp_secs(event.created_at).unwrap())
let message_id = Ulid::from_datetime( // .to_string();
Timestamp::UNIX_EPOCH
.checked_add(Duration::seconds(event.created_at))
.unwrap()
.into(),
)
.to_string();
let mut call_started_message = SystemMessage::CallStarted { // let mut call_started_message = SystemMessage::CallStarted {
by: user_id.to_string(), // by: user_id.to_string(),
finished_at: None, // finished_at: None,
} // }
.into_message(channel_id.clone()); // .into_message(channel.id().to_string());
call_started_message.id = message_id; // call_started_message.id = message_id;
set_channel_call_started_system_message(channel_id, &call_started_message.id) // set_channel_call_started_system_message(channel.id(), &call_started_message.id)
.await?; // .await?;
call_started_message // call_started_message
.send( // .send(
db, // db,
Some(amqp), // Some(amqp),
v0::MessageAuthor::System { // v0::MessageAuthor::System {
username: &user.username, // username: &user.username,
avatar: user.avatar.as_ref().map(|file| file.id.as_ref()), // avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
}, // },
None, // None,
None, // None,
&channel, // &channel,
false, // false,
) // )
.await?; // .await?;
let recipients = get_call_notification_recipients(channel_id, user_id).await?; // let recipients = get_call_notification_recipients(&channel_id, &user_id).await?;
let now = joined_at.format_short().to_string(); // let now = joined_at.format_short().to_string();
if let Err(e) = amqp // if let Err(e) = amqp
.dm_call_updated(&user.id, channel_id, Some(&now), false, recipients) // .dm_call_updated(&user.id, channel.id(), Some(&now), false, recipients)
.await // .await
{ // {
revolt_config::capture_error(&e); // revolt_config::capture_error(&e);
} // }
} // }
} }
// User left a channel // User left a channel
"participant_left" => { "participant_left" => {
let channel_id = channel_id.to_internal_error()?; let channel_id = channel_id.to_internal_error()?;
let user_id = user_id.to_internal_error()?; let user_id = user_id.to_internal_error()?;
let server_id = room_metadata.to_internal_error()?.server; let server_id = room_metadata.to_internal_error()?.server;
let voice_channel = UserVoiceChannel { let channel = UserVoiceChannel {
id: channel_id.clone(), id: channel_id.clone(),
server_id: server_id.clone(), server_id: server_id.clone(),
}; };
delete_voice_state(&voice_channel, user_id).await?; delete_voice_state(&channel, user_id).await?;
// Dont send leave event when a user is moved // Dont send leave event when a user is moved
if get_user_moved_from_voice(channel_id, user_id) if get_user_moved_from_voice(channel_id, user_id)
@@ -165,47 +164,49 @@ pub async fn ingress(
.await; .await;
}; };
// See above for why this is commented out
// // Update CallStarted system message if everyone has left with the end time // // Update CallStarted system message if everyone has left with the end time
let members = get_voice_channel_members(&voice_channel).await?; // let members = get_voice_channel_members(channel_id).await?;
if members.is_none_or(|m| m.is_empty()) { // if members.is_none_or(|m| m.is_empty()) {
// The channel is empty so send out an "end" message for ringing // // The channel is empty so send out an "end" message for ringing
if let Err(e) = amqp // if let Err(e) = amqp
.dm_call_updated(user_id, channel_id, None, true, None) // .dm_call_updated(user_id, channel_id, None, true, None)
.await // .await
{ // {
revolt_config::capture_internal_error!(&e); // revolt_config::capture_internal_error!(&e);
} // }
if let Some(system_message_id) = // if let Some(system_message_id) =
take_channel_call_started_system_message(channel_id).await? // take_channel_call_started_system_message(channel_id).await?
{ // {
// Could have been deleted // // Could have been deleted
if let Ok(mut message) = Reference::from_unchecked(&system_message_id) // if let Ok(mut message) = Reference::from_unchecked(&system_message_id)
.as_message(db) // .as_message(db)
.await // .await
{ // {
if let Some(SystemMessage::CallStarted { finished_at, .. }) = // if let Some(SystemMessage::CallStarted { finished_at, .. }) =
&mut message.system // &mut message.system
{ // {
*finished_at = Some(Timestamp::now_utc()); // *finished_at = Some(Timestamp::now_utc());
message // message
.update( // .update(
db, // db,
PartialMessage { // PartialMessage {
system: message.system.clone(), // system: message.system.clone(),
..Default::default() // ..Default::default()
}, // },
Vec::new(), // Vec::new(),
) // )
.await?; // .await?;
} else { // } else {
log::error!("Broken State: Call started message ID ({}) does not contain a CallStarted system message.", &message.id) // log::error!("Broken State: Call started message ID ({}) does not contain a CallStarted system message.", &message.id)
} // }
}; // };
}; // };
} // }
} }
// Audio/video track was started/stopped/unmuted/muted // Audio/video track was started/stopped/unmuted/muted
"track_published" | "track_unpublished" | "track_unmuted" | "track_muted" => { "track_published" | "track_unpublished" | "track_unmuted" | "track_muted" => {
+38 -49
View File
@@ -10,7 +10,7 @@ use tokio::sync::RwLock;
pub mod types; pub mod types;
const TENOR_API_BASE_URL: &str = "https://compat.gifbox.me/v2"; const TENOR_API_BASE_URL: &str = "https://tenor.googleapis.com/v2";
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum TenorError { pub enum TenorError {
@@ -59,11 +59,7 @@ impl Tenor {
} }
} }
pub async fn request<T: DeserializeOwned>( pub async fn request<T: DeserializeOwned>(&self, path: &str, query: &[Option<(&str, &str)>]) -> Result<Arc<T>, TenorError> {
&self,
path: &str,
query: &[Option<(&str, &str)>],
) -> Result<Arc<T>, TenorError> {
let response = self let response = self
.client .client
.get(format!("{TENOR_API_BASE_URL}{path}")) .get(format!("{TENOR_API_BASE_URL}{path}"))
@@ -99,27 +95,24 @@ impl Tenor {
} }
} }
let res = self let res = self.coalescion.execute(unique_key.clone(), || async move {
.coalescion self.request::<types::PaginatedMediaResponse>(
.execute(unique_key.clone(), || async move { "/search",
self.request::<types::PaginatedMediaResponse>( &[
"/search", Some(("key", &self.key)),
&[ Some(("q", query)),
Some(("key", &self.key)), Some(("client_key", "Gifbox")),
Some(("q", query)), Some(("media_filter", "webm,tinywebm")),
Some(("client_key", "Gifbox")), Some(("locale", locale)),
Some(("media_filter", "webm,tinywebm")), Some(("contentfilter", "high")),
Some(("locale", locale)), Some(("limit", &limit.to_string())),
Some(("contentfilter", "high")), position.is_empty().then_some(("pos", position)),
Some(("limit", &limit.to_string())), is_category.then_some(("component", "categories"))
position.is_empty().then_some(("pos", position)), ]
is_category.then_some(("component", "categories")), ).await
], })
) .await
.await .unwrap();
})
.await
.unwrap();
if let Ok(resp) = &*res { if let Ok(resp) = &*res {
self.cache.write().await.insert(unique_key, resp.clone()); self.cache.write().await.insert(unique_key, resp.clone());
@@ -150,9 +143,8 @@ impl Tenor {
Some(("client_key", "Gifbox")), Some(("client_key", "Gifbox")),
Some(("locale", locale)), Some(("locale", locale)),
Some(("contentfilter", "high")), Some(("contentfilter", "high")),
], ]
) ).await
.await
}) })
.await .await
.unwrap(); .unwrap();
@@ -181,25 +173,22 @@ impl Tenor {
} }
} }
let res = self let res = self.coalescion.execute(unique_key.clone(), || async move {
.coalescion self.request::<types::PaginatedMediaResponse>(
.execute(unique_key.clone(), || async move { "/featured",
self.request::<types::PaginatedMediaResponse>( &[
"/featured", Some(("key", &self.key)),
&[ Some(("client_key", "Gifbox")),
Some(("key", &self.key)), Some(("media_filter", "webm,tinywebm")),
Some(("client_key", "Gifbox")), Some(("locale", locale)),
Some(("media_filter", "webm,tinywebm")), Some(("contentfilter", "high")),
Some(("locale", locale)), Some(("limit", &limit.to_string())),
Some(("contentfilter", "high")), position.is_empty().then_some(("pos", position)),
Some(("limit", &limit.to_string())), ]
position.is_empty().then_some(("pos", position)), ).await
], })
) .await
.await .unwrap();
})
.await
.unwrap();
if let Ok(resp) = &*res { if let Ok(resp) = &*res {
self.featured.write().await.insert(unique_key, resp.clone()); self.featured.write().await.insert(unique_key, resp.clone());