Communities
This commit is contained in:
parent
6ae6e85b5a
commit
1c08956387
26 changed files with 1988 additions and 338 deletions
280
src/communities/community.rs
Normal file
280
src/communities/community.rs
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
use crate::communities::interactables::category::Category;
|
||||
use crate::communities::interactables::registry;
|
||||
use crate::communities::interactables::text_chat::TextChat;
|
||||
use crate::communities::interactables::voice_chat::VoiceChat;
|
||||
use crate::communities::{
|
||||
community_connection::CommunityConnection, interactables::interactable::Interactable,
|
||||
};
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue};
|
||||
use crate::util::file_util;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use json::JsonValue;
|
||||
use json::object::Object;
|
||||
use rand::RngCore;
|
||||
use rand_core::OsRng;
|
||||
use ratatui::text;
|
||||
use serde::de::value::StringDeserializer;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
use x448::{PublicKey, Secret};
|
||||
/// Permissions
|
||||
// uuid -> interactable/path/like/this/interactable_name:permission
|
||||
// -> role
|
||||
|
||||
/// Roles
|
||||
// rolename -> interactable/path/like/this/interactable_name:permission
|
||||
// -> other/path/like/this/interactable_name:permission
|
||||
|
||||
pub struct Community {
|
||||
name: String,
|
||||
owner_id: Uuid,
|
||||
members: Vec<Uuid>,
|
||||
permissions: HashMap<Uuid, Vec<String>>,
|
||||
roles: HashMap<String, Vec<String>>,
|
||||
private_key: Secret,
|
||||
public_key: PublicKey,
|
||||
pub interactables: Arc<RwLock<Vec<Arc<Box<dyn Interactable>>>>>,
|
||||
pub connections: Arc<RwLock<HashMap<Uuid, Vec<Arc<CommunityConnection>>>>>,
|
||||
}
|
||||
|
||||
impl Community {
|
||||
pub fn new() -> Self {
|
||||
let mut buf = [0u8; 56];
|
||||
let mut rng = OsRng;
|
||||
rng.fill_bytes(&mut buf);
|
||||
let private_key = Secret::from_bytes(&buf).unwrap();
|
||||
let public_key = PublicKey::from(&private_key);
|
||||
Community {
|
||||
name: String::new(),
|
||||
owner_id: Uuid::new_v4(),
|
||||
members: Vec::new(),
|
||||
permissions: HashMap::new(),
|
||||
roles: HashMap::new(),
|
||||
private_key,
|
||||
public_key,
|
||||
interactables: Arc::new(RwLock::new(Vec::new())),
|
||||
connections: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_member(&mut self, member_id: Uuid) {
|
||||
self.members.push(member_id);
|
||||
}
|
||||
|
||||
pub fn remove_member(&mut self, member_id: Uuid) {
|
||||
self.members.retain(|id| *id != member_id);
|
||||
}
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn get_private_key(&self) -> Secret {
|
||||
Secret::from_bytes(self.private_key.as_bytes()).unwrap()
|
||||
}
|
||||
pub fn get_public_key(&self) -> &PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
pub async fn add_connection(self: &Arc<Self>, other: Arc<CommunityConnection>) {
|
||||
let mut vec = self
|
||||
.connections
|
||||
.read()
|
||||
.await
|
||||
.get(&other.get_user_id().await.unwrap())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
vec.push(other.clone());
|
||||
self.connections
|
||||
.write()
|
||||
.await
|
||||
.insert(other.get_user_id().await.unwrap(), vec);
|
||||
}
|
||||
pub async fn get_connections(&self) -> HashMap<Uuid, Vec<Arc<CommunityConnection>>> {
|
||||
self.connections.read().await.clone()
|
||||
}
|
||||
pub async fn get_connections_for_user(&self, user_id: Uuid) -> Vec<Arc<CommunityConnection>> {
|
||||
self.connections
|
||||
.read()
|
||||
.await
|
||||
.get(&user_id)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
pub async fn get_interactables(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
) -> Vec<Arc<Box<dyn Interactable + 'static>>> {
|
||||
self.interactables.read().await.clone()
|
||||
}
|
||||
pub async fn add_interactable(self: &mut Arc<Self>, interactable: Arc<Box<dyn Interactable>>) {
|
||||
self.interactables.write().await.push(interactable);
|
||||
}
|
||||
pub async fn remove_interactable(
|
||||
self: &mut Arc<Self>,
|
||||
interactable: Arc<Box<dyn Interactable>>,
|
||||
) {
|
||||
self.interactables
|
||||
.write()
|
||||
.await
|
||||
.retain(|i| !Arc::ptr_eq(i, &interactable));
|
||||
}
|
||||
pub async fn run_function(
|
||||
self: &mut Arc<Self>,
|
||||
user_id: Uuid,
|
||||
name: &str,
|
||||
path: &str,
|
||||
function: &str,
|
||||
cv: &CommunicationValue,
|
||||
) -> CommunicationValue {
|
||||
if path.is_empty() {
|
||||
let mut target_interactables = &self.interactables.read().await.clone();
|
||||
for interactable in target_interactables.iter() {
|
||||
if interactable.get_name() == name {
|
||||
if interactable.get_codec() == "category" {
|
||||
return CommunicationValue::new(CommunicationType::error);
|
||||
} else {
|
||||
return interactable.run_function(cv.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let target_interactables = &self.interactables.read().await.clone();
|
||||
for interactable in target_interactables.iter() {
|
||||
if interactable.get_name() == name {
|
||||
if interactable.get_codec() == "category" {
|
||||
let category: &Category =
|
||||
interactable.as_any().downcast_ref::<Category>().unwrap();
|
||||
return category
|
||||
.get_child(path.to_string(), name.to_string())
|
||||
.unwrap()
|
||||
.run_function(cv.clone());
|
||||
} else {
|
||||
return CommunicationValue::new(CommunicationType::error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CommunicationValue::new(CommunicationType::add_chat)
|
||||
}
|
||||
|
||||
pub async fn save(&self) {
|
||||
let mut json = Object::new();
|
||||
json.insert("name", JsonValue::String(self.name.clone()));
|
||||
json.insert("owner_id", JsonValue::String(self.owner_id.to_string()));
|
||||
|
||||
json.insert(
|
||||
"private_key",
|
||||
JsonValue::String(STANDARD.encode(&self.private_key.as_bytes())),
|
||||
);
|
||||
json.insert(
|
||||
"public_key",
|
||||
JsonValue::String(STANDARD.encode(&self.public_key.as_bytes())),
|
||||
);
|
||||
|
||||
file_util::save_file(
|
||||
&format!("communities/{}/", self.name),
|
||||
"config.json",
|
||||
&json.dump(),
|
||||
);
|
||||
|
||||
let mut user_data = Object::new();
|
||||
for user in self.members.iter() {
|
||||
let mut data = JsonValue::new_object();
|
||||
|
||||
let mut permissions = JsonValue::new_array();
|
||||
for perm in self.permissions.get(user).unwrap() {
|
||||
permissions.push(perm.to_string());
|
||||
}
|
||||
|
||||
data.insert("permissions", permissions);
|
||||
user_data.insert(&user.to_string(), data);
|
||||
}
|
||||
file_util::save_file(
|
||||
&format!("communities/{}/", self.name),
|
||||
"users.json",
|
||||
&user_data.dump(),
|
||||
);
|
||||
|
||||
for interactable in self.interactables.read().await.clone().iter() {
|
||||
registry::save(interactable).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn load(name: &String) -> Option<Arc<Community>> {
|
||||
let file_contents = file_util::load_file(&format!("communities/{}/", name), "config.json");
|
||||
let json_content = json::parse(&file_contents).unwrap();
|
||||
|
||||
let user_data = file_util::load_file(&format!("communities/{}/", name), "users.json");
|
||||
let user_json: JsonValue = json::parse(&user_data).unwrap();
|
||||
let mut users = Vec::new();
|
||||
let mut permissions: HashMap<Uuid, Vec<String>> = HashMap::new();
|
||||
|
||||
for user in user_json.entries() {
|
||||
let (str, json): (&str, &JsonValue) = user;
|
||||
let perms_j = &json["permissions"];
|
||||
let perms = Vec::new();
|
||||
for i in perms_j.entries() {
|
||||
// let perm_j = i.as_str().unwrap();
|
||||
// perms.push(perm_j.to_string());
|
||||
}
|
||||
|
||||
let user_id = Uuid::parse_str(str).unwrap();
|
||||
|
||||
users.push(user_id);
|
||||
permissions.insert(user_id, perms);
|
||||
}
|
||||
|
||||
let role_data = file_util::load_file(&format!("communities/{}/", name), "roles.json");
|
||||
if let Ok(user_json) = json::parse(&role_data) {
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let mut roles: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
let community = Community {
|
||||
name: json_content["name"].as_str().unwrap().to_string(),
|
||||
owner_id: Uuid::parse_str(json_content["owner_id"].as_str().unwrap()).unwrap(),
|
||||
members: users,
|
||||
roles,
|
||||
permissions,
|
||||
private_key: Secret::from_bytes(
|
||||
&STANDARD
|
||||
.decode(json_content["private_key"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
public_key: PublicKey::from(
|
||||
&Secret::from_bytes(
|
||||
&STANDARD
|
||||
.decode(json_content["private_key"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
interactables: Arc::new(RwLock::new(Vec::new())),
|
||||
connections: Arc::new(RwLock::new(HashMap::new())),
|
||||
};
|
||||
let mut comarc = Arc::new(community);
|
||||
|
||||
let interactable_files: Vec<String> =
|
||||
file_util::get_children(&format!("communities/{}/interactables/", name));
|
||||
for file in interactable_files {
|
||||
if file.contains(".json") {
|
||||
let name = file.split('.').next().unwrap().to_string();
|
||||
let interactable: Box<dyn Interactable> =
|
||||
registry::load(comarc.clone(), String::new(), name).await;
|
||||
comarc.add_interactable(Arc::new(interactable)).await;
|
||||
}
|
||||
}
|
||||
let mut text_chat: TextChat = TextChat::new();
|
||||
text_chat.load(
|
||||
comarc.clone(),
|
||||
String::new(),
|
||||
String::from("a"),
|
||||
&JsonValue::Null,
|
||||
);
|
||||
|
||||
comarc.add_interactable(Arc::new(Box::new(text_chat))).await;
|
||||
|
||||
Some(comarc)
|
||||
}
|
||||
415
src/communities/community_connection.rs
Normal file
415
src/communities/community_connection.rs
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
use crate::auth::auth_connector::AuthUser;
|
||||
use crate::auth::auth_connector::get_user;
|
||||
use crate::communities::community::Community;
|
||||
use crate::communities::interactables::interactable::Interactable;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use aes_gcm::{Aes256Gcm, KeyInit, Nonce, aead::Aead};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use futures::SinkExt;
|
||||
use hkdf::Hkdf;
|
||||
use json::JsonValue;
|
||||
use json::object::Object;
|
||||
use rand::{Rng, distributions::Alphanumeric};
|
||||
use sha2::Sha256;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::{WebSocketStream, tungstenite::Message};
|
||||
use uuid::Uuid;
|
||||
use x448::PublicKey;
|
||||
pub struct CommunityConnection {
|
||||
pub session: Arc<Mutex<WebSocketStream<tokio::net::TcpStream>>>,
|
||||
pub user_id: Arc<RwLock<Option<Uuid>>>,
|
||||
pub community: Arc<RwLock<Option<Arc<Community>>>>,
|
||||
identified: Arc<RwLock<bool>>,
|
||||
challenged: Arc<RwLock<bool>>,
|
||||
challenge: Arc<RwLock<String>>,
|
||||
auth: Arc<RwLock<Option<AuthUser>>>,
|
||||
pub ping: Arc<RwLock<i64>>,
|
||||
}
|
||||
impl CommunityConnection {
|
||||
pub fn new(
|
||||
session: WebSocketStream<tokio::net::TcpStream>,
|
||||
community: Arc<Community>,
|
||||
) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
session: Arc::new(Mutex::new(session)),
|
||||
user_id: Arc::new(RwLock::new(None)),
|
||||
community: Arc::new(RwLock::new(Some(community))),
|
||||
identified: Arc::new(RwLock::new(false)),
|
||||
challenged: Arc::new(RwLock::new(false)),
|
||||
challenge: Arc::new(RwLock::new(String::new())),
|
||||
auth: Arc::new(RwLock::new(None)),
|
||||
ping: Arc::new(RwLock::new(-1)),
|
||||
})
|
||||
}
|
||||
pub async fn send_message(&self, message: &CommunicationValue) {
|
||||
let mut session = self.session.lock().await;
|
||||
session
|
||||
.send(Message::Text(message.to_json().to_string()))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
pub async fn get_community(&self) -> Option<Arc<Community>> {
|
||||
self.community.read().await.clone()
|
||||
}
|
||||
pub async fn get_user_id(&self) -> Option<Uuid> {
|
||||
*self.user_id.read().await
|
||||
}
|
||||
pub async fn is_identified(&self) -> bool {
|
||||
*self.identified.read().await && *self.challenged.read().await
|
||||
}
|
||||
|
||||
pub async fn handle_message(self: Arc<Self>, message: String) {
|
||||
let cv = CommunicationValue::from_json(&message);
|
||||
|
||||
if cv.is_type(CommunicationType::identification) && !self.is_identified().await {
|
||||
self.handle_identification(cv).await;
|
||||
return;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::challenge_response) && !self.is_identified().await {
|
||||
self.handle_challenge_response(cv).await;
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.is_identified().await {
|
||||
return;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::ping) {
|
||||
self.handle_ping(cv).await;
|
||||
return;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::client_changed) {
|
||||
//self.handle_client_changed(cv).await;
|
||||
return;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::function) {
|
||||
self.handle_function(cv).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
async fn handle_function(&self, cv: CommunicationValue) {
|
||||
/*
|
||||
* {
|
||||
* "type": "function",
|
||||
* "id": "<uuid>",
|
||||
* "log": {
|
||||
* "log_level": 0,
|
||||
* "message": "running function"
|
||||
* },
|
||||
* "data": {
|
||||
* "codec": "<channel_codec>", // optional
|
||||
* "name": "<channel_name>",
|
||||
* "path": "<overlord_category_name>/<overlord_category_name>...",
|
||||
* "function": "<function_to_execute>",
|
||||
* "payload": {
|
||||
* "<ARG_1>": "<VAL_1>",
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
let name = cv.get_data(DataTypes::name).unwrap().as_str().unwrap();
|
||||
let path = cv.get_data(DataTypes::path).unwrap().as_str().unwrap();
|
||||
let function = cv.get_data(DataTypes::function).unwrap().as_str().unwrap();
|
||||
|
||||
let result = self
|
||||
.get_community()
|
||||
.await
|
||||
.unwrap()
|
||||
.run_function(self.get_user_id().await.unwrap(), name, path, function, &cv)
|
||||
.await;
|
||||
|
||||
self.send_message(&result).await;
|
||||
}
|
||||
async fn handle_identification(&self, cv: CommunicationValue) {
|
||||
let user_id = match cv.get_data(DataTypes::user_id) {
|
||||
Some(id_str) => match Uuid::parse_str(&id_str.to_string()) {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
self.send_error_response(
|
||||
&cv.get_id(),
|
||||
CommunicationType::error_invalid_user_id,
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(user) = get_user(user_id).await else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
{
|
||||
let mut auth_guard = self.auth.write().await;
|
||||
*auth_guard = Some(user.clone());
|
||||
|
||||
let mut user_id_guard = self.user_id.write().await;
|
||||
*user_id_guard = Some(user_id);
|
||||
|
||||
let mut identified_guard = self.identified.write().await;
|
||||
*identified_guard = true;
|
||||
}
|
||||
|
||||
let challenge_str: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(32)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
{
|
||||
let mut challenge_guard = self.challenge.write().await;
|
||||
*challenge_guard = challenge_str.clone();
|
||||
}
|
||||
|
||||
let user_public_key_bytes = match STANDARD.decode(&user.public_key) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let user_pub_key: PublicKey = match PublicKey::from_bytes(&user_public_key_bytes) {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error_invalid_user_id)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(community) = self.community.read().await.clone() else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let community_private_key = community.get_private_key();
|
||||
let community_public_key = community.get_public_key();
|
||||
|
||||
let shared_secret = match community_private_key.to_diffie_hellman(&user_pub_key) {
|
||||
Some(secret) => secret,
|
||||
None => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let aes_key = {
|
||||
let hk = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
|
||||
let mut key_bytes = [0u8; 32];
|
||||
hk.expand(b"challenge", &mut key_bytes)
|
||||
.expect("HKDF failure");
|
||||
key_bytes
|
||||
};
|
||||
|
||||
let cipher = Aes256Gcm::new_from_slice(&aes_key).expect("AES init failed");
|
||||
|
||||
let nonce_bytes: [u8; 12] = rand::random();
|
||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||
|
||||
let encrypted_challenge = match cipher.encrypt(nonce, challenge_str.as_bytes()) {
|
||||
Ok(data) => data,
|
||||
Err(_) => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut encrypted_out = nonce_bytes.to_vec();
|
||||
encrypted_out.extend(encrypted_challenge);
|
||||
|
||||
let response = CommunicationValue::new(CommunicationType::challenge)
|
||||
.add_data_str(
|
||||
DataTypes::public_key,
|
||||
STANDARD.encode(community_public_key.as_bytes()),
|
||||
)
|
||||
.add_data_str(DataTypes::challenge, STANDARD.encode(&encrypted_out))
|
||||
.with_id(cv.get_id());
|
||||
|
||||
self.send_message(&response).await;
|
||||
}
|
||||
async fn handle_challenge_response(self: Arc<Self>, cv: CommunicationValue) {
|
||||
let client_challenge_response_b64 = match cv.get_data(DataTypes::challenge) {
|
||||
Some(data) => data.to_string(),
|
||||
None => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let challenge_response_bytes = match STANDARD.decode(&client_challenge_response_b64) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if challenge_response_bytes.len() < 12 {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(user) = self.auth.read().await.clone() else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(user_pub_bytes) = STANDARD.decode(&user.public_key).ok() else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(user_pub_key) = PublicKey::from_bytes(&user_pub_bytes) else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(community) = self.community.read().await.clone() else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
let community_private_key = community.get_private_key();
|
||||
|
||||
let shared_secret = match community_private_key.to_diffie_hellman(&user_pub_key) {
|
||||
Some(secret) => secret,
|
||||
None => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let aes_key = {
|
||||
use hkdf::Hkdf;
|
||||
use sha2::Sha256;
|
||||
|
||||
let hk = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
|
||||
let mut key_bytes = [0u8; 32];
|
||||
hk.expand(b"challenge", &mut key_bytes)
|
||||
.expect("HKDF failure");
|
||||
key_bytes
|
||||
};
|
||||
|
||||
let (nonce_bytes, ciphertext) = challenge_response_bytes.split_at(12);
|
||||
let nonce = Nonce::from_slice(nonce_bytes);
|
||||
let cipher = Aes256Gcm::new_from_slice(&aes_key).expect("AES init failed");
|
||||
|
||||
let decrypted_bytes = match cipher.decrypt(nonce, ciphertext) {
|
||||
Ok(pt) => pt,
|
||||
Err(_) => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let client_response = match String::from_utf8(decrypted_bytes) {
|
||||
Ok(str) => str,
|
||||
Err(_) => {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let expected_challenge = self.challenge.read().await.clone();
|
||||
|
||||
if client_response != expected_challenge {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
self.close().await;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let mut authenticated_guard = self.challenged.write().await;
|
||||
*authenticated_guard = true;
|
||||
}
|
||||
|
||||
let Some(community) = self.community.read().await.clone() else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
let arc = Arc::new(community);
|
||||
|
||||
let Some(user_id) = self.get_user_id().await else {
|
||||
self.send_error_response(&cv.get_id(), CommunicationType::error)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
arc.add_connection(self.clone()).await;
|
||||
|
||||
let response = CommunicationValue::new(CommunicationType::identification_response)
|
||||
.add_data(DataTypes::interactables, {
|
||||
let a: Vec<Arc<Box<dyn Interactable>>> = arc.get_interactables(user_id).await;
|
||||
let mut c: JsonValue = JsonValue::new_object();
|
||||
for b in a {
|
||||
let mut subject = JsonValue::new_object();
|
||||
subject["codec"] = JsonValue::String(b.get_codec());
|
||||
subject["data"] = b.get_data();
|
||||
c[b.get_name()] = subject;
|
||||
}
|
||||
c
|
||||
})
|
||||
.with_id(cv.get_id());
|
||||
|
||||
self.send_message(&response).await;
|
||||
}
|
||||
|
||||
async fn send_error_response(&self, message_id: &Uuid, error_type: CommunicationType) {
|
||||
let error = CommunicationValue::new(error_type).with_id(*message_id);
|
||||
self.send_message(&error).await;
|
||||
}
|
||||
pub async fn close(&self) {
|
||||
let mut session = self.session.lock().await;
|
||||
let _ = session.close(None).await;
|
||||
}
|
||||
pub async fn handle_close(&self) {
|
||||
if self.is_identified().await {
|
||||
if let Some(user_id) = self.get_user_id().await {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_ping(&self, cv: CommunicationValue) {
|
||||
if let Some(last_ping) = cv.get_data(DataTypes::last_ping) {
|
||||
if let Ok(ping_val) = last_ping.to_string().parse::<i64>() {
|
||||
let mut ping_guard = self.ping.write().await;
|
||||
*ping_guard = ping_val;
|
||||
}
|
||||
}
|
||||
|
||||
let response = CommunicationValue::new(CommunicationType::pong).with_id(cv.get_id());
|
||||
|
||||
self.send_message(&response).await;
|
||||
}
|
||||
}
|
||||
45
src/communities/community_manager.rs
Normal file
45
src/communities/community_manager.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use crate::communities::community::{self, Community};
|
||||
use crate::gui::log_panel;
|
||||
use crate::util::file_util;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub static COMMUNITY_REGISTRY: Lazy<Arc<Mutex<HashMap<String, Arc<Community>>>>> =
|
||||
Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
|
||||
|
||||
pub async fn add_community(community: Arc<Community>) {
|
||||
COMMUNITY_REGISTRY
|
||||
.lock()
|
||||
.await
|
||||
.insert(community.get_name().to_string(), community);
|
||||
}
|
||||
pub async fn get_community(name: &str) -> Option<Arc<Community>> {
|
||||
if let Some(c) = COMMUNITY_REGISTRY.lock().await.get(name) {
|
||||
Some(c.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_communities() {
|
||||
let community_names = file_util::get_children("communities");
|
||||
for name in community_names {
|
||||
if let Some(community) = community::load(&name).await {
|
||||
add_community(community).await;
|
||||
} else {
|
||||
log_panel::log_message(format!("failed to load the {} community", &name));
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn save_communities() {
|
||||
for community in COMMUNITY_REGISTRY.lock().await.values() {
|
||||
community.save().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_communities() -> Vec<Arc<Community>> {
|
||||
COMMUNITY_REGISTRY.lock().await.values().cloned().collect()
|
||||
}
|
||||
67
src/communities/community_socket.rs
Normal file
67
src/communities/community_socket.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use crate::communities::{
|
||||
community::Community, community_connection::CommunityConnection, community_manager,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tungstenite::accept_hdr_async;
|
||||
use tungstenite::handshake::server::{Request, Response};
|
||||
|
||||
pub async fn start(port: u16) -> bool {
|
||||
let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await;
|
||||
if let Err(_) = listener {
|
||||
return false;
|
||||
}
|
||||
let listener = listener.unwrap();
|
||||
tokio::spawn(async move {
|
||||
while let Ok((stream, _)) = listener.accept().await {
|
||||
tokio::spawn(async move {
|
||||
let mut path: String = "/".to_string();
|
||||
let callback = |req: &Request, response: Response| {
|
||||
path = format!("{}", &req.uri().path());
|
||||
Ok(response)
|
||||
};
|
||||
let ws_stream = match accept_hdr_async(stream, callback).await {
|
||||
Ok(ws) => ws,
|
||||
Err(e) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
if path.starts_with("/community/") {
|
||||
let community_id = path.split("/").nth(2).unwrap();
|
||||
if let Some(community) = community_manager::get_community(community_id).await {
|
||||
let community_conn: Arc<CommunityConnection> =
|
||||
Arc::from(CommunityConnection::new(ws_stream, community));
|
||||
loop {
|
||||
let msg_result = {
|
||||
let mut session_lock = community_conn.session.lock().await;
|
||||
session_lock.next().await
|
||||
};
|
||||
|
||||
match msg_result {
|
||||
Some(Ok(msg)) => {
|
||||
if msg.is_text() {
|
||||
let text = msg.into_text().unwrap();
|
||||
community_conn.clone().handle_message(text).await;
|
||||
} else if msg.is_close() {
|
||||
community_conn.handle_close().await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
community_conn.handle_close().await;
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
community_conn.handle_close().await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
true
|
||||
}
|
||||
108
src/communities/interactables/category.rs
Normal file
108
src/communities/interactables/category.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use crate::{
|
||||
communities::{community::Community, interactables::interactable::Interactable},
|
||||
data::communication::{CommunicationType, CommunicationValue},
|
||||
};
|
||||
use axum::Json;
|
||||
use json::JsonValue;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Category {
|
||||
name: String,
|
||||
path: String,
|
||||
community: Arc<Community>,
|
||||
children: Vec<Arc<Box<dyn Interactable>>>,
|
||||
}
|
||||
impl Category {
|
||||
pub fn new() -> Category {
|
||||
Category {
|
||||
name: String::new(),
|
||||
path: String::new(),
|
||||
community: Arc::new(Community::new()),
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_child(&self, path: String, name: String) -> Option<Arc<Box<dyn Interactable>>> {
|
||||
if path.is_empty() {
|
||||
self.children
|
||||
.iter()
|
||||
.find(|child| child.get_name() == &name)
|
||||
.cloned()
|
||||
} else {
|
||||
let sub_module = path.split("/").next().unwrap();
|
||||
let next = self
|
||||
.children
|
||||
.iter()
|
||||
.find(|child| child.get_name() == sub_module)
|
||||
.unwrap();
|
||||
if next.get_codec() == "category" {
|
||||
let next_cat = next.as_any().downcast_ref::<Category>().unwrap();
|
||||
next_cat.get_child(path, name)
|
||||
} else {
|
||||
Some(next.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn get_children(&self) -> Vec<Arc<Box<dyn Interactable>>> {
|
||||
self.children.iter().map(|child| child.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Interactable for Category {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn get_codec(&self) -> String {
|
||||
"category".to_string()
|
||||
}
|
||||
fn set_name(&mut self, name: String) {
|
||||
self.name = name;
|
||||
}
|
||||
fn set_path(&mut self, path: String) {
|
||||
self.path = path;
|
||||
}
|
||||
fn get_community(&self) -> &Arc<Community> {
|
||||
&self.community
|
||||
}
|
||||
fn set_community(&mut self, community: Arc<Community>) {
|
||||
self.community = community;
|
||||
}
|
||||
fn get_name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
fn get_path(&self) -> &String {
|
||||
&self.path
|
||||
}
|
||||
fn get_total_path(&self) -> String {
|
||||
String::new() + &self.path + "/" + &self.name
|
||||
}
|
||||
fn get_data(&self) -> JsonValue {
|
||||
let mut v = JsonValue::new_object();
|
||||
for child in &self.children {
|
||||
let mut subject = JsonValue::new_object();
|
||||
subject["codec"] = JsonValue::String(child.get_codec());
|
||||
subject["data"] = child.get_data();
|
||||
v[child.get_name()] = subject;
|
||||
}
|
||||
v
|
||||
}
|
||||
fn run_function(&self, cv: CommunicationValue) -> CommunicationValue {
|
||||
CommunicationValue::new(CommunicationType::error)
|
||||
}
|
||||
fn to_json(&self) -> JsonValue {
|
||||
let mut v = JsonValue::new_object();
|
||||
v["children"] = JsonValue::new_array();
|
||||
for child in &self.children {
|
||||
v["children"].push(child.to_json());
|
||||
}
|
||||
v
|
||||
}
|
||||
fn load(&mut self, community: Arc<Community>, path: String, name: String, json: &JsonValue) {
|
||||
self.community = community;
|
||||
self.name = name;
|
||||
self.path = path;
|
||||
}
|
||||
}
|
||||
27
src/communities/interactables/interactable.rs
Normal file
27
src/communities/interactables/interactable.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use crate::{
|
||||
communities::community::Community,
|
||||
data::communication::{CommunicationType, CommunicationValue},
|
||||
};
|
||||
use axum::Json;
|
||||
use json::JsonValue;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type InteractableFactory = fn() -> Box<dyn Interactable>;
|
||||
|
||||
pub trait Interactable: Send + Sync + Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn get_codec(&self) -> String;
|
||||
fn get_name(&self) -> &String;
|
||||
fn get_path(&self) -> &String;
|
||||
fn get_total_path(&self) -> String;
|
||||
fn set_name(&mut self, name: String);
|
||||
fn set_path(&mut self, path: String);
|
||||
fn get_community(&self) -> &Arc<Community>;
|
||||
fn set_community(&mut self, community: Arc<Community>);
|
||||
fn run_function(&self, cv: CommunicationValue) -> CommunicationValue;
|
||||
fn get_data(&self) -> JsonValue;
|
||||
fn to_json(&self) -> JsonValue;
|
||||
fn load(&mut self, community: Arc<Community>, path: String, name: String, json: &JsonValue);
|
||||
}
|
||||
73
src/communities/interactables/registry.rs
Normal file
73
src/communities/interactables/registry.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use crate::communities::community::Community;
|
||||
use crate::communities::interactables::category::Category;
|
||||
use crate::communities::interactables::interactable::{Interactable, InteractableFactory};
|
||||
use crate::communities::interactables::text_chat::TextChat;
|
||||
use crate::communities::interactables::voice_chat::VoiceChat;
|
||||
use crate::gui::log_panel;
|
||||
use crate::util::file_util;
|
||||
use json::JsonValue;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub static INTERACTABLE_REGISTRY: Lazy<Arc<Mutex<HashMap<String, InteractableFactory>>>> =
|
||||
Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
|
||||
pub async fn load_interactables() {
|
||||
INTERACTABLE_REGISTRY
|
||||
.lock()
|
||||
.await
|
||||
.insert(TextChat::new().get_codec(), || {
|
||||
Box::new(TextChat::new()) as Box<dyn Interactable>
|
||||
});
|
||||
INTERACTABLE_REGISTRY
|
||||
.lock()
|
||||
.await
|
||||
.insert(VoiceChat::new().get_codec(), || {
|
||||
Box::new(VoiceChat::new()) as Box<dyn Interactable>
|
||||
});
|
||||
INTERACTABLE_REGISTRY
|
||||
.lock()
|
||||
.await
|
||||
.insert(Category::new().get_codec(), || {
|
||||
Box::new(Category::new()) as Box<dyn Interactable>
|
||||
});
|
||||
}
|
||||
pub async fn register_interactable(name: String, interactable: InteractableFactory) {
|
||||
INTERACTABLE_REGISTRY
|
||||
.lock()
|
||||
.await
|
||||
.insert(name.to_string(), interactable)
|
||||
.unwrap();
|
||||
}
|
||||
pub async fn get_interactable(name: &str) -> Box<dyn Interactable> {
|
||||
INTERACTABLE_REGISTRY.lock().await.get(name).unwrap()()
|
||||
}
|
||||
pub async fn save(interactable: &Arc<Box<dyn Interactable>>) {
|
||||
let mut json_object: JsonValue = interactable.to_json().clone();
|
||||
json_object["codec"] = JsonValue::String(interactable.get_codec());
|
||||
file_util::save_file(
|
||||
&format!(
|
||||
"communities/{}/interactables/{}",
|
||||
interactable.get_community().get_name(),
|
||||
interactable.get_path()
|
||||
),
|
||||
&format!("{}.json", interactable.get_name()),
|
||||
&json_object.to_string(),
|
||||
);
|
||||
}
|
||||
pub async fn load(
|
||||
c: Arc<Community>,
|
||||
path: String,
|
||||
name: String,
|
||||
) -> Box<dyn Interactable + 'static> {
|
||||
let s = file_util::load_file(
|
||||
&format!("communities/{}/interactables/{}", c.get_name(), path),
|
||||
&format!("{}.json", name),
|
||||
);
|
||||
let json_object: JsonValue = json::parse(&s).unwrap();
|
||||
let codec: String = json_object["codec"].as_str().unwrap().to_string();
|
||||
let mut interactable = get_interactable(&codec).await;
|
||||
interactable.load(c, path, name, &json_object);
|
||||
interactable
|
||||
}
|
||||
225
src/communities/interactables/text_chat.rs
Normal file
225
src/communities/interactables/text_chat.rs
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
use crate::{
|
||||
communities::{community::Community, interactables::interactable::Interactable},
|
||||
data::communication::{CommunicationType, CommunicationValue, DataTypes},
|
||||
gui::log_panel::log_message,
|
||||
util::file_util::{get_children, load_file, save_file},
|
||||
};
|
||||
use aes_gcm::aead::Payload;
|
||||
use axum::Json;
|
||||
use json::{JsonValue, array, object};
|
||||
use std::any::Any;
|
||||
use std::fs::{self, File};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
pub struct TextChat {
|
||||
name: String,
|
||||
path: String,
|
||||
community: Arc<Community>,
|
||||
}
|
||||
impl TextChat {
|
||||
pub fn new() -> TextChat {
|
||||
TextChat {
|
||||
name: String::new(),
|
||||
path: String::new(),
|
||||
community: Arc::new(Community::new()),
|
||||
}
|
||||
}
|
||||
pub fn add_message(&self, send_time: u128, sender: Uuid, message: &str) {
|
||||
let user_dir = &format!(
|
||||
"communities/{}/interactables/{}/{}",
|
||||
self.get_community().get_name(),
|
||||
self.get_path(),
|
||||
self.get_name()
|
||||
);
|
||||
|
||||
if let Err(e) = fs::create_dir_all(user_dir) {
|
||||
log_message(format!("Failed to create chat directory: {}", e));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut chunk_index = 0;
|
||||
let mut message_chunk = array![];
|
||||
|
||||
// find latest chunk not full (max 800 msgs)
|
||||
loop {
|
||||
let file_name = format!("msgs_{}.json", chunk_index);
|
||||
let file_content = load_file(&user_dir, &file_name);
|
||||
|
||||
if !file_content.is_empty() {
|
||||
if let Ok(current_chunk) = json::parse(&file_content) {
|
||||
if current_chunk.is_array() && current_chunk.len() < 800 {
|
||||
message_chunk = current_chunk;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log_message(format!("Failed to parse existing JSON file: {}", file_name));
|
||||
}
|
||||
} else {
|
||||
// New file, use empty array
|
||||
break;
|
||||
}
|
||||
|
||||
chunk_index += 1;
|
||||
if chunk_index > 1000 {
|
||||
log_message(format!("Too many message chunks. Aborting add."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let json_obj = object! {
|
||||
"timestamp" => send_time as i64,
|
||||
"content" => message,
|
||||
"sender" => sender.to_string(),
|
||||
};
|
||||
|
||||
if let Err(e) = message_chunk.push(json_obj) {
|
||||
log_message(format!("Failed to push new message into JSON array: {}", e));
|
||||
return;
|
||||
}
|
||||
|
||||
let file_name = format!("msgs_{}.json", chunk_index);
|
||||
log_message(format!("Saving message to {}/{}", user_dir, file_name));
|
||||
save_file(&user_dir, &file_name, &message_chunk.dump());
|
||||
}
|
||||
pub fn get_messages(&self, loaded_messages: i64, amount: i64) -> JsonValue {
|
||||
let mut messages = array![];
|
||||
|
||||
let mut latest_chunk_index: i32 = -1;
|
||||
let files = get_children(&format!(
|
||||
"communities/{}/interactables/{}/{}",
|
||||
self.get_community().get_name(),
|
||||
self.get_path(),
|
||||
self.get_name()
|
||||
));
|
||||
|
||||
for entry in files {
|
||||
if let Some(num) = {
|
||||
entry
|
||||
.strip_prefix("msgs_")
|
||||
.and_then(|s| s.strip_suffix(".json"))
|
||||
} {
|
||||
if let Ok(index) = num.parse::<i32>() {
|
||||
if index > latest_chunk_index {
|
||||
latest_chunk_index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if latest_chunk_index == -1 {
|
||||
return messages;
|
||||
}
|
||||
|
||||
let mut to_skip = loaded_messages;
|
||||
let mut needed = amount;
|
||||
|
||||
for chunk_index in (0..=latest_chunk_index).rev() {
|
||||
if needed == 0 {
|
||||
break;
|
||||
}
|
||||
let file_name = format!("msgs_{}.json", chunk_index);
|
||||
let file_content = load_file(
|
||||
&format!(
|
||||
"communities/{}/interactables/{}/{}",
|
||||
self.get_community().get_name(),
|
||||
self.get_path(),
|
||||
self.get_name()
|
||||
),
|
||||
&file_name,
|
||||
);
|
||||
if file_content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Ok(chunk) = json::parse(&file_content) {
|
||||
for i in (0..chunk.len()).rev() {
|
||||
if needed == 0 {
|
||||
break;
|
||||
}
|
||||
if to_skip > 0 {
|
||||
to_skip -= 1;
|
||||
continue;
|
||||
}
|
||||
messages.push(chunk[i].clone()).unwrap();
|
||||
needed -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages
|
||||
}
|
||||
}
|
||||
impl Interactable for TextChat {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn get_codec(&self) -> String {
|
||||
"text".to_string()
|
||||
}
|
||||
fn set_name(&mut self, name: String) {
|
||||
self.name = name;
|
||||
}
|
||||
fn set_path(&mut self, path: String) {
|
||||
self.path = path;
|
||||
}
|
||||
fn get_community(&self) -> &Arc<Community> {
|
||||
&self.community
|
||||
}
|
||||
fn set_community(&mut self, community: Arc<Community>) {
|
||||
self.community = community;
|
||||
}
|
||||
fn get_name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
fn get_path(&self) -> &String {
|
||||
&self.path
|
||||
}
|
||||
fn get_total_path(&self) -> String {
|
||||
String::new() + &self.path + "/" + &self.name
|
||||
}
|
||||
fn get_data(&self) -> JsonValue {
|
||||
JsonValue::new_object()
|
||||
}
|
||||
fn run_function(&self, cv: CommunicationValue) -> CommunicationValue {
|
||||
let payload = cv.get_data(DataTypes::payload).unwrap();
|
||||
if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "get_messages" {
|
||||
let amount = payload["amount"].as_i64().unwrap();
|
||||
let loaded_messages = payload["loaded_messages"].as_i64().unwrap();
|
||||
let messages = self.get_messages(loaded_messages, amount);
|
||||
let mut payload = JsonValue::new_object();
|
||||
payload["messages"] = messages;
|
||||
return CommunicationValue::new(CommunicationType::function)
|
||||
.with_id(cv.get_id())
|
||||
.add_data_str(DataTypes::name, self.name.clone())
|
||||
.add_data_str(DataTypes::path, self.path.clone())
|
||||
.add_data_str(DataTypes::result, "message_chunk".to_string())
|
||||
.add_data(DataTypes::payload, payload);
|
||||
}
|
||||
if cv.get_data(DataTypes::function).unwrap().as_str().unwrap() == "send_message" {
|
||||
let message = payload["message"].as_str().unwrap();
|
||||
let milliseconds_timestamp: u128 = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis();
|
||||
self.add_message(milliseconds_timestamp, cv.get_sender().unwrap(), message);
|
||||
return CommunicationValue::new(CommunicationType::function)
|
||||
.with_id(cv.get_id())
|
||||
.add_data_str(DataTypes::name, self.name.clone())
|
||||
.add_data_str(DataTypes::path, self.path.clone())
|
||||
.add_data_str(DataTypes::result, "message_received".to_string())
|
||||
.add_data(DataTypes::payload, JsonValue::new_object());
|
||||
}
|
||||
CommunicationValue::new(CommunicationType::error).with_id(cv.get_id())
|
||||
}
|
||||
fn to_json(&self) -> JsonValue {
|
||||
let mut v = JsonValue::new_object();
|
||||
v
|
||||
}
|
||||
fn load(&mut self, community: Arc<Community>, path: String, name: String, json: &JsonValue) {
|
||||
self.community = community;
|
||||
self.name = name;
|
||||
self.path = path;
|
||||
}
|
||||
}
|
||||
83
src/communities/interactables/voice_chat.rs
Normal file
83
src/communities/interactables/voice_chat.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use crate::{
|
||||
communities::{community::Community, interactables::interactable::Interactable},
|
||||
data::communication::{CommunicationType, CommunicationValue},
|
||||
};
|
||||
use json::JsonValue;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
pub enum CallUserState {
|
||||
Active,
|
||||
Muted,
|
||||
Deafed,
|
||||
}
|
||||
|
||||
pub struct CallUser {
|
||||
user_id: Uuid,
|
||||
user_state: CallUserState,
|
||||
streaming: bool,
|
||||
}
|
||||
|
||||
pub struct VoiceChat {
|
||||
name: String,
|
||||
path: String,
|
||||
community: Arc<Community>,
|
||||
users: Vec<CallUser>,
|
||||
}
|
||||
impl VoiceChat {
|
||||
pub fn new() -> VoiceChat {
|
||||
VoiceChat {
|
||||
name: String::new(),
|
||||
path: String::new(),
|
||||
community: Arc::new(Community::new()),
|
||||
users: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Interactable for VoiceChat {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
fn get_codec(&self) -> String {
|
||||
"voice".to_string()
|
||||
}
|
||||
fn set_name(&mut self, name: String) {
|
||||
self.name = name;
|
||||
}
|
||||
fn set_path(&mut self, path: String) {
|
||||
self.path = path;
|
||||
}
|
||||
fn get_community(&self) -> &Arc<Community> {
|
||||
&self.community
|
||||
}
|
||||
fn set_community(&mut self, community: Arc<Community>) {
|
||||
self.community = community;
|
||||
}
|
||||
fn get_name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
fn get_path(&self) -> &String {
|
||||
&self.path
|
||||
}
|
||||
fn get_total_path(&self) -> String {
|
||||
String::new() + &self.path + "/" + &self.name
|
||||
}
|
||||
fn get_data(&self) -> JsonValue {
|
||||
JsonValue::new_object()
|
||||
}
|
||||
fn run_function(&self, cv: CommunicationValue) -> CommunicationValue {
|
||||
CommunicationValue::new(CommunicationType::error)
|
||||
}
|
||||
fn to_json(&self) -> JsonValue {
|
||||
let mut v = JsonValue::new_object();
|
||||
v
|
||||
}
|
||||
fn load(&mut self, community: Arc<Community>, path: String, name: String, json: &JsonValue) {
|
||||
self.community = community;
|
||||
self.name = name;
|
||||
self.path = path;
|
||||
}
|
||||
}
|
||||
11
src/communities/mod.rs
Normal file
11
src/communities/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pub mod community_manager;
|
||||
pub mod community_socket;
|
||||
pub mod interactables {
|
||||
pub mod category;
|
||||
pub mod interactable;
|
||||
pub mod registry;
|
||||
pub mod text_chat;
|
||||
pub mod voice_chat;
|
||||
}
|
||||
pub mod community;
|
||||
pub mod community_connection;
|
||||
Loading…
Reference in a new issue