using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
namespace AxCopilot.Handlers;
///
/// 루틴 자동화 핸들러. "routine" 프리픽스로 사용합니다.
/// 등록된 루틴을 실행하면 앱·폴더·URL을 순서대로 일괄 실행합니다.
/// 예: routine → 등록된 루틴 목록
/// routine morning → "morning" 루틴 실행
/// routine add morning → 루틴 추가 안내
/// 루틴 정의: %APPDATA%\AxCopilot\routines.json
///
public class RoutineHandler : IActionHandler
{
public string? Prefix => "routine";
public PluginMetadata Metadata => new(
"Routine",
"루틴 자동화 — routine",
"1.0",
"AX");
private static readonly string RoutineFile = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"AxCopilot", "routines.json");
private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true, PropertyNameCaseInsensitive = true };
// 내장 기본 루틴
private static readonly RoutineDefinition[] BuiltInRoutines =
[
new("morning", "출근 루틴", [
new("app", "explorer.exe", "파일 탐색기"),
new("info", "info", "시스템 정보 표시"),
]),
new("endofday", "퇴근 루틴", [
new("cmd", "journal", "오늘 업무 일지 생성"),
]),
];
public Task> GetItemsAsync(string query, CancellationToken ct)
{
var q = query.Trim();
var all = LoadRoutines();
if (string.IsNullOrWhiteSpace(q))
{
var items = new List
{
new("루틴 자동화",
$"총 {all.Count}개 루틴 · 이름 입력 시 실행 · routines.json에서 편집",
null, null, Symbol: Symbols.Info)
};
foreach (var r in all)
{
var steps = string.Join(" → ", r.Steps.Select(s => s.Label));
items.Add(new LauncherItem(
$"[{r.Name}] {r.Description}",
$"{r.Steps.Length}단계: {steps} · Enter로 실행",
null, r,
Symbol: Symbols.Lightbulb));
}
return Task.FromResult>(items);
}
// 루틴 검색/실행
var match = all.FirstOrDefault(r =>
r.Name.Equals(q, StringComparison.OrdinalIgnoreCase));
if (match != null)
{
var steps = string.Join(" → ", match.Steps.Select(s => s.Label));
return Task.FromResult>(
[
new LauncherItem(
$"[{match.Name}] 루틴 실행",
$"{match.Description} · {steps}",
null, match,
Symbol: Symbols.Lightbulb)
]);
}
// 부분 매칭
var filtered = all.Where(r =>
r.Name.Contains(q, StringComparison.OrdinalIgnoreCase) ||
r.Description.Contains(q, StringComparison.OrdinalIgnoreCase));
var result = filtered.Select(r => new LauncherItem(
$"[{r.Name}] {r.Description}",
$"{r.Steps.Length}단계 · Enter로 실행",
null, r,
Symbol: Symbols.Lightbulb)).ToList();
if (!result.Any())
{
result.Add(new LauncherItem(
$"'{q}' 루틴 없음",
$"routines.json에서 직접 추가하거나 routine으로 목록 확인",
null, null, Symbol: Symbols.Warning));
}
return Task.FromResult>(result);
}
public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
if (item.Data is not RoutineDefinition routine) return;
int executed = 0;
foreach (var step in routine.Steps)
{
try
{
switch (step.Type.ToLowerInvariant())
{
case "app":
case "url":
case "folder":
Process.Start(new ProcessStartInfo(step.Target) { UseShellExecute = true });
break;
case "cmd":
// PowerShell 명령 실행
Process.Start(new ProcessStartInfo("powershell.exe", $"-Command \"{step.Target}\"")
{ UseShellExecute = false, CreateNoWindow = true });
break;
case "info":
// 알림으로 대체
NotificationService.Notify("루틴", step.Label);
break;
}
executed++;
await Task.Delay(300, ct); // 앱 간 간격
}
catch (Exception ex)
{
LogService.Warn($"루틴 단계 실행 실패: {step.Label} — {ex.Message}");
}
}
NotificationService.Notify("루틴 완료", $"[{routine.Name}] {executed}/{routine.Steps.Length}단계 실행 완료");
}
private List LoadRoutines()
{
var list = new List(BuiltInRoutines);
try
{
if (File.Exists(RoutineFile))
{
var json = File.ReadAllText(RoutineFile);
var user = JsonSerializer.Deserialize>(json, JsonOpts);
if (user != null)
{
// 사용자 루틴이 내장 루틴을 오버라이드
foreach (var r in user)
{
list.RemoveAll(x => x.Name.Equals(r.Name, StringComparison.OrdinalIgnoreCase));
list.Add(r);
}
}
}
}
catch (Exception ex) { LogService.Warn($"루틴 로드 실패: {ex.Message}"); }
return list;
}
internal record RoutineDefinition(
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("steps")] RoutineStep[] Steps);
internal record RoutineStep(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("target")] string Target,
[property: JsonPropertyName("label")] string Label);
}