From a6598814f194ef5d45c8e9b1f5cae92d47887997 Mon Sep 17 00:00:00 2001 From: Mike Wills Date: Wed, 15 Jul 2026 13:30:30 -0500 Subject: [PATCH] Decouple IEM map images from Map.Enabled/Mapbox configuration MapService.GetMapUrlAsync gated its whole body -- including the two no-account IEM autoplot paths (#208 for VTEC alerts, #217 for SPS) -- behind Map.Enabled, a setting documented and named for Mapbox. A fresh install with no Mapbox account (the shipped default) got zero map images for ordinary NWS alerts, even though IEM needs no signup. Confirmed live: a Discord post for an Extreme Heat Warning had no image. IEM is now always attempted first regardless of Map.Enabled; that setting (and AccessToken) only gate the Mapbox fallback, routed through the existing GetMapboxFallbackUrlAsync so both call sites share one "is Mapbox usable" check. SPC Outlook/MCD/ERO were already unaffected. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CfWmHii9XYmPNBhz5dGDxZ --- CHANGELOG.md | 11 +++++++++++ Config/AppSettings.cs | 2 +- README.md | 13 +++++++------ Services/MapService.cs | 26 ++++++++++++++++---------- docs/TECHNICAL.md | 15 ++++++++++----- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe085f6..0bb89c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ Notable changes to NwsAlertBot, most recent first. For setup and usage, see [README.md](README.md); for architecture and internals, see [docs/TECHNICAL.md](docs/TECHNICAL.md). +- **Fix: `Map.Enabled: false` (the shipped default) silently disabled free IEM map images too, + not just Mapbox.** `MapService.GetMapUrlAsync` gated its entire body — including both + no-account-needed IEM autoplot paths (#208 for VTEC alerts, #217 for SPS) — behind + `Map.Enabled`, a setting documented and named for Mapbox specifically. A fresh install that + never set up Mapbox (the shipped default) got zero map images for ordinary NWS + warnings/watches/advisories, even though IEM needs no signup. Confirmed live: a Discord post + for an Extreme Heat Warning had no image attached. IEM is now always attempted first regardless + of `Map.Enabled`; that setting (and `AccessToken`) only gate the Mapbox fallback, routed through + the existing `GetMapboxFallbackUrlAsync` so both call sites share one "is Mapbox usable" check. + SPC Outlook/MCD/ERO were already unaffected — they build their own image URLs independently of + `MapService`. See docs/TECHNICAL.md "Map Images — Internals". - **Fix: "Details" links pointed at the raw `api.weather.gov` JSON API instead of a human-readable webpage, and were SMS-only.** NWS alerts and HWO posts built their `DetailsUrl` from `api.weather.gov/alerts/{id}` / `api.weather.gov/products/{uuid}` — link-preview generators diff --git a/Config/AppSettings.cs b/Config/AppSettings.cs index f8c9232..c073a7c 100644 --- a/Config/AppSettings.cs +++ b/Config/AppSettings.cs @@ -570,7 +570,7 @@ public class VoipMsSettings : IPlatformFilterSettings public class MapSettings { - /// Whether to generate Mapbox static map images for alert areas. + /// Whether to generate Mapbox static map images for alert areas. Only gates the Mapbox fallback — IEM autoplot maps (the primary source for most NWS alerts) need no account and are always attempted regardless of this setting. public bool Enabled { get; set; } /// diff --git a/README.md b/README.md index 34560e1..c209dbb 100644 --- a/README.md +++ b/README.md @@ -990,14 +990,15 @@ If nothing is enabled, it logs a warning and exits without posting anything. ## Map Images (Mapbox) -When enabled, the bot generates a map image for each alert (NWS warnings/watches/advisories, plus -SPC Outlook and WPC ERO posts) and attaches it to every platform that supports images. Most maps -come from a free IEM service requiring no setup on your part; Mapbox is used as a fallback for -alerts with no VTEC code. See +The bot generates a map image for each alert (NWS warnings/watches/advisories, plus SPC Outlook, +SPC MCD, and WPC ERO posts) and attaches it to every platform that supports images. Most maps come +from a free IEM service that needs no account or setup on your part — this always runs, even +without Mapbox configured. Mapbox is only used as a fallback, for the minority of alerts IEM can't +provide a map for (no VTEC code, or not yet indexed by IEM), and requires the setup below. See [docs/TECHNICAL.md — Map Images](docs/TECHNICAL.md#map-images--internals) for how the bot decides which source to use. -### Setup +### Setup (Mapbox fallback only — optional) 1. Create a free account at [account.mapbox.com](https://account.mapbox.com/) 2. Copy your **default public token** (starts with `pk.`) from the Tokens page @@ -1016,7 +1017,7 @@ which source to use. | Field | Description | Default | |---|---|---| -| `Enabled` | Whether to generate map images | `false` | +| `Enabled` | Whether to use Mapbox as a fallback when IEM has no map for an alert. Does not affect IEM, which is always attempted first regardless of this setting. | `false` | | `AccessToken` | Mapbox public token (starts with `pk.`) | `""` | | `Style` | Mapbox style ID, format `{username}/{style_id}` | `"mapbox/outdoors-v12"` | | `Width` | Image width in pixels (max 1280) | `600` | diff --git a/Services/MapService.cs b/Services/MapService.cs index d7dd1b5..7f2dd3c 100644 --- a/Services/MapService.cs +++ b/Services/MapService.cs @@ -17,7 +17,9 @@ namespace NwsAlertBot.Services; /// warning polygon, county boundaries, and NEXRAD radar overlay. IEM's VTEC JSON API is /// checked first to confirm the event exists in their database; if not found, falls through /// to Mapbox. IEM silently serves a default demo image (HTTP 200) for unknown events, so -/// the pre-flight JSON check is required to avoid posting wrong maps. +/// the pre-flight JSON check is required to avoid posting wrong maps. IEM needs no +/// account/token, so both IEM paths (#208 here and #217 for SPS) are always attempted +/// regardless of Map.Enabled/AccessToken — only the Mapbox fallback below is gated by those. /// /// Mapbox fallback: Overlay geometry priority: /// 1. Alert's own GeoJSON polygon (when NWS provides one). @@ -57,13 +59,13 @@ public MapService( } /// - /// Returns a map image URL for the alert, or null if map generation is disabled, - /// unconfigured, or no bounding box could be determined. + /// Returns a map image URL for the alert, or null if no image source is available. + /// IEM autoplot (both #217 and #208 below) needs no account/token and is always attempted + /// regardless of — that setting only gates the Mapbox + /// fallback, since Mapbox is the one source that requires a configured account. /// public async Task GetMapUrlAsync(NwsAlert alert) { - if (!_settings.Enabled) return null; - // IEM autoplot #217: SPS-specific map image (non-VTEC). Takes the IEM product ID // constructed from AWIPSidentifier (AFOS PIL) and WMOidentifier parsed from the alert. // Pre-flight GeoJSON check required: IEM returns HTTP 200 with a demo image for unknown @@ -110,8 +112,10 @@ alert.VtecAction is not ("CAN" or "EXP")) } // Fallback: Mapbox static image (used for non-VTEC events, CAN/EXP, or when IEM - // doesn't have the event in its VTEC database) - return await GetMapboxUrlAsync(alert); + // doesn't have the event in its VTEC database). Unlike IEM above, this does require + // Map.Enabled + a configured AccessToken — routed through GetMapboxFallbackUrlAsync so + // both entry points share the same "is Mapbox actually usable" check. + return await GetMapboxFallbackUrlAsync(alert); } // IEM sometimes uses a different phenomena code than NWS does in the VTEC string. @@ -172,9 +176,11 @@ alert.VtecAction is not ("CAN" or "EXP")) } /// - /// Returns a Mapbox static map URL for the alert, bypassing the IEM check. - /// Called when the IEM image download fails after all retries. - /// Returns null if Mapbox is not configured or no bounding box is available. + /// Returns a Mapbox static map URL for the alert, bypassing the IEM check. Called both by + /// GetMapUrlAsync (when no IEM image is available at all) and by + /// SocialMediaOrchestrator.DownloadMapImageAsync (when an IEM image URL was returned but its + /// download then failed after all retries). Returns null if Map is disabled, no AccessToken + /// is configured, or no bounding box is available. /// public Task GetMapboxFallbackUrlAsync(NwsAlert alert) { diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md index 3630816..7c2ee45 100644 --- a/docs/TECHNICAL.md +++ b/docs/TECHNICAL.md @@ -626,13 +626,18 @@ filter fields were added beyond `IncludeEro` (see [Configuration Reference](#con ## Map Images — Internals -When enabled, the bot generates a **Mapbox Static Images** URL for each NWS alert (warnings, -watches, advisories) and attaches it via the alert's `MapImageUrl` field to every platform that -supports images. SPC Convective Outlook and WPC ERO posts get their own image independently of -this setting through the same `MapImageUrl` field, so the platform behavior table in the README's +For each NWS alert (warnings, watches, advisories), `MapService.GetMapUrlAsync` generates an +image URL and attaches it via the alert's `MapImageUrl` field to every platform that supports +images. SPC Convective Outlook, SPC MCD, and WPC ERO posts get their own image independently of +`MapService` entirely (built directly from spc.noaa.gov/wpc.ncep.noaa.gov by their own services) +through the same `MapImageUrl` field, so the platform behavior table in the README's [Map Images](../README.md#map-images-mapbox) section applies to all of them. -Map images are generated from two sources depending on the alert type: +Map images are generated from two sources depending on the alert type. **`Map.Enabled` and +`Map.AccessToken` only gate the Mapbox source** — the two IEM paths below need no account/token +and are always attempted first, regardless of whether Mapbox is configured. (Before this was +decoupled, `Map.Enabled: false` — the shipped default — silently skipped IEM too, so a fresh +install with no Mapbox account got no map images at all for ordinary NWS alerts.) **IEM (primary — most NWS alerts):** The bot parses the VTEC code from each NWS alert (`parameters.VTEC`) and requests a pre-rendered PNG from Iowa State University's IEM Autoplot