Skip to content

Instantly share code, notes, and snippets.

@pirafrank
Created November 15, 2020 11:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pirafrank/1d7d729082e063de343ee6df4aa0b47a to your computer and use it in GitHub Desktop.
Save pirafrank/1d7d729082e063de343ee6df4aa0b47a to your computer and use it in GitHub Desktop.
Scriptable script to list your Hetzner servers in the app and on widgets. Requires iOS 14+ and Scriptable 1.6
// Your Hetzner API token
// Go to Cloud Console > [your project] > Security > API tokens
const token = "PUT_YOUR_TOKEN_HERE" // main
// urls
let listServersUrl = "https://api.hetzner.cloud/v1/servers"
let widget = await createWidget(listServersUrl)
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app
// list server
await list_servers(listServersUrl)
//widget.presentMedium()
}
// end the script
Script.complete()
// *********
// functions
// *********
async function list_servers(url) {
let req = new Request(url)
req.headers = {
Authorization: "Bearer " + token
}
let resp = await req.loadJSON()
console.log(resp)
let table = new UITable()
// inject row header in results
let header = {}
header.name = "Name"
header.status = "Status"
header.public_net = {}
header.public_net.ipv4 = {}
header.public_net.ipv4.ip = "IPv4"
header.protection = {}
header.protection.delete = "🔒"
resp.servers.splice(0,0,header)
// cycle results
for (server of resp.servers) {
let row = new UITableRow()
// cast dcell to a string, otherwise we will get an error like "Expected value of type UITableCell but got value of type null."
let nameCell = row.addText(server.name)
let statusCell = row.addText(server.status)
let ipCell = row.addText(server.public_net.ipv4.ip)
let protectionStatus
if(typeof server.protection.delete === "boolean")
protectionStatus = server.protection.delete ? "✅" : "❌"
else
protectionStatus = server.protection.delete
let protectedCell = row.addText(protectionStatus)
// Set the width weights of our cells
protectedCell.widthWeight = 15
nameCell.widthWeight = 25
statusCell.widthWeight = 20
ipCell.widthWeight = 40
// Set height of the row and spacing between cells, in pixels.
row.height = 60
row.cellSpacing = 10
// add row to table
table.addRow(row)
}
//QuickLook.present(table)
table.present()
}
async function get_number_of_servers (url) {
let req = new Request(url)
req.headers = {
Authorization: "Bearer " + token
}
let resp = await req.loadJSON()
// get number of servers per available status values
let result = resp.servers.reduce((acc, server) => {
(acc[server.status] = (acc[server.status] || 0) + 1, acc);
return acc;
}, {});
return result
}
async function createWidget(url) {
// get number of servers from API
let servers = await get_number_of_servers(listServersUrl)
// Show widget icon and title
let title = "Hetzner"
let widget = new ListWidget()
// background
let gradient = new LinearGradient()
gradient.locations = [0, 1]
gradient.colors = [
new Color("D72300"),
new Color("DF2700")
]
widget.backgroundGradient = gradient
let titleStack = widget.addStack()
let cloudSymbol = SFSymbol.named("cloud")
let cloudElement = titleStack.addImage(cloudSymbol.image)
cloudElement.imageSize = new Size(16, 16)
cloudElement.tintColor = Color.white()
cloudElement.imageOpacity = 0.7
cloudElement.url = "https://google.com"
titleStack.addSpacer(4)
let titleElement = titleStack.addText(title)
titleElement.textColor = Color.white()
titleElement.textOpacity = 0.7
titleElement.font = Font.mediumSystemFont(13)
widget.addSpacer(10)
// show real widget info
let infoText = "Your servers"
let infoElement = widget.addText(infoText)
infoElement.textColor = Color.white()
infoElement.font = Font.mediumSystemFont(14)
widget.addSpacer(6)
for (item in servers) {
let infoStack = widget.addStack()
let descElement = infoStack.addText(item.toUpperCase())
descElement.textColor = Color.white()
descElement.font = Font.systemFont(16)
infoStack.addSpacer()
let numberOfServersElement = infoStack.addText(String(servers[item]))
numberOfServersElement.textColor = Color.white()
numberOfServersElement.font = Font.systemFont(16)
numberOfServersElement.minimumScaleFactor = 0.6
}
return widget
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment