From 766aa76b71f7d6c8da37248f542b1bcbdebca9f2 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Thu, 26 Feb 2026 18:20:54 +0800 Subject: [PATCH] feat: implement multi-source weather data collection for OpenWeatherMap, Visual Crossing, and NOAA METAR, including Polymarket settlement sources. --- bot_listener.py | 87 ++++++++++++++++++++------ src/data_collection/weather_sources.py | 19 ++++++ 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/bot_listener.py b/bot_listener.py index bb7d079f..851fae9a 100644 --- a/bot_listener.py +++ b/bot_listener.py @@ -201,12 +201,51 @@ def analyze_weather_trend(weather_data, temp_symbol): # === 核心判断:实测是否已超预报 === is_breakthrough = False + + # METAR 趋势分析(最近 3-4 条报文) + recent_temps = metar.get("recent_temps", []) # [("15:00", 5), ("14:20", 5), ("14:00", 3)] 倒序 + trend_desc = "" + if len(recent_temps) >= 2: + temps_only = [t for _, t in recent_temps] # 倒序:最新在前 + latest_val = temps_only[0] + prev_val = temps_only[1] + diff = latest_val - prev_val + + if len(temps_only) >= 3: + # 3 条以上:判断整体趋势 + all_same = all(t == latest_val for t in temps_only[:3]) + all_rising = all(temps_only[i] >= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1)) + all_falling = all(temps_only[i] <= temps_only[i+1] for i in range(min(3, len(temps_only)) - 1)) + + trend_display = " → ".join([f"{t}{temp_symbol}@{tm}" for tm, t in recent_temps[:3]]) + + if all_same: + trend_desc = f"📉 温度已停滞({trend_display}),大概率到顶。" + elif all_rising and diff > 0: + trend_desc = f"📈 仍在升温({trend_display})。" + elif all_falling and diff < 0: + trend_desc = f"📉 已开始降温({trend_display})。" + else: + trend_desc = f"📊 温度波动中({trend_display})。" + elif diff == 0: + trend_desc = f"📉 温度持平(最近两条都是 {latest_val}{temp_symbol})。" + elif diff > 0: + trend_desc = f"📈 仍在升温({prev_val} → {latest_val}{temp_symbol})。" + else: + trend_desc = f"📉 已开始降温({prev_val} → {latest_val}{temp_symbol})。" + if max_so_far is not None and forecast_high is not None: if max_so_far > forecast_high + 0.5: is_breakthrough = True exceed_by = max_so_far - forecast_high - insights.append(f"🚨 实测已超预报:实测最高 {max_so_far}{temp_symbol} 超过了所有预报的天花板 {forecast_high}{temp_symbol},多了 {exceed_by:.1f}°!") - insights.append(f"💡 建议:预报已经不准了,实际温度比所有模型预测的都高,需要重新判断。") + # 合并为一条:事实 + 趋势(不给主观建议) + bt_msg = ( + f"🚨 实测已超预报:{max_so_far}{temp_symbol} 超过预报上限 " + f"{forecast_high}{temp_symbol}(+{exceed_by:.1f}°)。" + ) + if trend_desc: + bt_msg += f"\n{trend_desc}" + insights.append(bt_msg) # === 结算取整分析 (Wunderground 四舍五入到整数) === if max_so_far is not None: @@ -387,26 +426,38 @@ def analyze_weather_trend(weather_data, temp_symbol): if analysis_wind is not None: wd = analysis_wind + # 用趋势数据判断风的实际影响 + if trend_desc: + # 从趋势中提取关键词 + is_stagnant = "停滞" in trend_desc or "持平" in trend_desc + is_rising = "升温" in trend_desc + is_falling = "降温" in trend_desc + else: + is_stagnant = False + is_rising = False + is_falling = False + if 315 <= wd or wd <= 45: - insights.append(f"🌬️ 吹北风({wind_source} {wd:.0f}°):从北方来的冷空气,会压制升温。") + effect = "" + if is_stagnant or is_falling: + effect = ",升温确实被压制" + elif is_rising: + effect = ",但温度仍在上升" + insights.append(f"🌬️ 吹北风({wind_source} {wd:.0f}°):冷空气{effect}。") elif 135 <= wd <= 225: - gap_to_forecast = forecast_high - (max_so_far if max_so_far is not None else curr_temp) - if is_peak_passed and not is_breakthrough: - insights.append(f"🔥 吹南风({wind_source} {wd:.0f}°):南方的暖空气还在吹过来,但最热时段已过,后劲不足了。") - elif gap_to_forecast > 0.5 or is_breakthrough: - status = "温度还有继续上涨的空间" if not is_breakthrough else "可能把温度推得更高" - insights.append(f"🔥 吹南风({wind_source} {wd:.0f}°):南方的暖空气正在吹过来,{status}。") - else: - insights.append(f"🔥 吹南风({wind_source} {wd:.0f}°):南方的暖空气正在吹过来,但温度已接近预报峰值。") + effect = "" + if is_rising: + effect = ",温度仍在上升" + elif is_stagnant: + effect = ",但温度已停滞" + elif is_falling: + effect = ",但温度已开始下降" + insights.append(f"🔥 吹南风({wind_source} {wd:.0f}°):暖空气{effect}。") elif 225 < wd < 315: - if wd <= 260: - insights.append(f"🌬️ 吹西南风({wind_source} {wd:.0f}°):带有一定暖湿气流,对升温有轻微帮助。") - elif wd >= 280: - insights.append(f"🌬️ 吹西北风({wind_source} {wd:.0f}°):偏冷的气流,会拖慢升温。") - else: - insights.append(f"🌬️ 吹西风({wind_source} {wd:.0f}°):对温度影响不大,主要取决于日照和云量。") + dir_name = "西南风" if wd <= 260 else ("西北风" if wd >= 280 else "西风") + insights.append(f"🌬️ 吹{dir_name}({wind_source} {wd:.0f}°)。") elif 45 < wd < 135: - insights.append(f"🌬️ 吹东风({wind_source} {wd:.0f}°):对温度影响较小,主要看日照和云量。") + insights.append(f"🌬️ 吹东风({wind_source} {wd:.0f}°)。") except (TypeError, ValueError): pass diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py index 9d272e78..a15b6194 100644 --- a/src/data_collection/weather_sources.py +++ b/src/data_collection/weather_sources.py @@ -279,17 +279,35 @@ class WeatherDataCollector: except: continue + # 3. 提取最近 4 条报文的温度(用于趋势分析) + recent_temps_raw = [] # [(local_time_str, temp_c), ...] + for obs in data[:4]: # data 已按时间倒序 + obs_rt = obs.get("reportTime", "") + obs_temp = obs.get("temp") + if obs_temp is not None: + try: + ct = obs_rt.replace(" ", "T") + if not ct.endswith("Z"): ct += "Z" + rdt = datetime.fromisoformat(ct.replace("Z", "+00:00")) + local_rt = rdt + timedelta(seconds=utc_offset) + recent_temps_raw.append((local_rt.strftime("%H:%M"), obs_temp)) + except: + continue + # 转换为单位 if use_fahrenheit: temp = temp_c * 9 / 5 + 32 if temp_c is not None else None max_so_far = max_so_far_c * 9 / 5 + 32 if max_so_far_c > -900 else None dewp = dewp_c * 9 / 5 + 32 if dewp_c is not None else None unit = "fahrenheit" + # 转换最近温度 + recent_temps = [(t, round(v * 9 / 5 + 32, 1)) for t, v in recent_temps_raw] else: temp = temp_c max_so_far = max_so_far_c if max_so_far_c > -900 else None dewp = dewp_c unit = "celsius" + recent_temps = [(t, v) for t, v in recent_temps_raw] result = { "source": "metar", @@ -310,6 +328,7 @@ class WeatherDataCollector: "altimeter": latest.get("altim"), "clouds": latest.get("clouds", []), }, + "recent_temps": recent_temps, # 最近4条: [("15:00", 5), ("14:20", 5), ...] "unit": unit, }