2025-12-03 16:05:30 -03:00
|
|
|
pub mod model_routing_config;
|
|
|
|
|
pub mod sse_config;
|
|
|
|
|
pub mod user_memory_config;
|
|
|
|
|
|
|
|
|
|
pub use model_routing_config::{ModelRoutingConfig, RoutingStrategy, TaskType};
|
|
|
|
|
pub use sse_config::SseConfig;
|
|
|
|
|
pub use user_memory_config::UserMemoryConfig;
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
use crate::shared::utils::DbPool;
|
|
|
|
|
use diesel::prelude::*;
|
|
|
|
|
use diesel::r2d2::{ConnectionManager, PooledConnection};
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
pub type Config = AppConfig;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct AppConfig {
|
|
|
|
|
pub drive: DriveConfig,
|
|
|
|
|
pub server: ServerConfig,
|
|
|
|
|
pub email: EmailConfig,
|
|
|
|
|
pub site_path: String,
|
2025-11-30 23:48:08 -03:00
|
|
|
pub data_dir: String,
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct DriveConfig {
|
|
|
|
|
pub server: String,
|
|
|
|
|
pub access_key: String,
|
|
|
|
|
pub secret_key: String,
|
|
|
|
|
}
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct ServerConfig {
|
|
|
|
|
pub host: String,
|
|
|
|
|
pub port: u16,
|
2025-11-30 23:48:08 -03:00
|
|
|
pub base_url: String,
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct EmailConfig {
|
|
|
|
|
pub server: String,
|
|
|
|
|
pub port: u16,
|
|
|
|
|
pub username: String,
|
|
|
|
|
pub password: String,
|
|
|
|
|
pub from: String,
|
|
|
|
|
pub smtp_server: String,
|
|
|
|
|
pub smtp_port: u16,
|
|
|
|
|
}
|
2025-12-03 16:05:30 -03:00
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
|
|
|
pub struct CustomDatabaseConfig {
|
|
|
|
|
pub server: String,
|
|
|
|
|
pub port: u16,
|
|
|
|
|
pub database: String,
|
|
|
|
|
pub username: String,
|
|
|
|
|
pub password: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CustomDatabaseConfig {
|
|
|
|
|
pub fn from_bot_config(
|
|
|
|
|
pool: &DbPool,
|
|
|
|
|
target_bot_id: &Uuid,
|
|
|
|
|
) -> Result<Option<Self>, diesel::result::Error> {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
|
|
|
|
|
|
|
|
|
let mut conn = pool.get().map_err(|e| {
|
|
|
|
|
diesel::result::Error::DatabaseError(
|
|
|
|
|
diesel::result::DatabaseErrorKind::UnableToSendCommand,
|
|
|
|
|
Box::new(e.to_string()),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let database: Option<String> = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq("custom-database"))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.filter(|s| !s.is_empty());
|
|
|
|
|
|
2025-12-26 08:59:25 -03:00
|
|
|
let Some(database) = database else {
|
|
|
|
|
return Ok(None);
|
2025-12-03 16:05:30 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let server: String = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq("custom-server"))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.filter(|s| !s.is_empty())
|
|
|
|
|
.unwrap_or_else(|| "localhost".to_string());
|
|
|
|
|
|
|
|
|
|
let port: u16 = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq("custom-port"))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|v| v.parse().ok())
|
|
|
|
|
.unwrap_or(3306);
|
|
|
|
|
|
|
|
|
|
let username: String = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq("custom-username"))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let password: String = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq("custom-password"))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
2025-12-26 08:59:25 -03:00
|
|
|
Ok(Some(Self {
|
2025-12-03 16:05:30 -03:00
|
|
|
server,
|
|
|
|
|
port,
|
|
|
|
|
database,
|
|
|
|
|
username,
|
|
|
|
|
password,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn connection_string(&self) -> String {
|
|
|
|
|
format!(
|
|
|
|
|
"mysql://{}:{}@{}:{}/{}",
|
|
|
|
|
self.username, self.password, self.server, self.port, self.database
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_valid(&self) -> bool {
|
|
|
|
|
!self.database.is_empty() && !self.server.is_empty()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EmailConfig {
|
|
|
|
|
pub fn from_bot_config(
|
|
|
|
|
pool: &DbPool,
|
|
|
|
|
target_bot_id: &Uuid,
|
|
|
|
|
) -> Result<Self, diesel::result::Error> {
|
|
|
|
|
let mut conn = pool.get().map_err(|e| {
|
|
|
|
|
diesel::result::Error::DatabaseError(
|
|
|
|
|
diesel::result::DatabaseErrorKind::UnableToSendCommand,
|
|
|
|
|
Box::new(e.to_string()),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
fn get_config_value(
|
|
|
|
|
conn: &mut diesel::r2d2::PooledConnection<
|
|
|
|
|
diesel::r2d2::ConnectionManager<diesel::PgConnection>,
|
|
|
|
|
>,
|
|
|
|
|
target_bot_id: &Uuid,
|
|
|
|
|
key: &str,
|
|
|
|
|
default: &str,
|
|
|
|
|
) -> String {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
|
|
|
|
bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq(key))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(conn)
|
|
|
|
|
.unwrap_or_else(|_| default.to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_port_value(
|
|
|
|
|
conn: &mut diesel::r2d2::PooledConnection<
|
|
|
|
|
diesel::r2d2::ConnectionManager<diesel::PgConnection>,
|
|
|
|
|
>,
|
|
|
|
|
target_bot_id: &Uuid,
|
|
|
|
|
key: &str,
|
|
|
|
|
default: u16,
|
|
|
|
|
) -> u16 {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
|
|
|
|
bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq(key))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(conn)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|v| v.parse().ok())
|
|
|
|
|
.unwrap_or(default)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_smtp_server = get_config_value(&mut conn, target_bot_id, "email-server", "");
|
2025-12-26 08:59:25 -03:00
|
|
|
let smtp_server = if new_smtp_server.is_empty() {
|
2025-12-03 16:05:30 -03:00
|
|
|
get_config_value(
|
|
|
|
|
&mut conn,
|
|
|
|
|
target_bot_id,
|
|
|
|
|
"EMAIL_SMTP_SERVER",
|
|
|
|
|
"smtp.gmail.com",
|
|
|
|
|
)
|
2025-12-26 08:59:25 -03:00
|
|
|
} else {
|
|
|
|
|
new_smtp_server
|
2025-12-03 16:05:30 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let new_smtp_port = get_port_value(&mut conn, target_bot_id, "email-port", 0);
|
|
|
|
|
let smtp_port = if new_smtp_port > 0 {
|
|
|
|
|
new_smtp_port
|
|
|
|
|
} else {
|
|
|
|
|
get_port_value(&mut conn, target_bot_id, "EMAIL_SMTP_PORT", 587)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let new_from = get_config_value(&mut conn, target_bot_id, "email-from", "");
|
2025-12-26 08:59:25 -03:00
|
|
|
let from = if new_from.is_empty() {
|
2025-12-03 16:05:30 -03:00
|
|
|
get_config_value(&mut conn, target_bot_id, "EMAIL_FROM", "")
|
2025-12-26 08:59:25 -03:00
|
|
|
} else {
|
|
|
|
|
new_from
|
2025-12-03 16:05:30 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let new_user = get_config_value(&mut conn, target_bot_id, "email-user", "");
|
2025-12-26 08:59:25 -03:00
|
|
|
let username = if new_user.is_empty() {
|
2025-12-03 16:05:30 -03:00
|
|
|
get_config_value(&mut conn, target_bot_id, "EMAIL_USERNAME", "")
|
2025-12-26 08:59:25 -03:00
|
|
|
} else {
|
|
|
|
|
new_user
|
2025-12-03 16:05:30 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let new_pass = get_config_value(&mut conn, target_bot_id, "email-pass", "");
|
2025-12-26 08:59:25 -03:00
|
|
|
let password = if new_pass.is_empty() {
|
2025-12-03 16:05:30 -03:00
|
|
|
get_config_value(&mut conn, target_bot_id, "EMAIL_PASSWORD", "")
|
2025-12-26 08:59:25 -03:00
|
|
|
} else {
|
|
|
|
|
new_pass
|
2025-12-03 16:05:30 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let server = get_config_value(
|
|
|
|
|
&mut conn,
|
|
|
|
|
target_bot_id,
|
|
|
|
|
"EMAIL_IMAP_SERVER",
|
|
|
|
|
"imap.gmail.com",
|
|
|
|
|
);
|
|
|
|
|
let port = get_port_value(&mut conn, target_bot_id, "EMAIL_IMAP_PORT", 993);
|
|
|
|
|
|
2025-12-26 08:59:25 -03:00
|
|
|
Ok(Self {
|
2025-12-03 16:05:30 -03:00
|
|
|
server,
|
|
|
|
|
port,
|
|
|
|
|
username,
|
|
|
|
|
password,
|
|
|
|
|
from,
|
|
|
|
|
smtp_server,
|
|
|
|
|
smtp_port,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
impl AppConfig {
|
|
|
|
|
pub fn from_database(pool: &DbPool) -> Result<Self, diesel::result::Error> {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
2025-12-08 00:19:29 -03:00
|
|
|
use diesel::prelude::*;
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
let mut conn = pool.get().map_err(|e| {
|
|
|
|
|
diesel::result::Error::DatabaseError(
|
|
|
|
|
diesel::result::DatabaseErrorKind::UnableToSendCommand,
|
|
|
|
|
Box::new(e.to_string()),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
2025-12-08 00:19:29 -03:00
|
|
|
|
|
|
|
|
let config_map: HashMap<String, String> = bot_configuration
|
|
|
|
|
.select((config_key, config_value))
|
|
|
|
|
.load::<(String, String)>(&mut conn)
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let get_str = |key: &str, default: &str| -> String {
|
2025-11-22 22:55:35 -03:00
|
|
|
config_map
|
|
|
|
|
.get(key)
|
2025-12-08 00:19:29 -03:00
|
|
|
.cloned()
|
|
|
|
|
.unwrap_or_else(|| default.to_string())
|
2025-11-22 22:55:35 -03:00
|
|
|
};
|
2025-12-08 00:19:29 -03:00
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
let get_u16 = |key: &str, default: u16| -> u16 {
|
|
|
|
|
config_map
|
|
|
|
|
.get(key)
|
2025-12-08 00:19:29 -03:00
|
|
|
.and_then(|v| v.parse().ok())
|
2025-11-22 22:55:35 -03:00
|
|
|
.unwrap_or(default)
|
|
|
|
|
};
|
|
|
|
|
let drive = DriveConfig {
|
2025-11-29 17:27:13 -03:00
|
|
|
server: crate::core::urls::InternalUrls::DRIVE.to_string(),
|
2025-12-23 18:40:58 -03:00
|
|
|
access_key: String::new(),
|
|
|
|
|
secret_key: String::new(),
|
2025-11-22 22:55:35 -03:00
|
|
|
};
|
|
|
|
|
let email = EmailConfig {
|
|
|
|
|
server: get_str("EMAIL_IMAP_SERVER", "imap.gmail.com"),
|
|
|
|
|
port: get_u16("EMAIL_IMAP_PORT", 993),
|
|
|
|
|
username: get_str("EMAIL_USERNAME", ""),
|
|
|
|
|
password: get_str("EMAIL_PASSWORD", ""),
|
|
|
|
|
from: get_str("EMAIL_FROM", ""),
|
|
|
|
|
smtp_server: get_str("EMAIL_SMTP_SERVER", "smtp.gmail.com"),
|
|
|
|
|
smtp_port: get_u16("EMAIL_SMTP_PORT", 587),
|
|
|
|
|
};
|
2025-12-26 08:59:25 -03:00
|
|
|
Ok(Self {
|
2025-11-22 22:55:35 -03:00
|
|
|
drive,
|
|
|
|
|
email,
|
|
|
|
|
server: ServerConfig {
|
2025-12-08 00:19:29 -03:00
|
|
|
host: get_str("server_host", "0.0.0.0"),
|
|
|
|
|
port: get_u16("server_port", 8080),
|
|
|
|
|
base_url: get_str("server_base_url", "http://localhost:8080"),
|
2025-11-22 22:55:35 -03:00
|
|
|
},
|
|
|
|
|
site_path: {
|
2025-12-26 08:59:25 -03:00
|
|
|
ConfigManager::new(pool.clone()).get_config(
|
|
|
|
|
&Uuid::nil(),
|
|
|
|
|
"SITES_ROOT",
|
|
|
|
|
Some("./botserver-stack/sites"),
|
|
|
|
|
)?
|
2025-11-22 22:55:35 -03:00
|
|
|
},
|
2025-11-30 23:48:08 -03:00
|
|
|
data_dir: get_str("DATA_DIR", "./botserver-stack/data"),
|
2025-11-22 22:55:35 -03:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
pub fn from_env() -> Result<Self, anyhow::Error> {
|
|
|
|
|
let minio = DriveConfig {
|
2025-11-29 17:27:13 -03:00
|
|
|
server: crate::core::urls::InternalUrls::DRIVE.to_string(),
|
2025-12-23 18:40:58 -03:00
|
|
|
access_key: String::new(),
|
|
|
|
|
secret_key: String::new(),
|
2025-11-22 22:55:35 -03:00
|
|
|
};
|
|
|
|
|
let email = EmailConfig {
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
server: "imap.gmail.com".to_string(),
|
|
|
|
|
port: 993,
|
|
|
|
|
username: String::new(),
|
|
|
|
|
password: String::new(),
|
|
|
|
|
from: String::new(),
|
|
|
|
|
smtp_server: "smtp.gmail.com".to_string(),
|
|
|
|
|
smtp_port: 587,
|
2025-11-22 22:55:35 -03:00
|
|
|
};
|
2025-12-26 08:59:25 -03:00
|
|
|
Ok(Self {
|
2025-11-22 22:55:35 -03:00
|
|
|
drive: minio,
|
|
|
|
|
email,
|
|
|
|
|
server: ServerConfig {
|
2025-12-11 08:43:28 -03:00
|
|
|
host: "0.0.0.0".to_string(),
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
port: 8080,
|
2025-11-30 23:48:08 -03:00
|
|
|
base_url: "http://localhost:8080".to_string(),
|
2025-11-22 22:55:35 -03:00
|
|
|
},
|
2025-12-23 18:40:58 -03:00
|
|
|
|
2025-12-10 08:30:49 -03:00
|
|
|
site_path: "./botserver-stack/sites".to_string(),
|
2025-11-30 23:48:08 -03:00
|
|
|
data_dir: "./botserver-stack/data".to_string(),
|
2025-11-22 22:55:35 -03:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct ConfigManager {
|
|
|
|
|
conn: DbPool,
|
|
|
|
|
}
|
|
|
|
|
impl ConfigManager {
|
|
|
|
|
pub fn new(conn: DbPool) -> Self {
|
|
|
|
|
Self { conn }
|
|
|
|
|
}
|
|
|
|
|
fn get_conn(
|
|
|
|
|
&self,
|
|
|
|
|
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, diesel::result::Error> {
|
|
|
|
|
self.conn.get().map_err(|e| {
|
|
|
|
|
diesel::result::Error::DatabaseError(
|
|
|
|
|
diesel::result::DatabaseErrorKind::UnableToSendCommand,
|
|
|
|
|
Box::new(e.to_string()),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
pub fn get_config(
|
|
|
|
|
&self,
|
|
|
|
|
code_bot_id: &uuid::Uuid,
|
|
|
|
|
key: &str,
|
|
|
|
|
fallback: Option<&str>,
|
|
|
|
|
) -> Result<String, diesel::result::Error> {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
|
|
|
|
let mut conn = self.get_conn()?;
|
|
|
|
|
let fallback_str = fallback.unwrap_or("");
|
|
|
|
|
let result = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(code_bot_id))
|
|
|
|
|
.filter(config_key.eq(key))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn);
|
|
|
|
|
let value = match result {
|
|
|
|
|
Ok(v) => v,
|
|
|
|
|
Err(_) => {
|
|
|
|
|
let (default_bot_id, _default_bot_name) = crate::bot::get_default_bot(&mut conn);
|
|
|
|
|
bot_configuration
|
|
|
|
|
.filter(bot_id.eq(default_bot_id))
|
|
|
|
|
.filter(config_key.eq(key))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
2025-12-26 08:59:25 -03:00
|
|
|
.unwrap_or_else(|_| fallback_str.to_string())
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Ok(value)
|
|
|
|
|
}
|
2025-11-26 22:54:22 -03:00
|
|
|
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
pub fn get_bot_config_value(
|
2025-11-26 22:54:22 -03:00
|
|
|
&self,
|
|
|
|
|
target_bot_id: &uuid::Uuid,
|
|
|
|
|
key: &str,
|
|
|
|
|
) -> Result<String, String> {
|
|
|
|
|
use crate::shared::models::schema::bot_configuration::dsl::*;
|
|
|
|
|
use diesel::prelude::*;
|
|
|
|
|
|
|
|
|
|
let mut conn = self
|
|
|
|
|
.get_conn()
|
|
|
|
|
.map_err(|e| format!("Failed to acquire connection: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let value = bot_configuration
|
|
|
|
|
.filter(bot_id.eq(target_bot_id))
|
|
|
|
|
.filter(config_key.eq(key))
|
|
|
|
|
.select(config_value)
|
|
|
|
|
.first::<String>(&mut conn)
|
|
|
|
|
.map_err(|e| format!("Failed to get bot config value: {}", e))?;
|
|
|
|
|
|
|
|
|
|
Ok(value)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 22:55:35 -03:00
|
|
|
pub fn sync_gbot_config(&self, bot_id: &uuid::Uuid, content: &str) -> Result<usize, String> {
|
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
|
hasher.update(content.as_bytes());
|
|
|
|
|
let mut conn = self
|
|
|
|
|
.get_conn()
|
|
|
|
|
.map_err(|e| format!("Failed to acquire connection: {}", e))?;
|
|
|
|
|
let mut updated = 0;
|
|
|
|
|
for line in content.lines().skip(1) {
|
|
|
|
|
let parts: Vec<&str> = line.split(',').collect();
|
|
|
|
|
if parts.len() >= 2 {
|
|
|
|
|
let key = parts[0].trim();
|
|
|
|
|
let value = parts[1].trim();
|
|
|
|
|
let new_id: uuid::Uuid = uuid::Uuid::new_v4();
|
|
|
|
|
diesel::sql_query("INSERT INTO bot_configuration (id, bot_id, config_key, config_value, config_type) VALUES ($1, $2, $3, $4, 'string') ON CONFLICT (bot_id, config_key) DO UPDATE SET config_value = EXCLUDED.config_value, updated_at = NOW()")
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(new_id)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(key)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(value)
|
|
|
|
|
.execute(&mut conn)
|
|
|
|
|
.map_err(|e| format!("Failed to update config: {}", e))?;
|
|
|
|
|
updated += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(updated)
|
|
|
|
|
}
|
feat(autotask): Implement AutoTask system with intent classification and app generation
- Add IntentClassifier with 7 intent types (APP_CREATE, TODO, MONITOR, ACTION, SCHEDULE, GOAL, TOOL)
- Add AppGenerator with LLM-powered app structure analysis
- Add DesignerAI for modifying apps through conversation
- Add app_server for serving generated apps with clean URLs
- Add db_api for CRUD operations on bot database tables
- Add ask_later keyword for pending info collection
- Add migration 6.1.1 with tables: pending_info, auto_tasks, execution_plans, task_approvals, task_decisions, safety_audit_log, generated_apps, intent_classifications, designer_changes
- Write apps to S3 drive and sync to SITE_ROOT for serving
- Clean URL structure: /apps/{app_name}/
- Integrate with DriveMonitor for file sync
Based on Chapter 17 - Autonomous Tasks specification
2025-12-27 21:10:09 -03:00
|
|
|
|
|
|
|
|
/// Set a single configuration value for a bot (upsert)
|
|
|
|
|
pub fn set_config(
|
|
|
|
|
&self,
|
|
|
|
|
target_bot_id: &uuid::Uuid,
|
|
|
|
|
key: &str,
|
|
|
|
|
value: &str,
|
|
|
|
|
) -> Result<(), diesel::result::Error> {
|
|
|
|
|
let mut conn = self.get_conn()?;
|
|
|
|
|
let new_id: uuid::Uuid = uuid::Uuid::new_v4();
|
|
|
|
|
|
|
|
|
|
diesel::sql_query(
|
|
|
|
|
"INSERT INTO bot_configuration (id, bot_id, config_key, config_value, config_type) \
|
|
|
|
|
VALUES ($1, $2, $3, $4, 'string') \
|
|
|
|
|
ON CONFLICT (bot_id, config_key) DO UPDATE SET config_value = EXCLUDED.config_value, updated_at = NOW()"
|
|
|
|
|
)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(new_id)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(target_bot_id)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(key)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(value)
|
|
|
|
|
.execute(&mut conn)?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-11-22 22:55:35 -03:00
|
|
|
}
|