feat: implement runway observations dashboard panel, add telegram alert utility, and create city payload service
This commit is contained in:
@@ -148,7 +148,7 @@ export function RunwayObservationsPanel() {
|
|||||||
async () => {
|
async () => {
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
RUNWAY_OBSERVATION_CITIES.map((city) =>
|
RUNWAY_OBSERVATION_CITIES.map((city) =>
|
||||||
ensureCityDetail(city.key, false, "panel"),
|
ensureCityDetail(city.key, true, "panel"),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -502,6 +502,7 @@ MARKET_MONITOR_CITIES = [
|
|||||||
|
|
||||||
_FUNCTION_HASHTAGS = {
|
_FUNCTION_HASHTAGS = {
|
||||||
"runway": "#跑道观测",
|
"runway": "#跑道观测",
|
||||||
|
"airport": "#机场观测",
|
||||||
"market": "#市场监控",
|
"market": "#市场监控",
|
||||||
"trade": "#交易机会",
|
"trade": "#交易机会",
|
||||||
}
|
}
|
||||||
@@ -714,10 +715,10 @@ def _build_airport_status_message(
|
|||||||
and latest_temp - max_so_far >= 0.3)
|
and latest_temp - max_so_far >= 0.3)
|
||||||
|
|
||||||
flag = " \U0001f536新" if new_high else ""
|
flag = " \U0001f536新" if new_high else ""
|
||||||
|
has_runway = bool(runway_pairs and runway_temps and len(runway_pairs) == len(runway_temps))
|
||||||
hashtag_line = _build_telegram_hashtag_line(
|
hashtag_line = _build_telegram_hashtag_line(
|
||||||
"runway",
|
"runway" if has_runway else "airport",
|
||||||
city=city,
|
city=city,
|
||||||
station=airport_icao,
|
|
||||||
)
|
)
|
||||||
lines = [hashtag_line, header + flag, ""]
|
lines = [hashtag_line, header + flag, ""]
|
||||||
runway_shown = False
|
runway_shown = False
|
||||||
@@ -1020,6 +1021,7 @@ def _run_market_monitor_cycle(bot: Any, chat_ids: List[str]) -> bool:
|
|||||||
sent_any = False
|
sent_any = False
|
||||||
try:
|
try:
|
||||||
from web.app import _analyze
|
from web.app import _analyze
|
||||||
|
from web.services.city_payloads import build_city_market_scan_payload
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("market monitor push skipped: analyze import failed: {}", exc)
|
logger.warning("market monitor push skipped: analyze import failed: {}", exc)
|
||||||
return False
|
return False
|
||||||
@@ -1027,9 +1029,11 @@ def _run_market_monitor_cycle(bot: Any, chat_ids: List[str]) -> bool:
|
|||||||
for city in MARKET_MONITOR_CITIES:
|
for city in MARKET_MONITOR_CITIES:
|
||||||
try:
|
try:
|
||||||
city_weather = _analyze(city)
|
city_weather = _analyze(city)
|
||||||
market = city_weather.get("market_scan") or {}
|
scan_payload = build_city_market_scan_payload(city_weather)
|
||||||
|
market = scan_payload.get("market_scan") or {}
|
||||||
if not market.get("available"):
|
if not market.get("available"):
|
||||||
continue
|
continue
|
||||||
|
city_weather["market_scan"] = market
|
||||||
message = _build_market_monitor_message(city, city_weather)
|
message = _build_market_monitor_message(city, city_weather)
|
||||||
for chat_id in chat_ids:
|
for chat_id in chat_ids:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ def build_city_detail_payload(
|
|||||||
"center_station_candidate": data.get("center_station_candidate"),
|
"center_station_candidate": data.get("center_station_candidate"),
|
||||||
"airport_vs_network_delta": data.get("airport_vs_network_delta"),
|
"airport_vs_network_delta": data.get("airport_vs_network_delta"),
|
||||||
"airport_current": data.get("airport_current") or {},
|
"airport_current": data.get("airport_current") or {},
|
||||||
|
"amos": data.get("amos") or {},
|
||||||
"nearby_source": data.get("nearby_source") or ("mgm" if str(data.get("name") or "").lower() in TURKISH_MGM_CITIES else "metar_cluster"),
|
"nearby_source": data.get("nearby_source") or ("mgm" if str(data.get("name") or "").lower() in TURKISH_MGM_CITIES else "metar_cluster"),
|
||||||
"ai_analysis": data.get("ai_analysis") or "",
|
"ai_analysis": data.get("ai_analysis") or "",
|
||||||
"errors": {},
|
"errors": {},
|
||||||
|
|||||||
Reference in New Issue
Block a user