/* A widget that displays your current Twitter follower count and growth chart, powered by your ilo.so dashboard. (Only works for ilo.so users!) You will need to put in your Twitter username and API key in the widget's Parameter field. Like this: "username:myapikey" You can get your API key from ilo.so/settings/) Sign up to ilo at https://ilo.so Version 1.0.0 (March 3, 2021) by Dan Rowden (@dr) */ // Chart config const daysInChart = 7 // How many days you want to show in the chart // Some colours you can change const chartColor = "#c88e69" const chartOpacity = 0.3 const widgetBgColor = "#fff7f2" const usernameColor = '#6d6d6d' const followersColor = '#1e272e' const dailyChangeColor = '#b77a54' // ===================================================================== // Here starts all the widget code. Feel free to edit below if you want! // Share your widget on Twitter! I'm @dr :) // ===================================================================== let twitterParameter = args.widgetParameter.split(':') let twitterUsername = twitterParameter[0] let apiKey = twitterParameter[1] let url = "https://api.ilo.so/v1/stats/"+twitterUsername+"/?history="+daysInChart+'&api_key='+apiKey let req = new Request(url) const json = await req.loadJSON() const followers_total = parseInt(json['followers']).toLocaleString() const followers_today = parseInt(getCountsFromData(json['followers_daily']).pop()).toLocaleString() const chartData = json['followers_total_daily'] // LineChart by https://kevinkub.de/ // Used as the widget background class LineChart { constructor(width, height, values) { this.ctx = new DrawContext(); this.ctx.size = new Size(width, height); this.values = getCountsFromData(values); } _calculatePath() { let maxValue = Math.max(...this.values); let minValue = Math.min(...this.values); let difference = maxValue - minValue; let count = this.values.length; let step = this.ctx.size.width / (count - 1); let points = this.values.map((current, index, all) => { let x = step*index; let y = this.ctx.size.height - (current - minValue) / difference * (this.ctx.size.height * 0.5); return new Point(x, y); }); return this._getSmoothPath(points); } _getSmoothPath(points) { let path = new Path(); path.move(new Point(0, this.ctx.size.height)); path.addLine(points[0]); for(let i = 0; i < points.length-1; i++) { let xAvg = (points[i].x + points[i+1].x) / 2; let yAvg = (points[i].y + points[i+1].y) / 2; let avg = new Point(xAvg, yAvg); let cp1 = new Point((xAvg + points[i].x) / 2, points[i].y); let next = new Point(points[i+1].x, points[i+1].y); let cp2 = new Point((xAvg + points[i+1].x) / 2, points[i+1].y); path.addQuadCurve(avg, cp1); path.addQuadCurve(next, cp2); } path.addLine(new Point(this.ctx.size.width, this.ctx.size.height)); path.closeSubpath(); return path; } configure(fn) { let path = this._calculatePath(); if(fn) { fn(this.ctx, path); } else { this.ctx.addPath(path); this.ctx.fillPath(path); } return this.ctx; } } // Convert API data into a simple list function getCountsFromData(values) { let chartValues = [] for(let data in values) { let thisDay = values[data] let keys = Object.keys(thisDay); for (let i = 0; i < keys.length; i++) { let val = thisDay[keys[i]]; chartValues.push(val); } } return chartValues; } // Fonts const smallText = new Font("HelveticaNeue", 14); const largeText = new Font("HelveticaNeue-Medium", 16); // Create the widget let w = new ListWidget() w.backgroundColor = new Color(widgetBgColor); w.url = 'https://ilo.so/'+twitterUsername+'/' var refreshDate = Date.now() + 1000*60*5 // Refresh after at least 5 minutes w.refreshAfterDate = new Date(refreshDate) // ilo icon let icon = await loadImage('https://ilo.so/static/favicon.png') let iconImg = w.addImage(icon) iconImg.imageSize = new Size(20,20) w.addSpacer(20) // Show username const usernameStr = '@' + twitterUsername textUsername = w.addText(usernameStr) textUsername.textColor = new Color(usernameColor); textUsername.font = smallText; w.addSpacer(4) // Show current follower count const followerStr = followers_total + " followers" textFollowers = w.addText(followerStr) textFollowers.textColor = new Color(followersColor); textFollowers.font = largeText w.addSpacer(4) // Show today's follower change const followerTodayStr = (followers_today > -1 ? '+' : '') + followers_today + " today" textChange = w.addText(followerTodayStr) textChange.textColor = new Color(dailyChangeColor); textChange.font = smallText; // Line chart as bg // This will create a square chart let chart = new LineChart(1200, 1200, chartData).configure((ctx, path) => { ctx.opaque = false; ctx.setFillColor(new Color(chartColor, chartOpacity)); ctx.addPath(path); ctx.fillPath(path); }).getImage(); w.backgroundImage = chart // Download an image from a given url async function loadImage(imgUrl) { const req = new Request(imgUrl) return await req.loadImage() } if (config.runsInWidget) { Script.setWidget(w) } else { await w.presentSmall() } if (config.runsWithSiri) { Speech.speak('You have '+followers_total+' followers') } Script.complete()