├── .circleci └── config.yml ├── .github ├── FUNDING.yml └── workflows │ └── music-box.yml ├── .gitignore ├── .mailmap ├── LICENSE ├── README.md ├── action.yml ├── branding └── musicbox-preview.png ├── index.js ├── package-lock.json ├── package.json └── sample.env /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | defaults: &defaults 4 | working_directory: ~/repo 5 | docker: 6 | - image: circleci/node:10 7 | 8 | commands: 9 | install: 10 | steps: 11 | - checkout 12 | - restore_cache: 13 | keys: 14 | - dependency-cache-{{ checksum "package.json" }} 15 | - dependency-cache- 16 | - run: npm install 17 | - save_cache: 18 | key: dependency-cache-{{ checksum "package.json" }} 19 | paths: 20 | - node_modules 21 | 22 | jobs: 23 | musicbox: 24 | <<: *defaults 25 | steps: 26 | - install 27 | - run: node index.js 28 | 29 | workflows: 30 | version: 2 31 | 32 | test: 33 | jobs: 34 | - musicbox 35 | 36 | poll: 37 | triggers: 38 | - schedule: 39 | # Run every hour. 40 | cron: "0 * * * *" 41 | filters: 42 | branches: 43 | only: 44 | - master 45 | jobs: 46 | - musicbox 47 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: http://cash.app/$jacklafo 4 | -------------------------------------------------------------------------------- /.github/workflows/music-box.yml: -------------------------------------------------------------------------------- 1 | name: music-box 2 | on: 3 | schedule: 4 | - cron: '0 * * * *' # hourly 5 | push: 6 | branches: master 7 | jobs: 8 | musicbox: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: npm install 13 | - name: Update 14 | uses: ./ 15 | env: 16 | LASTFM_KEY: ${{ secrets.LASTFM_KEY }} 17 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 18 | LFMUSERNAME: ${{ secrets.LFMUSERNAME }} 19 | GIST_ID: ${{ secrets.GIST_ID }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS Files 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Dependencies 6 | node_modules/ 7 | 8 | # Unconfigured Editors 9 | .vscode/ 10 | .idea 11 | 12 | # Dev 13 | test.js 14 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Jack LaFond 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

music-box

4 |

Update a gist to contain your weekly plays on Last.fm

5 |

6 | 7 | --- 8 | > 📌✨ For more pinned-gist projects like this one, check out: https://github.com/matchai/awesome-pinned-gists 9 | 10 | ## ✨ Inspiration 11 | This code was heavily inspired by [@JohnPhamous's strava-box](https://github.com/JohnPhamous/strava-box). 12 | 13 | ## 🎒 Prep Work 14 | 1. Create a new public GitHub Gist (https://gist.github.com/) 15 | 1. Create a token with the `gist` scope and copy it. (https://github.com/settings/tokens/new) 16 | 1. Create a Last.fm Application (https://www.last.fm/api/account/create) 17 | 1. Copy the `API token` 18 | 19 | ## 🖥 Project Setup 20 | 1. Fork this repo 21 | 2. Go to your fork's `Settings` > `Secrets` > `Add a new secret` for each environment secret (below) 22 | 23 | ## 🤫 Environment Secrets 24 | - **GIST_ID:** The ID portion from your gist url `https://gist.github.com//`**`6d5f84419863089a167387da62dd7081`**. 25 | - **GH_TOKEN:** The GitHub token generated above. 26 | - **LASTFM_KEY:** The API key you got from creating a Last.fm API account. 27 | - **LFMUSERNAME:** Your Last.fm username. 28 | 29 | ## 💸 Donations 30 | 31 | Feel free to use the GitHub Sponsor button to donate towards my work if you're feeling generous <3 32 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: music-box 2 | author: Jack LaFond & Casper da Costa-Luis 3 | description: GitHub Action for injecting last week's listening report from last.fm into a gist 4 | 5 | # using env: instead for now to match circleci 6 | # inputs: 7 | # LASTFM_KEY: 8 | # required: true 9 | # description: "last.fm key" 10 | # GH_TOKEN: 11 | # LFMUSERNAME: 12 | # GIST_ID: 13 | 14 | runs: 15 | using: node12 16 | main: ./index.js 17 | 18 | branding: 19 | icon: music 20 | color: red 21 | -------------------------------------------------------------------------------- /branding/musicbox-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacc/music-box/336bca4c5edccfe36fa0a257af9068ad20993c82/branding/musicbox-preview.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const Octokit = require("@octokit/rest"); 3 | const fetch = require("node-fetch"); 4 | const eaw = require("eastasianwidth"); 5 | 6 | const { 7 | GIST_ID: gistId, 8 | GH_TOKEN: githubToken, 9 | LASTFM_KEY: lfmAPI, 10 | LFMUSERNAME: user, 11 | } = process.env; 12 | 13 | const octokit = new Octokit({ 14 | auth: `token ${githubToken}`, 15 | }); 16 | 17 | const API_BASE = 18 | "http://ws.audioscrobbler.com/2.0/?method=user.gettopartists&format=json&period=7day&"; 19 | 20 | async function main() { 21 | const username = user; 22 | const gistID = gistId; 23 | const lfm = lfmAPI; 24 | 25 | if (!lfm || !username || !gistID || !githubToken) 26 | throw new Error( 27 | "Please check your environment variables, as you are missing one." 28 | ); 29 | const API = `${API_BASE}user=${username}&api_key=${lfm}`; 30 | 31 | const data = await fetch(API); 32 | const json = await data.json(); 33 | 34 | let gist; 35 | try { 36 | gist = await octokit.gists.get({ 37 | gist_id: gistID, 38 | }); 39 | } catch (error) { 40 | console.error(`music-box ran into an issue getting your Gist:\n${error}`); 41 | } 42 | 43 | const numArtitst = Math.min(10, json.topartists.artist.length); 44 | let playsTotal = 0; 45 | for (let i = 0; i < numArtitst; i++) { 46 | playsTotal += parseInt(json.topartists.artist[i].playcount, 10); 47 | } 48 | 49 | const lines = []; 50 | for (let i = 0; i < numArtitst; i++) { 51 | const plays = json.topartists.artist[i].playcount; 52 | let name = json.topartists.artist[i].name.substring(0, 25); 53 | // trim off long widechars 54 | for (let i = 24; i >= 0; i--) { 55 | if (eaw.length(name) <= 26) break; 56 | name = name.substring(0, i); 57 | } 58 | // pad short strings 59 | name = name.padEnd(26 + name.length - eaw.length(name)); 60 | 61 | lines.push( 62 | [ 63 | name, 64 | generateBarChart((plays * 100) / playsTotal, 17), 65 | `${plays}`.padStart(5), 66 | "plays", 67 | ].join(" ") 68 | ); 69 | } 70 | 71 | try { 72 | // Get original filename to update that same file 73 | const filename = Object.keys(gist.data.files)[0]; 74 | await octokit.gists.update({ 75 | gist_id: gistID, 76 | files: { 77 | [filename]: { 78 | filename: `🎵 My last week in music`, 79 | content: lines.join("\n"), 80 | }, 81 | }, 82 | }); 83 | } catch (error) { 84 | console.error(`Unable to update gist\n${error}`); 85 | } 86 | } 87 | 88 | function generateBarChart(percent, size) { 89 | const syms = "░▏▎▍▌▋▊▉█"; 90 | 91 | const frac = Math.floor((size * 8 * percent) / 100); 92 | const barsFull = Math.floor(frac / 8); 93 | if (barsFull >= size) { 94 | return syms.substring(8, 9).repeat(size); 95 | } 96 | const semi = frac % 8; 97 | 98 | return [syms.substring(8, 9).repeat(barsFull), syms.substring(semi, semi + 1)] 99 | .join("") 100 | .padEnd(size, syms.substring(0, 1)); 101 | } 102 | 103 | async function updateGist() { 104 | let gist; 105 | try { 106 | gist = await octokit.gists.get({ 107 | gist_id: gistID, 108 | }); 109 | } catch (error) { 110 | console.error(`music-box ran into an issue:\n${error}`); 111 | } 112 | } 113 | 114 | (async () => { 115 | await main(); 116 | })(); 117 | 118 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "music-box", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@octokit/endpoint": { 8 | "version": "3.2.3", 9 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-3.2.3.tgz", 10 | "integrity": "sha512-yUPCt4vMIOclox13CUxzuKiPJIFo46b/6GhUnUTw5QySczN1L0DtSxgmIZrZV4SAb9EyAqrceoyrWoYVnfF2AA==", 11 | "requires": { 12 | "deepmerge": "3.2.0", 13 | "is-plain-object": "^2.0.4", 14 | "universal-user-agent": "^2.0.1", 15 | "url-template": "^2.0.8" 16 | } 17 | }, 18 | "@octokit/request": { 19 | "version": "2.4.2", 20 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-2.4.2.tgz", 21 | "integrity": "sha512-lxVlYYvwGbKSHXfbPk5vxEA8w4zHOH1wobado4a9EfsyD3Cbhuhus1w0Ye9Ro0eMubGO8kNy5d+xNFisM3Tvaw==", 22 | "requires": { 23 | "@octokit/endpoint": "^3.2.0", 24 | "deprecation": "^1.0.1", 25 | "is-plain-object": "^2.0.4", 26 | "node-fetch": "^2.3.0", 27 | "once": "^1.4.0", 28 | "universal-user-agent": "^2.0.1" 29 | } 30 | }, 31 | "@octokit/request-error": { 32 | "version": "1.0.4", 33 | "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", 34 | "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", 35 | "requires": { 36 | "deprecation": "^2.0.0", 37 | "once": "^1.4.0" 38 | }, 39 | "dependencies": { 40 | "deprecation": { 41 | "version": "2.3.1", 42 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", 43 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" 44 | } 45 | } 46 | }, 47 | "@octokit/rest": { 48 | "version": "16.32.0", 49 | "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.32.0.tgz", 50 | "integrity": "sha512-H8X4OGHK7KFNlrx11xQLBQcFGjracSLSoV8JC/N8/BfLl9L5ZMpxt5iFrudZM6alsCZd4hQ5f/GW0hEjAW0ZvQ==", 51 | "requires": { 52 | "@octokit/request": "^5.0.0", 53 | "@octokit/request-error": "^1.0.2", 54 | "atob-lite": "^2.0.0", 55 | "before-after-hook": "^2.0.0", 56 | "btoa-lite": "^1.0.0", 57 | "deprecation": "^2.0.0", 58 | "lodash.get": "^4.4.2", 59 | "lodash.set": "^4.3.2", 60 | "lodash.uniq": "^4.5.0", 61 | "octokit-pagination-methods": "^1.1.0", 62 | "once": "^1.4.0", 63 | "universal-user-agent": "^4.0.0" 64 | }, 65 | "dependencies": { 66 | "@octokit/endpoint": { 67 | "version": "5.4.0", 68 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.4.0.tgz", 69 | "integrity": "sha512-DWTNgEKg5KXzvNjKTzcFTnkZiL7te6pQxxumvxPjyjDpcY5V3xzywnNu1WVqySY3Ct1flF/kAoyDdZos6acq3Q==", 70 | "requires": { 71 | "is-plain-object": "^3.0.0", 72 | "universal-user-agent": "^4.0.0" 73 | } 74 | }, 75 | "@octokit/request": { 76 | "version": "5.1.0", 77 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.1.0.tgz", 78 | "integrity": "sha512-I15T9PwjFs4tbWyhtFU2Kq7WDPidYMvRB7spmxoQRZfxSmiqullG+Nz+KbSmpkfnlvHwTr1e31R5WReFRKMXjg==", 79 | "requires": { 80 | "@octokit/endpoint": "^5.1.0", 81 | "@octokit/request-error": "^1.0.1", 82 | "deprecation": "^2.0.0", 83 | "is-plain-object": "^3.0.0", 84 | "node-fetch": "^2.3.0", 85 | "once": "^1.4.0", 86 | "universal-user-agent": "^4.0.0" 87 | } 88 | }, 89 | "deprecation": { 90 | "version": "2.3.1", 91 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", 92 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" 93 | }, 94 | "is-plain-object": { 95 | "version": "3.0.0", 96 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", 97 | "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", 98 | "requires": { 99 | "isobject": "^4.0.0" 100 | } 101 | }, 102 | "isobject": { 103 | "version": "4.0.0", 104 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", 105 | "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" 106 | }, 107 | "universal-user-agent": { 108 | "version": "4.0.0", 109 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", 110 | "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", 111 | "requires": { 112 | "os-name": "^3.1.0" 113 | } 114 | } 115 | } 116 | }, 117 | "atob-lite": { 118 | "version": "2.0.0", 119 | "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", 120 | "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" 121 | }, 122 | "before-after-hook": { 123 | "version": "2.1.0", 124 | "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", 125 | "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" 126 | }, 127 | "btoa-lite": { 128 | "version": "1.0.0", 129 | "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", 130 | "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" 131 | }, 132 | "cross-spawn": { 133 | "version": "6.0.5", 134 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 135 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 136 | "requires": { 137 | "nice-try": "^1.0.4", 138 | "path-key": "^2.0.1", 139 | "semver": "^5.5.0", 140 | "shebang-command": "^1.2.0", 141 | "which": "^1.2.9" 142 | } 143 | }, 144 | "deepmerge": { 145 | "version": "3.2.0", 146 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", 147 | "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==" 148 | }, 149 | "deprecation": { 150 | "version": "1.0.1", 151 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-1.0.1.tgz", 152 | "integrity": "sha512-ccVHpE72+tcIKaGMql33x5MAjKQIZrk+3x2GbJ7TeraUCZWHoT+KSZpoC+JQFsUBlSTXUrBaGiF0j6zVTepPLg==" 153 | }, 154 | "dotenv": { 155 | "version": "8.1.0", 156 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", 157 | "integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==" 158 | }, 159 | "eastasianwidth": { 160 | "version": "0.2.0", 161 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 162 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" 163 | }, 164 | "end-of-stream": { 165 | "version": "1.4.1", 166 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 167 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 168 | "requires": { 169 | "once": "^1.4.0" 170 | } 171 | }, 172 | "execa": { 173 | "version": "1.0.0", 174 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 175 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 176 | "requires": { 177 | "cross-spawn": "^6.0.0", 178 | "get-stream": "^4.0.0", 179 | "is-stream": "^1.1.0", 180 | "npm-run-path": "^2.0.0", 181 | "p-finally": "^1.0.0", 182 | "signal-exit": "^3.0.0", 183 | "strip-eof": "^1.0.0" 184 | } 185 | }, 186 | "get-stream": { 187 | "version": "4.1.0", 188 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 189 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 190 | "requires": { 191 | "pump": "^3.0.0" 192 | } 193 | }, 194 | "gist-box": { 195 | "version": "0.3.0", 196 | "resolved": "https://registry.npmjs.org/gist-box/-/gist-box-0.3.0.tgz", 197 | "integrity": "sha512-t78HUmukCIKzO14fC0cJmUzq0csYP1m0s2NFJRNz4EuUq5QFJk6SGRp1IwVJX0aQcf0HojVb1+qJrzkyTzmsQQ==", 198 | "requires": { 199 | "@octokit/request": "^2.4.2" 200 | } 201 | }, 202 | "is-plain-object": { 203 | "version": "2.0.4", 204 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 205 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 206 | "requires": { 207 | "isobject": "^3.0.1" 208 | } 209 | }, 210 | "is-stream": { 211 | "version": "1.1.0", 212 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 213 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 214 | }, 215 | "isexe": { 216 | "version": "2.0.0", 217 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 218 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 219 | }, 220 | "isobject": { 221 | "version": "3.0.1", 222 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 223 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 224 | }, 225 | "lodash.get": { 226 | "version": "4.4.2", 227 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 228 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" 229 | }, 230 | "lodash.set": { 231 | "version": "4.3.2", 232 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", 233 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" 234 | }, 235 | "lodash.uniq": { 236 | "version": "4.5.0", 237 | "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", 238 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 239 | }, 240 | "macos-release": { 241 | "version": "2.3.0", 242 | "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", 243 | "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" 244 | }, 245 | "nice-try": { 246 | "version": "1.0.5", 247 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 248 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 249 | }, 250 | "node-fetch": { 251 | "version": "2.6.1", 252 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 253 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 254 | }, 255 | "npm-run-path": { 256 | "version": "2.0.2", 257 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 258 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 259 | "requires": { 260 | "path-key": "^2.0.0" 261 | } 262 | }, 263 | "octokit": { 264 | "version": "1.0.0-hello-world", 265 | "resolved": "https://registry.npmjs.org/octokit/-/octokit-1.0.0-hello-world.tgz", 266 | "integrity": "sha1-mX8irutd/iiB54xpQYxJKBqt+Y8=" 267 | }, 268 | "octokit-pagination-methods": { 269 | "version": "1.1.0", 270 | "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", 271 | "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" 272 | }, 273 | "once": { 274 | "version": "1.4.0", 275 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 276 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 277 | "requires": { 278 | "wrappy": "1" 279 | } 280 | }, 281 | "os-name": { 282 | "version": "3.1.0", 283 | "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", 284 | "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", 285 | "requires": { 286 | "macos-release": "^2.2.0", 287 | "windows-release": "^3.1.0" 288 | } 289 | }, 290 | "p-finally": { 291 | "version": "1.0.0", 292 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 293 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 294 | }, 295 | "path-key": { 296 | "version": "2.0.1", 297 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 298 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 299 | }, 300 | "pump": { 301 | "version": "3.0.0", 302 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 303 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 304 | "requires": { 305 | "end-of-stream": "^1.1.0", 306 | "once": "^1.3.1" 307 | } 308 | }, 309 | "semver": { 310 | "version": "5.7.0", 311 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 312 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 313 | }, 314 | "shebang-command": { 315 | "version": "1.2.0", 316 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 317 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 318 | "requires": { 319 | "shebang-regex": "^1.0.0" 320 | } 321 | }, 322 | "shebang-regex": { 323 | "version": "1.0.0", 324 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 325 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 326 | }, 327 | "signal-exit": { 328 | "version": "3.0.2", 329 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 330 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 331 | }, 332 | "strip-eof": { 333 | "version": "1.0.0", 334 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 335 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 336 | }, 337 | "universal-user-agent": { 338 | "version": "2.1.0", 339 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", 340 | "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", 341 | "requires": { 342 | "os-name": "^3.0.0" 343 | } 344 | }, 345 | "url-template": { 346 | "version": "2.0.8", 347 | "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", 348 | "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" 349 | }, 350 | "which": { 351 | "version": "1.3.1", 352 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 353 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 354 | "requires": { 355 | "isexe": "^2.0.0" 356 | } 357 | }, 358 | "windows-release": { 359 | "version": "3.2.0", 360 | "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", 361 | "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", 362 | "requires": { 363 | "execa": "^1.0.0" 364 | } 365 | }, 366 | "wrappy": { 367 | "version": "1.0.2", 368 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 369 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "music-box", 3 | "version": "1.1.0", 4 | "description": "See your weekly top artists in a GitHub gist.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/jacc/music-box.git" 12 | }, 13 | "author": "Jack LaFond", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/jacc/music-box/issues" 17 | }, 18 | "homepage": "https://github.com/jacc/music-box#readme", 19 | "dependencies": { 20 | "@octokit/rest": "^16.32.0", 21 | "dotenv": "^8.1.0", 22 | "eastasianwidth": "^0.2.0", 23 | "gist-box": "^0.3.0", 24 | "octokit": "^1.0.0-hello-world" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | GIST_ID= 2 | GITHUB_TOKEN= 3 | LASTFM_KEY= 4 | LFMUSERNAME= --------------------------------------------------------------------------------