Last active
December 21, 2020 16:30
-
-
Save fschiettecatte/32bb21527645312606aa7ec98b5d7048 to your computer and use it in GitHub Desktop.
SwiftBar plugin to display a website status in the macOS menu bar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
#-------------------------------------------------------------------------- | |
# | |
# Author: Francois Schiettecatte (FS Consulting LLC.) | |
# Creation Date: October 10, 2020 | |
# | |
#-------------------------------------------------------------------------- | |
# | |
# Description: | |
# | |
# BitBar plugin to display a website status in the menu bar: | |
# | |
# https://github.com/matryer/bitbar | |
# | |
# This code assumes that the website has a status URL that returns a JSON | |
# object that contains a 'status' key with an integer value of 200, | |
# for example: | |
# | |
# { "status": 200 } | |
# | |
# Obviously you should adapt this to your own sites. | |
# | |
#-------------------------------------------------------------------------- | |
# | |
# Imported modules | |
# | |
import copy | |
import json | |
import socket | |
import sys | |
import time | |
import urllib.error | |
import urllib.request | |
# Use this monkey patch around SSL certificate errors: | |
# [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590) | |
import ssl | |
ssl._create_default_https_context = ssl._create_unverified_context | |
#-------------------------------------------------------------------------- | |
# | |
# Constants | |
# | |
# Site dict to check, expects JSON object with {'status': 200} | |
SITES_LIST = [ | |
# Development Website | |
# { | |
# 'name': 'Development Website', | |
# 'url': 'http://website.internal:8000/status', | |
# }, | |
# Staging Website | |
{ | |
'name': 'Staging Website', | |
'url': 'http://staging.website.internal/status', | |
}, | |
# Production Website | |
{ | |
'name': 'Production Website', | |
'url': 'https://website.net/status', | |
}, | |
] | |
# Connection timeout | |
CONNECTION_TIMEOUT = 60 | |
# Connection attempts | |
CONNECTION_ATTEMPTS = 5 | |
# Connection interval delay (seconds) | |
CONNECTION_INTERVAL_DELAY = 1 | |
#-------------------------------------------------------------------------- | |
# | |
# Method: checkSite | |
# | |
# Purpose: check site | |
# | |
# Parameters: url the url | |
# | |
# Exceptions: ValueError Invalid url | |
# | |
# Return: (True, None,) for ok, (False, message,) if not | |
# | |
def checkSite(url): | |
# Check parameters | |
if not url: | |
raise ValueError('Invalid url') | |
# Content, content dict, status, request time, exception and error, declared here because we need it later | |
content = None | |
contentDict = None | |
status = None | |
savedException = None | |
savedError = None | |
# Loop for the number of attempts | |
for attempt in range(0, CONNECTION_ATTEMPTS): | |
# Sleep between attempts | |
if attempt > 0: | |
time.sleep(CONNECTION_INTERVAL_DELAY) | |
# Clear the content, content dict, status, request time, exception and error | |
content = None | |
contentDict = None | |
status = None | |
savedException = None | |
savedError = None | |
# Set the start time | |
startTime = int(round(time.time() * 1000)) | |
# Wrap all in a 'try' to catch exceptions | |
try: | |
# Open the URL | |
response = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT) | |
# Save the status and read the content | |
status = response.status | |
content = response.read() | |
# Decode the content and parse the JSON string | |
contentDict = json.loads(content.decode()) | |
# Check the top level status on the content dict, | |
# we already know that the request status is 200 | |
if contentDict and contentDict.get('status') == 200: | |
return True, None | |
# HTTP error | |
except urllib.error.HTTPError as exception: | |
savedException = exception | |
status = exception.code | |
# URL error | |
except urllib.error.URLError as exception: | |
savedException = exception | |
# JSON decode error | |
except json.JSONDecodeError as exception: | |
savedException = exception | |
# Socket error | |
except (socket.error, socket.timeout) as error: | |
savedError = error | |
# Set the message | |
message = None | |
if savedError: | |
message = 'error: \'{0}\''.format(savedError) | |
elif savedException: | |
message = 'exception: \'{0}\''.format(savedException) | |
elif contentDict: | |
message = 'site status: \'{0}\''.format(contentDict.get('status')) | |
elif status: | |
message = 'request status: \'{0}\''.format(status) | |
elif not contentDict: | |
message = 'missing/invalid content' | |
return False, message | |
#-------------------------------------------------------------------------- | |
# | |
# Method: checkSites | |
# | |
# Purpose: check sites | |
# | |
# Parameters: | |
# | |
# Exceptions: | |
# | |
# Return: | |
# | |
def checkSites(): | |
# Up list, copy of site dict | |
upSiteDictList = list() | |
# Down list, copy of site dict | |
downSiteDictList = list() | |
# Check each site | |
for siteDict in SITES_LIST: | |
# Make a copy of the site dict | |
siteDict = copy.deepcopy(siteDict) | |
# Check the site | |
status, message = checkSite(siteDict['url']) | |
# Add site dict & message to the appropriate list | |
if status: | |
upSiteDictList.append(siteDict) | |
else: | |
siteDict['message'] = message | |
downSiteDictList.append(siteDict) | |
# Add down site list to rotate in the menu bar | |
if downSiteDictList: | |
for downSiteDict in downSiteDictList: | |
print('π {0}'.format(downSiteDict['name'])) | |
else: | |
print('β ') | |
# Separator | |
print('---') | |
# Down site list | |
for name, url, message in downSiteDictList: | |
print('π {0} ({1}) | href={2}'.format(downSiteDict['name'], downSiteDict.get('message', 'N/A'), downSiteDict['url'])) | |
# Up site list | |
for upSiteDict in upSiteDictList: | |
if downSiteDictList: | |
print('β ', end='') | |
print('{0} | href={1}'.format(upSiteDict['name'], upSiteDict['url'])) | |
#-------------------------------------------------------------------------- | |
# | |
# Method: __main__ | |
# | |
# Purpose: __main__ | |
# | |
# Parameters: | |
# | |
# Exceptions: | |
# | |
# Returns: | |
# | |
if __name__ == '__main__': | |
# Check sites | |
checkSites() | |
# And exit | |
sys.exit(0) | |
#-------------------------------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment