diff --git a/src/prompts/ai/analyze-customer-sentiment.bas b/prompts/ai/analyze-customer-sentiment.bas similarity index 100% rename from src/prompts/ai/analyze-customer-sentiment.bas rename to prompts/ai/analyze-customer-sentiment.bas diff --git a/src/prompts/analytics/sales-performance.bas b/prompts/analytics/sales-performance.bas similarity index 100% rename from src/prompts/analytics/sales-performance.bas rename to prompts/analytics/sales-performance.bas diff --git a/src/prompts/business/create-lead-from-draft.bas b/prompts/business/create-lead-from-draft.bas similarity index 100% rename from src/prompts/business/create-lead-from-draft.bas rename to prompts/business/create-lead-from-draft.bas diff --git a/src/prompts/business/data-enrichment.bas b/prompts/business/data-enrichment.bas similarity index 100% rename from src/prompts/business/data-enrichment.bas rename to prompts/business/data-enrichment.bas diff --git a/src/prompts/business/on-emulator-sent.bas b/prompts/business/on-emulator-sent.bas similarity index 100% rename from src/prompts/business/on-emulator-sent.bas rename to prompts/business/on-emulator-sent.bas diff --git a/src/prompts/business/send-proposal-v0.bas b/prompts/business/send-proposal-v0.bas similarity index 100% rename from src/prompts/business/send-proposal-v0.bas rename to prompts/business/send-proposal-v0.bas diff --git a/src/prompts/business/send-proposal.bas b/prompts/business/send-proposal.bas similarity index 100% rename from src/prompts/business/send-proposal.bas rename to prompts/business/send-proposal.bas diff --git a/src/prompts/calendar/schedule-meeting.bas b/prompts/calendar/schedule-meeting.bas similarity index 100% rename from src/prompts/calendar/schedule-meeting.bas rename to prompts/calendar/schedule-meeting.bas diff --git a/src/prompts/code/system-code.bas b/prompts/code/system-code.bas similarity index 100% rename from src/prompts/code/system-code.bas rename to prompts/code/system-code.bas diff --git a/src/prompts/communication/keyword-sendmail.bas b/prompts/communication/keyword-sendmail.bas similarity index 100% rename from src/prompts/communication/keyword-sendmail.bas rename to prompts/communication/keyword-sendmail.bas diff --git a/src/prompts/conversations/meeting-assistant.bas b/prompts/conversations/meeting-assistant.bas similarity index 100% rename from src/prompts/conversations/meeting-assistant.bas rename to prompts/conversations/meeting-assistant.bas diff --git a/src/prompts/core/system-prompt.bas b/prompts/core/system-prompt.bas similarity index 100% rename from src/prompts/core/system-prompt.bas rename to prompts/core/system-prompt.bas diff --git a/src/prompts/crm/update-opportunity.bas b/prompts/crm/update-opportunity.bas similarity index 100% rename from src/prompts/crm/update-opportunity.bas rename to prompts/crm/update-opportunity.bas diff --git a/src/PROMPT.md b/prompts/development/general.md similarity index 100% rename from src/PROMPT.md rename to prompts/development/general.md diff --git a/src/services/prompt.md b/prompts/development/service.md similarity index 100% rename from src/services/prompt.md rename to prompts/development/service.md diff --git a/src/prompts/files/search-documents.bas b/prompts/files/search-documents.bas similarity index 100% rename from src/prompts/files/search-documents.bas rename to prompts/files/search-documents.bas diff --git a/src/prompts/geral.bas b/prompts/geral.bas similarity index 100% rename from src/prompts/geral.bas rename to prompts/geral.bas diff --git a/src/prompts/groups/create-workspace.bas b/prompts/groups/create-workspace.bas similarity index 100% rename from src/prompts/groups/create-workspace.bas rename to prompts/groups/create-workspace.bas diff --git a/src/prompts/health/system-check.bas b/prompts/health/system-check.bas similarity index 100% rename from src/prompts/health/system-check.bas rename to prompts/health/system-check.bas diff --git a/src/prompts/marketing/add-new-idea.bas b/prompts/marketing/add-new-idea.bas similarity index 100% rename from src/prompts/marketing/add-new-idea.bas rename to prompts/marketing/add-new-idea.bas diff --git a/src/prompts/scheduled/basic-check.bas b/prompts/scheduled/basic-check.bas similarity index 100% rename from src/prompts/scheduled/basic-check.bas rename to prompts/scheduled/basic-check.bas diff --git a/src/prompts/security/access-review.bas b/prompts/security/access-review.bas similarity index 100% rename from src/prompts/security/access-review.bas rename to prompts/security/access-review.bas diff --git a/src/prompts/tools/on-receive-email.bas b/prompts/tools/on-receive-email.bas similarity index 100% rename from src/prompts/tools/on-receive-email.bas rename to prompts/tools/on-receive-email.bas diff --git a/src/scripts/prompt.txt b/scripts/containers/PROMPT.md similarity index 100% rename from src/scripts/prompt.txt rename to scripts/containers/PROMPT.md diff --git a/src/scripts/containers/alm-ci.sh b/scripts/containers/alm-ci.sh similarity index 100% rename from src/scripts/containers/alm-ci.sh rename to scripts/containers/alm-ci.sh diff --git a/src/scripts/containers/alm.sh b/scripts/containers/alm.sh similarity index 100% rename from src/scripts/containers/alm.sh rename to scripts/containers/alm.sh diff --git a/src/scripts/containers/bot.sh b/scripts/containers/bot.sh similarity index 100% rename from src/scripts/containers/bot.sh rename to scripts/containers/bot.sh diff --git a/src/scripts/containers/cache.sh b/scripts/containers/cache.sh similarity index 100% rename from src/scripts/containers/cache.sh rename to scripts/containers/cache.sh diff --git a/src/scripts/containers/desktop.sh b/scripts/containers/desktop.sh similarity index 100% rename from src/scripts/containers/desktop.sh rename to scripts/containers/desktop.sh diff --git a/src/scripts/containers/directory.sh b/scripts/containers/directory.sh similarity index 100% rename from src/scripts/containers/directory.sh rename to scripts/containers/directory.sh diff --git a/src/scripts/containers/dns.sh b/scripts/containers/dns.sh similarity index 100% rename from src/scripts/containers/dns.sh rename to scripts/containers/dns.sh diff --git a/src/scripts/containers/doc-editor.sh b/scripts/containers/doc-editor.sh similarity index 100% rename from src/scripts/containers/doc-editor.sh rename to scripts/containers/doc-editor.sh diff --git a/src/scripts/containers/drive.sh b/scripts/containers/drive.sh similarity index 100% rename from src/scripts/containers/drive.sh rename to scripts/containers/drive.sh diff --git a/src/scripts/containers/email.sh b/scripts/containers/email.sh similarity index 100% rename from src/scripts/containers/email.sh rename to scripts/containers/email.sh diff --git a/src/scripts/containers/host.sh b/scripts/containers/host.sh similarity index 100% rename from src/scripts/containers/host.sh rename to scripts/containers/host.sh diff --git a/src/scripts/containers/llm.sh b/scripts/containers/llm.sh similarity index 100% rename from src/scripts/containers/llm.sh rename to scripts/containers/llm.sh diff --git a/src/scripts/containers/meeting.sh b/scripts/containers/meeting.sh similarity index 100% rename from src/scripts/containers/meeting.sh rename to scripts/containers/meeting.sh diff --git a/src/scripts/containers/proxy.sh b/scripts/containers/proxy.sh similarity index 100% rename from src/scripts/containers/proxy.sh rename to scripts/containers/proxy.sh diff --git a/src/scripts/containers/social.sh b/scripts/containers/social.sh similarity index 100% rename from src/scripts/containers/social.sh rename to scripts/containers/social.sh diff --git a/src/scripts/containers/system.sh b/scripts/containers/system.sh similarity index 100% rename from src/scripts/containers/system.sh rename to scripts/containers/system.sh diff --git a/src/scripts/containers/table-editor.sh b/scripts/containers/table-editor.sh similarity index 100% rename from src/scripts/containers/table-editor.sh rename to scripts/containers/table-editor.sh diff --git a/src/scripts/containers/tables.sh b/scripts/containers/tables.sh similarity index 100% rename from src/scripts/containers/tables.sh rename to scripts/containers/tables.sh diff --git a/src/scripts/containers/vector-db.sh b/scripts/containers/vector-db.sh similarity index 100% rename from src/scripts/containers/vector-db.sh rename to scripts/containers/vector-db.sh diff --git a/src/scripts/containers/webmail.sh b/scripts/containers/webmail.sh similarity index 100% rename from src/scripts/containers/webmail.sh rename to scripts/containers/webmail.sh diff --git a/src/scripts/database/0001.sql b/scripts/database/0001.sql similarity index 100% rename from src/scripts/database/0001.sql rename to scripts/database/0001.sql diff --git a/src/scripts/database/0002.sql b/scripts/database/0002.sql similarity index 100% rename from src/scripts/database/0002.sql rename to scripts/database/0002.sql diff --git a/src/scripts/database/0003.sql b/scripts/database/0003.sql similarity index 100% rename from src/scripts/database/0003.sql rename to scripts/database/0003.sql diff --git a/src/scripts/database/0004.sql b/scripts/database/0004.sql similarity index 100% rename from src/scripts/database/0004.sql rename to scripts/database/0004.sql diff --git a/src/scripts/database/001_init.sql b/scripts/database/001_init.sql similarity index 100% rename from src/scripts/database/001_init.sql rename to scripts/database/001_init.sql diff --git a/src/scripts/utils/add-drive-user.sh b/scripts/utils/add-drive-user.sh similarity index 100% rename from src/scripts/utils/add-drive-user.sh rename to scripts/utils/add-drive-user.sh diff --git a/src/scripts/check-space.sh b/scripts/utils/check-space.sh similarity index 100% rename from src/scripts/check-space.sh rename to scripts/utils/check-space.sh diff --git a/src/scripts/utils/cleaner.sh b/scripts/utils/cleaner.sh similarity index 100% rename from src/scripts/utils/cleaner.sh rename to scripts/utils/cleaner.sh diff --git a/src/scripts/utils/disk-size.md b/scripts/utils/disk-size.md similarity index 100% rename from src/scripts/utils/disk-size.md rename to scripts/utils/disk-size.md diff --git a/src/scripts/utils/email-ips.sh b/scripts/utils/email-ips.sh similarity index 100% rename from src/scripts/utils/email-ips.sh rename to scripts/utils/email-ips.sh diff --git a/src/scripts/utils/install-libreoffice-online.sh b/scripts/utils/install-libreoffice-online.sh similarity index 100% rename from src/scripts/utils/install-libreoffice-online.sh rename to scripts/utils/install-libreoffice-online.sh diff --git a/src/scripts/utils/set-limits.sh b/scripts/utils/set-limits.sh similarity index 100% rename from src/scripts/utils/set-limits.sh rename to scripts/utils/set-limits.sh diff --git a/src/scripts/utils/set-size-5GB.sh b/scripts/utils/set-size-5GB.sh similarity index 100% rename from src/scripts/utils/set-size-5GB.sh rename to scripts/utils/set-size-5GB.sh diff --git a/src/scripts/utils/setup-host.sh b/scripts/utils/setup-host.sh similarity index 100% rename from src/scripts/utils/setup-host.sh rename to scripts/utils/setup-host.sh diff --git a/src/scripts/utils/startup.sh b/scripts/utils/startup.sh similarity index 100% rename from src/scripts/utils/startup.sh rename to scripts/utils/startup.sh diff --git a/src/services/auth/mod.rs b/src/auth/mod.rs similarity index 100% rename from src/services/auth/mod.rs rename to src/auth/mod.rs diff --git a/src/services/automation/mod.rs b/src/automation/mod.rs similarity index 98% rename from src/services/automation/mod.rs rename to src/automation/mod.rs index 08206d0..5aec59e 100644 --- a/src/services/automation/mod.rs +++ b/src/automation/mod.rs @@ -1,6 +1,6 @@ use crate::models::automation_model::{Automation, TriggerKind}; -use crate::services::script::ScriptService; -use crate::services::state::AppState; +use crate::basic::ScriptService; +use crate::state::AppState; use chrono::Datelike; use chrono::Timelike; use chrono::{DateTime, Utc}; diff --git a/src/services/keywords/create_draft.rs b/src/basic/keywords/create_draft.rs similarity index 93% rename from src/services/keywords/create_draft.rs rename to src/basic/keywords/create_draft.rs index eb14054..7e866c5 100644 --- a/src/services/keywords/create_draft.rs +++ b/src/basic/keywords/create_draft.rs @@ -1,6 +1,6 @@ -use crate::services::email::save_email_draft; -use crate::services::email::{fetch_latest_sent_to, SaveDraftRequest}; -use crate::services::state::AppState; +use crate::email::save_email_draft; +use crate::email::{fetch_latest_sent_to, SaveDraftRequest}; +use crate::state::AppState; use rhai::Dynamic; use rhai::Engine; diff --git a/src/services/keywords/create_site.rs b/src/basic/keywords/create_site.rs similarity index 96% rename from src/services/keywords/create_site.rs rename to src/basic/keywords/create_site.rs index 69db7c2..cc63c3f 100644 --- a/src/services/keywords/create_site.rs +++ b/src/basic/keywords/create_site.rs @@ -7,8 +7,8 @@ use std::fs; use std::io::Read; use std::path::PathBuf; -use crate::services::state::AppState; -use crate::services::utils; +use crate::state::AppState; +use crate::utils; pub fn create_site_keyword(state: &AppState, engine: &mut Engine) { let state_clone = state.clone(); @@ -43,7 +43,7 @@ pub fn create_site_keyword(state: &AppState, engine: &mut Engine) { } async fn create_site( - config: &crate::services::config::AppConfig, + config: &crate::config::AppConfig, alias: Dynamic, template_dir: Dynamic, prompt: Dynamic, diff --git a/src/services/keywords/find.rs b/src/basic/keywords/find.rs similarity index 94% rename from src/services/keywords/find.rs rename to src/basic/keywords/find.rs index 34a9dde..51b08de 100644 --- a/src/services/keywords/find.rs +++ b/src/basic/keywords/find.rs @@ -4,10 +4,10 @@ use rhai::Engine; use serde_json::{json, Value}; use sqlx::PgPool; -use crate::services::state::AppState; -use crate::services::utils; -use crate::services::utils::row_to_json; -use crate::services::utils::to_array; +use crate::state::AppState; +use crate::utils; +use crate::utils::row_to_json; +use crate::utils::to_array; pub fn find_keyword(state: &AppState, engine: &mut Engine) { let db = state.db_custom.clone(); diff --git a/src/services/keywords/first.rs b/src/basic/keywords/first.rs similarity index 100% rename from src/services/keywords/first.rs rename to src/basic/keywords/first.rs diff --git a/src/services/keywords/for_next.rs b/src/basic/keywords/for_next.rs similarity index 98% rename from src/services/keywords/for_next.rs rename to src/basic/keywords/for_next.rs index 97e73d1..b1ca46a 100644 --- a/src/services/keywords/for_next.rs +++ b/src/basic/keywords/for_next.rs @@ -1,4 +1,4 @@ -use crate::services::state::AppState; +use crate::state::AppState; use log::info; use rhai::Dynamic; use rhai::Engine; diff --git a/src/services/keywords/format.rs b/src/basic/keywords/format.rs similarity index 100% rename from src/services/keywords/format.rs rename to src/basic/keywords/format.rs diff --git a/src/services/keywords/get.rs b/src/basic/keywords/get.rs similarity index 98% rename from src/services/keywords/get.rs rename to src/basic/keywords/get.rs index ecdb97a..0d00d5b 100644 --- a/src/services/keywords/get.rs +++ b/src/basic/keywords/get.rs @@ -1,6 +1,6 @@ use log::info; -use crate::services::state::AppState; +use crate::state::AppState; use reqwest::{self, Client}; use rhai::{Dynamic, Engine}; use scraper::{Html, Selector}; diff --git a/src/services/keywords/get_website.rs b/src/basic/keywords/get_website.rs similarity index 98% rename from src/services/keywords/get_website.rs rename to src/basic/keywords/get_website.rs index c58b6d6..0223808 100644 --- a/src/services/keywords/get_website.rs +++ b/src/basic/keywords/get_website.rs @@ -1,4 +1,4 @@ -use crate::services::{state::AppState, web_automation::BrowserPool}; +use crate::{state::AppState, web_automation::BrowserPool}; use log::info; use rhai::{Dynamic, Engine}; use std::error::Error; diff --git a/src/services/keywords/last.rs b/src/basic/keywords/last.rs similarity index 100% rename from src/services/keywords/last.rs rename to src/basic/keywords/last.rs diff --git a/src/services/keywords/llm_keyword.rs b/src/basic/keywords/llm_keyword.rs similarity index 94% rename from src/services/keywords/llm_keyword.rs rename to src/basic/keywords/llm_keyword.rs index 0454401..0e38057 100644 --- a/src/services/keywords/llm_keyword.rs +++ b/src/basic/keywords/llm_keyword.rs @@ -1,6 +1,6 @@ use log::info; -use crate::services::{state::AppState, utils::call_llm}; +use crate::{state::AppState, utils::call_llm}; use rhai::{Dynamic, Engine}; pub fn llm_keyword(state: &AppState, engine: &mut Engine) { diff --git a/src/services/keywords/mod.rs b/src/basic/keywords/mod.rs similarity index 100% rename from src/services/keywords/mod.rs rename to src/basic/keywords/mod.rs diff --git a/src/services/keywords/on.rs b/src/basic/keywords/on.rs similarity index 98% rename from src/services/keywords/on.rs rename to src/basic/keywords/on.rs index d2b8d7d..a048866 100644 --- a/src/services/keywords/on.rs +++ b/src/basic/keywords/on.rs @@ -5,7 +5,7 @@ use serde_json::{json, Value}; use sqlx::PgPool; use crate::models::automation_model::TriggerKind; -use crate::services::state::AppState; +use crate::state::AppState; pub fn on_keyword(state: &AppState, engine: &mut Engine) { let db = state.db_custom.clone(); diff --git a/src/services/keywords/print.rs b/src/basic/keywords/print.rs similarity index 92% rename from src/services/keywords/print.rs rename to src/basic/keywords/print.rs index befc808..30fd4ea 100644 --- a/src/services/keywords/print.rs +++ b/src/basic/keywords/print.rs @@ -2,7 +2,7 @@ use log::info; use rhai::Dynamic; use rhai::Engine; -use crate::services::state::AppState; +use crate::state::AppState; pub fn print_keyword(_state: &AppState, engine: &mut Engine) { // PRINT command diff --git a/src/services/keywords/prompt.md b/src/basic/keywords/prompt.md similarity index 100% rename from src/services/keywords/prompt.md rename to src/basic/keywords/prompt.md diff --git a/src/services/keywords/set.rs b/src/basic/keywords/set.rs similarity index 98% rename from src/services/keywords/set.rs rename to src/basic/keywords/set.rs index f31594d..ca5f301 100644 --- a/src/services/keywords/set.rs +++ b/src/basic/keywords/set.rs @@ -5,8 +5,8 @@ use serde_json::{json, Value}; use sqlx::PgPool; use std::error::Error; -use crate::services::state::AppState; -use crate::services::utils; +use crate::state::AppState; +use crate::utils; pub fn set_keyword(state: &AppState, engine: &mut Engine) { let db = state.db_custom.clone(); diff --git a/src/services/keywords/set_schedule.rs b/src/basic/keywords/set_schedule.rs similarity index 98% rename from src/services/keywords/set_schedule.rs rename to src/basic/keywords/set_schedule.rs index 98cb4ec..a83db27 100644 --- a/src/services/keywords/set_schedule.rs +++ b/src/basic/keywords/set_schedule.rs @@ -5,7 +5,7 @@ use serde_json::{json, Value}; use sqlx::PgPool; use crate::models::automation_model::TriggerKind; -use crate::services::state::AppState; +use crate::state::AppState; pub fn set_schedule_keyword(state: &AppState, engine: &mut Engine) { let db = state.db_custom.clone(); diff --git a/src/services/keywords/wait.rs b/src/basic/keywords/wait.rs similarity index 97% rename from src/services/keywords/wait.rs rename to src/basic/keywords/wait.rs index 11f9426..ad6d3a0 100644 --- a/src/services/keywords/wait.rs +++ b/src/basic/keywords/wait.rs @@ -1,4 +1,4 @@ -use crate::services::state::AppState; +use crate::state::AppState; use log::info; use rhai::{Dynamic, Engine}; use std::thread; diff --git a/src/services/script/script.rs b/src/basic/mod.rs similarity index 84% rename from src/services/script/script.rs rename to src/basic/mod.rs index a38a879..cb0d388 100644 --- a/src/services/script/script.rs +++ b/src/basic/mod.rs @@ -1,19 +1,21 @@ -use crate::services::keywords::create_draft::create_draft_keyword; -use crate::services::keywords::create_site::create_site_keyword; -use crate::services::keywords::find::find_keyword; -use crate::services::keywords::first::first_keyword; -use crate::services::keywords::last::last_keyword; -use crate::services::keywords::format::format_keyword; -use crate::services::keywords::for_next::for_keyword; -use crate::services::keywords::get::get_keyword; -use crate::services::keywords::get_website::get_website_keyword; -use crate::services::keywords::llm_keyword::llm_keyword; -use crate::services::keywords::on::on_keyword; -use crate::services::keywords::print::print_keyword; -use crate::services::keywords::set::set_keyword; -use crate::services::keywords::set_schedule::set_schedule_keyword; -use crate::services::keywords::wait::wait_keyword; -use crate::services::state::AppState; +mod keywords; + +use self::keywords::create_draft::create_draft_keyword; +use self::keywords::create_site::create_site_keyword; +use self::keywords::find::find_keyword; +use self::keywords::first::first_keyword; +use self::keywords::for_next::for_keyword; +use self::keywords::format::format_keyword; +use self::keywords::get::get_keyword; +use self::keywords::get_website::get_website_keyword; +use self::keywords::last::last_keyword; +use self::keywords::llm_keyword::llm_keyword; +use self::keywords::on::on_keyword; +use self::keywords::print::print_keyword; +use self::keywords::set::set_keyword; +use self::keywords::set_schedule::set_schedule_keyword; +use self::keywords::wait::wait_keyword; +use crate::shared::AppState; use log::info; use rhai::{Dynamic, Engine, EvalAltResult}; diff --git a/src/services/bot/mod.rs b/src/bot/mod.rs similarity index 100% rename from src/services/bot/mod.rs rename to src/bot/mod.rs diff --git a/src/services/channels/mod.rs b/src/channels/mod.rs similarity index 98% rename from src/services/channels/mod.rs rename to src/channels/mod.rs index a4cd302..278514a 100644 --- a/src/services/channels/mod.rs +++ b/src/channels/mod.rs @@ -1,3 +1,5 @@ +pub mod channels; + use async_trait::async_trait; use chrono::Utc; use livekit::{DataPacketKind, Room, RoomOptions}; @@ -6,7 +8,7 @@ use std::collections::HashMap; use std::sync::Arc; use tokio::sync::{mpsc, Mutex}; -use crate::services::shared::{BotResponse, UserMessage}; +use crate::shared::{BotResponse, UserMessage}; #[async_trait] pub trait ChannelAdapter: Send + Sync { diff --git a/src/services/chart/mod.rs b/src/chart/mod.rs similarity index 100% rename from src/services/chart/mod.rs rename to src/chart/mod.rs diff --git a/src/services/config/mod.rs b/src/config/mod.rs similarity index 100% rename from src/services/config/mod.rs rename to src/config/mod.rs diff --git a/src/services/context/mod.rs b/src/context/mod.rs similarity index 100% rename from src/services/context/mod.rs rename to src/context/mod.rs diff --git a/src/services/email/mod.rs b/src/email/mod.rs similarity index 99% rename from src/services/email/mod.rs rename to src/email/mod.rs index af0d556..0c8041f 100644 --- a/src/services/email/mod.rs +++ b/src/email/mod.rs @@ -1,4 +1,4 @@ -use crate::services::{config::EmailConfig, state::AppState}; +use crate::{config::EmailConfig, state::AppState}; use log::info; use actix_web::error::ErrorInternalServerError; diff --git a/src/services/file/mod.rs b/src/file/mod.rs similarity index 90% rename from src/services/file/mod.rs rename to src/file/mod.rs index 22d9235..f3aeda8 100644 --- a/src/services/file/mod.rs +++ b/src/file/mod.rs @@ -1,14 +1,12 @@ - - -use actix_web::{ web}; +use actix_web::web; use actix_multipart::Multipart; use actix_web::{post, HttpResponse}; use minio::s3::builders::ObjectContent; +use minio::s3::types::ToStream; use minio::s3::Client; use std::io::Write; use tempfile::NamedTempFile; -use minio::s3::types::ToStream; use tokio_stream::StreamExt; use minio::s3::client::{Client as MinioClient, ClientBuilder as MinioClientBuilder}; @@ -16,18 +14,18 @@ use minio::s3::creds::StaticProvider; use minio::s3::http::BaseUrl; use std::str::FromStr; -use crate::services::config::{AppConfig}; -use crate::services::state::AppState; +use crate::config::AppConfig; +use crate::state::AppState; pub async fn init_minio(config: &AppConfig) -> Result { - let scheme = if config.minio.use_ssl { "https" } else { "http" }; + let scheme = if config.minio.use_ssl { + "https" + } else { + "http" + }; let base_url = format!("{}://{}", scheme, config.minio.server); let base_url = BaseUrl::from_str(&base_url)?; - let credentials = StaticProvider::new( - &config.minio.access_key, - &config.minio.secret_key, - None, - ); + let credentials = StaticProvider::new(&config.minio.access_key, &config.minio.secret_key, None); let minio_client = MinioClientBuilder::new(base_url) .provider(Some(credentials)) @@ -104,8 +102,6 @@ pub async fn upload_file( ))) } - - #[post("/files/list/{folder_path}")] pub async fn list_file( folder_path: web::Path, @@ -124,7 +120,7 @@ pub async fn list_file( .await; let mut file_list = Vec::new(); - + // Use StreamExt::next() to iterate through the stream while let Some(items) = objects_stream.next().await { match items { @@ -132,11 +128,12 @@ pub async fn list_file( for item in result.contents { file_list.push(item.name); } - }, + } Err(e) => { - return Err(actix_web::error::ErrorInternalServerError( - format!("Failed to list files in MinIO: {}", e) - )); + return Err(actix_web::error::ErrorInternalServerError(format!( + "Failed to list files in MinIO: {}", + e + ))); } } } diff --git a/src/services/llm/llm.rs b/src/llm/llm.rs similarity index 98% rename from src/services/llm/llm.rs rename to src/llm/llm.rs index 0166d32..63fa096 100644 --- a/src/services/llm/llm.rs +++ b/src/llm/llm.rs @@ -18,7 +18,7 @@ use langchain_rust::{ template_fstring, }; -use crate::services::{state::AppState, utils::azure_from_config}; +use crate::{state::AppState, utils::azure_from_config}; #[derive(serde::Deserialize)] struct ChatRequest { diff --git a/src/services/llm/llm_generic.rs b/src/llm/llm_generic.rs similarity index 100% rename from src/services/llm/llm_generic.rs rename to src/llm/llm_generic.rs diff --git a/src/services/llm/llm_local.rs b/src/llm/llm_local.rs similarity index 100% rename from src/services/llm/llm_local.rs rename to src/llm/llm_local.rs diff --git a/src/services/llm/llm_provider.rs b/src/llm/llm_provider.rs similarity index 100% rename from src/services/llm/llm_provider.rs rename to src/llm/llm_provider.rs diff --git a/src/services/llm/mod.rs b/src/llm/mod.rs similarity index 98% rename from src/services/llm/mod.rs rename to src/llm/mod.rs index 298a83a..194ab19 100644 --- a/src/services/llm/mod.rs +++ b/src/llm/mod.rs @@ -1,3 +1,7 @@ +pub mod llm_generic; +pub mod llm_local; +pub mod llm_provider; + use async_trait::async_trait; use futures::StreamExt; use langchain_rust::{ @@ -9,7 +13,7 @@ use serde_json::Value; use std::sync::Arc; use tokio::sync::mpsc; -use crate::services::tools::ToolManager; +use crate::tools::ToolManager; #[async_trait] pub trait LLMProvider: Send + Sync { diff --git a/src/main.rs b/src/main.rs index 70db9fa..9d4883b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,22 @@ +mod auth; +mod automation; +mod basic; +mod bot; +mod channels; +mod chart; +mod config; +mod context; +mod email; +mod file; +mod llm; +mod models; +mod org; +mod session; +mod shared; +mod tools; +mod web_automation; +mod whatsapp; + use actix_web::middleware::Logger; use log::info; use qdrant_client::Qdrant; @@ -7,27 +26,24 @@ use actix_web::{web, App, HttpServer}; use dotenv::dotenv; use sqlx::PgPool; -use crate::services::auth::AuthService; -use crate::services::automation::AutomationService; -use crate::services::channels::ChannelAdapter; -use crate::services::config::AppConfig; -use crate::services::email::{ +use crate::bot::BotOrchestrator; +use crate::channels::ChannelAdapter; +use crate::config::AppConfig; +use crate::email::{ get_emails, get_latest_email_from, list_emails, save_click, save_draft, send_email, }; -use crate::services::file::{list_file, upload_file}; -use crate::services::llm_generic::generic_chat_completions; -use crate::services::llm_local::{ +use crate::file::{list_file, upload_file}; +use crate::llm::llm_generic::generic_chat_completions; +use crate::llm::llm_local::{ chat_completions_local, embeddings_local, ensure_llama_servers_running, }; -use crate::services::orchestrator::BotOrchestrator; -use crate::services::session::SessionManager; -use crate::services::state::AppState; -use crate::services::tools::{RedisToolExecutor, ToolManager}; -use crate::services::web_automation::{initialize_browser_pool, BrowserPool}; -use crate::services::whatsapp::WhatsAppAdapter; - -mod models; -mod services; +use crate::session::SessionManager; +use crate::state::AppState; +use crate::tools::{RedisToolExecutor, ToolManager}; +use crate::web_automation::{initialize_browser_pool, BrowserPool}; +use crate::whatsapp::WhatsAppAdapter; +use crate::AuthService; +use crate::BotOrchestrator; #[tokio::main(flavor = "multi_thread")] async fn main() -> std::io::Result<()> { @@ -81,28 +97,27 @@ async fn main() -> std::io::Result<()> { let session_manager = SessionManager::new(db.clone(), redis_conn.clone()); let auth_service = AuthService::new(db.clone(), redis_conn.clone()); - let llm_provider: Arc = - match std::env::var("LLM_PROVIDER") - .unwrap_or("mock".to_string()) - .as_str() - { - "openai" => Arc::new(crate::services::llm_local::OpenAIClient::new( - std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY required"), - )), - "anthropic" => Arc::new(crate::services::llm_local::AnthropicClient::new( - std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY required"), - )), - _ => Arc::new(crate::services::llm_local::MockLLMProvider::new()), - }; + let llm_provider: Arc = match std::env::var("LLM_PROVIDER") + .unwrap_or("mock".to_string()) + .as_str() + { + "openai" => Arc::new(crate::llm_local::OpenAIClient::new( + std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY required"), + )), + "anthropic" => Arc::new(crate::llm_local::AnthropicClient::new( + std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY required"), + )), + _ => Arc::new(crate::llm_local::MockLLMProvider::new()), + }; - let web_adapter = Arc::new(crate::services::channels::WebChannelAdapter::new()); - let voice_adapter = Arc::new(crate::services::channels::VoiceAdapter::new( + let web_adapter = Arc::new(crate::channels::WebChannelAdapter::new()); + let voice_adapter = Arc::new(crate::channels::VoiceAdapter::new( std::env::var("LIVEKIT_URL").unwrap_or("ws://localhost:7880".to_string()), std::env::var("LIVEKIT_API_KEY").unwrap_or("dev".to_string()), std::env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string()), )); - let whatsapp_adapter = Arc::new(crate::services::whatsapp::WhatsAppAdapter::new( + let whatsapp_adapter = Arc::new(crate::whatsapp::WhatsAppAdapter::new( std::env::var("META_ACCESS_TOKEN").unwrap_or("".to_string()), std::env::var("META_PHONE_NUMBER_ID").unwrap_or("".to_string()), std::env::var("META_WEBHOOK_VERIFY_TOKEN").unwrap_or("".to_string()), @@ -111,7 +126,7 @@ async fn main() -> std::io::Result<()> { let tool_executor = Arc::new( RedisToolExecutor::new( redis_url.as_str(), - web_adapter.clone() as Arc, + web_adapter.clone() as Arc, db.clone(), redis_conn.clone(), ) @@ -216,8 +231,8 @@ async fn main() -> std::io::Result<()> { .service(get_emails) .service(list_emails) .service(send_email) - .service(crate::services::orchestrator::chat_stream) - .service(crate::services::orchestrator::chat) + .service(crate::orchestrator::chat_stream) + .service(crate::orchestrator::chat) .service(chat_completions_local) .service(save_draft) .service(generic_chat_completions) @@ -232,6 +247,11 @@ async fn main() -> std::io::Result<()> { .service(services::orchestrator::get_sessions) .service(services::orchestrator::get_session_history) .service(services::orchestrator::index) + .service(create_organization) + .service(get_organization) + .service(list_organizations) + .service(update_organization) + .service(delete_organization) }) .bind((config.server.host.clone(), config.server.port))? .run() diff --git a/src/models.rs b/src/models.rs deleted file mode 100644 index 17de93b..0000000 --- a/src/models.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod automation_model; diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1 @@ + diff --git a/src/models/org_model.md b/src/models/org_model.rs similarity index 100% rename from src/models/org_model.md rename to src/models/org_model.rs diff --git a/src/services/org/org.md b/src/org/mod.rs similarity index 95% rename from src/services/org/org.md rename to src/org/mod.rs index 50245f9..df3565d 100644 --- a/src/services/org/org.md +++ b/src/org/mod.rs @@ -1,10 +1,3 @@ - .service(create_organization) - .service(get_organization) - .service(list_organizations) - .service(update_organization) - .service(delete_organization) - - use actix_web::{web, HttpResponse, Result}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/src/services.rs b/src/services.rs deleted file mode 100644 index 586a31d..0000000 --- a/src/services.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub mod auth; -pub mod automation; -pub mod bot; -pub mod channels; -pub mod chart; -pub mod config; -pub mod context; -pub mod email; -pub mod file; -pub mod keywords; -pub mod llm; -pub mod session; -pub mod shared; -pub mod tools; -pub mod web_automation; -pub mod whatsapp; diff --git a/src/services/bot/bot.md b/src/services/bot/bot.md deleted file mode 100644 index 2f17c2b..0000000 --- a/src/services/bot/bot.md +++ /dev/null @@ -1,233 +0,0 @@ - // .service(create_bot) - // .service(get_bot) - // .service(list_bots) - // .service(update_bot) - // .service(delete_bot) - // .service(update_bot_status) - // .service(execute_bot_command) - - - -use crate::services::{config::BotConfig, state::AppState}; -use actix_web::{ - delete, get, post, put, - web::{self, Data, Json, Path}, - HttpResponse, Responder, Result, -}; -use chrono::Utc; -use serde::{Deserialize, Serialize}; -use sqlx::{postgres::PgQueryResult, FromRow, PgPool}; -use uuid::Uuid; - -// 1. Core Data Structures - -// 2. Request/Response DTOs -#[derive(Debug, Serialize, Deserialize)] -pub struct CreateBotRequest { - pub name: String, - pub initial_config: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UpdateBotRequest { - pub name: Option, - pub status: Option, - pub config: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct BotResponse { - pub bot_id: Uuid, - pub name: String, - pub status: BotStatus, - pub created_at: chrono::DateTime, - pub updated_at: chrono::DateTime, -} - -// 3. Helper Functions -impl From for BotResponse { - fn from(bot: Bot) -> Self { - BotResponse { - bot_id: bot.bot_id, - name: bot.name, - status: bot.status, - created_at: bot.created_at, - updated_at: bot.updated_at, - } - } -} - -async fn find_bot(bot_id: Uuid, pool: &PgPool) -> Result { - sqlx::query_as::<_, Bot>("SELECT * FROM bots WHERE bot_id = $1") - .bind(bot_id) - .fetch_one(pool) - .await -} - -// 4. API Endpoints -#[post("/bots/create")] -pub async fn create_bot( - payload: Json, - state: Data, -) -> Result { - let new_bot = sqlx::query_as::<_, Bot>( - r#" - INSERT INTO bots (name, status, config) - VALUES ($1, 'active', $2) - RETURNING * - "#, - ) - .bind(&payload.name) - .bind(&payload.initial_config) - .fetch_one(&state.db) - .await - .map_err(|e| { - log::error!("Failed to create bot: {}", e); - actix_web::error::ErrorInternalServerError("Failed to create bot") - })?; - - Ok(HttpResponse::Created().json(BotResponse::from(new_bot))) -} - -#[get("/bots/{bot_id}")] -pub async fn get_bot( - path: Path, - state: Data, -) -> Result { - let bot_id = path.into_inner(); - let bot = find_bot(bot_id, &state.db).await.map_err(|e| match e { - sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"), - _ => { - log::error!("Failed to fetch bot: {}", e); - actix_web::error::ErrorInternalServerError("Failed to fetch bot") - } - })?; - - Ok(HttpResponse::Ok().json(BotResponse::from(bot))) -} - -#[get("/bots")] -pub async fn list_bots(state: Data) -> Result { - let bots = sqlx::query_as::<_, Bot>("SELECT * FROM bots ORDER BY created_at DESC") - .fetch_all(&state.db) - .await - .map_err(|e| { - log::error!("Failed to list bots: {}", e); - actix_web::error::ErrorInternalServerError("Failed to list bots") - })?; - - let responses: Vec = bots.into_iter().map(BotResponse::from).collect(); - Ok(HttpResponse::Ok().json(responses)) -} - -#[put("/bots/{bot_id}")] -pub async fn update_bot( - path: Path, - payload: Json, - state: Data, -) -> Result { - let bot_id = path.into_inner(); - - let updated_bot = sqlx::query_as::<_, Bot>( - r#" - UPDATE bots - SET - name = COALESCE($1, name), - status = COALESCE($2, status), - config = COALESCE($3, config), - updated_at = NOW() - WHERE bot_id = $4 - RETURNING * - "#, - ) - .bind(&payload.name) - .bind(&payload.status) - .bind(&payload.config) - .bind(bot_id) - .fetch_one(&state.db) - .await - .map_err(|e| match e { - sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"), - _ => { - log::error!("Failed to update bot: {}", e); - actix_web::error::ErrorInternalServerError("Failed to update bot") - } - })?; - - Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot))) -} - -#[delete("/bots/{bot_id}")] -pub async fn delete_bot( - path: Path, - state: Data, -) -> Result { - let bot_id = path.into_inner(); - - let result = sqlx::query("DELETE FROM bots WHERE bot_id = $1") - .bind(bot_id) - .execute(&state.db) - .await - .map_err(|e| { - log::error!("Failed to delete bot: {}", e); - actix_web::error::ErrorInternalServerError("Failed to delete bot") - })?; - - if result.rows_affected() == 0 { - return Err(actix_web::error::ErrorNotFound("Bot not found")); - } - - Ok(HttpResponse::NoContent().finish()) -} - -#[put("/bots/{bot_id}/status")] -pub async fn update_bot_status( - path: Path, - new_status: Json, - state: Data, -) -> Result { - let bot_id = path.into_inner(); - - let updated_bot = sqlx::query_as::<_, Bot>( - "UPDATE bots SET status = $1, updated_at = NOW() WHERE bot_id = $2 RETURNING *", - ) - .bind(new_status.into_inner()) - .bind(bot_id) - .fetch_one(&state.db) - .await - .map_err(|e| match e { - sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"), - _ => { - log::error!("Failed to update bot status: {}", e); - actix_web::error::ErrorInternalServerError("Failed to update bot status") - } - })?; - - Ok(HttpResponse::Ok().json(BotResponse::from(updated_bot))) -} - -#[post("/bots/{bot_id}/execute")] -pub async fn execute_bot_command( - path: Path, - command: Json, - state: Data, -) -> Result { - let bot_id = path.into_inner(); - - // Verify bot exists - let _ = find_bot(bot_id, &state.db).await.map_err(|e| match e { - sqlx::Error::RowNotFound => actix_web::error::ErrorNotFound("Bot not found"), - _ => { - log::error!("Failed to fetch bot: {}", e); - actix_web::error::ErrorInternalServerError("Failed to fetch bot") - } - })?; - - // Here you would implement your bot execution logic - // For now, we'll just echo back the command - Ok(HttpResponse::Ok().json(json!({ - "bot_id": bot_id, - "command": command, - "result": "Command executed successfully (simulated)" - }))) -} diff --git a/src/services/email/llm-email.md b/src/services/email/llm-email.md deleted file mode 100644 index 5bce081..0000000 --- a/src/services/email/llm-email.md +++ /dev/null @@ -1,215 +0,0 @@ -use actix_web::{web, HttpResponse, Result}; -use serde::{Deserialize, Serialize}; - -#[derive(serde::Deserialize)] -struct ChatRequest { - input: String, - context: Option, -} - -#[derive(serde::Deserialize)] -struct AppContext { - view_type: Option, - email_context: Option, -} - -#[derive(serde::Deserialize)] -struct EmailContext { - id: String, - subject: String, - labels: Vec, - from: Option, - to: Option>, - body: Option, -} - -#[derive(serde::Serialize)] -struct ChatResponse { - response: String, - tool_calls: Option>, -} - -#[derive(serde::Serialize)] -struct ToolCall { - tool_name: String, - parameters: serde_json::Value, -} - -#[derive(serde::Serialize)] -struct ToolDefinition { - name: String, - description: String, - parameters: serde_json::Value, -} - -#[actix_web::post("/chat")] -pub async fn chat( - web::Json(request): web::Json, - state: web::Data, -) -> Result { - let azure_config = from_config(&state.config.clone().unwrap().ai); - let open_ai = OpenAI::new(azure_config); - - // Define available tools based on context - let tools = get_available_tools(&request.context); - - // Build the prompt with context and available tools - let system_prompt = build_system_prompt(&request.context, &tools); - let user_message = format!("{}\n\nUser input: {}", system_prompt, request.input); - - let response = match open_ai.invoke(&user_message).await { - Ok(res) => res, - Err(err) => { - error!("Error invoking API: {}", err); - return Err(actix_web::error::ErrorInternalServerError( - "Failed to invoke OpenAI API", - )); - } - }; - - // Parse the response for tool calls - let tool_calls = parse_tool_calls(&response); - - let chat_response = ChatResponse { - response, - tool_calls, - }; - - Ok(HttpResponse::Ok().json(chat_response)) -} - -fn get_available_tools(context: &Option) -> Vec { - let mut tools = Vec::new(); - - if let Some(ctx) = context { - if let Some(view_type) = &ctx.view_type { - match view_type.as_str() { - "email" => { - tools.push(ToolDefinition { - name: "replyEmail".to_string(), - description: "Reply to the current email with generated content".to_string(), - parameters: serde_json::json!({ - "type": "object", - "properties": { - "content": { - "type": "string", - "description": "The reply content to send" - } - }, - "required": ["content"] - }), - }); - - tools.push(ToolDefinition { - name: "forwardEmail".to_string(), - description: "Forward the current email to specified recipients".to_string(), - parameters: serde_json::json!({ - "type": "object", - "properties": { - "recipients": { - "type": "array", - "items": {"type": "string"}, - "description": "Email addresses to forward to" - }, - "content": { - "type": "string", - "description": "Additional message to include" - } - }, - "required": ["recipients"] - }), - }); - } - _ => {} - } - } - } - - tools -} - -fn build_system_prompt(context: &Option, tools: &[ToolDefinition]) -> String { - let mut prompt = String::new(); - - if let Some(ctx) = context { - if let Some(view_type) = &ctx.view_type { - match view_type.as_str() { - "email" => { - if let Some(email_ctx) = &ctx.email_context { - prompt.push_str(&format!( - "You are an email assistant. Current email context:\n\ - Subject: {}\n\ - ID: {}\n\ - Labels: {:?}\n\n", - email_ctx.subject, email_ctx.id, email_ctx.labels - )); - - if let Some(from) = &email_ctx.from { - prompt.push_str(&format!("From: {}\n", from)); - } - - if let Some(body) = &email_ctx.body { - prompt.push_str(&format!("Body: {}\n", body)); - } - } - } - _ => {} - } - } - } - - if !tools.is_empty() { - prompt.push_str("\nAvailable tools:\n"); - for tool in tools { - prompt.push_str(&format!( - "- {}: {}\n Parameters: {}\n\n", - tool.name, tool.description, tool.parameters - )); - } - - prompt.push_str( - "If you need to use a tool, respond with:\n\ - TOOL_CALL: tool_name\n\ - PARAMETERS: {json_parameters}\n\ - RESPONSE: your_response_text\n\n\ - Otherwise, just provide a normal response.\n" - ); - } - - prompt -} - -fn parse_tool_calls(response: &str) -> Option> { - if !response.contains("TOOL_CALL:") { - return None; - } - - let mut tool_calls = Vec::new(); - let lines: Vec<&str> = response.lines().collect(); - - let mut i = 0; - while i < lines.len() { - if lines[i].starts_with("TOOL_CALL:") { - let tool_name = lines[i].replace("TOOL_CALL:", "").trim().to_string(); - - // Look for parameters in the next line - if i + 1 < lines.len() && lines[i + 1].starts_with("PARAMETERS:") { - let params_str = lines[i + 1].replace("PARAMETERS:", "").trim(); - - if let Ok(parameters) = serde_json::from_str::(params_str) { - tool_calls.push(ToolCall { - tool_name, - parameters, - }); - } - } - } - i += 1; - } - - if tool_calls.is_empty() { - None - } else { - Some(tool_calls) - } -} diff --git a/src/services/session/mod.rs b/src/session/mod.rs similarity index 99% rename from src/services/session/mod.rs rename to src/session/mod.rs index 5c211af..06f4362 100644 --- a/src/services/session/mod.rs +++ b/src/session/mod.rs @@ -1,4 +1,4 @@ -use crate::services::shared::shared::UserSession; +use crate::shared::shared::UserSession; use sqlx::Row; use redis::{aio::Connection as ConnectionManager, AsyncCommands}; diff --git a/src/services/shared/mod.rs b/src/shared/mod.rs similarity index 100% rename from src/services/shared/mod.rs rename to src/shared/mod.rs diff --git a/src/services/shared/state.rs b/src/shared/state.rs similarity index 88% rename from src/services/shared/state.rs rename to src/shared/state.rs index fe45b56..aad299d 100644 --- a/src/services/shared/state.rs +++ b/src/shared/state.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use minio::s3::Client; -use crate::services::{config::AppConfig, web_automation::BrowserPool}; +use crate::{config::AppConfig, web_automation::BrowserPool}; #[derive(Clone)] pub struct AppState { diff --git a/src/services/shared/utils.rs b/src/shared/utils.rs similarity index 99% rename from src/services/shared/utils.rs rename to src/shared/utils.rs index a7b3caa..d42f91c 100644 --- a/src/services/shared/utils.rs +++ b/src/shared/utils.rs @@ -1,4 +1,4 @@ -use crate::services::config::AIConfig; +use crate::config::AIConfig; use langchain_rust::llm::OpenAI; use langchain_rust::{language_models::llm::LLM, llm::AzureConfig}; use log::error; diff --git a/src/services/tools/mod.rs b/src/tools/mod.rs similarity index 100% rename from src/services/tools/mod.rs rename to src/tools/mod.rs diff --git a/src/services/web_automation/mod.rs b/src/web_automation/mod.rs similarity index 99% rename from src/services/web_automation/mod.rs rename to src/web_automation/mod.rs index 35bb8a2..1cb85a3 100644 --- a/src/services/web_automation/mod.rs +++ b/src/web_automation/mod.rs @@ -2,7 +2,7 @@ // sudo dpkg -i google-chrome-stable_current_amd64.deb use log::info; -use crate::services::utils; +use crate::utils; use std::env; use std::error::Error; diff --git a/src/services/whatsapp/mod.rs b/src/whatsapp/mod.rs similarity index 100% rename from src/services/whatsapp/mod.rs rename to src/whatsapp/mod.rs