// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: deep-blue; icon-glyph: shopping-cart; // Version 1.2.1 /// Used by enums const enumValue = (name) => Object.freeze({toString: () => name}) /// Used by DisplayMode const lightBackgroundColor = Color.white() const darkBackgroundColor = new Color('#222', 1.0) const autoBackgroundColor = Color.dynamic(lightBackgroundColor, darkBackgroundColor) const lightTextColor = Color.black() const darkTextColor = Color.white() const autoTextColor = Color.dynamic(lightTextColor, darkTextColor) const lightBackgroundProgressColor = new Color('#D2D2D7', 1.0) const darkBackgroundProgressColor = new Color('#707070', 1.0) const autoBackgroundProgressColor = Color.dynamic(lightBackgroundProgressColor, darkBackgroundProgressColor) const lightFillProgressColor = new Color('#008009', 1.0) const darkFillProgressColor = new Color('#00A00D', 1.0) const autoFillProgressColor = Color.dynamic(lightFillProgressColor, darkFillProgressColor) /** * Enum for display mode. * @readonly * @enum {{name: string, backgroundColor: Color, textColor: Color}} */ const DisplayMode = Object.freeze({ LIGHT: { name: "light", backgroundColor: lightBackgroundColor, textColor: lightTextColor, backgroundProgressColor: lightBackgroundProgressColor, fillProgressColor: lightFillProgressColor, toString: () => name }, DARK: { name: "dark", backgroundColor: darkBackgroundColor, textColor: darkTextColor, backgroundProgressColor: darkBackgroundProgressColor, fillProgressColor: darkFillProgressColor, toString: () => name }, AUTO: { name: "auto", backgroundColor: autoBackgroundColor, textColor: autoTextColor, backgroundProgressColor: autoBackgroundProgressColor, fillProgressColor: autoFillProgressColor, toString: () => name } }) /** * Enum for widget family. * @readonly * @enum {Symbol} */ const WidgetFamily = Object.freeze({ SMALL: enumValue("small"), MEDIUM: enumValue("medium"), LARGE: enumValue("large") }) //////////////////// - EDIT ME - /////////////////////////// /// Display mode /// /// - DisplayMode.LIGHT: Light mode /// - DisplayMode.DARK: Dark mode /// - DisplayMode.AUTO: Follow system settings const displayMode = DisplayMode.LIGHT /// Debug mode: on / off const debug = false /// Debug input, following widget format: /// - ";" /// - ";;" /// /// ie. const debugInput = "W111111111;tim@apple.com;5" const debugInput = null /// Debug widget size (LARGE, MEDIUM or SMALL) const debugWidgetFamily = WidgetFamily.MEDIUM //////////////////////////////////////////////////////////// const cacheMinutes = 60 * 2 const today = new Date() let width; let widgetFamily; const h = 5 const backgroundColor = displayMode.backgroundColor const textColor = displayMode.textColor const backgroundProgressColor = displayMode.backgroundProgressColor const fillProgressColor = displayMode.fillProgressColor if (debug && debugWidgetFamily !== null) { widgetFamily = debugWidgetFamily } else { switch (config.widgetFamily) { case 'small': widgetFamily = WidgetFamily.SMALL width = 200 break case 'medium': widgetFamily = WidgetFamily.MEDIUM width = 400 break case 'large': widgetFamily = WidgetFamily.LARGE width = 400 break } } switch (widgetFamily) { case WidgetFamily.SMALL: width = 200 break case WidgetFamily.MEDIUM: width = 400 break case WidgetFamily.LARGE: width = 400 break } //////////////////////////////////////////////////////////// let widgetInputRAW = args.widgetParameter; let widgetInput; if (widgetInputRAW !== null || (debug && debugInput !== null)) { if (widgetInputRAW !== null) { widgetInput = widgetInputRAW.toString().trim().split(';').map(v => v.trim()) } else { widgetInput = debugInput.trim().split(';').map(v => v.trim()) } if (!/^[A-Za-z][0-9]+/.test(widgetInput[0])) { throw new Error('Invalid ordernumber format: "' + widgetInput[0] + '"') } if (widgetInput[2] && !/^[\d]+$/.test(widgetInput[2])) { throw new Error('Third parameter has to be a number') } } else { throw new Error('No Ordernumber and E-Mail address set') } //////////////////////////////////////////////////////////// const files = FileManager.local() const path = files.joinPath(files.cacheDirectory(), "widget-apple-store-order-" + widgetInput[0]) const cacheExists = files.fileExists(path) const cacheDate = cacheExists ? files.modificationDate(path) : 0 //////////////////////////////////////////////////////////// const localeText = { default: ['Day', 'Days', { 'PLACED': 'Order Placed', 'PROCESSING': 'Processing', 'PREPARED_FOR_SHIPMENT': 'Preparing for Ship', 'SHIPPED': 'Shipped', 'DELIVERED': 'Delivered' }], en: ['Day', 'Days', { 'PLACED': 'Order Placed', 'PROCESSING': 'Processing', 'PREPARED_FOR_SHIPMENT': 'Preparing for Ship', 'SHIPPED': 'Shipped', 'DELIVERED': 'Delivered' }], de: ['Tag', 'Tage', { 'PLACED': 'Bestellung aufgegeben', 'PROCESSING': 'Vorgang läuft', 'PREPARED_FOR_SHIPMENT': 'Versand wird vorbereitet', 'SHIPPED': 'Bestellung versandt', 'DELIVERED': 'Geliefert' }], fr: ['Jour', 'Jours', { 'PLACED': 'Commande enregistrée', 'PROCESSING': 'Traitement', 'PREPARED_FOR_SHIPMENT': 'En cours de préparation pour expédition', 'SHIPPED': 'Expédiée', 'DELIVERED': 'Livrée' }], es: ['día', 'días', { 'PLACED': 'Pedido recibido', 'PROCESSING': 'Procesando', 'PREPARED_FOR_SHIPMENT': 'Preparando envío', 'SHIPPED': 'Enviado', 'DELIVERED': 'Entregado' }], it: ['giorno', 'giorni', { 'PLACED': 'Ordine inoltrato', 'PROCESSING': 'ElaborazioneIn', 'PREPARED_FOR_SHIPMENT': 'Spedizione in preparazione', 'SHIPPED': 'Spedito', 'DELIVERED': 'ConsegnatoIncompleto' }] } //////////////////////////////////////////////////////////// const parseLongDate = (stringDate) => { const months = { 'January': 0, 'February': 1, 'March': 2, 'April': 3, 'May': 4, 'June': 5, 'July': 6, 'August': 7, 'September': 8, 'October': 9, 'November': 10, 'December': 11 } const m = stringDate.match(/([\w]+)[\s]([\d]{1,2}),[\s]([0-9]{4})/) return new Date(m[3], months[m[1]], m[2]) } const parseShortDate = (stringDate, orderDate) => { const months = { 'Jan': 0, 'Feb': 1, 'Mar': 2, 'Apr': 3, 'May': 4, 'Jun': 5, 'Jul': 6, 'Aug': 7, 'Sep': 8, 'Oct': 9, 'Okt': 9, 'Nov': 10, 'Dec': 11, 'Dez': 11 } let m m = stringDate.match(/([\d]{1,2}) ([\w]{3})/) if (!m) { m = stringDate.match(/([\w]+),? ([\d]{1,2})/) if (m) { const t = m[1].slice(0, 3) m[1] = m[2] m[2] = t } else { throw new Error('Failed to extract the delivery date from string: ' + stringDate) } } let deliveryDate = new Date((new Date().getFullYear()), months[m[2]], m[1]) if (deliveryDate < orderDate) { deliveryDate.setFullYear(deliveryDate.getFullYear() + 1) } return deliveryDate } //////////////////////////////////////////////////////////// function creatProgress(total, havegone) { const context = new DrawContext() context.size = new Size(width, h) context.opaque = false context.respectScreenScale = true context.setFillColor(backgroundProgressColor) const path = new Path() path.addRoundedRect(new Rect(0, 0, width, h), 3, 2) context.addPath(path) context.fillPath() context.setFillColor(fillProgressColor) const path1 = new Path() const path1width = (width * havegone / total > width) ? width : width * havegone / total path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2) context.addPath(path1) context.fillPath() return context.getImage() } //////////////////////////////////////////////////////////// const getTimeRemaining = function (endtime) { const total = Date.parse(endtime) - Date.parse(new Date()); const seconds = Math.floor((total / 1000) % 60); const minutes = Math.floor((total / 1000 / 60) % 60); const hours = Math.floor((total / (1000 * 60 * 60)) % 24); const days = Math.floor(total / (1000 * 60 * 60 * 24)); return { total, days, hours, minutes, seconds }; } //////////////////////////////////////////////////////////// const getOrderdetails = async (ordernumber, email) => { const reqSession = new Request('https://secure.store.apple.com/shop/order/list') resSession = await reqSession.loadString() const CookieValues = reqSession.response.cookies.map((v) => { return v.name + "=" + v.value }) const xAosStkMatch = resSession.match(/"x-aos-stk":"([\w-_]+)"/) if (!xAosStkMatch) { throw new Error('Needed x-aos-stk token not found') } const postUrl = (reqSession.response.url.replace('/orders', '/orderx')) + '&_a=guestUserOrderLookUp&_m=signIn.orderLookUp' const postReq = new Request(postUrl) postReq.headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Referer': reqSession.response.url, 'x-aos-model-page': 'olssSignInPage', 'x-aos-stk': xAosStkMatch[1], 'X-Requested-With': 'XMLHttpRequest', 'Cookie': CookieValues.join('; ') } postReq.method = "POST"; postReq.body = `signIn.orderLookUp.orderNumber=${ordernumber}&signIn.orderLookUp.emailAddress=${email}` const resPostReq = await postReq.loadString() if (postReq.response.statusCode !== 200) { throw new Error(`Got HTTP ${postReq.response.statusCode} from API.`) } let postResData try { postResData = JSON.parse(resPostReq) } catch (e) { throw new Error('Can\'t parse API response.') } if (postResData['head']['status'] !== 302) { throw new Error('Fetching the data failed. Got unexpected response. Please try it later.') } const req = new Request(postResData['head']['data']['url']) const res = await req.loadString() const rawJSON = res.match(/