Last active
September 26, 2024 17:30
-
-
Save marco79cgn/40ce08a1735dede2ab35acf375b6a4df to your computer and use it in GitHub Desktop.
Custom iOS widget that shows both the store and online availability of a given product (for Scriptable.app)
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: blue; icon-glyph: magic; | |
// default zip and partNo - will be overwritten by your widget parameters | |
let zip = '50670' | |
let partNo = "MU7A3ZD/A" | |
// insert your ntfy url | |
const notifyUrl = "https://ntfy.sh/******" | |
// force push notification - set to true in order to test that your setup is working correctly | |
const forcePushNotification = false | |
let storeName | |
let city | |
let params = getParams(args.widgetParameter) | |
const shopAvailability = await getShopAvailability() | |
const onlineAvailability = await getOnlineAvailability() | |
loadCachedAvailability() | |
let widget = new ListWidget() | |
widget.setPadding(0, 8, 6, 8) | |
let appleText = widget.addText("") | |
appleText.centerAlignText() | |
appleText.font = Font.boldMonospacedSystemFont(24) | |
widget.addSpacer(2) | |
let productText = widget.addText(shopAvailability.product) | |
productText.font = Font.boldRoundedSystemFont(13) | |
// productText.textColor = Color.orange() | |
productText.textOpacity = 1 | |
productText.lineLimit = 4 | |
productText.minimumScaleFactor = 0.8 | |
productText.centerAlignText() | |
widget.addSpacer(4) | |
let onlineText = widget.addText("Online") | |
onlineText.font = Font.semiboldRoundedSystemFont(11) | |
onlineText.textOpacity = 0.5 | |
onlineText.centerAlignText() | |
let onlineAvailabilityText = widget.addText(onlineAvailability) | |
onlineAvailabilityText.font = Font.semiboldRoundedSystemFont(11) | |
onlineAvailabilityText.textOpacity = 1 | |
onlineAvailabilityText.centerAlignText() | |
widget.addSpacer(3) | |
let storeText | |
if(shopAvailability.storeName !== shopAvailability.city) { | |
storeText = widget.addText(shopAvailability.storeName + ' ' + shopAvailability.city) | |
} else { | |
storeText = widget.addText(shopAvailability.city) | |
} | |
storeText.font = Font.semiboldRoundedSystemFont(11) | |
storeText.textOpacity = 0.5 | |
storeText.lineLimit = 2 | |
storeText.minimumScaleFactor = 0.8 | |
storeText.centerAlignText() | |
let availabilityText = widget.addText(shopAvailability.message) | |
availabilityText.font = Font.semiboldRoundedSystemFont(11) | |
if (shopAvailability.message.toLowerCase().indexOf('nicht') >=0) { | |
availabilityText.textColor = new Color("#DF0000") | |
await saveStatus("unavailable") | |
} else { | |
availabilityText.textColor = Color.green() | |
// check if unavailable before | |
const previousStatus = await loadCachedAvailability() | |
if (previousStatus == "unavailable") { | |
await sendNotification() | |
} | |
await saveStatus("available") | |
} | |
if(forcePushNotification) { | |
await sendNotification() | |
} | |
availabilityText.textOpacity = 1 | |
availabilityText.minimumScaleFactor = 0.8 | |
availabilityText.centerAlignText() | |
widget.url = "https://store.apple.com/de/xc/product/" + params.partNo | |
if(config.runsInApp) { | |
widget.presentSmall() | |
} | |
Script.setWidget(widget) | |
Script.complete() | |
// fetches the local shop availability | |
async function getShopAvailability() { | |
let availabilityMessage | |
let productName | |
const url = "https://www.apple.com/de/shop/retail/pickup-message?pl=true&parts.0=" | |
+ params.partNo + "&location=" + params.zip | |
let req = new Request(url) | |
try { | |
let result = await req.loadJSON() | |
const store = result.body.stores[0] | |
// console.log("result: " + JSON.stringify(result)) | |
const item = result.body.stores[0].partsAvailability[params.partNo] | |
availabilityMessage = item.pickupSearchQuote | |
productName = item.messageTypes.regular.storePickupProductTitle | |
storeName = store.storeName | |
city = store.city | |
} catch(exception){ | |
console.log("Exception Occured.") | |
availabilityMessage = "N/A" | |
productName = "Keine Internetverbindung" | |
storeName = "Oder ungültige" | |
city = "PartNo" | |
} | |
return { "message" : availabilityMessage, "product" : productName , "storeName" : storeName, "city" : city } | |
} | |
// fetches the online store availability | |
async function getOnlineAvailability() { | |
let deliveryDate | |
const url = "https://www.apple.com/de/shop/delivery-message?mt=regular&parts.0=" + params.partNo | |
let req = new Request(url) | |
try { | |
let result = await req.loadJSON() | |
// console.log(result) | |
deliveryDate = result.body.content.deliveryMessage[params.partNo].regular.deliveryOptions[0].date | |
} catch(exception){ | |
console.log("Exception Occured. " + exception) | |
deliveryDate = "N/A" | |
} | |
return deliveryDate | |
} | |
function getParams(widgetParams) { | |
if(widgetParams) { | |
let split = widgetParams.split(';') | |
partNo = split[0] | |
if(split.length > 1) { | |
zip = split[1] | |
} | |
} | |
return { "partNo" : partNo, "zip" : zip } | |
} | |
async function sendNotification() { | |
let req = new Request(notifyUrl) | |
req.method = "POST" | |
if(forcePushNotification) { | |
req.headers = { | |
"Title": "*TEST MODE* Apple Store", | |
"Click": "https://store.apple.com/de/xc/product/" + params.partNo, | |
"Tags": "apple" | |
} | |
} else { | |
req.headers = { | |
"Title": "Apple Store", | |
"Click": "https://store.apple.com/de/xc/product/" + params.partNo, | |
"Tags": "apple" | |
} | |
} | |
req.body = shopAvailability.product +" ist jetzt verfügbar im Apple Store " + storeName + " in " + city + "! (Tap to order)" | |
req.loadJSON() | |
} | |
async function loadCachedAvailability() { | |
let fm = FileManager.iCloud() | |
let dir = fm.documentsDirectory() | |
let partNoEscaped = params.partNo.replace("/", "-") | |
let path = fm.joinPath(dir, partNoEscaped + ".txt") | |
let lastStatus = Data.fromFile(path) | |
if (lastStatus != null) { | |
return lastStatus.toRawString() | |
} else { | |
return "first-call" | |
} | |
} | |
async function saveStatus(status) { | |
let fm = FileManager.iCloud() | |
let dir = fm.documentsDirectory() | |
let partNoEscaped = params.partNo.replace("/", "-") | |
let path = fm.joinPath(dir, partNoEscaped + ".txt") | |
fm.writeString(path, status) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Vielen Dank und macht absolut Sinn 👍☺️
Da bin ich doch mal gespannt ob ich so mein iPhone bekomme.
Die Frage bezüglich Aktualisierung hattest du weiter oben ja bereits beantwortet