Utility · Root
Weatherbot
A headless Root that adds a weather slash command, interactive output cards, status-bar badges, and optional !w or ,w channel replies.
- Version
- 0.3.0
- Runs as
- Root
- Grove API
- 1
- Updated
- 2026-09-15
Requested access
5 permissions
Network access
Declared hosts
Grove blocks every network destination except these declared origins:
https://api.open-meteo.comhttps://geocoding-api.open-meteo.com
From the extension
README
Weatherbot — a Grove Root
A Root: a Grove extension with no UI. It never renders a pane, window, or tab — everything it does reaches you through capabilities.
It’s the reference example for the capabilities added alongside it:
| Capability | What Weatherbot does with it |
|---|---|
commands.register |
One /weather command with six subcommands |
| (command output) | An interactive card above the composer, with buttons and native menus |
statusbar |
A live temperature badge per tracked city |
channel.read |
Optionally watches for !w <city> or ,w <city> in the channel |
channel.send |
Optionally answers channel weather requests, and /weather share posts a lookup to the channel |
storage |
Tracked cities survive a reload |
Command output (the card) isn’t a capability of its own — it comes with
commands.register, since a command needs somewhere to put its answer.
Weatherbot uses Mango’s native Markdown card rendering for compact condition tables and readable command examples. The required plain-text body remains available for Copy and as a fallback on older Mango versions.
No build step — plain HTML + JS, dev-loadable as-is.
Try it
- Install this folder (or a
.grovearchive containing its package files at the archive root). - Installation only copies and validates the Root. Press Start beside
Weatherbot, then approve its five capabilities and two network hosts. Once
it appears under Running,
/weatheris available. /weather get London→ a card appears above the composer with Refresh, Track, and Share to channel. A loading card appears immediately while Weatherbot resolves the place and fetches conditions; the lookup stops with a visible error after 15 seconds instead of waiting indefinitely. The result carries conditions, humidity, and wind speed.- Press Refresh → the card replaces itself in place (same card
id). - Press Track → a badge appears in the status bar. Switch channels and networks: it stays.
/weather list→ choose a city from the compact Stop tracking… menu./weather remove <city>does the same;/weather remove allexplicitly clears everything.- Open Weatherbot’s Settings, enable Answer !w and ,w requests in channels,
then type
!w Pragueor,w Pragueas a normal message. It answers with conditions, humidity, and wind. A bare!wor,wreceives a formatted usage hint as an IRCv3 reply. This bot behavior is off by default so several users’ installed copies do not all answer the same request. - Close the Root in the Grove manager → its badges vanish with it. Re-open it:
the cities you tracked are still there, because of
storage.
Weather units
In Weatherbot’s Settings, choose Metric (°C, km/h), Imperial (°F, mph),
or Both, then press Save. Metric remains the default.
Both shows temperatures like 22°C / 72°F and wind like 8 km/h / 5 mph.
The choice applies to weather cards, shared conditions, channel replies, and tracked-city badges. Badges update when the setting is saved; refresh an existing card to see its conditions in the new units. Tracked cities and the separate channel-replies preference are preserved when updating to version 0.3.0.
Commands
Weatherbot registers one slash command and handles the rest as subcommands:
/weather get <city> Show current conditions in a card
/weather share <city> Post current conditions to this channel
/weather track <city> Add a live status-bar badge
/weather remove <city> Stop tracking a city
/weather remove all Stop tracking every city
/weather list List tracked cities
/weather help Show command help
A bare /weather also shows help. The location is the entire remainder of the
command, so /weather get New York works without quotes; matching outer quotes
are accepted too. The command palette also discovers these subcommands: after
typing /weather , choose one with the pointer or arrow keys and Return.
The manager is also the only place a Root is visible, since it has no surface
— it’s listed as Root · no window, with Stop and Console controls. JavaScript
exceptions, failed page loads, and console.* output appear there. If a Root
requests command registration but finishes loading without registering any,
the manager calls that out explicitly instead of leaving only an “unknown
command” symptom.
Naming
The geocoder canonicalises names — Praha comes back as Prague, Czechia —
so a badge is keyed by the resolved label, not what you typed. Card buttons
and menu items always pass that resolved label, so they match exactly.
Typing a name matches on an exact hit or a prefix of at least three
characters, so /weather remove prag finds Prague, Czechia but
/weather remove praha does not. Prefix rather than substring is deliberate:
untracking is destructive, and a substring match on a one-letter argument
silently removed every city with that letter anywhere in its name. When nothing
matches, the card lists what is tracked. Removal actions live in one native
Stop tracking… menu rather than expanding into a row of buttons.
Answering in channel
!w <city> and ,w <city> are ignored by default. A user who intends this
Mango instance to act as the channel’s bot can enable Answer !w and ,w
requests in channels from the extension’s Settings page. The Root reads that saved
preference before every message, so changing it takes effect without restarting
Weatherbot. When enabled, a bare !w or ,w replies to that message with a
monospaced !w city example instead of silently doing nothing.
When enabled, both aliases are watched for in ordinary channel traffic, which
is why the manifest asks for channel.read — a materially louder grant than
the rest: it sees every message in the bound channel, not just commands aimed
at it. The consent sheet says so (“Read messages in this channel”).
Two things worth copying if you write a bot that talks in channel:
- Its own output can’t re-trigger it. Grove delivers a Root the messages it
sends as well as everyone else’s — there is no self-filter — so the guard has
to come from the message shape: every reply and every error string this bot
emits starts with something other than
!wor,w, and the trigger is anchored (/^[!,]w(?:\s+(.+))?$/). That’s a by-construction argument, not an enforced one; if you change the reply format, re-check it. A bot that answers its own output is the classic way to flood a channel. - It rate-limits itself (one reply per 5s). Dropped requests are silent: a “slow down” reply would itself be the flood.
- Failures don’t leak upstream detail. A lookup error goes to
grove.logand the channel gets a fixed sentence — HTTP statuses and internals have no business in a public room.
Humidity and wind are appended only when the station reports them — Open-Meteo
omits fields rather than sending nulls, and humidity undefined% in a channel
is worse than saying nothing.
Notes on the shape of a Root
"surfaces": ["headless"]in the manifest is what makes it a Root. Theindex.htmlis never displayed; it exists only to load the script.- It keeps running while you use the app, independent of which channel or network is on screen — that’s what makes the 15-minute badge refresh and the always-visible badges meaningful.
- It coexists with a channel’s Sprig. A Root doesn’t own a pane, so it doesn’t displace anything docked in the channel it was launched from.
- Cards never write to the message field.
/weather sharesends explicitly viagrove.send, so posting to a channel is always something the extension asks for, never a side effect of showing output. - Buttons and menu items can only invoke this Root’s own registered
commands. A control naming a built-in (
/join) or another extension’s command is dropped when the card is created.
Network
Weather data is Open-Meteo — no API key, no account.
"hostPermissions": [
"https://api.open-meteo.com",
"https://geocoding-api.open-meteo.com"
]
Grove’s egress sandbox blocks every other host, so this extension cannot reach anywhere it hasn’t declared — including to exfiltrate what you type.
manifest.jsonapp.mango.grove.weatherbot
{
"grove": 1,
"id": "app.mango.grove.weatherbot",
"name": "Weatherbot",
"version": "0.3.0",
"description": "A headless Root that adds a weather slash command, interactive output cards, status-bar badges, and optional !w or ,w channel replies.",
"author": {
"name": "Mango",
"url": "https://mango.app"
},
"readme": "README.md",
"entry": "index.html",
"settings": "settings.html",
"presentation": {
"surfaces": [
"headless"
]
},
"capabilities": [
"commands.register",
"statusbar",
"channel.read",
"channel.send",
"storage"
],
"hostPermissions": [
"https://api.open-meteo.com",
"https://geocoding-api.open-meteo.com"
]
}