-
-
Save marco79cgn/40ce08a1735dede2ab35acf375b6a4df to your computer and use it in GitHub Desktop.
// 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) | |
} |
Unfortunately that's not possible (iOS restrictions).
Gibt es eine Möglichkeit einer bestimmten Konfiguration zu die Verfügbarkeit anzeigen zu lassen?
Mac Studio
Hey there, thanks for the good work. Could you also limit the item view to one specific Apple Store, eg if I don't want to see nearby stores but only one specific? Thanks!
Hi @marco79cgn, thank you for your wonderful script. Unfortunately it does not work any longer:
2022-07-29 08:38:37: Error on line 17:33: Expected value of type string but got value of type undefined.
Any recent changes to the Apple interface?
Found it. Had to change line 82 to:
productName = item.messageTypes.regular.storePickupProductTitle
Update 17.09.2023, 21:17 Uhr
- fixed script
- added notifications via ntfy app
→ please follow the updated instructions in the first comment!
bei mir scheitert es bereits beim topic für subscribe in notify. der button ist grau..
@r32er
Einfach in der App oben rechts auf das „+“ tippen und einen beliebigen Namen eingeben, danach auf Subscribe.
@r32er Einfach in der App oben rechts auf das „+“ tippen und einen beliebigen Namen eingeben, danach auf Subscribe.)
und genau das subscribe bleibt grau geht nicht. ah lel lag am leerzeichen sorry.
und die "ntfy.sh/iphone15...." muss dann in line 8 im script rein?
Hi Marco,
kannst du mir sagen warum ich diesen Schritt benötige?
configure the widget on your homescreen with the following parameter scheme (separated by semicolon):
MJ123D/A;50670;de
Ich habe Scriptable unter meinen Widgets hinzugefügt.
Weshalb sollte ich unter Parameter nun MJ123D/A;50670;de eingeben?
In deinem Script sind diese Werte doch bereits festgelegt!?
Da ich 3 Apple Stores in der Nähe habe, habe ich dein Script zweimal kopiert und die PLZ geändert.
Würde es auch einfacher gehen?
Besten Dank
Hi Nick,
du hast dir deine Frage bereits selbst beantwortet. ;)
Wenn du die Parameter in den Widget Einstellungen vornimmst, musst du das Skript selbst überhaupt nicht anfassen bzw. editieren. Zudem musst du es nur ein einziges Mal in Scriptable hinterlegen und kannst dennoch mehrere Widgets für unterschiedliche Produkte oder Postleitzahlen auf deinem Homescreen anlegen.
Die Kodierung im Skript selbst ist nur ein Fallback, falls man keine Parameter im Widget selbst gesetzt hat. Letztere haben aber Priorität, sofern vorhanden.
Hi Nick, du hast dir deine Frage bereits selbst beantwortet. ;) Wenn du die Parameter in den Widget Einstellungen vornimmst, musst du das Skript selbst überhaupt nicht anfassen bzw. editieren. Zudem musst du es nur ein einziges Mal in Scriptable hinterlegen und kannst dennoch mehrere Widgets für unterschiedliche Produkte oder Postleitzahlen auf deinem Homescreen anlegen.
Die Kodierung im Skript selbst ist nur ein Fallback, falls man keine Parameter im Widget selbst gesetzt hat. Letztere haben aber Priorität, sofern vorhanden.
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
@marco79cgn is possible to add a small button to refresh with scriptable? to interact with the widget? not sure if possible with iOS widgets?