wip: use modals v2 for bug reports
@@ -4,12 +4,12 @@ "workspaces": {
"": { "name": "template", "dependencies": { - "@google-cloud/vision": "^5.3.0", + "@google-cloud/vision": "^5.3.4", "@sillowww/lily": "^0.2.0", - "discord.js": "^14.19.3", - "mongodb": "^6.16.0", - "mongoose": "^8.15.0", - "zod": "^3.24.4", + "discord.js": "^14.24.0", + "mongodb": "^6.20.0", + "mongoose": "^8.19.2", + "zod": "^3.25.76", }, "devDependencies": { "@biomejs/biome": "^1.9.4",@@ -17,7 +17,7 @@ "@types/bun": "latest",
"@types/mongoose": "^5.11.97", }, "peerDependencies": { - "typescript": "^5.0.0", + "typescript": "^5.8.3", }, }, },@@ -40,7 +40,7 @@ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], - "@discordjs/builders": ["@discordjs/builders@1.12.2", "", { "dependencies": { "@discordjs/formatters": "^0.6.1", "@discordjs/util": "^1.1.1", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.26", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-AugKmrgRJOHXEyMkABH/hXpAmIBKbYokCEl9VAM4Kh6FvkdobQ+Zhv7AR6K+y5hS7+VQ7gKXPYCe1JQmV07H1g=="], + "@discordjs/builders": ["@discordjs/builders@1.13.0", "", { "dependencies": { "@discordjs/formatters": "^0.6.1", "@discordjs/util": "^1.1.1", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.31", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-COK0uU6ZaJI+LA67H/rp8IbEkYwlZf3mAoBI5wtPh5G5cbEQGNhVpzINg2f/6+q/YipnNIKy6fJDg6kMUKUw4Q=="], "@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],@@ -56,13 +56,13 @@ "@google-cloud/promisify": ["@google-cloud/promisify@5.0.0", "", {}, "sha512-N8qS6dlORGHwk7WjGXKOSsLjIjNINCPicsOX6gyyLiYk7mq3MtII96NZ9N2ahwA2vnkLmZODOIH9rlNniYWvCQ=="],
"@google-cloud/vision": ["@google-cloud/vision@5.3.4", "", { "dependencies": { "@google-cloud/promisify": "^5.0.0", "google-gax": "^5.0.0" } }, "sha512-KQ/0/klu6jranByRv+BpZbSBFirJNE07AO0wNhUTlYpKRv4UnSvICAVegWXQWN4oljr9EasWyft1667lhFFPAQ=="], - "@grpc/grpc-js": ["@grpc/grpc-js@1.13.4", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg=="], + "@grpc/grpc-js": ["@grpc/grpc-js@1.14.0", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg=="], - "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="], "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], - "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.1", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg=="], + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.2", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg=="], "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],@@ -94,37 +94,27 @@ "@sillowww/lily": ["@sillowww/lily@0.2.0", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-ayQi4anINUfu5B/evMIBIMD9BlOueeSKwGXoE+z+VtCVxc2ypMEb3sJyMFqjiStAW7ORMGM3oLV65JFK6PoaUw=="],
"@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="], - "@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="], - - "@types/caseless": ["@types/caseless@0.12.5", "", {}, "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg=="], + "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], "@types/mongoose": ["@types/mongoose@5.11.97", "", { "dependencies": { "mongoose": "*" } }, "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q=="], - "@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="], + "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], - "@types/request": ["@types/request@2.48.12", "", { "dependencies": { "@types/caseless": "*", "@types/node": "*", "@types/tough-cookie": "*", "form-data": "^2.5.0" } }, "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw=="], - - "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], - "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], "@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.6", "", {}, "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA=="], - - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="], "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],@@ -134,9 +124,7 @@ "bson": ["bson@6.10.4", "", {}, "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], - "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],@@ -144,21 +132,15 @@ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], - "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - - "discord-api-types": ["discord-api-types@0.38.30", "", {}, "sha512-KhAqlBrg+rVK+Ob7INMF5o63yW4/GUzRatG/AjyVsIO8lgcLyR8qCl2HokIVzWwmzkJYG0CEPXsKMOqau3E8NA=="], + "discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="], - "discord.js": ["discord.js@14.23.2", "", { "dependencies": { "@discordjs/builders": "^1.12.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.29", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-tU2NFr823X3TXEc8KyR/4m296KLxPai4nirN3q9kHCpY4TKj96n9lHZnyLzRNMui8EbL07jg9hgH2PWWfKMGIg=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "discord.js": ["discord.js@14.24.0", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.31", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-KNq/ekT8bsmT3ZAfVre8cPbl+DfVYSdlLnDmGZPoz7Cw21LYeWHllRA9MivqNq5b1GPGAxGvyUN1vxbTb/PQWw=="], "duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="],@@ -168,17 +150,7 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],@@ -186,37 +158,21 @@ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], - "form-data": ["form-data@2.5.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ=="], - "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "gaxios": ["gaxios@7.1.2", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-/Szrn8nr+2TsQT1Gp8iIe/BEytJmbyfrbFh419DfGQSkEgNEhbPi7JRJuughjkTzPWgU9gBQf5AVu3DbHt0OXA=="], - "gaxios": ["gaxios@7.1.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ=="], - - "gcp-metadata": ["gcp-metadata@7.0.1", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ=="], + "gcp-metadata": ["gcp-metadata@8.1.1", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-dTCcAe9fRQf06ELwel6lWWFrEbstwjUBYEhr5VRGoC+iPDZQucHppCowaIp8b8v92tU1G4X4H3b/Y6zXZxkMsQ=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + "google-auth-library": ["google-auth-library@10.4.2", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-EKiQasw6aEdxSovPEf1oBxCEvxjFamZ6MPaVOSPXZMnqKFLo+rrYjHyjKlFfZcXiKi9qAH6cutr5WRqqa1jKhg=="], - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "google-auth-library": ["google-auth-library@10.1.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^7.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-GspVjZj1RbyRWpQ9FbAXMKjFGzZwDKnUHi66JJ+tcjcu5/xYAP1pdlWotCuIkMwjfVsxxDvsGZXGLzRt72D0sQ=="], - - "google-gax": ["google-gax@5.0.1", "", { "dependencies": { "@grpc/grpc-js": "^1.12.6", "@grpc/proto-loader": "^0.7.13", "abort-controller": "^3.0.0", "duplexify": "^4.1.3", "google-auth-library": "^10.1.0", "google-logging-utils": "^1.1.1", "node-fetch": "^3.3.2", "object-hash": "^3.0.0", "proto3-json-serializer": "^3.0.0", "protobufjs": "^7.5.3", "retry-request": "^8.0.0" } }, "sha512-I8fTFXvIG8tYpiDxDXwCXoFsTVsvHJ2GA7DToH+eaRccU8r3nqPMFghVb2GdHSVcu4pq9ScRyB2S1BjO+vsa1Q=="], + "google-gax": ["google-gax@5.0.4", "", { "dependencies": { "@grpc/grpc-js": "^1.12.6", "@grpc/proto-loader": "^0.8.0", "duplexify": "^4.1.3", "google-auth-library": "^10.1.0", "google-logging-utils": "^1.1.1", "node-fetch": "^3.3.2", "object-hash": "^3.0.0", "proto3-json-serializer": "^3.0.0", "protobufjs": "^7.5.3", "retry-request": "^8.0.0" } }, "sha512-HmQ6zIYBs2EikTk+kjeHmtHprNTEpsnVaKONw9cwZZwUNCkUb+D5RYrJpCxyjdvIDvJp3wLbVReolJLRZRms1g=="], "google-logging-utils": ["google-logging-utils@1.1.1", "", {}, "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],@@ -244,19 +200,13 @@ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"magic-bytes.js": ["magic-bytes.js@1.12.1", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "mongodb": ["mongodb@6.20.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.3.0", "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.2" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.3.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ=="], "mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.2", "", { "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^14.1.0 || ^13.0.0" } }, "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA=="], - "mongoose": ["mongoose@8.19.1", "", { "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", "mongodb": "~6.20.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", "sift": "17.1.3" } }, "sha512-oB7hGQJn4f8aebqE7mhE54EReb5cxVgpCxQCQj0K/cK3q4J3Tg08nFP6sM52nJ4Hlm8jsDnhVYpqIITZUAhckQ=="], + "mongoose": ["mongoose@8.19.2", "", { "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", "mongodb": "~6.20.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", "sift": "17.1.3" } }, "sha512-ww2T4dBV+suCbOfG5YPwj9pLCfUVyj8FEA1D3Ux1HHqutpLxGyOYEPU06iPRBW4cKr3PJfOSYsIpHWPTkz5zig=="], "mpath": ["mpath@0.9.0", "", {}, "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew=="],@@ -272,9 +222,9 @@ "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "proto3-json-serializer": ["proto3-json-serializer@3.0.1", "", { "dependencies": { "protobufjs": "^7.4.0" } }, "sha512-Rug90pDIefARAG9MgaFjd0yR/YP4bN3Fov00kckXMjTZa0x86c4WoWfCQFdSeWi9DvRXjhfLlPDIvODB5LOTfg=="], + "proto3-json-serializer": ["proto3-json-serializer@3.0.3", "", { "dependencies": { "protobufjs": "^7.4.0" } }, "sha512-iUi7jGLuECChuoUwtvf6eXBDcFXTHAt5GM6ckvtD3RqD+j2wW0GW6WndPOu9IWeUk7n933lzrskcNMHJy2tFSw=="], - "protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="], + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],@@ -282,7 +232,7 @@ "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - "retry-request": ["retry-request@8.0.0", "", { "dependencies": { "@types/request": "^2.48.12", "extend": "^3.0.2", "teeny-request": "^10.0.0" } }, "sha512-dJkZNmyV9C8WKUmbdj1xcvVlXBSvsUQCkg89TCK8rD72RdSn9A2jlXlS2VuYSTHoPJjJEfUHhjNYrlvuksF9cg=="], + "retry-request": ["retry-request@8.0.2", "", { "dependencies": { "extend": "^3.0.2", "teeny-request": "^10.0.0" } }, "sha512-JzFPAfklk1kjR1w76f0QOIhoDkNkSqW8wYKT08n9yysTmZfB+RQ2QoXoTAeOi1HD9ZipTyTAZg3c4pM/jeqgSw=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],@@ -310,11 +260,11 @@ "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],@@ -328,7 +278,7 @@ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],@@ -338,24 +288,14 @@ "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@discordjs/formatters/discord-api-types": ["discord-api-types@0.38.8", "", {}, "sha512-xuRXPD44FcbKHrQK15FS1HFlMRNJtsaZou/SVws18vQ7zHqmlxyDktMkZpyvD6gE2ctGOVYC/jUyoMMAyBWfcw=="], - "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], - "@types/mongoose/mongoose": ["mongoose@8.15.0", "", { "dependencies": { "bson": "^6.10.3", "kareem": "2.6.3", "mongodb": "~6.16.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", "sift": "17.1.3" } }, "sha512-WFKsY1q12ScGabnZWUB9c/QzZmz/ESorrV27OembB7Gz6rrh9m3GA4Srsv1uvW1s9AHO5DeZ6DdUTyF9zyNERQ=="], - "http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], "teeny-request/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - "@types/mongoose/mongoose/bson": ["bson@6.10.3", "", {}, "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ=="], - - "@types/mongoose/mongoose/mongodb": ["mongodb@6.16.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.3", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw=="], - "teeny-request/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - - "@types/mongoose/mongoose/mongodb/@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.2.2", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA=="], } }
@@ -26,9 +26,9 @@ },
"dependencies": { "@google-cloud/vision": "^5.3.4", "@sillowww/lily": "^0.2.0", - "discord.js": "^14.23.2", + "discord.js": "^14.24.0", "mongodb": "^6.20.0", - "mongoose": "^8.19.1", + "mongoose": "^8.19.2", "zod": "^3.25.76" } }
@@ -10,7 +10,6 @@ string,
...string[], ]; -// mongoose schemas const CounterSchema = new Schema({ _id: String, sequence_value: { type: Number, default: 0 },@@ -43,7 +42,9 @@ bug_id: { type: Number, required: true, unique: true },
user_id: { type: String, required: true, ref: "User" }, status: { type: String, enum: ["open", "closed"], default: "open" }, title: { type: String, required: true }, - project: { type: String, enum: uniqueProjects, required: true }, + /** @deprecated */ + project: { type: String, enum: uniqueProjects, required: false }, + projects: [{ type: String, enum: uniqueProjects, required: false }], description: { type: String, required: true }, sent: { type: Boolean, default: false }, message_id: String,@@ -58,6 +59,7 @@ media_type: { type: String, enum: ["image", "video"], required: true },
data: { type: Buffer, required: true }, user_id: { type: String, required: true, ref: "User" }, bug_id: { type: String, ref: "Bug" }, + extension: { type: String, required: true }, }, { timestamps: true }, );
@@ -8,16 +8,7 @@ const commandData = new SlashCommandBuilder()
.setName("bug") .setDescription("bug report management") .addSubcommand((subcommand) => - subcommand - .setName("report") - .setDescription("report a new bug") - .addStringOption((option) => - option - .setName(config.data.terminology) - .setDescription(`which ${config.data.terminology} this bug affects`) - .setRequired(true) - .addChoices(...reportCommand.getProjectChoices()), - ), + subcommand.setName("report").setDescription("report a new bug"), ) .addSubcommand((subcommand) => subcommand@@ -53,6 +44,6 @@ });
} }, modalExecute: reportCommand.modalExecute, - buttonExecute: reportCommand.buttonExecute, - selectMenuExecute: reportCommand.selectMenuExecute, + // buttonExecute: reportCommand.buttonExecute, + // selectMenuExecute: reportCommand.selectMenuExecute, } satisfies ICommand;
@@ -1,461 +1,454 @@
import config from "@/config.ts"; -import { BugModel, GuildModel, getNextBugId } from "@/database/schemas.ts"; +import { + BugModel, + GuildModel, + MediaModel, + type MediaType, + getNextBugId, +} from "@/database/schemas.ts"; import { createUserIfNotExists } from "@/utils/exists.ts"; -import { hasManagerPermissions } from "@/utils/permissions.ts"; import { - ActionRowBuilder, - ButtonBuilder, - type ButtonInteraction, - ButtonStyle, - ChannelType, + type Attachment, + AttachmentBuilder, type ChatInputCommandInteraction, type Client, - EmbedBuilder, - type GuildMember, + ContainerBuilder, + FileUploadBuilder, + LabelBuilder, + MediaGalleryBuilder, + MediaGalleryItemBuilder, ModalBuilder, type ModalSubmitInteraction, + type ReadonlyCollection, + SectionBuilder, + type Snowflake, StringSelectMenuBuilder, - type StringSelectMenuInteraction, - StringSelectMenuOptionBuilder, + TextDisplayBuilder, TextInputBuilder, TextInputStyle, + ThumbnailBuilder, } from "discord.js"; -import { - PROJECT_MAP, - getProjectName, - logger as lily, - updateBugEmbed, -} from "./shared.ts"; +import { PROJECT_MAP, logger as lily } from "./shared.ts"; const logger = lily.child("report"); -export function getProjectChoices() { - return Object.entries(PROJECT_MAP).map(([key, project]) => ({ - name: project.displayName, - value: key, - })); -} +// constants +const INPUT_IDS = { + AFFECTED: "affectedInput", + TITLE: "titleInput", + DESCRIPTION: "descriptionInput", + MEDIA: "fileUploadInput", +} as const; -export async function execute( - _client: Client, - interaction: ChatInputCommandInteraction, -) { - const project = interaction.options.getString(config.data.terminology, true); +const MODAL_IDS = { + REPORT: "bug:report", + EDIT: "bug:edit", +} as const; - const modal = new ModalBuilder() - .setCustomId(`bug:${project}`) - .setTitle(`report bug - ${getProjectName(project)}`); +const VALIDATION = { + TITLE_MAX_LENGTH: 100, + DESCRIPTION_MAX_LENGTH: 1000, + THREAD_NAME_MAX_LENGTH: 50, + AUTO_ARCHIVE_DURATION: 1440, +} as const; - const titleInput = new TextInputBuilder() - .setCustomId("title") - .setLabel("bug title") - .setStyle(TextInputStyle.Short) - .setPlaceholder("short description of the bug") - .setRequired(true) - .setMaxLength(100); +// types +type ProjectChoice<T extends "name" | "label" = "name"> = T extends "name" + ? { name: string; value: string } + : { label: string; value: string }; - const descriptionInput = new TextInputBuilder() - .setCustomId("description") - .setLabel("bug description") - .setStyle(TextInputStyle.Paragraph) - .setPlaceholder("detailed description of the bug and steps to reproduce") - .setRequired(true) - .setMaxLength(1000); +interface DownloadedMediaResult { + media: MediaType[]; + totalSize: number; +} - const firstRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - titleInput, - ); - const secondRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - descriptionInput, - ); - - modal.addComponents(firstRow, secondRow); - await interaction.showModal(modal); +// utils +function buildTrelloUrl(title: string, url: string): string { + const params = new URLSearchParams({ + name: title, + url: url, + idBoard: config.data?.trelloBoardId || "", + }); + return `https://trello.com/addCard?${params.toString()}`; } -export async function modalExecute( - client: Client, - interaction: ModalSubmitInteraction, -) { - if (!interaction.guild) { - await interaction.reply({ - content: "❌ this command can only be used in a server.", - flags: ["Ephemeral"], - }); - return; - } - - const customIdParts = interaction.customId.split(":"); - const title = interaction.fields.getTextInputValue("title"); - const description = interaction.fields.getTextInputValue("description"); - await interaction.deferReply({ flags: ["Ephemeral"] }); - - // handle edit case - if (customIdParts[1] === "edit") { - const bugId = Number.parseInt(customIdParts[2]); - - try { - const bug = await BugModel.findOne({ bug_id: bugId }); - if (!bug) { - await interaction.editReply({ - content: "❌ bug not found.", - }); - return; - } - - bug.title = title; - bug.description = description; - await bug.save(); +function formatFileSize(bytes: number): string { + return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; +} - if (bug.message_id && interaction.guild) { - const guild = await GuildModel.findOne({ - guild_id: interaction.guild.id, - }); - if (guild?.bug_channel) { - await updateBugEmbed(client, bug, bug.message_id, guild.bug_channel); - } - } +function truncateTitle(title: string, maxLength: number): string { + return title.length > maxLength + ? `${title.substring(0, maxLength)}...` + : title; +} - await interaction.editReply({ - content: `✅ bug #${bug.bug_id} updated!`, - }); - } catch (error) { - logger.error("failed to update bug:", error); - await interaction.editReply({ - content: "❌ failed to update bug. please try again later.", - }); - } - return; +// media handling +async function downloadAttachment( + attachment: Attachment, + bugId: number, + userId: Snowflake, +): Promise<MediaType> { + if (!attachment.contentType) { + throw new Error(`Attachment ${attachment.id} has no content type`); } - const project = customIdParts[1]; - try { - await createUserIfNotExists(interaction.user.id, interaction.guild.id); + const response = await fetch(attachment.url); + if (!response.ok) { + throw new Error(`Failed to download attachment: ${response.statusText}`); + } - const bugId = await getNextBugId(); + const buffer = await response.arrayBuffer(); + const [mainType, subType] = attachment.contentType.split("/"); + const type = mainType === "image" ? "image" : "video"; + const extension = subType.split(";")[0]; - const bug = new BugModel({ + const media = new MediaModel({ bug_id: bugId, - user_id: interaction.user.id, - project, - title, - description, - status: "open", - sent: false, + user_id: userId, + media_type: type, + data: Buffer.from(buffer), + extension, }); - await bug.save(); - logger.info(`bug report created: ${bug._id}`); + await media.save(); + return media; + } catch (error) { + logger.error("Failed to download attachment", { + error, + attachmentId: attachment.id, + bugId, + }); + throw error; + } +} + +async function downloadAttachments( + attachments: ReadonlyCollection<string, Attachment>, + bugId: number, + userId: Snowflake, +): Promise<DownloadedMediaResult> { + const totalSize = Array.from(attachments.values()).reduce( + (acc, att) => acc + att.size, + 0, + ); + + const mediaPromises = attachments.map((attachment) => + downloadAttachment(attachment, bugId, userId), + ); - // send to bug channel if configured - const guild = await GuildModel.findOne({ - guild_id: interaction.guild.id, - }); - let msgUrl = ""; + const media = await Promise.all(mediaPromises); - if (guild?.bug_channel) { - try { - const channel = await client.channels.fetch(guild.bug_channel); - if (channel?.type !== ChannelType.GuildText) { - await interaction.editReply({ - content: "❌ the bug channel is misconfigured", - }); - } + return { media, totalSize }; +} - if (channel?.isTextBased() && "send" in channel) { - const projectInfo = PROJECT_MAP[project as keyof typeof PROJECT_MAP]; +// project choices +export function getProjectChoices<T extends "name" | "label" = "name">( + label: T = "name" as T, +): ProjectChoice<T>[] { + return Object.entries(PROJECT_MAP).map(([key, project]) => + label === "name" + ? { name: project.displayName, value: key } + : { label: project.displayName, value: key }, + ) as ProjectChoice<T>[]; +} - const embed = new EmbedBuilder() - .setAuthor({ - name: interaction.user.displayName, - url: `https://discord.com/users/${interaction.user.id}`, - iconURL: interaction.user?.avatarURL() || undefined, - }) - .setTitle(title) - .setColor(0xff6b6b) - .setDescription(description) - .setFooter({ text: `${projectInfo.name} • bug #${bugId}` }) - .setTimestamp(); +// Modal builders +function buildReportModal(): ModalBuilder { + const terminology = config.data.terminology; + const capitalizedTerminology = + terminology.charAt(0).toUpperCase() + terminology.slice(1); - if (projectInfo.iconURL) embed.setThumbnail(projectInfo.iconURL); + const gameLabel = new LabelBuilder() + .setLabel(capitalizedTerminology) + .setDescription(`The ${terminology} affected by this bug`) + .setStringSelectMenuComponent( + new StringSelectMenuBuilder() + .setMaxValues(1) + .setMinValues(1) + .setCustomId(INPUT_IDS.AFFECTED) + .setPlaceholder(`Select the ${terminology} you encountered the bug in`) + .addOptions(getProjectChoices("label")), + ); - const trelloUrlParts = [ - "https://trello.com/addCard?name=", - encodeURIComponent(title), - "&url=", - encodeURIComponent( - interaction.message?.url || "https://example.com", - ), - "&idBoard=", - config.data?.trelloBoardId || "", - ]; + const titleLabel = new LabelBuilder() + .setLabel("Bug Title") + .setDescription("Provide a short description of the bug") + .setTextInputComponent( + new TextInputBuilder() + .setCustomId(INPUT_IDS.TITLE) + .setStyle(TextInputStyle.Short) + .setRequired(true) + .setMaxLength(VALIDATION.TITLE_MAX_LENGTH), + ); - const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents( - new ButtonBuilder() - .setCustomId(`bug:close:${bugId}`) - .setLabel("close") - .setStyle(ButtonStyle.Secondary) - .setEmoji("🔒"), - new ButtonBuilder() - .setCustomId(`bug:edit:${bugId}`) - .setLabel("edit") - .setStyle(ButtonStyle.Primary), - new ButtonBuilder() - .setCustomId(`bug:delete:${bugId}`) - .setLabel("delete") - .setStyle(ButtonStyle.Danger), - new ButtonBuilder() - .setStyle(ButtonStyle.Link) - .setLabel("add to trello") - .setURL(trelloUrlParts.join("")), - new ButtonBuilder() - .setCustomId("bug:new") - .setLabel("new bug") - .setStyle(ButtonStyle.Success), - ); + const descriptionLabel = new LabelBuilder() + .setLabel("Bug Description") + .setDescription( + "Provide a detailed description of the bug and steps to reproduce", + ) + .setTextInputComponent( + new TextInputBuilder() + .setCustomId(INPUT_IDS.DESCRIPTION) + .setStyle(TextInputStyle.Paragraph) + .setRequired(true) + .setMaxLength(VALIDATION.DESCRIPTION_MAX_LENGTH), + ); - const message = await channel.send({ - embeds: [embed], - components: [buttons], - }); + const mediaLabel = new LabelBuilder() + .setLabel("Media") + .setDescription( + "Provide any screenshots or videos that demonstrate the bug", + ) + .setFileUploadComponent( + new FileUploadBuilder().setCustomId(INPUT_IDS.MEDIA).setRequired(false), + ); - try { - const thread = await message.startThread({ - name: `#${bugId}: ${title.substring(0, 50)}${title.length > 50 ? "..." : ""}`, - reason: `bug report thread for bug #${bugId}`, - }); + return new ModalBuilder() + .setCustomId(MODAL_IDS.REPORT) + .setTitle("Report a Bug") + .setLabelComponents(gameLabel, titleLabel, descriptionLabel, mediaLabel); +} - await thread.members.add(interaction.user.id); - await thread.send({ - content: - `thread created for bug #${bugId} affecting ${projectInfo.displayName}. - use this space to discuss the bug report, provide additional - details, or ask questions.` - .replace(/\s+/g, " ") - .trim(), - }); +// Validation +async function validateGuildSetup(interaction: ModalSubmitInteraction) { + if (!interaction.guild) { + throw new Error("This command can only be used in a server."); + } - bug.thread_id = thread.id; - await bug.save(); - } catch (error) {} + const guildData = await GuildModel.findOne({ + guild_id: interaction.guild.id, + }); - msgUrl = message.url; + if (!guildData) + throw new Error("Guild data not found. Please configure the bot first."); + if (!guildData.bug_channel) + throw new Error("The bug channel is not configured."); - bug.message_id = message.id; - bug.sent = true; - await bug.save(); + const channel = interaction.guild.channels.cache.get(guildData.bug_channel); + if (!channel?.isTextBased() || !("send" in channel)) + throw new Error("The bug channel configured is not a valid text channel."); - logger.info( - `sent bug report ${bug._id} to channel ${guild.bug_channel}`, - ); - } - } catch (error) { - logger.error(`failed to send bug report to channel: ${error}`); - } - } + return { guildData, channel }; +} - await interaction.editReply({ - content: `✅ bug report submitted! ${msgUrl}`, - }); - } catch (error) { - logger.error("failed to create bug report:", error); - await interaction.editReply({ - content: "❌ failed to submit bug report. please try again later.", - }); +function parseModalCustomId(customId: string): { type: string } { + const parts = customId.split(":"); + if (parts.length !== 2 || parts[0] !== "bug") { + throw new Error("Invalid modal submission format."); } + return { type: parts[1] }; } -export async function buttonExecute( - client: Client, - interaction: ButtonInteraction, -) { - const [, action, bugId] = interaction.customId.split(":"); +// containers +function buildBugContainer( + bug: InstanceType<typeof BugModel>, + projectKey: string, + userId: Snowflake, + mediaUrls: string[], +): ContainerBuilder { + const projectInfo = PROJECT_MAP[projectKey]; + if (!projectInfo) { + throw new Error(`Project ${projectKey} not found in PROJECT_MAP`); + } - if (action === "new") { - const options = Object.entries(PROJECT_MAP).map(([key, project]) => - new StringSelectMenuOptionBuilder() - .setLabel(project.displayName) - .setValue(key), - ); + const textContent = `### ${bug.title}\n${bug.description}`; + const footerContent = [ + `-# #${bug.bug_id}`, + projectInfo.displayName, + `Reported by <@${userId}>`, + ].join(" • "); - const selectMenu = new StringSelectMenuBuilder() - .setCustomId("bug:project-select") - .setPlaceholder(`select a ${config.data.terminology}`) - .addOptions(...options); + const container = new ContainerBuilder().addSectionComponents( + new SectionBuilder() + .setThumbnailAccessory( + new ThumbnailBuilder().setURL(projectInfo.iconURL || ""), + ) + .addTextDisplayComponents( + new TextDisplayBuilder().setContent(textContent), + ), + ); - const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents( - selectMenu, + if (mediaUrls.length > 0) { + container.addMediaGalleryComponents( + new MediaGalleryBuilder().addItems( + ...mediaUrls.map((url) => new MediaGalleryItemBuilder().setURL(url)), + ), ); - - await interaction.reply({ - content: `select a ${config.data.terminology} to report a bug for:`, - components: [row], - flags: ["Ephemeral"], - }); - return; } - const member = interaction.member as GuildMember; - const isManager = await hasManagerPermissions(member); + container.addTextDisplayComponents( + new TextDisplayBuilder().setContent(footerContent), + ); - const bug = await BugModel.findOne({ bug_id: Number.parseInt(bugId) }); - if (!bug) { - await interaction.reply({ - content: "❌ bug not found.", - flags: ["Ephemeral"], - }); - return; - } + return container; +} - const isAuthor = bug.user_id === interaction.user.id; - - if (!isManager && !isAuthor) { - await interaction.reply({ - content: "❌ you don't have permission to do this.", - flags: ["Ephemeral"], - }); - return; - } +// main +export async function execute( + _client: Client, + interaction: ChatInputCommandInteraction, +): Promise<void> { + const modal = buildReportModal(); + await interaction.showModal(modal); +} - switch (action) { - case "close": { - const newStatus = bug.status === "closed" ? "open" : "closed"; - bug.status = newStatus; - await bug.save(); +// modal submission handling +export async function modalExecute( + client: Client, + interaction: ModalSubmitInteraction, +): Promise<void> { + try { + if (!interaction.guild) { + await interaction.reply({ + content: "❌ This command can only be used in a server.", + flags: ["Ephemeral"], + }); + return; + } - if (bug.message_id && interaction.guild) { - const guild = await GuildModel.findOne({ - guild_id: interaction.guild.id, - }); - if (guild?.bug_channel) { - await updateBugEmbed(client, bug, bug.message_id, guild.bug_channel); - } - } + const { type } = parseModalCustomId(interaction.customId); + if (type === "edit") { await interaction.reply({ - content: `✅ bug #${bug.bug_id} ${newStatus === "closed" ? "closed" : "reopened"}.`, + content: "❌ Editing bugs is not implemented yet.", flags: ["Ephemeral"], }); - break; + return; } - case "edit": { - if (bug.status === "closed") { - await interaction.reply({ - content: "❌ cannot edit a closed bug.", - flags: ["Ephemeral"], - }); - return; - } + const { channel } = await validateGuildSetup(interaction); + await createUserIfNotExists(interaction.user.id, interaction.guild.id); + + // collect inputs & defer + const title = interaction.fields.getTextInputValue(INPUT_IDS.TITLE); + const description = interaction.fields.getTextInputValue( + INPUT_IDS.DESCRIPTION, + ); + const affected = interaction.fields + .getStringSelectValues(INPUT_IDS.AFFECTED) + .join(""); + const mediaFiles = interaction.fields.getUploadedFiles(INPUT_IDS.MEDIA); - const modal = new ModalBuilder() - .setCustomId(`bug:edit:${bug.bug_id}`) - .setTitle(`edit bug #${bug.bug_id}`); + await interaction.deferReply({ flags: ["Ephemeral"] }); - const titleInput = new TextInputBuilder() - .setCustomId("title") - .setLabel("bug title") - .setStyle(TextInputStyle.Short) - .setPlaceholder("short description of the bug") - .setRequired(true) - .setMaxLength(100) - .setValue(bug.title); + // create record + const bugId = await getNextBugId(); + const bug = new BugModel({ + bug_id: bugId, + guild_id: interaction.guild.id, + user_id: interaction.user.id, + title, + description, + projects: affected, + status: "open", + sent: false, + }); + await bug.save(); - const descriptionInput = new TextInputBuilder() - .setCustomId("description") - .setLabel("bug description") - .setStyle(TextInputStyle.Paragraph) - .setPlaceholder( - "detailed description of the bug and steps to reproduce", - ) - .setRequired(true) - .setMaxLength(1000) - .setValue(bug.description); + logger.info("Bug report created", { + bugId, + userId: interaction.user.id, + guildId: interaction.guild.id, + }); - const firstRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - titleInput, - ); - const secondRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - descriptionInput, + // handle media uploads + let downloadedMedia: MediaType[] = []; + if (mediaFiles && mediaFiles.size > 0) { + const { media, totalSize } = await downloadAttachments( + mediaFiles, + bugId, + interaction.user.id, ); - modal.addComponents(firstRow, secondRow); - await interaction.showModal(modal); - break; + logger.info("Downloading attachments", { + bugId, + userId: interaction.user.id, + fileCount: mediaFiles.size, + totalSize, + }); + + await interaction.editReply({ + content: `⏳ Downloading ${mediaFiles.size} attachment(s) (${formatFileSize(totalSize)})... This may take a moment.`, + }); + + downloadedMedia = media; } - case "delete": { - bug.deleteOne(); + // build message container + const projectKey = affected.split(",")[0]; + const mediaUrls = mediaFiles + ? Array.from(mediaFiles.values()).map((att) => att.url) + : []; + const container = buildBugContainer( + bug, + projectKey, + interaction.user.id, + mediaUrls, + ); - if (bug.message_id && interaction.guild) { - const guild = await GuildModel.findOne({ - guild_id: interaction.guild.id, - }); - if (guild?.bug_channel) { - try { - const channel = await client.channels.fetch(guild.bug_channel); - if (channel?.isTextBased() && "messages" in channel) { - const message = await channel.messages.fetch(bug.message_id); - await message.delete(); - } - } catch (error) { - logger.error("failed to delete bug message:", error); - } - } - } + // send message to bug channel + const message = await channel.send({ + components: [container], + flags: "IsComponentsV2", + files: downloadedMedia.map((media) => + new AttachmentBuilder(media.data).setName( + `bug_${bugId}_media.${media.extension}`, + ), + ), + allowedMentions: { users: [] }, + }); - await interaction.reply({ - content: `✅ bug #${bug.bug_id} deleted.`, - flags: ["Ephemeral"], - }); - break; - } - } -} + // create thread + const threadName = `#${bugId} ${truncateTitle(title, VALIDATION.THREAD_NAME_MAX_LENGTH)}`; + const thread = await message.startThread({ + name: threadName, + autoArchiveDuration: VALIDATION.AUTO_ARCHIVE_DURATION, + reason: `Thread for bug report #${bugId}`, + }); -export async function selectMenuExecute( - _client: Client, - interaction: StringSelectMenuInteraction, -) { - if (interaction.customId !== "bug:project-select") return; + await thread.send({ + content: + "Use this space to discuss the bug, provide additional details, or ask questions.", + }); + await thread.members.add(interaction.user.id); - const project = interaction.values[0]; - const modal = new ModalBuilder() - .setCustomId(`bug:${project}`) - .setTitle(`report bug - ${getProjectName(project)}`); + // Update bug with message and thread IDs + bug.message_id = message.id; + bug.thread_id = thread.id; + await bug.save(); - const titleInput = new TextInputBuilder() - .setCustomId("title") - .setLabel("bug title") - .setStyle(TextInputStyle.Short) - .setPlaceholder("short description of the bug") - .setRequired(true) - .setMaxLength(100); + // Final success message + await interaction.editReply({ + content: `✅ Bug report #${bugId} has been submitted successfully! Check <#${channel.id}> for your report.`, + }); - const descriptionInput = new TextInputBuilder() - .setCustomId("description") - .setLabel("bug description") - .setStyle(TextInputStyle.Paragraph) - .setPlaceholder("detailed description of the bug and steps to reproduce") - .setRequired(true) - .setMaxLength(1000); + logger.info("Bug report submitted successfully", { + bugId, + messageId: message.id, + threadId: thread.id, + }); + } catch (error) { + logger.error("Failed to process bug report", { error }); - const firstRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - titleInput, - ); - const secondRow = new ActionRowBuilder<TextInputBuilder>().addComponents( - descriptionInput, - ); + const errorMessage = + error instanceof Error ? error.message : "An unknown error occurred."; - modal.addComponents(firstRow, secondRow); - await interaction.showModal(modal); + if (interaction.deferred || interaction.replied) { + await interaction.editReply({ + content: `❌ ${errorMessage}`, + }); + } else { + await interaction.reply({ + content: `❌ ${errorMessage}`, + flags: ["Ephemeral"], + }); + } + } } export const reportCommand = { execute, modalExecute, - buttonExecute, - selectMenuExecute, getProjectChoices, };