init weather plugin
This commit is contained in:
456
Main.cs
Normal file
456
Main.cs
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using Wox.Plugin;
|
||||||
|
|
||||||
|
namespace Wox.Plugin.Weather
|
||||||
|
{
|
||||||
|
public class Main : IPlugin
|
||||||
|
{
|
||||||
|
private PluginInitContext _context;
|
||||||
|
private const string API_KEY = "f4aeaa37939b47aa864a6e1b3ad4a084";
|
||||||
|
private const string API_HOST = "nm6hewkf7w.re.qweatherapi.com";
|
||||||
|
|
||||||
|
public void Init(PluginInitContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
LogInfo("Weather Plugin initialized with built-in API key");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Result> Query(Query query)
|
||||||
|
{
|
||||||
|
List<Result> resultList = new List<Result>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string cityName;
|
||||||
|
if (string.IsNullOrEmpty(query.Search))
|
||||||
|
{
|
||||||
|
// 如果没有输入城市名称,通过IP获取当前位置
|
||||||
|
cityName = GetCityFromIP();
|
||||||
|
if (string.IsNullOrEmpty(cityName))
|
||||||
|
{
|
||||||
|
cityName = "北京"; // 如果IP定位失败,使用默认城市
|
||||||
|
LogInfo("IP定位失败,使用默认城市:北京");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("通过IP定位获取到城市:{0}", cityName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cityName = query.Search;
|
||||||
|
}
|
||||||
|
LogInfo(string.Format("Weather query started for city: {0}", cityName));
|
||||||
|
var weatherData = GetWeatherData(cityName);
|
||||||
|
if (weatherData != null)
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("Weather data retrieved successfully for {0}: {1}℃, {2}", cityName, weatherData.Temperature, weatherData.Description));
|
||||||
|
resultList.Add(new Result
|
||||||
|
{
|
||||||
|
Title = string.Format("{0}, 实时: {1}℃, {2}", weatherData.City, weatherData.Temperature, weatherData.Description),
|
||||||
|
SubTitle = string.Format("湿度: {0}%, 风速: {1}km/h", weatherData.Humidity, weatherData.WindSpeed),
|
||||||
|
IcoPath = string.Format("Images\\weatherpic\\{0}.png", weatherData.IconCode),
|
||||||
|
Action = e => false
|
||||||
|
});
|
||||||
|
foreach (var forecast in weatherData.Forecasts)
|
||||||
|
{
|
||||||
|
string dayPrefix = GetDayPrefix(forecast.Date);
|
||||||
|
resultList.Add(new Result
|
||||||
|
{
|
||||||
|
Title = string.Format("{0}, {1}-{2}℃", forecast.Description, forecast.MaxTemp, forecast.MinTemp),
|
||||||
|
SubTitle = string.Format("{0}{1}, {2}", dayPrefix, GetWeekDay(forecast.Date.DayOfWeek), forecast.Date.ToShortDateString()),
|
||||||
|
IcoPath = string.Format("Images\\weatherpic\\{0}.png", forecast.IconCode),
|
||||||
|
Action = e => false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError(string.Format("Failed to retrieve weather data for city: {0}", cityName));
|
||||||
|
resultList.Add(new Result
|
||||||
|
{
|
||||||
|
Title = string.Format("无法获取 {0} 的天气信息", cityName),
|
||||||
|
SubTitle = "请检查城市名称是否正确,或稍后重试",
|
||||||
|
IcoPath = "Images\\logo.png",
|
||||||
|
Action = e => false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogError(string.Format("Exception in Query method: {0}", ex.Message));
|
||||||
|
resultList.Add(new Result
|
||||||
|
{
|
||||||
|
Title = "请输入城市名称或者拼音",
|
||||||
|
SubTitle = "API调用失败,请稍后重试",
|
||||||
|
IcoPath = "Images\\logo.png",
|
||||||
|
Action = e => false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WeatherData GetWeatherData(string cityName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("Getting location ID for city: {0}", cityName));
|
||||||
|
string locationId = GetLocationId(cityName, API_KEY);
|
||||||
|
if (string.IsNullOrEmpty(locationId))
|
||||||
|
{
|
||||||
|
LogError(string.Format("Failed to get location ID for city: {0}", cityName));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LogInfo(string.Format("Location ID found: {0} for city: {1}", locationId, cityName));
|
||||||
|
|
||||||
|
string currentWeatherUrl = string.Format("https://{0}/v7/weather/now?location={1}&key={2}", API_HOST, locationId, API_KEY);
|
||||||
|
LogInfo(string.Format("Requesting current weather from: {0}", currentWeatherUrl.Replace(API_KEY, "***")));
|
||||||
|
string currentResponse = MakeHttpRequest(currentWeatherUrl);
|
||||||
|
|
||||||
|
string forecastUrl = string.Format("https://{0}/v7/weather/7d?location={1}&key={2}", API_HOST, locationId, API_KEY);
|
||||||
|
LogInfo(string.Format("Requesting forecast from: {0}", forecastUrl.Replace(API_KEY, "***")));
|
||||||
|
string forecastResponse = MakeHttpRequest(forecastUrl);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(currentResponse) && !string.IsNullOrEmpty(forecastResponse))
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("API responses received - Current: {0} chars, Forecast: {1} chars", currentResponse.Length, forecastResponse.Length));
|
||||||
|
|
||||||
|
// 检查当前天气响应是否为有效JSON
|
||||||
|
if (!currentResponse.TrimStart().StartsWith("{") && !currentResponse.TrimStart().StartsWith("["))
|
||||||
|
{
|
||||||
|
LogError(string.Format("Current weather response is not valid JSON. First 200 chars: {0}",
|
||||||
|
currentResponse.Length > 200 ? currentResponse.Substring(0, 200) : currentResponse));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查预报响应是否为有效JSON
|
||||||
|
if (!forecastResponse.TrimStart().StartsWith("{") && !forecastResponse.TrimStart().StartsWith("["))
|
||||||
|
{
|
||||||
|
LogError(string.Format("Forecast response is not valid JSON. First 200 chars: {0}",
|
||||||
|
forecastResponse.Length > 200 ? forecastResponse.Substring(0, 200) : forecastResponse));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentJson = JObject.Parse(currentResponse);
|
||||||
|
var forecastJson = JObject.Parse(forecastResponse);
|
||||||
|
|
||||||
|
string currentCode = (string)currentJson["code"];
|
||||||
|
string forecastCode = (string)forecastJson["code"];
|
||||||
|
LogInfo(string.Format("API response codes - Current: {0}, Forecast: {1}", currentCode, forecastCode));
|
||||||
|
|
||||||
|
if (currentCode != "200" || forecastCode != "200")
|
||||||
|
{
|
||||||
|
LogError(string.Format("API returned error codes - Current: {0}, Forecast: {1}", currentCode, forecastCode));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var current = currentJson["now"];
|
||||||
|
var dailyForecasts = forecastJson["daily"];
|
||||||
|
var weatherData = new WeatherData();
|
||||||
|
weatherData.City = cityName;
|
||||||
|
weatherData.Temperature = (int)current["temp"];
|
||||||
|
weatherData.Description = (string)current["text"];
|
||||||
|
weatherData.Humidity = (int)current["humidity"];
|
||||||
|
weatherData.WindSpeed = (int)current["windSpeed"];
|
||||||
|
weatherData.IconCode = GetHeFengIconCode((string)current["icon"]);
|
||||||
|
foreach (var day in dailyForecasts.Take(5))
|
||||||
|
{
|
||||||
|
var forecast = new ForecastData();
|
||||||
|
forecast.Date = DateTime.Parse((string)day["fxDate"]);
|
||||||
|
forecast.MaxTemp = (int)day["tempMax"];
|
||||||
|
forecast.MinTemp = (int)day["tempMin"];
|
||||||
|
forecast.Description = (string)day["textDay"];
|
||||||
|
forecast.IconCode = GetHeFengIconCode((string)day["iconDay"]);
|
||||||
|
weatherData.Forecasts.Add(forecast);
|
||||||
|
}
|
||||||
|
return weatherData;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError("Empty response received from weather API");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogError(string.Format("Exception in GetWeatherData: {0}", ex.Message));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetLocationId(string cityName, string apiKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 根据公告,geoapi需要特殊处理:域名替换为API Host,路径从v2改为geo/v2
|
||||||
|
string searchUrl = string.Format("https://{0}/geo/v2/city/lookup?location={1}&key={2}", API_HOST, Uri.EscapeDataString(cityName), apiKey);
|
||||||
|
LogInfo(string.Format("Requesting location ID from: {0}", searchUrl.Replace(apiKey, "***")));
|
||||||
|
string response = MakeHttpRequest(searchUrl);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(response))
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("Location search response: {0} chars", response.Length));
|
||||||
|
|
||||||
|
// 记录响应内容的前100个字符用于调试
|
||||||
|
string responsePreview = response.Length > 100 ? response.Substring(0, 100) + "..." : response;
|
||||||
|
LogInfo(string.Format("Response preview: {0}", responsePreview));
|
||||||
|
|
||||||
|
// 检查响应是否看起来像JSON
|
||||||
|
if (!response.TrimStart().StartsWith("{") && !response.TrimStart().StartsWith("["))
|
||||||
|
{
|
||||||
|
LogError(string.Format("Response does not appear to be JSON. First 200 chars: {0}",
|
||||||
|
response.Length > 200 ? response.Substring(0, 200) : response));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JObject.Parse(response);
|
||||||
|
string code = (string)json["code"];
|
||||||
|
LogInfo(string.Format("Location search response code: {0}", code));
|
||||||
|
|
||||||
|
if (code == "200" && json["location"] != null && json["location"].HasValues)
|
||||||
|
{
|
||||||
|
string locationId = (string)json["location"][0]["id"];
|
||||||
|
LogInfo(string.Format("Location ID found: {0}", locationId));
|
||||||
|
return locationId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError(string.Format("Location search failed - Code: {0}, HasLocation: {1}", code, json["location"] != null && json["location"].HasValues));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError("Empty response from location search API");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogError(string.Format("Exception in GetLocationId: {0}", ex.Message));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCityFromIP()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogInfo("开始通过IP获取城市位置");
|
||||||
|
// 使用免费的ip-api.com服务获取IP位置信息
|
||||||
|
string ipApiUrl = "http://ip-api.com/json/?lang=zh-CN&fields=status,message,country,regionName,city";
|
||||||
|
LogInfo(string.Format("请求IP定位API: {0}", ipApiUrl));
|
||||||
|
|
||||||
|
string response = MakeHttpRequest(ipApiUrl);
|
||||||
|
if (!string.IsNullOrEmpty(response))
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("IP定位API响应: {0} chars", response.Length));
|
||||||
|
|
||||||
|
var json = JObject.Parse(response);
|
||||||
|
string status = (string)json["status"];
|
||||||
|
|
||||||
|
if (status == "success")
|
||||||
|
{
|
||||||
|
string country = (string)json["country"];
|
||||||
|
string region = (string)json["regionName"];
|
||||||
|
string city = (string)json["city"];
|
||||||
|
|
||||||
|
LogInfo(string.Format("IP定位成功 - 国家: {0}, 省份: {1}, 城市: {2}", country, region, city));
|
||||||
|
|
||||||
|
// 优先返回城市,如果没有城市则返回省份
|
||||||
|
if (!string.IsNullOrEmpty(city))
|
||||||
|
{
|
||||||
|
return city;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(region))
|
||||||
|
{
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError("IP定位成功但未获取到有效的城市或省份信息");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string message = (string)json["message"];
|
||||||
|
LogError(string.Format("IP定位失败 - Status: {0}, Message: {1}", status, message));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError("IP定位API返回空响应");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogError(string.Format("IP定位异常: {0}", ex.Message));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MakeHttpRequest(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("Making HTTP request to: {0}", url.Replace(API_KEY, "***")));
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
|
request.Method = "GET";
|
||||||
|
request.Timeout = 10000; // 增加超时时间到10秒
|
||||||
|
request.UserAgent = "Wox Weather Plugin/1.2.0";
|
||||||
|
|
||||||
|
// 明确设置接受的编码和内容类型
|
||||||
|
request.Accept = "application/json, text/plain, */*";
|
||||||
|
request.Headers.Add("Accept-Encoding", "gzip, deflate");
|
||||||
|
request.Headers.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
|
||||||
|
|
||||||
|
// 自动解压缩响应
|
||||||
|
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||||
|
|
||||||
|
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||||
|
{
|
||||||
|
LogInfo(string.Format("HTTP response received - Status: {0}, ContentLength: {1}, ContentEncoding: {2}, ContentType: {3}",
|
||||||
|
response.StatusCode, response.ContentLength, response.ContentEncoding, response.ContentType));
|
||||||
|
|
||||||
|
using (Stream responseStream = response.GetResponseStream())
|
||||||
|
{
|
||||||
|
// 根据响应头确定编码
|
||||||
|
Encoding encoding = Encoding.UTF8;
|
||||||
|
if (!string.IsNullOrEmpty(response.CharacterSet))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
encoding = Encoding.GetEncoding(response.CharacterSet);
|
||||||
|
LogInfo(string.Format("Using encoding from response header: {0}", response.CharacterSet));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
LogInfo("Failed to parse charset from response, using UTF-8");
|
||||||
|
encoding = Encoding.UTF8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (StreamReader reader = new StreamReader(responseStream, encoding))
|
||||||
|
{
|
||||||
|
string result = reader.ReadToEnd();
|
||||||
|
LogInfo(string.Format("HTTP response content length: {0} characters", result.Length));
|
||||||
|
|
||||||
|
// 记录响应的前200个字符用于调试
|
||||||
|
string debugPreview = result.Length > 200 ? result.Substring(0, 200) + "..." : result;
|
||||||
|
LogInfo(string.Format("Response content preview: {0}", debugPreview));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogError(string.Format("HTTP request failed for URL {0}: {1}", url.Replace(API_KEY, "***"), ex.Message));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetHeFengIconCode(string heFengIcon)
|
||||||
|
{
|
||||||
|
var codeMap = new Dictionary<string, string>();
|
||||||
|
codeMap.Add("100", "100"); codeMap.Add("150", "100");
|
||||||
|
codeMap.Add("101", "101"); codeMap.Add("102", "102"); codeMap.Add("103", "103");
|
||||||
|
codeMap.Add("151", "101"); codeMap.Add("152", "102"); codeMap.Add("153", "103");
|
||||||
|
codeMap.Add("104", "104"); codeMap.Add("300", "300"); codeMap.Add("301", "301");
|
||||||
|
codeMap.Add("302", "302"); codeMap.Add("303", "303"); codeMap.Add("304", "304");
|
||||||
|
codeMap.Add("305", "305"); codeMap.Add("306", "306"); codeMap.Add("307", "307");
|
||||||
|
codeMap.Add("308", "308"); codeMap.Add("309", "309"); codeMap.Add("310", "310");
|
||||||
|
codeMap.Add("400", "400"); codeMap.Add("401", "401"); codeMap.Add("402", "402");
|
||||||
|
codeMap.Add("403", "403"); codeMap.Add("404", "404"); codeMap.Add("500", "501");
|
||||||
|
return codeMap.ContainsKey(heFengIcon) ? codeMap[heFengIcon] : "999";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDayPrefix(DateTime date)
|
||||||
|
{
|
||||||
|
if (date.Date == DateTime.Today) return "今天, ";
|
||||||
|
else if (date.Date == DateTime.Today.AddDays(1)) return "明天, ";
|
||||||
|
else if (date.Date == DateTime.Today.AddDays(2)) return "后天, ";
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetWeekDay(DayOfWeek dayOfWeek)
|
||||||
|
{
|
||||||
|
string[] weekDays = { "周日", "周一", "周二", "周三", "周四", "周五", "周六" };
|
||||||
|
return weekDays[(int)dayOfWeek];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogInfo(string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WriteLog("INFO", message);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 如果日志记录失败,忽略错误以避免影响主要功能
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogError(string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WriteLog("ERROR", message);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 如果日志记录失败,忽略错误以避免影响主要功能
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteLog(string level, string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Wox", "Logs");
|
||||||
|
if (!Directory.Exists(logDir))
|
||||||
|
Directory.CreateDirectory(logDir);
|
||||||
|
|
||||||
|
string logFile = Path.Combine(logDir, "Weather.Plugin.log");
|
||||||
|
string logEntry = string.Format("[{0}] [{1}] {2}: {3}{4}",
|
||||||
|
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
level,
|
||||||
|
"Weather Plugin",
|
||||||
|
message,
|
||||||
|
Environment.NewLine);
|
||||||
|
|
||||||
|
File.AppendAllText(logFile, logEntry);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 如果日志记录失败,忽略错误以避免影响主要功能
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WeatherData
|
||||||
|
{
|
||||||
|
public WeatherData() { Forecasts = new List<ForecastData>(); }
|
||||||
|
public string City { get; set; }
|
||||||
|
public int Temperature { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public int Humidity { get; set; }
|
||||||
|
public int WindSpeed { get; set; }
|
||||||
|
public string IconCode { get; set; }
|
||||||
|
public List<ForecastData> Forecasts { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ForecastData
|
||||||
|
{
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
public int MaxTemp { get; set; }
|
||||||
|
public int MinTemp { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string IconCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
36
Properties/AssemblyInfo.cs
Normal file
36
Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Wox.Plugin.Weather")]
|
||||||
|
[assembly: AssemblyDescription("Weather plugin for Wox launcher")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Wox.Plugin.Weather")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("d2e3c23b-084d-411d-b66f-e0c79d6c2a6f")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.1.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.1.0.0")]
|
87
README.md
Normal file
87
README.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# Wox Weather Plugin - 和风天气版本(内置API Key)
|
||||||
|
|
||||||
|
这是一个使用和风天气API的Wox天气插件,使用内置的API Key,无需额外配置。
|
||||||
|
|
||||||
|
## 主要特性
|
||||||
|
|
||||||
|
### 1. 修复的问题
|
||||||
|
- ✅ 修复了项目GUID格式错误
|
||||||
|
- ✅ 修复了AssemblyInfo中的GUID格式错误
|
||||||
|
- ✅ 修复了Main.cs中的类型错误(List<Result>)
|
||||||
|
- ✅ 添加了缺失的using语句
|
||||||
|
- ✅ 移除了编译警告
|
||||||
|
|
||||||
|
### 2. API更换
|
||||||
|
- ✅ 从wttr.in API更换为和风天气API
|
||||||
|
- ✅ 支持实时天气和7天预报
|
||||||
|
- ✅ 完整的和风天气图标代码映射
|
||||||
|
- ✅ 城市搜索功能
|
||||||
|
|
||||||
|
### 3. 简化配置
|
||||||
|
- ✅ 使用内置API Key,无需配置
|
||||||
|
- ✅ 移除了所有外部配置文件读取
|
||||||
|
- ✅ 移除了Wox设置界面的配置选项
|
||||||
|
- ✅ 开箱即用,无需额外设置
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
- ✅ 移除了mock数据
|
||||||
|
- ✅ API调用失败时返回真实错误信息
|
||||||
|
- ✅ 友好的错误提示
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
- `Main.cs` - 主要插件代码,使用和风天气API
|
||||||
|
- `plugin.json` - Wox插件配置文件,包含设置界面定义
|
||||||
|
- `config.txt` - 配置文件,用于设置API Key
|
||||||
|
- `compile.bat` - 编译脚本
|
||||||
|
- `build.bat` - 带输出信息的编译脚本
|
||||||
|
- `verify.bat` - 验证编译结果的脚本
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
### 1. 编译插件
|
||||||
|
运行 `build.bat` 或 `compile.bat` 编译插件
|
||||||
|
|
||||||
|
### 2. 使用插件
|
||||||
|
在Wox中输入:
|
||||||
|
- `weather 北京` - 查询北京天气
|
||||||
|
- `天气 上海` - 查询上海天气
|
||||||
|
|
||||||
|
### 3. 无需配置
|
||||||
|
- 插件使用内置的和风天气API Key
|
||||||
|
- 无需注册账号或获取API Key
|
||||||
|
- 无需配置文件或设置
|
||||||
|
- 开箱即用
|
||||||
|
|
||||||
|
## 技术特性
|
||||||
|
|
||||||
|
- 支持中文城市名称和拼音
|
||||||
|
- 实时天气信息(温度、湿度、风速等)
|
||||||
|
- 7天天气预报
|
||||||
|
- 完整的天气图标映射
|
||||||
|
- 多级配置系统
|
||||||
|
- 错误处理和用户友好提示
|
||||||
|
- 符合原有设计模式
|
||||||
|
- 支持API Host配置,适配和风天气新域名政策
|
||||||
|
|
||||||
|
## API限制
|
||||||
|
|
||||||
|
- 免费版本:每天1000次调用
|
||||||
|
- 支持全球城市查询
|
||||||
|
- 实时天气 + 7天预报
|
||||||
|
- 建议用户注册自己的API Key和API Host以获得更好体验
|
||||||
|
|
||||||
|
## 重要提醒
|
||||||
|
|
||||||
|
根据和风天气2025年6月公告,公共API域名将逐步停止服务:
|
||||||
|
- `devapi.qweather.com` 将于2026年1月1日停止服务
|
||||||
|
- `api.qweather.com` 和 `geoapi.qweather.com` 将于2026年6月1日停止服务
|
||||||
|
|
||||||
|
**强烈建议用户尽快配置自己的专属API Host以确保服务正常使用。**
|
||||||
|
|
||||||
|
## 编译要求
|
||||||
|
|
||||||
|
- .NET Framework 4.8
|
||||||
|
- C# 编译器
|
||||||
|
- Newtonsoft.Json 库
|
||||||
|
- Wox.Plugin 库
|
67
Wox.Plugin.Weather.csproj
Normal file
67
Wox.Plugin.Weather.csproj
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{D2E3C23B-084D-411D-B66F-E0C79D6C2A6F}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Wox.Plugin.Weather</RootNamespace>
|
||||||
|
<AssemblyName>Wox.Plugin.Weather</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>lib\Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Wox.Plugin, Version=1.4.1196.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>lib\Wox.Plugin.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Main.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="lib\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<PostBuildEvent>
|
||||||
|
copy "$(TargetPath)" "$(ProjectDir)"
|
||||||
|
copy "$(TargetDir)$(TargetName).pdb" "$(ProjectDir)" 2>nul
|
||||||
|
copy "$(ProjectDir)lib\Newtonsoft.Json.dll" "$(ProjectDir)" 2>nul
|
||||||
|
copy "$(ProjectDir)lib\Wox.Plugin.dll" "$(ProjectDir)" 2>nul
|
||||||
|
</PostBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
BIN
Wox.Plugin.Weather.dll
Normal file
BIN
Wox.Plugin.Weather.dll
Normal file
Binary file not shown.
3
compile.bat
Normal file
3
compile.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" /target:library /reference:"lib\Wox.Plugin.dll" /reference:"lib\Newtonsoft.Json.dll" /out:"Wox.Plugin.Weather.dll" "Main.cs" "Properties\AssemblyInfo.cs"
|
||||||
|
pause
|
BIN
lib/Newtonsoft.Json.dll
Normal file
BIN
lib/Newtonsoft.Json.dll
Normal file
Binary file not shown.
BIN
lib/Wox.Plugin.dll
Normal file
BIN
lib/Wox.Plugin.dll
Normal file
Binary file not shown.
12
plugin.json
Normal file
12
plugin.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"ID": "Wox.Plugin.Weather",
|
||||||
|
"ActionKeywords": ["weather", "tq"],
|
||||||
|
"Name": "Weather",
|
||||||
|
"Description": "查询天气信息 - 使用和风天气API(内置API Key)",
|
||||||
|
"Author": "Wox Team",
|
||||||
|
"Version": "1.2.0",
|
||||||
|
"Language": "csharp",
|
||||||
|
"Website": "https://github.com/Wox-launcher/Wox",
|
||||||
|
"IcoPath": "Images\\logo.png",
|
||||||
|
"ExecuteFileName": "Wox.Plugin.Weather.dll"
|
||||||
|
}
|
Reference in New Issue
Block a user