feat: add utility module for Telegram alert push notifications and state management
This commit is contained in:
@@ -488,7 +488,7 @@ def _alert_signature(alert_payload: Dict[str, Any]) -> str:
|
|||||||
|
|
||||||
# ── high-freq airport push loop ──
|
# ── high-freq airport push loop ──
|
||||||
|
|
||||||
HIGH_FREQ_AIRPORT_CITIES = {"seoul", "busan", "tokyo", "ankara", "helsinki", "amsterdam", "istanbul", "paris", "hong kong", "lau fau shan", "taipei"}
|
HIGH_FREQ_AIRPORT_CITIES = {"seoul", "busan", "tokyo", "ankara", "helsinki", "amsterdam", "istanbul", "paris", "hong kong", "lau fau shan", "taipei", "beijing", "shanghai", "guangzhou", "shenzhen", "qingdao", "chengdu", "chongqing", "wuhan"}
|
||||||
HIGH_FREQ_AIRPORT_ICAO = {"seoul": "RKSI", "busan": "RKPK", "tokyo": "44166", "ankara": "17128", "helsinki": "EFHK", "amsterdam": "EHAM", "istanbul": "17058", "paris": "LFPB", "hong kong": "HKO", "lau fau shan": "LFS", "taipei": "466920", "beijing": "ZBAA", "shanghai": "ZSPD", "guangzhou": "ZGGG", "shenzhen": "ZGSZ", "qingdao": "ZSQD", "chengdu": "ZUUU", "chongqing": "ZUCK", "wuhan": "ZHHH"}
|
HIGH_FREQ_AIRPORT_ICAO = {"seoul": "RKSI", "busan": "RKPK", "tokyo": "44166", "ankara": "17128", "helsinki": "EFHK", "amsterdam": "EHAM", "istanbul": "17058", "paris": "LFPB", "hong kong": "HKO", "lau fau shan": "LFS", "taipei": "466920", "beijing": "ZBAA", "shanghai": "ZSPD", "guangzhou": "ZGGG", "shenzhen": "ZGSZ", "qingdao": "ZSQD", "chengdu": "ZUUU", "chongqing": "ZUCK", "wuhan": "ZHHH"}
|
||||||
MARKET_MONITOR_INTERVAL_SEC = 300
|
MARKET_MONITOR_INTERVAL_SEC = 300
|
||||||
MARKET_MONITOR_CITIES = [
|
MARKET_MONITOR_CITIES = [
|
||||||
@@ -547,6 +547,19 @@ def _build_telegram_hashtag_line(
|
|||||||
return " ".join(tags)
|
return " ".join(tags)
|
||||||
|
|
||||||
|
|
||||||
|
def _fmt(value: Any) -> str:
|
||||||
|
"""Format a single temperature reading; returns '--' for missing values."""
|
||||||
|
if value is None:
|
||||||
|
return "--"
|
||||||
|
try:
|
||||||
|
f = float(value)
|
||||||
|
if not -80.0 < f < 80.0:
|
||||||
|
return "--"
|
||||||
|
return f"{f:.1f}"
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return "--"
|
||||||
|
|
||||||
|
|
||||||
def _format_percent(value: Any) -> str:
|
def _format_percent(value: Any) -> str:
|
||||||
try:
|
try:
|
||||||
numeric = float(value)
|
numeric = float(value)
|
||||||
@@ -577,7 +590,7 @@ def _build_market_monitor_message(city: str, city_weather: Dict[str, Any]) -> st
|
|||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
_build_telegram_hashtag_line("market", city=city),
|
_build_telegram_hashtag_line("market", city=city),
|
||||||
f"{city_label} 1分钟快报 {local_time}",
|
f"{city_label} {local_time}",
|
||||||
]
|
]
|
||||||
if current_temp is not None or deb_pred is not None:
|
if current_temp is not None or deb_pred is not None:
|
||||||
current_text = f"{float(current_temp):.1f}{temp_symbol}" if current_temp is not None else "--"
|
current_text = f"{float(current_temp):.1f}{temp_symbol}" if current_temp is not None else "--"
|
||||||
@@ -659,9 +672,9 @@ def _build_airport_status_message(
|
|||||||
"ankara": "Esenboğa", "helsinki": "Vantaa", "amsterdam": "Schiphol",
|
"ankara": "Esenboğa", "helsinki": "Vantaa", "amsterdam": "Schiphol",
|
||||||
"istanbul": "Airport", "paris": "Le Bourget",
|
"istanbul": "Airport", "paris": "Le Bourget",
|
||||||
"hong kong": "Observatory", "lau fau shan": "Lau Fau Shan",
|
"hong kong": "Observatory", "lau fau shan": "Lau Fau Shan",
|
||||||
"taipei": "Songshan", "beijing": "ZBAA", "shanghai": "ZSPD",
|
"taipei": "Songshan", "beijing": "Capital", "shanghai": "Pudong",
|
||||||
"guangzhou": "ZGGG", "shenzhen": "ZGSZ", "qingdao": "ZSQD",
|
"guangzhou": "Baiyun", "shenzhen": "Bao'an", "qingdao": "Jiaodong",
|
||||||
"chengdu": "ZUUU", "chongqing": "ZUCK", "wuhan": "ZHHH"}
|
"chengdu": "Shuangliu", "chongqing": "Jiangbei", "wuhan": "Tianhe"}
|
||||||
en_name = city.title()
|
en_name = city.title()
|
||||||
ap_name = _AIRPORT_EN.get(city, "")
|
ap_name = _AIRPORT_EN.get(city, "")
|
||||||
time_suffix = f" {local_time}" if local_time else ""
|
time_suffix = f" {local_time}" if local_time else ""
|
||||||
@@ -698,6 +711,7 @@ 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 ""
|
||||||
|
is_amsc = amos.get("source") == "amsc_awos"
|
||||||
has_runway = bool(runway_pairs and runway_temps and len(runway_pairs) == len(runway_temps))
|
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" if has_runway else "airport",
|
"runway" if has_runway else "airport",
|
||||||
@@ -705,7 +719,22 @@ def _build_airport_status_message(
|
|||||||
)
|
)
|
||||||
lines = [hashtag_line, header + flag, ""]
|
lines = [hashtag_line, header + flag, ""]
|
||||||
runway_shown = False
|
runway_shown = False
|
||||||
if runway_pairs and runway_temps and len(runway_pairs) == len(runway_temps):
|
if is_amsc and runway_pairs and runway_temps and len(runway_pairs) == len(runway_temps):
|
||||||
|
point_temps = runway_data.get("point_temperatures") or []
|
||||||
|
for i, ((r1, r2), (t, _d)) in enumerate(zip(runway_pairs, runway_temps)):
|
||||||
|
if t is not None:
|
||||||
|
pts = point_temps[i] if i < len(point_temps) else {}
|
||||||
|
tdz = pts.get("tdz_temp")
|
||||||
|
mid = pts.get("mid_temp")
|
||||||
|
end = pts.get("end_temp")
|
||||||
|
if tdz is not None or mid is not None or end is not None:
|
||||||
|
lines.append(
|
||||||
|
f"{r1}/{r2} TDZ:{_fmt(tdz)} MID:{_fmt(mid)} END:{_fmt(end)}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
lines.append(f"{r1}/{r2} {t:.1f}°C")
|
||||||
|
runway_shown = True
|
||||||
|
elif has_runway:
|
||||||
for (r1, r2), (t, _d) in zip(runway_pairs, runway_temps):
|
for (r1, r2), (t, _d) in zip(runway_pairs, runway_temps):
|
||||||
if t is not None:
|
if t is not None:
|
||||||
lines.append(f"{r1}/{r2} {t:.1f}°C")
|
lines.append(f"{r1}/{r2} {t:.1f}°C")
|
||||||
@@ -753,6 +782,14 @@ _AIRPORT_PUSH_INTERVAL = {
|
|||||||
"hong kong": 60,
|
"hong kong": 60,
|
||||||
"lau fau shan": 60,
|
"lau fau shan": 60,
|
||||||
"taipei": 60,
|
"taipei": 60,
|
||||||
|
"beijing": 60,
|
||||||
|
"shanghai": 60,
|
||||||
|
"guangzhou": 60,
|
||||||
|
"shenzhen": 60,
|
||||||
|
"qingdao": 60,
|
||||||
|
"chengdu": 60,
|
||||||
|
"chongqing": 60,
|
||||||
|
"wuhan": 60,
|
||||||
}
|
}
|
||||||
# Per-city temperature window threshold (°C below DEB predicted high)
|
# Per-city temperature window threshold (°C below DEB predicted high)
|
||||||
# Continental airports: wider window (temp rises steadily over land)
|
# Continental airports: wider window (temp rises steadily over land)
|
||||||
|
|||||||
Reference in New Issue
Block a user