diff --git a/biome.json b/biome.json index 1bb4791..93aff74 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json", "vcs": { "enabled": true, "clientKind": "git", diff --git a/bun.lock b/bun.lock index 60fd324..698a829 100644 --- a/bun.lock +++ b/bun.lock @@ -6,58 +6,51 @@ "name": "qweribot", "dependencies": { "@fontsource/jersey-15": "^5.2.8", - "@twurple/api": "7.4.0", - "@twurple/auth": "^7.4.0", - "@twurple/eventsub-http": "^7.4.0", + "@twurple/api": "8.0.3", + "@twurple/auth": "^8.0.3", + "@twurple/eventsub-http": "^8.0.3", "discord.js": "^14.25.1", - "drizzle-orm": "^0.44.7", - "kleur": "^4.1.5", + "drizzle-orm": "^0.45.1", }, "devDependencies": { - "@biomejs/biome": "^2.3.8", - "@twurple/eventsub-ngrok": "^7.4.0", + "@biomejs/biome": "^2.3.13", + "@twurple/eventsub-ngrok": "^8.0.3", "@types/bun": "latest", "drizzle-kit": "^0.31.8", - "pg": "^8.16.3", + "pg": "^8.17.2", }, "peerDependencies": { - "typescript": "^5.8.3", + "typescript": "^5.9.3", }, }, }, "packages": { - "@biomejs/biome": ["@biomejs/biome@2.3.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.8", "@biomejs/cli-darwin-x64": "2.3.8", "@biomejs/cli-linux-arm64": "2.3.8", "@biomejs/cli-linux-arm64-musl": "2.3.8", "@biomejs/cli-linux-x64": "2.3.8", "@biomejs/cli-linux-x64-musl": "2.3.8", "@biomejs/cli-win32-arm64": "2.3.8", "@biomejs/cli-win32-x64": "2.3.8" }, "bin": { "biome": "bin/biome" } }, "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA=="], + "@biomejs/biome": ["@biomejs/biome@2.3.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.13", "@biomejs/cli-darwin-x64": "2.3.13", "@biomejs/cli-linux-arm64": "2.3.13", "@biomejs/cli-linux-arm64-musl": "2.3.13", "@biomejs/cli-linux-x64": "2.3.13", "@biomejs/cli-linux-x64-musl": "2.3.13", "@biomejs/cli-win32-arm64": "2.3.13", "@biomejs/cli-win32-x64": "2.3.13" }, "bin": { "biome": "bin/biome" } }, "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-AGr8OoemT/ejynbIu56qeil2+F2WLkIjn2d8jGK1JkchxnMUhYOfnqc9sVzcRxpG9Ycvw4weQ5sprRvtb7Yhcw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-xvOiFkrDNu607MPMBUQ6huHmBG1PZLOrqhtK6pXJW3GjfVqJg0Z/qpTdhXfcqWdSZHcT+Nct2fOgewZvytESkw=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-TUdDCSY+Eo/EHjhJz7P2GnWwfqet+lFxBZzGHldrvULr59AgahamLs/N85SC4+bdF86EhqDuuw9rYLvLFWWlXA=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-s+YsZlgiXNq8XkgHs6xdvKDFOj/bwTEevqEY6rC2I3cBHbxXYU1LOZstH3Ffw9hE5tE1sqT7U23C00MzkXztMw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-0bdwFVSbbM//Sds6OjtnmQGp4eUjOTt6kHvR/1P0ieR9GcTUAlPNvPC3DiavTqq302W34Ae2T6u5VVNGuQtGlQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-QweDxY89fq0VvrxME+wS/BXKmqMrOTZlN9SqQ79kQSIc3FrEwvW/PvUegQF6XIVaekncDykB5dzPqjbwSKs9DA=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.13", "", { "os": "win32", "cpu": "x64" }, "sha512-trDw2ogdM2lyav9WFQsdsfdVy1dvZALymRpgmWsvSez0BJzBjulhOT/t+wyKeh3pZWvwP3VMs1SoOKwO3wecMQ=="], "@d-fischer/cache-decorators": ["@d-fischer/cache-decorators@4.0.1", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-HNYLBLWs/t28GFZZeqdIBqq8f37mqDIFO6xNPof94VjpKvuP6ROqCZGafx88dk5zZUlBfViV9jD8iNNlXfc4CA=="], - "@d-fischer/cross-fetch": ["@d-fischer/cross-fetch@5.0.5", "", { "dependencies": { "node-fetch": "^2.6.12" } }, "sha512-symjDUPInTrkfIsZc2n2mo9hiAJLcTJsZkNICjZajEWnWpJ3s3zn50/FY8xpNUAf5w3eFuQii2wxztTGpvG1Xg=="], - "@d-fischer/detect-node": ["@d-fischer/detect-node@3.0.1", "", {}, "sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w=="], "@d-fischer/logger": ["@d-fischer/logger@4.2.3", "", { "dependencies": { "@d-fischer/detect-node": "^3.0.1", "@d-fischer/shared-utils": "^3.2.0", "tslib": "^2.0.3" } }, "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw=="], - "@d-fischer/qs": ["@d-fischer/qs@7.0.2", "", {}, "sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ=="], - "@d-fischer/rate-limiter": ["@d-fischer/rate-limiter@1.1.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.3", "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-O5HgACwApyCZhp4JTEBEtbv/W3eAwEkrARFvgWnEsDmXgCMWjIHwohWoHre5BW6IYXFSHBGsuZB/EvNL3942kQ=="], - "@d-fischer/raw-body": ["@d-fischer/raw-body@2.4.3", "", { "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.3", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-rtPTezQLROnTDdRij0Vo5OJ41aGvfKj9pQ7CkzFssQy+Jyc9BUVLV/DXLIGgvEGUaWt09Jq3im4WgvvPYqTomw=="], - "@d-fischer/shared-utils": ["@d-fischer/shared-utils@3.6.4", "", { "dependencies": { "tslib": "^2.4.1" } }, "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw=="], "@d-fischer/typed-event-emitter": ["@d-fischer/typed-event-emitter@3.3.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ=="], @@ -168,23 +161,23 @@ "@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="], - "@twurple/api": ["@twurple/api@7.4.0", "", { "dependencies": { "@d-fischer/cache-decorators": "^4.0.0", "@d-fischer/cross-fetch": "^5.0.1", "@d-fischer/detect-node": "^3.0.1", "@d-fischer/logger": "^4.2.1", "@d-fischer/rate-limiter": "^1.1.0", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "7.4.0", "@twurple/common": "7.4.0", "retry": "^0.13.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/auth": "7.4.0" } }, "sha512-RlXLs4ZvS8n0+iIk7YyVDwrjhlwpn+N+h7fX5Q61HoxlmzoCShmnnFo03abYw9i8Cc3deGpbQATOSVmigXM4qg=="], + "@twurple/api": ["@twurple/api@8.0.3", "", { "dependencies": { "@d-fischer/cache-decorators": "^4.0.0", "@d-fischer/detect-node": "^3.0.1", "@d-fischer/logger": "^4.2.1", "@d-fischer/rate-limiter": "^1.1.0", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "8.0.3", "@twurple/common": "8.0.3", "retry": "^0.13.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/auth": "8.0.3" } }, "sha512-vnqVi9YlNDbCqgpUUvTIq4sDitKCY0dkTw9zPluZvRNqUB1eCsuoaRNW96HQDhKtA9P4pRzwZ8xU7v/1KU2ytg=="], - "@twurple/api-call": ["@twurple/api-call@7.4.0", "", { "dependencies": { "@d-fischer/cross-fetch": "^5.0.1", "@d-fischer/qs": "^7.0.2", "@d-fischer/shared-utils": "^3.6.1", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-WNxvjp/hMqZREElbvE4rHMyUIrHdGY5cbG8xbqgSM9CESFvJ1wm5BubhyANOyKd1TxABacLddbfbO//Fz9YHgA=="], + "@twurple/api-call": ["@twurple/api-call@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-/5DBTqFjpYB+qqOkkFzoTWE79a7+I8uLXmBIIIYjGoq/CIPxKcHnlemXlU8cQhTr87PVa3th8zJXGYiNkpRx8w=="], - "@twurple/auth": ["@twurple/auth@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "7.4.0", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-WAQV6nJGkfY7r2BkRYhnzUpdfozLvjNsCxkyNVprl4dCWdJzccnTvqkKTdDRJc5ZJxDVaB9Drzwx9/fCp/gRDA=="], + "@twurple/auth": ["@twurple/auth@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "8.0.3", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-Xlv+WNXmGQir4aBXYeRCqdno5XurA6jzYTIovSEHa7FZf3AMHMFqtzW7yqTCUn4iOahfUSA2TIIxmxFM0wis0g=="], - "@twurple/common": ["@twurple/common@7.4.0", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "klona": "^2.0.4", "tslib": "^2.0.3" } }, "sha512-lX5cVkYar6jGvni6iLmMYjhxH1oPSl2v7XVeZ4C7U1GbLz/Jwk0L0uldQNGUIf9gpRHPY+TXRlk0UIpz2yo8DA=="], + "@twurple/common": ["@twurple/common@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "klona": "^2.0.4", "tslib": "^2.0.3" } }, "sha512-JQ2lb5qSFT21Y9qMfIouAILb94ppedLHASq49Fe/AP8oq0k3IC9Q7tX2n6tiMzGWqn+n8MnONUpMSZ6FhulMXA=="], - "@twurple/eventsub-base": ["@twurple/eventsub-base@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/api": "7.4.0", "@twurple/auth": "7.4.0", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-Umx0kNZKxBUTF2/MHAlnnCuNPs8Tl1Aw8EzDJI2AW10tOiWvgeCR889fKCFBPlHXvcMYSEvsItkX+pXeZ8GkeQ=="], + "@twurple/eventsub-base": ["@twurple/eventsub-base@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/api": "8.0.3", "@twurple/auth": "8.0.3", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-59G5xJbHWLTSO6NAgwtkHPfIlmdjrABgiEumFnHhNusMbLM9qdA+kLcW5NB2NImNliytl6zZtqY92FInzUE6NA=="], - "@twurple/eventsub-http": ["@twurple/eventsub-http@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/raw-body": "^2.4.3", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/auth": "7.4.0", "@twurple/common": "7.4.0", "@twurple/eventsub-base": "7.4.0", "@types/express-serve-static-core": "^4.17.24", "httpanda": "^0.4.6", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "7.4.0" } }, "sha512-Ua8cP4OPfgyUxlNG2BSJ2Ck02Axk3YXIBoQqoTURlSI0wix8+kTK0X4QuDvxicPxn9iRV1prNimOSvt4HXSkrQ=="], + "@twurple/eventsub-http": ["@twurple/eventsub-http@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/auth": "8.0.3", "@twurple/common": "8.0.3", "@twurple/eventsub-base": "8.0.3", "@types/express-serve-static-core": "^5.1.0", "httpanda": "^0.4.6", "raw-body": "^3.0.2", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "8.0.3" } }, "sha512-ds8l01GfsIC0hhILepv/UUn/Ix8s0wLg9aGy10xWaG9/Hlfe82NPI8gAg0LYsmlCsOADPwJZSckMTGPJrpw1Iw=="], - "@twurple/eventsub-ngrok": ["@twurple/eventsub-ngrok@7.4.0", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@ngrok/ngrok": "^0.5.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "7.4.0", "@twurple/eventsub-http": "7.4.0" } }, "sha512-YPk4TtYmCFQwBuIUgEpd6D29wqhJJQq6fxjWG83E86lp3vfcaY6aq7kE39kSqTz8TDkx62xnSH9lsMmZImIQ0w=="], + "@twurple/eventsub-ngrok": ["@twurple/eventsub-ngrok@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@ngrok/ngrok": "^0.5.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "8.0.3", "@twurple/eventsub-http": "8.0.3" } }, "sha512-wt4keLIivnEpv0EpQw1zgBD6tinaDmVf5VhvQqr8NABCpL4TuZNQAIveIUelHmY+phlISIX/42mvXqHNfmMTwg=="], - "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="], - "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg=="], + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="], "@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="], @@ -200,13 +193,13 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="], - "bytes": ["bytes@3.1.0", "", {}, "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "discord-api-types": ["discord-api-types@0.38.36", "", {}, "sha512-qrbUbjjwtyeBg5HsAlm1C859epfOyiLjPqAOzkdWlCNsZCWJrertnETF/NwM8H+waMFU58xGSc5eXUfXah+WTQ=="], @@ -214,7 +207,7 @@ "drizzle-kit": ["drizzle-kit@0.31.8", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="], - "drizzle-orm": ["drizzle-orm@0.44.7", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ=="], + "drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="], "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], @@ -224,16 +217,14 @@ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], - "http-errors": ["http-errors@1.7.3", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } }, "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "httpanda": ["httpanda@0.4.7", "", { "dependencies": { "@types/node": "^14.11.2", "tslib": "^2.0.3" } }, "sha512-NieTiR7kfOheL9OeEi6+JKFmJ2JP9ZRqUQ4tiXZ9J+EMMKxApHUQlEM5l4gZ+l67lxE9Er6oigZnujmhlodNCg=="], - "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], @@ -244,19 +235,17 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="], - "pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="], + "pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="], - "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], - - "pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="], + "pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="], "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], - "pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="], + "pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="], - "pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="], + "pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="], "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], @@ -270,13 +259,15 @@ "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "setprototypeof": ["setprototypeof@1.1.1", "", {}, "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -284,11 +275,9 @@ "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - "statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="], - - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], @@ -302,10 +291,6 @@ "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - - "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "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=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], diff --git a/package.json b/package.json index 9edb532..d433c6d 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "qweribot", "module": "src/index.ts", "devDependencies": { - "@biomejs/biome": "^2.3.8", - "@twurple/eventsub-ngrok": "^7.4.0", + "@biomejs/biome": "^2.3.13", + "@twurple/eventsub-ngrok": "^8.0.3", "@types/bun": "latest", "drizzle-kit": "^0.31.8", - "pg": "^8.16.3" + "pg": "^8.17.2" }, "scripts": { "check": "biome check && tsc -b", @@ -26,11 +26,10 @@ "type": "module", "dependencies": { "@fontsource/jersey-15": "^5.2.8", - "@twurple/api": "7.4.0", - "@twurple/auth": "^7.4.0", - "@twurple/eventsub-http": "^7.4.0", + "@twurple/api": "8.0.3", + "@twurple/auth": "^8.0.3", + "@twurple/eventsub-http": "^8.0.3", "discord.js": "^14.25.1", - "drizzle-orm": "^0.44.7", - "kleur": "^4.1.5" + "drizzle-orm": "^0.45.1" } } diff --git a/src/auth.ts b/src/auth.ts index 21e3ad7..233f8f6 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -9,7 +9,6 @@ import { getAuthRecord, updateAuthRecord, } from "db/dbAuth"; -import kleur from "kleur"; import logger from "lib/logger"; async function initAuth( @@ -26,15 +25,10 @@ async function initAuth( const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase(); // Generate random state variable to prevent cross-site-scripting attacks - const instruction = `Visit this URL as ${kleur - .red() - .underline() - .italic( - streamer ? "the streamer" : "the chatter", - )} to authenticate the bot.`; + const instruction = `Visit this URL as \x1b[3;4;1;95m${streamer ? "the streamer" : "the chatter"}\x1b[0;97m to authenticate the bot.`; logger.info(instruction); logger.info( - `https://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join("+")}&state=${state}`, + `\x1b[3;4;1;95mhttps://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join("+")}&state=${state}\x1b[0;97m`, ); const createCodePromise = () => { @@ -61,7 +55,7 @@ async function initAuth( return new Response("Successfully authenticated!"); } else { return new Response( - `Authentication attempt unsuccessful, please make sure the redirect url in the twitch developer console is set to ${redirectURL} and that the bot is listening to that url & port.`, + `Authentication attempt unsuccessful, please make sure the redirect url in the twitch developer console is set to \x1b[3;4;1;95m${redirectURL}\x1b[0;97m and that the bot is listening to that url & port.`, { status: 400 }, ); } @@ -112,13 +106,15 @@ export async function createAuthProvider( }); authData.onRefresh(async (user, token) => { - logger.ok(`Successfully refreshed auth for user ${user}`); + logger.ok( + `Successfully refreshed auth for user \x1b[3;4;1;95m${user}\x1b[0;97m`, + ); await updateAuthRecord(user, token); }); authData.onRefreshFailure((user, err) => { logger.err( - `Failed to refresh auth for user ${user}: ${err.name} ${err.message}`, + `Failed to refresh auth for user \x1b[3;4;1;95m${user}\x1b[0;97m: ${err.name} ${err.message}`, ); }); @@ -149,7 +145,7 @@ export async function createAuthProvider( await authData.refreshAccessTokenForUser(user.userId); } catch (_err) { logger.err( - `Failed to refresh user ${user.userId}. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`, + `Failed to refresh user \x1b[3;4;1;95m${user.userId}\x1b[0;97m. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`, ); await deleteAuthRecord(user.userId); process.exit(1); diff --git a/src/cheers/execute.ts b/src/cheers/execute.ts index b438406..bb98a08 100644 --- a/src/cheers/execute.ts +++ b/src/cheers/execute.ts @@ -1,6 +1,8 @@ import { Cheer, handleNoTarget } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createTimeoutRecord } from "db/dbTimeouts"; +import { + createCompensatedItemCheer, + createTimeoutEventCheer, +} from "db/CheerEvents"; import { getUserRecord } from "db/dbUser"; import { sendMessage } from "lib/commandUtils"; import { parseCheerArgs } from "lib/parseCommandArgs"; @@ -30,7 +32,8 @@ export default new Cheer({ } if (users.length === 0) { await sendMessage("No vulnerable chatters"); - await handleNoTarget(msg, user, ITEMNAME, true); + const compensated = await handleNoTarget(msg, user, ITEMNAME, true); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } target = users[Math.floor(Math.random() * users.length)]!; @@ -45,7 +48,8 @@ export default new Cheer({ target = await User.initUsername(args[0].toLowerCase()); } if (!target) { - await handleNoTarget(msg, user, ITEMNAME, false); + const compensated = await handleNoTarget(msg, user, ITEMNAME); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } await getUserRecord(target); @@ -60,8 +64,7 @@ export default new Cheer({ sendMessage( `KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`, ), - createTimeoutRecord(user, target, ITEMNAME), - createCheerEventRecord(user, ITEMNAME), + createTimeoutEventCheer(user, target, "execute"), playAlert({ name: "userExecution", user: user.displayName, @@ -69,7 +72,8 @@ export default new Cheer({ }), ]); else { - await handleNoTarget(msg, user, ITEMNAME); + const compensated = await handleNoTarget(msg, user, ITEMNAME); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); switch (result.reason) { case "banned": await sendMessage( diff --git a/src/cheers/grenade.ts b/src/cheers/grenade.ts index 61aa1c4..5e982c8 100644 --- a/src/cheers/grenade.ts +++ b/src/cheers/grenade.ts @@ -1,6 +1,8 @@ import { Cheer, handleNoTarget } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createTimeoutRecord } from "db/dbTimeouts"; +import { + createCompensatedItemCheer, + createTimeoutEventCheer, +} from "db/CheerEvents"; import { getUserRecord } from "db/dbUser"; import { sendMessage } from "lib/commandUtils"; import { redis } from "lib/redis"; @@ -18,7 +20,8 @@ export default new Cheer({ const targets = await redis.keys(`user:*:vulnerable`); if (targets.length === 0) { await sendMessage("No vulnerable chatters to blow up!", msg.messageId); - await handleNoTarget(msg, user, ITEMNAME); + const compensated = await handleNoTarget(msg, user, ITEMNAME, true); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } const selection = targets[Math.floor(Math.random() * targets.length)]!; @@ -32,8 +35,7 @@ export default new Cheer({ sendMessage( `wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`, ), - createTimeoutRecord(user, target!, ITEMNAME), - createCheerEventRecord(user, ITEMNAME), + createTimeoutEventCheer(user, target!, "grenade"), playAlert({ name: "grenadeExplosion", user: user.displayName, diff --git a/src/cheers/index.ts b/src/cheers/index.ts index 6ce372f..126d0d5 100644 --- a/src/cheers/index.ts +++ b/src/cheers/index.ts @@ -63,13 +63,13 @@ export async function handleNoTarget( user: User, itemname: items, silent = true, -) { +): Promise { if (await user.itemLock()) { await sendMessage( `Cannot give ${user.displayName} a ${itemname} (itemlock)`, msg.messageId, ); - return; + return false; } await user.setLock(); const userRecord = await getUserRecord(user); @@ -80,4 +80,5 @@ export async function handleNoTarget( ); await changeItemCount(user, userRecord, itemname, 1); await user.clearLock(); + return true; } diff --git a/src/cheers/realsilverbullet.ts b/src/cheers/realsilverbullet.ts index 06163db..65427b6 100644 --- a/src/cheers/realsilverbullet.ts +++ b/src/cheers/realsilverbullet.ts @@ -1,6 +1,5 @@ import { Cheer } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createTimeoutRecord } from "db/dbTimeouts"; +import { createTimeoutEventCheer } from "db/CheerEvents"; import { getUserRecord } from "db/dbUser"; import { sendMessage } from "lib/commandUtils"; import { parseCheerArgs } from "lib/parseCommandArgs"; @@ -57,8 +56,7 @@ export default new Cheer({ sendMessage( `KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`, ), - createTimeoutRecord(user, target, "realsilverbullet"), - createCheerEventRecord(user, "realsilverbullet"), + createTimeoutEventCheer(user, target, "realsilverbullet"), playAlert({ name: "userExecution", user: user.displayName, diff --git a/src/cheers/superloot.ts b/src/cheers/superloot.ts index 7783b04..e323775 100644 --- a/src/cheers/superloot.ts +++ b/src/cheers/superloot.ts @@ -1,6 +1,5 @@ import { Cheer } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createGetLootRecord } from "db/dbGetLoot"; +import { createSuperLootEvent } from "db/CheerEvents"; import { getUserRecord, updateUserRecord } from "db/dbUser"; import itemMap, { type inventory, type items } from "items"; import { sendMessage } from "lib/commandUtils"; @@ -12,10 +11,10 @@ export default new Cheer({ amount: 150, isItem: true, async execute(msg, user) { - if (!(await redis.exists("streamIsLive"))) { - await sendMessage(`No loot while stream is offline`, msg.messageId); - return; - } + // if (!(await redis.exists("streamIsLive"))) { + // await sendMessage(`No loot while stream is offline`, msg.messageId); + // return; + // } if (await user.itemLock()) { await sendMessage(`Cannot get loot (itemlock)`, msg.messageId); return; @@ -81,8 +80,7 @@ export default new Cheer({ await Promise.all([ updateUserRecord(user, userData), sendMessage(message, msg.messageId), - createCheerEventRecord(user, "superloot"), - createGetLootRecord(user, gainedqbucks, itemDiff, "superloot"), + createSuperLootEvent(user, gainedqbucks, itemDiff), user.clearLock(), ]); }, diff --git a/src/cheers/timeout.ts b/src/cheers/timeout.ts index 982cab5..b11db35 100644 --- a/src/cheers/timeout.ts +++ b/src/cheers/timeout.ts @@ -1,6 +1,8 @@ import { Cheer, handleNoTarget } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createTimeoutRecord } from "db/dbTimeouts"; +import { + createTimeoutEventCheer, + createCompensatedItemCheer, +} from "db/CheerEvents"; import { getUserRecord } from "db/dbUser"; import { sendMessage } from "lib/commandUtils"; import { parseCheerArgs } from "lib/parseCommandArgs"; @@ -17,12 +19,14 @@ export default new Cheer({ async execute(msg, user) { const args = parseCheerArgs(msg.messageText); if (!args[0]) { - await handleNoTarget(msg, user, ITEMNAME, false); + const compensated = await handleNoTarget(msg, user, ITEMNAME, false); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } const target = await User.initUsername(args[0].toLowerCase()); if (!target) { - await handleNoTarget(msg, user, ITEMNAME, false); + const compensated = await handleNoTarget(msg, user, ITEMNAME, false); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } await getUserRecord(target); @@ -37,8 +41,7 @@ export default new Cheer({ sendMessage( `GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`, ), - createTimeoutRecord(user, target, ITEMNAME), - createCheerEventRecord(user, ITEMNAME), + createTimeoutEventCheer(user, target, "timeout"), playAlert({ name: "userBlast", user: user.displayName, @@ -46,7 +49,8 @@ export default new Cheer({ }), ]); else { - await handleNoTarget(msg, user, ITEMNAME); + const compensated = await handleNoTarget(msg, user, ITEMNAME); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); switch (result.reason) { case "banned": await sendMessage( diff --git a/src/cheers/tnt.ts b/src/cheers/tnt.ts index 997cf4f..13abbcf 100644 --- a/src/cheers/tnt.ts +++ b/src/cheers/tnt.ts @@ -1,6 +1,8 @@ import { Cheer, handleNoTarget } from "cheers"; -import { createCheerEventRecord } from "db/dbCheerEvents"; -import { createTimeoutRecord } from "db/dbTimeouts"; +import { + createTimeoutEventCheer, + createCompensatedItemCheer, +} from "db/CheerEvents"; import { getUserRecord } from "db/dbUser"; import { getTNTTargets } from "items/tnt"; import { sendMessage } from "lib/commandUtils"; @@ -21,7 +23,8 @@ export default new Cheer({ .then((a) => a.map((b) => b.slice(5, -11))); if (vulntargets.length === 0) { await sendMessage("No vulnerable chatters to blow up", msg.messageId); - await handleNoTarget(msg, user, ITEMNAME); + const compensated = await handleNoTarget(msg, user, ITEMNAME, true); + if (compensated) await createCompensatedItemCheer(user, ITEMNAME); return; } const targets = getTNTTargets(vulntargets); @@ -36,13 +39,12 @@ export default new Cheer({ sendMessage( `wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`, ), - createTimeoutRecord(user, target!, ITEMNAME), ]); }), ); await Promise.all([ - createCheerEventRecord(user, ITEMNAME), + createTimeoutEventCheer(user, targets, "tnt"), playAlert({ name: "tntExplosion", user: user.displayName, diff --git a/src/commands/getloot.ts b/src/commands/getloot.ts index 695da76..77581a2 100644 --- a/src/commands/getloot.ts +++ b/src/commands/getloot.ts @@ -1,5 +1,5 @@ -import { createGetLootRecord } from "db/dbGetLoot"; import { getUserRecord, updateUserRecord } from "db/dbUser"; +import { createGetLootEvent } from "db/LootEvents"; import itemMap, { type inventory, type items } from "items"; import { Command, sendMessage } from "lib/commandUtils"; import { buildTimeString } from "lib/dateManager"; @@ -132,7 +132,7 @@ export default new Command({ await Promise.all([ updateUserRecord(user, userData), sendMessage(message, msg.messageId), - createGetLootRecord(user, gainedqbucks, itemDiff, "getloot"), + createGetLootEvent(user, gainedqbucks, itemDiff, "getloot"), user.clearLock(), ]); diff --git a/src/connectionCheck.ts b/src/connectionCheck.ts index 0d4be89..bd62e6f 100644 --- a/src/connectionCheck.ts +++ b/src/connectionCheck.ts @@ -19,7 +19,7 @@ export async function connectionCheck() { redisstatus = true; } catch {} logger.info( - `Currently using the "${process.env.NODE_ENV ?? "production"}" database`, + `Currently using the \x1b[3;4;1;95m"${process.env.NODE_ENV ?? "production"}"\x1b[0;97m database`, ); pgstatus ? logger.ok(`Postgresql status: good`) diff --git a/src/db/CheerEvents.ts b/src/db/CheerEvents.ts new file mode 100644 index 0000000..80fb3e7 --- /dev/null +++ b/src/db/CheerEvents.ts @@ -0,0 +1,151 @@ +import type { cheers } from "cheers"; +import db from "db/connection"; +import { cheerEvents, events, timeouts } from "db/schema"; +import type { inventory, items } from "items"; +import type User from "user"; +import { createGetLootEvent } from "./LootEvents"; +import { eq } from "drizzle-orm"; + +/** + * Use this function to create a cheer event with timeouts + * This can only be used if the cheer succeeded + * + * The target can either be a single User object or an array of targetIDs + */ +export async function createTimeoutEventCheer( + user: User, + target: User | string[], + event: cheers, +) { + const userInt = parseInt(user.id, 10); + return await db.transaction(async (tx) => { + const cheerEventRecord = await tx + .insert(cheerEvents) + .values({ + user: userInt, + event, + status: "success", + }) + .returning(); + + if (Array.isArray(target)) + target.map( + async (ripbozo) => + await tx.insert(timeouts).values({ + user: userInt, + target: parseInt(ripbozo, 10), + item: event, + cheer: cheerEventRecord[0]?.id, + }), + ); + else + await tx.insert(timeouts).values({ + user: userInt, + target: parseInt(target.id, 10), + item: event, + cheer: cheerEventRecord[0]?.id, + }); + + await tx.insert(events).values({ + user: userInt, + cheer: cheerEventRecord[0]?.id, + }); + + if (!cheerEventRecord[0]) { + tx.rollback(); + return false; + } + }); +} + +/** + * Use this function to create a cheer event without timeouts + * This can only be used if the cheer succeeded + */ +export async function createRegularEventCheer( + user: User, + event: cheers | items, +) { + const userInt = parseInt(user.id, 10); + return await db.transaction(async (tx) => { + const cheerEventRecord = await tx + .insert(cheerEvents) + .values({ + user: userInt, + event, + status: "success", + }) + .returning(); + + await tx.insert(events).values({ + user: userInt, + cheer: cheerEventRecord[0]?.id, + }); + + if (!cheerEventRecord[0]) { + tx.rollback(); + return false; + } + }); +} + +/** + * Use this function to create a cheer event where the user got an item after the cheer failed + */ +export async function createCompensatedItemCheer(user: User, item: items) { + const userInt = parseInt(user.id, 10); + return await db.transaction(async (tx) => { + const cheerEventRecord = await tx + .insert(cheerEvents) + .values({ user: userInt, event: item, status: "compensated" }) + .returning(); + + await tx.insert(events).values({ + user: userInt, + cheer: cheerEventRecord[0]?.id, + }); + + if (!cheerEventRecord[0]) { + tx.rollback(); + return false; + } + }); +} + +/** + * Because superloot is a special case for cheers, as the event needs to link to the getLoot table and the cheerEvents table, we have this special function + */ +export async function createSuperLootEvent( + user: User, + qbucks: number, + inventory: inventory, +) { + const eventRecord = await createGetLootEvent( + user, + qbucks, + inventory, + "superloot", + ); + + if (eventRecord === false) return; + + return await db.transaction(async (tx) => { + const cheerEventRecord = await tx + .insert(cheerEvents) + .values({ + user: parseInt(user.id, 10), + event: "superloot", + }) + .returning(); + + await tx + .update(events) + .set({ cheer: cheerEventRecord[0]?.id }) + .where(eq(events.id, eventRecord.id)); + + if (!cheerEventRecord[0]) { + tx.rollback(); + return false; + } + }); +} diff --git a/src/db/ItemEvents.ts b/src/db/ItemEvents.ts new file mode 100644 index 0000000..206f086 --- /dev/null +++ b/src/db/ItemEvents.ts @@ -0,0 +1,72 @@ +import db from "db/connection"; +import { events, timeouts, usedItems } from "db/schema"; +import type { items } from "items"; +import type User from "user"; + +/** + * Use this function for doing all item usages with timeouts + */ +export async function createTimeoutEventItem( + user: User, + target: User | string[], + item: items, +) { + const userInt = parseInt(user.id, 10); + return await db.transaction(async (tx) => { + const usedItemRecord = await tx + .insert(usedItems) + .values({ user: userInt, item }) + .returning(); + + if (Array.isArray(target)) + target.map( + async (ripbozo) => + await tx.insert(timeouts).values({ + user: userInt, + target: parseInt(ripbozo, 10), + item, + usedItem: usedItemRecord[0]?.id, + }), + ); + else + await tx.insert(timeouts).values({ + user: userInt, + target: parseInt(target.id, 10), + item, + usedItem: usedItemRecord[0]?.id, + }); + + await tx.insert(events).values({ + user: userInt, + usedItem: usedItemRecord[0]?.id, + }); + + if (!usedItemRecord[0]) { + tx.rollback(); + return false; + } + }); +} + +/** + * Use this function for doing all regular item usages (no timeouts) + */ +export async function createNormalEventItem(user: User, item: items) { + const userInt = parseInt(user.id, 10); + return await db.transaction(async (tx) => { + const usedItemRecord = await tx + .insert(usedItems) + .values({ user: userInt, item }) + .returning(); + + await tx.insert(events).values({ + user: userInt, + usedItem: usedItemRecord[0]?.id, + }); + + if (!usedItemRecord[0]) { + tx.rollback(); + return false; + } + }); +} diff --git a/src/db/LootEvents.ts b/src/db/LootEvents.ts new file mode 100644 index 0000000..7297716 --- /dev/null +++ b/src/db/LootEvents.ts @@ -0,0 +1,39 @@ +import db from "db/connection"; +import type { lootTriggers } from "db/schema"; +import { events, getLoots } from "db/schema"; +import type { inventory } from "items"; +import type User from "user"; + +export async function createGetLootEvent( + user: User, + qbucks: number, + inventory: inventory, + trigger: lootTriggers, +) { + return await db.transaction(async (tx) => { + const glRecord = await tx + .insert(getLoots) + .values({ + user: parseInt(user.id, 10), + qbucks: qbucks, + items: inventory, + trigger, + }) + .returning(); + + const eventRecord = await tx + .insert(events) + .values({ + user: parseInt(user.id, 10), + getLoot: glRecord[0]?.id, + }) + .returning(); + + if (!glRecord[0]) { + tx.rollback(); + return false; + } + + return eventRecord[0]!; + }); +} diff --git a/src/db/UndoEvent.ts b/src/db/UndoEvent.ts new file mode 100644 index 0000000..0a4e5a7 --- /dev/null +++ b/src/db/UndoEvent.ts @@ -0,0 +1,4 @@ +import db from "db/connection"; +import { events } from "db/schema"; +import type User from "user"; +import { desc, eq, and } from "drizzle-orm"; diff --git a/src/db/dbCheerEvents.ts b/src/db/dbCheerEvents.ts index 023e383..0d953cd 100644 --- a/src/db/dbCheerEvents.ts +++ b/src/db/dbCheerEvents.ts @@ -1,19 +1,8 @@ -import type { cheers } from "cheers"; import db from "db/connection"; import { cheerEvents } from "db/schema"; import { and, between, eq, type SQL } from "drizzle-orm"; -import type { items } from "items"; import type User from "user"; -export async function createCheerEventRecord( - user: User, - cheer: items | cheers, -): Promise { - await db - .insert(cheerEvents) - .values({ user: parseInt(user.id, 10), event: cheer }); -} - export async function getCheerEvents(user: User, monthData?: string) { let condition: SQL | undefined = eq( cheerEvents.user, diff --git a/src/db/dbGetLoot.ts b/src/db/dbGetLoot.ts deleted file mode 100644 index 7d34e48..0000000 --- a/src/db/dbGetLoot.ts +++ /dev/null @@ -1,18 +0,0 @@ -import db from "db/connection"; -import { getLoots, type lootTriggers } from "db/schema"; -import type { inventory } from "items"; -import type User from "user"; - -export async function createGetLootRecord( - user: User, - qbucks: number, - inventory: inventory, - trigger: lootTriggers, -) { - await db.insert(getLoots).values({ - user: parseInt(user.id, 10), - qbucks: qbucks, - items: inventory, - trigger, - }); -} diff --git a/src/db/dbTimeouts.ts b/src/db/dbTimeouts.ts index 292385d..acb288f 100644 --- a/src/db/dbTimeouts.ts +++ b/src/db/dbTimeouts.ts @@ -1,22 +1,8 @@ -import type { cheers } from "cheers"; import db from "db/connection"; import { timeouts } from "db/schema"; import { and, between, eq, type SQL } from "drizzle-orm"; -import type { items } from "items"; import type User from "user"; -export async function createTimeoutRecord( - user: User, - target: User, - item: items | cheers, -): Promise { - await db.insert(timeouts).values({ - user: parseInt(user.id, 10), - target: parseInt(target.id, 10), - item, - }); -} - export async function getTimeoutsAsUser(user: User, monthData?: string) { let condition: SQL | undefined = eq( timeouts.user, diff --git a/src/db/dbUsedItems.ts b/src/db/dbUsedItems.ts index 98eadef..aa16203 100644 --- a/src/db/dbUsedItems.ts +++ b/src/db/dbUsedItems.ts @@ -1,16 +1,8 @@ import db from "db/connection"; import { usedItems } from "db/schema"; import { and, between, eq, type SQL } from "drizzle-orm"; -import type { items } from "items"; import type User from "user"; -export async function createUsedItemRecord( - user: User, - item: items, -): Promise { - await db.insert(usedItems).values({ user: parseInt(user.id, 10), item }); -} - export async function getItemsUsed(user: User, monthData?: string) { let condition: SQL | undefined = eq( usedItems.user, diff --git a/src/db/schema.ts b/src/db/schema.ts index 29e12f4..08a3069 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -33,6 +33,7 @@ export const usersRelations = relations(users, ({ many }) => ({ cheers: many(cheers), anivTimeouts: many(anivTimeouts), getLoots: many(getLoots), + events: many(events), })); export const timeouts = pgTable("timeouts", { @@ -45,6 +46,8 @@ export const timeouts = pgTable("timeouts", { .references(() => users.id), item: varchar().$type().notNull(), created: timestamp().defaultNow().notNull(), + cheer: uuid().references(() => cheerEvents.id), + usedItem: uuid().references(() => usedItems.id), }); export const timeoutsRelations = relations(timeouts, ({ one }) => ({ @@ -58,6 +61,14 @@ export const timeoutsRelations = relations(timeouts, ({ one }) => ({ references: [users.id], relationName: "target", }), + cheer: one(cheerEvents, { + fields: [timeouts.cheer], + references: [cheerEvents.id], + }), + usedItem: one(usedItems, { + fields: [timeouts.usedItem], + references: [usedItems.id], + }), })); export const usedItems = pgTable("usedItems", { @@ -69,27 +80,36 @@ export const usedItems = pgTable("usedItems", { created: timestamp().defaultNow().notNull(), }); -export const usedItemsRelations = relations(usedItems, ({ one }) => ({ +export const usedItemsRelations = relations(usedItems, ({ one, many }) => ({ user: one(users, { fields: [usedItems.user], references: [users.id], }), + timeouts: many(timeouts), })); +/** + * "success" when everything works + * "compensated" when the user gets an item in their inventory for a cheer + */ +export type cheerEventStatus = "success" | "compensated"; + export const cheerEvents = pgTable("cheerEvents", { id: uuid().defaultRandom().primaryKey(), user: integer() .notNull() .references(() => users.id), event: varchar().$type().notNull(), + status: varchar().$type().default("success").notNull(), created: timestamp().defaultNow().notNull(), }); -export const cheerEventsRelations = relations(cheerEvents, ({ one }) => ({ +export const cheerEventsRelations = relations(cheerEvents, ({ one, many }) => ({ user: one(users, { fields: [cheerEvents.user], references: [users.id], }), + timeouts: many(timeouts), })); export const cheers = pgTable("cheers", { @@ -146,3 +166,33 @@ export const getLootsRelations = relations(getLoots, ({ one }) => ({ references: [users.id], }), })); + +export const events = pgTable("events", { + id: uuid().defaultRandom().primaryKey(), + user: integer() + .notNull() + .references(() => users.id), + created: timestamp().defaultNow().notNull(), + usedItem: uuid().references(() => usedItems.id), + cheer: uuid().references(() => cheerEvents.id), + getLoot: uuid().references(() => getLoots.id), +}); + +export const eventsRelations = relations(events, ({ one }) => ({ + user: one(users, { + fields: [events.user], + references: [users.id], + }), + usedItem: one(usedItems, { + fields: [events.usedItem], + references: [usedItems.id], + }), + cheer: one(cheerEvents, { + fields: [events.cheer], + references: [cheerEvents.id], + }), + getLoot: one(getLoots, { + fields: [events.getLoot], + references: [getLoots.id], + }), +})); diff --git a/src/events/index.ts b/src/events/index.ts index 139ff61..df40c0c 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,34 +1,33 @@ import { api, eventSub } from "index"; -import kleur from "kleur"; import logger from "lib/logger"; eventSub.onRevoke((event) => { logger.ok( - `Successfully revoked EventSub subscription: ${kleur.underline(event.id)}`, + `Successfully revoked EventSub subscription: \x1b[3;4;1;95m${event.id}`, ); }); eventSub.onSubscriptionCreateSuccess((event) => { logger.ok( - `Successfully created EventSub subscription: ${kleur.underline(event.id)}`, + `Successfully created EventSub subscription: \x1b[3;4;1;95m${event.id}`, ); }); eventSub.onSubscriptionCreateFailure((event) => { logger.err( - `Failed to create EventSub subscription: ${kleur.underline(event.id)}`, + `Failed to create EventSub subscription: \x1b[3;4;4;95m${event.id}`, ); }); eventSub.onSubscriptionDeleteSuccess((event) => { logger.ok( - `Successfully deleted EventSub subscription: ${kleur.underline(event.id)}`, + `Successfully deleted EventSub subscription: \x1b[3;4;1;95m${event.id}`, ); }); eventSub.onSubscriptionDeleteFailure((event) => { logger.err( - `Failed to delete EventSub subscription: ${kleur.underline(event.id)}`, + `Failed to delete EventSub subscription: \x1b[3;4;1;95m${event.id}`, ); }); diff --git a/src/events/subscription.ts b/src/events/subscription.ts index 076746c..92c42e2 100644 --- a/src/events/subscription.ts +++ b/src/events/subscription.ts @@ -48,7 +48,7 @@ eventSub.onChannelSubscription(streamerId, async (msg) => { }); eventSub.onChannelSubscriptionGift(streamerId, async (msg) => { - if (msg.isAnonymous) { + if (msg.gifterName === null) { switch (msg.tier) { case "1000": await sendMessage( diff --git a/src/index.ts b/src/index.ts index 66dbc21..4140392 100644 --- a/src/index.ts +++ b/src/index.ts @@ -161,7 +161,7 @@ for (const ban of banned) { Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1, ); logger.info( - `Set the timeout of ${ban.userDisplayName} in the Redis/Valkey database.`, + `Set the timeout of \x1b[3;4;1;95m${ban.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`, ); } } @@ -170,7 +170,7 @@ const mods = await api.moderation.getModerators(streamerId).then((a) => a.data); for (const mod of mods) { await redis.set(`user:${mod.userId}:mod`, "1"); logger.info( - `Set the mod status of ${mod.userDisplayName} in the Redis/Valkey database.`, + `Set the mod status of \x1b[3;4;1;95m${mod.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`, ); } @@ -185,7 +185,7 @@ for (const remod of bannedmods) { duration = Math.floor((durationdata * 1000 - Date.now()) / 1000); remodMod(target!, duration); logger.info( - `Set the remod timer for ${target?.displayName} to ${duration} seconds.`, + `Set the remod timer for \x1b[3;4;1;95m${target?.displayName}\x1b[0;97m to \x1b[3;4;1;95m${duration}\x1b[0;97m seconds.`, ); } diff --git a/src/items/blaster.ts b/src/items/blaster.ts index 2f7cb41..5e620f3 100644 --- a/src/items/blaster.ts +++ b/src/items/blaster.ts @@ -1,6 +1,5 @@ -import { createTimeoutRecord } from "db/dbTimeouts"; -import { createUsedItemRecord } from "db/dbUsedItems"; import { getUserRecord } from "db/dbUser"; +import { createTimeoutEventItem } from "db/ItemEvents"; import { changeItemCount, Item } from "items"; import { sendMessage } from "lib/commandUtils"; import parseCommandArgs from "lib/parseCommandArgs"; @@ -57,8 +56,7 @@ export default new Item({ `GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`, ), changeItemCount(user, userObj, ITEMNAME), - createTimeoutRecord(user, target, ITEMNAME), - createUsedItemRecord(user, ITEMNAME), + createTimeoutEventItem(user, target, ITEMNAME), playAlert({ name: "userBlast", user: user.displayName, diff --git a/src/items/grenade.ts b/src/items/grenade.ts index 0e3592a..56fab77 100644 --- a/src/items/grenade.ts +++ b/src/items/grenade.ts @@ -1,6 +1,5 @@ -import { createTimeoutRecord } from "db/dbTimeouts"; -import { createUsedItemRecord } from "db/dbUsedItems"; import { getUserRecord } from "db/dbUser"; +import { createTimeoutEventItem } from "db/ItemEvents"; import { changeItemCount, Item } from "items"; import { sendMessage } from "lib/commandUtils"; import { redis } from "lib/redis"; @@ -47,8 +46,7 @@ export default new Item({ `wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`, ), changeItemCount(user, userObj, ITEMNAME), - createTimeoutRecord(user, target!, ITEMNAME), - createUsedItemRecord(user, ITEMNAME), + createTimeoutEventItem(user, target!, ITEMNAME), playAlert({ name: "grenadeExplosion", user: user.displayName, diff --git a/src/items/silverbullet.ts b/src/items/silverbullet.ts index 6b38be0..ee7c74c 100644 --- a/src/items/silverbullet.ts +++ b/src/items/silverbullet.ts @@ -1,6 +1,5 @@ -import { createTimeoutRecord } from "db/dbTimeouts"; -import { createUsedItemRecord } from "db/dbUsedItems"; import { getUserRecord } from "db/dbUser"; +import { createTimeoutEventItem } from "db/ItemEvents"; import { changeItemCount, Item } from "items"; import { sendMessage } from "lib/commandUtils"; import parseCommandArgs from "lib/parseCommandArgs"; @@ -89,12 +88,11 @@ export default new Item({ target: target.displayName, }), ]); - if (user.id !== streamerId) + if (user.id !== streamerId || process.env.NODE_ENV === "development") // streamer doesn't consume bullets and doesn't count for timeouts await Promise.all([ changeItemCount(user, userObj, ITEMNAME), - createTimeoutRecord(user, target, ITEMNAME), - createUsedItemRecord(user, ITEMNAME), + createTimeoutEventItem(user, target, ITEMNAME), ]); } else { switch (result.reason) { diff --git a/src/items/tnt.ts b/src/items/tnt.ts index 749d227..b9f6106 100644 --- a/src/items/tnt.ts +++ b/src/items/tnt.ts @@ -1,6 +1,5 @@ -import { createTimeoutRecord } from "db/dbTimeouts"; -import { createUsedItemRecord } from "db/dbUsedItems"; import { getUserRecord } from "db/dbUser"; +import { createTimeoutEventItem } from "db/ItemEvents"; import { changeItemCount, Item } from "items"; import { sendMessage } from "lib/commandUtils"; import { redis } from "lib/redis"; @@ -49,13 +48,12 @@ export default new Item({ sendMessage( `wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`, ), - createTimeoutRecord(user, target!, ITEMNAME), ]); }), ); await Promise.all([ - createUsedItemRecord(user, ITEMNAME), + createTimeoutEventItem(user, targets, ITEMNAME), playAlert({ name: "tntExplosion", user: user.displayName, diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 408af63..2be7d2c 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,18 +1,39 @@ -import kleur from "kleur"; - const logger = { err: (arg: string) => console.error( - kleur.red().bold().italic("[ERROR] ") + kleur.red().bold(arg), + Bun.wrapAnsi( + `\x1b[1;91m[ERROR]: \x1b[0;97m${arg}\x1b[0m`, + process.stdout.columns, + ), ), warn: (arg: string) => console.warn( - kleur.yellow().bold().italic("[WARN] ") + kleur.yellow().bold(arg), + Bun.wrapAnsi( + `\x1b[1;93m[WARN]: \x1b[0;97m${arg}\x1b[0m`, + process.stdout.columns, + ), ), info: (arg: string) => - console.info(kleur.white().bold().italic("[INFO] ") + kleur.white(arg)), - ok: (arg: string) => console.info(kleur.green().bold(arg)), - enverr: (arg: string) => logger.err(`Please provide a ${arg} in the .env`), + console.info( + Bun.wrapAnsi( + `\x1b[37;1m[INFO]: \x1b[0;97m${arg}\x1b[0m`, + process.stdout.columns, + ), + ), + ok: (arg: string) => + console.info( + Bun.wrapAnsi( + `\x1b[1;92m[OK]: \x1b[0;97m${arg}\x1b[0m`, + process.stdout.columns, + ), + ), + enverr: (arg: string) => + logger.err( + Bun.wrapAnsi( + `Please provide a \x1b[4;3;93m${arg}\x1b[0;97m in the .env`, + process.stdout.columns, + ), + ), }; export default logger; diff --git a/src/pointRedeems/index.ts b/src/pointRedeems/index.ts index 7a81b91..079fee6 100644 --- a/src/pointRedeems/index.ts +++ b/src/pointRedeems/index.ts @@ -88,7 +88,9 @@ for (const [_, redeem] of Array.from(namedRedeems)) { backgroundColor: redeem.color, userInputRequired: redeem.input, }); - logger.ok(`Created custom point redeem ${redeem.title}`); + logger.ok( + `Created custom point redeem \x1b[3;4;1;95m${redeem.title}\x1b[0;97m`, + ); idMap.set(redeem.name, creation.id); activeRedeems.set(creation.id, redeem); } @@ -97,7 +99,7 @@ for (const [_, redeem] of Array.from(namedRedeems)) { Array.from(currentRedeems).map(async ([title, redeem]) => { if (process.env.NODE_ENV !== "production") return; await api.channelPoints.deleteCustomReward(streamerId, redeem); - logger.ok(`Deleted custom point redeem ${title}`); + logger.ok(`Deleted custom point redeem \x1b[3;4;1;95m${title}\x1b[0;97m`); }); logger.ok("Successfully synced all custom point redeems"); @@ -108,7 +110,7 @@ export async function enableRedeem(redeem: PointRedeem, id: string) { isEnabled: true, }); activeRedeems.set(id, redeem); - logger.ok(`Enabled the ${redeem.name} point redeem`); + logger.ok(`Enabled the \x1b[3;4;1;95m${redeem.name}\x1b[0;97m point redeem`); } export async function disableRedeem(redeem: PointRedeem, id: string) { @@ -117,7 +119,7 @@ export async function disableRedeem(redeem: PointRedeem, id: string) { isEnabled: false, }); activeRedeems.delete(id); - logger.ok(`Disabled the ${redeem.name} point redeem`); + logger.ok(`Disabled the \x1b[3;4;1;95m${redeem.name}\x1b[0;97m point redeem`); } export { activeRedeems, idMap };