Files
MAF1/MAF1.Core/Tools/WeatherTools.cs
T
2026-08-26 18:06:02 +08:00

101 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.ComponentModel;
using System.Net.Http.Headers;
using System.Text.Json;
namespace MAF1.Tools;
public sealed class WeatherTools(WeatherOptions options, HttpClient http)
{
[Description("Look up live weather for a city or location. Always call this instead of guessing.")]
public async Task<string> GetWeather(
[Description("City or location name, for example Amsterdam or 北京.")] string location)
{
if (string.IsNullOrWhiteSpace(location))
{
return "未提供地点,无法查询天气。";
}
string provider = options.Provider.Trim();
try
{
if (provider.Equals("OpenWeather", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(options.OpenWeather.ApiKey))
{
return "OpenWeather 已启用,但 appsettings.json 里 Weather:OpenWeather:ApiKey 为空。请填入密钥,或把 Provider 改回 Wttr。";
}
return await QueryOpenWeatherAsync(location);
}
return await QueryWttrAsync(location);
}
catch (Exception ex)
{
return $"查询天气失败({provider} / {location}):{ex.Message}";
}
}
private async Task<string> QueryWttrAsync(string location)
{
string url = Expand(options.Wttr.UrlTemplate, location, apiKey: null);
using HttpResponseMessage response = await http.GetAsync(url);
string body = (await response.Content.ReadAsStringAsync()).Trim();
if (!response.IsSuccessStatusCode)
{
return $"wttr.in 返回 {(int)response.StatusCode}{body}";
}
return string.IsNullOrWhiteSpace(body)
? $"wttr.in 没有返回 {location} 的天气。"
: body;
}
private async Task<string> QueryOpenWeatherAsync(string location)
{
string url = Expand(options.OpenWeather.UrlTemplate, location, options.OpenWeather.ApiKey);
using HttpResponseMessage response = await http.GetAsync(url);
string body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
return $"OpenWeather 返回 {(int)response.StatusCode}{body}";
}
using JsonDocument doc = JsonDocument.Parse(body);
JsonElement root = doc.RootElement;
string name = root.TryGetProperty("name", out JsonElement nameEl) ? nameEl.GetString() ?? location : location;
string description = root.TryGetProperty("weather", out JsonElement weather)
&& weather.ValueKind == JsonValueKind.Array
&& weather.GetArrayLength() > 0
&& weather[0].TryGetProperty("description", out JsonElement descEl)
? descEl.GetString() ?? ""
: "";
double temp = root.TryGetProperty("main", out JsonElement main) && main.TryGetProperty("temp", out JsonElement tempEl)
? tempEl.GetDouble()
: double.NaN;
int humidity = main.ValueKind == JsonValueKind.Object && main.TryGetProperty("humidity", out JsonElement humidityEl)
? humidityEl.GetInt32()
: 0;
return double.IsNaN(temp)
? body
: $"{name}{description},气温 {temp:0.#}°C,湿度 {humidity}%";
}
private string Expand(string template, string location, string? apiKey)
{
return template
.Replace("{location}", Uri.EscapeDataString(location), StringComparison.OrdinalIgnoreCase)
.Replace("{lang}", Uri.EscapeDataString(options.Language), StringComparison.OrdinalIgnoreCase)
.Replace("{apiKey}", apiKey ?? "", StringComparison.OrdinalIgnoreCase);
}
public static HttpClient CreateHttpClient()
{
HttpClient http = new() { Timeout = TimeSpan.FromSeconds(20) };
http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MAF1", "1.0"));
http.DefaultRequestHeaders.AcceptLanguage.ParseAdd("zh-CN,zh;q=0.9,en;q=0.8");
return http;
}
}