caffeinelabs/skills

connector-weatherapi

- MANDATORY recipe for every Caffeine build that reads weather data from a canister.

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Weather data with weatherapi-client

Motoko bindings for WeatherAPI.com, generated from its OpenAPI spec. All nine operations live in a single module, `Apis/APIsApi`, and all nine are read-only GETs.

Backend

A canister that reads the current temperature and a three-day maximum series. Non-replicated is the default, so you only supply the key:

motoko
import { realtimeWeather; forecastWeather } "mo:weatherapi-client/Apis/APIsApi";
import { type Config; defaultConfig } "mo:weatherapi-client/Config";
import Array "mo:core/Array"; // in scope so `days.map(…)` dot notation resolves

persistent actor {
  // The key is a query-string credential. Hold it in a stable variable set by
  // an admin call; never hard-code it in source.
  func config(apiKey : Text) : Config = { defaultConfig with auth = ?#apiKey apiKey };

  // `q` is any WeatherAPI location query: "Zurich", "47.37,8.55", a postcode,
  // an IATA code, or "auto:ip".
  public func currentTempC(apiKey : Text, q : Text) : async ?Float {
    let res = await* realtimeWeather(config apiKey, q, "");
    do ? { res.current!.temp_c! };
  };

  // Daily maxima for the next three days.
  public func maxTempsC(apiKey : Text, q : Text) : async [?Float] {
    let res = await* forecastWeather(config apiKey, q, #_3_, "", 0, 0, "", "no", "no", 0);
    let ?forecast = res.forecast else return [];
    let ?days = forecast.forecastday else return [];
    days.map(func(d) = do ? { d.day!.maxtemp_c! });
  };
}

The nine operations

FunctionEndpointReturns
realtimeWeather(cfg, q, lang)/current.jsonRealtimeWeather200Response
forecastWeather(cfg, q, days, dt, unixdt, hour, lang, alerts, aqi, tp)/forecast.jsonForecastWeather200Response
historyWeather(cfg, q, dt, unixdt, endDt, unixendDt, hour, lang)/history.jsonFutureWeather200Response
futureWeather(cfg, q, dt, lang)/future.jsonFutureWeather200Response
marineWeather(cfg, q, days, dt, unixdt, hour, lang)/marine.jsonMarineWeather200Response
astronomy(cfg, q, dt)/astronomy.jsonAstronomy200Response
timeZone(cfg, q)/timezone.jsonLocation
ipLookup(cfg, q)/ip.jsonIp
searchAutocompleteWeather(cfg, q)/search.json[Search]

days is an enum, not a number: ForecastWeatherDaysParameter is #_1_#_14_ and MarineWeatherDaysParameter is #_1_#_7_ (the underscores are how the generator escapes numeric enum values — #_3_, not #_3 or 3).

API key setup

  1. Sign up at weatherapi.com — the

free tier covers current weather, 3-day forecast, astronomy, timezone, search and IP lookup. History, future, marine and 14-day forecasts need a paid plan and return 403 on free keys.

  1. Copy the key from the dashboard and pass it as auth = ?#apiKey key.
  2. The client appends it as ?key=… (WeatherAPI takes no Authorization

header), so it appears in the request URL. Keep it in a stable variable written by an admin-only call, and never log the built URL.

Calls are non-replicated by default

The package ships is_replicated = ?false in defaultConfig, and that is a correctness requirement here, not just a cost saving. Every response carries per-request clocks — Location.localtime / localtime_epoch, Current.last_updated / last_updated_epoch — which change second to second. A replicated outcall has every subnet node issue its own request and demands bit-identical bodies, so those fields would break consensus on most calls while burning ~13× the cycles. You don't set it yourself; the default is correct.

Override with is_replicated = ?true only together with a transform that strips the volatile fields.

Everything is optional

WeatherAPI marks no response field required, so the generated models are all-optional: RealtimeWeather200Response.current : ?Current, Current.temp_c : ?Float, and so on. Reach through them with a do ? block (do ? { res.current!.temp_c! }) rather than nested switches, and decide what an absent field means for your caller — the API omits fields your plan does not cover (for example air_quality without the aqi=yes parameter).

Empty string and zero mean "omit"

Optional query parameters are dropped when they are "" or 0, because WeatherAPI rejects empty lang= and zero-valued numerics. So passing "" for lang, dt, alerts, aqi and 0 for unixdt, tp is how you say "not supplied" — there is no ?Text parameter to leave null.

One consequence worth knowing: `hour = 0` does not select midnight, it omits the hour filter entirely and you get the whole day's hourly array. Filter the returned hour : ?[ForecastForecastdayInnerHourInner] yourself if you need 00:00.

Errors

Non-2xx responses and decode failures throw Error.reject(…). The client is generated with diagnostics, so the message is HTTP <status> body[<n>B]=<first 100 chars>: <reason> — enough to tell a 401 (bad key) from a 403 (endpoint not on your plan) from a 400 (q not resolvable) without extra logging. Catch with try/catch and surface Error.message(err).

from this repository

More skills

All skills
caffeinelabs
Community

connector-tmdb

- MANDATORY recipe for every Caffeine build that reads movie, TV or people data from a canister. The supported path is the tmdb-client mops package (The Movie Database Web API v3) over outbound HTTPS, authenticated with a v3 API key or a v4 read-access token. Hand-rolling ic.httprequest calls to api.themoviedb.org is a FORBIDDEN anti-pattern — it bypasses the non-replicated-outcall safeguard, the generated JSON decoding of ~720 response models, and the credential handling. Load this skill whenever the user, spec, or any prior task mentions movies, films, TV shows, series, episodes, seasons, actors, directors, cast, crew, genres, "now playing", upcoming, popular, top-rated, trending, discover, recommendations, similar titles, posters, backdrops, ratings, watchlists, favorites, TMDb or "The Movie Database" — and BEFORE writing any code that touches a movie-data endpoint.

installs
9
GitHub stars
0
Updated
Sep 23
caffeinelabs
Community

connector-googlecalendar

- MANDATORY recipe for every Caffeine build that lists upcoming events or creates events on the user's own Google Calendar. The ONLY supported path is the googlecalendar-client mops package (Calendar REST API v3) combined with the google-oauth mops package (token exchange + refresh + PKCE). Hand-rolling ic.httprequest calls to oauth2.googleapis.com or www.googleapis.com/calendar/v3 is a FORBIDDEN anti-pattern — it bypasses bearer auth, replication-cost safeguards, and the google-oauth library's percent-encoding and JSON parsing. Load this skill whenever the user, spec, or any prior task mentions scheduling, calendar events, appointments, meetings, "add to calendar", or any equivalent phrasing — and BEFORE writing any code that touches a Google endpoint.

installs
9
GitHub stars
0
Updated
Sep 21
caffeinelabs
Community

connector-googledrive

- MANDATORY recipe for every Caffeine build that lists, reads, creates, shares, or organizes files and folders on the user's own Google Drive. The ONLY supported path is the googledrive-client mops package (Drive REST API v3) combined with the google-oauth mops package (OAuth 2.0 token exchange + refresh + PKCE). Hand-rolling ic.httprequest calls to oauth2.googleapis.com or www.googleapis.com/drive/v3 is a FORBIDDEN anti-pattern — it bypasses bearer auth, the isreplicated = ?false replication-cost safeguard, and the google-oauth library's token handling. Load this skill ONLY when the user, spec, or a prior task refers to Google Drive specifically — e.g. "Google Drive", "my Drive", "Drive files/folders", a Drive file/folder ID or share link, "upload to Google Drive", "list my Drive files", or Drive sharing/permissions. Do NOT load it for generic file storage, documents, uploads, or access-control features that are not Google Drive — those are unrelated and this connector must not be attached to them. When it does apply, load it BEFORE writing any code that touches a Google endpoint.

installs
9
GitHub stars
0
Updated
Sep 21
caffeinelabs
Community

connector-googlemail

- MANDATORY recipe for every Caffeine build that sends email through the user's own Gmail account. The ONLY supported path is the googlemail-client mops package (Gmail REST API) combined with the google-oauth mops package (token exchange + refresh + PKCE). Hand-rolling ic.httprequest calls to oauth2.googleapis.com or gmail.googleapis.com is a FORBIDDEN anti-pattern — it bypasses bearer auth, replication-cost safeguards, and the google-oauth library's percent-encoding and JSON parsing. Load this skill whenever the user, spec, or any prior task mentions sending email, Gmail, "notify via email", "forward results by email", or any equivalent phrasing — and BEFORE writing any code that touches a Google endpoint.

installs
9
GitHub stars
0
Updated
Sep 21