chore: Remove emoji icons from log messages and UI

Replace emoji icons with plain text/ASCII equivalents to prevent
encoding issues and improve console compatibility:
- Replace checkmarks with *
- Replace x marks with x
- Replace status indicators with text [OK], [ERR], etc.
- Remove decorative emojis from info/debug log messages
- Keep functional emojis in user-facing chat/sentiment features
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-09 07:55:11 -03:00
parent b204aebd50
commit 583e764bb9
24 changed files with 203 additions and 203 deletions

View file

@ -631,10 +631,10 @@ pub async fn handle_recent_activity(State(state): State<Arc<AppState>>) -> impl
for activity in &activities {
let icon = match activity.activity_type.as_str() {
"session" => "💬",
"error" => "⚠️",
"bot" => "🤖",
_ => "📌",
"session" => "",
"error" => "",
"bot" => "",
_ => "",
};
html.push_str("<div class=\"activity-item\">");
@ -773,7 +773,7 @@ pub async fn handle_analytics_chat(
let mut html = String::new();
html.push_str("<div class=\"chat-message assistant\">");
html.push_str("<div class=\"message-avatar\">🤖</div>");
html.push_str("<div class=\"message-avatar\"></div>");
html.push_str("<div class=\"message-content\">");
html.push_str(&html_escape(response));
html.push_str("</div>");

View file

@ -1059,10 +1059,10 @@ async fn handle_queue_command(state: &Arc<AppState>) -> Result<String, String> {
.map_err(|e| e.to_string())??;
if result.is_empty() {
return Ok("📋 *Queue is empty*\nNo conversations waiting for attention.".to_string());
return Ok(" *Queue is empty*\nNo conversations waiting for attention.".to_string());
}
let mut response = format!("📋 *Queue* ({} waiting)\n\n", result.len());
let mut response = format!(" *Queue* ({} waiting)\n\n", result.len());
for (i, session) in result.iter().enumerate() {
let name = session
@ -1144,12 +1144,12 @@ async fn handle_take_command(
.unwrap_or("Unknown");
Ok(format!(
" *Conversation assigned*\n\nCustomer: *{}*\nSession: {}\n\nYou can now respond to this customer. Their messages will be forwarded to you.",
" *Conversation assigned*\n\nCustomer: *{}*\nSession: {}\n\nYou can now respond to this customer. Their messages will be forwarded to you.",
name,
&session.id.to_string()[..8]
))
} else {
Ok("📭 No conversations waiting in queue.".to_string())
Ok(" No conversations waiting in queue.".to_string())
}
})
.await
@ -1165,17 +1165,17 @@ async fn handle_status_command(
) -> Result<String, String> {
if args.is_empty() {
return Ok(
"📊 *Status Options*\n\n`/status online` - Available\n`/status busy` - In conversation\n`/status away` - Temporarily away\n`/status offline` - Not available"
" *Status Options*\n\n`/status online` - Available\n`/status busy` - In conversation\n`/status away` - Temporarily away\n`/status offline` - Not available"
.to_string(),
);
}
let status = args[0].to_lowercase();
let (emoji, text, status_value) = match status.as_str() {
"online" => ("🟢", "Online - Available for conversations", "online"),
"busy" => ("🟡", "Busy - Handling conversations", "busy"),
"away" => ("🟠", "Away - Temporarily unavailable", "away"),
"offline" => ("", "Offline - Not available", "offline"),
"online" => ("", "Online - Available for conversations", "online"),
"busy" => ("", "Busy - Handling conversations", "busy"),
"away" => ("", "Away - Temporarily unavailable", "away"),
"offline" => ("", "Offline - Not available", "offline"),
_ => {
return Err(format!(
"Invalid status: {}. Use online, busy, away, or offline.",
@ -1306,7 +1306,7 @@ async fn handle_transfer_command(
);
Ok(format!(
"🔄 *Transfer initiated*\n\nSession {} is being transferred to *{}*.\n\nThe conversation is now in the queue for the target attendant. They will be notified when they check their queue.",
" *Transfer initiated*\n\nSession {} is being transferred to *{}*.\n\nThe conversation is now in the queue for the target attendant. They will be notified when they check their queue.",
&session_id.to_string()[..8],
target_clean
))
@ -1345,7 +1345,7 @@ async fn handle_resolve_command(
.map_err(|e| e.to_string())??;
Ok(format!(
" *Conversation resolved*\n\nSession {} has been marked as resolved. The customer will be returned to bot mode.",
" *Conversation resolved*\n\nSession {} has been marked as resolved. The customer will be returned to bot mode.",
&session_id.to_string()[..8]
))
}
@ -1361,7 +1361,7 @@ async fn handle_tips_command(
if history.is_empty() {
return Ok(
"💡 No messages yet. Tips will appear when the customer sends a message.".to_string(),
" No messages yet. Tips will appear when the customer sends a message.".to_string(),
);
}
@ -1383,19 +1383,19 @@ async fn handle_tips_command(
let (_, Json(tip_response)) = response.into_response().into_parts();
if tip_response.tips.is_empty() {
return Ok("💡 No specific tips for this conversation yet.".to_string());
return Ok(" No specific tips for this conversation yet.".to_string());
}
let mut result = "💡 *Tips for this conversation*\n\n".to_string();
let mut result = " *Tips for this conversation*\n\n".to_string();
for tip in tip_response.tips {
let emoji = match tip.tip_type {
TipType::Intent => "🎯",
TipType::Action => "",
TipType::Warning => "⚠️",
TipType::Knowledge => "📚",
TipType::History => "📜",
TipType::General => "💡",
TipType::Intent => "",
TipType::Action => "",
TipType::Warning => "",
TipType::Knowledge => "",
TipType::History => "",
TipType::General => "",
};
result.push_str(&format!("{} {}\n\n", emoji, tip.content));
}
@ -1429,7 +1429,7 @@ async fn handle_polish_command(
.unwrap_or("Failed to polish message".to_string()));
}
let mut result = " *Polished message*\n\n".to_string();
let mut result = " *Polished message*\n\n".to_string();
result.push_str(&format!("_{}_\n\n", polish_response.polished));
if !polish_response.changes.is_empty() {
@ -1461,10 +1461,10 @@ async fn handle_replies_command(
let (_, Json(replies_response)) = response.into_response().into_parts();
if replies_response.replies.is_empty() {
return Ok("💬 No reply suggestions available.".to_string());
return Ok(" No reply suggestions available.".to_string());
}
let mut result = "💬 *Suggested replies*\n\n".to_string();
let mut result = " *Suggested replies*\n\n".to_string();
for (i, reply) in replies_response.replies.iter().enumerate() {
result.push_str(&format!(
@ -1497,7 +1497,7 @@ async fn handle_summary_command(
let summary = summary_response.summary;
let mut result = "📝 *Conversation Summary*\n\n".to_string();
let mut result = " *Conversation Summary*\n\n".to_string();
result.push_str(&format!("{}\n\n", summary.brief));
if !summary.key_points.is_empty() {
@ -1525,13 +1525,13 @@ async fn handle_summary_command(
}
result.push_str(&format!(
"📊 {} messages | {} minutes | Sentiment: {}",
" {} messages | {} minutes | Sentiment: {}",
summary.message_count, summary.duration_minutes, summary.sentiment_trend
));
if !summary.recommended_action.is_empty() {
result.push_str(&format!(
"\n\n💡 *Recommended:* {}",
"\n\n *Recommended:* {}",
summary.recommended_action
));
}
@ -1540,7 +1540,7 @@ async fn handle_summary_command(
}
fn get_help_text() -> String {
r#"🤖 *Attendant Commands*
r#" *Attendant Commands*
*Queue Management:*
`/queue` - View waiting conversations

View file

@ -44,7 +44,7 @@ pub fn register_clear_kb_keyword(
match result {
Ok(Ok(_)) => {
info!(
" KB '{}' removed from session {}",
" KB '{}' removed from session {}",
kb_name, session_clone.id
);
Ok(Dynamic::UNIT)
@ -131,7 +131,7 @@ fn clear_specific_kb(
);
} else {
info!(
" Cleared KB '{}' from session {}, {} KB(s) remaining active",
" Cleared KB '{}' from session {}, {} KB(s) remaining active",
kb_name, session_id, remaining_count
);
}
@ -160,7 +160,7 @@ fn clear_all_kbs(
if rows_affected > 0 {
info!(
" Cleared {} active KBs from session {}",
" Cleared {} active KBs from session {}",
rows_affected, session_id
);
} else {

View file

@ -104,23 +104,23 @@ pub fn register_core_functions(state: Arc<AppState>, user: UserSession, engine:
// Register math functions (ABS, ROUND, INT, MAX, MIN, MOD, RANDOM, etc.)
register_math_functions(state.clone(), user.clone(), engine);
debug!(" Math functions registered");
debug!(" * Math functions registered");
// Register date/time functions (NOW, TODAY, YEAR, MONTH, DAY, etc.)
register_datetime_functions(state.clone(), user.clone(), engine);
debug!(" Date/Time functions registered");
debug!(" * Date/Time functions registered");
// Register validation functions (VAL, STR, ISNULL, ISEMPTY, TYPEOF, etc.)
register_validation_functions(state.clone(), user.clone(), engine);
debug!(" Validation functions registered");
debug!(" * Validation functions registered");
// Register array functions (SORT, UNIQUE, CONTAINS, PUSH, POP, etc.)
register_array_functions(state.clone(), user.clone(), engine);
debug!(" Array functions registered");
debug!(" * Array functions registered");
// Register error handling functions (THROW, ERROR, IS_ERROR, ASSERT, etc.)
register_error_functions(state, user, engine);
debug!(" Error handling functions registered");
debug!(" * Error handling functions registered");
debug!("All core BASIC functions registered successfully");
}

View file

@ -63,7 +63,7 @@ pub fn register_use_kb_keyword(
match result {
Ok(Ok(_)) => {
info!(" KB '{}' added to session {}", kb_name, session_clone.id);
info!(" KB '{}' added to session {}", kb_name, session_clone.id);
Ok(Dynamic::UNIT)
}
Ok(Err(e)) => {
@ -163,7 +163,7 @@ fn add_kb_to_session(
.map_err(|e| format!("Failed to add KB association: {}", e))?;
info!(
" Added KB '{}' to session {} (collection: {}, path: {})",
" Added KB '{}' to session {} (collection: {}, path: {})",
kb_name, session_id, qdrant_collection, kb_folder_path
);

View file

@ -263,7 +263,7 @@ fn add_website_to_session(
.map_err(|e| format!("Failed to add website to session: {}", e))?;
info!(
" Added website '{}' to session {} (collection: {})",
" Added website '{}' to session {} (collection: {})",
url, session_id, collection_name
);

View file

@ -350,12 +350,12 @@ async fn fetch_fallback_weather(location: &str) -> Result<String, String> {
fn format_weather_response(weather: &WeatherData) -> String {
format!(
"Current weather in {}:\n\
🌡 Temperature: {:.1}{} (feels like {:.1}{})\n\
Conditions: {}\n\
💧 Humidity: {}%\n\
💨 Wind: {:.1} m/s {}\n\
🔍 Visibility: {:.1} km\n\
📊 Pressure: {} hPa",
Temperature: {:.1}{} (feels like {:.1}{})\n\
Conditions: {}\n\
Humidity: {}%\n\
Wind: {:.1} m/s {}\n\
Visibility: {:.1} km\n\
Pressure: {} hPa",
weather.location,
weather.temperature,
weather.temperature_unit,
@ -375,10 +375,10 @@ fn format_forecast_response(weather: &WeatherData) -> String {
for day in &weather.forecast {
response.push_str(&format!(
"📅 {}\n\
🌡 High: {:.1}°C, Low: {:.1}°C\n\
{}\n\
Rain chance: {}%\n\n",
" {}\n\
High: {:.1}°C, Low: {:.1}°C\n\
{}\n\
Rain chance: {}%\n\n",
day.date, day.temp_high, day.temp_low, day.description, day.rain_chance
));
}

View file

@ -37,7 +37,7 @@ impl FileTree {
for bucket in buckets {
if let Some(name) = bucket.name() {
let icon = if name.ends_with(".gbai") {
"🤖"
""
} else {
"📦"
};
@ -53,7 +53,7 @@ impl FileTree {
}
Err(e) => {
self.items.push((
format!(" Error: {}", e),
format!("x Error: {}", e),
TreeNode::Bucket {
name: String::new(),
},
@ -62,7 +62,7 @@ impl FileTree {
}
} else {
self.items.push((
" Drive not connected".to_string(),
"x Drive not connected".to_string(),
TreeNode::Bucket {
name: String::new(),
},
@ -116,7 +116,7 @@ impl FileTree {
async fn load_bucket_contents(&mut self, bucket: &str, prefix: &str) -> Result<()> {
self.items.clear();
self.items.push((
"⬆️ .. (go back)".to_string(),
" .. (go back)".to_string(),
TreeNode::Folder {
bucket: bucket.to_string(),
path: "..".to_string(),
@ -195,13 +195,13 @@ impl FileTree {
files.sort_by(|(a, _), (b, _)| a.cmp(b));
for (name, full_path) in files {
let icon = if name.ends_with(".bas") {
"⚙️"
""
} else if name.ends_with(".ast") {
"🔧"
""
} else if name.ends_with(".csv") {
"📊"
""
} else if name.ends_with(".gbkb") {
"📚"
""
} else if name.ends_with(".json") {
"🔖"
} else {

View file

@ -350,9 +350,9 @@ impl XtreeUI {
.iter()
.map(|(comp_name, process, _port)| {
let status = if status_panel::StatusPanel::check_component_running(process) {
format!("🟢 {}", comp_name)
format!(" {}", comp_name)
} else {
format!("🔴 {}", comp_name)
format!(" {}", comp_name)
};
status
})
@ -467,7 +467,7 @@ impl XtreeUI {
.style(Style::default().bg(bg));
let status_text = format!(
"\n {}\n\n Components:\n ○ Vault\n ○ Database\n ○ Drive\n ○ Cache\n ○ LLM",
"\n {}\n\n Components:\n ○ Vault\n ○ Database\n ○ Drive\n ○ Cache\n ○ LLM",
self.bootstrap_status
);
let status_para = Paragraph::new(status_text)

View file

@ -104,9 +104,9 @@ impl StatusPanel {
for (comp_name, process, port) in components {
let status = if Self::check_component_running(process) {
format!("🟢 ONLINE [Port: {}]", port)
format!(" ONLINE [Port: {}]", port)
} else {
"🔴 OFFLINE".to_string()
" OFFLINE".to_string()
};
lines.push(format!(" {:<10} {}", comp_name, status));
}
@ -138,7 +138,7 @@ impl StatusPanel {
} else {
" "
};
lines.push(format!(" {} 🤖 {}", marker, bot_name));
lines.push(format!(" {} {}", marker, bot_name));
if let Some(ref selected) = selected_bot {
if selected == &bot_name {

View file

@ -666,7 +666,7 @@ impl StartupWizard {
stdout,
cursor::MoveTo(4, 19 + i as u16),
SetForegroundColor(Color::Green),
Print(" "),
Print("* "),
ResetColor,
Print(format!("{}", component))
)?;
@ -718,7 +718,7 @@ impl StartupWizard {
execute!(
stdout,
SetForegroundColor(Color::Green),
Print(" "),
Print("> "),
Print(format!("{:<25}", name)),
SetForegroundColor(Color::DarkGrey),
Print(format!(" {}", desc)),
@ -775,8 +775,8 @@ impl StartupWizard {
for (i, (component, _, can_toggle)) in options.iter().enumerate() {
execute!(stdout, cursor::MoveTo(4, start_row + i as u16))?;
let checkbox = if selected[i] { "[]" } else { "[ ]" };
let prefix = if i == cursor { "" } else { " " };
let checkbox = if selected[i] { "[*]" } else { "[ ]" };
let prefix = if i == cursor { ">" } else { " " };
if !can_toggle {
execute!(
@ -930,7 +930,7 @@ pub fn apply_wizard_config(config: &WizardConfig) -> io::Result<()> {
fs::write(config.data_dir.join(".env"), env_content)?;
println!("\n Configuration applied successfully!");
println!("\n Configuration applied successfully!");
println!(" Data directory: {}", config.data_dir.display());
println!("\n Next steps:");
println!(" 1. Run: botserver start");

View file

@ -491,13 +491,13 @@ impl BootstrapManager {
pub async fn bootstrap(&mut self) -> Result<()> {
// Generate certificates first (including for Vault)
info!("🔒 Generating TLS certificates...");
info!("Generating TLS certificates...");
if let Err(e) = self.generate_certificates().await {
error!("Failed to generate certificates: {}", e);
}
// Create Vault configuration with mTLS
info!("📝 Creating Vault configuration...");
info!("Creating Vault configuration...");
if let Err(e) = self.create_vault_config().await {
error!("Failed to create Vault config: {}", e);
}
@ -562,7 +562,7 @@ impl BootstrapManager {
// After tables is installed, START PostgreSQL and create Zitadel config files before installing directory
if component == "tables" {
info!("🚀 Starting PostgreSQL database...");
info!("Starting PostgreSQL database...");
match pm.start("tables") {
Ok(_) => {
info!("PostgreSQL started successfully");
@ -575,7 +575,7 @@ impl BootstrapManager {
}
// Run migrations using direct connection (Vault not set up yet)
info!("🔄 Running database migrations...");
info!("Running database migrations...");
let database_url =
format!("postgres://gbuser:{}@localhost:5432/botserver", db_password);
match diesel::PgConnection::establish(&database_url) {
@ -583,7 +583,7 @@ impl BootstrapManager {
if let Err(e) = self.apply_migrations(&mut conn) {
error!("Failed to apply migrations: {}", e);
} else {
info!("Database migrations applied");
info!("Database migrations applied");
}
}
Err(e) => {
@ -591,7 +591,7 @@ impl BootstrapManager {
}
}
info!("🔧 Creating Directory configuration files...");
info!("Creating Directory configuration files...");
if let Err(e) = self.configure_services_in_directory(&db_password).await {
error!("Failed to create Directory config files: {}", e);
}
@ -599,7 +599,7 @@ impl BootstrapManager {
// Directory configuration - setup happens after install starts Zitadel
if component == "directory" {
info!("🔧 Waiting for Directory to be ready...");
info!("Waiting for Directory to be ready...");
if let Err(e) = self.setup_directory().await {
// Don't fail completely - Zitadel may still be usable with first instance setup
warn!("Directory additional setup had issues: {}", e);
@ -608,7 +608,7 @@ impl BootstrapManager {
// After Vault is installed, START the server then initialize it
if component == "vault" {
info!("🚀 Starting Vault server...");
info!("Starting Vault server...");
match pm.start("vault") {
Ok(_) => {
info!("Vault server started");
@ -620,7 +620,7 @@ impl BootstrapManager {
}
}
info!("🔐 Initializing Vault with secrets...");
info!("Initializing Vault with secrets...");
if let Err(e) = self
.setup_vault(
&db_password,
@ -634,14 +634,14 @@ impl BootstrapManager {
}
// Initialize the global SecretsManager so other components can use Vault
info!("🔑 Initializing SecretsManager...");
info!("Initializing SecretsManager...");
debug!(
"VAULT_ADDR={:?}, VAULT_TOKEN set={}",
std::env::var("VAULT_ADDR").ok(),
std::env::var("VAULT_TOKEN").is_ok()
);
match init_secrets_manager().await {
Ok(_) => info!("SecretsManager initialized successfully"),
Ok(_) => info!("SecretsManager initialized successfully"),
Err(e) => {
error!("Failed to initialize SecretsManager: {}", e);
// Don't continue if SecretsManager fails - it's required for DB connection
@ -654,21 +654,21 @@ impl BootstrapManager {
}
if component == "email" {
info!("🔧 Auto-configuring Email (Stalwart)...");
info!("Auto-configuring Email (Stalwart)...");
if let Err(e) = self.setup_email().await {
error!("Failed to setup Email: {}", e);
}
}
if component == "proxy" {
info!("🔧 Configuring Caddy reverse proxy...");
info!("Configuring Caddy reverse proxy...");
if let Err(e) = self.setup_caddy_proxy().await {
error!("Failed to setup Caddy: {}", e);
}
}
if component == "dns" {
info!("🔧 Configuring CoreDNS for dynamic DNS...");
info!("Configuring CoreDNS for dynamic DNS...");
if let Err(e) = self.setup_coredns().await {
error!("Failed to setup CoreDNS: {}", e);
}
@ -1217,7 +1217,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, db_password
))
.output()?;
info!(" Stored database credentials");
info!(" Stored database credentials");
// Drive credentials
let _ = std::process::Command::new("sh")
@ -1227,7 +1227,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, drive_accesskey, drive_secret
))
.output()?;
info!(" Stored drive credentials");
info!(" Stored drive credentials");
// Cache credentials
let _ = std::process::Command::new("sh")
@ -1237,7 +1237,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, cache_password
))
.output()?;
info!(" Stored cache credentials");
info!(" Stored cache credentials");
// Directory placeholder (will be updated after Zitadel setup)
let _ = std::process::Command::new("sh")
@ -1247,7 +1247,7 @@ meet IN A 127.0.0.1
vault_addr, root_token
))
.output()?;
info!(" Created directory placeholder");
info!(" Created directory placeholder");
// LLM placeholder
let _ = std::process::Command::new("sh")
@ -1257,7 +1257,7 @@ meet IN A 127.0.0.1
vault_addr, root_token
))
.output()?;
info!(" Created LLM placeholder");
info!(" Created LLM placeholder");
// Email placeholder
let _ = std::process::Command::new("sh")
@ -1267,7 +1267,7 @@ meet IN A 127.0.0.1
vault_addr, root_token
))
.output()?;
info!(" Created email placeholder");
info!(" Created email placeholder");
// Encryption key
let encryption_key = self.generate_secure_password(32);
@ -1278,7 +1278,7 @@ meet IN A 127.0.0.1
vault_addr, root_token, encryption_key
))
.output()?;
info!(" Generated and stored encryption key");
info!(" Generated and stored encryption key");
// Write .env file with ONLY Vault variables - NO LEGACY FALLBACK
info!("Writing .env file with Vault configuration...");
@ -1308,9 +1308,9 @@ VAULT_CACHE_TTL=300
);
fs::write(&env_file_path, env_content)?;
info!(" Created .env file with Vault configuration");
info!(" Created .env file with Vault configuration");
info!("🔐 Vault setup complete!");
info!("Vault setup complete!");
info!(" Vault UI: {}/ui", vault_addr);
info!(" Root token saved to: {}", vault_init_path.display());
@ -1341,7 +1341,7 @@ VAULT_CACHE_TTL=300
info!(" IMAP: {}:{}", config.imap_host, config.imap_port);
info!(" Admin: {} / {}", config.admin_user, config.admin_pass);
if config.directory_integration {
info!(" 🔗 Integrated with Directory for authentication");
info!(" Integrated with Directory for authentication");
}
Ok(())
@ -1562,7 +1562,7 @@ VAULT_CACHE_TTL=300
if synced > 0 {
info!(
"Synced {} config values for bot {} (skipped {} empty lines)",
"Synced {} config values for bot {} (skipped {} empty lines)",
synced, bot_id, skipped
);
} else {

View file

@ -79,12 +79,12 @@ impl OAuthProvider {
/// Get icon/emoji for UI
pub fn icon(&self) -> &'static str {
match self {
OAuthProvider::Google => "🔵",
OAuthProvider::Discord => "🎮",
OAuthProvider::Reddit => "🟠",
OAuthProvider::Twitter => "🐦",
OAuthProvider::Microsoft => "🪟",
OAuthProvider::Facebook => "📘",
OAuthProvider::Google => "",
OAuthProvider::Discord => "",
OAuthProvider::Reddit => "",
OAuthProvider::Twitter => "",
OAuthProvider::Microsoft => "",
OAuthProvider::Facebook => "",
}
}
}

View file

@ -32,12 +32,12 @@ pub async fn run() -> Result<()> {
for component in components {
if pm.is_installed(component.name) {
match pm.start(component.name) {
Ok(_) => println!(" Started {}", component.name),
Err(e) => eprintln!(" Failed to start {}: {}", component.name, e),
Ok(_) => println!("* Started {}", component.name),
Err(e) => eprintln!("x Failed to start {}: {}", component.name, e),
}
}
}
println!(" BotServer components started");
println!("* BotServer components started");
}
"stop" => {
println!("Stopping all components...");
@ -48,7 +48,7 @@ pub async fn run() -> Result<()> {
.arg(component.termination_command)
.output();
}
println!(" BotServer components stopped");
println!("* BotServer components stopped");
}
"restart" => {
println!("Restarting BotServer...");
@ -77,7 +77,7 @@ pub async fn run() -> Result<()> {
let _ = pm.start(component.name);
}
}
println!(" BotServer restarted");
println!("* BotServer restarted");
}
"install" => {
if args.len() < 3 {
@ -97,7 +97,7 @@ pub async fn run() -> Result<()> {
};
let pm = PackageManager::new(mode, tenant)?;
pm.install(component).await?;
println!(" Component '{}' installed successfully", component);
println!("* Component '{}' installed successfully", component);
}
"remove" => {
if args.len() < 3 {
@ -117,7 +117,7 @@ pub async fn run() -> Result<()> {
};
let pm = PackageManager::new(mode, tenant)?;
pm.remove(component)?;
println!(" Component '{}' removed successfully", component);
println!("* Component '{}' removed successfully", component);
}
"list" => {
let mode = if args.contains(&"--container".to_string()) {
@ -134,7 +134,7 @@ pub async fn run() -> Result<()> {
println!("Available components:");
for component in pm.list() {
let status = if pm.is_installed(&component) {
" installed"
"* installed"
} else {
" available"
};
@ -159,9 +159,9 @@ pub async fn run() -> Result<()> {
};
let pm = PackageManager::new(mode, tenant)?;
if pm.is_installed(component) {
println!(" Component '{}' is installed", component);
println!("* Component '{}' is installed", component);
} else {
println!(" Component '{}' is not installed", component);
println!("x Component '{}' is not installed", component);
}
}
"--help" | "-h" => {

View file

@ -117,7 +117,7 @@ impl DirectorySetup {
/// Initialize directory with default configuration
pub async fn initialize(&mut self) -> Result<DirectoryConfig> {
log::info!("🔧 Initializing Directory (Zitadel) with defaults...");
log::info!(" Initializing Directory (Zitadel) with defaults...");
// Check if already initialized
if let Ok(existing_config) = self.load_existing_config().await {
@ -133,19 +133,19 @@ impl DirectorySetup {
// Create default organization
let org = self.create_default_organization().await?;
log::info!(" Created default organization: {}", org.name);
log::info!(" Created default organization: {}", org.name);
// Create default user
let user = self.create_default_user(&org.id).await?;
log::info!(" Created default user: {}", user.username);
log::info!(" Created default user: {}", user.username);
// Create OAuth2 application for BotServer
let (project_id, client_id, client_secret) = self.create_oauth_application(&org.id).await?;
log::info!(" Created OAuth2 application");
log::info!(" Created OAuth2 application");
// Grant user admin permissions
self.grant_user_permissions(&org.id, &user.id).await?;
log::info!(" Granted admin permissions to default user");
log::info!(" Granted admin permissions to default user");
let config = DirectoryConfig {
base_url: self.base_url.clone(),
@ -159,15 +159,15 @@ impl DirectorySetup {
// Save configuration
self.save_config_internal(&config).await?;
log::info!(" Saved Directory configuration");
log::info!(" Saved Directory configuration");
log::info!("🎉 Directory initialization complete!");
log::info!(" Directory initialization complete!");
log::info!(
"📧 Default user: {} / {}",
" Default user: {} / {}",
config.default_user.email,
config.default_user.password
);
log::info!("🌐 Login at: {}", self.base_url);
log::info!(" Login at: {}", self.base_url);
Ok(config)
}

View file

@ -93,7 +93,7 @@ impl EmailSetup {
&mut self,
directory_config_path: Option<PathBuf>,
) -> Result<EmailConfig> {
log::info!("🔧 Initializing Email (Stalwart) server...");
log::info!(" Initializing Email (Stalwart) server...");
// Check if already initialized
if let Ok(existing_config) = self.load_existing_config().await {
@ -106,17 +106,17 @@ impl EmailSetup {
// Create default domain
self.create_default_domain().await?;
log::info!(" Created default email domain: localhost");
log::info!(" Created default email domain: localhost");
// Set up Directory (Zitadel) integration if available
let directory_integration = if let Some(dir_config_path) = directory_config_path {
match self.setup_directory_integration(&dir_config_path).await {
Ok(_) => {
log::info!(" Integrated with Directory for authentication");
log::info!(" Integrated with Directory for authentication");
true
}
Err(e) => {
log::warn!("⚠️ Directory integration failed: {}", e);
log::warn!(" Directory integration failed: {}", e);
false
}
}
@ -126,7 +126,7 @@ impl EmailSetup {
// Create admin account
self.create_admin_account().await?;
log::info!(" Created admin email account: {}", self.admin_user);
log::info!(" Created admin email account: {}", self.admin_user);
let config = EmailConfig {
base_url: self.base_url.clone(),
@ -141,9 +141,9 @@ impl EmailSetup {
// Save configuration
self.save_config(&config).await?;
log::info!(" Saved Email configuration");
log::info!(" Saved Email configuration");
log::info!("🎉 Email initialization complete!");
log::info!(" Email initialization complete!");
log::info!("📧 SMTP: localhost:25 (587 for TLS)");
log::info!("📬 IMAP: localhost:143 (993 for TLS)");
log::info!("👤 Admin: {} / {}", config.admin_user, config.admin_pass);
@ -288,7 +288,7 @@ impl EmailSetup {
if !email.is_empty() {
self.create_user_mailbox(username, password, email).await?;
log::info!(" Created mailbox for: {}", email);
log::info!(" Created mailbox for: {}", email);
}
}

View file

@ -258,7 +258,7 @@ pub async fn handle_save(
Ok(_) => {
let mut html = String::new();
html.push_str("<div class=\"save-result success\">");
html.push_str("<span class=\"save-icon\"></span>");
html.push_str("<span class=\"save-icon\">*</span>");
html.push_str("<span class=\"save-message\">Saved successfully</span>");
html.push_str("</div>");
Html(html)
@ -266,7 +266,7 @@ pub async fn handle_save(
Err(e) => {
let mut html = String::new();
html.push_str("<div class=\"save-result error\">");
html.push_str("<span class=\"save-icon\"></span>");
html.push_str("<span class=\"save-icon\">x</span>");
html.push_str("<span class=\"save-message\">Save failed: ");
html.push_str(&html_escape(&e));
html.push_str("</span>");
@ -289,13 +289,13 @@ pub async fn handle_validate(
if validation.valid {
html.push_str("<div class=\"validation-success\">");
html.push_str("<span class=\"validation-icon\"></span>");
html.push_str("<span class=\"validation-icon\">*</span>");
html.push_str("<span class=\"validation-text\">Dialog is valid</span>");
html.push_str("</div>");
} else {
html.push_str("<div class=\"validation-errors\">");
html.push_str("<div class=\"validation-header\">");
html.push_str("<span class=\"validation-icon\"></span>");
html.push_str("<span class=\"validation-icon\">x</span>");
html.push_str("<span class=\"validation-text\">");
html.push_str(&validation.errors.len().to_string());
html.push_str(" error(s) found</span>");
@ -318,7 +318,7 @@ pub async fn handle_validate(
if !validation.warnings.is_empty() {
html.push_str("<div class=\"validation-warnings\">");
html.push_str("<div class=\"validation-header\">");
html.push_str("<span class=\"validation-icon\"></span>");
html.push_str("<span class=\"validation-icon\">!</span>");
html.push_str("<span class=\"validation-text\">");
html.push_str(&validation.warnings.len().to_string());
html.push_str(" warning(s)</span>");

View file

@ -364,7 +364,7 @@ pub fn convert_tree_to_items(tree: &FileTree) -> Vec<FileItem> {
size: None,
modified: None,
icon: if name.ends_with(".gbai") {
"🤖".to_string()
"".to_string()
} else {
"📦".to_string()
},
@ -586,13 +586,13 @@ pub async fn create_folder(
/// Get appropriate icon for file based on extension
fn get_file_icon(path: &str) -> String {
if path.ends_with(".bas") {
"⚙️".to_string()
"".to_string()
} else if path.ends_with(".ast") {
"🔧".to_string()
"".to_string()
} else if path.ends_with(".csv") {
"📊".to_string()
"".to_string()
} else if path.ends_with(".gbkb") {
"📚".to_string()
"".to_string()
} else if path.ends_with(".json") {
"🔖".to_string()
} else if path.ends_with(".txt") || path.ends_with(".md") {
@ -602,7 +602,7 @@ fn get_file_icon(path: &str) -> String {
} else if path.ends_with(".zip") || path.ends_with(".tar") || path.ends_with(".gz") {
"📦".to_string()
} else if path.ends_with(".jpg") || path.ends_with(".png") || path.ends_with(".gif") {
"🖼️".to_string()
"".to_string()
} else {
"📄".to_string()
}

View file

@ -1782,10 +1782,10 @@ pub async fn list_folders_htmx(
let mut html = String::new();
for (folder_name, icon, count) in &[
("inbox", "📥", folder_counts.get("INBOX").unwrap_or(&0)),
("sent", "📤", folder_counts.get("Sent").unwrap_or(&0)),
("drafts", "📝", folder_counts.get("Drafts").unwrap_or(&0)),
("trash", "🗑️", folder_counts.get("Trash").unwrap_or(&0)),
("inbox", "", folder_counts.get("INBOX").unwrap_or(&0)),
("sent", "", folder_counts.get("Sent").unwrap_or(&0)),
("drafts", "", folder_counts.get("Drafts").unwrap_or(&0)),
("trash", "", folder_counts.get("Trash").unwrap_or(&0)),
] {
let active = if *folder_name == "inbox" { "active" } else { "" };
let count_badge = if **count > 0 {

View file

@ -796,7 +796,7 @@ pub async fn handle_save_document(
log::info!("Document saved: {} at {}", doc_id, path);
let mut html = String::new();
html.push_str("<div class=\"save-success\">");
html.push_str("<span class=\"save-icon\"></span>");
html.push_str("<span class=\"save-icon\">*</span>");
html.push_str("<span>Saved</span>");
html.push_str("</div>");
Html(html)
@ -1392,7 +1392,7 @@ fn format_ai_response(content: &str) -> String {
html.push_str("<div class=\"ai-response\">");
html.push_str("<div class=\"ai-response-header\">");
html.push_str("<span class=\"ai-icon\">🤖</span>");
html.push_str("<span class=\"ai-icon\"></span>");
html.push_str("<span>AI Response</span>");
html.push_str("</div>");
html.push_str("<div class=\"ai-response-content\">");
@ -1415,7 +1415,7 @@ fn format_ai_response(content: &str) -> String {
fn format_error(message: &str) -> String {
let mut html = String::new();
html.push_str("<div class=\"error-message\">");
html.push_str("<span class=\"error-icon\">⚠️</span>");
html.push_str("<span class=\"error-icon\"></span>");
html.push_str("<span>");
html.push_str(&html_escape(message));
html.push_str("</span>");

View file

@ -110,7 +110,7 @@ pub async fn handle_list_collections(State(state): State<Arc<AppState>>) -> impl
html.push_str("<div class=\"collection-item\" data-id=\"");
html.push_str(&html_escape(id));
html.push_str("\">");
html.push_str("<div class=\"collection-icon\">📁</div>");
html.push_str("<div class=\"collection-icon\"></div>");
html.push_str("<div class=\"collection-info\">");
html.push_str("<span class=\"collection-name\">");
html.push_str(&html_escape(name));
@ -193,7 +193,7 @@ pub async fn handle_create_collection(
html.push_str("<div class=\"collection-item new-item\" data-id=\"");
html.push_str(&html_escape(&id_clone));
html.push_str("\">");
html.push_str("<div class=\"collection-icon\">📁</div>");
html.push_str("<div class=\"collection-icon\"></div>");
html.push_str("<div class=\"collection-info\">");
html.push_str("<span class=\"collection-name\">");
html.push_str(&html_escape(&name_clone));
@ -324,7 +324,7 @@ pub async fn handle_search(
if results.is_empty() {
html.push_str("<div class=\"no-results\">");
html.push_str("<div class=\"no-results-icon\">🔍</div>");
html.push_str("<div class=\"no-results-icon\"></div>");
html.push_str("<h4>No results found</h4>");
html.push_str("<p>Try different keywords or check your spelling</p>");
html.push_str("</div>");
@ -449,19 +449,19 @@ pub async fn handle_trending_tags(State(_state): State<Arc<AppState>>) -> impl I
pub async fn handle_prompts(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let prompts = vec![
(
"📚",
"",
"Getting Started",
"Learn the basics and set up your first bot",
),
("🔧", "Configuration", "Customize settings and preferences"),
("", "Configuration", "Customize settings and preferences"),
(
"🔌",
"Integrations",
"Connect with external services and APIs",
),
("🚀", "Deployment", "Deploy your bot to production"),
("🔒", "Security", "Best practices for securing your bot"),
("📊", "Analytics", "Monitor and analyze bot performance"),
("", "Deployment", "Deploy your bot to production"),
("", "Security", "Best practices for securing your bot"),
("", "Analytics", "Monitor and analyze bot performance"),
];
let mut html = String::new();

View file

@ -45,13 +45,13 @@ pub async fn handle_prompts(
html.push_str("<div class=\"category-list\">");
let categories = vec![
("all", "All Prompts", "📋"),
("writing", "Writing", "✍️"),
("coding", "Coding", "💻"),
("analysis", "Analysis", "📊"),
("creative", "Creative", "🎨"),
("business", "Business", "💼"),
("education", "Education", "📚"),
("all", "All Prompts", ""),
("writing", "Writing", ""),
("coding", "Coding", ""),
("analysis", "Analysis", ""),
("creative", "Creative", ""),
("business", "Business", ""),
("education", "Education", ""),
];
for (id, name, icon) in &categories {
@ -160,11 +160,11 @@ pub async fn handle_templates(State(_state): State<Arc<AppState>>) -> impl IntoR
/// GET /api/sources/news - News tab content
pub async fn handle_news(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let news_items = vec![
("🚀", "General Bots 6.0 Released", "Major update with improved performance and new features", "2 hours ago"),
("🔧", "New MCP Server Integration", "Connect to external tools more easily with our new MCP support", "1 day ago"),
("📊", "Analytics Dashboard Update", "Real-time metrics and improved visualizations", "3 days ago"),
("🔒", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"),
("🌐", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"),
("", "General Bots 6.0 Released", "Major update with improved performance and new features", "2 hours ago"),
("", "New MCP Server Integration", "Connect to external tools more easily with our new MCP support", "1 day ago"),
("", "Analytics Dashboard Update", "Real-time metrics and improved visualizations", "3 days ago"),
("", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"),
("", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"),
];
let mut html = String::new();
@ -203,12 +203,12 @@ pub async fn handle_news(State(_state): State<Arc<AppState>>) -> impl IntoRespon
/// GET /api/sources/mcp-servers - MCP Servers tab content
pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let servers = vec![
("🗄️", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true),
("📁", "Filesystem Server", "Local and cloud file access", "Active", true),
("🌐", "Web Server", "HTTP/REST API integrations", "Active", true),
("📧", "Email Server", "SMTP/IMAP email handling", "Inactive", false),
("💬", "Slack Server", "Slack workspace integration", "Active", true),
("📊", "Analytics Server", "Data processing and reporting", "Active", true),
("", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true),
("", "Filesystem Server", "Local and cloud file access", "Active", true),
("", "Web Server", "HTTP/REST API integrations", "Active", true),
("", "Email Server", "SMTP/IMAP email handling", "Inactive", false),
("", "Slack Server", "Slack workspace integration", "Active", true),
("", "Analytics Server", "Data processing and reporting", "Active", true),
];
let mut html = String::new();
@ -259,14 +259,14 @@ pub async fn handle_mcp_servers(State(_state): State<Arc<AppState>>) -> impl Int
/// GET /api/sources/llm-tools - LLM Tools tab content
pub async fn handle_llm_tools(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
let tools = vec![
("🔍", "Web Search", "Search the web for real-time information", true),
("🧮", "Calculator", "Perform mathematical calculations", true),
("📅", "Calendar", "Manage calendar events and schedules", true),
("📝", "Note Taking", "Create and manage notes", true),
("🌤️", "Weather", "Get weather forecasts and conditions", false),
("📰", "News Reader", "Fetch and summarize news articles", false),
("🔗", "URL Fetcher", "Retrieve and parse web content", true),
("💾", "Code Executor", "Run code snippets safely", false),
("", "Web Search", "Search the web for real-time information", true),
("", "Calculator", "Perform mathematical calculations", true),
("", "Calendar", "Manage calendar events and schedules", true),
("", "Note Taking", "Create and manage notes", true),
("", "Weather", "Get weather forecasts and conditions", false),
("", "News Reader", "Fetch and summarize news articles", false),
("", "URL Fetcher", "Retrieve and parse web content", true),
("", "Code Executor", "Run code snippets safely", false),
];
let mut html = String::new();
@ -318,7 +318,7 @@ pub async fn handle_models(State(_state): State<Arc<AppState>>) -> impl IntoResp
("🦙", "Llama 3.1 70B", "Meta", "Open source large language model", "Available"),
("🔷", "Claude 3.5 Sonnet", "Anthropic", "Advanced reasoning and analysis", "Available"),
("💎", "Gemini Pro", "Google", "Multimodal AI with long context", "Available"),
("🌐", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"),
("", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"),
];
let mut html = String::new();
@ -496,42 +496,42 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
title: "Summarize Text".to_string(),
description: "Create concise summaries of long documents or articles".to_string(),
category: "writing".to_string(),
icon: "📝".to_string(),
icon: "".to_string(),
},
PromptData {
id: "code-review".to_string(),
title: "Code Review".to_string(),
description: "Analyze code for bugs, improvements, and best practices".to_string(),
category: "coding".to_string(),
icon: "💻".to_string(),
icon: "".to_string(),
},
PromptData {
id: "data-analysis".to_string(),
title: "Data Analysis".to_string(),
description: "Extract insights and patterns from data sets".to_string(),
category: "analysis".to_string(),
icon: "📊".to_string(),
icon: "".to_string(),
},
PromptData {
id: "creative-writing".to_string(),
title: "Creative Writing".to_string(),
description: "Generate stories, poems, and creative content".to_string(),
category: "creative".to_string(),
icon: "🎨".to_string(),
icon: "".to_string(),
},
PromptData {
id: "email-draft".to_string(),
title: "Email Draft".to_string(),
description: "Compose professional emails quickly".to_string(),
category: "business".to_string(),
icon: "📧".to_string(),
icon: "".to_string(),
},
PromptData {
id: "explain-concept".to_string(),
title: "Explain Concept".to_string(),
description: "Break down complex topics into simple explanations".to_string(),
category: "education".to_string(),
icon: "📚".to_string(),
icon: "".to_string(),
},
PromptData {
id: "debug-code".to_string(),
@ -545,7 +545,7 @@ fn get_prompts_data(category: &str) -> Vec<PromptData> {
title: "Meeting Notes".to_string(),
description: "Organize and format meeting discussions".to_string(),
category: "business".to_string(),
icon: "📋".to_string(),
icon: "".to_string(),
},
];
@ -571,13 +571,13 @@ fn get_templates_data() -> Vec<TemplateData> {
name: "FAQ Bot".to_string(),
description: "Answer frequently asked questions from your knowledge base".to_string(),
category: "Support".to_string(),
icon: "".to_string(),
icon: "".to_string(),
},
TemplateData {
name: "Lead Generation Bot".to_string(),
description: "Qualify leads and collect prospect information".to_string(),
category: "Sales".to_string(),
icon: "🎯".to_string(),
icon: "".to_string(),
},
TemplateData {
name: "Onboarding Bot".to_string(),
@ -589,13 +589,13 @@ fn get_templates_data() -> Vec<TemplateData> {
name: "Survey Bot".to_string(),
description: "Collect feedback through conversational surveys".to_string(),
category: "Research".to_string(),
icon: "📊".to_string(),
icon: "".to_string(),
},
TemplateData {
name: "Appointment Scheduler".to_string(),
description: "Book and manage appointments automatically".to_string(),
category: "Productivity".to_string(),
icon: "📅".to_string(),
icon: "".to_string(),
},
]
}

View file

@ -1371,12 +1371,12 @@ pub async fn handle_task_list_htmx(
<button class="action-btn edit-btn"
data-action="edit"
data-task-id="{}">
</button>
<button class="action-btn delete-btn"
data-action="delete"
data-task-id="{}">
🗑
</button>
</div>
</div>
@ -1392,7 +1392,7 @@ pub async fn handle_task_list_htmx(
},
if let Some(due) = &task.due_date {
format!(
r#"<span class="task-due-date">📅 {}</span>"#,
r#"<span class="task-due-date"> {}</span>"#,
due.format("%Y-%m-%d")
)
} else {

View file

@ -120,7 +120,7 @@ impl VectorDBIndexer {
*running = true;
drop(running);
info!("🚀 Starting Vector DB Indexer background service");
info!(" Starting Vector DB Indexer background service");
let indexer = Arc::clone(&self);
tokio::spawn(async move {
@ -148,7 +148,7 @@ impl VectorDBIndexer {
}
}
info!("🔄 Running vector DB indexing cycle...");
info!(" Running vector DB indexing cycle...");
// Get all active users
match self.get_active_users().await {
@ -166,7 +166,7 @@ impl VectorDBIndexer {
}
}
info!(" Indexing cycle complete");
info!(" Indexing cycle complete");
// Sleep until next cycle
sleep(Duration::from_secs(self.interval_seconds)).await;
@ -325,7 +325,7 @@ impl VectorDBIndexer {
if let Err(e) = email_db.index_email(&email, embedding).await {
error!("Failed to index email {}: {}", email.id, e);
} else {
info!(" Indexed email: {}", email.subject);
info!(" Indexed email: {}", email.subject);
}
}
Err(e) => {
@ -401,7 +401,7 @@ impl VectorDBIndexer {
if let Err(e) = drive_db.index_file(&file, embedding).await {
error!("Failed to index file {}: {}", file.id, e);
} else {
info!(" Indexed file: {}", file.file_name);
info!(" Indexed file: {}", file.file_name);
}
}
Err(e) => {
@ -531,7 +531,7 @@ impl VectorDBIndexer {
/// Trigger immediate indexing for a user
pub async fn trigger_user_indexing(&self, user_id: Uuid, bot_id: Uuid) -> Result<()> {
info!("🔄 Triggering immediate indexing for user {}", user_id);
info!(" Triggering immediate indexing for user {}", user_id);
self.index_user_data(user_id, bot_id).await
}
}