feat: implement multi-source weather data collection functionality with OpenWeatherMap, Visual Crossing, and METAR APIs.
This commit is contained in:
+30
-8
@@ -276,6 +276,8 @@ def start_bot():
|
||||
msg_lines.append(f"\n📊 <b>Open-Meteo 7天预测</b>")
|
||||
nws = weather_data.get("nws", {})
|
||||
nws_high = nws.get("today_high")
|
||||
mgm = weather_data.get("mgm", {})
|
||||
mgm_high = mgm.get("today_high")
|
||||
|
||||
for i, (d, t) in enumerate(zip(dates[:7], max_temps[:7])):
|
||||
# 跳过无效数据
|
||||
@@ -285,17 +287,37 @@ def start_bot():
|
||||
day_label = "今天" if d == city_today_str else d[5:]
|
||||
indicator = "👉 " if d == city_today_str else " "
|
||||
|
||||
# 如果是今天且有 NWS 数据,显示模型对比
|
||||
if d == city_today_str and nws_high is not None:
|
||||
diff = abs(t - nws_high)
|
||||
if diff > 1:
|
||||
msg_lines.append(f"{indicator}{day_label}: 最高 {t}{temp_symbol} ⚠️")
|
||||
msg_lines.append(f" (NWS官方预报: {nws_high}{temp_symbol},差异 {diff:.1f}°)")
|
||||
else:
|
||||
msg_lines.append(f"{indicator}{day_label}: 最高 {t}{temp_symbol} (NWS: {nws_high}{temp_symbol})")
|
||||
# 如果是今天且有 NWS 或 MGM 数据,显示模型对比
|
||||
comp_lines = []
|
||||
if d == city_today_str:
|
||||
if nws_high is not None:
|
||||
diff_nws = abs(t - nws_high)
|
||||
warning = " ⚠️" if diff_nws > 1 else ""
|
||||
comp_lines.append(f"{indicator}{day_label}: 最高 {t}{temp_symbol}{warning}")
|
||||
comp_lines.append(f" (NWS官方预报: {nws_high}{temp_symbol},差异 {diff_nws:.1f}°)")
|
||||
elif mgm_high is not None:
|
||||
# 安卡拉 MGM 对比
|
||||
diff_mgm = abs(t - mgm_high)
|
||||
warning = " ⚠️" if diff_mgm > 1 else ""
|
||||
comp_lines.append(f"{indicator}{day_label}: 最高 {t}{temp_symbol}{warning}")
|
||||
comp_lines.append(f" (MGM官方预报: {mgm_high}{temp_symbol},差异 {diff_mgm:.1f}°)")
|
||||
|
||||
if comp_lines:
|
||||
msg_lines.extend(comp_lines)
|
||||
else:
|
||||
msg_lines.append(f"{indicator}{day_label}: 最高 {t}{temp_symbol}")
|
||||
|
||||
# MGM 官方实测显示
|
||||
if mgm:
|
||||
mgm_curr = mgm.get("current", {})
|
||||
mgm_temp = mgm_curr.get("temp")
|
||||
if mgm_temp is not None:
|
||||
msg_lines.append(f"\n🏛️ <b>MGM 官方实测 ({mgm_curr.get('station_name', 'Ankara')})</b>")
|
||||
msg_lines.append(f" 🌡️ {mgm_temp}°C (湿度: {mgm_curr.get('nem', mgm_curr.get('humidity'))}%)")
|
||||
msg_lines.append(f" 💨 风速: {mgm_curr.get('wind_speed_kt')}kt")
|
||||
if mgm_curr.get("rain_24h"):
|
||||
msg_lines.append(f" 🌧️ 24h降水: {mgm_curr.get('rain_24h')}mm")
|
||||
|
||||
if metar:
|
||||
icao = metar.get("icao", "")
|
||||
metar_temp = metar.get("current", {}).get("temp")
|
||||
|
||||
@@ -319,6 +319,50 @@ class WeatherDataCollector:
|
||||
logger.error(f"METAR 数据解析失败 ({icao}): {e}")
|
||||
return None
|
||||
|
||||
def fetch_from_mgm(self, istno: str) -> Optional[Dict]:
|
||||
"""
|
||||
从土耳其气象局 (MGM) 获取实时数据和预测 (由用户提供其内部 API)
|
||||
"""
|
||||
base_url = "https://servis.mgm.gov.tr/web"
|
||||
# 必须带 Origin,否则会被反爬拦截
|
||||
headers = {
|
||||
"Origin": "https://www.mgm.gov.tr",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
}
|
||||
results = {}
|
||||
|
||||
try:
|
||||
# 1. 实时数据
|
||||
obs_resp = self.session.get(f"{base_url}/sondurumlar?istno={istno}", headers=headers, timeout=self.timeout)
|
||||
if obs_resp.status_code == 200:
|
||||
data = obs_resp.json()
|
||||
if data:
|
||||
latest = data[0] if isinstance(data, list) else data
|
||||
# MGM 数据字段映射
|
||||
results["current"] = {
|
||||
"temp": latest.get("sicaklik"),
|
||||
"humidity": latest.get("nem"),
|
||||
"wind_speed_kt": round(latest.get("ruzgarHiz", 0) * 1.94, 1) if latest.get("ruzgarHiz") is not None else None,
|
||||
"wind_dir": latest.get("ruzgarYon"),
|
||||
"rain_24h": latest.get("toplamYagis"),
|
||||
"time": latest.get("veriZamani"),
|
||||
"station_name": latest.get("istasyonAd")
|
||||
}
|
||||
|
||||
# 2. 每日预报
|
||||
daily_resp = self.session.get(f"{base_url}/tahminler/gunluk?istno={istno}", headers=headers, timeout=self.timeout)
|
||||
if daily_resp.status_code == 200:
|
||||
forecasts = daily_resp.json()
|
||||
if forecasts and isinstance(forecasts, list):
|
||||
today = forecasts[0]
|
||||
results["today_high"] = today.get("enYuksekGun1")
|
||||
results["today_low"] = today.get("enDusukGun1")
|
||||
|
||||
return results if "current" in results else None
|
||||
except Exception as e:
|
||||
logger.error(f"MGM API 请求失败 ({istno}): {e}")
|
||||
return None
|
||||
|
||||
def fetch_nws(self, lat: float, lon: float) -> Optional[Dict]:
|
||||
"""
|
||||
从 NWS (美国国家气象局) 获取高精度预报
|
||||
@@ -653,6 +697,12 @@ class WeatherDataCollector:
|
||||
if metar_data:
|
||||
results["metar"] = metar_data
|
||||
|
||||
# 对安卡拉,额外获取 MGM 官方数据
|
||||
if city_lower == "ankara":
|
||||
mgm_data = self.fetch_from_mgm("17128")
|
||||
if mgm_data:
|
||||
results["mgm"] = mgm_data
|
||||
|
||||
# 对美国城市,额外获取 NWS 高精预报
|
||||
if use_fahrenheit:
|
||||
nws_data = self.fetch_nws(lat, lon)
|
||||
|
||||
Reference in New Issue
Block a user