botserver/src/meet/conversations.rs
Rodrigo Rodriguez 5ea171d126
Some checks failed
BotServer CI / build (push) Failing after 1m34s
Refactor: Split large files into modular subdirectories
Split 20+ files over 1000 lines into focused subdirectories for better
maintainability and code organization. All changes maintain backward
compatibility through re-export wrappers.

Major splits:
- attendance/llm_assist.rs (2074→7 modules)
- basic/keywords/face_api.rs → face_api/ (7 modules)
- basic/keywords/file_operations.rs → file_ops/ (8 modules)
- basic/keywords/hear_talk.rs → hearing/ (6 modules)
- channels/wechat.rs → wechat/ (10 modules)
- channels/youtube.rs → youtube/ (5 modules)
- contacts/mod.rs → contacts_api/ (6 modules)
- core/bootstrap/mod.rs → bootstrap/ (5 modules)
- core/shared/admin.rs → admin_*.rs (5 modules)
- designer/canvas.rs → canvas_api/ (6 modules)
- designer/mod.rs → designer_api/ (6 modules)
- docs/handlers.rs → handlers_api/ (11 modules)
- drive/mod.rs → drive_handlers.rs, drive_types.rs
- learn/mod.rs → types.rs
- main.rs → main_module/ (7 modules)
- meet/webinar.rs → webinar_api/ (8 modules)
- paper/mod.rs → (10 modules)
- security/auth.rs → auth_api/ (7 modules)
- security/passkey.rs → (4 modules)
- sources/mod.rs → sources_api/ (5 modules)
- tasks/mod.rs → task_api/ (5 modules)

Stats: 38,040 deletions, 1,315 additions across 318 files

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 21:09:30 +00:00

509 lines
14 KiB
Rust

use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::Json,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use uuid::Uuid;
use crate::core::shared::state::AppState;
#[derive(Debug, Deserialize)]
pub struct CreateConversationRequest {
pub name: String,
pub description: Option<String>,
pub conversation_type: Option<String>,
pub participants: Vec<Uuid>,
pub is_private: Option<bool>,
}
#[derive(Debug, Deserialize)]
pub struct JoinConversationRequest {
pub user_id: Uuid,
pub display_name: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct LeaveConversationRequest {
pub user_id: Uuid,
}
#[derive(Debug, Deserialize)]
pub struct SendMessageRequest {
pub content: String,
pub message_type: Option<String>,
pub reply_to: Option<Uuid>,
pub attachments: Option<Vec<String>>,
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Deserialize)]
pub struct EditMessageRequest {
pub content: String,
}
#[derive(Debug, Deserialize)]
pub struct ReactToMessageRequest {
pub reaction: String,
}
#[derive(Debug, Deserialize)]
pub struct SearchMessagesQuery {
pub query: String,
pub from_date: Option<String>,
pub to_date: Option<String>,
pub user_id: Option<Uuid>,
}
#[derive(Debug, Deserialize)]
pub struct StartCallRequest {
pub call_type: String,
pub participants: Option<Vec<Uuid>>,
}
#[derive(Debug, Deserialize)]
pub struct ScreenShareRequest {
pub quality: Option<String>,
pub audio_included: Option<bool>,
}
#[derive(Debug, Serialize)]
pub struct ConversationResponse {
pub id: Uuid,
pub name: String,
pub description: Option<String>,
pub conversation_type: String,
pub is_private: bool,
pub participant_count: u32,
pub unread_count: u32,
pub created_by: Uuid,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub last_message: Option<MessageSummary>,
}
#[derive(Debug, Serialize)]
pub struct MessageSummary {
pub id: Uuid,
pub sender_id: Uuid,
pub content: String,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct MessageResponse {
pub id: Uuid,
pub conversation_id: Uuid,
pub sender_id: Uuid,
pub sender_name: String,
pub content: String,
pub message_type: String,
pub reply_to: Option<Uuid>,
pub attachments: Vec<String>,
pub reactions: Vec<ReactionResponse>,
pub is_pinned: bool,
pub is_edited: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct ReactionResponse {
pub user_id: Uuid,
pub reaction: String,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct ParticipantResponse {
pub user_id: Uuid,
pub username: String,
pub display_name: Option<String>,
pub role: String,
pub status: String,
pub joined_at: DateTime<Utc>,
pub is_typing: bool,
}
#[derive(Debug, Serialize)]
pub struct CallResponse {
pub id: Uuid,
pub conversation_id: Uuid,
pub call_type: String,
pub status: String,
pub started_by: Uuid,
pub participants: Vec<CallParticipant>,
pub started_at: DateTime<Utc>,
pub ended_at: Option<DateTime<Utc>>,
pub duration_seconds: Option<i64>,
pub recording_url: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct CallParticipant {
pub user_id: Uuid,
pub username: String,
pub status: String,
pub is_muted: bool,
pub is_video_enabled: bool,
pub is_screen_sharing: bool,
pub joined_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct ScreenShareResponse {
pub id: Uuid,
pub user_id: Uuid,
pub conversation_id: Uuid,
pub status: String,
pub quality: String,
pub audio_included: bool,
pub started_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct WhiteboardResponse {
pub id: Uuid,
pub conversation_id: Uuid,
pub name: String,
pub created_by: Uuid,
pub collaborators: Vec<Uuid>,
pub content_url: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct SuccessResponse {
pub success: bool,
pub message: Option<String>,
}
pub async fn create_conversation(
State(_state): State<Arc<AppState>>,
Json(req): Json<CreateConversationRequest>,
) -> Result<Json<ConversationResponse>, (StatusCode, Json<serde_json::Value>)> {
let conversation_id = Uuid::new_v4();
let now = Utc::now();
let creator_id = Uuid::new_v4();
let conversation = ConversationResponse {
id: conversation_id,
name: req.name,
description: req.description,
conversation_type: req.conversation_type.unwrap_or_else(|| "group".to_string()),
is_private: req.is_private.unwrap_or(false),
participant_count: req.participants.len() as u32,
unread_count: 0,
created_by: creator_id,
created_at: now,
updated_at: now,
last_message: None,
};
Ok(Json(conversation))
}
pub async fn join_conversation(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
Json(req): Json<JoinConversationRequest>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some(format!(
"User {} joined conversation {}",
req.user_id, conversation_id
)),
}))
}
pub async fn leave_conversation(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
Json(req): Json<LeaveConversationRequest>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some(format!(
"User {} left conversation {}",
req.user_id, conversation_id
)),
}))
}
pub async fn get_conversation_members(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<Vec<ParticipantResponse>>, (StatusCode, Json<serde_json::Value>)> {
let members = vec![ParticipantResponse {
user_id: Uuid::new_v4(),
username: "user1".to_string(),
display_name: Some("User One".to_string()),
role: "member".to_string(),
status: "online".to_string(),
joined_at: Utc::now(),
is_typing: false,
}];
Ok(Json(members))
}
pub async fn get_conversation_messages(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
let messages = vec![];
Ok(Json(messages))
}
pub async fn send_message(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
Json(req): Json<SendMessageRequest>,
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
let message_id = Uuid::new_v4();
let sender_id = Uuid::new_v4();
let now = Utc::now();
let message = MessageResponse {
id: message_id,
conversation_id,
sender_id,
sender_name: "User".to_string(),
content: req.content,
message_type: req.message_type.unwrap_or_else(|| "text".to_string()),
reply_to: req.reply_to,
attachments: req.attachments.unwrap_or_default(),
reactions: vec![],
is_pinned: false,
is_edited: false,
created_at: now,
updated_at: now,
};
Ok(Json(message))
}
pub async fn edit_message(
State(_state): State<Arc<AppState>>,
Path((conversation_id, message_id)): Path<(Uuid, Uuid)>,
Json(req): Json<EditMessageRequest>,
) -> Result<Json<MessageResponse>, (StatusCode, Json<serde_json::Value>)> {
let now = Utc::now();
let message = MessageResponse {
id: message_id,
conversation_id,
sender_id: Uuid::new_v4(),
sender_name: "User".to_string(),
content: req.content,
message_type: "text".to_string(),
reply_to: None,
attachments: vec![],
reactions: vec![],
is_pinned: false,
is_edited: true,
created_at: now,
updated_at: now,
};
Ok(Json(message))
}
pub async fn delete_message(
State(_state): State<Arc<AppState>>,
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some(format!("Message {} deleted", message_id)),
}))
}
pub async fn react_to_message(
State(_state): State<Arc<AppState>>,
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
Json(req): Json<ReactToMessageRequest>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some(format!(
"Reaction '{}' added to message {}",
req.reaction, message_id
)),
}))
}
pub async fn pin_message(
State(_state): State<Arc<AppState>>,
Path((_conversation_id, message_id)): Path<(Uuid, Uuid)>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some(format!("Message {} pinned", message_id)),
}))
}
pub async fn search_messages(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
Query(_params): Query<SearchMessagesQuery>,
) -> Result<Json<Vec<MessageResponse>>, (StatusCode, Json<serde_json::Value>)> {
let messages = vec![];
Ok(Json(messages))
}
pub async fn start_call(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
Json(req): Json<StartCallRequest>,
) -> Result<Json<CallResponse>, (StatusCode, Json<serde_json::Value>)> {
let call_id = Uuid::new_v4();
let starter_id = Uuid::new_v4();
let now = Utc::now();
let call = CallResponse {
id: call_id,
conversation_id,
call_type: req.call_type,
status: "active".to_string(),
started_by: starter_id,
participants: vec![],
started_at: now,
ended_at: None,
duration_seconds: None,
recording_url: None,
};
Ok(Json(call))
}
pub async fn join_call(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Joined call successfully".to_string()),
}))
}
pub async fn leave_call(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Left call successfully".to_string()),
}))
}
pub async fn mute_call(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Audio muted".to_string()),
}))
}
pub async fn unmute_call(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Audio unmuted".to_string()),
}))
}
pub async fn start_screen_share(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
Json(req): Json<ScreenShareRequest>,
) -> Result<Json<ScreenShareResponse>, (StatusCode, Json<serde_json::Value>)> {
let share_id = Uuid::new_v4();
let user_id = Uuid::new_v4();
let now = Utc::now();
let screen_share = ScreenShareResponse {
id: share_id,
user_id,
conversation_id,
status: "active".to_string(),
quality: req.quality.unwrap_or_else(|| "high".to_string()),
audio_included: req.audio_included.unwrap_or(false),
started_at: now,
};
Ok(Json(screen_share))
}
pub async fn stop_screen_share(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Screen sharing stopped".to_string()),
}))
}
pub async fn start_recording(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Recording started".to_string()),
}))
}
pub async fn stop_recording(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Recording stopped".to_string()),
}))
}
pub async fn create_whiteboard(
State(_state): State<Arc<AppState>>,
Path(conversation_id): Path<Uuid>,
) -> Result<Json<WhiteboardResponse>, (StatusCode, Json<serde_json::Value>)> {
let whiteboard_id = Uuid::new_v4();
let creator_id = Uuid::new_v4();
let now = Utc::now();
let whiteboard = WhiteboardResponse {
id: whiteboard_id,
conversation_id,
name: "New Whiteboard".to_string(),
created_by: creator_id,
collaborators: vec![creator_id],
content_url: format!("/whiteboards/{}/content", whiteboard_id),
created_at: now,
updated_at: now,
};
Ok(Json(whiteboard))
}
pub async fn collaborate_whiteboard(
State(_state): State<Arc<AppState>>,
Path(_conversation_id): Path<Uuid>,
Json(_data): Json<serde_json::Value>,
) -> Result<Json<SuccessResponse>, (StatusCode, Json<serde_json::Value>)> {
Ok(Json(SuccessResponse {
success: true,
message: Some("Whiteboard collaboration started".to_string()),
}))
}