This skill uses data from the OpenWeatherMap API. Don’t worry, you don’t need to do anything in order to use it. Install it, and have fun!
Get the weather in your city
This skill uses data from the OpenWeatherMap API. Don’t worry, you don’t need to do anything in order to use it. Install it, and have fun!
Subscribe to weather forecasts by saying @abbot weather watch {zipcode}
(US only) or @abbot weather watch {city}
. Unsubscribe using @abbot weather unwatch {zipcode}
or @abbot weather unwatch {city}
.
Check the forecasts with @abbot weather check
, or get the current weather with @abbot weather {zipcode}
(US only) like @abbot weather 90210
, or @abbot weather {city}
, like @abbot weather paris
.
Disambiguate city names by optionally passing a country code, like @abbot weather cambridge uk
var (cmd, args) = Bot.Arguments.Pop();
Task reply = cmd switch {
“check” => CheckSubscriptions(),
“watch” or “start” or “add” => HandleSubscribe(args),
“unwatch” or “stop” or “remove” => HandleUnsubscribe(args),
“subs” => HandleSubs(args),
_ => HandleOther(Bot.Arguments)
};
await reply;
async Task<(string, string, bool, bool, bool, bool)> ParseLocation(IArguments args) {
var (zc, ct) = args;
var country = “US”;
var zipOrCity = “”;
var parsedFromLocation = false;
var success = true;
var isZip = false;
var hasCountry = false;
if (zc is IMissingArgument) {
if (Bot.From.Location.Coordinate is null) {
success = false;
} else {
var parts = Bot.From.Location.FormattedAddress.Split(‘,’)[^2..^0];
country = parts[1];
zipOrCity = parts[0];
hasCountry = true;
parsedFromLocation = true;
}
} else {
zipOrCity = zc.Value;
isZip = Regex.Match(zipOrCity, zipUSRegex).Success;
hasCountry = !(ct is IMissingArgument);
if (hasCountry) {
country = ct.Value;
} else if (!isZip) {
var endpoint = $”{weatherEndpoint}?q={zipOrCity}”;
try {
var results = await Bot.Http.GetJsonAsync(endpoint);
country = results.sys.country;
hasCountry = true;
} catch (HttpRequestException) {
await Bot.ReplyAsync($”The location you provided ({zipOrCity}) could not be found.”);
success = false;
} catch (Exception e) {
await Bot.ReplyAsync($”There was an unspecified error finding {zipOrCity} {e.Message}.”);
success = false;
}
}
}
return (zipOrCity, country, isZip, parsedFromLocation, hasCountry, success);
}
async Task HandleSubscribe(IArguments args) {
var (zipOrCity, country, isZip, _, hasCountry, success) = await ParseLocation(args);
if (!success) {
await Bot.ReplyAsync($”I do not know your location to give you the weather. You can try `@abbot my location is {{zip|city}}` to tell me your location. Also try `{Bot} help weather` to learn everything the weather skill can do.”);
return;
}
await AddWeatherSubscription(zipOrCity, country, isZip, hasCountry);
}
async Task HandleUnsubscribe(IArguments args) {
var (zipOrCity, _, __, parsedFromLocation, ___, success) = await ParseLocation(args);
if (!success || parsedFromLocation) {
await Bot.ReplyAsync($”I do not know what location to stop watching.”);
return;
}
await RemoveWeatherSubscription(zipOrCity);
}
async Task HandleOther(IArguments args) {
var (cmd, rest) = args;
var (zipOrCity, country, isZip, _, hasCountry, success) = await ParseLocation(args);
if (cmd is IMissingArgument) {
if (!success) {
await Bot.ReplyAsync($”I do not know your location to give you the weather. You can try `@abbot my location is {{zip|city}}` to tell me your location. Also try `{Bot} help weather` to learn everything the weather skill can do.”);
return;
}
var (_, unit) = GetUnits(country);
var daily = await GetWeather(Bot.From.Location.Coordinate, country);
await Bot.ReplyAsync($”{Bot.From.Location.FormattedAddress} – {GetWeatherDescription(daily, unit)}.\n\nRun `@abbot help weather` to learn everything the weather skill can do.”);
} else {
await ReportWeather(zipOrCity, country, isZip, hasCountry);
}
}
async Task HandleSubs(IArguments args) {
var (subcmd, subargs) = args.Pop();
switch (subcmd) {
case “migrate”: {
var items = await Bot.Brain.GetAllAsync();
var subscriptions = new List<WeatherSubscription>();
var keys = new List<string>();
foreach (var item in items) {
var z = (WeatherSubscription)item.Value;
// migrating old subscription data
if (z.Version == 0) {
z.Country = “US”;
(z.Units, z.Unit) = GetUnits(z.Country);
z.Version++;
}
subscriptions.Add(z);
keys.Add(item.Key);
}
for (var i = 0; i < keys.Count; i++) {
await Bot.Brain.DeleteAsync(keys[i]);
await Bot.Brain.WriteAsync(subscriptions[i].Zip, subscriptions[i]);
}
await Bot.ReplyAsync(“Migration done”);
}
break;
case “list”:
default: {
var reply = new System.Text.StringBuilder();
reply.AppendLine(“I’m tracking the following locations:”);
reply.AppendLine();
var items = await Bot.Brain.GetAllAsync();
foreach (var item in items) {
reply.AppendLine($”* {item.Key}”);
}
await Bot.ReplyAsync(reply.ToString());
}
break;
}
}
// =============== Weather stuff ======================
const string zipUSRegex = @”^\d{5}(?:[-\s]\d{4})?$”;
public class WeatherSubscription {
public string Name { get; set; }
public string Zip { get; set; }
public string Lat { get; set; }
public string Lon { get; set; }
public string Country { get; set; }
public string Units { get; set; }
public string Unit { get; set; }
public string AddedBy { get; set; }
public DateTime AddedAt { get; set; }
public int Version { get; set; }
}
const string weatherEndpoint = “https://link.ab.bot/api/HFYFbHt2W6tLQ2v_23qvWCBg”;
const string oneCallEndpoint = “https://link.ab.bot/api/sqRQB5M426T4W2HS8RVZ6NV1”;
(string, string) GetUnits(string country) {
var units = country.ToUpper() == “US” || country.ToUpper() == “USA” ? “imperial” : “metric”;
var unit = units == “imperial” ? “F” : “C”;
return (units, unit);
}
string GetEndpoint(string zipOrCity, string country, string units, bool isZip, bool hasCountry) =>
$”{weatherEndpoint}?{(isZip ? “zip” : “q”)}={zipOrCity}{(hasCountry || isZip ? “,”+country : “”)}&units={units}”;
async Task ReportWeather(string zipOrCity, string country, bool isZip, bool hasCountry) {
var (units, unit) = GetUnits(country);
var endpoint = GetEndpoint(zipOrCity, country, units, isZip, hasCountry);
try {
var results = await Bot.Http.GetJsonAsync(endpoint);
var response = $”The weather in {results.name} ({results.sys.country}) is currently {results.main.temp}{unit}. \n”;
response += $”({results.weather[0].main}, feels like {results.main.feels_like}{unit})”;
await Bot.ReplyAsync(response);
} catch {
await Bot.ReplyAsync(“Could not find a result for ” + zipOrCity);
}
}
async Task<string> CheckWeather(WeatherSubscription sub) {
try {
var daily = await GetWeather(sub.Lat, sub.Lon, sub.Country);
return $”* {sub.Name} ({sub.Zip}) – {GetWeatherDescription(daily, sub.Unit)}”;
} catch {
return $”There was a problem getting the weather report for {sub.Name} ({sub.Zip})”;
}
}
async Task AddWeatherSubscription(string zipOrCity, string country, bool isZip, bool hasCountry) {
var (units, unit) = GetUnits(country);
// Need to get the lat/lng of the zipcode to get info from the right endpoint
var endpoint = GetEndpoint(zipOrCity, country, units, isZip, hasCountry);
try {
var results = await Bot.Http.GetJsonAsync(endpoint);
country = results.sys.country;
var subscription = new WeatherSubscription{
Name = $”{results.name}, {country}”,
Zip = zipOrCity,
Lat = results.coord.lat,
Lon = results.coord.lon,
Country = country,
Units = units,
Unit = unit,
AddedBy = Bot.From.Id,
AddedAt = DateTime.UtcNow,
Version = VERSION
};
await Bot.Brain.WriteAsync(subscription.Zip.ToLower(), subscription);
await Bot.ReplyAsync($”Adding a weather subscription for {zipOrCity},{country}”);
} catch (HttpRequestException){
await Bot.ReplyAsync($”The location you provided ({zipOrCity}) could not be found.”);
} catch (Exception e) {
await Bot.ReplyAsync($”There was an unspecified error subscribing to {zipOrCity} {e.Message}.”);
}
}
async Task<dynamic> GetWeather(string lat, string lon, string country){
var (units, _) = GetUnits(country);
var endpoint = $”{oneCallEndpoint}?lat={lat}&lon={lon}&units={units}”;
dynamic results = await Bot.Http.GetJsonAsync(endpoint);
return results.daily[0];
}
Task<dynamic> GetWeather(ICoordinate coordinate, string country) {
var (lat, lon) = coordinate;
return GetWeather(lat.ToString(), lon.ToString(), country);
}
async Task RemoveWeatherSubscription(string zipOrCity) {
if ((await Bot.Brain.GetAsync(zipOrCity)) != null) {
await Bot.Brain.DeleteAsync(zipOrCity);
await Bot.ReplyAsync($”Removed a weather subscription for {zipOrCity}”);
} else {
await Bot.ReplyAsync($”Hmmm, there isn’t a weather subscription for {zipOrCity}”);
}
}
async Task CheckSubscriptions() {
var items = await Bot.Brain.GetAllAsync();
var report = “Here’s your weather report: “;
foreach (var item in items) {
var z = (WeatherSubscription)item.Value;
// migrating old subscription data
if (z.Version == 0) {
z.Country = “US”;
(z.Units, z.Unit) = GetUnits(z.Country);
z.Version++;
}
report += “\n” + await CheckWeather(z);
}
await Bot.ReplyAsync(report);
}
string GetWeatherDescription(dynamic daily, string unit) {
return $”High: {daily.temp.max}{unit} / Low: {daily.temp.min}{unit} with {daily.weather[0].description}”;
}
Version | Created | Author | Release Notes | Installs |
---|---|---|---|---|
1.3.0 | 8/20/2021 |
![]() shana |
Now with international support! You can now call the weather skill with your city name (and optional country code) and get the weather of any location around the world! |
11 |
1.2.3 | 2/4/2021 |
![]() haacked |
|
11 |
1.2.2 | 1/30/2021 |
![]() haacked |
|
0 |
1.1.2 | 12/4/2020 |
![]() Paul |
Updated usage text to let folks know about |
3 |
1.1.1 | 12/3/2020 |
![]() Paul |
The weather report (@abbot weather check) now returns a single time, instead of Abbot sending a message for each line in the report. |
0 |
1.0.1 | 12/1/2020 |
![]() Paul |
Updated usage text |
0 |
1.0.0 | 12/1/2020 |
![]() Paul |
First version. Enjoy the weather! |
0 |
ab.bot is a web platform serves as a comprehensive guide for Irish residents exploring online casinos and sportsbooks. We provide detailed insights and reviews to help you make informed decisions about online gambling. Your safety and well-being are our top priorities, which is why we emphasize thorough research and responsible gaming practices before you begin your online casino journey.
Gambling can be addictive, please play responsibly.
If gambling is negatively impacting your life, please contact Gamblingcare.ie from the phone number below
ab.bot is managed by Merri Media Agency. Unless declared otherwise, all of the visible content on this site, such as texts and images, including the brand name and logo, belongs to Beggie Labs (a Merri Media Agency company) - Company Registration Number C44120, VAT ID: MT1891432, @GIG Beach Triq id-Pragunafa, St. Martin, STJ1488, Malta.
Copyright © 2025 ab.bot