diff --git a/DatabaseSchema.md b/DatabaseSchema.md index 3d792d2..abb0539 100644 --- a/DatabaseSchema.md +++ b/DatabaseSchema.md @@ -1,15 +1,15 @@ # SponsorTimesDB -[vipUsers](###vipUsers) -[sponsorTimes](###sponsorTimes) -[userNames](###userNames) -[userNameLogs](###userNameLogs) -[categoryVotes](###categoryVotes) -[lockCategories](###lockCategories) -[warnings](###warnings) -[shadowBannedUsers](###shadowBannedUsers) -[unlistedVideos](###unlistedVideos) -[config](###config) +[vipUsers](#vipUsers) +[sponsorTimes](#sponsorTimes) +[userNames](#userNames) +[userNameLogs](#userNameLogs) +[categoryVotes](#categoryVotes) +[lockCategories](#lockCategories) +[warnings](#warnings) +[shadowBannedUsers](#shadowBannedUsers) +[unlistedVideos](#unlistedVideos) +[config](#config) ### vipUsers | Name | Type | | @@ -142,10 +142,10 @@ # Private -[vote](###vote) -[categoryVotes](###categoryVotes) -[sponsorTimes](###sponsorTimes) -[config](###config) +[vote](#vote) +[categoryVotes](#categoryVotes) +[sponsorTimes](#sponsorTimes) +[config](#config) ### vote diff --git a/package-lock.json b/package-lock.json index ae13a93..2a1e3db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "express-promise-router": "^4.1.0", "express-rate-limit": "^5.1.3", "http": "0.0.0", - "iso8601-duration": "^1.2.0", "node-fetch": "^2.6.0", "pg": "^8.5.1", "redis": "^3.1.1", @@ -159,9 +158,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -221,9 +220,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -541,9 +540,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -614,24 +613,6 @@ "eslint": "*" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, "node_modules/@typescript-eslint/parser": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz", @@ -660,9 +641,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -740,9 +721,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1861,27 +1842,21 @@ } }, "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" + }, + "peerDependencies": { + "eslint": ">=5" } }, "node_modules/eslint-visitor-keys": { @@ -1926,9 +1901,9 @@ } }, "node_modules/eslint/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1954,6 +1929,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -2314,9 +2313,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", + "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3018,11 +3017,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "node_modules/iso8601-duration": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/iso8601-duration/-/iso8601-duration-1.2.0.tgz", - "integrity": "sha512-ErTBd++b17E8nmWII1K1uZtBgD1E8RjyvwmxlCjPHNqHMD7gmcMHOw0E8Ro/6+QT4PhHRSnnMo7bxa1vFPkwhg==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4827,9 +4821,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz", + "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -5666,9 +5660,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -5710,9 +5704,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -5998,9 +5992,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -6044,17 +6038,6 @@ "@typescript-eslint/typescript-estree": "4.28.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - } } }, "@typescript-eslint/parser": { @@ -6070,9 +6053,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -6118,9 +6101,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -7026,9 +7009,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -7040,6 +7023,23 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -7132,20 +7132,12 @@ } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^2.0.0" } }, "eslint-visitor-keys": { @@ -7346,9 +7338,9 @@ "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", + "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -7876,11 +7868,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "iso8601-duration": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/iso8601-duration/-/iso8601-duration-1.2.0.tgz", - "integrity": "sha512-ErTBd++b17E8nmWII1K1uZtBgD1E8RjyvwmxlCjPHNqHMD7gmcMHOw0E8Ro/6+QT4PhHRSnnMo7bxa1vFPkwhg==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9280,9 +9267,9 @@ }, "dependencies": { "ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz", + "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", diff --git a/package.json b/package.json index cc9a2e0..06381b4 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "express-promise-router": "^4.1.0", "express-rate-limit": "^5.1.3", "http": "0.0.0", - "iso8601-duration": "^1.2.0", "node-fetch": "^2.6.0", "pg": "^8.5.1", "redis": "^3.1.1", diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index 368781d..d4ae698 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -3,7 +3,7 @@ import { config } from '../config'; import { db, privateDB } from '../databases/databases'; import { skipSegmentsHashKey, skipSegmentsKey } from '../utils/redisKeys'; import { SBRecord } from '../types/lib.model'; -import { Category, CategoryActionType, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; +import { Category, CategoryActionType, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, SegmentUUID, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; import { getCategoryActionType } from '../utils/categoryInfo'; import { getHash } from '../utils/getHash'; import { getIP } from '../utils/getIP'; @@ -14,7 +14,7 @@ import { getReputation } from '../utils/reputation'; async function prepareCategorySegments(req: Request, videoID: VideoID, category: Category, segments: DBSegment[], cache: SegmentCache = {shadowHiddenSegmentIPs: {}}): Promise { const shouldFilter: boolean[] = await Promise.all(segments.map(async (segment) => { - if (segment.votes < -1) { + if (segment.votes < -1 && !segment.required) { return false; //too untrustworthy, just ignore it } @@ -50,7 +50,7 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category: })); } -async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: Category[], service: Service): Promise { +async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: Category[], requiredSegments: SegmentUUID[], service: Service): Promise { const cache: SegmentCache = {shadowHiddenSegmentIPs: {}}; const segments: Segment[] = []; @@ -61,6 +61,8 @@ async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: const segmentsByCategory: SBRecord = (await getSegmentsFromDBByVideoID(videoID, service)) .filter((segment: DBSegment) => categories.includes(segment?.category)) .reduce((acc: SBRecord, segment: DBSegment) => { + if (requiredSegments.includes(segment.UUID)) segment.required = true; + acc[segment.category] = acc[segment.category] || []; acc[segment.category].push(segment); @@ -80,7 +82,7 @@ async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: } } -async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, categories: Category[], service: Service): Promise> { +async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, categories: Category[], requiredSegments: SegmentUUID[], service: Service): Promise> { const cache: SegmentCache = {shadowHiddenSegmentIPs: {}}; const segments: SBRecord = {}; @@ -97,8 +99,9 @@ async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, hash: segment.hashedVideoID, segmentPerCategory: {}, }; - const videoCategories = acc[segment.videoID].segmentPerCategory; + if (requiredSegments.includes(segment.UUID)) segment.required = true; + const videoCategories = acc[segment.videoID].segmentPerCategory; videoCategories[segment.category] = videoCategories[segment.category] || []; videoCategories[segment.category].push(segment); @@ -214,7 +217,7 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise cursor) { - currentGroup = {segments: [], votes: 0, reputation: 0, locked: false}; + currentGroup = {segments: [], votes: 0, reputation: 0, locked: false, required: false}; overlappingSegmentsGroups.push(currentGroup); } @@ -233,11 +236,18 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise { - if (group.locked) { + if (group.required) { + // Required beats locked + group.segments = group.segments.filter((segment) => segment.required); + } else if (group.locked) { group.segments = group.segments.filter((segment) => segment.locked); } @@ -277,12 +287,24 @@ async function handleGetSegments(req: Request, res: Response): Promise val == service)) { service = Service.YouTube; } - const segments = await getSegmentsByVideoID(req, videoID, categories, service); + const segments = await getSegmentsByVideoID(req, videoID, categories, requiredSegments, service); if (segments === null || segments === undefined) { res.sendStatus(500); diff --git a/src/routes/getSkipSegmentsByHash.ts b/src/routes/getSkipSegmentsByHash.ts index 29194d4..318ee24 100644 --- a/src/routes/getSkipSegmentsByHash.ts +++ b/src/routes/getSkipSegmentsByHash.ts @@ -1,7 +1,7 @@ import {hashPrefixTester} from '../utils/hashPrefixTester'; import {getSegmentsByHash} from './getSkipSegments'; import {Request, Response} from 'express'; -import { Category, Service, VideoIDHash } from '../types/segments.model'; +import { Category, SegmentUUID, Service, VideoIDHash } from '../types/segments.model'; export async function getSkipSegmentsByHash(req: Request, res: Response) { let hashPrefix = req.params.prefix as VideoIDHash; @@ -16,16 +16,33 @@ export async function getSkipSegmentsByHash(req: Request, res: Response) { categories = req.query.categories ? JSON.parse(req.query.categories as string) : req.query.category - ? [req.query.category] - : ["sponsor"]; + ? Array.isArray(req.query.category) + ? req.query.category + : [req.query.category] + : ['sponsor']; if (!Array.isArray(categories)) { return res.status(400).send("Categories parameter does not match format requirements."); } - } - catch(error) { + } catch(error) { return res.status(400).send("Bad parameter: categories (invalid JSON)"); } + let requiredSegments: SegmentUUID[] = []; + try { + requiredSegments = req.query.requiredSegments + ? JSON.parse(req.query.requiredSegments as string) + : req.query.requiredSegment + ? Array.isArray(req.query.requiredSegment) + ? req.query.requiredSegment + : [req.query.requiredSegment] + : []; + if (!Array.isArray(requiredSegments)) { + return res.status(400).send("requiredSegments parameter does not match format requirements."); + } + } catch(error) { + return res.status(400).send("Bad parameter: requiredSegments (invalid JSON)"); + } + let service: Service = req.query.service ?? req.body.service ?? Service.YouTube; if (!Object.values(Service).some((val) => val == service)) { service = Service.YouTube; @@ -35,7 +52,7 @@ export async function getSkipSegmentsByHash(req: Request, res: Response) { categories = categories.filter((item: any) => typeof item === "string"); // Get all video id's that match hash prefix - const segments = await getSegmentsByHash(req, hashPrefix, categories, service); + const segments = await getSegmentsByHash(req, hashPrefix, categories, requiredSegments, service); if (!segments) return res.status(404).json([]); diff --git a/src/routes/postSkipSegments.ts b/src/routes/postSkipSegments.ts index fffb51a..d9b8869 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -4,7 +4,6 @@ import {db, privateDB} from '../databases/databases'; import {getMaxResThumbnail, YouTubeAPI} from '../utils/youtubeApi'; import {getSubmissionUUID} from '../utils/getSubmissionUUID'; import fetch from 'node-fetch'; -import isoDurations, { end } from 'iso8601-duration'; import {getHash} from '../utils/getHash'; import {getIP} from '../utils/getIP'; import {getFormattedTime} from '../utils/getFormattedTime'; @@ -272,6 +271,34 @@ async function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promi } } +async function checkUserActiveWarning(userID: string): Promise<{ pass: boolean; errorMessage: string; }> { + const MILLISECONDS_IN_HOUR = 3600000; + const now = Date.now(); + const warnings = await db.prepare('all', + `SELECT "reason" + FROM warnings + WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1 + ORDER BY "issueTime" DESC + LIMIT ?`, + [ + userID, + Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR)), + config.maxNumberOfActiveWarnings + ], + ) as {reason: string}[] + + if (warnings?.length >= config.maxNumberOfActiveWarnings) { + const defaultMessage = 'Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?'; + + return { + pass: false, + errorMessage: warnings[0]?.reason?.length > 0 ? warnings[0].reason : defaultMessage + }; + } + + return {pass: true, errorMessage: ''}; +} + function proxySubmission(req: Request) { fetch(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, { method: 'POST', @@ -319,26 +346,18 @@ export async function postSkipSegments(req: Request, res: Response) { } if (invalidFields.length !== 0) { - // invalid request - const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, ''); - res.status(400).send(`No valid ${fields} field(s) provided`); - return; + // invalid request + const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, ''); + res.status(400).send(`No valid ${fields} field(s) provided`); + return; } //hash the userID userID = getHash(userID); - //hash the ip 5000 times so no one can get it from the database - const hashedIP = getHash(getIP(req) + config.globalSalt); - - const MILLISECONDS_IN_HOUR = 3600000; - const now = Date.now(); - const warningsCount = (await db.prepare('get', `SELECT count(*) as count FROM warnings WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1`, - [userID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))], - )).count; - - if (warningsCount >= config.maxNumberOfActiveWarnings) { - return res.status(403).send('Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?'); + const warningResult: {pass: boolean, errorMessage: string} = await checkUserActiveWarning(userID); + if (!warningResult.pass) { + return res.status(403).send(warningResult.errorMessage); } let lockedCategoryList = (await db.prepare('all', 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => { @@ -350,9 +369,15 @@ export async function postSkipSegments(req: Request, res: Response) { const decreaseVotes = 0; - const previousSubmissions = await db.prepare('all', `SELECT "videoDuration", "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "service" = ? AND "hidden" = 0 - AND "shadowHidden" = 0 AND "votes" >= 0 AND "videoDuration" != 0`, [videoID, service]) as - {videoDuration: VideoDuration, UUID: SegmentUUID}[]; + const previousSubmissions = await db.prepare('all', + `SELECT "videoDuration", "UUID" + FROM "sponsorTimes" + WHERE "videoID" = ? AND "service" = ? AND + "hidden" = 0 AND "shadowHidden" = 0 AND + "votes" >= 0 AND "videoDuration" != 0`, + [videoID, service] + ) as {videoDuration: VideoDuration, UUID: SegmentUUID}[]; + // If the video's duration is changed, then the video should be unlocked and old submissions should be hidden const videoDurationChanged = (videoDuration: number) => videoDuration != 0 && previousSubmissions.length > 0 && !previousSubmissions.some((e) => Math.abs(videoDuration - e.videoDuration) < 2); @@ -452,6 +477,9 @@ export async function postSkipSegments(req: Request, res: Response) { const UUIDs = []; const newSegments = []; + //hash the ip 5000 times so no one can get it from the database + const hashedIP = getHash(getIP(req) + config.globalSalt); + try { //get current time const timeSubmitted = Date.now(); diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index 945f294..2fe358c 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -46,6 +46,7 @@ export interface DBSegment { userID: UserID; votes: number; locked: boolean; + required: boolean; // Requested specifically from the client shadowHidden: Visibility; videoID: VideoID; videoDuration: VideoDuration; @@ -57,6 +58,7 @@ export interface OverlappingSegmentGroup { segments: DBSegment[], votes: number; locked: boolean; // Contains a locked segment + required: boolean; // Requested specifically from the client reputation: number; } diff --git a/test/cases/getSkipSegments.ts b/test/cases/getSkipSegments.ts index 7ec0798..d8c951e 100644 --- a/test/cases/getSkipSegments.ts +++ b/test/cases/getSkipSegments.ts @@ -17,6 +17,10 @@ describe('getSkipSegments', () => { await db.prepare("run", query, ['locked', 20, 33, 2, 1, '1-uuid-locked-8', 'testman', 0, 50, 'intro', 'YouTube', 230, 0, 0, getHash('locked', 1)]); await db.prepare("run", query, ['locked', 20, 34, 100000, 0, '1-uuid-9', 'testman', 0, 50, 'intro', 'YouTube', 190, 0, 0, getHash('locked', 1)]); await db.prepare("run", query, ['onlyHiddenSegments', 20, 34, 100000, 0, 'onlyHiddenSegments', 'testman', 0, 50, 'sponsor', 'YouTube', 190, 1, 0, getHash('onlyHiddenSegments', 1)]); + await db.prepare("run", query, ['requiredSegmentVid-raw', 60, 70, 2, 0, 'requiredSegmentVid-raw-1', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 0, getHash('requiredSegmentVid-raw', 1)]); + await db.prepare("run", query, ['requiredSegmentVid-raw', 60, 70, -2, 0, 'requiredSegmentVid-raw-2', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 0, getHash('requiredSegmentVid-raw', 1)]); + await db.prepare("run", query, ['requiredSegmentVid-raw', 80, 90, -2, 0, 'requiredSegmentVid-raw-3', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 0, getHash('requiredSegmentVid-raw', 1)]); + await db.prepare("run", query, ['requiredSegmentVid-raw', 80, 90, 2, 0, 'requiredSegmentVid-raw-4', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 0, getHash('requiredSegmentVid-raw', 1)]); return; }); @@ -309,4 +313,34 @@ describe('getSkipSegments', () => { }) .catch(err => ("Couldn't call endpoint")); }); + + it('Should be able to get specific segments with requiredSegments', (done: Done) => { + fetch(getbaseURL() + '/api/skipSegments?videoID=requiredSegmentVid-raw&requiredSegments=["requiredSegmentVid-raw-2","requiredSegmentVid-raw-3"]') + .then(async res => { + if (res.status !== 200) done("non 200 status code, was " + res.status); + else { + const body = await res.json(); + if (body.length !== 2) done("expected 2 segments, got " + body.length); + else if (body[0].UUID !== 'requiredSegmentVid-raw-2' + || body[1].UUID !== 'requiredSegmentVid-raw-3') done("Did not recieve the correct segments\n" + JSON.stringify(body, null, 2)); + else done(); + } + }) + .catch(err => done("Couldn't call endpoint")); + }); + + it('Should be able to get specific segments with repeating requiredSegment', (done: Done) => { + fetch(getbaseURL() + '/api/skipSegments?videoID=requiredSegmentVid-raw&requiredSegment=requiredSegmentVid-raw-2&requiredSegment=requiredSegmentVid-raw-3') + .then(async res => { + if (res.status !== 200) done("non 200 status code, was " + res.status); + else { + const body = await res.json(); + if (body.length !== 2) done("expected 2 segments, got " + body.length); + else if (body[0].UUID !== 'requiredSegmentVid-raw-2' + || body[1].UUID !== 'requiredSegmentVid-raw-3') done("Did not recieve the correct segments\n" + JSON.stringify(body, null, 2)); + else done(); + } + }) + .catch(err => done("Couldn't call endpoint")); + }); }); diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 92cce4d..b5c87c3 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -21,6 +21,10 @@ describe('getSegmentsByHash', () => { await db.prepare("run", query, ['onlyHidden', 60, 70, 2, 'onlyHidden', 'testman', 0, 50, 'sponsor', 'YouTube', 1, 0, 'f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3']); await db.prepare("run", query, ['highlightVid', 60, 60, 2, 'highlightVid-1', 'testman', 0, 50, 'highlight', 'YouTube', 0, 0, getHash('highlightVid', 1)]); await db.prepare("run", query, ['highlightVid', 70, 70, 2, 'highlightVid-2', 'testman', 0, 50, 'highlight', 'YouTube', 0, 0, getHash('highlightVid', 1)]); + await db.prepare("run", query, ['requiredSegmentVid', 60, 70, 2, 'requiredSegmentVid-1', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 'd51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61']); + await db.prepare("run", query, ['requiredSegmentVid', 60, 70, -2, 'requiredSegmentVid-2', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 'd51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61']); + await db.prepare("run", query, ['requiredSegmentVid', 80, 90, -2, 'requiredSegmentVid-3', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 'd51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61']); + await db.prepare("run", query, ['requiredSegmentVid', 80, 90, 2, 'requiredSegmentVid-4', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 'd51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61']); }); it('Should be able to get a 200', (done: Done) => { @@ -219,4 +223,67 @@ describe('getSegmentsByHash', () => { }) .catch(err => done('(post) ' + err)); }); + + it('Should be able to get multiple categories with repeating parameters', (done: Done) => { + fetch(getbaseURL() + "/api/skipSegments/fdaff4?&category=sponsor&category=intro") + .then(async res => { + if (res.status !== 200) done("Status code was: " + res.status); + else { + const body = await res.json(); + if (body.length !== 1) done("expected 1 video, got " + body.length); + + const data = body[0].segments; + if (data.length === 2) { + let success = true; + for (const segment of data) { + if ((segment.segment[0] !== 1 || segment.segment[1] !== 10 + || segment.category !== "sponsor" || segment.UUID !== "getSegmentsByHash-0-0") && + (segment.segment[0] !== 20 || segment.segment[1] !== 30 + || segment.category !== "intro" || segment.UUID !== "getSegmentsByHash-0-1")) { + success = false; + break; + } + } + + if (success) done(); + else done("Received incorrect body: " + JSON.stringify(body)); + } else { + done("Received incorrect body: " + JSON.stringify(body)); + } + } + }) + .catch(err => ("Couldn't call endpoint")); + }); + + it('Should be able to get specific segments with requiredSegments', (done: Done) => { + fetch(getbaseURL() + '/api/skipSegments/d518?requiredSegments=["requiredSegmentVid-2","requiredSegmentVid-3"]') + .then(async res => { + if (res.status !== 200) done("non 200 status code, was " + res.status); + else { + const body = await res.json(); + if (body.length !== 1) done("expected 1 video, got " + body.length); + else if (body[0].segments.length !== 2) done("expected 2 segments for video, got " + body[0].segments.length); + else if (body[0].segments[0].UUID !== 'requiredSegmentVid-2' + || body[0].segments[1].UUID !== 'requiredSegmentVid-3') done("Did not recieve the correct segments\n" + JSON.stringify(body, null, 2)); + else done(); + } + }) + .catch(err => done("Couldn't call endpoint")); + }); + + it('Should be able to get specific segments with repeating requiredSegment', (done: Done) => { + fetch(getbaseURL() + '/api/skipSegments/d518?requiredSegment=requiredSegmentVid-2&requiredSegment=requiredSegmentVid-3') + .then(async res => { + if (res.status !== 200) done("non 200 status code, was " + res.status); + else { + const body = await res.json(); + if (body.length !== 1) done("expected 1 video, got " + body.length); + else if (body[0].segments.length !== 2) done("expected 2 segments for video, got " + body[0].segments.length); + else if (body[0].segments[0].UUID !== 'requiredSegmentVid-2' + || body[0].segments[1].UUID !== 'requiredSegmentVid-3') done("Did not recieve the correct segments\n" + JSON.stringify(body, null, 2)); + else done(); + } + }) + .catch(err => done("Couldn't call endpoint")); + }); }); diff --git a/test/cases/postSkipSegments.ts b/test/cases/postSkipSegments.ts index 2c9a3d8..eb67fcd 100644 --- a/test/cases/postSkipSegments.ts +++ b/test/cases/postSkipSegments.ts @@ -23,22 +23,31 @@ describe('postSkipSegments', () => { const warnUser01Hash = getHash("warn-user01"); const warnUser02Hash = getHash("warn-user02"); const warnUser03Hash = getHash("warn-user03"); + const warnUser04Hash = getHash("warn-user04"); + const reason01 = 'Reason01'; + const reason02 = ''; + const reason03 = 'Reason03'; + const reason04 = ''; const MILLISECONDS_IN_HOUR = 3600000; const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires; - const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled") VALUES(?, ?, ?, ?)'; - db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 1000), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser03Hash, now, warnVip01Hash, 0]); - db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 1000), warnVip01Hash, 0]); - db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 2000), warnVip01Hash, 1]); - db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 3601000), warnVip01Hash, 1]); + const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled", "reason") VALUES(?, ?, ?, ?, ?)'; + db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1, reason01]); + db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 1000), warnVip01Hash, 1, reason01]); + db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1, reason01]); + db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1, reason01]); + db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1, reason02]); + db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1, reason02]); + db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1, reason02]); + db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1, reason02]); + db.prepare("run", insertWarningQuery, [warnUser03Hash, now, warnVip01Hash, 0, reason03]); + db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 1000), warnVip01Hash, 0, reason03]); + db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 2000), warnVip01Hash, 1, reason03]); + db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 3601000), warnVip01Hash, 1, reason03]); + db.prepare("run", insertWarningQuery, [warnUser04Hash, now, warnVip01Hash, 0, reason04]); + db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 1000), warnVip01Hash, 0, reason04]); + db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 2000), warnVip01Hash, 1, reason04]); + db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 3601000), warnVip01Hash, 1, reason04]); const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)'; db.prepare("run", insertVipUserQuery, [getHash("VIPUserSubmission")]); @@ -601,7 +610,7 @@ describe('postSkipSegments', () => { .catch(err => done("Couldn't call endpoint")); }); - it('Should be rejected if user has to many active warnings', (done: Done) => { + it('Should be rejected with custom message if user has to many active warnings', (done: Done) => { fetch(getbaseURL() + "/api/postVideoSponsorTimes", { method: 'POST', @@ -619,7 +628,12 @@ describe('postSkipSegments', () => { }) .then(async res => { if (res.status === 403) { - done(); // success + const errorMessage = await res.text(); + if (errorMessage === 'Reason01') { + done(); // success + } else { + done("Status code was 403 but message was: " + errorMessage); + } } else { done("Status code was " + res.status); } @@ -693,6 +707,37 @@ describe('postSkipSegments', () => { .catch(err => done(true)); }); + it('Should be rejected with default message if user has to many active warnings', (done: Done) => { + fetch(getbaseURL() + + "/api/postVideoSponsorTimes", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + userID: "warn-user01", + videoID: "dQw4w9WgXcF", + segments: [{ + segment: [0, 10], + category: "sponsor", + }], + }), + }) + .then(async res => { + if (res.status === 403) { + const errorMessage = await res.text(); + if (errorMessage !== '') { + done(); // success + } else { + done("Status code was 403 but message was: " + errorMessage); + } + } else { + done("Status code was " + res.status); + } + }) + .catch(err => done(err)); + }); + it('Should return 400 for missing params (JSON method) 1', (done: Done) => { fetch(getbaseURL() + "/api/postVideoSponsorTimes", {