TLS 1.3, channel persistence, membership signaling

- TLS 1.3 on TCP (rustls + tokio-rustls, optional self-signed cert)
- Server-side channel CRUD with SQLite persistence
- Gateway → voice-node membership bridge via broadcast channel
This commit is contained in:
loki5512344 2026-07-09 12:48:18 +02:00
parent 523680e05d
commit 5534cd01f7
Signed by: boba
GPG key ID: 253067914055423B
37 changed files with 771 additions and 160 deletions

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::{info, warn};
use crate::{
@ -17,7 +17,7 @@ use crate::{
/// Handle a ChannelCreate request — register a new channel in the store and
/// broadcast the new ChannelState to all sessions so their sidebars update.
pub async fn handle_channel_create(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -97,6 +97,17 @@ pub async fn handle_channel_create(
let created = channels::create(&state.channels, &channel_id, &channel_name, kind.clone()).await;
// Persist to DB (best-effort, log and continue on failure).
if created {
if let Err(e) = state
.storage
.create_channel(&channel_id, &channel_name, kind.as_str())
.await
{
warn!("failed to persist channel to storage: {e}");
}
}
if !created {
io::send_encrypted(
stream,
@ -160,7 +171,7 @@ pub async fn handle_channel_create(
/// Handle a ChannelDelete request — remove the channel from the store and
/// broadcast the deletion to all sessions.
pub async fn handle_channel_delete(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -190,6 +201,14 @@ pub async fn handle_channel_delete(
}
let existed = channels::delete(&state.channels, &req.channel_id).await;
// Remove from DB (best-effort).
if existed {
if let Err(e) = state.storage.delete_channel(&req.channel_id).await {
warn!("failed to remove channel from storage: {e}");
}
}
if !existed {
io::send_encrypted(
stream,
@ -229,7 +248,7 @@ pub async fn handle_channel_delete(
/// Handle a ChannelList request — reply with all known channels.
pub async fn handle_channel_list(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
crypto: &SessionCrypto,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::info;
use crate::{
@ -19,7 +19,7 @@ use super::{broadcast_leave, set_channel};
const HISTORY_LIMIT: i64 = 50;
pub async fn join(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
channel_id: &str,
@ -110,6 +110,18 @@ pub async fn join(
});
}
if let Some(tx) = &state.voice_member_tx {
if let Some(sess) = session::get(&state.sessions, session_id).await {
let event = serde_json::json!({
"type": "joined",
"channel_id": channel_id,
"session_id": session_id,
"user_id": sess.user_id,
});
let _ = tx.send(event.to_string());
}
}
info!("session {} joined {channel_id}", &session_id[..8]);
Ok(())
}

View file

@ -1,22 +1,39 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::info;
use crate::{domain::channels, net::state::State, proto::SessionCrypto};
use crate::{domain::{channels, session}, net::state::State, proto::SessionCrypto};
use super::{broadcast_leave, set_channel};
pub async fn leave(
_stream: &mut TcpStream,
_stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
_seq: &mut u32,
session_id: &str,
channel_id: &str,
_crypto: &SessionCrypto,
state: &State,
) -> Result<()> {
let user_id = session::get(&state.sessions, session_id)
.await
.map(|s| s.user_id);
channels::leave(&state.channels, channel_id, session_id).await;
set_channel(state, session_id, None).await;
broadcast_leave(state, channel_id, session_id).await;
if let Some(tx) = &state.voice_member_tx {
if let Some(ref uid) = user_id {
let event = serde_json::json!({
"type": "left",
"channel_id": channel_id,
"session_id": session_id,
"user_id": uid,
});
let _ = tx.send(event.to_string());
}
}
info!("session {} left {channel_id}", &session_id[..8]);
Ok(())
}

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -14,7 +14,7 @@ use crate::{
};
pub async fn handle_presence_update(
_stream: &mut TcpStream,
_stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -60,7 +60,7 @@ pub async fn handle_presence_update(
}
pub async fn handle_presence_sync(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
crypto: &SessionCrypto,

View file

@ -7,7 +7,7 @@ use crate::{
};
pub async fn handle_read_receipt(
_stream: &mut tokio::net::TcpStream,
_stream: &mut (impl tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn deliver_encrypted(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
msg: &BroadcastMsg,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn handle_dm_history(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::warn;
use crate::{
@ -14,7 +14,7 @@ use crate::{
};
pub async fn handle_dm_message(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn handle_dm_start(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -88,7 +88,7 @@ pub async fn handle_dm_start(
}
pub async fn handle_dm_read_ack(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,4 +1,5 @@
use anyhow::Result;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::debug;
use crate::{
@ -11,174 +12,402 @@ use crate::{
use super::{Ctx, channel, content, direct_message, friends, guild};
pub async fn dispatch(
ctx: &mut Ctx<'_>,
pub async fn dispatch<S: AsyncRead + AsyncWrite + Unpin>(
ctx: &mut Ctx<'_, S>,
session_id: &str,
pid: PacketId,
payload: &[u8],
addr: std::net::SocketAddr,
) -> Result<()> {
let Ctx {
stream,
seq,
crypto,
state,
} = ctx;
match pid {
PacketId::Ping => {
let ping: PingPayload = serde_json::from_slice(payload)?;
io::send_encrypted(
stream,
ctx.stream,
PacketId::Pong,
seq,
ctx.seq,
&to_payload(&PongPayload {
timestamp: ping.timestamp,
}),
crypto,
ctx.crypto,
)
.await?;
}
PacketId::JoinChannel => {
let m: JoinChannelPayload = serde_json::from_slice(payload)?;
channel::join(stream, seq, session_id, &m.channel_id, crypto, state).await?;
channel::join(ctx.stream, ctx.seq, session_id, &m.channel_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::LeaveChannel => {
let m: LeaveChannelPayload = serde_json::from_slice(payload)?;
channel::leave(stream, seq, session_id, &m.channel_id, crypto, state).await?;
channel::leave(ctx.stream, ctx.seq, session_id, &m.channel_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::ChannelCreate => {
channel::handle_channel_create(stream, seq, session_id, payload, crypto, state).await?;
channel::handle_channel_create(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::ChannelDelete => {
channel::handle_channel_delete(stream, seq, session_id, payload, crypto, state).await?;
channel::handle_channel_delete(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::ChannelList => {
channel::handle_channel_list(stream, seq, session_id, crypto, state).await?;
channel::handle_channel_list(ctx.stream, ctx.seq, session_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::ChatMessage => {
let m: ChatMessagePayload = serde_json::from_slice(payload)?;
content::chat::handle(session_id, m, state).await?;
content::chat::handle(session_id, m, ctx.state).await?;
}
PacketId::DmStart => {
direct_message::handle_dm_start(stream, seq, session_id, payload, crypto, state)
.await?;
direct_message::handle_dm_start(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::DmMessage => {
direct_message::handle_dm_message(stream, seq, session_id, payload, crypto, state)
.await?;
direct_message::handle_dm_message(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::DmHistory => {
direct_message::handle_dm_history(stream, seq, session_id, payload, crypto, state)
.await?;
direct_message::handle_dm_history(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::DmReadAck => {
direct_message::handle_dm_read_ack(stream, seq, session_id, payload, crypto, state)
.await?;
direct_message::handle_dm_read_ack(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildCreate => {
guild::handle_guild_create(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_guild_create(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildDelete => {
guild::handle_guild_delete(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_guild_delete(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildList => {
guild::handle_guild_list(stream, seq, session_id, crypto, state).await?;
guild::handle_guild_list(ctx.stream, ctx.seq, session_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::GuildMemberJoin => {
guild::handle_guild_member_join(stream, seq, session_id, payload, crypto, state)
.await?;
guild::handle_guild_member_join(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildMemberLeave => {
guild::handle_guild_member_leave(stream, seq, session_id, payload, crypto, state)
.await?;
guild::handle_guild_member_leave(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildMemberKick => {
guild::handle_guild_member_kick(stream, seq, session_id, payload, crypto, state)
.await?;
guild::handle_guild_member_kick(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::RoleCreate => {
guild::handle_role_create(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_role_create(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::RoleDelete => {
guild::handle_role_delete(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_role_delete(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::InviteCreate => {
guild::handle_invite_create(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_invite_create(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::InviteAccept => {
guild::handle_invite_accept(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_invite_accept(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::InviteDelete => {
guild::handle_invite_delete(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_invite_delete(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildAuditLogFetch => {
guild::handle_audit_log_fetch(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_audit_log_fetch(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildMemberListFetch => {
guild::handle_member_list_fetch(stream, seq, session_id, payload, crypto, state)
.await?;
guild::handle_member_list_fetch(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildRoleAssign => {
guild::handle_role_assign(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_role_assign(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildRoleUnassign => {
guild::handle_role_unassign(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_role_unassign(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::GuildRoleListFetch => {
guild::handle_role_list_fetch(stream, seq, session_id, payload, crypto, state).await?;
guild::handle_role_list_fetch(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::PresenceUpdate => {
content::presence::handle_presence_update(
stream, seq, session_id, payload, crypto, state,
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::PresenceSync => {
content::presence::handle_presence_sync(stream, seq, session_id, crypto, state).await?;
content::presence::handle_presence_sync(
ctx.stream,
ctx.seq,
session_id,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::FriendRequest => {
friends::handle_friend_request(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_friend_request(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::FriendAccept => {
friends::handle_friend_accept(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_friend_accept(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::FriendDecline => {
friends::handle_friend_decline(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_friend_decline(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::FriendRemove => {
friends::handle_friend_remove(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_friend_remove(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::FriendList => {
friends::handle_friend_list(stream, seq, session_id, crypto, state).await?;
friends::handle_friend_list(ctx.stream, ctx.seq, session_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::BlockUser => {
friends::handle_block_user(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_block_user(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::UnblockUser => {
friends::handle_unblock_user(stream, seq, session_id, payload, crypto, state).await?;
friends::handle_unblock_user(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::BlockList => {
friends::handle_block_list(stream, seq, session_id, crypto, state).await?;
friends::handle_block_list(ctx.stream, ctx.seq, session_id, ctx.crypto, ctx.state)
.await?;
}
PacketId::MessageReactionAdd => {
let m: ReactionPayload = serde_json::from_slice(payload)?;
content::reaction::handle_reaction_add(session_id, m, state).await?;
content::reaction::handle_reaction_add(session_id, m, ctx.state).await?;
}
PacketId::MessageReactionRemove => {
let m: ReactionPayload = serde_json::from_slice(payload)?;
content::reaction::handle_reaction_remove(session_id, m, state).await?;
content::reaction::handle_reaction_remove(session_id, m, ctx.state).await?;
}
PacketId::MessageEdit => {
content::message_edit::handle_message_edit(session_id, payload, state).await?;
content::message_edit::handle_message_edit(session_id, payload, ctx.state).await?;
}
PacketId::MessageDelete => {
content::message_edit::handle_message_delete(session_id, payload, state).await?;
content::message_edit::handle_message_delete(session_id, payload, ctx.state).await?;
}
PacketId::TypingStart => {
content::handle_typing_start(session_id, payload, state).await?;
content::handle_typing_start(session_id, payload, ctx.state).await?;
}
PacketId::ReadReceipt => {
content::handle_read_receipt(stream, seq, session_id, payload, crypto, state).await?;
content::handle_read_receipt(
ctx.stream,
ctx.seq,
session_id,
payload,
ctx.crypto,
ctx.state,
)
.await?;
}
PacketId::Disconnect => debug!("{addr} DISCONNECT"),
other => debug!("{addr} unhandled {:?}", other),

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn handle_friend_decline(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn handle_friend_list(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
crypto: &SessionCrypto,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -11,7 +11,7 @@ use crate::{
};
pub async fn handle_friend_remove(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -40,7 +40,7 @@ pub async fn handle_friend_remove(
}
pub async fn handle_block_user(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -69,7 +69,7 @@ pub async fn handle_block_user(
}
pub async fn handle_unblock_user(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -98,7 +98,7 @@ pub async fn handle_unblock_user(
}
pub async fn handle_block_list(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
crypto: &SessionCrypto,

View file

@ -4,7 +4,7 @@ mod manage;
mod requests;
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
net::io,
@ -12,7 +12,7 @@ use crate::{
};
async fn send_err(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
code: crate::proto::ErrorCode,
msg: &str,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -16,7 +16,7 @@ use crate::{
use super::send_err;
pub async fn handle_friend_request(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -100,7 +100,7 @@ pub async fn handle_friend_request(
}
pub async fn handle_friend_accept(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::permissions::Permissions,
@ -12,7 +12,7 @@ use crate::{
/// Fetch audit log entries for a guild (admin-only: requires VIEW_AUDIT_LOG or owner).
pub async fn handle_audit_log_fetch(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::debug;
use crate::{
@ -11,7 +11,7 @@ use crate::{
use super::send_err;
pub async fn handle_guild_create(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -67,7 +67,7 @@ pub async fn handle_guild_create(
}
pub async fn handle_guild_delete(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::{permissions::Permissions, session},
@ -13,7 +13,7 @@ use crate::{
use super::{now_ms, send_err};
pub async fn handle_invite_create(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -89,7 +89,7 @@ pub async fn handle_invite_create(
}
pub async fn handle_invite_accept(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -164,7 +164,7 @@ pub async fn handle_invite_accept(
}
pub async fn handle_invite_delete(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::session,
@ -8,7 +8,7 @@ use crate::{
};
pub async fn handle_guild_list(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
crypto: &SessionCrypto,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::{permissions::Permissions, session},
@ -13,7 +13,7 @@ use crate::{
use super::send_err;
pub async fn handle_guild_member_join(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -63,7 +63,7 @@ pub async fn handle_guild_member_join(
}
pub async fn handle_guild_member_leave(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -96,7 +96,7 @@ pub async fn handle_guild_member_leave(
}
pub async fn handle_guild_member_kick(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::{permissions::Permissions, session},
@ -15,7 +15,7 @@ use super::{require_perm, send_err};
/// Fetch the member list for a guild (visible to all members).
pub async fn handle_member_list_fetch(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -65,7 +65,7 @@ pub async fn handle_member_list_fetch(
/// Assign a role to a user (admin only: requires MANAGE_ROLES).
pub async fn handle_role_assign(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -129,7 +129,7 @@ pub async fn handle_role_assign(
/// Remove a role from a user (admin only: requires MANAGE_ROLES).
pub async fn handle_role_unassign(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -192,7 +192,7 @@ pub async fn handle_role_unassign(
/// Fetch all roles defined in a guild (visible to all members).
pub async fn handle_role_list_fetch(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -7,7 +7,7 @@ mod members_list;
mod roles;
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::permissions::Permissions,
@ -16,7 +16,7 @@ use crate::{
};
async fn send_err(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
code: crate::proto::ErrorCode,
msg: &str,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{
domain::{permissions::Permissions, session},
@ -10,7 +10,7 @@ use crate::{
use super::send_err;
pub async fn handle_role_create(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],
@ -73,7 +73,7 @@ pub async fn handle_role_create(
}
pub async fn handle_role_delete(
stream: &mut TcpStream,
stream: &mut (impl AsyncRead + AsyncWrite + Unpin),
seq: &mut u32,
session_id: &str,
payload: &[u8],

View file

@ -10,12 +10,12 @@ pub mod run;
pub use dispatch::dispatch;
pub use run::run_session;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use crate::{net::state::State, proto::SessionCrypto};
pub struct Ctx<'a> {
pub stream: &'a mut TcpStream,
pub struct Ctx<'a, S: AsyncRead + AsyncWrite + Unpin> {
pub stream: &'a mut S,
pub seq: &'a mut u32,
pub crypto: &'a SessionCrypto,
pub state: &'a State,

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use tokio::net::TcpStream;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::sync::broadcast;
use tracing::warn;
@ -10,8 +10,8 @@ use crate::{
use super::{Ctx, deliver::deliver_encrypted, dispatch};
pub async fn run_session(
stream: &mut TcpStream,
pub async fn run_session<S: AsyncRead + AsyncWrite + Unpin>(
stream: &mut S,
addr: std::net::SocketAddr,
seq: &mut u32,
session_id: &str,