181 lines
5.3 KiB
C#
181 lines
5.3 KiB
C#
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<LauncherItem>? _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<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
if (_cache != null && DateTime.Now < _cacheExpiry)
|
|
{
|
|
return _cache;
|
|
}
|
|
if (_def.Request == null || _def.Response == null)
|
|
{
|
|
return Enumerable.Empty<LauncherItem>();
|
|
}
|
|
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<LauncherItem>(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<LauncherItem> 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<LauncherItem>(new LauncherItem("네트워크 오류", "연결을 확인하세요", null, null));
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
LogService.Error("[" + _def.Name + "] API 호출 실패: " + ex2.Message);
|
|
return new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(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<LauncherItem> ParseResults(string json)
|
|
{
|
|
List<LauncherItem> list = new List<LauncherItem>();
|
|
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;
|
|
}
|
|
}
|