diff --git a/example.env b/example.env index f3908cd..3c184c5 100644 --- a/example.env +++ b/example.env @@ -1,5 +1,6 @@ DISCORD_TOKEN=MTgzMTM1MDU4NDM1NzYwNjIz.T33Rns.A5CRoasbuvPS8Uc1QeoqEA3QQI4 +GROQ_API_KEY=gsk_i5btqpox8Ei1s2bFdGRuWmVRH0QZIVuwnn8aFxa8KtXaZDetDYpZJRNCwRAp PORT=3000 HOST=127.0.0.1 -BASE_URL=https://bot.example.com +BASE_URL=https://bot.example.com \ No newline at end of file diff --git a/package.json b/package.json index b9dbe29..c575a29 100644 --- a/package.json +++ b/package.json @@ -11,15 +11,16 @@ "@himeka/booru": "^2.7.7", "@imgproxy/imgproxy-node": "^1.1.0", "better-sqlite3": "^11.10.0", - "bootstrap": "^5.3.7", - "canvas": "^3.1.2", - "discord.js": "^14.21.0", - "dotenv": "^16.6.1", + "bootstrap": "^5.3.6", + "canvas": "^3.1.0", + "discord.js": "^14.19.3", + "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^4.21.2", + "groq-sdk": "^0.7.0", "html-entities": "^2.6.0", "knex": "^3.1.0", "pagination.djs": "^4.0.18", "showdown": "^2.1.0" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91caa02..2e19308 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,23 +18,26 @@ importers: specifier: ^11.10.0 version: 11.10.0 bootstrap: - specifier: ^5.3.7 - version: 5.3.7(@popperjs/core@2.11.8) + specifier: ^5.3.6 + version: 5.3.6(@popperjs/core@2.11.8) canvas: - specifier: ^3.1.2 - version: 3.1.2 + specifier: ^3.1.0 + version: 3.1.0 discord.js: - specifier: ^14.21.0 - version: 14.21.0 + specifier: ^14.19.3 + version: 14.19.3 dotenv: - specifier: ^16.6.1 - version: 16.6.1 + specifier: ^16.5.0 + version: 16.5.0 ejs: specifier: ^3.1.10 version: 3.1.10 express: specifier: ^4.21.2 version: 4.21.2 + groq-sdk: + specifier: ^0.7.0 + version: 0.7.0 html-entities: specifier: ^2.6.0 version: 2.6.0 @@ -43,7 +46,7 @@ importers: version: 3.1.0(better-sqlite3@11.10.0) pagination.djs: specifier: ^4.0.18 - version: 4.0.18(discord.js@14.21.0) + version: 4.0.18(discord.js@14.19.3) showdown: specifier: ^2.1.0 version: 2.1.0 @@ -66,16 +69,16 @@ packages: resolution: {integrity: sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==} engines: {node: '>=16.11.0'} - '@discordjs/rest@2.5.1': - resolution: {integrity: sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw==} + '@discordjs/rest@2.5.0': + resolution: {integrity: sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ==} engines: {node: '>=18'} '@discordjs/util@1.1.1': resolution: {integrity: sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==} engines: {node: '>=18'} - '@discordjs/ws@1.2.3': - resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==} + '@discordjs/ws@1.2.2': + resolution: {integrity: sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w==} engines: {node: '>=16.11.0'} '@himeka/booru@2.7.7': @@ -103,8 +106,14 @@ packages: resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - '@types/node@24.0.6': - resolution: {integrity: sha512-ZOyn+gOs749xU7ovp+Ibj0g1o3dFRqsfPnT22C2t5JzcRvgsEDpGawPbCISGKLudJk9Y0wiu9sYd6kUh0pc9TA==} + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + + '@types/node@18.19.111': + resolution: {integrity: sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==} + + '@types/node@24.0.0': + resolution: {integrity: sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -113,10 +122,18 @@ packages: resolution: {integrity: sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -127,6 +144,9 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -146,16 +166,16 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - bootstrap@5.3.7: - resolution: {integrity: sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==} + bootstrap@5.3.6: + resolution: {integrity: sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==} peerDependencies: '@popperjs/core': ^2.11.8 - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -172,8 +192,8 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - canvas@3.1.2: - resolution: {integrity: sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==} + canvas@3.1.0: + resolution: {integrity: sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==} engines: {node: ^18.12.0 || >= 20.9.0} chalk@4.1.2: @@ -193,6 +213,10 @@ packages: 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'} @@ -244,6 +268,10 @@ packages: 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'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -256,15 +284,15 @@ packages: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} - discord-api-types@0.38.13: - resolution: {integrity: sha512-FELWJRgLVQuR7Az8RhdEZE0k6QNjSW9PCUcU1iyP2Gke8HrJmnMceSS9pD93UM64s3tvZzJPajpPLjWZJylf4g==} + discord-api-types@0.38.11: + resolution: {integrity: sha512-XN0qhcQpetkyb/49hcDHuoeUPsQqOkb17wbV/t48gUkoEDi4ajhsxqugGcxvcN17BBtI9FPPWEgzv6IhQmCwyw==} - discord.js@14.21.0: - resolution: {integrity: sha512-U5w41cEmcnSfwKYlLv5RJjB8Joa+QJyRwIJz5i/eg+v2Qvv6EYpCRhN9I2Rlf0900LuqSDg8edakUATrDZQncQ==} + discord.js@14.19.3: + resolution: {integrity: sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA==} engines: {node: '>=18'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -287,8 +315,8 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} @@ -302,6 +330,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -317,6 +349,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.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'} @@ -342,6 +378,17 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -378,6 +425,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + groq-sdk@0.7.0: + resolution: {integrity: sha512-OgPqrRtti5MjEVclR8sgBHrhSkTLdFCmi47yrEF29uJZaiCkX3s7bXpnMhq8Lwoe1f4AwgC0qGOeHXpeSgu5lg==} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -386,6 +436,10 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -397,6 +451,9 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -535,6 +592,11 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -582,8 +644,8 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} @@ -723,11 +785,14 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - undici@6.21.3: - resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} + undici@6.21.1: + resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} engines: {node: '>=18.17'} unfetch@4.2.0: @@ -748,6 +813,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -776,7 +845,7 @@ snapshots: '@discordjs/formatters': 0.6.1 '@discordjs/util': 1.1.1 '@sapphire/shapeshift': 4.0.0 - discord-api-types: 0.38.13 + discord-api-types: 0.38.11 fast-deep-equal: 3.1.3 ts-mixer: 6.0.4 tslib: 2.8.1 @@ -787,31 +856,31 @@ snapshots: '@discordjs/formatters@0.6.1': dependencies: - discord-api-types: 0.38.13 + discord-api-types: 0.38.11 - '@discordjs/rest@2.5.1': + '@discordjs/rest@2.5.0': dependencies: '@discordjs/collection': 2.1.1 '@discordjs/util': 1.1.1 '@sapphire/async-queue': 1.5.5 '@sapphire/snowflake': 3.5.3 '@vladfrangu/async_event_emitter': 2.4.6 - discord-api-types: 0.38.13 + discord-api-types: 0.38.11 magic-bytes.js: 1.12.1 tslib: 2.8.1 - undici: 6.21.3 + undici: 6.21.1 '@discordjs/util@1.1.1': {} - '@discordjs/ws@1.2.3': + '@discordjs/ws@1.2.2': dependencies: '@discordjs/collection': 2.1.1 - '@discordjs/rest': 2.5.1 + '@discordjs/rest': 2.5.0 '@discordjs/util': 1.1.1 '@sapphire/async-queue': 1.5.5 '@types/ws': 8.18.1 '@vladfrangu/async_event_emitter': 2.4.6 - discord-api-types: 0.38.13 + discord-api-types: 0.38.11 tslib: 2.8.1 ws: 8.18.2 transitivePeerDependencies: @@ -842,21 +911,38 @@ snapshots: '@sapphire/snowflake@3.5.3': {} - '@types/node@24.0.6': + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 18.19.111 + form-data: 4.0.3 + + '@types/node@18.19.111': + dependencies: + undici-types: 5.26.5 + + '@types/node@24.0.0': dependencies: undici-types: 7.8.0 '@types/ws@8.18.1': dependencies: - '@types/node': 24.0.6 + '@types/node': 24.0.0 '@vladfrangu/async_event_emitter@2.4.6': {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -865,6 +951,8 @@ snapshots: async@3.2.6: {} + asynckit@0.4.0: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -901,16 +989,16 @@ snapshots: transitivePeerDependencies: - supports-color - bootstrap@5.3.7(@popperjs/core@2.11.8): + bootstrap@5.3.6(@popperjs/core@2.11.8): dependencies: '@popperjs/core': 2.11.8 - brace-expansion@1.1.12: + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -931,7 +1019,7 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - canvas@3.1.2: + canvas@3.1.0: dependencies: node-addon-api: 7.1.1 prebuild-install: 7.1.3 @@ -951,6 +1039,10 @@ snapshots: colorette@2.0.19: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@10.0.1: {} commander@9.5.0: {} @@ -981,34 +1073,36 @@ snapshots: deep-extend@0.6.0: {} + delayed-stream@1.0.0: {} + depd@2.0.0: {} destroy@1.2.0: {} detect-libc@2.0.4: {} - discord-api-types@0.38.13: {} + discord-api-types@0.38.11: {} - discord.js@14.21.0: + discord.js@14.19.3: dependencies: '@discordjs/builders': 1.11.2 '@discordjs/collection': 1.5.3 '@discordjs/formatters': 0.6.1 - '@discordjs/rest': 2.5.1 + '@discordjs/rest': 2.5.0 '@discordjs/util': 1.1.1 - '@discordjs/ws': 1.2.3 + '@discordjs/ws': 1.2.2 '@sapphire/snowflake': 3.5.3 - discord-api-types: 0.38.13 + discord-api-types: 0.38.11 fast-deep-equal: 3.1.3 lodash.snakecase: 4.1.1 magic-bytes.js: 1.12.1 tslib: 2.8.1 - undici: 6.21.3 + undici: 6.21.1 transitivePeerDependencies: - bufferutil - utf-8-validate - dotenv@16.6.1: {} + dotenv@16.5.0: {} dunder-proto@1.0.1: dependencies: @@ -1026,7 +1120,7 @@ snapshots: encodeurl@2.0.0: {} - end-of-stream@1.4.5: + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -1038,6 +1132,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -1046,6 +1147,8 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + expand-template@2.0.3: {} express@4.21.2: @@ -1108,6 +1211,21 @@ snapshots: transitivePeerDependencies: - supports-color + form-data-encoder@1.7.2: {} + + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + forwarded@0.2.0: {} fresh@0.5.2: {} @@ -1142,10 +1260,26 @@ snapshots: gopd@1.2.0: {} + groq-sdk@0.7.0: + dependencies: + '@types/node': 18.19.111 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + has-flag@4.0.0: {} has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -1160,6 +1294,10 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -1239,11 +1377,11 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.11 minimatch@5.1.6: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.1 minimist@1.2.8: {} @@ -1265,6 +1403,8 @@ snapshots: node-addon-api@7.1.1: {} + node-domexception@1.0.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -1279,9 +1419,9 @@ snapshots: dependencies: wrappy: 1.0.2 - pagination.djs@4.0.18(discord.js@14.21.0): + pagination.djs@4.0.18(discord.js@14.19.3): dependencies: - discord.js: 14.21.0 + discord.js: 14.19.3 parseurl@1.3.3: {} @@ -1300,7 +1440,7 @@ snapshots: mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 node-abi: 3.75.0 - pump: 3.0.3 + pump: 3.0.2 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.3 @@ -1311,9 +1451,9 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - pump@3.0.3: + pump@3.0.2: dependencies: - end-of-stream: 1.4.5 + end-of-stream: 1.4.4 once: 1.4.0 qs@6.13.0: @@ -1449,13 +1589,13 @@ snapshots: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.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.5 + end-of-stream: 1.4.4 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 @@ -1481,9 +1621,11 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + undici-types@5.26.5: {} + undici-types@7.8.0: {} - undici@6.21.3: {} + undici@6.21.1: {} unfetch@4.2.0: {} @@ -1495,6 +1637,8 @@ snapshots: vary@1.1.2: {} + web-streams-polyfill@4.0.0-beta.3: {} + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: diff --git a/readme.md b/readme.md index 1b8ad2d..e5aef3a 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,8 @@ this bot does cool stuff I guess features: +- ai-powered alt text for images - booru search (20+ supported boorus) - online file search (using searxng) - quote image maker (funny) -- and more to come +- and more to come \ No newline at end of file diff --git a/src/commands/accessibility/describe.js b/src/commands/accessibility/describe.js new file mode 100644 index 0000000..e1880b6 --- /dev/null +++ b/src/commands/accessibility/describe.js @@ -0,0 +1,77 @@ +const { ContextMenuCommandBuilder, ApplicationCommandType, InteractionContextType, ApplicationIntegrationType, AttachmentBuilder, EmbedBuilder, basename } = require("discord.js"); + +const data = new ContextMenuCommandBuilder() + .setName("Describe Image(s)") + .setType(ApplicationCommandType.Message) + .setContexts([ + InteractionContextType.Guild, + InteractionContextType.BotDM, + InteractionContextType.PrivateChannel + ]) + .setIntegrationTypes([ + ApplicationIntegrationType.GuildInstall, + ApplicationIntegrationType.UserInstall + ]); + +module.exports = { + data, + async execute(interaction) { + await interaction.deferReply(); + + const groq = interaction.client.groq; + const message = interaction.targetMessage; + const attachments = message.attachments; + const images = message.embeds.filter(e => e.data.type == "image").map(e => e.data.url); + const urls = []; + const files = []; + const embeds = []; + + if (attachments.length == 0 && images.length == 0) { + await interaction.followUp("Message does not contain any images."); + return; + } + + for (const att of attachments) { + const attachment = att[1]; + if (!attachment.contentType.startsWith("image/")) + continue; + + images.push(attachment.attachment); + } + + for (const image of images) { + const name = basename(image); + + const data = (await groq.chat.completions.create({ + messages: [{ + "role": "user", + "content": [{ + "type": "text", + "text": interaction.client.prompts.image + }, { + "type": "image_url", + "image_url": { + "url": image + } + }] + }], + "model": "meta-llama/llama-4-maverick-17b-128e-instruct" + })); + + const description = data.choices[0].message.content.trim(); + + if (description.length < 2000) { + const embed = new EmbedBuilder() + .setTitle(name) + .setDescription(description); + embeds.push(embed); + } else { + files.push(new AttachmentBuilder() + .setName(name + ".md") + .setFile(Buffer.from(description, "utf-8"))); + } + } + + await interaction.followUp({ embeds, files }); + }, +}; diff --git a/src/commands/accessibility/summarize.js b/src/commands/accessibility/summarize.js new file mode 100644 index 0000000..9e69a7c --- /dev/null +++ b/src/commands/accessibility/summarize.js @@ -0,0 +1,39 @@ +const { ContextMenuCommandBuilder, ApplicationCommandType, InteractionContextType, ApplicationIntegrationType } = require("discord.js"); + +const data = new ContextMenuCommandBuilder() + .setName("Summarize") + .setType(ApplicationCommandType.Message) + .setContexts([ + InteractionContextType.Guild, + InteractionContextType.BotDM, + InteractionContextType.PrivateChannel + ]) + .setIntegrationTypes([ + ApplicationIntegrationType.GuildInstall, + ApplicationIntegrationType.UserInstall + ]); + +module.exports = { + data, + async execute(interaction) { + await interaction.deferReply(); + + const groq = interaction.client.groq; + const message = interaction.targetMessage; + + const summary = await groq.chat.completions.create({ + messages: [{ + role: "user", + content: interaction.client.prompts.summary + }, + { + role: "user", + content: message.content + } + ], + "model": interaction.defaultModel + }); + + await interaction.followUp(summary.choices[0].message.content); + }, +}; \ No newline at end of file diff --git a/src/commands/ai/prompt.js b/src/commands/ai/prompt.js new file mode 100644 index 0000000..2b479a2 --- /dev/null +++ b/src/commands/ai/prompt.js @@ -0,0 +1,69 @@ +const { InteractionContextType, ApplicationIntegrationType, SlashCommandBuilder } = require("discord.js"); +const { encode } = require("html-entities"); +const { knex } = require("../../db.js"); + +const data = new SlashCommandBuilder() + .setName("prompt") + .setDescription("Prompt an AI model with data") + .addStringOption(builder => + builder // + .setName("prompt") + .setRequired(true) + .setDescription("What to prompt the AI") + ) + .addStringOption(builder => + builder // + .setName("model") + .setRequired(false) + .setDescription("What AI model to use") + .addChoices({ name: "Llama 3.3", value: "llama-3.3-70b-versatile" }, { name: "DeepSeek R1", value: "deepseek-r1-distill-llama-70b" }) + ) + .addBooleanOption(builder => + builder // + .setName("send") + .setRequired(false) + .setDescription("Send the message?") + ) + .setContexts([ + InteractionContextType.Guild, + InteractionContextType.BotDM, + InteractionContextType.PrivateChannel + ]) + .setIntegrationTypes([ + ApplicationIntegrationType.GuildInstall, + ApplicationIntegrationType.UserInstall + ]); + +module.exports = { + data, + async execute(interaction) { + await interaction.deferReply({ ephemeral: !(interaction.options.getBoolean("send") || true) }); + + const groq = interaction.client.groq; + /** @type {string} */ + var response = (await groq.chat.completions.create({ + messages: [{ + role: "system", + content: interaction.client.prompts.query + }, { + role: "user", + content: interaction.options.getString("prompt") + }], + "model": interaction.options.getString("model") || interaction.defaultModel + })).choices[0].message.content; + + if (response.length > 2000) { + var id = Math.random().toString(16).slice(2, 10); + await knex.insert({ id, data: encode(response) }).into("pastes"); + + response = response.split("\n")[0]; + if (response.length > 100) { + response = response.slice(0, 100) + "..."; + } + + response += `\n[Read More](${process.env.BASE_URL}/view/${id})`; + } + + await interaction.followUp(response + "\n\n-# This content was generated by a LLM and may be incorrect"); + }, +}; diff --git a/src/commands/ai/query.js b/src/commands/ai/query.js new file mode 100644 index 0000000..a00471e --- /dev/null +++ b/src/commands/ai/query.js @@ -0,0 +1,37 @@ +const { ContextMenuCommandBuilder, ApplicationCommandType, InteractionContextType, ApplicationIntegrationType } = require("discord.js"); + +const data = new ContextMenuCommandBuilder() + .setName("Query AI") + .setType(ApplicationCommandType.Message) + .setContexts([ + InteractionContextType.Guild, + InteractionContextType.BotDM, + InteractionContextType.PrivateChannel + ]) + .setIntegrationTypes([ + ApplicationIntegrationType.GuildInstall, + ApplicationIntegrationType.UserInstall + ]); + +module.exports = { + data, + async execute(interaction) { + await interaction.deferReply(); + + const groq = interaction.client.groq; + const message = interaction.targetMessage; + + const summary = await groq.chat.completions.create({ + messages: [{ + role: "system", + content: interaction.client.prompts.query + }, { + role: "user", + content: message.content + }], + "model": interaction.defaultModel + }); + + await interaction.followUp(summary.choices[0].message.content); + }, +}; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 290e5cb..4d486ac 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ const { REST, Routes, Client, Collection, GatewayIntentBits, Events, Partials, InteractionType, ActivityType } = require("discord.js"); +const { default: Groq } = require("groq-sdk"); const server = require("./server"); const { knex } = require("./db.js"); const path = require("node:path"); @@ -11,6 +12,7 @@ const client = new Client({ }); client.commands = new Collection(); +client.groq = new Groq({ apiKey: process.env.GROQ_API_KEY }); client.prompts = []; var promptsDir = path.join(__dirname, "prompts"); @@ -53,6 +55,7 @@ client.on(Events.InteractionCreate, async interaction => { console.log(`${interaction.user.username} ran ${(interaction.isChatInputCommand() ? "/" : '') + interaction.commandName}`); try { + interaction.defaultModel = "llama-3.3-70b-versatile"; await command.execute(interaction); } catch (err) { console.error(err);