← All extensions

Utility · Root

Weatherbot

by Mango

A headless Root that adds a weather slash command, interactive output cards, status-bar badges, and optional !w or ,w channel replies.

Install in Mango 3.115 KB · Mango 3.1
Version
0.3.0
Runs as
Root
Grove API
1
Updated
2026-09-15

Requested access

5 permissions

  • Add slash commandsRegisters its own commands and can show native command-result cards.
  • Show status indicatorsPublishes small, attributed badges in Mango’s status bar.
  • Read channel messagesReceives messages from the channel where the extension is running.
  • Send channel messagesCan post attributed messages to the channel where it is running.
  • Keep local dataStores extension data on this device in its own isolated space.

Network access

Declared hosts

Grove blocks every network destination except these declared origins:

  • https://api.open-meteo.com
  • https://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

  1. Install this folder (or a .grove archive containing its package files at the archive root).
  2. 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, /weather is available.
  3. /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.
  4. Press Refresh → the card replaces itself in place (same card id).
  5. Press Track → a badge appears in the status bar. Switch channels and networks: it stays.
  6. /weather list → choose a city from the compact Stop tracking… menu. /weather remove <city> does the same; /weather remove all explicitly clears everything.
  7. Open Weatherbot’s Settings, enable Answer !w and ,w requests in channels, then type !w Prague or ,w Prague as a normal message. It answers with conditions, humidity, and wind. A bare !w or ,w receives 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.
  8. 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 !w or ,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.log and 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. The index.html is 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 share sends explicitly via grove.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"
  ]
}