Skip to content

Commit b2fb562

Browse files
feat: Remove Groups and Direct Messages from server when nobody is online
1 parent 544dbd7 commit b2fb562

5 files changed

Lines changed: 154 additions & 36 deletions

File tree

server/Config.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
port = 8443
22

3-
[tls]
4-
cert_path = "/etc/letsencrypt/live/nulltalk.viveksahani.com/fullchain.pem"
5-
key_path = "/etc/letsencrypt/live/nulltalk.viveksahani.com/privkey.pem"
3+
# [tls]
4+
# cert_path = "/etc/letsencrypt/live/nulltalk.viveksahani.com/fullchain.pem"
5+
# key_path = "/etc/letsencrypt/live/nulltalk.viveksahani.com/privkey.pem"

server/src/handlers/client.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
use std::sync::Arc;
2-
3-
use tokio::sync::{Mutex as AsyncMutex, mpsc::UnboundedSender};
4-
51
use crate::{
6-
data::CLIENTS, handlers::task::start_reader_task, net::perform_handshake, types::Client,
2+
data::{CLIENTS, CONVERSATIONS, GROUPS},
3+
handlers::task::start_reader_task,
4+
net::perform_handshake,
5+
types::Client,
76
};
87
use common::{
98
net::{AsyncStream, Packet, StreamReader, StreamWriter},
109
utils::enc::public_key_to_user_id,
1110
};
11+
use std::sync::Arc;
12+
use tokio::sync::{Mutex as AsyncMutex, mpsc::UnboundedSender};
1213

1314
/// Handle a new client connection
1415
pub async fn handle_client(
@@ -29,6 +30,8 @@ pub async fn handle_client(
2930
username: name.clone().to_string(),
3031
user_id: client_id.clone(),
3132
session_key: hex::encode(&session_key),
33+
dms: Vec::new(),
34+
groups: Vec::new(),
3235
writer: wt.clone(),
3336
};
3437

@@ -44,10 +47,76 @@ pub async fn handle_client(
4447
let _ = read_task.await;
4548

4649
println!("🔗 client disconnected: {}", &client_id[..8]);
50+
drop(rd);
51+
drop(wt);
52+
cleanup_client_data(client_id.clone()).await;
53+
}
54+
55+
/// Removes user from groups and update/remove DM Session, etc.
56+
async fn cleanup_client_data(client_id: String) {
57+
// Get client from CLIENTS
58+
let client = {
59+
let clients_lock = CLIENTS.lock().await;
60+
clients_lock.get(&client_id).cloned()
61+
};
62+
63+
match client {
64+
Some(client) => {
65+
for dm in client.dms {
66+
update_or_remove_dm_session(&dm, client_id.clone()).await;
67+
}
68+
for gp in client.groups {
69+
update_or_remove_group_session(&gp, client_id.clone()).await;
70+
}
71+
}
72+
None => {
73+
println!("⚠️ Client not found for cleanup: {}", &client_id[..8]);
74+
}
75+
}
76+
// Additional cleanup logic can be added here
4777

48-
// Remove client from map
78+
// Remove client from CLIENTS
4979
{
5080
let mut clients_lock = CLIENTS.lock().await;
5181
clients_lock.remove(&client_id);
82+
};
83+
}
84+
85+
async fn update_or_remove_dm_session(session_id: &str, client_id: String) {
86+
let mut conv = CONVERSATIONS.lock().await;
87+
let need_to_remove = match conv.get_mut(session_id) {
88+
Some(dm) => {
89+
dm.members.insert(client_id.clone(), false);
90+
// Check if we need to remove the DM session
91+
dm.members.values().all(|is_active| !is_active)
92+
}
93+
None => {
94+
println!("⚠️ DM session not found for ID: {}", session_id);
95+
false
96+
}
97+
};
98+
99+
if need_to_remove {
100+
conv.remove(session_id);
101+
}
102+
}
103+
104+
async fn update_or_remove_group_session(session_id: &str, client_id: String) {
105+
let mut groups = GROUPS.lock().await;
106+
let need_to_remove = match groups.get_mut(session_id) {
107+
Some(group) => {
108+
group.members.insert(client_id.clone(), false);
109+
110+
// Check if we need to remove the group session
111+
group.members.values().all(|is_active| !is_active)
112+
}
113+
None => {
114+
println!("⚠️ Group session not found for ID: {}", session_id);
115+
false
116+
}
117+
};
118+
119+
if need_to_remove {
120+
groups.remove(session_id);
52121
}
53122
}

server/src/handlers/cmd.rs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use crate::{
24
data::{CLIENTS, CONVERSATIONS, GROUPS},
35
types::{DmChat, GroupChat},
@@ -75,10 +77,23 @@ async fn create_new_session(payload: Vec<u8>, client_id: String) -> ServerRespon
7577
session_key = dm.session_key.clone();
7678
}
7779
None => {
80+
let mut members: HashMap<String, bool> = HashMap::new();
81+
members.insert(client_id.clone(), true);
82+
members.insert(new_session.id.clone(), true);
83+
84+
for member in members.keys() {
85+
let mut guard = CLIENTS.lock().await;
86+
if let Some(client) = guard.get_mut(member) {
87+
if !client.dms.contains(&session_id) {
88+
client.dms.push(session_id.clone());
89+
}
90+
}
91+
}
92+
7893
let dm_chat = DmChat {
7994
dm_id: session_id.clone(),
8095
session_key: session_key.clone(),
81-
members: (client_id.clone(), new_session.id.clone()),
96+
members,
8297
};
8398

8499
conversations
@@ -101,7 +116,7 @@ async fn create_new_session(payload: Vec<u8>, client_id: String) -> ServerRespon
101116
}
102117
};
103118

104-
if !group.members.contains(&client_id) {
119+
if !group.members.contains_key(&client_id) {
105120
response.success = false;
106121
response.error = Some("You are not a member of this group".to_string());
107122

@@ -110,6 +125,22 @@ async fn create_new_session(payload: Vec<u8>, client_id: String) -> ServerRespon
110125

111126
let session_id = group.group_id.clone();
112127
let session_key = group.session_key.clone();
128+
129+
{
130+
let mut guard = CLIENTS.lock().await;
131+
if let Some(client) = guard.get_mut(&client_id) {
132+
if !client.groups.contains(&session_id) {
133+
client.groups.push(session_id.clone());
134+
}
135+
}
136+
}
137+
{
138+
let mut groups = GROUPS.lock().await;
139+
if let Some(group) = groups.get_mut(&session_id) {
140+
group.members.insert(client_id.clone(), true);
141+
}
142+
}
143+
113144
(session_id, session_key)
114145
}
115146
};
@@ -136,7 +167,7 @@ async fn create_new_group(payload: Vec<u8>, client_id: String) -> ServerResponse
136167
error: None,
137168
};
138169

139-
let mut members: Vec<String> = Vec::new();
170+
let mut members: HashMap<String, bool> = HashMap::new();
140171

141172
let (group_info, _): (NewGroupPayload, usize) =
142173
match bincode::decode_from_slice(&payload, bincode::config::standard()) {
@@ -149,10 +180,10 @@ async fn create_new_group(payload: Vec<u8>, client_id: String) -> ServerResponse
149180
}
150181
};
151182

152-
members.extend_from_slice(&group_info.members);
153-
if !members.contains(&client_id) {
154-
members.push(client_id.clone());
183+
for member in &group_info.members {
184+
members.insert(member.clone(), false);
155185
}
186+
members.insert(client_id.clone(), true);
156187

157188
let group_id = group_info
158189
.group_id
@@ -162,7 +193,7 @@ async fn create_new_group(payload: Vec<u8>, client_id: String) -> ServerResponse
162193
group_id: group_id.clone(),
163194
session_key: session_key.clone(),
164195
admin: client_id.clone(),
165-
members,
196+
members: members.clone(),
166197
};
167198

168199
{
@@ -186,6 +217,15 @@ async fn create_new_group(payload: Vec<u8>, client_id: String) -> ServerResponse
186217
}
187218
}
188219

220+
for member in members.keys() {
221+
let mut guard = CLIENTS.lock().await;
222+
if let Some(client) = guard.get_mut(member) {
223+
if !client.groups.contains(&group_id) {
224+
client.groups.push(group_id.clone());
225+
}
226+
}
227+
}
228+
189229
let res_payload = NewGroupResponse {
190230
group_id,
191231
session_key,
@@ -231,19 +271,20 @@ async fn add_group_member(payload: Vec<u8>, client_id: String) -> ServerResponse
231271
return response;
232272
}
233273

234-
let member = match CLIENTS.lock().await.get(&data.member_id) {
235-
Some(member) => member.clone(),
274+
match CLIENTS.lock().await.get(&data.member_id).cloned() {
275+
Some(mut member) => {
276+
if !group.members.contains_key(&member.user_id) {
277+
group.members.insert(member.user_id.clone(), true);
278+
}
279+
member.groups.push(group.group_id.clone());
280+
}
236281
None => {
237-
response.success = false;
238-
response.error = Some("Member not found".to_string());
239-
return response;
282+
if !group.members.contains_key(&data.member_id) {
283+
group.members.insert(data.member_id.clone(), false);
284+
}
240285
}
241286
};
242287

243-
if !group.members.contains(&member.user_id) {
244-
group.members.push(member.user_id.clone());
245-
}
246-
247288
response.payload = Some(
248289
bincode::encode_to_vec("Member Added successfully", bincode::config::standard()).unwrap(),
249290
);

server/src/handlers/msg.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ pub async fn handle_group_message(packet: Packet, group_id: &str) {
1818
};
1919

2020
// Broadcast the message to all clients in the group
21-
for member_id in group.members {
21+
for (member_id, is_active) in group.members {
22+
if !is_active {
23+
continue;
24+
}
2225
if member_id == message.sender_id.clone() {
2326
continue;
2427
}
@@ -47,14 +50,13 @@ pub async fn handle_direct_message(packet: Packet, session_id: &str) {
4750
};
4851

4952
// Find recipient
50-
let member1 = dm.members.0;
51-
let member2 = dm.members.1;
52-
let recipient: String;
53-
54-
if message.sender_id.clone() == member1 {
55-
recipient = member2;
56-
} else {
57-
recipient = member1;
53+
let mut recipient = String::new();
54+
for (member, _) in &dm.members {
55+
if member == &message.sender_id {
56+
continue;
57+
} else {
58+
recipient = member.clone();
59+
}
5860
}
5961

6062
// Check if client is online

server/src/types.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use common::net::StreamWriter;
24

35
/// Represents a connected client
@@ -9,6 +11,10 @@ pub struct Client {
911
pub user_id: String,
1012
/// session key of the client
1113
pub session_key: String,
14+
/// direct message chats the client is part of
15+
pub dms: Vec<String>,
16+
/// group chats the client is part of
17+
pub groups: Vec<String>,
1218
/// Stream writer for the client
1319
pub writer: StreamWriter,
1420
}
@@ -19,7 +25,7 @@ pub struct DmChat {
1925
/// unique identifier for the direct message chat
2026
pub dm_id: String,
2127
/// members of the direct message chat
22-
pub members: (String, String),
28+
pub members: HashMap<String, bool>,
2329
/// session key for the direct message chat
2430
pub session_key: Vec<u8>,
2531
}
@@ -32,7 +38,7 @@ pub struct GroupChat {
3238
/// unique identifier for the group chat
3339
pub group_id: String,
3440
/// members of the group chat
35-
pub members: Vec<String>,
41+
pub members: HashMap<String, bool>,
3642
/// session key for the group chat
3743
pub session_key: Vec<u8>,
3844
/// admin's user_id of the group chat

0 commit comments

Comments
 (0)