mirror of
https://gitlab.com/qwerinope/qweribot.git
synced 2026-02-04 11:06:59 +01:00
Compare commits
16 Commits
c37d2f0a8b
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| b88a93a6cf | |||
| f3c6f6a6b3 | |||
|
aa757a563d
|
|||
|
5a17e405fa
|
|||
|
e24e00701c
|
|||
|
92fe7bb75d
|
|||
|
cb08cc0786
|
|||
|
45e09482be
|
|||
|
64dd8e6dd5
|
|||
|
2bc1d2b953
|
|||
|
07c619f54d
|
|||
|
6a71806881
|
|||
|
eb5cca7897
|
|||
|
3aca8a9210
|
|||
|
fd0461a30f
|
|||
|
afbf08f21a
|
30
README.md
30
README.md
@@ -19,6 +19,7 @@ Invulns don't need moderator or vip status in the channel.
|
||||
The chatterbot and streamer always are invuln and cannot be stripped of this status.
|
||||
Moderators can add and remove invulns.
|
||||
On your first message in chat you will recieve 10 minutes of invuln status.
|
||||
When a moderator adds you as invuln you will lose all items and qbucks.
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -121,6 +122,25 @@ The enable and disable redeem commands have a way to enable/disable all sound al
|
||||
|
||||
When running the development database and twitch api application, the redeems will not get created. This is because twitch only allows editing or deleting rewards when the same application created the reward. (fucking stupid)
|
||||
|
||||
### Welcome message
|
||||
|
||||
Welcome message in the context of this project means 2 things:
|
||||
- The message the bot says when it's the first time you're chatting in the channel.
|
||||
- The personalized message the bot says when you chat for the first time during a stream.
|
||||
|
||||
The personalized message can be set by using the channel point redemption. The message cannot be longer than 200 characters.
|
||||
|
||||
### TTS
|
||||
|
||||
There are 2 types of TTS supported:
|
||||
|
||||
- Microsoft Sam
|
||||
- DecTalk
|
||||
|
||||
Microsoft s(c)am is currently only used by `a_n_i_v` and `a_n_e_e_v`. Whenever the aniv clankers say something MS Sam will pronounce their message.
|
||||
|
||||
Dectalk is available for 25k channel points. If your message is longer than 2 minutes your message will get scammed. I'm not sorry, 100% deserved.
|
||||
|
||||
### Chatterbot/streamerbot
|
||||
|
||||
This depends on if the `CHATTER_IS_STREAMER` environment variable is set.
|
||||
@@ -150,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:
|
||||
`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:
|
||||
`economy`|Get a count of all items in circulation|anyone|`economy` `eco`|:white_check_mark:
|
||||
|
||||
### Qweribucks/Item commands
|
||||
|
||||
@@ -186,7 +207,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
||||
`getadmins`|Get a list of every admin in the channel|anyone|`getadmins`|:x:
|
||||
`itemlock {target}`|Toggle the itemlock on the specified target|moderator|`itemlock`|:x:
|
||||
`testcheer {amount} [args]`|Create a fake cheering event|streamer/chatterbot|`testcheer`|:x:
|
||||
`addinvuln {target}`|Adds an invuln user|moderator|`addinvuln`|:x:
|
||||
`addinvuln {target}`|Adds an invuln user and wipes the user's inventory and wallet|moderator|`addinvuln`|:x:
|
||||
`removeinvuln {target}`|Removes an invuln user|moderator|`removeinvuln`|:x:
|
||||
`addbot {target}`|Adds bot status to a specific chatter|streamer/chatterbot|`addbot`|:x:
|
||||
`removebot {target}`|Removes bot status from a specific chatter|streamer/chatterbot|`removebot`|:x:
|
||||
@@ -205,7 +226,7 @@ NAME|COMMAND|FUNCTION|ALIASES|COST
|
||||
-|-|-|-|-
|
||||
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
|
||||
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
|
||||
|
||||
## Cheers
|
||||
@@ -217,14 +238,17 @@ NAME|AMOUNT|USAGE|FUNCTION
|
||||
`superloot`|150|`cheer150`|Get superloot. Details and drop rates can be found [(here)](#lootbox).
|
||||
`execute`|666|`cheer666 [target]`|Times specified or random vulnerable user out for 30 minutes. On failure gives cheerer a silver bullet
|
||||
`tnt`|1000|`cheer1000`|Gives 5-10 random vulnerable chatters 60 second timeouts. On failure gives cheerer a TNT
|
||||
`realsilverbullet`|6666|`cheer6666 [target]`|Times specified or random vulnerable chatter out for 24 hours. On failure gives the user nothing. Get scammed.
|
||||
|
||||
## Point Redeems
|
||||
|
||||
NAME|COST|DESCRIPTION|INTERNALNAME
|
||||
-|-|-|-
|
||||
`Dectalk TTS`|25000|Play a custom dectalk TTS. If the sound is too long you WILL get scammed.|`dectalk`
|
||||
`Set welcome message`|15000|Set the message the bot will say when you first chat during a stream (character limit is 200)|`setwelcomemsg`
|
||||
`FREE MONEY`|1000|Get 100 qbucks|`qbucksredeem`
|
||||
`RIPBOZO`|500|Sound: Coffeezilla calls me a conman [(source)](https://www.youtube.com/watch?v=QRvEGn7i-wM)|`sfxripbozo`
|
||||
`Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse`
|
||||
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
|
||||
`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`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.7/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
@@ -19,7 +19,8 @@
|
||||
"suspicious": {
|
||||
"noNonNullAssertedOptionalChain": "off",
|
||||
"noExplicitAny": "off",
|
||||
"noControlCharactersInRegex": "off"
|
||||
"noControlCharactersInRegex": "off",
|
||||
"noTsIgnore": "off"
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off"
|
||||
|
||||
130
bun.lock
130
bun.lock
@@ -6,39 +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",
|
||||
"discord.js": "^14.24.0",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"kleur": "^4.1.5",
|
||||
"@twurple/api": "8.0.3",
|
||||
"@twurple/auth": "^8.0.3",
|
||||
"@twurple/eventsub-http": "^8.0.3",
|
||||
"discord.js": "^14.25.1",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
||||
"@biomejs/biome": "^2.3.13",
|
||||
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||
"@types/bun": "latest",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"pg": "^8.16.3",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"pg": "^8.17.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.3",
|
||||
"typescript": "^5.9.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@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=="],
|
||||
"@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=="],
|
||||
|
||||
"@d-fischer/cross-fetch": ["@d-fischer/cross-fetch@5.0.5", "", { "dependencies": { "node-fetch": "^2.6.12" } }, "sha512-symjDUPInTrkfIsZc2n2mo9hiAJLcTJsZkNICjZajEWnWpJ3s3zn50/FY8xpNUAf5w3eFuQii2wxztTGpvG1Xg=="],
|
||||
"@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.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-AGr8OoemT/ejynbIu56qeil2+F2WLkIjn2d8jGK1JkchxnMUhYOfnqc9sVzcRxpG9Ycvw4weQ5sprRvtb7Yhcw=="],
|
||||
|
||||
"@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.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-TUdDCSY+Eo/EHjhJz7P2GnWwfqet+lFxBZzGHldrvULr59AgahamLs/N85SC4+bdF86EhqDuuw9rYLvLFWWlXA=="],
|
||||
|
||||
"@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.13", "", { "os": "linux", "cpu": "x64" }, "sha512-0bdwFVSbbM//Sds6OjtnmQGp4eUjOTt6kHvR/1P0ieR9GcTUAlPNvPC3DiavTqq302W34Ae2T6u5VVNGuQtGlQ=="],
|
||||
|
||||
"@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.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/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=="],
|
||||
@@ -47,11 +59,11 @@
|
||||
|
||||
"@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],
|
||||
|
||||
"@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="],
|
||||
"@discordjs/formatters": ["@discordjs/formatters@0.6.2", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ=="],
|
||||
|
||||
"@discordjs/rest": ["@discordjs/rest@2.6.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.16", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w=="],
|
||||
|
||||
"@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
"@discordjs/util": ["@discordjs/util@1.2.0", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg=="],
|
||||
|
||||
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||
|
||||
@@ -149,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.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
||||
"@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=="],
|
||||
|
||||
@@ -173,8 +185,6 @@
|
||||
|
||||
"@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||
|
||||
"@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
@@ -183,23 +193,21 @@
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
||||
"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=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
"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.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
"discord-api-types": ["discord-api-types@0.38.36", "", {}, "sha512-qrbUbjjwtyeBg5HsAlm1C859epfOyiLjPqAOzkdWlCNsZCWJrertnETF/NwM8H+waMFU58xGSc5eXUfXah+WTQ=="],
|
||||
|
||||
"discord.js": ["discord.js@14.24.0", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.31", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-KNq/ekT8bsmT3ZAfVre8cPbl+DfVYSdlLnDmGZPoz7Cw21LYeWHllRA9MivqNq5b1GPGAxGvyUN1vxbTb/PQWw=="],
|
||||
"discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="],
|
||||
|
||||
"drizzle-kit": ["drizzle-kit@0.31.5", "", { "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-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg=="],
|
||||
"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.6", "", { "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-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ=="],
|
||||
"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=="],
|
||||
|
||||
@@ -209,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=="],
|
||||
@@ -229,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=="],
|
||||
|
||||
@@ -255,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=="],
|
||||
|
||||
@@ -269,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=="],
|
||||
|
||||
@@ -287,18 +291,28 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"@discordjs/builders/@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="],
|
||||
|
||||
"@discordjs/builders/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/builders/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@discordjs/rest/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/rest/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@discordjs/ws/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/ws/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"httpanda/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
in {
|
||||
devShells."${system}" = {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [ bun nodejs deno ];
|
||||
packages = with pkgs; [ bun biome ];
|
||||
shellHook = ''
|
||||
echo Loaded the qweribot dev shell
|
||||
'';
|
||||
|
||||
19
package.json
19
package.json
@@ -2,12 +2,14 @@
|
||||
"name": "qweribot",
|
||||
"module": "src/index.ts",
|
||||
"devDependencies": {
|
||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
||||
"@biomejs/biome": "^2.3.13",
|
||||
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||
"@types/bun": "latest",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"pg": "^8.16.3"
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"pg": "^8.17.2"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "biome check && tsc -b",
|
||||
"start": "NODE_ENV=production bun src/index.ts",
|
||||
"start-dev": "NODE_ENV=development bun src/index.ts",
|
||||
"start-discord": "NODE_ENV=production bun src/discord/index.ts",
|
||||
@@ -24,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",
|
||||
"discord.js": "^14.24.0",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"kleur": "^4.1.5"
|
||||
"@twurple/api": "8.0.3",
|
||||
"@twurple/auth": "^8.0.3",
|
||||
"@twurple/eventsub-http": "^8.0.3",
|
||||
"discord.js": "^14.25.1",
|
||||
"drizzle-orm": "^0.45.1"
|
||||
}
|
||||
}
|
||||
|
||||
20
src/auth.ts
20
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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import type User from "user";
|
||||
|
||||
export type cheers =
|
||||
| "execute"
|
||||
| "grenade"
|
||||
| "tnt"
|
||||
| "timeout"
|
||||
| "superloot"
|
||||
| "realsilverbullet";
|
||||
|
||||
type cheerOptions = {
|
||||
name: string;
|
||||
name: cheers;
|
||||
amount: number;
|
||||
isItem: boolean;
|
||||
execute: (
|
||||
@@ -12,7 +20,7 @@ type cheerOptions = {
|
||||
};
|
||||
|
||||
export class Cheer {
|
||||
public readonly name: string;
|
||||
public readonly name: cheers;
|
||||
public readonly amount: number;
|
||||
public readonly execute: (
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
@@ -20,7 +28,7 @@ export class Cheer {
|
||||
) => Promise<void>;
|
||||
public readonly isItem: boolean;
|
||||
constructor(options: cheerOptions) {
|
||||
this.name = options.name.toLowerCase();
|
||||
this.name = options.name;
|
||||
this.amount = options.amount;
|
||||
this.execute = options.execute;
|
||||
this.isItem = options.isItem;
|
||||
@@ -55,13 +63,13 @@ export async function handleNoTarget(
|
||||
user: User,
|
||||
itemname: items,
|
||||
silent = true,
|
||||
) {
|
||||
): Promise<boolean> {
|
||||
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);
|
||||
@@ -72,4 +80,5 @@ export async function handleNoTarget(
|
||||
);
|
||||
await changeItemCount(user, userRecord, itemname, 1);
|
||||
await user.clearLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
86
src/cheers/realsilverbullet.ts
Normal file
86
src/cheers/realsilverbullet.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Cheer } from "cheers";
|
||||
import { createTimeoutEventCheer } from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
export default new Cheer({
|
||||
name: "realsilverbullet",
|
||||
amount: 6666,
|
||||
isItem: false,
|
||||
async execute(msg, user) {
|
||||
const args = parseCheerArgs(msg.messageText);
|
||||
|
||||
let target: User | null;
|
||||
if (!args[0]) {
|
||||
const vulnsids = await redis.keys("user:*:vulnerable");
|
||||
const baseusers = vulnsids.map((a) => User.initUserId(a.slice(5, -11)));
|
||||
const users: User[] = [];
|
||||
for (const user of baseusers) {
|
||||
const a = await user;
|
||||
if (!a) continue;
|
||||
users.push(a);
|
||||
}
|
||||
if (users.length === 0) {
|
||||
await sendMessage("No vulnerable chatters, -6666 KEKPOINT");
|
||||
return;
|
||||
}
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: "blastinRoulette",
|
||||
user: user.displayName,
|
||||
targets: users.map((a) => a.displayName),
|
||||
finaltarget: target.displayName,
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 4000));
|
||||
} else {
|
||||
target = await User.initUsername(args[0].toLowerCase());
|
||||
}
|
||||
if (!target) {
|
||||
await sendMessage("dumbass wasted 6666 bits KEKPOINT", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await getUserRecord(target);
|
||||
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got fucking DELETED by ${user.displayName}!`,
|
||||
60 * 60 * 24,
|
||||
);
|
||||
if (result.status)
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||
),
|
||||
createTimeoutEventCheer(user, target, "realsilverbullet"),
|
||||
playAlert({
|
||||
name: "userExecution",
|
||||
user: user.displayName,
|
||||
target: target.displayName,
|
||||
}),
|
||||
]);
|
||||
else {
|
||||
switch (result.reason) {
|
||||
case "banned":
|
||||
await sendMessage(
|
||||
`${target.displayName} is already timed out/banned`,
|
||||
msg.messageId,
|
||||
);
|
||||
break;
|
||||
case "illegal":
|
||||
await Promise.all([
|
||||
sendMessage(`${user.displayName} Nou Nou Nou`),
|
||||
timeout(user, "nah", 60 * 60 * 24),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage("Something went wrong... oops", msg.messageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Cheer } from "cheers";
|
||||
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";
|
||||
@@ -11,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;
|
||||
@@ -80,7 +80,7 @@ export default new Cheer({
|
||||
await Promise.all([
|
||||
updateUserRecord(user, userData),
|
||||
sendMessage(message, msg.messageId),
|
||||
createGetLootRecord(user, gainedqbucks, itemDiff, "superloot"),
|
||||
createSuperLootEvent(user, gainedqbucks, itemDiff),
|
||||
user.clearLock(),
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import { emptyInventory } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { addInvuln } from "lib/invuln";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
@@ -20,12 +22,16 @@ export default new Command({
|
||||
return;
|
||||
}
|
||||
const data = await addInvuln(target.id);
|
||||
if (data === "OK")
|
||||
if (data === "OK") {
|
||||
const userRecord = await getUserRecord(target);
|
||||
userRecord.inventory = emptyInventory;
|
||||
userRecord.balance = 0;
|
||||
await updateUserRecord(target, userRecord);
|
||||
await sendMessage(
|
||||
`${target.displayName} is now an invuln`,
|
||||
`${target.displayName} is now an invuln. Their inventory and wallet have been wiped`,
|
||||
msg.messageId,
|
||||
);
|
||||
else
|
||||
} else
|
||||
await sendMessage(
|
||||
`${target.displayName} is already an invuln`,
|
||||
msg.messageId,
|
||||
|
||||
@@ -40,7 +40,7 @@ export default new Command({
|
||||
const selection = namedRedeems.get(args[0]);
|
||||
if (!selection) {
|
||||
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,
|
||||
);
|
||||
return;
|
||||
|
||||
22
src/commands/economy.ts
Normal file
22
src/commands/economy.ts
Normal 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);
|
||||
},
|
||||
});
|
||||
@@ -39,7 +39,7 @@ export default new Command({
|
||||
const selection = namedRedeems.get(args[0]);
|
||||
if (!selection) {
|
||||
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,
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@ export default new Command({
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) {
|
||||
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,
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@ export default new Command({
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) {
|
||||
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,
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -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,8 +132,11 @@ 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(),
|
||||
]);
|
||||
|
||||
if (itemstrings.length === 0 && gainedqbucks < 100)
|
||||
await sendMessage("YEOP THAT'S A SCAMBOX YEOP");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import items, { changeItemCount } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||
import logger from "lib/logger";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
@@ -10,6 +12,11 @@ export default new Command({
|
||||
aliases: ["give"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
if (Array.from<string>(ANIVNAMES).includes(msg.chatterName)) {
|
||||
await sendMessage("CLANKERS CAN'T GIVE ITEMS UltraMad UltraMad UltraMad");
|
||||
await timeout(user, "STUPID CLANKER", 30);
|
||||
return;
|
||||
}
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a user", msg.messageId);
|
||||
|
||||
@@ -25,6 +25,10 @@ export default new Command({
|
||||
}
|
||||
const selection = items.get(messagequery[0].toLowerCase());
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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`)
|
||||
|
||||
151
src/db/CheerEvents.ts
Normal file
151
src/db/CheerEvents.ts
Normal 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
72
src/db/ItemEvents.ts
Normal 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
39
src/db/LootEvents.ts
Normal 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
4
src/db/UndoEvent.ts
Normal 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";
|
||||
@@ -1,18 +1,8 @@
|
||||
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,
|
||||
): Promise<void> {
|
||||
await db
|
||||
.insert(cheerEvents)
|
||||
.values({ user: parseInt(user.id, 10), event: cheer });
|
||||
}
|
||||
|
||||
export async function getCheerEvents(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
cheerEvents.user,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -1,21 +1,8 @@
|
||||
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,
|
||||
): 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) {
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
timeouts.user,
|
||||
|
||||
@@ -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<void> {
|
||||
await db.insert(usedItems).values({ user: parseInt(user.id, 10), item });
|
||||
}
|
||||
|
||||
export async function getItemsUsed(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
usedItems.user,
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
type SQL,
|
||||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { itemarray } from "items";
|
||||
import { itemarray, type items } from "items";
|
||||
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||
import type User from "user";
|
||||
|
||||
/** Use this function to both ensure existance and to retreive data */
|
||||
@@ -120,3 +121,25 @@ export async function getKDLeaderboard(monthData?: string) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AccessToken } from "@twurple/auth";
|
||||
import type { cheers as cheertypes } from "cheers";
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
@@ -32,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", {
|
||||
@@ -42,8 +44,10 @@ export const timeouts = pgTable("timeouts", {
|
||||
target: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
item: varchar().$type<items>().notNull(),
|
||||
item: varchar().$type<items | cheertypes>().notNull(),
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
cheer: uuid().references(() => cheerEvents.id),
|
||||
usedItem: uuid().references(() => usedItems.id),
|
||||
});
|
||||
|
||||
export const timeoutsRelations = relations(timeouts, ({ one }) => ({
|
||||
@@ -57,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", {
|
||||
@@ -68,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<items>().notNull(),
|
||||
event: varchar().$type<items | cheertypes>().notNull(),
|
||||
status: varchar().$type<cheerEventStatus>().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", {
|
||||
@@ -145,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],
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -14,8 +14,6 @@ eventSub.onChannelRedemptionAdd(streamerId, async (msg) => {
|
||||
const user = await User.initUsername(msg.userName);
|
||||
try {
|
||||
await selection.execute(msg, user!);
|
||||
if (process.env.NODE_ENV === "production")
|
||||
await msg.updateStatus("FULFILLED"); // only on prod
|
||||
} catch (err) {
|
||||
await sendMessage(
|
||||
`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`,
|
||||
|
||||
@@ -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}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
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}:welcomemessageid`, message.id);
|
||||
@@ -48,6 +48,18 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
|
||||
|
||||
if (!(await isInvuln(user?.id!))) user?.setVulnerable(); // Make the user vulnerable to explosions if not marked as invuln
|
||||
|
||||
// Custom welcome messages
|
||||
const wcmessage = await redis.get(`user:${user?.id}:welcomemessagetext`);
|
||||
if (
|
||||
process.env.NODE_ENV === "production" && // when running prod DB
|
||||
wcmessage && // when chatter has a welcome message set
|
||||
(await redis.exists(`streamIsLive`)) && // when the stream is active
|
||||
!(await redis.exists(`user:${user?.id}:haschattedthisstream`)) // when the user hasn't chatted this stream
|
||||
)
|
||||
await sendMessage(wcmessage);
|
||||
|
||||
await redis.set(`user:${user?.id}:haschattedthisstream`, "1");
|
||||
|
||||
if (!msg.isCheer && !msg.isRedemption) await handleChatMessage(msg, user!);
|
||||
else if (msg.isCheer && !msg.isRedemption)
|
||||
await handleCheer(msg, msg.bits, user!);
|
||||
|
||||
@@ -5,16 +5,23 @@ import { streamerId } from "main";
|
||||
import { sendDiscordMessage } from "web/discordConnection";
|
||||
|
||||
eventSub.onStreamOnline(streamerId, async (msg) => {
|
||||
await redis.set("streamIsLive", "1");
|
||||
await sendMessage(
|
||||
await Promise.all([
|
||||
redis.set("streamIsLive", "1"),
|
||||
sendMessage(
|
||||
`${msg.broadcasterDisplayName.toUpperCase()} IS LIVE! START DIGGING!`,
|
||||
);
|
||||
await sendDiscordMessage({ message: "live" });
|
||||
),
|
||||
sendDiscordMessage({ message: "live" }),
|
||||
redis
|
||||
.keys("user:*:haschattedthisstream")
|
||||
.then((a) => a.map(async (b) => await redis.del(b))),
|
||||
]);
|
||||
});
|
||||
|
||||
eventSub.onStreamOffline(streamerId, async (msg) => {
|
||||
await redis.del("streamIsLive");
|
||||
await sendMessage(
|
||||
await Promise.all([
|
||||
redis.del("streamIsLive"),
|
||||
sendMessage(
|
||||
`${msg.broadcasterDisplayName.toUpperCase()} IS OFFLINE! NO MORE FREE LOOT!`,
|
||||
);
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -10,7 +10,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
|
||||
if (!msg.messageText.startsWith(commandPrefix)) {
|
||||
await whisper(
|
||||
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;
|
||||
}
|
||||
@@ -25,7 +25,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
|
||||
case "h":
|
||||
await whisper(
|
||||
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;
|
||||
case "ghostwhisper":
|
||||
|
||||
11
src/index.ts
11
src/index.ts
@@ -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
|
||||
.getBannedUsers(streamerId)
|
||||
.then((a) => a.data);
|
||||
@@ -156,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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -165,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.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -180,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.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
@@ -28,7 +27,7 @@ export default new Item({
|
||||
}
|
||||
const target = await User.initUsername(messagequery[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`);
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
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`,
|
||||
),
|
||||
changeItemCount(user, userObj, ITEMNAME),
|
||||
createTimeoutRecord(user, target, ITEMNAME),
|
||||
createUsedItemRecord(user, ITEMNAME),
|
||||
createTimeoutEventItem(user, target, ITEMNAME),
|
||||
playAlert({
|
||||
name: "userBlast",
|
||||
user: user.displayName,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Command, type commandOptions } from "lib/commandUtils";
|
||||
import type User from "user";
|
||||
|
||||
interface itemOptions extends commandOptions {
|
||||
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
||||
|
||||
interface itemOptions extends Omit<commandOptions, "usertype"> {
|
||||
name: items;
|
||||
prettyName: string;
|
||||
plural: string;
|
||||
@@ -10,7 +12,7 @@ interface itemOptions extends commandOptions {
|
||||
}
|
||||
|
||||
export class Item extends Command {
|
||||
public readonly name: items;
|
||||
public readonly name: items = "blaster";
|
||||
public readonly prettyName: string;
|
||||
public readonly plural: string;
|
||||
public readonly description: string;
|
||||
@@ -18,9 +20,11 @@ export class Item extends Command {
|
||||
|
||||
/** Creates an item object */
|
||||
constructor(options: itemOptions) {
|
||||
options.disableable = true; // Items can always be disabled
|
||||
options.usertype = "chatter"; // Everyone can use items
|
||||
super(options);
|
||||
super({
|
||||
...options,
|
||||
usertype: "chatter", // Everyone can use items
|
||||
disableable: true, // Items can always be disabled
|
||||
});
|
||||
this.name = options.name;
|
||||
this.prettyName = options.prettyName;
|
||||
this.plural = options.plural;
|
||||
@@ -59,7 +63,6 @@ for (const file of files) {
|
||||
export default itemAliasMap;
|
||||
export { emptyInventory, itemarray, specialAliasItems, itemObjectArray };
|
||||
|
||||
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
||||
export type inventory = {
|
||||
[key in items]?: number;
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
@@ -18,7 +17,7 @@ export default new Item({
|
||||
plural: "s",
|
||||
description: "Times targeted or random vulnerable user out for 30 minutes",
|
||||
aliases: ["execute", "silverbullet"],
|
||||
specialaliases: ["blastin"],
|
||||
specialaliases: ["blastin", "fuck"],
|
||||
price: 666,
|
||||
execution: async (msg, user, specialargs) => {
|
||||
const messagequery = parseCommandArgs(
|
||||
@@ -67,7 +66,7 @@ export default new Item({
|
||||
}
|
||||
if (!target) {
|
||||
await user.clearLock();
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`);
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,21 +77,24 @@ export default new Item({
|
||||
`You got blasted by ${user.displayName}!`,
|
||||
60 * 30,
|
||||
);
|
||||
if (result.status)
|
||||
if (result.status) {
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`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({
|
||||
name: "userExecution",
|
||||
user: user.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) {
|
||||
case "banned":
|
||||
await sendMessage(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -59,8 +59,11 @@ export async function getItemStats(target: User, thismonth: boolean) {
|
||||
}
|
||||
|
||||
for (const cheer of cheers) {
|
||||
if (!returnObj[cheer.event]) returnObj[cheer.event] = 0;
|
||||
returnObj[cheer.event]! += 1;
|
||||
if (cheer.event in returnObj) {
|
||||
if (!returnObj[cheer.event as keyof inventory])
|
||||
returnObj[cheer.event as keyof inventory] = 0;
|
||||
returnObj[cheer.event as keyof inventory]! += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return returnObj;
|
||||
|
||||
@@ -4,9 +4,9 @@ import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import type User from "user";
|
||||
import { playTTS } from "web/alerts/serverFunctions";
|
||||
import { playMSTTS } from "web/alerts/serverFunctions";
|
||||
|
||||
const ANIVNAMES: anivBots[] = ["a_n_e_e_v", "a_n_i_v"];
|
||||
export const ANIVNAMES: anivBots[] = ["a_n_e_e_v", "a_n_i_v"];
|
||||
|
||||
type anivMessageStore = {
|
||||
[key: string]: string;
|
||||
@@ -44,7 +44,7 @@ export default async function handleMessage(
|
||||
user: User,
|
||||
) {
|
||||
if (ANIVNAMES.map((a) => a.toLowerCase()).includes(user.username)) {
|
||||
await playTTS({ text: msg.messageText });
|
||||
await playMSTTS({ text: msg.messageText });
|
||||
const data: anivMessageStore = await redis
|
||||
.get("anivmessages")
|
||||
.then((a) => (a === null ? {} : JSON.parse(a)));
|
||||
|
||||
@@ -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;
|
||||
|
||||
16
src/pointRedeems/dectalk.ts
Normal file
16
src/pointRedeems/dectalk.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import PointRedeem from "pointRedeems";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { playDecTalk } from "web/alerts/serverFunctions";
|
||||
|
||||
export default new PointRedeem({
|
||||
name: "dectalk",
|
||||
cost: 25000,
|
||||
title: "Dectalk TTS",
|
||||
color: "#FF0000",
|
||||
input: true,
|
||||
prompt: "I HECKIN LOVE DECTALK TTS!!!",
|
||||
async execution(msg) {
|
||||
await playDecTalk(msg.input);
|
||||
await sendMessage("LETSGO SUFFERING LETSGO");
|
||||
},
|
||||
});
|
||||
@@ -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 };
|
||||
|
||||
26
src/pointRedeems/welcomemessageset.ts
Normal file
26
src/pointRedeems/welcomemessageset.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import PointRedeem from "pointRedeems";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new PointRedeem({
|
||||
name: "setwelcomemsg",
|
||||
cost: 15000,
|
||||
title: "Set welcome message",
|
||||
input: true,
|
||||
color: "#0099FF",
|
||||
prompt:
|
||||
"Set your welcome message (echoed once per stream). Character limit is 200",
|
||||
async execution(msg) {
|
||||
if (msg.input.length > 200) {
|
||||
await sendMessage(`Your desired welcome message is too long`);
|
||||
if (process.env.NODE_ENV === "production")
|
||||
await msg.updateStatus("CANCELED");
|
||||
}
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`${msg.userDisplayName} successfully set their new welcome message`,
|
||||
),
|
||||
redis.set(`user:${msg.userId}:welcomemessagetext`, "1"),
|
||||
]);
|
||||
},
|
||||
});
|
||||
@@ -6,4 +6,9 @@ export type MSTTS = {
|
||||
speed: string;
|
||||
};
|
||||
|
||||
export type TTS = MSTTS;
|
||||
export type DecTalk = {
|
||||
type: "dectalk";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type TTS = MSTTS | DecTalk;
|
||||
|
||||
@@ -12,14 +12,14 @@ export async function playAlert(alert: alert) {
|
||||
});
|
||||
}
|
||||
|
||||
type TTSOptions = {
|
||||
type MSTTSOptions = {
|
||||
text: string;
|
||||
voice?: string;
|
||||
pitch?: number;
|
||||
speed?: number;
|
||||
};
|
||||
|
||||
export async function playTTS(options: TTSOptions) {
|
||||
export async function playMSTTS(options: MSTTSOptions) {
|
||||
await sendAlertEvent({
|
||||
function: "playTTS",
|
||||
tts: {
|
||||
@@ -32,6 +32,16 @@ export async function playTTS(options: TTSOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function playDecTalk(input: string) {
|
||||
await sendAlertEvent({
|
||||
function: "playTTS",
|
||||
tts: {
|
||||
type: "dectalk",
|
||||
text: input,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function stopTTS() {
|
||||
await sendAlertEvent({
|
||||
function: "cancelTTS",
|
||||
|
||||
@@ -30,6 +30,9 @@ class TTSManager {
|
||||
`https://tetyys.com/SAPI4/SAPI4?text=${tts.text}&voice=${tts.voice}&pitch=${tts.pitch}&speed=${tts.speed}`,
|
||||
);
|
||||
break;
|
||||
case "dectalk":
|
||||
await this.playAudio(`http://tts.cyzon.us/tts?text=${tts.text}`);
|
||||
break;
|
||||
}
|
||||
|
||||
const newTTS = this.ttsQueue.shift();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { alertEventData } from "web/alerts/types";
|
||||
import type { serverInstruction } from "web/serverTypes";
|
||||
import AlertManager from "./AlertManager";
|
||||
// @ts-ignore
|
||||
import "@fontsource/jersey-15";
|
||||
import TTSManager from "./TTSManager";
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-ignore
|
||||
import "./style.css";
|
||||
// @ts-ignore
|
||||
import "@fontsource/jersey-15";
|
||||
|
||||
import type { twitchEventData } from "web/chatWidget/websockettypes";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"lib/*": ["./lib/*"],
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"lib": ["DOM", "ES2020"]
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["DOM", "ES2022"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/web/chatWidget/www/**/*", "src/web/alerts/www/**/*"]
|
||||
"include": ["src/web/chatWidget/www/**/*", "src/web/alerts/www/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user