diff --git a/PAYBACK-Points/PAYBACK-Points.js b/PAYBACK-Points/PAYBACK-Points.js new file mode 100644 index 0000000..baae46b --- /dev/null +++ b/PAYBACK-Points/PAYBACK-Points.js @@ -0,0 +1,149 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: blue; icon-glyph: shopping-basket; + +// Version 1.0.0 + +const cacheMinutes = 60 * 12 // 12h +const today = new Date() +const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:82.0) Gecko/20100101 Firefox/82.0' + +//////////////////////////////////////////////////////////// +let widgetInputRAW = args.widgetParameter; +let widgetInput; +if (widgetInputRAW !== null) { + widgetInput = widgetInputRAW.toString().trim().split(';') + + if (widgetInput[0] && !/^[\d]+$/.test(widgetInput[0])) { + throw new Error('the first parameter has to be a number') + } +} +//////////////////////////////////////////////////////////// +const getTokenAndCookies = async () => { + const req = new Request('https://www.payback.de/login') + req.headers = { + 'User-Agent': userAgent + } + const htmlString = await req.loadString() + const csrfToken = htmlString.match(//) + if (!csrfToken) { + if (htmlString.indexOf('dass Sie ein Bot sein') !== -1) { + if (config.runsInApp) { + const alert = new Alert() + alert.message = 'Please solve the capture so that the widget is no longer recognized as a bot.' + alert.addAction('Ok') + await alert.present() + const r = new Request('https://www.payback.de/login') + r.headers = { + 'User-Agent': userAgent + } + const view = new WebView() + view.loadRequest(r) + await view.present() + } else { + throw new Error('Widget was detected as a bot. Please execute the script in the Scriptable App to solve the capture.') + } + } + throw new Error('Unable to get Security Token') + } + return { token: csrfToken[1], cookies: req.response.cookies } +} +//////////////////////////////////////////////////////////// +const getPoints = async () => { + const { token, cookies } = await getTokenAndCookies() + if (widgetInput === undefined || !widgetInput[0] || !widgetInput[1]) { + throw new Error('No customer-number and password set. Please set needed data to widget parameter.') + } + let cookieString = cookies.map(function (v) { + return v.name + "=" + v.value + }).join('; ') + const req = new Request('https://www.payback.de/resources/action/login/login-action') + req.headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': userAgent, + Cookie: cookieString + } + req.method = 'POST'; + req.body = `login-method=pwd&SITE_NAME=payback-main-page&_csrf=${token}&aliasTypePassName=${widgetInput[0]}&passwordName=${widgetInput[1]}` + + const res = await req.loadString() + if (req.response.statusCode !== 200) { + throw new Error('Unable to fetch Point. Status-Code: ' + req.response.statusCode) + } + const points = res.match(/(.*)<\/strong><\/a>/) + if (!points) { + throw new Error('Unable to get Point out of HTML Body') + } + return points[1] +} +//////////////////////////////////////////////////////////// +const files = FileManager.local() + +const cachePath = files.joinPath(files.cacheDirectory(), "widget-payback-" + ((widgetInput !== undefined) ? widgetInput[0] : '')) +const cacheExists = files.fileExists(cachePath) +const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0 +let points +if (!config.runsInApp && cacheExists && (today.getTime() - cacheDate.getTime()) < (cacheMinutes * 60 * 1000)) { + console.log("Get from Cache") + points = files.readString(cachePath) +} else { + console.log("Get from Website") + try { + points = await getPoints() + console.log("Write to Cache") + files.writeString(cachePath, points) + } catch (e) { + console.error('Fetching data from website failed:') + console.error(e) + if (cacheExists) { + console.warn('Fallback to Cache') + points = files.readString(cachePath) + } else { + throw e + } + } +} + +const imageCachePath = files.joinPath(files.cacheDirectory(), "widget-payback-logo") +const imageCachePathExists = files.fileExists(imageCachePath) +let logo +if (imageCachePathExists) { + logo = files.readImage(imageCachePath) +} else { + const imgReq = new Request('https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Payback_Logo.svg/200px-Payback_Logo.svg.png') + logo = await imgReq.loadImage() + files.writeImage(imageCachePath, logo) +} +//////////////////////////////////////////////////////////// + +let widget = new ListWidget(); + +if (!points) { + widget.addText('Unable to get Payback Points. Please check logs.') +} else { + widget.setPadding(10, 10, 10, 10) + widget.backgroundColor = new Color('0046AA') + + const logoElement = widget.addImage(logo) + logoElement.imageSize = new Size(35, 35) + logoElement.applyFillingContentMode() + logoElement.centerAlignImage() + + widget.addSpacer(15) + + const pointText = widget.addText(points) + pointText.font = Font.regularSystemFont(36) + pointText.textColor = Color.white() + pointText.centerAlignText() + pointText.minimumScaleFactor = 0.5 + pointText.lineLimit = 1 + + widget.addSpacer() +} +if (!config.runsInWidget) { + await widget.presentSmall() +} else { + // Tell the system to show the widget. + Script.setWidget(widget) + Script.complete() +} \ No newline at end of file diff --git a/PAYBACK-Points/README.md b/PAYBACK-Points/README.md new file mode 100644 index 0000000..05b2a1e --- /dev/null +++ b/PAYBACK-Points/README.md @@ -0,0 +1,16 @@ +# PAYBACK Points +![PAYBACK Points Widget Preview light](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/PAYBACK-Points/previewLight.jpeg) + +This widget shows you your PAYBACK points. + +Notice: PAYBACK points will be cached and updated only every 12 hours. + +[[Download]](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/PAYBACK-Points/PAYBACK-Points.js) + +## Setup +Set the customer number and your password, separated by `;` to the Widget Parameter. + +![Setup](https://raw.githubusercontent.com/ThisIsBenny/iOS-Widgets/main/PAYBACK-Points/setup.jpeg) + +## Troubleshooting +* The widget will be recognized as a bot from time to time and therefore cannot update the points. To solve the problem, you have to execute the script via the Scriptable App to solve the captcha \ No newline at end of file diff --git a/PAYBACK-Points/previewLight.jpeg b/PAYBACK-Points/previewLight.jpeg new file mode 100644 index 0000000..c71deba Binary files /dev/null and b/PAYBACK-Points/previewLight.jpeg differ diff --git a/PAYBACK-Points/setup.jpeg b/PAYBACK-Points/setup.jpeg new file mode 100644 index 0000000..fe30c01 Binary files /dev/null and b/PAYBACK-Points/setup.jpeg differ