using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Threading; using AxCopilot.Models; using AxCopilot.SDK; using AxCopilot.Services; namespace AxCopilot.Handlers; public class ClipboardHandler : IActionHandler { private readonly SettingsService _settings; public string? Prefix => "$"; public PluginMetadata Metadata => new PluginMetadata("clipboard", "클립보드 변환", "1.0", "AX"); public ClipboardHandler(SettingsService settings) { _settings = settings; } public Task> GetItemsAsync(string query, CancellationToken ct) { List list = new List(); IEnumerable enumerable = from t in GetBuiltinTransformers() where string.IsNullOrEmpty(query) || t.Key.Contains(query, StringComparison.OrdinalIgnoreCase) select t; foreach (ClipboardTransformer item in enumerable) { list.Add(new LauncherItem(item.Key, item.Description ?? "", null, item, null, "\ue77f")); } IEnumerable collection = from t in _settings.Settings.ClipboardTransformers where string.IsNullOrEmpty(query) || t.Key.Contains(query, StringComparison.OrdinalIgnoreCase) select new LauncherItem(t.Key, t.Description ?? t.Type, null, t, null, "\ue77f"); list.AddRange(collection); return Task.FromResult((IEnumerable)list); } public async Task ExecuteAsync(LauncherItem item, CancellationToken ct) { object data = item.Data; if (!(data is ClipboardTransformer transformer)) { return; } string input = null; ((DispatcherObject)System.Windows.Application.Current).Dispatcher.Invoke((Action)delegate { input = (System.Windows.Clipboard.ContainsText() ? System.Windows.Clipboard.GetText() : null); }); if (input == null) { return; } string result = await TransformAsync(transformer, input, ct); if (result != null) { ((DispatcherObject)System.Windows.Application.Current).Dispatcher.Invoke((Action)delegate { System.Windows.Clipboard.SetText(result); }); nint prevHwnd = WindowTracker.PreviousWindow; if (prevHwnd != IntPtr.Zero) { SetForegroundWindow(prevHwnd); } await Task.Delay(120, ct); SendKeys.SendWait("^v"); LogService.Info("클립보드 변환: '" + transformer.Key + "' 적용"); } } private static async Task TransformAsync(ClipboardTransformer t, string input, CancellationToken ct) { try { string type = t.Type; if (1 == 0) { } string text = type; string result; if (!(text == "regex")) { if (!(text == "script") || t.Command == null) { goto IL_016f; } result = await RunScriptAsync(t.Command, input, t.Timeout, ct); } else { if (t.Pattern == null || t.Replace == null) { goto IL_016f; } result = Regex.Replace(input, t.Pattern, t.Replace, RegexOptions.None, TimeSpan.FromMilliseconds((t.Timeout > 0) ? t.Timeout : 5000)); } goto IL_0188; IL_016f: result = ExecuteBuiltin(t.Key, input); goto IL_0188; IL_0188: if (1 == 0) { } return result; } catch (Exception ex) { Exception ex2 = ex; LogService.Error("변환 실패 (" + t.Key + "): " + ex2.Message); return null; } } private static async Task RunScriptAsync(string command, string input, int timeoutMs, CancellationToken ct) { using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct); cts.CancelAfter(timeoutMs); string[] parts = command.Split(' ', 2); ProcessStartInfo psi = new ProcessStartInfo(parts[0]) { Arguments = ((parts.Length > 1) ? parts[1] : ""), RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8 }; using Process proc = Process.Start(psi); await proc.StandardInput.WriteAsync(input); proc.StandardInput.Close(); return await proc.StandardOutput.ReadToEndAsync(cts.Token); } internal static string? ExecuteBuiltin(string key, string input) { if (1 == 0) { } string result = key switch { "$json" => FormatJson(input), "$upper" => input.ToUpperInvariant(), "$lower" => input.ToLowerInvariant(), "$ts" => TryParseTimestamp(input), "$epoch" => TryParseDate(input), "$urle" => Uri.EscapeDataString(input), "$urld" => Uri.UnescapeDataString(input), "$b64e" => Convert.ToBase64String(Encoding.UTF8.GetBytes(input)), "$b64d" => Encoding.UTF8.GetString(Convert.FromBase64String(input)), "$md" => StripMarkdown(input), "$trim" => input.Trim(), "$lines" => string.Join(Environment.NewLine, from l in input.Split('\n') select l.Trim() into l where l.Length > 0 select l), _ => null, }; if (1 == 0) { } return result; } private static string FormatJson(string input) { try { JsonDocument value = JsonDocument.Parse(input); return JsonSerializer.Serialize(value, new JsonSerializerOptions { WriteIndented = true }); } catch { return input; } } private static string? TryParseTimestamp(string input) { if (long.TryParse(input.Trim(), out var result)) { return DateTimeOffset.FromUnixTimeSeconds(result).LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"); } return null; } private static string? TryParseDate(string input) { if (DateTime.TryParse(input.Trim(), out var result)) { return new DateTimeOffset(result).ToUnixTimeSeconds().ToString(); } return null; } private static string StripMarkdown(string input) { return Regex.Replace(input, "(\\*\\*|__)(.*?)\\1|(\\*|_)(.*?)\\3|`(.+?)`|#{1,6}\\s*", "$2$4$5"); } [DllImport("user32.dll")] private static extern bool SetForegroundWindow(nint hWnd); private static IEnumerable GetBuiltinTransformers() { return new _003C_003Ez__ReadOnlyArray(new ClipboardTransformer[12] { new ClipboardTransformer { Key = "$json", Type = "builtin", Description = "JSON 포맷팅 (들여쓰기 적용)" }, new ClipboardTransformer { Key = "$upper", Type = "builtin", Description = "대문자 변환" }, new ClipboardTransformer { Key = "$lower", Type = "builtin", Description = "소문자 변환" }, new ClipboardTransformer { Key = "$ts", Type = "builtin", Description = "유닉스 타임스탬프 → 날짜 문자열" }, new ClipboardTransformer { Key = "$epoch", Type = "builtin", Description = "날짜 문자열 → 유닉스 타임스탬프" }, new ClipboardTransformer { Key = "$urle", Type = "builtin", Description = "URL 인코딩" }, new ClipboardTransformer { Key = "$urld", Type = "builtin", Description = "URL 디코딩" }, new ClipboardTransformer { Key = "$b64e", Type = "builtin", Description = "Base64 인코딩" }, new ClipboardTransformer { Key = "$b64d", Type = "builtin", Description = "Base64 디코딩" }, new ClipboardTransformer { Key = "$md", Type = "builtin", Description = "마크다운 문법 제거" }, new ClipboardTransformer { Key = "$trim", Type = "builtin", Description = "앞뒤 공백 제거" }, new ClipboardTransformer { Key = "$lines", Type = "builtin", Description = "빈 줄 제거 및 각 줄 공백 정리" } }); } }