using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using AxCopilot.SDK; using AxCopilot.Services; namespace AxCopilot.Handlers; public class JsonSkillHandler : IActionHandler { private readonly JsonSkillDefinition _def; private readonly HttpClient _http = new HttpClient(); private List? _cache; private DateTime _cacheExpiry = DateTime.MinValue; public string? Prefix => _def.Prefix; public PluginMetadata Metadata => new PluginMetadata(_def.Id, _def.Name, _def.Version, "JSON Skill"); public JsonSkillHandler(JsonSkillDefinition def) { _def = def; _http.Timeout = TimeSpan.FromSeconds(3.0); if (def.Credential?.Type == "bearer_token") { string token = CredentialManager.GetToken(def.Credential.CredentialKey); if (!string.IsNullOrEmpty(token)) { _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); } } } public async Task> GetItemsAsync(string query, CancellationToken ct) { if (_cache != null && DateTime.Now < _cacheExpiry) { return _cache; } if (_def.Request == null || _def.Response == null) { return Enumerable.Empty(); } try { string url = _def.Request.Url.Replace("{{INPUT}}", Uri.EscapeDataString(query)); if (!Uri.TryCreate(url, UriKind.Absolute, out Uri parsedUrl) || (parsedUrl.Scheme != Uri.UriSchemeHttp && parsedUrl.Scheme != Uri.UriSchemeHttps)) { LogService.Error("[" + _def.Name + "] 유효하지 않은 URL: " + url); return new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("설정 오류", "스킬 URL이 유효하지 않습니다 (http/https만 허용)", null, null)); } string text = _def.Request.Method.ToUpper(); if (1 == 0) { } HttpResponseMessage httpResponseMessage = ((!(text == "POST")) ? (await _http.GetAsync(url, ct)) : (await _http.PostAsync(url, BuildBody(query), ct))); if (1 == 0) { } HttpResponseMessage response = httpResponseMessage; response.EnsureSuccessStatusCode(); List items = ParseResults(await response.Content.ReadAsStringAsync(ct)); JsonSkillCache? cache = _def.Cache; if (cache != null && cache.Ttl > 0) { _cache = items; _cacheExpiry = DateTime.Now.AddSeconds(_def.Cache.Ttl); } return items; } catch (TaskCanceledException) { if (_cache != null) { LogService.Warn("[" + _def.Name + "] API 타임아웃, 캐시 반환"); return _cache; } return new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("네트워크 오류", "연결을 확인하세요", null, null)); } catch (Exception ex2) { LogService.Error("[" + _def.Name + "] API 호출 실패: " + ex2.Message); return new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("오류: " + ex2.Message, _def.Name, null, null)); } } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.ActionUrl != null && Uri.TryCreate(item.ActionUrl, UriKind.Absolute, out Uri result) && (result.Scheme == Uri.UriSchemeHttp || result.Scheme == Uri.UriSchemeHttps)) { Process.Start(new ProcessStartInfo(item.ActionUrl) { UseShellExecute = true }); } return Task.CompletedTask; } private HttpContent BuildBody(string query) { string content = JsonSerializer.Serialize(_def.Request?.Body).Replace("\"{{INPUT}}\"", "\"" + query + "\""); return new StringContent(content, Encoding.UTF8, "application/json"); } private List ParseResults(string json) { List list = new List(); try { JsonNode jsonNode = JsonNode.Parse(json); if (jsonNode == null) { return list; } JsonNode jsonNode2 = NavigatePath(jsonNode, _def.Response.ResultsPath); if (!(jsonNode2 is JsonArray source)) { return list; } foreach (JsonNode item in source.Take(10)) { if (item != null) { string title = NavigatePath(item, _def.Response.TitleField)?.ToString() ?? "(제목 없음)"; string subtitle = ((_def.Response.SubtitleField == null) ? "" : (NavigatePath(item, _def.Response.SubtitleField)?.ToString() ?? "")); string actionUrl = ((_def.Response.ActionUrl == null) ? null : NavigatePath(item, _def.Response.ActionUrl)?.ToString()); list.Add(new LauncherItem(title, subtitle, null, item, actionUrl, "\ue82d")); } } } catch (Exception ex) { LogService.Error("[" + _def.Name + "] 응답 파싱 실패: " + ex.Message); } return list; } private static JsonNode? NavigatePath(JsonNode root, string path) { string[] array = path.Split('.'); JsonNode jsonNode = root; string[] array2 = array; foreach (string text in array2) { if (jsonNode == null) { return null; } int num = text.IndexOf('['); if (num >= 0) { int num2 = text.IndexOf(']'); if (num2 < 0) { return null; } string propertyName = text.Substring(0, num); int num3 = num + 1; int index = int.Parse(text.Substring(num3, num2 - num3)); jsonNode = jsonNode[propertyName]?[index]; } else { jsonNode = jsonNode[text]; } } return jsonNode; } }