diff --git a/LICENSE b/LICENSE index 40a5433..40bffdb 100644 --- a/LICENSE +++ b/LICENSE @@ -175,7 +175,7 @@ END OF TERMS AND CONDITIONS - Copyright 2020 - 2021 Shubham Parihar + Copyright 2020 Shubham Parihar Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/package-lock.json b/package-lock.json index 3750063..829b7bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@discordjs/collection": "^0.4.0", "@sapphire/async-queue": "^1.1.9", "oauth-1.0a": "^2.2.6", - "twitter-types": "^0.20.0", + "twitter-types": "^0.20.2", "undici": "^4.12.1" }, "devDependencies": { @@ -29,8 +29,7 @@ "prettier": "^2.5.1", "typedoc": "^0.22.10", "typescript": "^4.5.4", - "vitest": "^0.0.136", - "zx": "^4.2.0" + "vitest": "^0.0.136" }, "engines": { "node": ">=16.0.0", @@ -550,15 +549,6 @@ "@types/chai": "*" } }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -577,16 +567,6 @@ "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, - "node_modules/@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", @@ -956,12 +936,6 @@ "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1168,18 +1142,6 @@ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -1368,15 +1330,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1422,12 +1375,6 @@ "node": ">=8" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1968,21 +1915,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2126,26 +2058,6 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, "node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -2971,12 +2883,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, "node_modules/marked": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", @@ -3054,27 +2960,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3105,12 +2990,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -3149,18 +3028,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -3387,15 +3254,6 @@ "node": "*" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "dependencies": { - "through": "~2.3" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3474,21 +3332,6 @@ "node": ">=0.4.0" } }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3990,18 +3833,6 @@ "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", "dev": true }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -4011,15 +3842,6 @@ "readable-stream": "^3.0.0" } }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4199,12 +4021,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -4283,9 +4099,9 @@ "dev": true }, "node_modules/twitter-types": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/twitter-types/-/twitter-types-0.20.0.tgz", - "integrity": "sha512-UUjyHfbpcbldHb9mK1un9TLqr0eDbHFDX5149I+xnOetWYGFMcKyxqX1cggeW5NGc341S/WaynZbHd/ELjeJsQ==" + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/twitter-types/-/twitter-types-0.20.2.tgz", + "integrity": "sha512-0KCFo070I2ESO05nYpBdnIvVzNEaU4lxdQbBt3St/RgqPC14A7NdakYJML+UypvfPgz0IHWGqEOmLbU9h2Y2PA==" }, "node_modules/type-check": { "version": "0.4.0", @@ -4496,22 +4312,6 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4697,81 +4497,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zx": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/zx/-/zx-4.2.0.tgz", - "integrity": "sha512-/4f7FaJecA9I655KXKXIHO3CFNYjAz2uSmTz6v2eNlKdrQKyz4VyF3RjqFuP6nQG+Hd3+NjOvrVNBkv8Ne9d4Q==", - "dev": true, - "dependencies": { - "@types/fs-extra": "^9.0.12", - "@types/minimist": "^1.2.2", - "@types/node": "^16.6", - "@types/node-fetch": "^2.5.12", - "chalk": "^4.1.2", - "fs-extra": "^10.0.0", - "globby": "^12.0.1", - "minimist": "^1.2.5", - "node-fetch": "^2.6.1", - "ps-tree": "^1.2.0", - "which": "^2.0.2" - }, - "bin": { - "zx": "zx.mjs" - }, - "engines": { - "node": ">= 14.13.0" - } - }, - "node_modules/zx/node_modules/@types/node": { - "version": "16.11.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.19.tgz", - "integrity": "sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng==", - "dev": true - }, - "node_modules/zx/node_modules/array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zx/node_modules/globby": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz", - "integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==", - "dev": true, - "dependencies": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.8", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zx/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } }, "dependencies": { @@ -5179,15 +4904,6 @@ "@types/chai": "*" } }, - "@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -5206,16 +4922,6 @@ "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, - "@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", - "dev": true, - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, "@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", @@ -5444,12 +5150,6 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5610,15 +5310,6 @@ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -5759,12 +5450,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -5798,12 +5483,6 @@ "is-obj": "^2.0.0" } }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -6160,21 +5839,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6293,23 +5957,6 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -6910,12 +6557,6 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, "marked": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz", @@ -6971,21 +6612,6 @@ "picomatch": "^2.2.3" } }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7007,12 +6633,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -7042,15 +6662,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, "normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -7211,15 +6822,6 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7270,15 +6872,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -7628,15 +7221,6 @@ "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", "dev": true }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -7646,15 +7230,6 @@ "readable-stream": "^3.0.0" } }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -7785,12 +7360,6 @@ "is-number": "^7.0.0" } }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -7841,9 +7410,9 @@ } }, "twitter-types": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/twitter-types/-/twitter-types-0.20.0.tgz", - "integrity": "sha512-UUjyHfbpcbldHb9mK1un9TLqr0eDbHFDX5149I+xnOetWYGFMcKyxqX1cggeW5NGc341S/WaynZbHd/ELjeJsQ==" + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/twitter-types/-/twitter-types-0.20.2.tgz", + "integrity": "sha512-0KCFo070I2ESO05nYpBdnIvVzNEaU4lxdQbBt3St/RgqPC14A7NdakYJML+UypvfPgz0IHWGqEOmLbU9h2Y2PA==" }, "type-check": { "version": "0.4.0", @@ -7967,22 +7536,6 @@ "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", "dev": true }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8121,59 +7674,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true - }, - "zx": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/zx/-/zx-4.2.0.tgz", - "integrity": "sha512-/4f7FaJecA9I655KXKXIHO3CFNYjAz2uSmTz6v2eNlKdrQKyz4VyF3RjqFuP6nQG+Hd3+NjOvrVNBkv8Ne9d4Q==", - "dev": true, - "requires": { - "@types/fs-extra": "^9.0.12", - "@types/minimist": "^1.2.2", - "@types/node": "^16.6", - "@types/node-fetch": "^2.5.12", - "chalk": "^4.1.2", - "fs-extra": "^10.0.0", - "globby": "^12.0.1", - "minimist": "^1.2.5", - "node-fetch": "^2.6.1", - "ps-tree": "^1.2.0", - "which": "^2.0.2" - }, - "dependencies": { - "@types/node": { - "version": "16.11.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.19.tgz", - "integrity": "sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng==", - "dev": true - }, - "array-union": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", - "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", - "dev": true - }, - "globby": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz", - "integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==", - "dev": true, - "requires": { - "array-union": "^3.0.1", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.7", - "ignore": "^5.1.8", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } - } } } } diff --git a/package.json b/package.json index b47308e..8587a79 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "0.10.1", "author": "Shubham Parihar ", "description": "An object-oriented Node.js and TypeScript library for interacting with Twitter API v2", - "main": "dist/cjs/index.js", - "module": "dist/esm/index.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "scripts": { - "build": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json", + "build": "tsc", "build:dev": "tsc -w", "doc": "typedoc", "test": "vitest", @@ -15,14 +15,13 @@ "lint:fix": "eslint src --ext ts --fix", "format": "prettier --write **/*.{json,yml,yaml}", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", - "zx": "zx ./script.mjs", - "prepublishOnly": "npm run build && npm run zx" + "prepublishOnly": "npm run build" }, "dependencies": { "@discordjs/collection": "^0.4.0", "@sapphire/async-queue": "^1.1.9", "oauth-1.0a": "^2.2.6", - "twitter-types": "^0.20.0", + "twitter-types": "^0.20.2", "undici": "^4.12.1" }, "devDependencies": { @@ -39,8 +38,7 @@ "prettier": "^2.5.1", "typedoc": "^0.22.10", "typescript": "^4.5.4", - "vitest": "^0.0.136", - "zx": "^4.2.0" + "vitest": "^0.0.136" }, "files": [ "/dist" diff --git a/script.mjs b/script.mjs deleted file mode 100644 index 3626645..0000000 --- a/script.mjs +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env zx - -import { $ } from 'zx'; - -if (process.platform === 'win32') { - await $`echo { "type": "commonjs" } > dist/cjs/package.json`; - await $`echo { "type": "module" } > dist/esm/package.json`; -} else if (process.platform === 'linux') { - await $`echo { \\"type\\": \\"commonjs\\" } > dist/cjs/package.json`; - await $`echo { \\"type\\": \\"module\\" } > dist/esm/package.json`; -} diff --git a/src/books/BaseBook.ts b/src/books/BaseBook.ts index 4ff6484..17a92b7 100644 --- a/src/books/BaseBook.ts +++ b/src/books/BaseBook.ts @@ -1,4 +1,5 @@ import type { Client } from '../client'; +import type { BaseBookOptions } from '../typings'; /** * The base class for all books @@ -9,11 +10,47 @@ export class BaseBook { */ client: Client; + /** + * The token for fetching next page + * @internal + */ + _nextToken?: string; + + /** + * The token for fetching previous page + * @internal + */ + _previousToken?: string; + + /** + * Whether an initial request for fetching the first page has already been made + * + * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link ComposedTweetsBook.fetchNextPage} + * @internal + */ + _hasMadeInitialRequest?: boolean; + + /** + * The maximum amount of tweets that will be fetched per page. + * + * **Note:** This is the max count and will **not** always be equal to the number of tweets fetched in a page + */ + maxResultsPerPage: number | null; + + /** + * Whether there are more pages of tweets to be fetched + * + * **Note:** Use this as a check for deciding whether to fetch more pages + */ + hasMore: boolean; + /** * @param client The logged in {@link Client} instance */ - constructor(client: Client) { + constructor(client: Client, options?: BaseBookOptions) { Object.defineProperty(this, 'client', { writable: true, enumerable: false }); this.client = client; + this.hasMore = true; + this.maxResultsPerPage = options?.maxResultsPerPage ?? null; } } diff --git a/src/books/BaseRangeBook.ts b/src/books/BaseRangeBook.ts new file mode 100644 index 0000000..a9adc56 --- /dev/null +++ b/src/books/BaseRangeBook.ts @@ -0,0 +1,38 @@ +import { BaseBook } from './BaseBook'; +import type { Client } from '../client'; +import type { Snowflake } from 'twitter-types'; +import type { BaseRangeBookOptions } from '../typings'; + +export class BaseRangeBook extends BaseBook { + /** + * The book will fetch tweets that were created after this tweet Id + */ + afterTweetId: Snowflake | null; + + /** + * The book will fetch tweets that were created before this tweet Id + */ + beforeTweetId: Snowflake | null; + + /** + * The book will fetch tweets that were created at or after this timestamp + */ + startTimestamp: number | null; + + /** + * The book will fetch tweets that were created at or before this timestamp + */ + endTimestamp: number | null; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the composed tweets book with + */ + constructor(client: Client, options: BaseRangeBookOptions) { + super(client, options); + this.afterTweetId = (options.afterTweet && client.tweets.resolveId(options.afterTweet)) ?? null; + this.beforeTweetId = (options.beforeTweet && client.tweets.resolveId(options.beforeTweet)) ?? null; + this.startTimestamp = (options.startTime && new Date(options.startTime).getTime()) ?? null; + this.endTimestamp = (options.endTime && new Date(options.endTime).getTime()) ?? null; + } +} diff --git a/src/books/BlocksBook.ts b/src/books/BlocksBook.ts index b3ce6b2..0172e9f 100644 --- a/src/books/BlocksBook.ts +++ b/src/books/BlocksBook.ts @@ -12,50 +12,19 @@ import type { GETUsersIdBlockingQuery, GETUsersIdBlockingResponse, Snowflake } f */ export class BlocksBook extends BaseBook { /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link BlocksBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - - /** - * The ID of the user this book belongs to + * The Id of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of users that will be fetched per page - * - * **Note:** This is the max count and will **not** always be equal to the number of users fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of users to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the blocks book with */ constructor(client: Client, options: BlocksBookOptions) { - super(client); - this.userId = options.userId; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; - this.hasMore = true; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create BlocksBook for'); + this.userId = userId; } /** @@ -63,12 +32,12 @@ export class BlocksBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that have been blocked by the authorized user */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -76,8 +45,8 @@ export class BlocksBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that have been blocked by the authorized user */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -94,8 +63,8 @@ export class BlocksBook extends BaseBook { if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; const requestData = new RequestData({ query, isUserContext: true }); const data: GETUsersIdBlockingResponse = await this.client._api.users(this.userId).blocking.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return blockedUsersCollection; const rawUsers = data.data; diff --git a/src/books/ComposedTweetsBook.ts b/src/books/ComposedTweetsBook.ts index 8579ffb..0c3da11 100644 --- a/src/books/ComposedTweetsBook.ts +++ b/src/books/ComposedTweetsBook.ts @@ -1,7 +1,7 @@ import { Collection } from '../util'; -import { BaseBook } from './BaseBook'; import { CustomError } from '../errors'; import { RequestData } from '../structures'; +import { BaseRangeBook } from './BaseRangeBook'; import type { Client } from '../client'; import type { Tweet } from '../structures'; import type { ComposedTweetsBookOptions } from '../typings'; @@ -10,63 +10,12 @@ import type { GETUsersIdTweetsQuery, GETUsersIdTweetsResponse, Snowflake } from /** * A class for fetching tweets composed by a twitter user */ -export class ComposedTweetsBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link ComposedTweetsBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - +export class ComposedTweetsBook extends BaseRangeBook { /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of tweets that will be fetched per page. - * - * **Note:** This is the max count and will **not** always be equal to the number of tweets fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of tweets to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - - /** - * The book will fetch tweets that were created after this tweet ID - */ - afterTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created before this tweet ID - */ - beforeTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created after this timestamp - */ - afterTimestamp: number | null; - - /** - * The book will fetch tweets that were created before this timestamp - */ - beforeTimestamp: number | null; - /** * The types of tweets that the book should not fetch */ @@ -77,15 +26,11 @@ export class ComposedTweetsBook extends BaseBook { * @param options The options to initialize the composed tweets book with */ constructor(client: Client, options: ComposedTweetsBookOptions) { - super(client); - this.hasMore = true; - this.userId = options.userId; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create ComposedTweetsBook for'); + this.userId = userId; this.exclude = options.exclude ?? null; - this.afterTweetId = options.afterTweetId ?? null; - this.beforeTweetId = options.beforeTweetId ?? null; - this.afterTimestamp = options.afterTimestamp ?? null; - this.beforeTimestamp = options.beforeTimestamp ?? null; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; } /** @@ -93,12 +38,12 @@ export class ComposedTweetsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweet} objects composed by the owner of this book */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -106,8 +51,8 @@ export class ComposedTweetsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweet} objects composed by the owner of this book */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -128,12 +73,12 @@ export class ComposedTweetsBook extends BaseBook { if (this.afterTweetId) query.since_id = this.afterTweetId; if (this.beforeTweetId) query.until_id = this.beforeTweetId; if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; - if (this.afterTimestamp) query.start_time = new Date(this.afterTimestamp).toISOString(); - if (this.beforeTimestamp) query.end_time = new Date(this.beforeTimestamp).toISOString(); + if (this.startTimestamp) query.start_time = new Date(this.startTimestamp).toISOString(); + if (this.endTimestamp) query.end_time = new Date(this.endTimestamp).toISOString(); const requestData = new RequestData({ query }); const data: GETUsersIdTweetsResponse = await this.client._api.users(this.userId).tweets.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return tweetsCollection; const rawTweets = data.data; diff --git a/src/books/FollowedListsBook.ts b/src/books/FollowedListsBook.ts new file mode 100644 index 0000000..7f5a61c --- /dev/null +++ b/src/books/FollowedListsBook.ts @@ -0,0 +1,79 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { type List, RequestData } from '../structures'; +import type { Client } from '../client'; +import type { FollowedListsBookOptions } from '../typings'; +import type { GETUsersIdFollowedListsQuery, GETUsersIdFollowedListsResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching lists followed by a user + */ +export class FollowedListsBook extends BaseBook { + /** + * The Id of the user this book belongs to + */ + userId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the book with + */ + constructor(client: Client, options: FollowedListsBookOptions) { + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create FollowedListsBook for'); + this.userId = userId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link List} that the given user follows + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link List} that the given user follows + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const followedLists = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETUsersIdFollowedListsQuery = { + expansions: queryParameters?.listExpansions, + 'user.fields': queryParameters?.userFields, + 'list.fields': queryParameters?.listFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdFollowedListsResponse = await this.client._api + .users(this.userId) + .followed_lists.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return followedLists; + const rawLists = data.data; + const rawIncludes = data.includes; + for (const rawList of rawLists) { + const list = this.client.lists._add(rawList.id, { data: rawList, includes: rawIncludes }, false); + followedLists.set(list.id, list); + } + return followedLists; + } +} diff --git a/src/books/FollowersBook.ts b/src/books/FollowersBook.ts index 1668393..86b2027 100644 --- a/src/books/FollowersBook.ts +++ b/src/books/FollowersBook.ts @@ -11,52 +11,20 @@ import type { GETUsersIdFollowersQuery, GETUsersIdFollowersResponse, Snowflake } * A class for fetching followers of a twitter user */ export class FollowersBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link FollowersBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of users that will be fetched per page - * - * **Note:** This is the max count and will **not** always be equal to the number of users fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of users to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the followers book with */ constructor(client: Client, options: FollowersBookOptions) { - super(client); - - this.hasMore = true; - this.userId = options.userId; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create FollowersBook for'); + this.userId = userId; } /** @@ -64,12 +32,12 @@ export class FollowersBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects who have been following the owner of this book */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -77,8 +45,8 @@ export class FollowersBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects who have been following the owner of this book */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -95,8 +63,8 @@ export class FollowersBook extends BaseBook { if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; const requestData = new RequestData({ query }); const data: GETUsersIdFollowersResponse = await this.client._api.users(this.userId).followers.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return followersCollection; const rawUsers = data.data; diff --git a/src/books/FollowingsBook.ts b/src/books/FollowingsBook.ts index bcfa0fc..16f60eb 100644 --- a/src/books/FollowingsBook.ts +++ b/src/books/FollowingsBook.ts @@ -11,52 +11,20 @@ import type { GETUsersIdFollowingQuery, GETUsersIdFollowingResponse, Snowflake } * A class for fetching users followed by a twitter user */ export class FollowingsBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link FollowingsBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of users that will be fetched per page - * - * **Note:** This is the max count and will **not** always be equal to the number of users fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of users to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the followings book with */ constructor(client: Client, options: FollowingsBookOptions) { - super(client); - - this.hasMore = true; - this.userId = options.userId; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create FollowingsBook for'); + this.userId = userId; } /** @@ -64,12 +32,12 @@ export class FollowingsBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that the owner of this book is following */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -77,8 +45,8 @@ export class FollowingsBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that the owner of this book is following */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -95,8 +63,8 @@ export class FollowingsBook extends BaseBook { if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; const requestData = new RequestData({ query }); const data: GETUsersIdFollowingResponse = await this.client._api.users(this.userId).following.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return followingsCollection; const rawUsers = data.data; diff --git a/src/books/LikedTweetsBook.ts b/src/books/LikedTweetsBook.ts index 78ca3b8..01301b5 100644 --- a/src/books/LikedTweetsBook.ts +++ b/src/books/LikedTweetsBook.ts @@ -11,52 +11,20 @@ import type { GETUsersIdLikedTweetsQuery, GETUsersIdLikedTweetsResponse, Snowfla * A class for fetching tweets liked by a twitter user */ export class LikedTweetsBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link FollowersBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of tweets that will be fetched per page. - * - * **Note:** This is the max count and will **not** always be equal to the number of tweets fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of tweets to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the liked tweets book with */ constructor(client: Client, options: LikedTweetsBookOptions) { - super(client); - - this.hasMore = true; - this.userId = options.userId; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create LikedTweetsBook for'); + this.userId = userId; } /** @@ -64,12 +32,12 @@ export class LikedTweetsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweet} objects liked by the owner of this book */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -77,8 +45,8 @@ export class LikedTweetsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweet} objects liked by the owner of this book */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -98,8 +66,8 @@ export class LikedTweetsBook extends BaseBook { if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; const requestData = new RequestData({ query }); const data: GETUsersIdLikedTweetsResponse = await this.client._api.users(this.userId).liked_tweets.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return likedTweetsCollection; const rawTweets = data.data; diff --git a/src/books/ListFollowersBook.ts b/src/books/ListFollowersBook.ts new file mode 100644 index 0000000..30ae7c6 --- /dev/null +++ b/src/books/ListFollowersBook.ts @@ -0,0 +1,77 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { RequestData, type User } from '../structures'; +import type { Client } from '../client'; +import type { ListFollowersBookOptions } from '../typings'; +import type { GETListsIdFollowersQuery, GETUsersIdFollowersResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching users who are followers of a list + */ +export class ListFollowersBook extends BaseBook { + /** + * The Id of the list this book belongs to + */ + listId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the book with + */ + constructor(client: Client, options: ListFollowersBookOptions) { + super(client, options); + const listId = client.lists.resolveId(options.list); + if (!listId) throw new CustomError('LIST_RESOLVE_ID', 'create ListFollowersBook for'); + this.listId = listId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link User} who follow the given list + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link User} who follow the given list + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const listFollowers = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETListsIdFollowersQuery = { + expansions: queryParameters?.userExpansions, + 'tweet.fields': queryParameters?.tweetFields, + 'user.fields': queryParameters?.userFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdFollowersResponse = await this.client._api.users(this.listId).followers.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return listFollowers; + const rawUsers = data.data; + const rawIncludes = data.includes; + for (const rawUser of rawUsers) { + const user = this.client.users._add(rawUser.id, { data: rawUser, includes: rawIncludes }); + listFollowers.set(user.id, user); + } + return listFollowers; + } +} diff --git a/src/books/ListMembersBook.ts b/src/books/ListMembersBook.ts new file mode 100644 index 0000000..d1e8acf --- /dev/null +++ b/src/books/ListMembersBook.ts @@ -0,0 +1,77 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { RequestData, type User } from '../structures'; +import type { Client } from '../client'; +import type { ListMembersBookOptions } from '../typings'; +import type { GETListsIdMembersQuery, GETUsersIdFollowersResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching users who are members of a list + */ +export class ListMembersBook extends BaseBook { + /** + * The Id of the list this book belongs to + */ + listId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the book with + */ + constructor(client: Client, options: ListMembersBookOptions) { + super(client, options); + const listId = client.lists.resolveId(options.list); + if (!listId) throw new CustomError('LIST_RESOLVE_ID', 'create ListMembersBook for'); + this.listId = listId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link User} objects who are members of the given list + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link User} objects who are members of the given list + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const listMembers = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETListsIdMembersQuery = { + expansions: queryParameters?.userExpansions, + 'tweet.fields': queryParameters?.tweetFields, + 'user.fields': queryParameters?.userFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdFollowersResponse = await this.client._api.users(this.listId).followers.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return listMembers; + const rawUsers = data.data; + const rawIncludes = data.includes; + for (const rawUser of rawUsers) { + const user = this.client.users._add(rawUser.id, { data: rawUser, includes: rawIncludes }); + listMembers.set(user.id, user); + } + return listMembers; + } +} diff --git a/src/books/ListMembershipsBook.ts b/src/books/ListMembershipsBook.ts new file mode 100644 index 0000000..4633fc7 --- /dev/null +++ b/src/books/ListMembershipsBook.ts @@ -0,0 +1,79 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { type List, RequestData } from '../structures'; +import type { Client } from '../client'; +import type { ListMembershipsBookOptions } from '../typings'; +import type { GETUsersIdListMembershipsQuery, GETUsersIdListMembershipsResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching lists in which a user is a member + */ +export class ListMembershipsBook extends BaseBook { + /** + * The Id of the user this book belongs to + */ + userId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the book with + */ + constructor(client: Client, options: ListMembershipsBookOptions) { + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create ListMembershipsBook for'); + this.userId = userId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link List} in which the given user is a member + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link List} in which the given user is a member + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const lists = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETUsersIdListMembershipsQuery = { + expansions: queryParameters?.listExpansions, + 'user.fields': queryParameters?.userFields, + 'list.fields': queryParameters?.listFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdListMembershipsResponse = await this.client._api + .users(this.userId) + .list_memberships.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return lists; + const rawLists = data.data; + const rawIncludes = data.includes; + for (const rawList of rawLists) { + const list = this.client.lists._add(rawList.id, { data: rawList, includes: rawIncludes }, false); + lists.set(list.id, list); + } + return lists; + } +} diff --git a/src/books/ListTweetsBook.ts b/src/books/ListTweetsBook.ts new file mode 100644 index 0000000..619602c --- /dev/null +++ b/src/books/ListTweetsBook.ts @@ -0,0 +1,80 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { RequestData, type Tweet } from '../structures'; +import type { Client } from '../client'; +import type { ListTweetsBookOptions } from '../typings'; +import type { GETListsIdTweetsQuery, GETListsIdtweetsResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching tweets from a list + */ +export class ListTweetsBook extends BaseBook { + /** + * The Id of the list this book belongs to + */ + listId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the list tweets book with + */ + constructor(client: Client, options: ListTweetsBookOptions) { + super(client, options); + const listId = client.lists.resolveId(options.list); + if (!listId) throw new CustomError('LIST_RESOLVE_ID', 'create ListTweetsBook for'); + this.listId = listId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link Tweet} objects belonging to the given list + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link Tweet} objects belonging to the given list + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const listTweets = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETListsIdTweetsQuery = { + expansions: queryParameters?.tweetExpansions, + 'media.fields': queryParameters?.mediaFields, + 'place.fields': queryParameters?.placeFields, + 'poll.fields': queryParameters?.pollFields, + 'tweet.fields': queryParameters?.tweetFields, + 'user.fields': queryParameters?.userFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETListsIdtweetsResponse = await this.client._api.lists(this.listId).tweets.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return listTweets; + const rawTweets = data.data; + const rawIncludes = data.includes; + for (const rawTweet of rawTweets) { + const tweet = this.client.tweets._add(rawTweet.id, { data: rawTweet, includes: rawIncludes }); + listTweets.set(tweet.id, tweet); + } + return listTweets; + } +} diff --git a/src/books/MentionsBook.ts b/src/books/MentionsBook.ts index 0fb0809..9e07ae6 100644 --- a/src/books/MentionsBook.ts +++ b/src/books/MentionsBook.ts @@ -1,7 +1,7 @@ import { Collection } from '../util'; -import { BaseBook } from './BaseBook'; import { CustomError } from '../errors'; import { RequestData } from '../structures'; +import { BaseRangeBook } from './BaseRangeBook'; import type { Client } from '../client'; import type { Tweet } from '../structures'; import type { MentionsBookOptions } from '../typings'; @@ -10,76 +10,21 @@ import type { GETUsersIdMentionsQuery, GETUsersIdMentionsResponse, Snowflake } f /** * A class for fetching tweets that mention a twitter user */ -export class MentionsBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link FollowersBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - +export class MentionsBook extends BaseRangeBook { /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of tweets that will be fetched per page. - * - * **Note:** This is the max count and will **not** always be equal to the number of tweets fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of tweets to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - - /** - * The book will fetch tweets that were created after this tweet ID - */ - afterTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created before this tweet ID - */ - beforeTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created after this timestamp - */ - afterTimestamp: number | null; - - /** - * The book will fetch tweets that were created before this timestamp - */ - beforeTimestamp: number | null; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the mentions book with */ constructor(client: Client, options: MentionsBookOptions) { - super(client); - this.hasMore = true; - this.userId = options.userId; - this.afterTweetId = options.afterTweetId ?? null; - this.beforeTweetId = options.beforeTweetId ?? null; - this.afterTimestamp = options.afterTimestamp ?? null; - this.beforeTimestamp = options.beforeTimestamp ?? null; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create LikedTweetsBook for'); + this.userId = userId; } /** @@ -87,12 +32,12 @@ export class MentionsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweets} mentioning the owner of this book */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -100,8 +45,8 @@ export class MentionsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweets} mentioning the owner of this book */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -121,12 +66,12 @@ export class MentionsBook extends BaseBook { if (this.afterTweetId) query.since_id = this.afterTweetId; if (this.beforeTweetId) query.until_id = this.beforeTweetId; if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; - if (this.afterTimestamp) query.start_time = new Date(this.afterTimestamp).toISOString(); - if (this.beforeTimestamp) query.end_time = new Date(this.beforeTimestamp).toISOString(); + if (this.startTimestamp) query.start_time = new Date(this.startTimestamp).toISOString(); + if (this.endTimestamp) query.end_time = new Date(this.endTimestamp).toISOString(); const requestData = new RequestData({ query }); const data: GETUsersIdMentionsResponse = await this.client._api.users(this.userId).mentions.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return mentioningTweetsCollection; const rawTweets = data.data; diff --git a/src/books/MutesBook.ts b/src/books/MutesBook.ts index ed166ff..db21d87 100644 --- a/src/books/MutesBook.ts +++ b/src/books/MutesBook.ts @@ -11,51 +11,20 @@ import type { GETUsersIdMutingQuery, GETUsersIdMutingResponse, Snowflake } from * A class for fetching users muted by the authorized user */ export class MutesBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * The token for fetching previous page - */ - #previousToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link BlocksBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - /** * The ID of the user this book belongs to */ userId: Snowflake; - /** - * The maximum amount of users that will be fetched per page - * - * **Note:** This is the max count and will **not** always be equal to the number of users fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of users to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the mutes book with */ constructor(client: Client, options: MutesBookOptions) { - super(client); - this.userId = options.userId; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; - this.hasMore = true; + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create LikedTweetsBook for'); + this.userId = userId; } /** @@ -63,12 +32,12 @@ export class MutesBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that have been muted by the authorized user */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } /** @@ -76,8 +45,8 @@ export class MutesBook extends BaseBook { * @returns A {@link Collection} of {@link User} objects that have been muted by the authorized user */ async fetchPreviousPage(): Promise> { - if (!this.#previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); - return this.#fetchPages(this.#previousToken); + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -94,8 +63,8 @@ export class MutesBook extends BaseBook { if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; const requestData = new RequestData({ query, isUserContext: true }); const data: GETUsersIdMutingResponse = await this.client._api.users(this.userId).muting.get(requestData); - this.#nextToken = data.meta.next_token; - this.#previousToken = data.meta.previous_token; + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return mutedUsersCollection; const rawUsers = data.data; diff --git a/src/books/OwnedListsBook.ts b/src/books/OwnedListsBook.ts new file mode 100644 index 0000000..3a394d4 --- /dev/null +++ b/src/books/OwnedListsBook.ts @@ -0,0 +1,77 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { type List, RequestData } from '../structures'; +import type { Client } from '../client'; +import type { OwnedListsBookOptions } from '../typings'; +import type { GETUsersIdOwnedListsQuery, GETUsersIdOwnedListsResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching lists owned by a user + */ +export class OwnedListsBook extends BaseBook { + /** + * The Id of the user this book belongs to + */ + userId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the blocks book with + */ + constructor(client: Client, options: OwnedListsBookOptions) { + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create OwnedListsBook for'); + this.userId = userId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link List} objects owned by the user + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link List} objects owned by the user + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const ownedLists = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETUsersIdOwnedListsQuery = { + expansions: queryParameters?.listExpansions, + 'user.fields': queryParameters?.userFields, + 'list.fields': queryParameters?.listFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdOwnedListsResponse = await this.client._api.users(this.userId).owned_lists.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return ownedLists; + const rawLists = data.data; + const rawIncludes = data.includes; + for (const rawList of rawLists) { + const list = this.client.lists._add(rawList.id, { data: rawList, includes: rawIncludes }, false); + ownedLists.set(list.id, list); + } + return ownedLists; + } +} diff --git a/src/books/PinnedListsBook.ts b/src/books/PinnedListsBook.ts new file mode 100644 index 0000000..a0844c0 --- /dev/null +++ b/src/books/PinnedListsBook.ts @@ -0,0 +1,79 @@ +import { Collection } from '../util'; +import { BaseBook } from './BaseBook'; +import { CustomError } from '../errors'; +import { type List, RequestData } from '../structures'; +import type { Client } from '../client'; +import type { PinnedListsBookOptions } from '../typings'; +import type { GETUsersIdPinnedListsQuery, GETUsersIdPinnedListsResponse, Snowflake } from 'twitter-types'; + +/** + * A class for fetching lists pinned by the authorized user + */ +export class PinnedListsBook extends BaseBook { + /** + * The Id of the user this book belongs to + */ + userId: Snowflake; + + /** + * @param client The logged in {@link Client} instance + * @param options The options to initialize the book with + */ + constructor(client: Client, options: PinnedListsBookOptions) { + super(client, options); + const userId = client.users.resolveId(options.user); + if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create PinnedListsBook for'); + this.userId = userId; + } + + /** + * Fetches the next page of the book if there is one. + * @returns A {@link Collection} of {@link List} pinned by the given user + */ + async fetchNextPage(): Promise> { + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; + return this.#fetchPages(); + } + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); + } + + /** + * Fetches the previous page of the book if there is one. + * @returns A {@link Collection} of {@link List} pinned by the given user + */ + async fetchPreviousPage(): Promise> { + if (!this._previousToken) throw new CustomError('PAGINATED_RESPONSE_HEAD_REACHED'); + return this.#fetchPages(this._previousToken); + } + + // #### 🚧 PRIVATE METHODS 🚧 #### + + async #fetchPages(token?: string): Promise> { + const pinnedLists = new Collection(); + const queryParameters = this.client.options.queryParameters; + const query: GETUsersIdPinnedListsQuery = { + expansions: queryParameters?.listExpansions, + 'user.fields': queryParameters?.userFields, + 'list.fields': queryParameters?.listFields, + pagination_token: token, + }; + if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; + const requestData = new RequestData({ query }); + const data: GETUsersIdPinnedListsResponse = await this.client._api + .users(this.userId) + .followed_lists.get(requestData); + this._nextToken = data.meta.next_token; + this._previousToken = data.meta.previous_token; + this.hasMore = data.meta.next_token ? true : false; + if (data.meta.result_count === 0) return pinnedLists; + const rawLists = data.data; + const rawIncludes = data.includes; + for (const rawList of rawLists) { + const list = this.client.lists._add(rawList.id, { data: rawList, includes: rawIncludes }, false); + pinnedLists.set(list.id, list); + } + return pinnedLists; + } +} diff --git a/src/books/SearchTweetsBook.ts b/src/books/SearchTweetsBook.ts index cbe5ddb..b132300 100644 --- a/src/books/SearchTweetsBook.ts +++ b/src/books/SearchTweetsBook.ts @@ -1,7 +1,7 @@ import { Collection } from '../util'; -import { BaseBook } from './BaseBook'; import { CustomError } from '../errors'; import { RequestData } from '../structures'; +import { BaseRangeBook } from './BaseRangeBook'; import type { Client } from '../client'; import type { Tweet } from '../structures'; import type { SearchTweetsBookOptions } from '../typings'; @@ -10,71 +10,19 @@ import type { GETTweetsSearchRecentQuery, GETTweetsSearchRecentResponse, Snowfla /** * A class for fetching tweets using search query */ -export class SearchTweetsBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to not throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link FollowingsBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - - /** - * The maximum amount of tweets that will be fetched per page. - * - * **Note:** This is the max count and will **not** always be equal to the number of tweets fetched in a page - */ - maxResultsPerPage: number | null; - - /** - * Whether there are more pages of tweets to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - +export class SearchTweetsBook extends BaseRangeBook { /** * The query for searching tweets */ query: string; - /** - * The book will fetch tweets that were created after this tweet ID - */ - afterTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created before this tweet ID - */ - beforeTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created after this timestamp - */ - afterTimestamp: number | null; - - /** - * The book will fetch tweets that were created before this timestamp - */ - beforeTimestamp: number | null; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the search tweets book with */ constructor(client: Client, options: SearchTweetsBookOptions) { - super(client); - this.hasMore = true; + super(client, options); this.query = options.query; - this.afterTweetId = options.afterTweetId ?? null; - this.beforeTweetId = options.beforeTweetId ?? null; - this.afterTimestamp = options.afterTimestamp ?? null; - this.beforeTimestamp = options.beforeTimestamp ?? null; - this.maxResultsPerPage = options.maxResultsPerPage ?? null; } /** @@ -82,12 +30,12 @@ export class SearchTweetsBook extends BaseBook { * @returns A {@link Collection} of {@link Tweet} objects matching the search query */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -108,11 +56,11 @@ export class SearchTweetsBook extends BaseBook { if (this.afterTweetId) query.since_id = this.afterTweetId; if (this.beforeTweetId) query.until_id = this.beforeTweetId; if (this.maxResultsPerPage) query.max_results = this.maxResultsPerPage; - if (this.afterTimestamp) query.start_time = new Date(this.afterTimestamp).toISOString(); - if (this.beforeTimestamp) query.end_time = new Date(this.beforeTimestamp).toISOString(); + if (this.startTimestamp) query.start_time = new Date(this.startTimestamp).toISOString(); + if (this.endTimestamp) query.end_time = new Date(this.endTimestamp).toISOString(); const requestData = new RequestData({ query }); const data: GETTweetsSearchRecentResponse = await this.client._api.tweets.search.recent.get(requestData); - this.#nextToken = data.meta.next_token; + this._nextToken = data.meta.next_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.result_count === 0) return tweetsCollection; const rawTweets = data.data; diff --git a/src/books/TweetsCountBook.ts b/src/books/TweetsCountBook.ts index d37bbe7..c9505f4 100644 --- a/src/books/TweetsCountBook.ts +++ b/src/books/TweetsCountBook.ts @@ -1,33 +1,14 @@ -import { BaseBook } from './BaseBook'; import { CustomError } from '../errors'; +import { BaseRangeBook } from './BaseRangeBook'; import { RequestData, TweetCountBucket } from '../structures'; import type { Client } from '../client'; import type { TweetsCountBookOptions } from '../typings'; -import type { GETTweetsCountsRecentQuery, GETTweetsCountsRecentResponse, Snowflake } from 'twitter-types'; +import type { GETTweetsCountsRecentQuery, GETTweetsCountsRecentResponse } from 'twitter-types'; /** * A class for fetching number of tweets matching a search query */ -export class TweetsCountBook extends BaseBook { - /** - * The token for fetching next page - */ - #nextToken?: string; - - /** - * Whether an initial request for fetching the first page has already been made - * - * **Note**: Use this to **not** throw `PAGINATED_RESPONSE_TAIL_REACHED` error for initial page request in {@link BlocksBook.fetchNextPage} - */ - #hasMadeInitialRequest?: boolean; - - /** - * Whether there are more pages to be fetched - * - * **Note:** Use this as a check for deciding whether to fetch more pages - */ - hasMore: boolean; - +export class TweetsCountBook extends BaseRangeBook { /** * The query for searching tweets */ @@ -38,39 +19,14 @@ export class TweetsCountBook extends BaseBook { */ granularity: GETTweetsCountsRecentQuery['granularity'] | null; - /** - * The book will fetch tweets that were created after this tweet ID - */ - afterTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created before this tweet ID - */ - beforeTweetId: Snowflake | null; - - /** - * The book will fetch tweets that were created after this timestamp - */ - afterTimestamp: number | null; - - /** - * The book will fetch tweets that were created before this timestamp - */ - beforeTimestamp: number | null; - /** * @param client The logged in {@link Client} instance * @param options The options to initialize the count tweets book with */ constructor(client: Client, options: TweetsCountBookOptions) { - super(client); - this.hasMore = true; + super(client, options); this.query = options.query; this.granularity = options.granularity ?? null; - this.afterTweetId = options.afterTweetId ?? null; - this.beforeTweetId = options.beforeTweetId ?? null; - this.afterTimestamp = options.afterTimestamp ?? null; - this.beforeTimestamp = options.beforeTimestamp ?? null; } /** @@ -78,12 +34,12 @@ export class TweetsCountBook extends BaseBook { * @returns */ async fetchNextPage(): Promise> { - if (!this.#hasMadeInitialRequest) { - this.#hasMadeInitialRequest = true; + if (!this._hasMadeInitialRequest) { + this._hasMadeInitialRequest = true; return this.#fetchPages(); } - if (!this.#nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); - return this.#fetchPages(this.#nextToken); + if (!this._nextToken) throw new CustomError('PAGINATED_RESPONSE_TAIL_REACHED'); + return this.#fetchPages(this._nextToken); } // #### 🚧 PRIVATE METHODS 🚧 #### @@ -97,11 +53,11 @@ export class TweetsCountBook extends BaseBook { if (this.granularity) query.granularity = this.granularity; if (this.afterTweetId) query.since_id = this.afterTweetId; if (this.beforeTweetId) query.until_id = this.beforeTweetId; - if (this.afterTimestamp) query.start_time = new Date(this.afterTimestamp).toISOString(); - if (this.beforeTimestamp) query.end_time = new Date(this.beforeTimestamp).toISOString(); + if (this.startTimestamp) query.start_time = new Date(this.startTimestamp).toISOString(); + if (this.endTimestamp) query.end_time = new Date(this.endTimestamp).toISOString(); const requestData = new RequestData({ query }); const data: GETTweetsCountsRecentResponse = await this.client._api.tweets.counts.recent.get(requestData); - this.#nextToken = data.meta.next_token; + this._nextToken = data.meta.next_token; this.hasMore = data.meta.next_token ? true : false; if (data.meta.total_tweet_count === 0) return tweetCountBuckets; const rawBuckets = data.data; diff --git a/src/books/index.ts b/src/books/index.ts index 6bd4c89..1af443e 100644 --- a/src/books/index.ts +++ b/src/books/index.ts @@ -1,10 +1,18 @@ export * from './BaseBook'; +export * from './BaseRangeBook'; export * from './BlocksBook'; export * from './ComposedTweetsBook'; +export * from './FollowedListsBook'; export * from './FollowersBook'; export * from './FollowingsBook'; export * from './LikedTweetsBook'; +export * from './ListFollowersBook'; +export * from './ListMembersBook'; +export * from './ListMembershipsBook'; +export * from './ListTweetsBook'; export * from './MentionsBook'; export * from './MutesBook'; export * from './SearchTweetsBook'; export * from './TweetsCountBook'; +export * from './OwnedListsBook'; +export * from './PinnedListsBook'; diff --git a/src/managers/TweetManager.ts b/src/managers/TweetManager.ts index 7f0482e..f58e437 100644 --- a/src/managers/TweetManager.ts +++ b/src/managers/TweetManager.ts @@ -1,6 +1,5 @@ import { Collection } from '../util'; import { BaseManager } from './BaseManager'; -import { SearchTweetsBook, TweetsCountBook } from '../books'; import { RemovedRetweetResponse, RequestData, @@ -11,7 +10,6 @@ import { SimplifiedTweet, User, Tweet, - TweetCountBucket, TweetPayload, } from '../structures'; import { CustomError, CustomTypeError } from '../errors'; @@ -21,10 +19,6 @@ import type { TweetResolvable, FetchTweetOptions, FetchTweetsOptions, - SearchTweetsOptions, - SearchTweetsBookOptions, - TweetsCountBookOptions, - CountTweetsOptions, TweetCreateOptions, } from '../typings'; import type { @@ -252,73 +246,6 @@ export class TweetManager extends BaseManager return likedByUsersCollection; } - /** - * Fetches tweets using a search query. - * @param query The query to match tweets with - * @param options The options for searching tweets - * @returns A tuple containing {@link SearchTweetsBook} object and a {@link Collection} of {@link Tweet} objects representing the first page - */ - async search( - query: string, - options?: SearchTweetsOptions, - ): Promise<[SearchTweetsBook, Collection]> { - const bookData: SearchTweetsBookOptions = { query }; - if (options?.afterTweet) { - const afterTweetId = this.client.tweets.resolveId(options.afterTweet); - if (afterTweetId) bookData.afterTweetId = afterTweetId; - } - if (options?.beforeTweet) { - const beforeTweetId = this.client.tweets.resolveId(options.beforeTweet); - if (beforeTweetId) bookData.beforeTweetId = beforeTweetId; - } - if (options?.afterTime) { - const afterTimestamp = new Date(options.afterTime).getTime(); - if (afterTimestamp) bookData.afterTimestamp = afterTimestamp; - } - if (options?.beforeTime) { - const beforeTimestamp = new Date(options.beforeTime).getTime(); - if (beforeTimestamp) bookData.beforeTimestamp = beforeTimestamp; - } - if (options?.maxResultsPerPage) { - bookData.maxResultsPerPage = options.maxResultsPerPage; - } - const searchTweetsBook = new SearchTweetsBook(this.client, bookData); - const firstPage = await searchTweetsBook.fetchNextPage(); - return [searchTweetsBook, firstPage]; - } - - /** - * Fetches count of tweets matching a search query. - * @param query The query to match the tweets with - * @param options The options for searching tweets - * @returns A tuple containing {@link TweetsCountBook} object and an array of {@link TweetCountBucket} objects representing the first page - */ - async count(query: string, options?: CountTweetsOptions): Promise<[TweetsCountBook, Array]> { - const bookData: TweetsCountBookOptions = { query }; - if (options?.afterTweet) { - const afterTweetId = this.client.tweets.resolveId(options.afterTweet); - if (afterTweetId) bookData.afterTweetId = afterTweetId; - } - if (options?.beforeTweet) { - const beforeTweetId = this.client.tweets.resolveId(options.beforeTweet); - if (beforeTweetId) bookData.beforeTweetId = beforeTweetId; - } - if (options?.afterTime) { - const afterTimestamp = new Date(options.afterTime).getTime(); - if (afterTimestamp) bookData.afterTimestamp = afterTimestamp; - } - if (options?.beforeTime) { - const beforeTimestamp = new Date(options.beforeTime).getTime(); - if (beforeTimestamp) bookData.beforeTimestamp = beforeTimestamp; - } - if (options?.granularity) { - bookData.granularity = options.granularity; - } - const tweetsCountBook = new TweetsCountBook(this.client, bookData); - const firstPage = await tweetsCountBook.fetchNextPage(); - return [tweetsCountBook, firstPage]; - } - /** * Creates a new tweet. * @param options The options for creating the tweet diff --git a/src/managers/UserManager.ts b/src/managers/UserManager.ts index c72ac3e..d68d725 100644 --- a/src/managers/UserManager.ts +++ b/src/managers/UserManager.ts @@ -1,7 +1,6 @@ import { Collection } from '../util'; import { BaseManager } from './BaseManager'; import { CustomError, CustomTypeError } from '../errors'; -import { MentionsBook, FollowersBook, FollowingsBook, LikedTweetsBook, ComposedTweetsBook } from '../books'; import { RequestData, UserBlockResponse, @@ -24,10 +23,6 @@ import type { FetchUserOptions, FetchUsersByUsernamesOptions, FetchUsersOptions, - FetchComposedTweetsOptions, - ComposedTweetsBookOptions, - FetchMentionsOptions, - MentionsBookOptions, } from '../typings'; import type { DELETEUsersSourceUserIdBlockingTargetUserIdResponse, @@ -251,134 +246,6 @@ export class UserManager extends BaseManager { return new UserUnmuteResponse(data); } - /** - * Fetches followers of a given user. - * @param targetUser The user whose followers are to be fetched - * @param maxResultsPerPage The maximum amount of users to fetch per page. The API will default this to `100` if not provided - * @returns A tuple containing {@link FollowersBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchFollowers( - targetUser: UserResolvable, - maxResultsPerPage?: number, - ): Promise<[FollowersBook, Collection]> { - const userId = this.resolveId(targetUser); - if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create followers book for'); - const followersBook = new FollowersBook(this.client, { userId, maxResultsPerPage }); - const firstPage = await followersBook.fetchNextPage(); - return [followersBook, firstPage]; - } - - /** - * Fetches users followed by a given user. - * @param targetUser The user whose followings are to be fetched - * @param maxResultsPerPage The maximum amount of users to fetch per page. The API will default this to `100` if not provided - * @returns A tuple containing {@link FollowingsBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchFollowings( - targetUser: UserResolvable, - maxResultsPerPage?: number, - ): Promise<[FollowingsBook, Collection]> { - const userId = this.resolveId(targetUser); - if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create following book for'); - const followingBook = new FollowingsBook(this.client, { userId, maxResultsPerPage }); - const firstPage = await followingBook.fetchNextPage(); - return [followingBook, firstPage]; - } - - /** - * Fetches tweets liked by a given user. - * @param targetUser The user whose liked tweet are to be fetched - * @param maxResultsPerPage The maximum amount of tweets to fetch per page - * @returns A tuple containing {@link LikedTweetsBook} object and a {@link Collection} of {@link Tweet} objects representing the first page - */ - async fetchLikedTweets( - targetUser: UserResolvable, - maxResultsPerPage?: number, - ): Promise<[LikedTweetsBook, Collection]> { - const userId = this.resolveId(targetUser); - if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create liked book for'); - const likedTweetBook = new LikedTweetsBook(this.client, { userId, maxResultsPerPage }); - const firstPage = await likedTweetBook.fetchNextPage(); - return [likedTweetBook, firstPage]; - } - - /** - * Fetches tweets composed by a twitter user. - * @param targetUser The user whose tweets are to be fetched - * @param options The options for fetching tweets - * @returns A tuple containing {@link ComposedTweetsBook} object and a {@link Collection} of {@link Tweet} objects representing the first page - */ - async fetchComposedTweets( - targetUser: UserResolvable, - options?: FetchComposedTweetsOptions, - ): Promise<[ComposedTweetsBook, Collection]> { - const userId = this.resolveId(targetUser); - if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create tweets book for'); - const bookData: ComposedTweetsBookOptions = { userId }; - if (options?.afterTweet) { - const afterTweetId = this.client.tweets.resolveId(options.afterTweet); - if (afterTweetId) bookData.afterTweetId = afterTweetId; - } - if (options?.beforeTweet) { - const beforeTweetId = this.client.tweets.resolveId(options.beforeTweet); - if (beforeTweetId) bookData.beforeTweetId = beforeTweetId; - } - if (options?.afterTime) { - const afterTimestamp = new Date(options.afterTime).getTime(); - if (afterTimestamp) bookData.afterTimestamp = afterTimestamp; - } - if (options?.beforeTime) { - const beforeTimestamp = new Date(options.beforeTime).getTime(); - if (beforeTimestamp) bookData.beforeTimestamp = beforeTimestamp; - } - if (options?.exclude) { - bookData.exclude = options.exclude; - } - if (options?.maxResultsPerPage) { - bookData.maxResultsPerPage = options.maxResultsPerPage; - } - const composedTweetsBook = new ComposedTweetsBook(this.client, bookData); - const firstPage = await composedTweetsBook.fetchNextPage(); - return [composedTweetsBook, firstPage]; - } - - /** - * Fetches tweets that mention a given user. - * @param targetUser The mentioned user - * @param options The options for fetching tweets - * @returns A tuple containing {@link MentionsBook} object and a {@link Collection} of {@link Tweet} objects representing the first page - */ - async fetchMentions( - targetUser: UserResolvable, - options?: FetchMentionsOptions, - ): Promise<[MentionsBook, Collection]> { - const userId = this.resolveId(targetUser); - if (!userId) throw new CustomError('USER_RESOLVE_ID', 'create mentions book for'); - const bookData: MentionsBookOptions = { userId }; - if (options?.afterTweet) { - const afterTweetId = this.client.tweets.resolveId(options.afterTweet); - if (afterTweetId) bookData.afterTweetId = afterTweetId; - } - if (options?.beforeTweet) { - const beforeTweetId = this.client.tweets.resolveId(options.beforeTweet); - if (beforeTweetId) bookData.beforeTweetId = beforeTweetId; - } - if (options?.afterTime) { - const afterTimestamp = new Date(options.afterTime).getTime(); - if (afterTimestamp) bookData.afterTimestamp = afterTimestamp; - } - if (options?.beforeTime) { - const beforeTimestamp = new Date(options.beforeTime).getTime(); - if (beforeTimestamp) bookData.beforeTimestamp = beforeTimestamp; - } - if (options?.maxResultsPerPage) { - bookData.maxResultsPerPage = options.maxResultsPerPage; - } - const mentionsBook = new MentionsBook(this.client, bookData); - const firstPage = await mentionsBook.fetchNextPage(); - return [mentionsBook, firstPage]; - } - // #### 🚧 PRIVATE METHODS 🚧 #### async #fetchSingleUser(userId: Snowflake, options: FetchUserOptions): Promise { diff --git a/src/structures/ClientUser.ts b/src/structures/ClientUser.ts index c15d48e..fd5e8c3 100644 --- a/src/structures/ClientUser.ts +++ b/src/structures/ClientUser.ts @@ -1,34 +1,9 @@ import { User } from './User'; -import { BlocksBook, MutesBook } from '../books'; import type { Client } from '../client'; -import type { Collection } from '../util'; -import type { FetchBlocksOptions, FetchMutesOptions } from '../typings'; -import type { SingleUserLookupResponse, Snowflake } from 'twitter-types'; +import type { SingleUserLookupResponse } from 'twitter-types'; export class ClientUser extends User { constructor(client: Client, data: SingleUserLookupResponse) { super(client, data); } - - /** - * Fetches users blocked by the authorized user. - * @param options The options for fetching blocked users - * @returns A tuple containing {@link BlocksBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchBlocks(options?: FetchBlocksOptions): Promise<[BlocksBook, Collection]> { - const blocksBook = new BlocksBook(this.client, { userId: this.id, maxResultsPerPage: options?.maxResultsPerPage }); - const firstPage = await blocksBook.fetchNextPage(); - return [blocksBook, firstPage]; - } - - /** - * Fetches users muted by the authorized user. - * @param options The options for fetching muted users - * @returns A tuple containing {@link MutesBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchMutes(options?: FetchMutesOptions): Promise<[MutesBook, Collection]> { - const mutesBook = new MutesBook(this.client, { userId: this.id, maxResultsPerPage: options?.maxResultsPerPage }); - const firstPage = await mutesBook.fetchNextPage(); - return [mutesBook, firstPage]; - } } diff --git a/src/structures/SimplifiedUser.ts b/src/structures/SimplifiedUser.ts index 5503ba4..575ae24 100644 --- a/src/structures/SimplifiedUser.ts +++ b/src/structures/SimplifiedUser.ts @@ -1,9 +1,6 @@ import { BaseStructure } from './BaseStructure'; import { UserPublicMetrics, UserEntities, UserWitheld } from './misc'; -import { FollowersBook, FollowingsBook } from '../books'; -import type { User } from './User'; import type { Client } from '../client'; -import type { Collection } from '../util'; import type { APIUser, Snowflake } from 'twitter-types'; import type { UserFollowResponse, @@ -153,22 +150,4 @@ export class SimplifiedUser extends BaseStructure { async unmute(): Promise { return this.client.users.unmute(this.id); } - - /** - * Fetches followers of this user. - * @param maxResultsPerPage The maximum amount of users to fetch per page of the book. The API will default this to `100` if not provided - * @returns A tuple containing {@link FollowersBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchFollowers(maxResultsPerPage?: number): Promise<[FollowersBook, Collection]> { - return this.client.users.fetchFollowers(this.id, maxResultsPerPage); - } - - /** - * Fetches users followed by this user. - * @param maxResultsPerPage The maximum amount of users to fetch per page of the book. The API will default this to `100` if not provided - * @returns A tuple containing {@link FollowingsBook} object and a {@link Collection} of {@link User} objects representing the first page - */ - async fetchFollowings(maxResultsPerPage?: number): Promise<[FollowingsBook, Collection]> { - return this.client.users.fetchFollowings(this.id, maxResultsPerPage); - } } diff --git a/src/typings/Interfaces.ts b/src/typings/Interfaces.ts index da26033..8093bee 100644 --- a/src/typings/Interfaces.ts +++ b/src/typings/Interfaces.ts @@ -275,278 +275,105 @@ export interface RequestDataOptions { isUserContext?: boolean; } -/** - * The options used to fetch users blocked by the authorized user - */ -export interface FetchBlocksOptions { +export interface BaseBookOptions { /** - * The maximum number of users to fetch per page - */ - maxResultsPerPage?: number; -} - -/** - * The options used to fetch users muted by the authorized user - */ -export interface FetchMutesOptions { - /** - * The maximum number of users to fetch per page + * The maximum number of results to fetch per page */ maxResultsPerPage?: number; } -export interface BookOptions { - /** - * The ID of the user to create the book for - */ - userId: Snowflake; - - /** - * The maximum number of results to fetch per page - */ - maxResultsPerPage?: number; +export interface BlocksBookOptions extends BaseBookOptions { + user: UserResolvable; } -/** - * The options used to fetch tweets composed by a twitter user - */ -export interface FetchComposedTweetsOptions { +export interface BaseRangeBookOptions extends BaseBookOptions { /** - * Fetch tweets that were created after this point in time + * Only return tweets that were created at or after this time */ - afterTime?: Date | number; + startTime?: number | Date; /** - * Fetch tweets that were created before this point in time + * Only return tweets that were created at or before this time */ - beforeTime?: Date | number; + endTime?: number | Date; /** - * Fetch tweets that were created after this tweet + * Only return tweets that were created after this tweet */ afterTweet?: TweetResolvable; /** - * Fetch tweets that were created before this tweet + * Only return tweets that were created before this tweet */ beforeTweet?: TweetResolvable; - - /** - * The types of tweet to exclude from fetching - */ - exclude?: GETUsersIdTweetsQuery['exclude']; - - /** - * The maximum number of tweets to fetch per page - */ - maxResultsPerPage?: number; } -/** - * The options used to create a {@link ComposedTweetsBook} object for a user - */ -export interface ComposedTweetsBookOptions extends BookOptions { - /** - * Return tweets that were created after this timestamp - */ - afterTimestamp?: number; - - /** - * Return tweets that were created before this timestamp - */ - beforeTimestamp?: number; +export interface ComposedTweetsBookOptions extends BaseRangeBookOptions { + user: UserResolvable; /** * The types of tweets to exclude */ exclude?: GETUsersIdTweetsQuery['exclude']; - - /** - * Return tweets that were created after this tweet ID - */ - afterTweetId?: Snowflake; - - /** - * Return tweets that were created before this tweet ID - */ - beforeTweetId?: Snowflake; } -/** - * The options used to create a {@link MentionsBook} object - */ -export interface MentionsBookOptions extends BookOptions { - /** - * Return tweets that were created after this timestamp - */ - afterTimestamp?: number; - - /** - * Return tweets that were created before this timestamp - */ - beforeTimestamp?: number; - - /** - * Return tweets that were created after this tweet ID - */ - afterTweetId?: Snowflake; - - /** - * Return tweets that were created before this tweet ID - */ - beforeTweetId?: Snowflake; +export interface FollowedListsBookOptions extends BaseBookOptions { + user: UserResolvable; } -/** - * The options used to fetch tweets that mention a given user - */ -export interface FetchMentionsOptions { - /** - * Fetch tweets that were created after this point in time - */ - afterTime?: Date | number; - - /** - * Fetch tweets that were created before this point in time - */ - beforeTime?: Date | number; - - /** - * Fetch tweets that were created after this tweet - */ - afterTweet?: TweetResolvable; - - /** - * Fetch tweets that were created before this tweet - */ - beforeTweet?: TweetResolvable; - - /** - * The maximum number of tweets to fetch per page - */ - maxResultsPerPage?: number; +export interface FollowersBookOptions extends BaseBookOptions { + user: UserResolvable; } -/** - * The options used to create a {@link SearchTweetsBook} object - */ -export interface SearchTweetsBookOptions { - query: string; - - /** - * Return tweets that were created after this timestamp - */ - afterTimestamp?: number; +export interface FollowingsBookOptions extends BaseBookOptions { + user: UserResolvable; +} - /** - * Return tweets that were created before this timestamp - */ - beforeTimestamp?: number; +export interface LikedTweetsBookOptions extends BaseBookOptions { + user: UserResolvable; +} - /** - * Return tweets that were created after this tweet ID - */ - afterTweetId?: Snowflake; +export interface ListFollowersBookOptions extends BaseBookOptions { + list: ListResolvable; +} - /** - * Return tweets that were created before this tweet ID - */ - beforeTweetId?: Snowflake; +export interface ListMembersBookOptions extends BaseBookOptions { + list: ListResolvable; +} - /** - * The maximum number of results to fetch per page - */ - maxResultsPerPage?: number; +export interface ListMembershipsBookOptions extends BaseBookOptions { + user: UserResolvable; } -/** - * The options used to fetch tweets using query - */ -export interface SearchTweetsOptions { - /** - * Fetch tweets that were created after this point in time - */ - afterTime?: Date | number; +export interface ListTweetsBookOptions extends BaseBookOptions { + list: ListResolvable; +} - /** - * Fetch tweets that were created before this point in time - */ - beforeTime?: Date | number; +export interface MentionsBookOptions extends BaseRangeBookOptions { + user: UserResolvable; +} - /** - * Fetch tweets that were created after this tweet - */ - afterTweet?: TweetResolvable; +export interface MutesBookOptions extends BaseBookOptions { + user: UserResolvable; +} - /** - * Fetch tweets that were created before this tweet - */ - beforeTweet?: TweetResolvable; +export interface OwnedListsBookOptions extends BaseBookOptions { + user: UserResolvable; +} - /** - * The maximum number of tweets to fetch per page - */ - maxResultsPerPage?: number; +export interface PinnedListsBookOptions extends BaseBookOptions { + user: UserResolvable; } -/** - * The options used to create a {@link TweetsCountBook} object - */ -export interface TweetsCountBookOptions { - /** - * The query for matching tweets - */ +export interface SearchTweetsBookOptions extends BaseRangeBookOptions { query: string; - - /** - * Match tweets that were created after this timestamp - */ - afterTimestamp?: number; - - /** - * Match tweets that were created before this timestamp - */ - beforeTimestamp?: number; - - /** - * The granularity of the {@link TweetCountBucket} - */ - granularity?: GETTweetsCountsRecentQuery['granularity']; - - /** - * Match tweets that were created after this tweet ID - */ - afterTweetId?: Snowflake; - - /** - * Match tweets that weere created before this tweet ID - */ - beforeTweetId?: Snowflake; } -/** - * The options for fetching tweets count matching a query - */ -export interface CountTweetsOptions { - /** - * Match tweets that were created after this point in time - */ - afterTime?: Date | number; - +export interface TweetsCountBookOptions extends BaseRangeBookOptions { /** - * Match tweets that were created before this point in time - */ - beforeTime?: Date | number; - - /** - * Match tweets that were created after this tweet - */ - afterTweet?: TweetResolvable; - - /** - * Match tweets that were created before this tweet + * The query for matching tweets */ - beforeTweet?: TweetResolvable; + query: string; /** * The granularity of the {@link TweetCountBucket} diff --git a/src/typings/Types.ts b/src/typings/Types.ts index d3ec748..a680524 100644 --- a/src/typings/Types.ts +++ b/src/typings/Types.ts @@ -22,7 +22,6 @@ import type { FetchUsersOptions, FetchSpaceOptions, FetchSpacesOptions, - BookOptions, } from './Interfaces'; export type ClientEventArgsType = K extends keyof ClientEventsMapping @@ -66,31 +65,6 @@ export type SpaceManagerFetchResult = T extends undefined | FetchFilteredStreamRulesOptions diff --git a/src/util/Constants.ts b/src/util/Constants.ts index 3f5b0ef..0a91dbe 100644 --- a/src/util/Constants.ts +++ b/src/util/Constants.ts @@ -38,7 +38,10 @@ export const defaultClientOptions: ClientOptions = { */ queryParameters: { userFields: APIUserFieldsParameters, - tweetFields: APITweetFieldsParameters, + // TODO: remove this once twitter fixes the bug where unauthorized request of these fields result in an error instead of a partial error + tweetFields: APITweetFieldsParameters.filter( + f => !['promoted_metrics', 'organic_metrics', 'non_public_metrics'].includes(f), + ), spaceFields: APISpaceFieldsParameters, mediaFields: APIMediaFieldsParameters, placeFields: APIPlaceFieldsParameters, diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json deleted file mode 100644 index 6c3026c..0000000 --- a/tsconfig-cjs.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist/cjs" - } -} diff --git a/tsconfig.json b/tsconfig.json index 82aaa0e..ea661b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "target": "ES2020", "module": "ES2020", "declaration": true, - "outDir": "dist/esm", + "outDir": "dist", "strict": true, "moduleResolution": "Node", "allowSyntheticDefaultImports": true,