├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── security-vulnerability-report.md ├── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md └── workflows │ └── cla.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── images └── wakastats.jpeg ├── signatures └── version1 │ └── cla.json └── src ├── Cache.js └── WakaStats.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: kylereddoch # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: kylereddoch # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: 'https://buymeacoffee.com/kylereddoch' # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] - " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ### To Reproduce 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | ### Expected behavior 21 | A clear and concise description of what you expected to happen. 22 | 23 | ### Screenshots 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ### Desktop (please complete the following information): 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | ### Smartphone (please complete the following information): 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | ### Additional context 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE REQUEST] - " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your feature request related to a problem? Please describe. 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | ### Describe the solution you'd like 14 | A clear and concise description of what you want to happen. 15 | 16 | ### Describe alternatives you've considered 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | ### Additional context 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security-vulnerability-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security Vulnerability Report 3 | about: Report a security vulnerability 4 | title: "[SECURITY VULNERABILITY] - " 5 | labels: security vulnerability 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Summary 11 | 12 | *Short summary of the problem. Make the impact and severity as clear as possible. For example: An unsafe deserialization vulnerability allows any unauthenticated user to execute arbitrary code on the server.* 13 | 14 | ### Product 15 | 16 | [product] 17 | 18 | 21 | 22 | ### Details 23 | 24 | *Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.* 25 | 26 | ### PoC 27 | 28 | *Complete instructions, including specific configuration details, to reproduce the vulnerability* 29 | 30 | ### Impact 31 | 32 | [impact] 33 | 34 | ### Remediation 35 | 36 | *Propose a remediation suggestion if you have one. (This is just a suggestion, as the maintainer might have a better idea to fix the issue.)* 37 | 38 | ### Credit 39 | 40 | *List all researchers who contributed to this disclosure.* 41 | *If you found the vulnerability with a specific tool, you can also credit this tool.* 42 | 43 | ### Contact 44 | 45 | [contact] 46 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | - [ ] Documentation Update (if none of the other choices apply) 14 | 15 | ## Checklist 16 | 17 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 18 | 19 | - [ ] I have read the [CONTRIBUTING](https://github.com/appium/appium/blob/master/CONTRIBUTING.md) doc 20 | - [ ] Lint and unit tests pass locally with my changes 21 | - [ ] I have added tests that prove my fix is effective or that my feature works 22 | - [ ] I have added necessary documentation (if appropriate) 23 | - [ ] Any dependent changes have been merged and published in downstream modules 24 | 25 | ## Further comments 26 | 27 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 28 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA Assistant" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | jobs: 9 | CLAssistant: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "CLA Assistant" 13 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 14 | # Beta Release 15 | uses: cla-assistant/github-action@v2.1.3-beta 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | # the below token should have repo scope and must be manually added by you in the repository's secret 19 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} 20 | with: 21 | path-to-signatures: 'signatures/version1/cla.json' 22 | path-to-document: 'https://github.com/kylereddoch/scriptable/blob/main/CODE_OF_CONDUCT.md' # e.g. a CLA or a DCO document 23 | # branch should not be protected 24 | branch: 'main' 25 | #allowlist: user1,bot* 26 | 27 | #below are the optional inputs - If the optional inputs are not given, then default values will be taken 28 | #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) 29 | #remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) 30 | create-file-commit-message: 'Creating file for storing CLA Signatures' 31 | signed-commit-message: '$contributorName has signed the CLA in #$pullRequestNo' 32 | custom-notsigned-prcomment: 'Thank you for your contribution. Please kindly read and sign our CLA at https://github.com/kylereddoch/scriptable/blob/main/CODE_OF_CONDUCT.md.' 33 | custom-pr-sign-comment: 'I have read the CLA Document and accept the terms. I hereby sign the CLA.' 34 | custom-allsigned-prcomment: 'All Contributors have signed the CLA.' 35 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 36 | #use-dco-flag: true - If you are using DCO instead of CLA 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) 2 | 3 | In the interest of encouraging an approachable and motivating open-source community more widely, we want our project to be as welcoming as possible to those with different experience levels, ideas, viewpoints and backgrounds. We feel these values make for better communities and better products, in both the short and long term. 4 | 5 | As such, we expect all present and future contributors to the enclosed scriptable scripts to help adopt and enforce the values described in the [Contributor Covenant][homepage] code of conduct. 6 | 7 | --- 8 | 9 | # Contributor Covenant Code of Conduct 10 | 11 | ## Our Pledge 12 | 13 | In the interest of fostering an open and welcoming environment, we as 14 | contributors and maintainers pledge to making participation in our project and 15 | our community a harassment-free experience for everyone, regardless of age, body 16 | size, disability, ethnicity, gender identity and expression, level of experience, 17 | education, socio-economic status, nationality, personal appearance, race, 18 | religion, or sexual identity and orientation. 19 | 20 | ## Our Standards 21 | 22 | Examples of behavior that contributes to creating a positive environment 23 | include: 24 | 25 | * Using welcoming and inclusive language 26 | * Being respectful of differing viewpoints and experiences 27 | * Gracefully accepting constructive criticism 28 | * Focusing on what is best for the community 29 | * Showing empathy towards other community members 30 | 31 | Examples of unacceptable behavior by participants include: 32 | 33 | * The use of sexualized language or imagery and unwelcome sexual attention or 34 | advances 35 | * Trolling, insulting/derogatory comments, and personal or political attacks 36 | * Public or private harassment 37 | * Publishing others' private information, such as a physical or electronic 38 | address, without explicit permission 39 | * Other conduct which could reasonably be considered inappropriate in a 40 | professional setting 41 | 42 | ## Our Responsibilities 43 | 44 | Project maintainers are responsible for clarifying the standards of acceptable 45 | behavior and are expected to take appropriate and fair corrective action in 46 | response to any instances of unacceptable behavior. 47 | 48 | Project maintainers have the right and responsibility to remove, edit, or 49 | reject comments, commits, code, wiki edits, issues, and other contributions 50 | that are not aligned to this Code of Conduct, or to ban temporarily or 51 | permanently any contributor for other behaviors that they deem inappropriate, 52 | threatening, offensive, or harmful. 53 | 54 | ## Scope 55 | 56 | This Code of Conduct applies both within project spaces and in public spaces 57 | when an individual is representing the project or its community. Examples of 58 | representing a project or community include using an official project e-mail 59 | address, posting via an official social media account, or acting as an appointed 60 | representative at an online or offline event. Representation of a project may be 61 | further defined and clarified by project maintainers. 62 | 63 | ## Enforcement 64 | 65 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 66 | reported by contacting the project team at `kylereddoch@me.com`. All 67 | complaints will be reviewed and investigated and will result in a response that 68 | is deemed necessary and appropriate to the circumstances. The project team is 69 | obligated to maintain confidentiality with regard to the reporter of an incident. 70 | Further details of specific enforcement policies may be posted separately. 71 | 72 | Project maintainers who do not follow or enforce the Code of Conduct in good 73 | faith may face temporary or permanent repercussions as determined by other 74 | members of the project's leadership. 75 | 76 | ## Attribution 77 | 78 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 79 | version 2.1, available at 80 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 81 | 82 | [homepage]: https://www.contributor-covenant.org 83 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 84 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to projects 2 | 3 | 👍🏼 🎉 First off, a big welcome and thank you for considering contributing to my open source projects! 🎉 👍🏼 4 | 5 | It’s people like you that make these projects amazing for users. 6 | 7 | Reading and following these guidelines will help us make the contribution process easy and effective for everyone involved. It also communicates that you agree to respect the time of the developers managing and developing these open source projects. In return, we will reciprocate that respect by addressing your issue, assessing changes, and helping you finalize your pull requests. 8 | 9 | ## Quicklinks 10 | 11 | - [Contributing to projects](#contributing-to-projects) 12 | - [Quicklinks](#quicklinks) 13 | - [Code of Conduct](#code-of-conduct) 14 | - [Getting Started](#getting-started) 15 | - [Issues](#issues) 16 | - [Pull Requests](#pull-requests) 17 | - [Getting Help](#getting-help) 18 | 19 | ## Code of Conduct 20 | 21 | We take our open source community seriously and hold ourselves and other contributors to high standards of communication. By participating and contributing to this project, you agree to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). 22 | 23 | ## Getting Started 24 | 25 | Contributions are made to this repo via Issues and Pull Requests (PRs). A few general guidelines that cover both: 26 | 27 | - To report security vulnerabilities, please create an [issue](#issues) with the tag "security vulnerability". 28 | - Search for existing Issues and PRs before creating your own. 29 | - We work hard to makes sure issues are handled in a timely manner but, depending on the impact, it could take a while to investigate the root cause. A friendly ping in the comment thread to the submitter or a contributor can help draw attention if your issue is blocking. 30 | - If you've never contributed before, see [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) for resources and tips on how to get started. 31 | 32 | ### Issues 33 | 34 | Issues should be used to report problems, request a new feature, or to discuss potential changes before a PR is created. When you create a new Issue, a template will be loaded that will guide you through collecting and providing the information we need to investigate. 35 | 36 | If you find an Issue that addresses the problem you're having, please add your own reproduction information to the existing issue rather than creating a new one. Adding a [reaction](https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) can also help be indicating to our maintainers that a particular problem is affecting more than just the reporter. 37 | 38 | ### Pull Requests 39 | 40 | PRs to our libraries are always welcome and can be a quick way to get your fix or improvement slated for the next release. In general, PRs should: 41 | 42 | - Only fix/add the functionality in question **OR** address wide-spread whitespace/style issues, not both. 43 | - Add unit or integration tests for fixed or changed functionality (if a test suite already exists). 44 | - Address a single concern in the least number of changed lines as possible. 45 | - Include documentation in the repo. 46 | - Be accompanied by a complete Pull Request template (loaded automatically when a PR is created). 47 | 48 | For changes that address core functionality or would require breaking changes (e.g. a major release), it's best to open an Issue to discuss your proposal first. This is not required but can save time creating and reviewing changes. 49 | 50 | In general, we follow the ["fork-and-pull" Git workflow](https://github.com/susam/gitpr) 51 | 52 | 1. Fork the repository to your own Github account 53 | 2. Clone the project to your machine 54 | 3. Create a branch locally with a succinct but descriptive name 55 | 4. Commit changes to the branch 56 | 5. Following any formatting and testing guidelines specific to this repo 57 | 6. Push changes to your fork 58 | 7. Open a PR in our repository and follow the PR template so that we can efficiently review the changes. 59 | 60 | ## Getting Help 61 | 62 | If you need help, please reach out to project team at [kylereddoch@me.com][help]. 63 | 64 | [help]: mailto:kylereddoch@me.com 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kyle Reddoch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scriptable 2 | 3 | A collection of my scriptable.app scripts. 4 | 5 | These are open-spource and you are free to use or edit them for your personal use. It would be nice to get credit for them though. 6 | 7 | If you enjoy these scripts and find them useful, please consider sponsoring so I can continue the work that I do. 8 | 9 | ## Scripts 10 | 11 | ### WakaStats 12 | 13 | ![](/images/wakastats.jpeg) 14 | 15 | This script shows you your combined stats from WakaTime for the Last 7 days. 16 | 17 | - Categories 18 | - Editors 19 | - Languages 20 | - Daily Average Coding Time 21 | - Total Coding Time 22 | - Operating Systems 23 | 24 | This script depends on the Cache.js script (above) from [EvanDColeman](https://github.com/evandcoleman) also. Make sure you put it in Scriptable as well. 25 | 26 | #### How to install 27 | 28 | 1. Grab both the WakaStats.js and Cache.js scripts from the /src folder. 29 | 2. Place them in the Scriptable folder in your iCloud Drive or copy and paste the code into a new scriptable in the scriptable.app. 30 | 3. Fill out the values in the TODO section of the WakaStats.js script. 31 | 32 | ```javascript 33 | // TODO: PLEASE SET THESE VALUES 34 | const YOURNAME = 'TODO'; 35 | const WAKAUSER = 'TODO'; 36 | const API_KEY = 'TODO'; // Your wakatime API KEY from https://wakatime.com/api-key 37 | ``` 38 | 39 | You may also set up [automations in Shortcuts](https://support.apple.com/guide/shortcuts/create-a-new-personal-automation-apdfbdbd7123/ios) to run this script, to check for updates at times other than the ones dictated by Scriptable's normal widget refresh interval. 40 | 41 | #### Shoutouts 42 | 43 | I want to send a HUGE thanks to the follow people for helping me with this script. I'd probably still be stuck on some errors if it wasnt for them ☺️. 44 | 45 | - [Simon Stovring](https://twitter.com/simonbs) 46 | - [Josh Holtz](https://twitter.com/joshdholtz?s=21) -------------------------------------------------------------------------------- /images/wakastats.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kylereddoch/scriptable/47f9f2e15005595a3388d09bca3a84ebe0c86ab2/images/wakastats.jpeg -------------------------------------------------------------------------------- /signatures/version1/cla.json: -------------------------------------------------------------------------------- 1 | { 2 | "signedContributors": [ 3 | { 4 | "name": "jinzer0", 5 | "id": 70031788, 6 | "comment_id": 1270234727, 7 | "created_at": "2022-10-06T15:09:30Z", 8 | "repoId": 433288566, 9 | "pullRequestNo": 8 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/Cache.js: -------------------------------------------------------------------------------- 1 | // NOTE: This script was written by evandcoleman: https://github.com/evandcoleman/scriptable 2 | 3 | class Cache { 4 | constructor(name) { 5 | this.fm = FileManager.iCloud(); 6 | this.cachePath = this.fm.joinPath(this.fm.documentsDirectory(), name); 7 | 8 | if (!this.fm.fileExists(this.cachePath)) { 9 | this.fm.createDirectory(this.cachePath); 10 | } 11 | } 12 | async read(key, expirationMinutes) { 13 | try { 14 | const path = this.fm.joinPath(this.cachePath, key); 15 | await this.fm.downloadFileFromiCloud(path); 16 | const createdAt = this.fm.creationDate(path); 17 | 18 | if (expirationMinutes) { 19 | if ((new Date()) - createdAt > (expirationMinutes * 60000)) { 20 | this.fm.remove(path); 21 | return null; 22 | } 23 | } 24 | 25 | const value = this.fm.readString(path); 26 | 27 | try { 28 | return JSON.parse(value); 29 | } catch (error) { 30 | return value; 31 | } 32 | } catch (error) { 33 | return null; 34 | } 35 | } 36 | write(key, value) { 37 | const path = this.fm.joinPath(this.cachePath, key.replace('/', '-')); 38 | console.log(`Caching to ${path}...`); 39 | 40 | if (typeof value === 'string' || value instanceof String) { 41 | this.fm.writeString(path, value); 42 | } else { 43 | this.fm.writeString(path, JSON.stringify(value)); 44 | } 45 | } 46 | } 47 | 48 | module.exports = Cache; 49 | -------------------------------------------------------------------------------- /src/WakaStats.js: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Constants and Configurations 3 | *****************************************************************************/ 4 | 5 | // NOTE: This script uses the Cache script (https://github.com/kylereddoch/scriptable/src/Cache.js) 6 | // Make sure to add the Cache script in Scriptable as well! 7 | 8 | // Cache keys and default location 9 | const CACHE_KEY_LAST_UPDATED = 'last_updated'; 10 | 11 | // Date range for Waka data 12 | const DATERANGE = 'last_7_days'; 13 | 14 | // Get current date and time 15 | const updatedAt = new Date().toLocaleString(); 16 | 17 | // Font name and size 18 | const FONT_NAME = 'Menlo'; 19 | const FONT_SIZE = 9; 20 | 21 | // Colors 22 | const COLORS = { 23 | bg0: '#29323c', 24 | bg1: '#1c1c1c', 25 | }; 26 | 27 | // TODO: PLEASE SET THESE VALUES 28 | const YOURNAME = 'TODO'; 29 | const WAKAUSER = 'TODO'; // Your wakatime username 30 | const API_KEY = 'TODO'; // Your wakatime API KEY from https://wakatime.com/api-key 31 | 32 | /****************************************************************************** 33 | * Initial Setups 34 | *****************************************************************************/ 35 | 36 | // Import and setup Cache 37 | const Cache = importModule('Cache'); 38 | const cache = new Cache('WakaStats'); 39 | 40 | // Fetch data and create widget 41 | const data = await fetchData(); 42 | const widget = createWidget(data); 43 | 44 | const bgColor = new LinearGradient(); 45 | bgColor.colors = [new Color("#29323c"), new Color("#1c1c1c")]; 46 | bgColor.locations = [0.0, 1.0]; 47 | widget.backgroundGradient = bgColor; 48 | 49 | Script.setWidget(widget); 50 | Script.complete(); 51 | 52 | /****************************************************************************** 53 | * Main Functions (Widget and Data-Fetching) 54 | *****************************************************************************/ 55 | 56 | /** 57 | * Main widget function. 58 | * 59 | * @param {} data The data for the widget to display 60 | */ 61 | 62 | function createWidget(data) { 63 | console.log(`Creating widget with data: ${JSON.stringify(data)}`); 64 | 65 | const widget = new ListWidget(); 66 | const bgColor = new LinearGradient(); 67 | bgColor.colors = [new Color(COLORS.bg0), new Color(COLORS.bg1)]; 68 | bgColor.locations = [0.0, 1.0]; 69 | widget.backgroundGradient = bgColor; 70 | widget.setPadding(10, 10, 10, 10); 71 | 72 | const stack = widget.addStack(); 73 | stack.layoutVertically(); 74 | stack.spacing = 4.85; 75 | stack.size = new Size(320, 0); 76 | 77 | // Line 0 - Title 78 | const titleLine = stack.addText('WakaStats for' + " " + YOURNAME + " | Last 7 Days"); 79 | titleLine.textColor = Color.white(); 80 | titleLine.textOpacity = 0.7; 81 | titleLine.font = new Font(FONT_NAME, FONT_SIZE); 82 | 83 | // Line 1 - Categories 84 | const categoriesLine = stack.addText('🗂 Categories:' + " " + data.waka.categories); 85 | categoriesLine.textColor = Color.white(); 86 | categoriesLine.font = new Font(FONT_NAME, FONT_SIZE); 87 | 88 | // Line 2 - Editors 89 | const editorsLine = stack.addText('🛠 Editors:' + " " + data.waka.editors); 90 | editorsLine.textColor = Color.white(); 91 | editorsLine.font = new Font(FONT_NAME, FONT_SIZE); 92 | 93 | // Line 3 - Languages 94 | const languagesLine = stack.addText('🌎 Languages:' + " " + data.waka.languages); 95 | languagesLine.textColor = Color.white(); 96 | languagesLine.font = new Font(FONT_NAME, FONT_SIZE); 97 | 98 | // Line 4 - Daily Average Coding Time 99 | const dayAvLine = stack.addText('⏱ Daily Average:' + " " + data.waka.dayAv); 100 | dayAvLine.textColor = Color.white(); 101 | dayAvLine.font = new Font(FONT_NAME, FONT_SIZE); 102 | 103 | // Line 5 - Total Coding Time 104 | const totalLine = stack.addText('⌛️ Total Time:' + " " + data.waka.total); 105 | totalLine.textColor = Color.white(); 106 | totalLine.font = new Font(FONT_NAME, FONT_SIZE); 107 | 108 | // Line 6 - Operating Systems 109 | const opSysLine = stack.addText('🖥 Operating Systems Used:' + " " + data.waka.opSys); 110 | opSysLine.textColor = Color.white(); 111 | opSysLine.font = new Font(FONT_NAME, FONT_SIZE); 112 | 113 | // Line 6 - Updated time 114 | const updatedTime = stack.addText('Last updated:' + " " + updatedAt); 115 | updatedTime.textColor = Color.white(); 116 | updatedTime.textOpacity = 0.7; 117 | updatedTime.font = new Font(FONT_NAME, 7); 118 | 119 | return widget; 120 | } 121 | 122 | /* 123 | * Fetch pieces of data for the widget. 124 | */ 125 | 126 | async function fetchData() { 127 | // Get the waka data 128 | const waka = await fetchWaka(); 129 | 130 | // Get last data update time (and set) 131 | const lastUpdated = await getLastUpdated(); 132 | cache.write(CACHE_KEY_LAST_UPDATED, new Date().getTime()); 133 | 134 | return { 135 | waka, 136 | lastUpdated, 137 | }; 138 | } 139 | 140 | /****************************************************************************** 141 | * Helper Functions 142 | *****************************************************************************/ 143 | 144 | //------------------------------------- 145 | // Wakatime Helper Functions 146 | //------------------------------------- 147 | 148 | /* 149 | * Fetch the stats from Wakatime 150 | */ 151 | 152 | async function fetchWaka() { 153 | 154 | const url = "https://wakatime.com/api/v1/users/" + WAKAUSER + "/stats/" + DATERANGE; 155 | const key = Data.fromString(API_KEY).toBase64String(); 156 | const headers = {Authorization: `Basic ${key}`}; 157 | 158 | const data = await fetchJson(url, headers); 159 | 160 | if (!data) { 161 | return 'No data found'; 162 | } 163 | 164 | return { 165 | categories: data.data.categories.map(e => e.name).join(", "), 166 | editors: data.data.editors.map(e => e.name).join(", "), 167 | languages: data.data.languages.map(e => e.name).join(", "), 168 | dayAv: data.data.human_readable_daily_average, 169 | total: data.data.human_readable_total, 170 | opSys: data.data.operating_systems.map(e => e.name).join(", "), 171 | } 172 | } 173 | 174 | //------------------------------------- 175 | // Misc. Helper Functions 176 | //------------------------------------- 177 | 178 | /** 179 | * Make a REST request and return the response 180 | * 181 | * @param {*} url URL to make the request to 182 | * @param {*} headers Headers for the request 183 | */ 184 | 185 | async function fetchJson(url, headers) { 186 | try { 187 | console.log(`Fetching url: ${url}`); 188 | const req = new Request(url, headers); 189 | req.headers = headers; 190 | const resp = await req.loadJSON(); 191 | return resp; 192 | } catch (error) { 193 | console.error('Error fetching from url: ${url}, error: ${JSON.stringify(error)}'); 194 | } 195 | } 196 | 197 | /* 198 | * Get the last updated timestamp from the Cache. 199 | */ 200 | 201 | async function getLastUpdated() { 202 | let cachedLastUpdated = await cache.read(CACHE_KEY_LAST_UPDATED); 203 | 204 | if (!cachedLastUpdated) { 205 | cachedLastUpdated = new Date().getTime(); 206 | cache.write(CACHE_KEY_LAST_UPDATED, cachedLastUpdated); 207 | } 208 | 209 | return cachedLastUpdated; 210 | } --------------------------------------------------------------------------------