diff --git a/.gitignore b/.gitignore index 1b3e370..065fd69 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,7 @@ dist .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz -.pnp.* \ No newline at end of file +.pnp.* + +# database +data.db \ No newline at end of file diff --git a/package.json b/package.json index e333a86..93b5a06 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ "dependencies": { "@himeka/booru": "^2.7.7", "@imgproxy/imgproxy-node": "^1.0.6", + "better-sqlite3": "^11.3.0", "discord.js": "^14.16.3", "dotenv": "^16.4.5", "groq-sdk": "^0.7.0", "html-entities": "^2.5.2", + "knex": "^3.1.0", "pagination.djs": "^4.0.16" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ad5676..c75b115 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@imgproxy/imgproxy-node': specifier: ^1.0.6 version: 1.0.6 + better-sqlite3: + specifier: ^11.3.0 + version: 11.3.0 discord.js: specifier: ^14.16.3 version: 14.16.3 @@ -26,6 +29,9 @@ importers: html-entities: specifier: ^2.5.2 version: 2.5.2 + knex: + specifier: ^3.1.0 + version: 3.1.0(better-sqlite3@11.3.0) pagination.djs: specifier: ^4.0.16 version: 4.0.16(discord.js@14.16.3) @@ -109,14 +115,60 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-sqlite3@11.3.0: + resolution: {integrity: sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + colorette@2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + discord-api-types@0.37.100: resolution: {integrity: sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==} @@ -134,10 +186,25 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -145,6 +212,9 @@ packages: resolution: {integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==} hasBin: true + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} @@ -156,18 +226,83 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + getopts@2.3.0: + resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + groq-sdk@0.7.0: resolution: {integrity: sha512-OgPqrRtti5MjEVclR8sgBHrhSkTLdFCmi47yrEF29uJZaiCkX3s7bXpnMhq8Lwoe1f4AwgC0qGOeHXpeSgu5lg==} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + interpret@2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + isomorphic-unfetch@3.1.0: resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} + knex@3.1.0: + resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + pg-native: + optional: true + sqlite3: + optional: true + tedious: + optional: true + lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} @@ -185,9 +320,29 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + + node-abi@3.68.0: + resolution: {integrity: sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==} + engines: {node: '>=10'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -201,14 +356,91 @@ packages: encoding: optional: true + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + pagination.djs@4.0.16: resolution: {integrity: sha512-Rq7PnkT42QxMvnuF9BsKEfPOwow9CIJeEXQUj+IVW/lqdSliGoNs0MCoxBM/R6zKXs6EPurQPYiF/n21ATR+hA==} peerDependencies: discord.js: ^14.1.2 + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + + prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} + engines: {node: '>=10'} + hasBin: true + + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tarn@3.0.2: + resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} + engines: {node: '>=8.0.0'} + + tildify@2.0.0: + resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} + engines: {node: '>=8'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -218,6 +450,9 @@ packages: tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -231,6 +466,9 @@ packages: unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -241,6 +479,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -353,12 +594,52 @@ snapshots: asynckit@0.4.0: {} + base64-js@1.5.1: {} + + better-sqlite3@11.3.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.2 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chownr@1.1.4: {} + + colorette@2.0.19: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 + commander@10.0.1: {} + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + delayed-stream@1.0.0: {} + detect-libc@2.0.3: {} + discord-api-types@0.37.100: {} discord-api-types@0.37.83: {} @@ -385,14 +666,26 @@ snapshots: dotenv@16.4.5: {} + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + escalade@3.2.0: {} + + esm@3.2.25: {} + event-target-shim@5.0.1: {} + expand-template@2.0.3: {} + fast-deep-equal@3.1.3: {} fast-xml-parser@4.4.0: dependencies: strnum: 1.0.5 + file-uri-to-path@1.0.0: {} + form-data-encoder@1.7.2: {} form-data@4.0.0: @@ -406,6 +699,16 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + fs-constants@1.0.0: {} + + function-bind@1.1.2: {} + + get-package-type@0.1.0: {} + + getopts@2.3.0: {} + + github-from-package@0.0.0: {} + groq-sdk@0.7.0: dependencies: '@types/node': 18.19.54 @@ -418,12 +721,28 @@ snapshots: transitivePeerDependencies: - encoding + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + html-entities@2.5.2: {} humanize-ms@1.2.1: dependencies: ms: 2.1.3 + ieee754@1.2.1: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + interpret@2.2.0: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + isomorphic-unfetch@3.1.0: dependencies: node-fetch: 2.7.0 @@ -431,6 +750,27 @@ snapshots: transitivePeerDependencies: - encoding + knex@3.1.0(better-sqlite3@11.3.0): + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4 + escalade: 3.2.0 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.17.21 + pg-connection-string: 2.6.2 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + optionalDependencies: + better-sqlite3: 11.3.0 + transitivePeerDependencies: + - supports-color + lodash.snakecase@4.1.1: {} lodash@4.17.21: {} @@ -443,26 +783,136 @@ snapshots: dependencies: mime-db: 1.52.0 + mimic-response@3.1.0: {} + + minimist@1.2.8: {} + + mkdirp-classic@0.5.3: {} + + ms@2.1.2: {} + ms@2.1.3: {} + napi-build-utils@1.0.2: {} + + node-abi@3.68.0: + dependencies: + semver: 7.6.3 + node-domexception@1.0.0: {} node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + pagination.djs@4.0.16(discord.js@14.16.3): dependencies: discord.js: 14.16.3 + path-parse@1.0.7: {} + + pg-connection-string@2.6.2: {} + + prebuild-install@7.1.2: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.68.0 + pump: 3.0.2 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + + pump@3.0.2: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + rechoir@0.8.0: + dependencies: + resolve: 1.22.8 + + resolve-from@5.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + safe-buffer@5.2.1: {} + + semver@7.6.3: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@2.0.1: {} + strnum@1.0.5: {} + supports-preserve-symlinks-flag@1.0.0: {} + + tar-fs@2.1.1: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.2 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tarn@3.0.2: {} + + tildify@2.0.0: {} + tr46@0.0.3: {} ts-mixer@6.0.4: {} tslib@2.7.0: {} + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + undici-types@5.26.5: {} undici-types@6.19.8: {} @@ -471,6 +921,8 @@ snapshots: unfetch@4.2.0: {} + util-deprecate@1.0.2: {} + web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} @@ -480,4 +932,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + wrappy@1.0.2: {} + ws@8.18.0: {} diff --git a/src/commands/fun/blacklist.js b/src/commands/fun/blacklist.js new file mode 100644 index 0000000..564705a --- /dev/null +++ b/src/commands/fun/blacklist.js @@ -0,0 +1,91 @@ +const { InteractionContextType, ApplicationIntegrationType, SlashCommandBuilder, EmbedBuilder } = require("discord.js"); +const { format } = require("node:util"); +const { knex } = require("../../db.js"); + +const data = new SlashCommandBuilder() + .setName("blacklist") + .setDescription("Manage your booru tag blacklist") + .addSubcommand((builder) => + builder // + .setName("add") + .setDescription("Add a tag to your blacklist") + .addStringOption(builder => + builder // + .setName("tag") + .setRequired(true) + .setDescription("Tag to blacklist") + )) + .addSubcommand((builder) => + builder // + .setName("remove") + .setDescription("Remove a tag from your blacklist") + .addStringOption(builder => + builder // + .setName("tag") + .setRequired(true) + .setDescription("Tag to unblacklist") + .setAutocomplete(true) + )) + .setContexts([ + InteractionContextType.Guild, + InteractionContextType.BotDM, + InteractionContextType.PrivateChannel + ]) + .setIntegrationTypes([ + ApplicationIntegrationType.GuildInstall, + ApplicationIntegrationType.UserInstall + ]); + +module.exports = { + data, + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + const command = interaction.options.getSubcommand(true); + const tag = interaction.options.getString("tag").replaceAll(" ", "_"); + const blacklist = ((await knex.select("blacklist").from("blacklists").where("user", interaction.user.id).first()).blacklist ?? "").split(" "); + + const data = { + user: interaction.user.id, + blacklist: [...blacklist].join(" ").trim() + } + + switch (command) { + case "add": + if (blacklist.includes(tag)) { + await interaction.followUp("This tag is already blacklisted."); + return; + } + + data.blacklist += " " + tag; + await interaction.followUp("Successfully blacklisted!"); + break; + case "remove": + data.blacklist = data.blacklist.split(" ").filter(i => i != tag).join(" ").trim(); + await interaction.followUp("Successfully removed!"); + break; + default: + break; + } + + await knex.raw(format('%s ON CONFLICT (user) DO UPDATE SET %s', + knex("blacklists").insert(data).toString().toString(), + knex("blacklists").update(data).whereRaw(`'blacklists'.user = '${data.user}'`).toString().replace(/^update\s.*\sset\s/i, '') + )); + }, + async autocomplete(interaction) { + const value = interaction.options.getFocused() ?? ""; + const command = interaction.options.getSubcommand(true); + if (command == "remove") { + const blacklist = ((await knex.select("blacklist").from("blacklists").where("user", interaction.user.id).first()).blacklist ?? "").trim().split(" "); + const choices = []; + for (const tag of blacklist) { + if (value == "" || tag.startsWith(value.trim())) + choices.push(tag); + } + + console.log(choices); + + await interaction.respond(choices.map(choice => ({ name: choice, value: choice }))) + } + }, +}; diff --git a/src/commands/fun/gelbooru.js b/src/commands/fun/gelbooru.js index 152e552..435d608 100644 --- a/src/commands/fun/gelbooru.js +++ b/src/commands/fun/gelbooru.js @@ -4,6 +4,7 @@ const { stringify } = require("node:querystring"); const { readFileSync } = require("node:fs"); const { decode } = require("html-entities"); const { extname } = require("node:path"); +const { knex } = require("../../db.js"); const Booru = require("@himeka/booru"); const boorus = []; @@ -102,9 +103,7 @@ function formatTime(time) { const blacklist = [ "ai_generated", - "ai_art", - "child", - "loli" + "ai_art" ]; var credentials = JSON.parse(readFileSync("credentials.json")); @@ -136,20 +135,12 @@ module.exports = { await interaction.deferReply(); const tags = (interaction.options.getString("tags") ?? "").split(" "); - const containsBlacklist = tags.filter(i => blacklist.includes(i)); - if (containsBlacklist.length > 0) { - await interaction.followUp(`Search included blacklisted tag(s): \`${containsBlacklist.join(", ")}\`.\n-# This incident will be reported.`); - var incidentChannel = interaction.client.channels.cache.get(process.env.INCIDENT_CHANNEL); - if (incidentChannel) { - incidentChannel.send(`User \`${interaction.user.username}\` (<@${interaction.user.id}>) searched for blacklisted tags:\n\`${containsBlacklist.join(", ")}\``); - } - return; - } const booru = interaction.options.getString("booru") ?? defaultBooru; const rating = (interaction.options.getString("rating") ?? ratings[0]); console.log(rating); - const searchTags = [...tags, ...blacklist.map(i => "-" + i), ...ratings.filter(v => v != rating).map(i => "-" + i)]; + const userBlacklist = ((await knex.select("blacklist").from("blacklists").where("user", interaction.user.id).first()).blacklist ?? "").split(" "); + const searchTags = [rating, ...tags, ...[...blacklist, ...userBlacklist].map(i => "-" + i)]; console.log(rating, searchTags); const startTime = process.hrtime.bigint(); @@ -188,7 +179,7 @@ module.exports = { .setTitle(`Post #${post.id}`) .setURL(post.postView) .setDescription(description) - .setImage(proxy(post.fileUrl)) + .setImage(post.fileUrl) .setFooter({ text: footerText, iconURL: proxy(`https://${post.booru.domain}/favicon.ico`), diff --git a/src/db.js b/src/db.js new file mode 100644 index 0000000..1b05d30 --- /dev/null +++ b/src/db.js @@ -0,0 +1,9 @@ +const knex = require("knex")({ + client: "better-sqlite3", + useNullAsDefault: true, + connection: { + filename: "data.db" + } +}); + +module.exports = { knex }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 252bf73..83b2ccf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const { REST, Routes, Client, Collection, GatewayIntentBits, Events, Partials, InteractionType } = require("discord.js"); const { default: Groq } = require("groq-sdk"); +const { knex } = require("./db.js"); const path = require("node:path"); const fs = require("node:fs"); require("dotenv").config(); @@ -11,7 +12,6 @@ const client = new Client({ client.commands = new Collection(); client.groq = new Groq({ apiKey: process.env.GROQ_API_KEY }); -//process.env.PROMPT = fs.readFileSync(path.join(__dirname, "prompt.txt")); client.prompts = []; var promptsDir = path.join(__dirname, "prompts"); @@ -50,11 +50,8 @@ client.on(Events.InteractionCreate, async interaction => { } } else if (interaction.isCommand()) { var options = ""; - for (const option of interaction.options.data) { - options += option.name + ":" + option.value - } - console.log(`${interaction.user.username} ran /${interaction.commandName} ${options}`); + console.log(`${interaction.user.username} ran /${interaction.commandName}`); try { interaction.defaultModel = "llama-3.1-70b-versatile"; @@ -77,6 +74,12 @@ client.on(Events.InteractionCreate, async interaction => { client.once(Events.ClientReady, async () => { console.log(`Ready! Logged in as ${client.user.displayName}`); + if (!(await knex.schema.hasTable("blacklists"))) + await knex.schema.createTable("blacklists", function (table) { + table.string("user").primary(); + table.string("blacklist"); + }); + var user = client.user.toJSON(); for (const prompt of fs.readdirSync(promptsDir)) {