Compare commits

...

8 Commits

43 changed files with 558 additions and 230 deletions

View File

@@ -170,6 +170,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
`anivtimeouts`|Get the amount of timeouts, dodges and dodge percentage from aniv timeouts [(info)](#aniv-timeouts)|anyone|`anivtimeouts` `anivtimeout`|:white_check_mark: `anivtimeouts`|Get the amount of timeouts, dodges and dodge percentage from aniv timeouts [(info)](#aniv-timeouts)|anyone|`anivtimeouts` `anivtimeout`|:white_check_mark:
`racetime`|Get the racetime.gg room the streamer is currently in. Needs to have twitch linked to racetime account|anyone|`racetime` `raceroom`|:white_check_mark: `racetime`|Get the racetime.gg room the streamer is currently in. Needs to have twitch linked to racetime account|anyone|`racetime` `raceroom`|:white_check_mark:
`randomchatter`|Get a random chatter for whatever reason|moderators|`randomchatter`|:white_check_mark: `randomchatter`|Get a random chatter for whatever reason|moderators|`randomchatter`|:white_check_mark:
`economy`|Get a count of all items in circulation|anyone|`economy` `eco`|:white_check_mark:
### Qweribucks/Item commands ### Qweribucks/Item commands
@@ -225,7 +226,7 @@ NAME|COMMAND|FUNCTION|ALIASES|COST
-|-|-|-|- -|-|-|-|-
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100 Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99 Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666 Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}` `{fuck}`|666
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000 TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
## Cheers ## Cheers
@@ -250,4 +251,4 @@ NAME|COST|DESCRIPTION|INTERNALNAME
`Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse` `Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse`
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream` `Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
`Factorio Building Destroyed`|100|Sound: Factorio Building Destroyed alert|`sfxfactorioalert` `Factorio Building Destroyed`|100|Sound: Factorio Building Destroyed alert|`sfxfactorioalert`
`Fail`|100|Sound: Either the sad trumpet or trombone meme sound|`sfxFail` `Fail`|100|Sound: Either the sad trumpet or trombone meme sound|`sfxfail`

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",

103
bun.lock
View File

@@ -6,58 +6,51 @@
"name": "qweribot", "name": "qweribot",
"dependencies": { "dependencies": {
"@fontsource/jersey-15": "^5.2.8", "@fontsource/jersey-15": "^5.2.8",
"@twurple/api": "7.4.0", "@twurple/api": "8.0.3",
"@twurple/auth": "^7.4.0", "@twurple/auth": "^8.0.3",
"@twurple/eventsub-http": "^7.4.0", "@twurple/eventsub-http": "^8.0.3",
"discord.js": "^14.24.0", "discord.js": "^14.25.1",
"drizzle-orm": "^0.44.6", "drizzle-orm": "^0.45.1",
"kleur": "^4.1.5",
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.3.8", "@biomejs/biome": "^2.3.13",
"@twurple/eventsub-ngrok": "^7.4.0", "@twurple/eventsub-ngrok": "^8.0.3",
"@types/bun": "latest", "@types/bun": "latest",
"drizzle-kit": "^0.31.5", "drizzle-kit": "^0.31.8",
"pg": "^8.16.3", "pg": "^8.17.2",
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.8.3", "typescript": "^5.9.3",
}, },
}, },
}, },
"packages": { "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/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/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/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/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/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=="], "@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=="], "@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=="], "@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=="], "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=="], "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=="], "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-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=="], "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=="], "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=="], "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=="], "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=="], "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
@@ -244,19 +235,17 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "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.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
"pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="],
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], "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=="], "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=="], "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=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "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=="], "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
@@ -284,11 +275,9 @@
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], "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=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
@@ -302,10 +291,6 @@
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "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=="], "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=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],

View File

@@ -8,7 +8,7 @@
in { in {
devShells."${system}" = { devShells."${system}" = {
default = pkgs.mkShell { default = pkgs.mkShell {
packages = with pkgs; [ bun nodejs deno ]; packages = with pkgs; [ bun biome ];
shellHook = '' shellHook = ''
echo Loaded the qweribot dev shell echo Loaded the qweribot dev shell
''; '';

View File

@@ -2,11 +2,11 @@
"name": "qweribot", "name": "qweribot",
"module": "src/index.ts", "module": "src/index.ts",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.3.8", "@biomejs/biome": "^2.3.13",
"@twurple/eventsub-ngrok": "^7.4.0", "@twurple/eventsub-ngrok": "^8.0.3",
"@types/bun": "latest", "@types/bun": "latest",
"drizzle-kit": "^0.31.8", "drizzle-kit": "^0.31.8",
"pg": "^8.16.3" "pg": "^8.17.2"
}, },
"scripts": { "scripts": {
"check": "biome check && tsc -b", "check": "biome check && tsc -b",
@@ -26,11 +26,10 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fontsource/jersey-15": "^5.2.8", "@fontsource/jersey-15": "^5.2.8",
"@twurple/api": "7.4.0", "@twurple/api": "8.0.3",
"@twurple/auth": "^7.4.0", "@twurple/auth": "^8.0.3",
"@twurple/eventsub-http": "^7.4.0", "@twurple/eventsub-http": "^8.0.3",
"discord.js": "^14.25.1", "discord.js": "^14.25.1",
"drizzle-orm": "^0.44.7", "drizzle-orm": "^0.45.1"
"kleur": "^4.1.5"
} }
} }

View File

@@ -9,7 +9,6 @@ import {
getAuthRecord, getAuthRecord,
updateAuthRecord, updateAuthRecord,
} from "db/dbAuth"; } from "db/dbAuth";
import kleur from "kleur";
import logger from "lib/logger"; import logger from "lib/logger";
async function initAuth( async function initAuth(
@@ -26,15 +25,10 @@ async function initAuth(
const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase(); const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase();
// Generate random state variable to prevent cross-site-scripting attacks // Generate random state variable to prevent cross-site-scripting attacks
const instruction = `Visit this URL as ${kleur const instruction = `Visit this URL as \x1b[3;4;1;95m${streamer ? "the streamer" : "the chatter"}\x1b[0;97m to authenticate the bot.`;
.red()
.underline()
.italic(
streamer ? "the streamer" : "the chatter",
)} to authenticate the bot.`;
logger.info(instruction); logger.info(instruction);
logger.info( 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 = () => { const createCodePromise = () => {
@@ -61,7 +55,7 @@ async function initAuth(
return new Response("Successfully authenticated!"); return new Response("Successfully authenticated!");
} else { } else {
return new Response( 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 }, { status: 400 },
); );
} }
@@ -112,13 +106,15 @@ export async function createAuthProvider(
}); });
authData.onRefresh(async (user, token) => { 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); await updateAuthRecord(user, token);
}); });
authData.onRefreshFailure((user, err) => { authData.onRefreshFailure((user, err) => {
logger.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); await authData.refreshAccessTokenForUser(user.userId);
} catch (_err) { } catch (_err) {
logger.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); await deleteAuthRecord(user.userId);
process.exit(1); process.exit(1);

View File

@@ -1,6 +1,8 @@
import { Cheer, handleNoTarget } from "cheers"; import { Cheer, handleNoTarget } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import {
import { createTimeoutRecord } from "db/dbTimeouts"; createCompensatedItemCheer,
createTimeoutEventCheer,
} from "db/CheerEvents";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { parseCheerArgs } from "lib/parseCommandArgs"; import { parseCheerArgs } from "lib/parseCommandArgs";
@@ -30,7 +32,8 @@ export default new Cheer({
} }
if (users.length === 0) { if (users.length === 0) {
await sendMessage("No vulnerable chatters"); 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; return;
} }
target = users[Math.floor(Math.random() * users.length)]!; target = users[Math.floor(Math.random() * users.length)]!;
@@ -45,7 +48,8 @@ export default new Cheer({
target = await User.initUsername(args[0].toLowerCase()); target = await User.initUsername(args[0].toLowerCase());
} }
if (!target) { if (!target) {
await handleNoTarget(msg, user, ITEMNAME, false); const compensated = await handleNoTarget(msg, user, ITEMNAME);
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
return; return;
} }
await getUserRecord(target); await getUserRecord(target);
@@ -60,8 +64,7 @@ export default new Cheer({
sendMessage( sendMessage(
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`, `KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
), ),
createTimeoutRecord(user, target, ITEMNAME), createTimeoutEventCheer(user, target, "execute"),
createCheerEventRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "userExecution", name: "userExecution",
user: user.displayName, user: user.displayName,
@@ -69,7 +72,8 @@ export default new Cheer({
}), }),
]); ]);
else { else {
await handleNoTarget(msg, user, ITEMNAME); const compensated = await handleNoTarget(msg, user, ITEMNAME);
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
switch (result.reason) { switch (result.reason) {
case "banned": case "banned":
await sendMessage( await sendMessage(

View File

@@ -1,6 +1,8 @@
import { Cheer, handleNoTarget } from "cheers"; import { Cheer, handleNoTarget } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import {
import { createTimeoutRecord } from "db/dbTimeouts"; createCompensatedItemCheer,
createTimeoutEventCheer,
} from "db/CheerEvents";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { redis } from "lib/redis"; import { redis } from "lib/redis";
@@ -18,7 +20,8 @@ export default new Cheer({
const targets = await redis.keys(`user:*:vulnerable`); const targets = await redis.keys(`user:*:vulnerable`);
if (targets.length === 0) { if (targets.length === 0) {
await sendMessage("No vulnerable chatters to blow up!", msg.messageId); 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; return;
} }
const selection = targets[Math.floor(Math.random() * targets.length)]!; const selection = targets[Math.floor(Math.random() * targets.length)]!;
@@ -32,8 +35,7 @@ export default new Cheer({
sendMessage( sendMessage(
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`, `wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
), ),
createTimeoutRecord(user, target!, ITEMNAME), createTimeoutEventCheer(user, target!, "grenade"),
createCheerEventRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "grenadeExplosion", name: "grenadeExplosion",
user: user.displayName, user: user.displayName,

View File

@@ -63,13 +63,13 @@ export async function handleNoTarget(
user: User, user: User,
itemname: items, itemname: items,
silent = true, silent = true,
) { ): Promise<boolean> {
if (await user.itemLock()) { if (await user.itemLock()) {
await sendMessage( await sendMessage(
`Cannot give ${user.displayName} a ${itemname} (itemlock)`, `Cannot give ${user.displayName} a ${itemname} (itemlock)`,
msg.messageId, msg.messageId,
); );
return; return false;
} }
await user.setLock(); await user.setLock();
const userRecord = await getUserRecord(user); const userRecord = await getUserRecord(user);
@@ -80,4 +80,5 @@ export async function handleNoTarget(
); );
await changeItemCount(user, userRecord, itemname, 1); await changeItemCount(user, userRecord, itemname, 1);
await user.clearLock(); await user.clearLock();
return true;
} }

View File

@@ -1,6 +1,5 @@
import { Cheer } from "cheers"; import { Cheer } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import { createTimeoutEventCheer } from "db/CheerEvents";
import { createTimeoutRecord } from "db/dbTimeouts";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { parseCheerArgs } from "lib/parseCommandArgs"; import { parseCheerArgs } from "lib/parseCommandArgs";
@@ -57,8 +56,7 @@ export default new Cheer({
sendMessage( sendMessage(
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`, `KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
), ),
createTimeoutRecord(user, target, "realsilverbullet"), createTimeoutEventCheer(user, target, "realsilverbullet"),
createCheerEventRecord(user, "realsilverbullet"),
playAlert({ playAlert({
name: "userExecution", name: "userExecution",
user: user.displayName, user: user.displayName,

View File

@@ -1,6 +1,5 @@
import { Cheer } from "cheers"; import { Cheer } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import { createSuperLootEvent } from "db/CheerEvents";
import { createGetLootRecord } from "db/dbGetLoot";
import { getUserRecord, updateUserRecord } from "db/dbUser"; import { getUserRecord, updateUserRecord } from "db/dbUser";
import itemMap, { type inventory, type items } from "items"; import itemMap, { type inventory, type items } from "items";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
@@ -12,10 +11,10 @@ export default new Cheer({
amount: 150, amount: 150,
isItem: true, isItem: true,
async execute(msg, user) { async execute(msg, user) {
if (!(await redis.exists("streamIsLive"))) { // if (!(await redis.exists("streamIsLive"))) {
await sendMessage(`No loot while stream is offline`, msg.messageId); // await sendMessage(`No loot while stream is offline`, msg.messageId);
return; // return;
} // }
if (await user.itemLock()) { if (await user.itemLock()) {
await sendMessage(`Cannot get loot (itemlock)`, msg.messageId); await sendMessage(`Cannot get loot (itemlock)`, msg.messageId);
return; return;
@@ -81,8 +80,7 @@ export default new Cheer({
await Promise.all([ await Promise.all([
updateUserRecord(user, userData), updateUserRecord(user, userData),
sendMessage(message, msg.messageId), sendMessage(message, msg.messageId),
createCheerEventRecord(user, "superloot"), createSuperLootEvent(user, gainedqbucks, itemDiff),
createGetLootRecord(user, gainedqbucks, itemDiff, "superloot"),
user.clearLock(), user.clearLock(),
]); ]);
}, },

View File

@@ -1,6 +1,8 @@
import { Cheer, handleNoTarget } from "cheers"; import { Cheer, handleNoTarget } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import {
import { createTimeoutRecord } from "db/dbTimeouts"; createTimeoutEventCheer,
createCompensatedItemCheer,
} from "db/CheerEvents";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { parseCheerArgs } from "lib/parseCommandArgs"; import { parseCheerArgs } from "lib/parseCommandArgs";
@@ -17,12 +19,14 @@ export default new Cheer({
async execute(msg, user) { async execute(msg, user) {
const args = parseCheerArgs(msg.messageText); const args = parseCheerArgs(msg.messageText);
if (!args[0]) { if (!args[0]) {
await handleNoTarget(msg, user, ITEMNAME, false); const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
return; return;
} }
const target = await User.initUsername(args[0].toLowerCase()); const target = await User.initUsername(args[0].toLowerCase());
if (!target) { if (!target) {
await handleNoTarget(msg, user, ITEMNAME, false); const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
return; return;
} }
await getUserRecord(target); await getUserRecord(target);
@@ -37,8 +41,7 @@ export default new Cheer({
sendMessage( sendMessage(
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`, `GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
), ),
createTimeoutRecord(user, target, ITEMNAME), createTimeoutEventCheer(user, target, "timeout"),
createCheerEventRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "userBlast", name: "userBlast",
user: user.displayName, user: user.displayName,
@@ -46,7 +49,8 @@ export default new Cheer({
}), }),
]); ]);
else { else {
await handleNoTarget(msg, user, ITEMNAME); const compensated = await handleNoTarget(msg, user, ITEMNAME);
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
switch (result.reason) { switch (result.reason) {
case "banned": case "banned":
await sendMessage( await sendMessage(

View File

@@ -1,6 +1,8 @@
import { Cheer, handleNoTarget } from "cheers"; import { Cheer, handleNoTarget } from "cheers";
import { createCheerEventRecord } from "db/dbCheerEvents"; import {
import { createTimeoutRecord } from "db/dbTimeouts"; createTimeoutEventCheer,
createCompensatedItemCheer,
} from "db/CheerEvents";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { getTNTTargets } from "items/tnt"; import { getTNTTargets } from "items/tnt";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
@@ -21,7 +23,8 @@ export default new Cheer({
.then((a) => a.map((b) => b.slice(5, -11))); .then((a) => a.map((b) => b.slice(5, -11)));
if (vulntargets.length === 0) { if (vulntargets.length === 0) {
await sendMessage("No vulnerable chatters to blow up", msg.messageId); 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; return;
} }
const targets = getTNTTargets(vulntargets); const targets = getTNTTargets(vulntargets);
@@ -36,13 +39,12 @@ export default new Cheer({
sendMessage( sendMessage(
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`, `wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
), ),
createTimeoutRecord(user, target!, ITEMNAME),
]); ]);
}), }),
); );
await Promise.all([ await Promise.all([
createCheerEventRecord(user, ITEMNAME), createTimeoutEventCheer(user, targets, "tnt"),
playAlert({ playAlert({
name: "tntExplosion", name: "tntExplosion",
user: user.displayName, user: user.displayName,

View File

@@ -40,7 +40,7 @@ export default new Command({
const selection = namedRedeems.get(args[0]); const selection = namedRedeems.get(args[0]);
if (!selection) { if (!selection) {
await sendMessage( await sendMessage(
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`, `Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
msg.messageId, msg.messageId,
); );
return; return;

22
src/commands/economy.ts Normal file
View File

@@ -0,0 +1,22 @@
import { getTotalItemCounts } from "db/dbUser";
import itemAliasMap from "items";
import { Command, sendMessage } from "lib/commandUtils";
export default new Command({
name: "economy",
aliases: ["economy", "eco"],
usertype: "chatter",
execution: async (msg) => {
const allitems = await getTotalItemCounts();
const itemList = Object.entries(allitems)
.sort(([, a], [, b]) => b - a)
.map(([item, count]) => {
const itemobj = itemAliasMap.get(item);
if (itemobj) return `${itemobj.prettyName}: ${count}`;
return `${item}: ${count}`; // Fallback if an item doesn't have their name as an alias
})
.join(" | ");
await sendMessage(`Total Items in circulation: ${itemList}`, msg.messageId);
},
});

View File

@@ -39,7 +39,7 @@ export default new Command({
const selection = namedRedeems.get(args[0]); const selection = namedRedeems.get(args[0]);
if (!selection) { if (!selection) {
await sendMessage( await sendMessage(
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`, `Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
msg.messageId, msg.messageId,
); );
return; return;

View File

@@ -12,7 +12,7 @@ export default new Command({
const args = parseCommandArgs(msg.messageText); const args = parseCommandArgs(msg.messageText);
if (!args[0]) { if (!args[0]) {
await sendMessage( await sendMessage(
`A full list of cheers can be found here: https://github.com/qwerinope/qweribot#cheers`, `A full list of cheers can be found here: https://gitlab.com/qwerinope/qweribot#cheers`,
msg.messageId, msg.messageId,
); );
return; return;

View File

@@ -12,7 +12,7 @@ export default new Command({
const args = parseCommandArgs(msg.messageText); const args = parseCommandArgs(msg.messageText);
if (!args[0]) { if (!args[0]) {
await sendMessage( await sendMessage(
`A full list of commands can be found here: https://github.com/qwerinope/qweribot#commands-1`, `A full list of commands can be found here: https://gitlab.com/qwerinope/qweribot#commands-1`,
msg.messageId, msg.messageId,
); );
return; return;

View File

@@ -1,5 +1,5 @@
import { createGetLootRecord } from "db/dbGetLoot";
import { getUserRecord, updateUserRecord } from "db/dbUser"; import { getUserRecord, updateUserRecord } from "db/dbUser";
import { createGetLootEvent } from "db/LootEvents";
import itemMap, { type inventory, type items } from "items"; import itemMap, { type inventory, type items } from "items";
import { Command, sendMessage } from "lib/commandUtils"; import { Command, sendMessage } from "lib/commandUtils";
import { buildTimeString } from "lib/dateManager"; import { buildTimeString } from "lib/dateManager";
@@ -132,8 +132,11 @@ export default new Command({
await Promise.all([ await Promise.all([
updateUserRecord(user, userData), updateUserRecord(user, userData),
sendMessage(message, msg.messageId), sendMessage(message, msg.messageId),
createGetLootRecord(user, gainedqbucks, itemDiff, "getloot"), createGetLootEvent(user, gainedqbucks, itemDiff, "getloot"),
user.clearLock(), user.clearLock(),
]); ]);
if (itemstrings.length === 0 && gainedqbucks < 100)
await sendMessage("YEOP THAT'S A SCAMBOX YEOP");
}, },
}); });

View File

@@ -25,6 +25,10 @@ export default new Command({
} }
const selection = items.get(messagequery[0].toLowerCase()); const selection = items.get(messagequery[0].toLowerCase());
if (messagequery[0].toLowerCase() === "lootbox") { if (messagequery[0].toLowerCase() === "lootbox") {
if (await redis.sismember("disabledcommands", "getloot")) {
await sendMessage("Lootboxes are currently disabled", msg.messageId);
return;
}
await getloot.execute(msg, user); await getloot.execute(msg, user);
return; return;
} }

View File

@@ -19,7 +19,7 @@ export async function connectionCheck() {
redisstatus = true; redisstatus = true;
} catch {} } catch {}
logger.info( 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 pgstatus
? logger.ok(`Postgresql status: good`) ? logger.ok(`Postgresql status: good`)

151
src/db/CheerEvents.ts Normal file
View File

@@ -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;
}
});
}

72
src/db/ItemEvents.ts Normal file
View File

@@ -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;
}
});
}

39
src/db/LootEvents.ts Normal file
View File

@@ -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]!;
});
}

4
src/db/UndoEvent.ts Normal file
View File

@@ -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";

View File

@@ -1,19 +1,8 @@
import type { cheers } from "cheers";
import db from "db/connection"; import db from "db/connection";
import { cheerEvents } from "db/schema"; import { cheerEvents } from "db/schema";
import { and, between, eq, type SQL } from "drizzle-orm"; import { and, between, eq, type SQL } from "drizzle-orm";
import type { items } from "items";
import type User from "user"; import type User from "user";
export async function createCheerEventRecord(
user: User,
cheer: items | cheers,
): Promise<void> {
await db
.insert(cheerEvents)
.values({ user: parseInt(user.id, 10), event: cheer });
}
export async function getCheerEvents(user: User, monthData?: string) { export async function getCheerEvents(user: User, monthData?: string) {
let condition: SQL<unknown> | undefined = eq( let condition: SQL<unknown> | undefined = eq(
cheerEvents.user, cheerEvents.user,

View File

@@ -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,
});
}

View File

@@ -1,22 +1,8 @@
import type { cheers } from "cheers";
import db from "db/connection"; import db from "db/connection";
import { timeouts } from "db/schema"; import { timeouts } from "db/schema";
import { and, between, eq, type SQL } from "drizzle-orm"; import { and, between, eq, type SQL } from "drizzle-orm";
import type { items } from "items";
import type User from "user"; import type User from "user";
export async function createTimeoutRecord(
user: User,
target: User,
item: items | cheers,
): Promise<void> {
await db.insert(timeouts).values({
user: parseInt(user.id, 10),
target: parseInt(target.id, 10),
item,
});
}
export async function getTimeoutsAsUser(user: User, monthData?: string) { export async function getTimeoutsAsUser(user: User, monthData?: string) {
let condition: SQL<unknown> | undefined = eq( let condition: SQL<unknown> | undefined = eq(
timeouts.user, timeouts.user,

View File

@@ -1,16 +1,8 @@
import db from "db/connection"; import db from "db/connection";
import { usedItems } from "db/schema"; import { usedItems } from "db/schema";
import { and, between, eq, type SQL } from "drizzle-orm"; import { and, between, eq, type SQL } from "drizzle-orm";
import type { items } from "items";
import type User from "user"; import type User from "user";
export async function createUsedItemRecord(
user: User,
item: items,
): Promise<void> {
await db.insert(usedItems).values({ user: parseInt(user.id, 10), item });
}
export async function getItemsUsed(user: User, monthData?: string) { export async function getItemsUsed(user: User, monthData?: string) {
let condition: SQL<unknown> | undefined = eq( let condition: SQL<unknown> | undefined = eq(
usedItems.user, usedItems.user,

View File

@@ -12,7 +12,8 @@ import {
type SQL, type SQL,
sql, sql,
} from "drizzle-orm"; } from "drizzle-orm";
import { itemarray } from "items"; import { itemarray, type items } from "items";
import { ANIVNAMES } from "lib/handleAnivMessage";
import type User from "user"; import type User from "user";
/** Use this function to both ensure existance and to retreive data */ /** Use this function to both ensure existance and to retreive data */
@@ -120,3 +121,25 @@ export async function getKDLeaderboard(monthData?: string) {
return result; return result;
} }
type ItemCounts = Record<items, number>;
export async function getTotalItemCounts(): Promise<ItemCounts> {
const allUsers = await db
.select({ username: users.username, inventory: users.inventory })
.from(users);
const filteredUsers = allUsers.filter(
(user) =>
!Array.from<string>(ANIVNAMES).includes(user.username.toLowerCase()),
);
const counts = itemarray.reduce((acc, item) => {
acc[item] = filteredUsers.reduce((sum, user) => {
return sum + (user.inventory[item] || 0);
}, 0);
return acc;
}, {} as ItemCounts);
return counts;
}

View File

@@ -33,6 +33,7 @@ export const usersRelations = relations(users, ({ many }) => ({
cheers: many(cheers), cheers: many(cheers),
anivTimeouts: many(anivTimeouts), anivTimeouts: many(anivTimeouts),
getLoots: many(getLoots), getLoots: many(getLoots),
events: many(events),
})); }));
export const timeouts = pgTable("timeouts", { export const timeouts = pgTable("timeouts", {
@@ -45,6 +46,8 @@ export const timeouts = pgTable("timeouts", {
.references(() => users.id), .references(() => users.id),
item: varchar().$type<items | cheertypes>().notNull(), item: varchar().$type<items | cheertypes>().notNull(),
created: timestamp().defaultNow().notNull(), created: timestamp().defaultNow().notNull(),
cheer: uuid().references(() => cheerEvents.id),
usedItem: uuid().references(() => usedItems.id),
}); });
export const timeoutsRelations = relations(timeouts, ({ one }) => ({ export const timeoutsRelations = relations(timeouts, ({ one }) => ({
@@ -58,6 +61,14 @@ export const timeoutsRelations = relations(timeouts, ({ one }) => ({
references: [users.id], references: [users.id],
relationName: "target", 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", { export const usedItems = pgTable("usedItems", {
@@ -69,27 +80,36 @@ export const usedItems = pgTable("usedItems", {
created: timestamp().defaultNow().notNull(), created: timestamp().defaultNow().notNull(),
}); });
export const usedItemsRelations = relations(usedItems, ({ one }) => ({ export const usedItemsRelations = relations(usedItems, ({ one, many }) => ({
user: one(users, { user: one(users, {
fields: [usedItems.user], fields: [usedItems.user],
references: [users.id], 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", { export const cheerEvents = pgTable("cheerEvents", {
id: uuid().defaultRandom().primaryKey(), id: uuid().defaultRandom().primaryKey(),
user: integer() user: integer()
.notNull() .notNull()
.references(() => users.id), .references(() => users.id),
event: varchar().$type<items | cheertypes>().notNull(), event: varchar().$type<items | cheertypes>().notNull(),
status: varchar().$type<cheerEventStatus>().default("success").notNull(),
created: timestamp().defaultNow().notNull(), created: timestamp().defaultNow().notNull(),
}); });
export const cheerEventsRelations = relations(cheerEvents, ({ one }) => ({ export const cheerEventsRelations = relations(cheerEvents, ({ one, many }) => ({
user: one(users, { user: one(users, {
fields: [cheerEvents.user], fields: [cheerEvents.user],
references: [users.id], references: [users.id],
}), }),
timeouts: many(timeouts),
})); }));
export const cheers = pgTable("cheers", { export const cheers = pgTable("cheers", {
@@ -146,3 +166,33 @@ export const getLootsRelations = relations(getLoots, ({ one }) => ({
references: [users.id], 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],
}),
}));

View File

@@ -14,8 +14,6 @@ eventSub.onChannelRedemptionAdd(streamerId, async (msg) => {
const user = await User.initUsername(msg.userName); const user = await User.initUsername(msg.userName);
try { try {
await selection.execute(msg, user!); await selection.execute(msg, user!);
if (process.env.NODE_ENV === "production")
await msg.updateStatus("FULFILLED"); // only on prod
} catch (err) { } catch (err) {
await sendMessage( await sendMessage(
`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`, `[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`,

View File

@@ -1,34 +1,33 @@
import { api, eventSub } from "index"; import { api, eventSub } from "index";
import kleur from "kleur";
import logger from "lib/logger"; import logger from "lib/logger";
eventSub.onRevoke((event) => { eventSub.onRevoke((event) => {
logger.ok( logger.ok(
`Successfully revoked EventSub subscription: ${kleur.underline(event.id)}`, `Successfully revoked EventSub subscription: \x1b[3;4;1;95m${event.id}`,
); );
}); });
eventSub.onSubscriptionCreateSuccess((event) => { eventSub.onSubscriptionCreateSuccess((event) => {
logger.ok( logger.ok(
`Successfully created EventSub subscription: ${kleur.underline(event.id)}`, `Successfully created EventSub subscription: \x1b[3;4;1;95m${event.id}`,
); );
}); });
eventSub.onSubscriptionCreateFailure((event) => { eventSub.onSubscriptionCreateFailure((event) => {
logger.err( 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) => { eventSub.onSubscriptionDeleteSuccess((event) => {
logger.ok( logger.ok(
`Successfully deleted EventSub subscription: ${kleur.underline(event.id)}`, `Successfully deleted EventSub subscription: \x1b[3;4;1;95m${event.id}`,
); );
}); });
eventSub.onSubscriptionDeleteFailure((event) => { eventSub.onSubscriptionDeleteFailure((event) => {
logger.err( logger.err(
`Failed to delete EventSub subscription: ${kleur.underline(event.id)}`, `Failed to delete EventSub subscription: \x1b[3;4;1;95m${event.id}`,
); );
}); });

View File

@@ -38,7 +38,7 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
) { ) {
// The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored // The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
const message = await sendMessage( const message = await sendMessage(
`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`, `Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://gitlab.com/qwerinope/qweribot/#qweribot`,
); );
await redis.set(`user:${user?.id}:haschatted`, "1"); await redis.set(`user:${user?.id}:haschatted`, "1");
await redis.set(`user:${user?.id}:welcomemessageid`, message.id); await redis.set(`user:${user?.id}:welcomemessageid`, message.id);

View File

@@ -48,7 +48,7 @@ eventSub.onChannelSubscription(streamerId, async (msg) => {
}); });
eventSub.onChannelSubscriptionGift(streamerId, async (msg) => { eventSub.onChannelSubscriptionGift(streamerId, async (msg) => {
if (msg.isAnonymous) { if (msg.gifterName === null) {
switch (msg.tier) { switch (msg.tier) {
case "1000": case "1000":
await sendMessage( await sendMessage(

View File

@@ -10,7 +10,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
if (!msg.messageText.startsWith(commandPrefix)) { if (!msg.messageText.startsWith(commandPrefix)) {
await whisper( await whisper(
msg.senderUserId, msg.senderUserId,
`Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`, `Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
); );
return; return;
} }
@@ -25,7 +25,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
case "h": case "h":
await whisper( await whisper(
msg.senderUserId, msg.senderUserId,
`All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`, `All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
); );
break; break;
case "ghostwhisper": case "ghostwhisper":

View File

@@ -144,6 +144,11 @@ streamerUsers.forEach(
]), ]),
); );
// Deleting all timeouts to prevent ghosts while bot was off
await redis
.keys("user:*:timeout")
.then(async (a) => a.map(async (b) => await redis.del(b)));
const banned = await api.moderation const banned = await api.moderation
.getBannedUsers(streamerId) .getBannedUsers(streamerId)
.then((a) => a.data); .then((a) => a.data);
@@ -156,7 +161,7 @@ for (const ban of banned) {
Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1, Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1,
); );
logger.info( 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.`,
); );
} }
} }
@@ -165,7 +170,7 @@ const mods = await api.moderation.getModerators(streamerId).then((a) => a.data);
for (const mod of mods) { for (const mod of mods) {
await redis.set(`user:${mod.userId}:mod`, "1"); await redis.set(`user:${mod.userId}:mod`, "1");
logger.info( 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.`,
); );
} }
@@ -180,7 +185,7 @@ for (const remod of bannedmods) {
duration = Math.floor((durationdata * 1000 - Date.now()) / 1000); duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
remodMod(target!, duration); remodMod(target!, duration);
logger.info( 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.`,
); );
} }

View File

@@ -1,6 +1,5 @@
import { createTimeoutRecord } from "db/dbTimeouts";
import { createUsedItemRecord } from "db/dbUsedItems";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { createTimeoutEventItem } from "db/ItemEvents";
import { changeItemCount, Item } from "items"; import { changeItemCount, Item } from "items";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import parseCommandArgs from "lib/parseCommandArgs"; import parseCommandArgs from "lib/parseCommandArgs";
@@ -28,7 +27,7 @@ export default new Item({
} }
const target = await User.initUsername(messagequery[0].toLowerCase()); const target = await User.initUsername(messagequery[0].toLowerCase());
if (!target) { if (!target) {
await sendMessage(`${messagequery[0]} doesn't exist`); await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
return; return;
} }
await getUserRecord(target); // make sure the user record exist in the database await getUserRecord(target); // make sure the user record exist in the database
@@ -57,8 +56,7 @@ export default new Item({
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`, `GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
), ),
changeItemCount(user, userObj, ITEMNAME), changeItemCount(user, userObj, ITEMNAME),
createTimeoutRecord(user, target, ITEMNAME), createTimeoutEventItem(user, target, ITEMNAME),
createUsedItemRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "userBlast", name: "userBlast",
user: user.displayName, user: user.displayName,

View File

@@ -1,6 +1,5 @@
import { createTimeoutRecord } from "db/dbTimeouts";
import { createUsedItemRecord } from "db/dbUsedItems";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { createTimeoutEventItem } from "db/ItemEvents";
import { changeItemCount, Item } from "items"; import { changeItemCount, Item } from "items";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { redis } from "lib/redis"; import { redis } from "lib/redis";
@@ -47,8 +46,7 @@ export default new Item({
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`, `wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
), ),
changeItemCount(user, userObj, ITEMNAME), changeItemCount(user, userObj, ITEMNAME),
createTimeoutRecord(user, target!, ITEMNAME), createTimeoutEventItem(user, target!, ITEMNAME),
createUsedItemRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "grenadeExplosion", name: "grenadeExplosion",
user: user.displayName, user: user.displayName,

View File

@@ -1,6 +1,5 @@
import { createTimeoutRecord } from "db/dbTimeouts";
import { createUsedItemRecord } from "db/dbUsedItems";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { createTimeoutEventItem } from "db/ItemEvents";
import { changeItemCount, Item } from "items"; import { changeItemCount, Item } from "items";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import parseCommandArgs from "lib/parseCommandArgs"; import parseCommandArgs from "lib/parseCommandArgs";
@@ -18,7 +17,7 @@ export default new Item({
plural: "s", plural: "s",
description: "Times targeted or random vulnerable user out for 30 minutes", description: "Times targeted or random vulnerable user out for 30 minutes",
aliases: ["execute", "silverbullet"], aliases: ["execute", "silverbullet"],
specialaliases: ["blastin"], specialaliases: ["blastin", "fuck"],
price: 666, price: 666,
execution: async (msg, user, specialargs) => { execution: async (msg, user, specialargs) => {
const messagequery = parseCommandArgs( const messagequery = parseCommandArgs(
@@ -67,7 +66,7 @@ export default new Item({
} }
if (!target) { if (!target) {
await user.clearLock(); await user.clearLock();
await sendMessage(`${messagequery[0]} doesn't exist`); await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
return; return;
} }
@@ -78,21 +77,24 @@ export default new Item({
`You got blasted by ${user.displayName}!`, `You got blasted by ${user.displayName}!`,
60 * 30, 60 * 30,
); );
if (result.status) if (result.status) {
await Promise.all([ await Promise.all([
sendMessage( sendMessage(
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`, `KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
), ),
changeItemCount(user, userObj, ITEMNAME),
createTimeoutRecord(user, target, ITEMNAME),
createUsedItemRecord(user, ITEMNAME),
playAlert({ playAlert({
name: "userExecution", name: "userExecution",
user: user.displayName, user: user.displayName,
target: target.displayName, target: target.displayName,
}), }),
]); ]);
else { 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),
createTimeoutEventItem(user, target, ITEMNAME),
]);
} else {
switch (result.reason) { switch (result.reason) {
case "banned": case "banned":
await sendMessage( await sendMessage(

View File

@@ -1,6 +1,5 @@
import { createTimeoutRecord } from "db/dbTimeouts";
import { createUsedItemRecord } from "db/dbUsedItems";
import { getUserRecord } from "db/dbUser"; import { getUserRecord } from "db/dbUser";
import { createTimeoutEventItem } from "db/ItemEvents";
import { changeItemCount, Item } from "items"; import { changeItemCount, Item } from "items";
import { sendMessage } from "lib/commandUtils"; import { sendMessage } from "lib/commandUtils";
import { redis } from "lib/redis"; import { redis } from "lib/redis";
@@ -49,13 +48,12 @@ export default new Item({
sendMessage( sendMessage(
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`, `wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
), ),
createTimeoutRecord(user, target!, ITEMNAME),
]); ]);
}), }),
); );
await Promise.all([ await Promise.all([
createUsedItemRecord(user, ITEMNAME), createTimeoutEventItem(user, targets, ITEMNAME),
playAlert({ playAlert({
name: "tntExplosion", name: "tntExplosion",
user: user.displayName, user: user.displayName,

View File

@@ -1,18 +1,39 @@
import kleur from "kleur";
const logger = { const logger = {
err: (arg: string) => err: (arg: string) =>
console.error( 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) => warn: (arg: string) =>
console.warn( 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) => info: (arg: string) =>
console.info(kleur.white().bold().italic("[INFO] ") + kleur.white(arg)), console.info(
ok: (arg: string) => console.info(kleur.green().bold(arg)), Bun.wrapAnsi(
enverr: (arg: string) => logger.err(`Please provide a ${arg} in the .env`), `\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; export default logger;

View File

@@ -88,7 +88,9 @@ for (const [_, redeem] of Array.from(namedRedeems)) {
backgroundColor: redeem.color, backgroundColor: redeem.color,
userInputRequired: redeem.input, 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); idMap.set(redeem.name, creation.id);
activeRedeems.set(creation.id, redeem); activeRedeems.set(creation.id, redeem);
} }
@@ -97,7 +99,7 @@ for (const [_, redeem] of Array.from(namedRedeems)) {
Array.from(currentRedeems).map(async ([title, redeem]) => { Array.from(currentRedeems).map(async ([title, redeem]) => {
if (process.env.NODE_ENV !== "production") return; if (process.env.NODE_ENV !== "production") return;
await api.channelPoints.deleteCustomReward(streamerId, redeem); 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"); logger.ok("Successfully synced all custom point redeems");
@@ -108,7 +110,7 @@ export async function enableRedeem(redeem: PointRedeem, id: string) {
isEnabled: true, isEnabled: true,
}); });
activeRedeems.set(id, redeem); 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) { export async function disableRedeem(redeem: PointRedeem, id: string) {
@@ -117,7 +119,7 @@ export async function disableRedeem(redeem: PointRedeem, id: string) {
isEnabled: false, isEnabled: false,
}); });
activeRedeems.delete(id); 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 }; export { activeRedeems, idMap };