Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Config/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ public class VoipMsSettings : IPlatformFilterSettings

public class MapSettings
{
/// <summary>Whether to generate Mapbox static map images for alert areas.</summary>
/// <summary>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.</summary>
public bool Enabled { get; set; }

/// <summary>
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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` |
Expand Down
26 changes: 16 additions & 10 deletions Services/MapService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -57,13 +59,13 @@ public MapService(
}

/// <summary>
/// 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 <see cref="MapSettings.Enabled"/> — that setting only gates the Mapbox
/// fallback, since Mapbox is the one source that requires a configured account.
/// </summary>
public async Task<string?> 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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -172,9 +176,11 @@ alert.VtecAction is not ("CAN" or "EXP"))
}

/// <summary>
/// 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.
/// </summary>
public Task<string?> GetMapboxFallbackUrlAsync(NwsAlert alert)
{
Expand Down
15 changes: 10 additions & 5 deletions docs/TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading