Last active
November 7, 2020 17:51
-
-
Save ffittschen/ba6c2fc5af4065e34de0200f349a4b77 to your computer and use it in GitHub Desktop.
COVID-19 Test Result Widget for DoctorBox (Scriptable)
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
// Variables used by Scriptable. | |
// These must be at the very top of the file. Do not edit. | |
// icon-color: red; icon-glyph: microscope; | |
/** | |
* Parameters: | |
* | |
* TEST_ID: The test number (9 or 10 digit number below barcode) | |
* DAY: The day of your birthdate. | |
* MONTH: The month of your birthdate. | |
* YEAR: The year of your birthdate. | |
* | |
* | |
* Format: | |
* | |
* TEST_ID,DAY,MONTH,YEAR | |
* | |
* | |
* Examples: | |
* | |
* 0123456789,31,03,1984 | |
*/ | |
const RESULT_URL = `https://www.doctorbox.de/covid19.jsp` | |
const API_URL = `https://www.doctorbox.de/covid19-result.jsp` | |
const COLOR_RED = new Color('f6000f') | |
const COLOR_ORANGE = new Color('ff7927') | |
const COLOR_GREEN = new Color('1CC747') | |
const TEST_RESULT_XPATH = `document.evaluate('//*[@id="covid19-2"]//h2', document, null, XPathResult.STRING_TYPE, null).stringValue` | |
let widgetConfiguration = {} | |
if (args.widgetParameter) { | |
widgetConfiguration = parseInput(args.widgetParameter) | |
} else { | |
// widgetConfiguration = { covid19ID: "0123456789", day: "31", month: "03", year: "1984" } | |
} | |
class TestResultWidget { | |
async init() { | |
const widget = await this.createWidget() | |
if (!config.runsInWidget) { | |
await widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
} | |
async createWidget() { | |
const list = new ListWidget() | |
list.spacing = 8 | |
list.url = RESULT_URL | |
list.setPadding(10,10,10,10) | |
const headerRow = list.addStack() | |
headerRow.spacing = 2 | |
addLabelTo(headerRow, "🧪", Font.mediumSystemFont(16)) | |
addLabelTo(headerRow, "Test Result", Font.mediumSystemFont(14)) | |
headerRow.addSpacer() | |
const dataResponse = await getTestResult(widgetConfiguration) | |
if (dataResponse.status === 200) { | |
const resultRow = list.addStack() | |
addResultBlockTo(resultRow, dataResponse.data) | |
list.refreshAfterDate = new Date(Date.now() + 60 * 60 * 1000) // Refresh after 1h | |
} else { | |
list.addSpacer() | |
let errorBox = list.addStack() | |
addLabelTo(errorBox, "⚡️ Daten konnten nicht geladen werden. \nWidget öffnen für reload.", Font.mediumSystemFont(10), COLOR_ORANGE) | |
list.addSpacer() | |
} | |
return list | |
} | |
} | |
class DataResponse { | |
constructor(data, status = 200) { | |
this.data = data | |
this.status = status | |
} | |
} | |
// MARK: Formatting | |
function addLabelTo(view, text, font = false, textColor = false) { | |
const label = view.addText('' + text) | |
if (font) label.font = font | |
if (textColor) label.textColor = (typeof textColor === 'string') ? new Color(textColor) : textColor | |
return label | |
} | |
function addResultBlockTo(view, data) { | |
const resultBlockBox = view.addStack() | |
resultBlockBox.backgroundColor = new Color('cccccc', 0.1) | |
resultBlockBox.spacing = 2 | |
resultBlockBox.cornerRadius = 14 | |
resultBlockBox.setPadding(8,8,8,8) | |
addResult(resultBlockBox, data) | |
resultBlockBox.addSpacer() | |
return resultBlockBox | |
} | |
function addResult(view, data) { | |
let formatter = new DateFormatter() | |
formatter.useShortDateStyle() | |
formatter.useShortTimeStyle() | |
const dateString = formatter.string(new Date()) | |
const resultBox = view.addStack() | |
resultBox.spacing = 2 | |
resultBox.layoutVertically() | |
addLabelTo(resultBox, dateString, Font.mediumSystemFont(10), new Color('888888')) | |
addLabelTo(resultBox, data.text, Font.mediumSystemFont(18), data.color) | |
resultBox.addSpacer() | |
} | |
// MARK: Fetching Data | |
async function getTestResult(widgetConfiguration) { | |
const formParams = Object.keys(widgetConfiguration).map((key) => { | |
return encodeURIComponent(key) + '=' + encodeURIComponent(widgetConfiguration[key]) | |
}).join('&') | |
try { | |
let request = new Request(API_URL) | |
request.method = "POST" | |
request.headers = { | |
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8" | |
} | |
request.body = formParams | |
let webView = new WebView() | |
await webView.loadRequest(request) | |
const xPathResult = await webView.evaluateJavaScript(TEST_RESULT_XPATH) | |
const normalizedResponse = xPathResult | |
.trim() | |
.replace(/(\r\n|\n|\r)/gm," ") // Replace any newlines | |
.replace(/\s+/g," ") // Replace extra white space | |
.replace(/\s?\(.*?\)/gm, "") // Remove test number | |
const parsedResponse = parseResponse(normalizedResponse) | |
return new DataResponse(parsedResponse) | |
} catch(e) { console.warn(e) } | |
return new DataResponse({}, 404) | |
} | |
// MARK: Parsing | |
function parseInput (input) { | |
const configArray = input.split(",") | |
const config = { | |
covid19ID: configArray[0], | |
day: configArray[1], | |
month: configArray[2], | |
year: configArray[3] | |
} | |
return config | |
} | |
function parseResponse (response) { | |
const resultPrefix = "Ihr Ergebnis" | |
let result = { | |
text: "", | |
color: new Color('999999') | |
} | |
if (response.includes("kein Testergebnis")) { | |
result.text = "No result yet" | |
} else if (response.startsWith(resultPrefix) && response.includes("negativ")) { | |
result.text = "Negative" | |
result.color = COLOR_GREEN | |
} else if (response.startsWith(resultPrefix) && response.includes("positiv")) { | |
result.text = "Positive" | |
result.color = COLOR_RED | |
} else if (response.startsWith(resultPrefix)) { | |
result.text = response | |
result.color = COLOR_ORANGE | |
} else { | |
console.warn("Could not parse response: " + response) | |
result.text = "Could not parse response" | |
result.color = COLOR_ORANGE | |
} | |
return result | |
} | |
await new TestResultWidget().init() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
COVID-19 Test Result Widget for DoctorBox (Scriptable)
This Widget will periodically check the DoctorBox website for the results of a COVID-19 test. The Widget can be configured with your test number and birthdate by passing the following parameters to the Scriptable widget:
Parameters
TEST_ID
: The test number (9 or 10 digit number below barcode)DAY
: The day of your birthdate.MONTH
: The month of your birthdate.YEAR
: The year of your birthdate.Format
TEST_ID,DAY,MONTH,YEAR
Example
0123456789,31,03,1984