WeatherGuard: The Telegram Weather Bot That Only Talks to People You Approve

A NestJS and React monorepo that turns weather data into a gated alert system, using Clerk, Telegram deep links, cron scheduling, and a strict user state machine.

8 min read • View on GitHub • More from komal0a

A locked post office sorting room where weather reports arrive on one side and only approved envelopes pass through to a messenger pouch on the other. Three stamps control the flow, showing how identity, approval, and delivery are separate steps in the system.
WeatherGuard is less like a weather app and more like a controlled delivery desk. The point is not just to fetch forecasts, but to decide exactly who is allowed to receive them.
Key Takeaways

Most weather tools answer a simple question: what is the forecast? WeatherGuard asks a stricter one: who is allowed to hear it? That shift changes everything. The app becomes a permissioned delivery system, with weather data as the payload and Telegram as the channel.

The real product is control, not forecasts

That matters for any group that needs private alerts: a team watching travel conditions, a club coordinating event weather, or a small organization that does not want to broadcast updates in public. WeatherGuard sits in the narrow space between a weather feed and a private message channel. It is a notification layer with a gate, not a generic dashboard.

The codebase reflects that idea. The repository is split into an /api service built with NestJS and an /admin app built with React and Vite. The split is practical, but the more interesting part is that the backend owns the rules of delivery while the frontend mainly handles identity, approval, and connection.

A three-step identity bridge from web app to Telegram chat

The clever part is the handoff. A browser identity becomes a Telegram chat identity without a manual code exchange, and the scheduler only delivers to users who survive every gate.

A close-up of two connected identity cards joined by a thin thread. One card represents a browser session, the other a Telegram chat, with three small valves between them showing approval, registration, and notification control.
This is the project’s most interesting trick. A user does not copy a token into chat. The web app and the bot bind identities through a deep link and a database write.

The user state machine keeps notifications separate from approval

The user model is more disciplined than a typical bot project needs to be. Users move through PENDING, APPROVED, or REJECTED, but approval is not the same as delivery. The schema also tracks telegramChatId and notificationsEnabled, which lets the system remember that a user is allowed in while still letting them opt out of alerts.

StateCan receive alerts?Why it matters
PendingNoThe user exists, but a moderator has not granted access.
Approved with notifications onYesThis is the only path that reaches the scheduler.
Approved with notifications offNoThe user stays approved, but delivery stops without revoking access.
RejectedNoThe system keeps the relationship closed.

That separation is subtle, and it is the kind of detail that makes a prototype feel like a product instead of a demo. It prevents approval from being overloaded with preference, and it prevents preference from being overloaded with trust.

The scheduler is the heartbeat

The delivery engine runs on a minute-by-minute cron job. Each tick fetches weather, loads the approved recipient set, and filters again before anything is sent. In other words, the system does not trust a single database flag. It checks the whole chain every time.

@Cron('*/1 * * * *')
async handleWeatherUpdates() {
  const weather = await this.weatherService.getCurrentWeather();
  const users = await this.usersService.getApprovedUsers();

  const recipients = users.filter(user =>
    user.status === UserStatus.APPROVED &&
    user.telegramChatId &&
    user.notificationsEnabled
  );

  for (const user of recipients) {
    await this.telegramService.sendWeatherUpdate(user.telegramChatId, weather);
  }
}

That tiny filter is the whole product in miniature. It says the system cares about three things before it sends a message: the person is approved, the Telegram binding exists, and alerts are enabled. Everything else is noise.

Why the system is harder to break than it looks

The repo layers its defenses. ClerkAuthGuard verifies browser identity. AdminGuard limits sensitive actions like approvals. The backend also uses role checks, fallback weather data, and retry logic for Telegram delivery. None of those pieces is magical on its own, but together they reduce the chance that a bad state turns into a bad send.

LayerWhat it protectsWhat it prevents
Clerk authBrowser accessUnauthenticated admin actions
Admin guardModeration endpointsNon-admins approving users
State machineRecipient eligibilitySending to unreviewed users
Notification toggleUser preferenceSpamming someone who opted out
Retry logicTelegram deliveryTransient network failures

The fallback weather path is worth noting too. If the external API fails or the key is missing, the service keeps the system alive with substitute data instead of collapsing the scheduler. That is a pragmatic choice. It keeps the alerting pipeline operational even when the weather source is not.

What WeatherGuard is really competing with

WeatherGuard is not competing with a full weather platform. It is competing with the awkward glue people usually build around notifications. That includes smart home automations, one-off API wrappers, and no-code tools that can send a weather-triggered message but do not really understand access control.

OptionBest forAccess controlSelf-hostedShape of the product
WeatherGuardPrivate weather alertsBuilt inYesPermissioned delivery system
Home Assistant weather automationsBroad home automationIndirectYesGeneral automation platform
wttr.inQuick weather lookupNoneYesWeather data surface
Zapier or IFTTTSimple no-code triggersLimitedNoHosted automation glue

That comparison makes the niche clear. WeatherGuard is narrow on purpose. It is built for a case where the right answer is not "send an alert" but "send it only to the people who were explicitly admitted."

The trade-off: elegant pattern, early-stage product

The repo looks disciplined, but the public surface is still thin. There is no broad ecosystem, no visible author footprint to anchor a larger narrative, and no evidence that this is meant to be a finished consumer product. That is not a flaw. It is a signal that the interesting part here is the architecture, not the market.

Seen that way, WeatherGuard is a strong prototype. It shows how to bind web identity to a chat channel, how to separate approval from delivery, and how to build a notification path that fails closed instead of open. That is a useful pattern even if you never care about weather.