202 lines
4.7 KiB
C#
202 lines
4.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Windows;
|
|
using System.Windows.Threading;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
public class AgentTriggerService : IDisposable
|
|
{
|
|
private readonly SettingsService _settings;
|
|
|
|
private FileSystemWatcher? _fileWatcher;
|
|
|
|
private DispatcherTimer? _scheduleTimer;
|
|
|
|
private readonly List<TriggerRule> _rules = new List<TriggerRule>();
|
|
|
|
private bool _disposed;
|
|
|
|
public Action<string, string>? OnTriggerFired { get; set; }
|
|
|
|
public IReadOnlyList<TriggerRule> Rules => _rules;
|
|
|
|
public AgentTriggerService(SettingsService settings)
|
|
{
|
|
_settings = settings;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
LoadRules();
|
|
StartFileWatcher();
|
|
StartScheduleTimer();
|
|
LogService.Info($"에이전트 트리거 서비스 시작: {_rules.Count}개 규칙");
|
|
}
|
|
|
|
public void Reload()
|
|
{
|
|
Stop();
|
|
Start();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_fileWatcher?.Dispose();
|
|
_fileWatcher = null;
|
|
DispatcherTimer? scheduleTimer = _scheduleTimer;
|
|
if (scheduleTimer != null)
|
|
{
|
|
scheduleTimer.Stop();
|
|
}
|
|
}
|
|
|
|
private void LoadRules()
|
|
{
|
|
_rules.Clear();
|
|
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "triggers.json");
|
|
if (!File.Exists(path))
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
string json = File.ReadAllText(path);
|
|
List<TriggerRule> list = JsonSerializer.Deserialize<List<TriggerRule>>(json);
|
|
if (list != null)
|
|
{
|
|
_rules.AddRange(list.Where((TriggerRule r) => r.Enabled));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogService.Warn("트리거 규칙 로드 실패: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private void StartFileWatcher()
|
|
{
|
|
List<TriggerRule> fileRules = _rules.Where((TriggerRule r) => r.Type == "file_change").ToList();
|
|
if (fileRules.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
string workFolder = _settings.Settings.Llm.WorkFolder;
|
|
if (string.IsNullOrEmpty(workFolder) || !Directory.Exists(workFolder))
|
|
{
|
|
return;
|
|
}
|
|
_fileWatcher = new FileSystemWatcher(workFolder)
|
|
{
|
|
IncludeSubdirectories = true,
|
|
NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite),
|
|
EnableRaisingEvents = true
|
|
};
|
|
DateTime lastTrigger = DateTime.MinValue;
|
|
_fileWatcher.Changed += delegate(object _, FileSystemEventArgs e)
|
|
{
|
|
if ((DateTime.Now - lastTrigger).TotalSeconds < 5.0)
|
|
{
|
|
return;
|
|
}
|
|
foreach (TriggerRule rule in fileRules)
|
|
{
|
|
if (MatchesPattern(e.FullPath, rule.Pattern))
|
|
{
|
|
lastTrigger = DateTime.Now;
|
|
string prompt = rule.Prompt.Replace("{file}", e.FullPath).Replace("{name}", e.Name ?? "");
|
|
Application current = Application.Current;
|
|
if (current != null)
|
|
{
|
|
((DispatcherObject)current).Dispatcher.BeginInvoke((Delegate)(Action)delegate
|
|
{
|
|
OnTriggerFired?.Invoke(rule.Name, prompt);
|
|
}, Array.Empty<object>());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private void StartScheduleTimer()
|
|
{
|
|
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0077: Expected O, but got Unknown
|
|
List<TriggerRule> scheduleRules = _rules.Where((TriggerRule r) => r.Type == "schedule").ToList();
|
|
if (scheduleRules.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
_scheduleTimer = new DispatcherTimer
|
|
{
|
|
Interval = TimeSpan.FromMinutes(1.0)
|
|
};
|
|
_scheduleTimer.Tick += delegate
|
|
{
|
|
DateTime now = DateTime.Now;
|
|
foreach (TriggerRule item in scheduleRules)
|
|
{
|
|
if (ShouldRunSchedule(item, now))
|
|
{
|
|
item.LastRun = now;
|
|
OnTriggerFired?.Invoke(item.Name, item.Prompt);
|
|
}
|
|
}
|
|
};
|
|
_scheduleTimer.Start();
|
|
}
|
|
|
|
private static bool MatchesPattern(string filePath, string? pattern)
|
|
{
|
|
if (string.IsNullOrEmpty(pattern))
|
|
{
|
|
return true;
|
|
}
|
|
if (pattern.StartsWith("*."))
|
|
{
|
|
return filePath.EndsWith(pattern.Substring(1, pattern.Length - 1), StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
return filePath.Contains(pattern, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private static bool ShouldRunSchedule(TriggerRule rule, DateTime now)
|
|
{
|
|
if (rule.LastRun.HasValue && (now - rule.LastRun.Value).TotalMinutes < (double)(rule.IntervalMinutes ?? 60))
|
|
{
|
|
return false;
|
|
}
|
|
if (rule.ActiveHourStart.HasValue && now.Hour < rule.ActiveHourStart.Value)
|
|
{
|
|
return false;
|
|
}
|
|
if (rule.ActiveHourEnd.HasValue && now.Hour >= rule.ActiveHourEnd.Value)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void FireManual(string ruleName)
|
|
{
|
|
TriggerRule triggerRule = _rules.FirstOrDefault((TriggerRule r) => r.Name == ruleName);
|
|
if (triggerRule != null)
|
|
{
|
|
OnTriggerFired?.Invoke(triggerRule.Name, triggerRule.Prompt);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
_disposed = true;
|
|
Stop();
|
|
}
|
|
}
|
|
}
|