2025-10-06 10:30:17 -03:00
|
|
|
use crate::basic::ScriptService;
|
2025-11-05 21:10:03 -03:00
|
|
|
use crate::config::ConfigManager;
|
2025-10-06 14:55:04 -03:00
|
|
|
use crate::shared::models::{Automation, TriggerKind};
|
2025-10-06 10:30:17 -03:00
|
|
|
use crate::shared::state::AppState;
|
2025-11-05 21:10:03 -03:00
|
|
|
use chrono::Utc;
|
|
|
|
|
use cron::Schedule;
|
|
|
|
|
use diesel::prelude::*;
|
|
|
|
|
use log::{error, info};
|
|
|
|
|
use std::str::FromStr;
|
2025-10-11 20:02:14 -03:00
|
|
|
use std::sync::Arc;
|
2025-11-05 21:10:03 -03:00
|
|
|
use tokio::time::{interval, Duration};
|
2025-10-11 12:29:03 -03:00
|
|
|
|
2025-10-06 10:30:17 -03:00
|
|
|
pub struct AutomationService {
|
2025-11-05 21:10:03 -03:00
|
|
|
state: Arc<AppState>,
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AutomationService {
|
2025-11-03 08:23:03 -03:00
|
|
|
pub fn new(state: Arc<AppState>) -> Self {
|
2025-11-05 21:10:03 -03:00
|
|
|
Self { state }
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
pub async fn spawn(self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Automation service started");
|
2025-10-06 10:30:17 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let mut ticker = interval(Duration::from_secs(60));
|
2025-10-06 10:30:17 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
loop {
|
|
|
|
|
ticker.tick().await;
|
|
|
|
|
if let Err(e) = self.check_scheduled_tasks().await {
|
|
|
|
|
error!("Error checking scheduled tasks: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
async fn check_scheduled_tasks(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
use crate::shared::models::system_automations::dsl::{system_automations, is_active, kind, id, last_triggered as lt_column};
|
2025-10-11 20:02:14 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let mut conn = self.state.conn.lock().map_err(|e| format!("Failed to acquire lock: {}", e))?;
|
2025-10-11 20:02:14 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let automations: Vec<Automation> = system_automations
|
|
|
|
|
.filter(is_active.eq(true))
|
|
|
|
|
.filter(kind.eq(TriggerKind::Scheduled as i32))
|
|
|
|
|
.load::<Automation>(&mut *conn)?;
|
2025-10-06 10:30:17 -03:00
|
|
|
|
|
|
|
|
for automation in automations {
|
2025-11-05 21:10:03 -03:00
|
|
|
if let Some(schedule_str) = &automation.schedule {
|
|
|
|
|
if let Ok(parsed_schedule) = Schedule::from_str(schedule_str) {
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
let next_run = parsed_schedule.upcoming(Utc).next();
|
|
|
|
|
|
|
|
|
|
if let Some(next_time) = next_run {
|
|
|
|
|
let time_until_next = next_time - now;
|
|
|
|
|
if time_until_next.num_minutes() < 1 {
|
|
|
|
|
if let Some(last_triggered) = automation.last_triggered {
|
|
|
|
|
if (now - last_triggered).num_minutes() < 1 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.execute_automation(&automation).await?;
|
|
|
|
|
|
|
|
|
|
diesel::update(system_automations.filter(id.eq(automation.id)))
|
|
|
|
|
.set(lt_column.eq(Some(now)))
|
|
|
|
|
.execute(&mut *conn)?;
|
2025-11-02 20:57:53 -03:00
|
|
|
}
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
Ok(())
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
async fn execute_automation(&self, automation: &Automation) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Executing automation: {}", automation.param);
|
2025-10-16 14:22:28 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let config_manager = ConfigManager::new(Arc::clone(&self.state.conn));
|
2025-11-02 20:57:53 -03:00
|
|
|
let bot_name: String = {
|
2025-11-05 21:10:03 -03:00
|
|
|
use crate::shared::models::schema::bots::dsl::*;
|
|
|
|
|
let mut conn = self.state.conn.lock().map_err(|e| format!("Lock failed: {}", e))?;
|
|
|
|
|
bots.filter(id.eq(automation.bot_id))
|
2025-11-02 20:57:53 -03:00
|
|
|
.select(name)
|
2025-11-05 21:10:03 -03:00
|
|
|
.first(&mut *conn)?
|
2025-11-02 14:08:49 -03:00
|
|
|
};
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let script_path = format!("./work/{}.gbai/{}.gbdialog/{}.ast", bot_name, bot_name, automation.param);
|
|
|
|
|
|
|
|
|
|
let script_content = match tokio::fs::read_to_string(&script_path).await {
|
|
|
|
|
Ok(content) => content,
|
2025-10-11 20:25:08 -03:00
|
|
|
Err(e) => {
|
2025-11-05 21:10:03 -03:00
|
|
|
error!("Failed to read script {}: {}", script_path, e);
|
2025-11-02 20:57:53 -03:00
|
|
|
return Ok(());
|
2025-10-11 20:25:08 -03:00
|
|
|
}
|
|
|
|
|
};
|
2025-10-17 13:11:49 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let session = {
|
|
|
|
|
let mut sm = self.state.session_manager.lock().await;
|
|
|
|
|
let admin_user = uuid::Uuid::nil();
|
|
|
|
|
sm.get_or_create_user_session(admin_user, automation.bot_id, "Automation")?.ok_or("Failed to create session")?
|
2025-10-11 20:25:08 -03:00
|
|
|
};
|
2025-10-20 19:49:54 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
let script_service = ScriptService::new(Arc::clone(&self.state), session);
|
2025-10-11 20:25:08 -03:00
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
match script_service.compile(&script_content) {
|
|
|
|
|
Ok(ast) => {
|
|
|
|
|
if let Err(e) = script_service.run(&ast) {
|
|
|
|
|
error!("Script execution failed: {}", e);
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
2025-11-05 21:10:03 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Script compilation failed: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-16 14:22:28 -03:00
|
|
|
|
2025-11-02 20:57:53 -03:00
|
|
|
Ok(())
|
2025-10-16 14:22:28 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 21:10:03 -03:00
|
|
|
async fn execute_compact_prompt(&self, automation: &Automation) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Executing prompt compaction for bot: {}", automation.bot_id);
|
|
|
|
|
|
|
|
|
|
let config_manager = ConfigManager::new(Arc::clone(&self.state.conn));
|
|
|
|
|
let compact_threshold = config_manager
|
|
|
|
|
.get_config(&automation.bot_id, "prompt-compact", None)?
|
|
|
|
|
.parse::<usize>()
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
if compact_threshold == 0 {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut session_manager = self.state.session_manager.lock().await;
|
|
|
|
|
let sessions = session_manager.get_user_sessions(uuid::Uuid::nil())?;
|
|
|
|
|
|
|
|
|
|
for session in sessions {
|
|
|
|
|
if session.bot_id != automation.bot_id {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let history = session_manager.get_conversation_history(session.id, session.user_id)?;
|
|
|
|
|
|
|
|
|
|
if history.len() > compact_threshold {
|
|
|
|
|
info!("Compacting prompt for session {}: {} messages", session.id, history.len());
|
|
|
|
|
|
|
|
|
|
let mut compacted = String::new();
|
|
|
|
|
for (role, content) in &history[..history.len() - compact_threshold] {
|
|
|
|
|
compacted.push_str(&format!("{}: {}\n", role, content));
|
2025-10-16 14:22:28 -03:00
|
|
|
}
|
2025-11-05 21:10:03 -03:00
|
|
|
|
|
|
|
|
let summarized = format!("SUMMARY: {}", compacted);
|
|
|
|
|
|
|
|
|
|
session_manager.save_message(
|
|
|
|
|
session.id,
|
|
|
|
|
session.user_id,
|
|
|
|
|
3,
|
|
|
|
|
&summarized,
|
|
|
|
|
1
|
|
|
|
|
)?;
|
2025-10-16 14:22:28 -03:00
|
|
|
}
|
|
|
|
|
}
|
2025-11-05 21:10:03 -03:00
|
|
|
|
|
|
|
|
Ok(())
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
}
|