From fc217056e1ca7cdc70ee1d30febf022481394fdc Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Wed, 9 Mar 2022 01:34:35 +0000 Subject: [PATCH] Initial commit --- .gitignore | 3 + .vscode/settings.json | 5 + bin/discord-switch-presence.js | 3 + package-lock.json | 887 +++++++++++++++++++++++++++++++++ package.json | 41 ++ src/api.ts | 535 ++++++++++++++++++++ src/cli.ts | 358 +++++++++++++ src/titles.ts | 115 +++++ tsconfig.json | 11 + 9 files changed, 1958 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100755 bin/discord-switch-presence.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/api.ts create mode 100644 src/cli.ts create mode 100644 src/titles.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20172f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +data diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ad9703b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "**/*/data/*": "json" + } +} diff --git a/bin/discord-switch-presence.js b/bin/discord-switch-presence.js new file mode 100755 index 0000000..3d23368 --- /dev/null +++ b/bin/discord-switch-presence.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/cli.js').then(cli => cli.default.argv); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..be704f6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,887 @@ +{ + "name": "discord-switch-presence", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "discord-switch-presence", + "version": "1.0.0", + "dependencies": { + "cli-table": "^0.3.11", + "debug": "^4.3.3", + "discord-rpc": "^4.0.1", + "node-fetch": "^3.2.2", + "node-persist": "^3.1.0", + "read": "^1.0.7", + "uuid": "^8.3.2", + "yargs": "^17.3.1" + }, + "devDependencies": { + "@types/cli-table": "^0.3.0", + "@types/debug": "^4.1.7", + "@types/discord-rpc": "^4.0.0", + "@types/node": "^17.0.21", + "@types/node-persist": "^3.1.2", + "@types/read": "^0.0.29", + "@types/uuid": "^8.3.4", + "@types/yargs": "^17.0.9", + "typescript": "^4.7.0-dev.20220308" + } + }, + "node_modules/@types/cli-table": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@types/cli-table/-/cli-table-0.3.0.tgz", + "integrity": "sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ==", + "dev": true + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/discord-rpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.0.tgz", + "integrity": "sha512-a5HiKOcBkB43g/lN6fBYw8FyGc6Ue9CYucxxHxXlELXpb1CxCa2NA2pGK2Ub88pi4uY5+HQeSFbYtH6DJtV3Qw==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", + "dev": true + }, + "node_modules/@types/node-persist": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/node-persist/-/node-persist-3.1.2.tgz", + "integrity": "sha512-aLFUB1951wOfR+tZ4f3TudLPblo9+PfnduMh6feuOTijD0Q6YkMdqPXSgQIjI23FGmCuoZaIZ9x6cvEG/TdiSg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/read": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/read/-/read-0.0.29.tgz", + "integrity": "sha512-TisW3O3OhpP8/ZwaiqV7kewh9gnoH7PfqHd4hkCM9ogiqWEagu43WXpHWqgPbltXhembYJDpYB3cVwUIOweHXg==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.9.tgz", + "integrity": "sha512-Ci8+4/DOtkHRylcisKmVMtmVO5g7weUVCKcsu1sJvF1bn0wExTmbHmhFKj7AnEm0de800iovGhdSKzYnzbaHpg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "dependencies": { + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/discord-rpc": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz", + "integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==", + "dependencies": { + "node-fetch": "^2.6.1", + "ws": "^7.3.1" + }, + "optionalDependencies": { + "register-scheme": "github:devsnek/node-register-scheme" + } + }, + "node_modules/discord-rpc/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fetch-blob": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", + "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.2.tgz", + "integrity": "sha512-Cwhq1JFIoon15wcIkFzubVNFE5GvXGV82pKf4knXXjvGmn7RJKcypeuqcVNZMGDZsAFWyIRya/anwAJr7TWJ7w==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-persist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-3.1.0.tgz", + "integrity": "sha512-/j+fd/u71wNgKf3V2bx4tnDm+3GvLnlCuvf2MXbJ3wern+67IAb6zN9Leu1tCWPlPNZ+v1hLSibVukkPK2HqJw==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/register-scheme": { + "version": "0.0.2", + "resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bindings": "^1.3.0", + "node-addon-api": "^1.3.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/typescript": { + "version": "4.7.0-dev.20220308", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.0-dev.20220308.tgz", + "integrity": "sha512-dWx2ETRQadrYmrtU4XDnjQsxPNzX1u3+/TWJZ8kU0gOwdyZQ8NrO5BSzhIwp5eqAGe8xdBg+xPDT9tI4mxLfjw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", + "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "engines": { + "node": ">=12" + } + } + }, + "dependencies": { + "@types/cli-table": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@types/cli-table/-/cli-table-0.3.0.tgz", + "integrity": "sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ==", + "dev": true + }, + "@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, + "@types/discord-rpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.0.tgz", + "integrity": "sha512-a5HiKOcBkB43g/lN6fBYw8FyGc6Ue9CYucxxHxXlELXpb1CxCa2NA2pGK2Ub88pi4uY5+HQeSFbYtH6DJtV3Qw==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "@types/node": { + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", + "dev": true + }, + "@types/node-persist": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/node-persist/-/node-persist-3.1.2.tgz", + "integrity": "sha512-aLFUB1951wOfR+tZ4f3TudLPblo9+PfnduMh6feuOTijD0Q6YkMdqPXSgQIjI23FGmCuoZaIZ9x6cvEG/TdiSg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/read": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/read/-/read-0.0.29.tgz", + "integrity": "sha512-TisW3O3OhpP8/ZwaiqV7kewh9gnoH7PfqHd4hkCM9ogiqWEagu43WXpHWqgPbltXhembYJDpYB3cVwUIOweHXg==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.9.tgz", + "integrity": "sha512-Ci8+4/DOtkHRylcisKmVMtmVO5g7weUVCKcsu1sJvF1bn0wExTmbHmhFKj7AnEm0de800iovGhdSKzYnzbaHpg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "requires": { + "colors": "1.0.3" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "discord-rpc": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/discord-rpc/-/discord-rpc-4.0.1.tgz", + "integrity": "sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==", + "requires": { + "node-fetch": "^2.6.1", + "register-scheme": "github:devsnek/node-register-scheme", + "ws": "^7.3.1" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "fetch-blob": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", + "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.2.tgz", + "integrity": "sha512-Cwhq1JFIoon15wcIkFzubVNFE5GvXGV82pKf4knXXjvGmn7RJKcypeuqcVNZMGDZsAFWyIRya/anwAJr7TWJ7w==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "node-persist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-3.1.0.tgz", + "integrity": "sha512-/j+fd/u71wNgKf3V2bx4tnDm+3GvLnlCuvf2MXbJ3wern+67IAb6zN9Leu1tCWPlPNZ+v1hLSibVukkPK2HqJw==" + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, + "register-scheme": { + "version": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17", + "from": "register-scheme@github:devsnek/node-register-scheme", + "optional": true, + "requires": { + "bindings": "^1.3.0", + "node-addon-api": "^1.3.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "typescript": { + "version": "4.7.0-dev.20220308", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.0-dev.20220308.tgz", + "integrity": "sha512-dWx2ETRQadrYmrtU4XDnjQsxPNzX1u3+/TWJZ8kU0gOwdyZQ8NrO5BSzhIwp5eqAGe8xdBg+xPDT9tI4mxLfjw==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "web-streams-polyfill": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", + "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f73f765 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "discord-switch-presence", + "version": "1.0.0", + "description": "", + "author": "Samuel Elliott", + "main": "dist/index.js", + "type": "module", + "repository": { + "type": "git", + "url": "git@gitlab.fancy.org.uk:samuel/discord-switch-presence.git" + }, + "bin": { + "discord-switch-presence": "bin/discord-switch-presence.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "cli": "node bin/discord-switch-presence.js", + "start": "node bin/discord-switch-presence.js presence" + }, + "dependencies": { + "cli-table": "^0.3.11", + "debug": "^4.3.3", + "discord-rpc": "^4.0.1", + "node-fetch": "^3.2.2", + "node-persist": "^3.1.0", + "read": "^1.0.7", + "uuid": "^8.3.2", + "yargs": "^17.3.1" + }, + "devDependencies": { + "@types/cli-table": "^0.3.0", + "@types/debug": "^4.1.7", + "@types/discord-rpc": "^4.0.0", + "@types/node": "^17.0.21", + "@types/node-persist": "^3.1.2", + "@types/read": "^0.0.29", + "@types/uuid": "^8.3.4", + "@types/yargs": "^17.0.9", + "typescript": "^4.7.0-dev.20220308" + } +} diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..0362c2d --- /dev/null +++ b/src/api.ts @@ -0,0 +1,535 @@ +import fetch from 'node-fetch'; +import { v4 as uuidgen } from 'uuid'; +import { stringify as buildQueryString } from 'querystring'; +import createDebug from 'debug'; + +const debug = createDebug('api'); + +export default class ZncApi { + constructor( + private token: string + ) {} + + protected async fetch(url: string, method = 'GET', body?: string, headers?: object) { + const response = await fetch('https://api-lp1.znc.srv.nintendo.net' + url, { + method: method, + body: body, + headers: Object.assign({ + 'X-Platform': 'Android', + 'X-ProductVersion': '2.0.0', + 'Authorization': 'Bearer ' + this.token, + 'Content-Type': 'application/json; charset=utf-8', + 'User-Agent': 'com.nintendo.znca/2.0.0(Android/8.0.0)', + }, headers), + }); + + const data = await response.json() as ZncResponse; + + if ('errorMessage' in data) { + const error = new Error('[znc] ' + data.errorMessage); + // @ts-expect-error + error.response = response; + // @ts-expect-error + error.data = data; + throw error; + } + if (data.status !== 0) { + const error = new Error('[znc] Unknown error'); + // @ts-expect-error + error.response = response; + // @ts-expect-error + error.data = data; + throw error; + } + + return data; + } + + async getAnnouncements() { + return this.fetch('/v1/Announcement/List', 'POST', '{"parameter":{}}'); + } + + async getFriendList() { + return this.fetch('/v3/Friend/List', 'POST', '{"parameter":{}}'); + } + + async getWebServices() { + const uuid = uuidgen(); + + return this.fetch('/v1/Game/ListWebServices', 'POST', JSON.stringify({ + requestId: uuid, + })); + } + + async getActiveEvent() { + return this.fetch('/v1/Event/GetActiveEvent', 'POST', '{"parameter":{}}'); + } + + async getCurrentUser() { + return this.fetch('/v3/User/ShowSelf', 'POST', '{"parameter":{}}'); + } + + async getWebServiceToken(id: string, nintendoAccountToken: string) { + const uuid = uuidgen(); + const timestamp = '' + Math.floor(Date.now() / 1000); + + const flapg = await ZncApi.flapg(nintendoAccountToken, timestamp, uuid, 'app'); + + const req = { + id, + registrationToken: flapg.p1, + f: flapg.f, + requestId: flapg.p3, + timestamp: flapg.p2, + }; + + return this.fetch('/v2/Game/GetWebServiceToken', 'POST', JSON.stringify({ + parameter: req, + })); + } + + static async createWithSessionToken(token: string) { + const data = await this.loginWithSessionToken(token); + + return { + nso: new this(data.credential.accessToken), + data, + }; + } + + async renewToken(token: string) { + const data = await ZncApi.loginWithSessionToken(token); + + this.token = data.credential.accessToken; + + return data; + } + + static async loginWithSessionToken(token: string) { + const uuid = uuidgen(); + const timestamp = '' + Math.floor(Date.now() / 1000); + + // + // Nintendo Account token + // + + debug('[na] Getting Nintendo Account token'); + + const nintendoAccountTokenResponse = await fetch('https://accounts.nintendo.com/connect/1.0.0/api/token', { + method: 'POST', + body: JSON.stringify({ + client_id: '71b963c1b7b6d119', + session_token: token, + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer-session-token', + }), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 8.0.0)', + }, + }); + + const nintendoAccountToken = await nintendoAccountTokenResponse.json() as + NintendoAccountToken | NintendoAccountError; + + if ('errorCode' in nintendoAccountToken) { + const error = new Error('[na] ' + nintendoAccountToken.detail); + // @ts-expect-error + error.response = nintendoAccountTokenResponse; + // @ts-expect-error + error.data = nintendoAccountToken; + throw error; + } + + debug('[na] Got Nintendo Account token', nintendoAccountToken); + + // + // Nintendo Account user data + // + + debug('[na] Getting Nintendo Account user info'); + + const userResponse = await fetch('https://api.accounts.nintendo.com/2.0.0/users/me', { + headers: { + 'Accept-Language': 'en-GB', + 'User-Agent': 'NASDKAPI; Android', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + nintendoAccountToken.access_token!, + }, + }); + + const user = await userResponse.json() as NintendoAccountUser | NintendoAccountError; + + if ('errorCode' in user) { + const error = new Error('[na] ' + user.detail); + // @ts-expect-error + error.response = userResponse; + // @ts-expect-error + error.data = user; + throw error; + } + + debug('[na] Got Nintendo Account user info', user); + + // + // Nintendo Switch Online app token + // + + const flapg = await this.flapg(nintendoAccountToken.id_token, timestamp, uuid, 'nso'); + + debug('[znc] Getting Nintendo Switch Online app token'); + + const response = await fetch('https://api-lp1.znc.srv.nintendo.net/v3/Account/Login', { + method: 'POST', + body: JSON.stringify({ + parameter: { + naIdToken: flapg.p1, + naBirthday: user.birthday, + naCountry: user.country, + language: user.language, + timestamp: flapg.p2, + requestId: flapg.p3, + f: flapg.f, + }, + }), + headers: { + 'X-Platform': 'Android', + 'X-ProductVersion': '2.0.0', + 'Content-Type': 'application/json; charset=utf-8', + 'User-Agent': 'com.nintendo.znca/2.0.0(Android/8.0.0)', + }, + }); + + const data = await response.json() as ZncResponse; + + debug('[znc] Got Nintendo Switch Online app token', data); + + if ('errorMessage' in data) { + const error = new Error('[znc] ' + data.errorMessage); + // @ts-expect-error + error.response = response; + // @ts-expect-error + error.data = data; + throw error; + } + if (data.status !== 0) { + const error = new Error('[znc] Unknown error'); + // @ts-expect-error + error.response = response; + // @ts-expect-error + error.data = data; + throw error; + } + + return { + uuid, + timestamp, + nintendoAccountToken, + user, + flapg, + credential: data.result.webApiServerCredential, + }; + } + + static async getLoginHash(token: string, timestamp: string | number) { + debug('[s2s] Getting login hash'); + + const response = await fetch('https://elifessler.com/s2s/api/gen2', { + method: 'POST', + body: buildQueryString({ + naIdToken: token, + timestamp: '' + timestamp, + }), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'discord-switch-presence/1.0.0', + }, + }); + + const data = await response.json() as LoginHashApiResponse | LoginHashApiError; + + if ('error' in data) { + const error = new Error('[s2s] ' + data.error); + // @ts-expect-error + error.response = response; + // @ts-expect-error + error.data = data; + throw error; + } + + debug('[s2s] Got login hash "%s"', data.hash, data); + + return data.hash; + } + + static async flapg(token: string, timestamp: string | number, guid: string, iid: 'nso' | 'app') { + const hash = await this.getLoginHash(token, timestamp); + + debug('[flapg] Getting f parameter', { + token, timestamp, guid, iid, + }); + + const response = await fetch('https://flapg.com/ika2/api/login?public', { + headers: { + 'x-token': token, + 'x-time': '' + timestamp, + 'x-guid': guid, + 'x-hash': hash, + 'x-ver': '3', + 'x-iid': iid, + }, + }); + + const data = await response.json() as FlapgApiResponse; + + debug('[flapg] Got f parameter "%s"', data.result.f); + + return data.result; + } +} + +interface LoginHashApiResponse { + hash: string; +} +interface LoginHashApiError { + error: string; +} + +export interface FlapgApiResponse { + result: { + f: string; + p1: string; + p2: string; + p3: string; + }; +} + +export interface NintendoAccountToken { + scope: ['openid', 'user', 'user.birthday', 'user.mii', 'user.screenName']; + token_type: 'Bearer'; + id_token: string; + access_token?: string; + expires_in: 900; +} + +export interface NintendoAccountUser { + emailOptedIn: boolean; + language: string; + country: string; + timezone: { + name: string; + id: string; + utcOffsetSeconds: number; + utcOffset: string; + }; + region: null; + nickname: string; + clientFriendsOptedIn: boolean; + mii: { + favoriteColor: string; + id: string; + updatedAt: number; + coreData: { + '4': string; + } + clientId: '1cfe3a55ed8924d9'; + imageUriTemplate: string; + storeData: { + '3': string; + }; + imageOrigin: string; + etag: string; + type: 'profile'; + }; + isChild: boolean; + eachEmailOptedIn: { + survey: { + updatedAt: number; + optedIn: boolean; + }; + deals: { + updatedAt: number; + optedIn: boolean; + }; + }; + updatedAt: number; + candidateMiis: unknown[]; + id: string; + createdAt: number; + emailVerified: boolean; + analyticsPermissions: { + internalAnalysis: { + updatedAt: number; + permitted: boolean; + }; + targetMarketing: { + updatedAt: number; + permitted: boolean; + }; + }; + emailOptedInUpdatedAt: number; + birthday: string; + screenName: string; + gender: string; + analyticsOptedInUpdatedAt: number; + analyticsOptedIn: boolean; + clientFriendsOptedInUpdatedAt: number; +} + +interface NintendoAccountError { + errorCode: string; + detail: string; + instance: string; + title: string; + status: number; + type: string; +} + +interface ZncSuccessResponse { + status: 0; + result: T; + correlationId: string; +} + +interface ZncErrorResponse { + status: number; + errorMessage: string; + correlationId: string; +} + +type ZncResponse = ZncSuccessResponse | ZncErrorResponse; + +export interface AccountLogin { + user: { + id: number; + nsaId: string; + imageUri: string; + name: string; + supportId: string; + isChildRestricted: boolean; + etag: string; + links: { + nintendoAccount: { + membership: { + active: boolean; + }; + }; + friendCode: { + regenerable: boolean; + regenerableAt: number; + id: string; + }; + }; + permissions: { + presence: string; + }; + presence: Presence; + }; + webApiServerCredential: { + accessToken: string; + expiresIn: number; + }; + firebaseCredential: { + accessToken: string; + expiresIn: number; + }; +} + +interface Announcement { + announcementId: number; + priority: number; + forceDisplayEndDate: number; + distributionDate: number; + title: string; + description: string; +} + +interface Friends { + friends: Friend[]; +} + +interface Friend { + id: number; + nsaId: string; + imageUri: string; + name: string; + isFriend: boolean; + isFavoriteFriend: boolean; + isServiceUser: boolean; + friendCreatedAt: number; + presence: Presence; +} + +export interface Presence { + state: PresenceState; + updatedAt: number; + logoutAt: number; + game: Game | {}; +} + +export enum PresenceState { + OFFLINE = 'OFFLINE', + INACTIVE = 'INACTIVE', + ONLINE = 'ONLINE', +} + +export interface Game { + name: string; + imageUri: string; + shopUri: string; + totalPlayTime: number; + firstPlayedAt: number; + sysDescription: string; +} + +interface WebService { + id: number; + uri: string; + customAttributes: WebServiceAttribute[]; + whiteList: string[]; + name: string; + imageUri: string; +} + +interface WebServiceAttribute { + attrValue: string; + attrKey: string; +} + +interface ActiveEvent { + // ?? +} + +interface CurrentUser { + id: number; + nsaId: string; + imageUri: string; + name: string; + supportId: string; + isChildRestricted: boolean; + etag: string; + links: { + nintendoAccount: { + membership: { + active: { + active: boolean; + }; + }; + }; + friendCode: { + regenerable: boolean; + regenerableAt: number; + id: string; + }; + }; + permissions: { + presence: string; + }; + presence: Presence; +} + +interface WebServiceToken { + accessToken: string; + expiresIn: number; +} diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..8c6429c --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,358 @@ +import Yargs from 'yargs'; +import ZncApi, { AccountLogin, FlapgApiResponse, Game, NintendoAccountToken, NintendoAccountUser, Presence, PresenceState } from './api.js'; +import persist from 'node-persist'; +import * as path from 'path'; +// @ts-expect-error +import Table from 'cli-table/lib/index.js'; +import DiscordRPC from 'discord-rpc'; +import titles, { defaultTitle } from './titles.js'; +import * as util from 'util'; +import createDebug from 'debug'; + +const debug = createDebug('cli'); + +interface SavedToken { + uuid: string; + timestamp: string; + nintendoAccountToken: NintendoAccountToken; + user: NintendoAccountUser; + flapg: FlapgApiResponse['result']; + credential: AccountLogin['webApiServerCredential']; + + expires_at: number; +} + +async function initStorage(dir = path.join(import.meta.url.substr(7), '..', '..', 'data')) { + const storage = persist.create({ + dir, + stringify: data => JSON.stringify(data, null, 4) + '\n', + }); + await storage.init(); + return storage; +} + +async function getToken(storage: persist.LocalStorage, token: string) { + if (!token) { + console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `discord-switch-presence token`.'); + throw new Error('Invalid token'); + } + + const existingToken: SavedToken | undefined = await storage.getItem('NsoToken.' + token); + + if (!existingToken || existingToken.expires_at <= Date.now()) { + debug('Authenticating to znc with session token'); + + const data = await ZncApi.createWithSessionToken(token); + + const existingToken: SavedToken = { + ...data.data, + expires_at: Date.now() + (data.data.credential.expiresIn * 1000), + }; + + await storage.setItem('NsoToken.' + token, existingToken); + + return data; + } + + debug('Using existing token'); + + return { + nso: new ZncApi(existingToken.credential.accessToken), + data: existingToken, + }; +} + +const yargs = Yargs(process.argv.slice(2)); + +yargs.option('data-path', { + describe: 'Data storage path', + type: 'string', + default: path.join(import.meta.url.substr(7), '..', '..', 'data'), +}); + +yargs.command('token [token]', 'Set the default Nintendo Account session token', yargs => { + yargs.option('token', { + describe: 'Nintendo Account session token (it is recommended this is not set and you enter it interactively)', + type: 'string', + requiresArg: false, + }); + yargs.option('auth', { + describe: 'Authenticate immediately', + type: 'boolean', + default: true, + }); +}, async argv => { + // @ts-expect-error + const storage = await initStorage(argv.dataPath); + + let token = argv.token as string | undefined; + + if (!token) { + const read = await import('read'); + // @ts-expect-error + const prompt = util.promisify(read.default as typeof read); + + token = await prompt({ + prompt: `Token: `, + silent: true, + output: process.stderr, + }); + } + + await storage.setItem('SessionToken', token); + + if (argv.auth) { + const {nso, data} = await getToken(storage, token); + + console.log('Authenticated as Nintendo Account %s (%s)', data.user.screenName, data.user.nickname); + } else { + console.log('Saved token'); + } +}); + +yargs.command('user', 'Get the authenticated Nintendo Account', yargs => { + yargs.option('token', { + describe: 'Nintendo Account session token', + type: 'string', + requiresArg: false, + }); +}, async argv => { + // @ts-expect-error + const storage = await initStorage(argv.dataPath); + + const token: string = (argv.token as string) || await storage.getItem('SessionToken'); + const {nso, data} = await getToken(storage, token); + + console.log('Nintendo Account', data.user); +}); + +yargs.command('friends', 'List Nintendo Switch friends', yargs => { + yargs.option('token', { + describe: 'Nintendo Account session token', + type: 'string', + requiresArg: false, + }); +}, async argv => { + console.log('Listing friends'); + + // @ts-expect-error + const storage = await initStorage(argv.dataPath); + + const token: string = (argv.token as string) || await storage.getItem('SessionToken'); + const {nso, data} = await getToken(storage, token); + + const announcements = await nso.getAnnouncements(); + const friends = await nso.getFriendList(); + const webservices = await nso.getWebServices(); + const activeevent = await nso.getActiveEvent(); + + const table = new Table({ + head: [ + 'ID', + 'NA ID', + 'Name', + 'Status', + 'Favourite?', + 'Added at', + ], + }); + + for (const friend of friends.result.friends) { + const hours = 'name' in friend.presence.game ? Math.floor(friend.presence.game.totalPlayTime / 60) : 0; + const minutes = 'name' in friend.presence.game ? friend.presence.game.totalPlayTime - (hours * 60) : 0; + + table.push([ + friend.id, + friend.nsaId, + friend.name, + friend.presence.state === PresenceState.ONLINE ? + 'name' in friend.presence.game ? + 'Playing ' + friend.presence.game.name + + '; played for ' + (hours || !minutes ? hours + ' hour' + (hours === 1 ? '' : 's') : '') + + (minutes ? ', ' + minutes + ' minute' + (minutes === 1 ? '' : 's'): '') + + ' since ' + new Date(friend.presence.game.firstPlayedAt * 1000).toLocaleDateString('en-GB') : + 'Online' : + friend.presence.logoutAt ? + 'Last seen ' + new Date(friend.presence.logoutAt * 1000).toISOString() : + 'Offline', + friend.isFavoriteFriend ? 'Yes' : 'No', + new Date(friend.friendCreatedAt * 1000).toISOString(), + ]); + } + + console.log(table.toString()); +}); + +function getDiscordPresence(game: Game): { + id: string; + title: string | undefined; + presence: DiscordRPC.Presence; + showTimestamp?: boolean; +} { + const match = game.shopUri.match(/^https:\/\/ec\.nintendo\.com\/apps\/([0-9a-f]{16})\//); + + const titleid = match?.[1]; + const title = titles.find(t => t.id === titleid) || defaultTitle; + + const hours = Math.floor(game.totalPlayTime / 60); + const minutes = game.totalPlayTime - (hours * 60); + + const text = []; + + if (title.titleName === true) text.push(game.name); + else if (title.titleName) text.push(title.titleName); + + if (hours >= 1) text.push('Played for ' + hours + ' hour' + (hours === 1 ? '' : 's') + + (minutes ? ', ' + minutes + ' minute' + (minutes === 1 ? '' : 's'): '') + + ' since ' + new Date(game.firstPlayedAt * 1000).toLocaleDateString('en-GB')); + + return { + id: title.client || defaultTitle.client, + title: titleid, + presence: { + details: text[0], + state: text[1], + largeImageKey: title.largeImageKey, + smallImageKey: title.smallImageKey, + }, + showTimestamp: title.showTimestamp, + }; +} + +yargs.command('presence', 'Start Discord Rich Presence', yargs => { + yargs.option('token', { + describe: 'Nintendo Account session token', + type: 'string', + requiresArg: false, + }); + yargs.option('friend-naid', { + describe: 'Friend\'s Nintendo Account ID', + type: 'string', + requiresArg: false, + }); +}, async argv => { + // @ts-expect-error + const storage = await initStorage(argv.dataPath); + + const token: string = (argv.token as string) || await storage.getItem('SessionToken'); + const {nso, data} = await getToken(storage, token); + + let rpc: {client: DiscordRPC.Client, id: string} | null = null; + let title: {id: string; since: number} | null = null; + let i = 0; + + async function updatePresence(presence: Presence | null) { + console.log('Presence', i++, presence); + + if (presence?.state === PresenceState.ONLINE && 'name' in presence.game) { + const discordpresence = getDiscordPresence(presence.game); + + if (rpc && rpc.id !== discordpresence.id) { + await rpc?.client.destroy(); + rpc = null; + } + + if (!rpc) { + const client = new DiscordRPC.Client({transport: 'ipc'}); + await client.connect(discordpresence.id); + rpc = {client, id: discordpresence.id}; + } + + if (discordpresence.title) { + if (discordpresence.title !== title?.id) { + title = {id: discordpresence.title, since: Date.now()}; + } + + if (discordpresence.showTimestamp) { + discordpresence.presence.startTimestamp = title.since; + } + } else { + title = null; + } + + rpc.client.setActivity(discordpresence.presence); + } + + if (!presence || presence.state !== PresenceState.ONLINE || !('name' in presence.game)) { + if (rpc) { + await rpc.client.destroy(); + rpc = null; + } + + title = null; + } + } + + const announcements = await nso.getAnnouncements(); + const friends = await nso.getFriendList(); + const webservices = await nso.getWebServices(); + const activeevent = await nso.getActiveEvent(); + + if (argv.friendNaid) { + const friend = friends.result.friends.find(f => f.nsaId === argv.friendNaid); + + if (!friend) { + throw new Error('User "' + argv.friendNaid + '" is not friends with this user'); + } + + await updatePresence(friend.presence); + } else { + const user = await nso.getCurrentUser(); + + await updatePresence(user.result.presence); + } + + await new Promise(rs => setTimeout(rs, 30000)); + + while (true) { + try { + if (argv.friendNaid) { + await nso.getActiveEvent(); + await nso.getFriendList(); + await nso.getWebServices(); + + const friend = friends.result.friends.find(f => f.nsaId === argv.friendNaid); + + if (!friend) { + // Is the authenticated user no longer friends with this user? + await updatePresence(null); + continue; + } + + await updatePresence(friend.presence); + } else { + const user = await nso.getCurrentUser(); + + await updatePresence(user.result.presence); + } + + await new Promise(rs => setTimeout(rs, 30000)); + } catch (err) { + // @ts-expect-error + if (err?.data?.status === 9404) { + // Token expired + debug('Renewing token'); + + const data = await nso.renewToken(token); + + const existingToken: SavedToken = { + ...data, + expires_at: Date.now() + (data.credential.expiresIn * 1000), + }; + + await storage.setItem('NsoToken.' + token, existingToken); + } else { + throw err; + } + } + } +}); + +yargs + .scriptName('discord-switch-presence') + .demandCommand() + .help() + // .version(false) + .showHelpOnFail(false, 'Specify --help for available options'); + +export default yargs; diff --git a/src/titles.ts b/src/titles.ts new file mode 100644 index 0000000..492ec90 --- /dev/null +++ b/src/titles.ts @@ -0,0 +1,115 @@ +interface Title { + id: string; + client: string; + titleName?: string | true; + largeImageKey?: string; + smallImageKey?: string; + showTimestamp?: boolean; +} + +export const defaultTitle: Title = { + id: '0000000000000000', + client: '950883021165330493', + titleName: true, + largeImageKey: 'nintendoswitch', +}; + +const titles: Title[] = [ + { + // Splatoon 2 [Europe] + id: '0100f8f0000a2000', + client: '950886725398429726', + largeImageKey: '0100f8f0000a2000', + }, + { + // Splatoon 2 [The Americas] + id: '01003bc0000a0000', + client: '950886725398429726', + largeImageKey: '0100f8f0000a2000', + }, + { + // Splatoon 2 [Japan] + id: '01003c700009c000', + client: '950886725398429726', + largeImageKey: '01003c700009c000', + }, + + { + // Super Smash Bros. Ultimate + id: '01006a800016e000', + client: '950894516104212490', + largeImageKey: '01006a800016e000', + }, + + { + // Mario Kart 8 Deluxe + id: '0100152000022000', + client: '950905573149409280', + largeImageKey: '0100152000022000', + }, + + { + // Super Mario Odyssey + id: '0100000000010000', + client: '950905939899351050', + largeImageKey: '0100000000010000', + }, + + { + // Minecraft + id: '0100d71004694000', + client: '950906152391168020', + largeImageKey: '0100d71004694000', + }, + { + // Minecraft: Nintendo Switch Edition + id: '01006bd001e06000', + client: '950906152391168020', + largeImageKey: '01006bd001e06000', + }, + + { + // Nintendo Entertainment System - Nintendo Switch Online + id: '0100d870045b6000', + client: '950907272438104064', + titleName: 'Nintendo Entertainment System', + largeImageKey: '0100d870045b6000', + }, + { + // Super Nintendo Entertainment System - Nintendo Switch Online + id: '01008d300c50c000', + client: '950907272438104064', + titleName: 'Super Nintendo Entertainment System', + largeImageKey: '01008d300c50c000', + }, + { + // Nintendo 64 - Nintendo Switch Online + id: '0100c9a00ece6000', + client: '950907272438104064', + titleName: 'Nintendo 64', + largeImageKey: '0100c9a00ece6000', + }, + { + // SEGA Mega Drive - Nintendo Switch Online + id: '0100b3c014bda000', + client: '950907272438104064', + titleName: 'SEGA Mega Drive', + largeImageKey: '0100b3c014bda000', + }, + + { + // Animal Crossing: New Horizons + id: '01006f8002326000', + client: '950908097235415079', + largeImageKey: '01006f8002326000', + }, + { + // Animal Crossing: New Horizons Island Transfer Tool + id: '0100f38011cfe000', + client: '950908097235415079', + titleName: 'Island Transfer Tool', + largeImageKey: '0100f38011cfe000', + }, +]; + +export default titles; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2f44a73 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "target": "es2015", + "module": "es2022", + "moduleResolution": "node12", + "declaration": true, + "rootDir": "src", + "outDir": "dist" + } +}