Manage and Monitor your services with Uptime Robot https://uptimerobot.com/
About Abbot
Abbot is a programmable bot that turns your team chat into a shared command center. We handle all the boilerplate of building and running these conmmands so that you can focus on making tools that help you ship faster.
We built Abbot because we saw the power of this style of work (called ChatOps), when we worked at GitHub. ChatOps made it possible for GitHub to work productively without meetings, while globally distributed. We think it’s a pretty great way to work, so we made it easy to use in Slack, Discord, and Microsoft Teams.
You can read more about Abbot here, check out our blog, or take a look at some of the other cool packages available as a one-click install from Abbot’s Package Directory.
README
UptimeRobot is a super useful free service monitoring service. This skill is the beginning of support for querying/configuring the monitors in your UptimeRobot account.
Required Secrets
api-key
API key from UptimeRobot. Full read/write permissions expected. Get an API key from My Settings page → API Settings section → Main API Key.
Triggers
This skill can respond to post to channels when UptimeRobot detects that a monitor has gone up/down. To do this a Webhook Alert needs to be added to UptimeRobot and configured in a specific way that Abbot can recognize it.
Add a Trigger
- In a channel in your chat platform ask Abbot to attach to the channel.
@abbot attach uptimerobot
Abbot will respond with a link to manage your triggers. Follow the link and note the trigger url for the channel you just attached.
- In the My Settings page of UptimeRobot, Add Alert Contact in the Alert Contacts section. And fill out the following details:
- Alert Contact Type: Webhook
- Friendly Name: < Whatever you want to call this alert >
- URL to Notify: < The url noted from step 1 >
- POST Value:
{"stat":"ok"}
(This was the only way I could find to convince UptimeRobot to trigger the Webhook with POST rather than GET. Abbot only listens for POST requests.)
- Check “Send as JSON …”
- Enable notifications for: < Select your preference >
- Get SSL expiration reminder notifications: Unchecked (this isn’t supported by the skill yet).
- Create Alert Contact!
- In the My Settings page of UptimeRobot add the new Webhook alert to all the Minitors that you wish to have trigger this skill. (Note: There’s a Bulk Actions link towards the top of the Monitor list that allows you to add the alert to all your Monitors at once.)
Usage
@abbot uptimerobot list
– Lists all monitors and their current status.
@abbot uptimerobot new {type} {url/ip} {name}
– Create a new monitor. Currently only supports type 3 (PING). Do not include protocol in URL.
@abbot uptimerobot delete {id}
– Delete monitor. WARNING: Minimal error protection, be careful!
@abbot uptimerobot set {id} {key} {value}
– Edit the some property of a monitor. (Note: Monitor type cannot be changed.)
Code
readonly Dictionary<Status, string> StatusEmoji = new Dictionary<Status, string> {
{Status.Unknown, “❔”},
{Status.Paused, “⏸”},
{Status.NotChecked, “⌛”},
{Status.Up, “🟢”},
{Status.SeemsDown, “❗”},
{Status.Down, “❌”}
};
// Handle HTTP Triggers for up/down alerts from Uptime Robot
// TODO: check queries has all the data that is expected
if (Bot.IsRequest)
{
var queries = Bot.Request.Query;
Status status = String.Join(“”, queries[“alertType”]) switch {
“1” => Status.Down,
“2” => Status.Up,
_ => Status.Unknown,
};
var friendlyStatus = String.Join(“”, queries[“alertTypeFriendlyName”]);
var friendlyName = String.Join(“”, queries[“monitorFriendlyName”]);
var url = String.Join(“”, queries[“monitorURL”]);
var builder = new System.Text.StringBuilder();
builder.Append($”{StatusEmoji[status]} Monitor is {friendlyStatus}: {friendlyName} ( {url} ) {StatusEmoji[status]}”);
switch (status) {
case Status.Up:
var duration = String.Join(“”, queries[“alertFriendlyDuration”]);
builder.AppendLine($”Monitor was down for {duration}.”);
break;
case Status.Down:
var details = String.Join(“”, queries[“alertDetails”]);
builder.AppendLine($”Details: {details}”);
break;
}
await Bot.ReplyAsync(builder.ToString());
return;
}
var (command, arguments) = Bot.Arguments.Pop();
string output = command switch {
“list” => await ListMonitors(),
“new” => await NewMonitor(arguments),
“delete” => await DeleteMonitor(arguments),
“set” => await SetMonitor(arguments),
_ => “Unrecognized command.”,
};
await Bot.ReplyAsync(output);
class GetMonitorsData {
public string api_key;
}
async Task<dynamic> GetMonitors() {
var key = await Bot.Secrets.GetAsync(“api-key”);
return await Bot.Http.PostJsonAsync(“https://api.uptimerobot.com/v2/getMonitors”, new GetMonitorsData(){api_key = key});
}
async Task<string> ListMonitors() {
IEnumerable<dynamic> monitors = (await GetMonitors()).monitors;
var output = $”Monitors ({monitors.Count()})\n”;
output += String.Join(“\n”, monitors.Select(mon => $”{StatusEmoji[(Status)mon.status]} – {mon.friendly_name} (id: {mon.id})”));
return output;
}
class NewMonitorData {
public string api_key;
public string friendly_name;
public string url;
public string type;
}
async Task<string> NewMonitor(IArguments args) {
var (montype, url, name) = args;
// TODO: Support more than PING type
if (montype.Value != “3”)
return “Unsupported type.”;
// TODO: Check for http/https schemes and strip them if PING type. CheckHostName will fail if it sees them.
if (Uri.CheckHostName(url.Value) is not (UriHostNameType.IPv4 or UriHostNameType.IPv6 or UriHostNameType.Dns))
return “Invalid url.”;
if (String.IsNullOrEmpty(name.Value))
return “Invalid name.”;
var key = await Bot.Secrets.GetAsync(“api-key”);
var response = await Bot.Http.PostJsonAsync(“https://api.uptimerobot.com/v2/newMonitor”, new NewMonitorData(){api_key = key, friendly_name = name.Value, type = montype.Value, url = url.Value});
if (response.stat == “fail”)
return $”Failed to create monitor.\n{response.error.message}”;
return “Monitor Created”;
}
class DeleteMonitorData {
public string api_key;
public string id;
}
async Task<string> DeleteMonitor(IArguments args) {
var (id, _) = args.Pop();
if (String.IsNullOrEmpty(id))
return “Invalid id.”;
var key = await Bot.Secrets.GetAsync(“api-key”);
var response = await Bot.Http.PostJsonAsync(“https://api.uptimerobot.com/v2/deleteMonitor”, new DeleteMonitorData(){api_key = key, id = id});
if (response.stat == “fail”)
return $”Failed to delete monitor ‘{id}’.\n{response.error.message}”;
return “Monitor Deleted”;
}
async Task<string> SetMonitor(IArguments args) {
var (id, key, val) = args;
dynamic data = new ExpandoObject();
data.api_key = await Bot.Secrets.GetAsync(“api-key”);
data.id = id.Value;
// TODO: Check for keys supported by the uptimerobot api
(data as IDictionary<string, Object>).Add(key.Value, val.Value);
var response = await Bot.Http.PostJsonAsync(“https://api.uptimerobot.com/v2/editMonitor”, data as Object);
if (response.stat == “fail”)
return $”Failed to set ‘{key}’ to ‘{val}’ on monitor ‘{id}’.\n{response.error.message}”;
return $”‘{key}’ set to ‘{val}’ on monitor ‘{id}’.”;
}