mirror of
https://gitlab.com/qwerinope/qweribot.git
synced 2026-02-04 12:36:59 +01:00
Compare commits
24 Commits
c9273afa88
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| b88a93a6cf | |||
| f3c6f6a6b3 | |||
|
aa757a563d
|
|||
|
5a17e405fa
|
|||
|
e24e00701c
|
|||
|
92fe7bb75d
|
|||
|
cb08cc0786
|
|||
|
45e09482be
|
|||
|
64dd8e6dd5
|
|||
|
2bc1d2b953
|
|||
|
07c619f54d
|
|||
|
6a71806881
|
|||
|
eb5cca7897
|
|||
|
3aca8a9210
|
|||
|
fd0461a30f
|
|||
|
afbf08f21a
|
|||
|
c37d2f0a8b
|
|||
|
fd4afb1530
|
|||
|
87a99331a8
|
|||
|
5ddbad1212
|
|||
|
a86ea710cb
|
|||
|
d501277511
|
|||
|
461cefd5e8
|
|||
|
d59bbd3690
|
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.
|
The chatterbot and streamer always are invuln and cannot be stripped of this status.
|
||||||
Moderators can add and remove invulns.
|
Moderators can add and remove invulns.
|
||||||
On your first message in chat you will recieve 10 minutes of invuln status.
|
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
|
### 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)
|
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
|
### Chatterbot/streamerbot
|
||||||
|
|
||||||
This depends on if the `CHATTER_IS_STREAMER` environment variable is set.
|
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:
|
`anivtimeouts`|Get the amount of timeouts, dodges and dodge percentage from aniv timeouts [(info)](#aniv-timeouts)|anyone|`anivtimeouts` `anivtimeout`|:white_check_mark:
|
||||||
`racetime`|Get the racetime.gg room the streamer is currently in. Needs to have twitch linked to racetime account|anyone|`racetime` `raceroom`|:white_check_mark:
|
`racetime`|Get the racetime.gg room the streamer is currently in. Needs to have twitch linked to racetime account|anyone|`racetime` `raceroom`|:white_check_mark:
|
||||||
`randomchatter`|Get a random chatter for whatever reason|moderators|`randomchatter`|:white_check_mark:
|
`randomchatter`|Get a random chatter for whatever reason|moderators|`randomchatter`|:white_check_mark:
|
||||||
|
`economy`|Get a count of all items in circulation|anyone|`economy` `eco`|:white_check_mark:
|
||||||
|
|
||||||
### Qweribucks/Item commands
|
### Qweribucks/Item commands
|
||||||
|
|
||||||
@@ -186,7 +207,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
|||||||
`getadmins`|Get a list of every admin in the channel|anyone|`getadmins`|:x:
|
`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:
|
`itemlock {target}`|Toggle the itemlock on the specified target|moderator|`itemlock`|:x:
|
||||||
`testcheer {amount} [args]`|Create a fake cheering event|streamer/chatterbot|`testcheer`|: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:
|
`removeinvuln {target}`|Removes an invuln user|moderator|`removeinvuln`|:x:
|
||||||
`addbot {target}`|Adds bot status to a specific chatter|streamer/chatterbot|`addbot`|: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:
|
`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
|
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
|
||||||
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
||||||
Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}` `{fuck}`|666
|
||||||
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
||||||
|
|
||||||
## Cheers
|
## Cheers
|
||||||
@@ -217,12 +238,17 @@ NAME|AMOUNT|USAGE|FUNCTION
|
|||||||
`superloot`|150|`cheer150`|Get superloot. Details and drop rates can be found [(here)](#lootbox).
|
`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
|
`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
|
`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
|
## Point Redeems
|
||||||
|
|
||||||
NAME|COST|DESCRIPTION|INTERNALNAME
|
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`
|
`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`
|
`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`
|
`Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse`
|
||||||
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
|
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
|
||||||
|
`Factorio Building Destroyed`|100|Sound: Factorio Building Destroyed alert|`sfxfactorioalert`
|
||||||
|
`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": {
|
"vcs": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"clientKind": "git",
|
"clientKind": "git",
|
||||||
@@ -19,7 +19,8 @@
|
|||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noNonNullAssertedOptionalChain": "off",
|
"noNonNullAssertedOptionalChain": "off",
|
||||||
"noExplicitAny": "off",
|
"noExplicitAny": "off",
|
||||||
"noControlCharactersInRegex": "off"
|
"noControlCharactersInRegex": "off",
|
||||||
|
"noTsIgnore": "off"
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"noNonNullAssertion": "off"
|
"noNonNullAssertion": "off"
|
||||||
|
|||||||
130
bun.lock
130
bun.lock
@@ -6,39 +6,51 @@
|
|||||||
"name": "qweribot",
|
"name": "qweribot",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/jersey-15": "^5.2.8",
|
"@fontsource/jersey-15": "^5.2.8",
|
||||||
"@twurple/api": "7.4.0",
|
"@twurple/api": "8.0.3",
|
||||||
"@twurple/auth": "^7.4.0",
|
"@twurple/auth": "^8.0.3",
|
||||||
"@twurple/eventsub-http": "^7.4.0",
|
"@twurple/eventsub-http": "^8.0.3",
|
||||||
"discord.js": "^14.24.0",
|
"discord.js": "^14.25.1",
|
||||||
"drizzle-orm": "^0.44.6",
|
"drizzle-orm": "^0.45.1",
|
||||||
"kleur": "^4.1.5",
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
"@biomejs/biome": "^2.3.13",
|
||||||
|
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"drizzle-kit": "^0.31.5",
|
"drizzle-kit": "^0.31.8",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.17.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.9.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@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/detect-node": ["@d-fischer/detect-node@3.0.1", "", {}, "sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w=="],
|
||||||
|
|
||||||
"@d-fischer/logger": ["@d-fischer/logger@4.2.3", "", { "dependencies": { "@d-fischer/detect-node": "^3.0.1", "@d-fischer/shared-utils": "^3.2.0", "tslib": "^2.0.3" } }, "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw=="],
|
"@d-fischer/logger": ["@d-fischer/logger@4.2.3", "", { "dependencies": { "@d-fischer/detect-node": "^3.0.1", "@d-fischer/shared-utils": "^3.2.0", "tslib": "^2.0.3" } }, "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw=="],
|
||||||
|
|
||||||
"@d-fischer/qs": ["@d-fischer/qs@7.0.2", "", {}, "sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ=="],
|
|
||||||
|
|
||||||
"@d-fischer/rate-limiter": ["@d-fischer/rate-limiter@1.1.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.3", "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-O5HgACwApyCZhp4JTEBEtbv/W3eAwEkrARFvgWnEsDmXgCMWjIHwohWoHre5BW6IYXFSHBGsuZB/EvNL3942kQ=="],
|
"@d-fischer/rate-limiter": ["@d-fischer/rate-limiter@1.1.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.3", "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-O5HgACwApyCZhp4JTEBEtbv/W3eAwEkrARFvgWnEsDmXgCMWjIHwohWoHre5BW6IYXFSHBGsuZB/EvNL3942kQ=="],
|
||||||
|
|
||||||
"@d-fischer/raw-body": ["@d-fischer/raw-body@2.4.3", "", { "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.3", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-rtPTezQLROnTDdRij0Vo5OJ41aGvfKj9pQ7CkzFssQy+Jyc9BUVLV/DXLIGgvEGUaWt09Jq3im4WgvvPYqTomw=="],
|
|
||||||
|
|
||||||
"@d-fischer/shared-utils": ["@d-fischer/shared-utils@3.6.4", "", { "dependencies": { "tslib": "^2.4.1" } }, "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw=="],
|
"@d-fischer/shared-utils": ["@d-fischer/shared-utils@3.6.4", "", { "dependencies": { "tslib": "^2.4.1" } }, "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw=="],
|
||||||
|
|
||||||
"@d-fischer/typed-event-emitter": ["@d-fischer/typed-event-emitter@3.3.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ=="],
|
"@d-fischer/typed-event-emitter": ["@d-fischer/typed-event-emitter@3.3.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ=="],
|
||||||
@@ -47,11 +59,11 @@
|
|||||||
|
|
||||||
"@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],
|
"@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/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=="],
|
"@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=="],
|
"@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=="],
|
"@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/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/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=="],
|
"@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=="],
|
"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=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
"depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
|
|
||||||
"discord-api-types": ["discord-api-types@0.38.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=="],
|
"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=="],
|
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
||||||
|
|
||||||
"http-errors": ["http-errors@1.7.3", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } }, "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw=="],
|
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
"httpanda": ["httpanda@0.4.7", "", { "dependencies": { "@types/node": "^14.11.2", "tslib": "^2.0.3" } }, "sha512-NieTiR7kfOheL9OeEi6+JKFmJ2JP9ZRqUQ4tiXZ9J+EMMKxApHUQlEM5l4gZ+l67lxE9Er6oigZnujmhlodNCg=="],
|
"httpanda": ["httpanda@0.4.7", "", { "dependencies": { "@types/node": "^14.11.2", "tslib": "^2.0.3" } }, "sha512-NieTiR7kfOheL9OeEi6+JKFmJ2JP9ZRqUQ4tiXZ9J+EMMKxApHUQlEM5l4gZ+l67lxE9Er6oigZnujmhlodNCg=="],
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
|
||||||
|
|
||||||
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
||||||
|
|
||||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||||
@@ -229,19 +235,17 @@
|
|||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
"pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="],
|
||||||
|
|
||||||
"pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="],
|
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
|
||||||
|
|
||||||
"pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="],
|
"pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
|
||||||
|
|
||||||
"pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="],
|
|
||||||
|
|
||||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||||
|
|
||||||
"pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="],
|
"pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="],
|
||||||
|
|
||||||
"pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="],
|
"pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
|
||||||
|
|
||||||
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||||
|
|
||||||
@@ -255,13 +259,15 @@
|
|||||||
|
|
||||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||||
|
|
||||||
|
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||||
|
|
||||||
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
||||||
|
|
||||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
|
|
||||||
"setprototypeof": ["setprototypeof@1.1.1", "", {}, "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="],
|
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||||
|
|
||||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
@@ -269,11 +275,9 @@
|
|||||||
|
|
||||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||||
|
|
||||||
"statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="],
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
|
||||||
|
|
||||||
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||||
|
|
||||||
@@ -287,18 +291,28 @@
|
|||||||
|
|
||||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
|
||||||
|
|
||||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
|
||||||
|
|
||||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||||
|
|
||||||
|
"@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/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/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=="],
|
"@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=="],
|
"httpanda/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="],
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
in {
|
in {
|
||||||
devShells."${system}" = {
|
devShells."${system}" = {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
packages = with pkgs; [ bun nodejs deno ];
|
packages = with pkgs; [ bun biome ];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
echo Loaded the qweribot dev shell
|
echo Loaded the qweribot dev shell
|
||||||
'';
|
'';
|
||||||
|
|||||||
19
package.json
19
package.json
@@ -2,12 +2,14 @@
|
|||||||
"name": "qweribot",
|
"name": "qweribot",
|
||||||
"module": "src/index.ts",
|
"module": "src/index.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
"@biomejs/biome": "^2.3.13",
|
||||||
|
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"drizzle-kit": "^0.31.5",
|
"drizzle-kit": "^0.31.8",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.17.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"check": "biome check && tsc -b",
|
||||||
"start": "NODE_ENV=production bun src/index.ts",
|
"start": "NODE_ENV=production bun src/index.ts",
|
||||||
"start-dev": "NODE_ENV=development bun src/index.ts",
|
"start-dev": "NODE_ENV=development bun src/index.ts",
|
||||||
"start-discord": "NODE_ENV=production bun src/discord/index.ts",
|
"start-discord": "NODE_ENV=production bun src/discord/index.ts",
|
||||||
@@ -24,11 +26,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/jersey-15": "^5.2.8",
|
"@fontsource/jersey-15": "^5.2.8",
|
||||||
"@twurple/api": "7.4.0",
|
"@twurple/api": "8.0.3",
|
||||||
"@twurple/auth": "^7.4.0",
|
"@twurple/auth": "^8.0.3",
|
||||||
"@twurple/eventsub-http": "^7.4.0",
|
"@twurple/eventsub-http": "^8.0.3",
|
||||||
"discord.js": "^14.24.0",
|
"discord.js": "^14.25.1",
|
||||||
"drizzle-orm": "^0.44.6",
|
"drizzle-orm": "^0.45.1"
|
||||||
"kleur": "^4.1.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/__tests__/argsparse.test.ts
Normal file
46
src/__tests__/argsparse.test.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import parseCommandArgs, { parseCheerArgs } from "lib/parseCommandArgs";
|
||||||
|
|
||||||
|
describe("command argument parser", () => {
|
||||||
|
test("split command into chunks", () => {
|
||||||
|
expect(parseCommandArgs("!execute eponirewq")).toEqual(["eponirewq"]);
|
||||||
|
expect(parseCommandArgs("!getloot")).toEqual([]);
|
||||||
|
expect(parseCommandArgs("!inv qwerinope")).toEqual(["qwerinope"]);
|
||||||
|
expect(parseCommandArgs("!admingive qwerinope silverbullet 67")).toEqual([
|
||||||
|
"qwerinope",
|
||||||
|
"silverbullet",
|
||||||
|
"67",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("handle the !use command", () => {
|
||||||
|
expect(parseCommandArgs("!use silverbullet albeees")).toEqual(["albeees"]);
|
||||||
|
expect(parseCommandArgs("!use grenade")).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("handle special aliases", () => {
|
||||||
|
expect(parseCommandArgs("i blast mrockstar20", "i")).toEqual([
|
||||||
|
"mrockstar20",
|
||||||
|
]);
|
||||||
|
expect(parseCommandArgs("blastin sefi", "blastin")).toEqual(["sefi"]);
|
||||||
|
expect(parseCommandArgs("i grenade", "i")).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("cheer argument parser", () => {
|
||||||
|
test("basic parsing", () => {
|
||||||
|
expect(parseCheerArgs("cheer99")).toEqual([]);
|
||||||
|
expect(parseCheerArgs("grenade out! cheer99")).toEqual(["grenade", "out!"]);
|
||||||
|
expect(parseCheerArgs("cheer666 albeees")).toEqual(["albeees"]);
|
||||||
|
expect(parseCheerArgs("albeees cheer666")).toEqual(["albeees"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Remove all cheers", () => {
|
||||||
|
expect(parseCheerArgs("cheer1 cheer1 cheer1")).toEqual([]);
|
||||||
|
expect(parseCheerArgs("TAKE CHEER1 THIS chEEr1 SPAM CheeR6969")).toEqual([
|
||||||
|
"take",
|
||||||
|
"this",
|
||||||
|
"spam",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
20
src/auth.ts
20
src/auth.ts
@@ -9,7 +9,6 @@ import {
|
|||||||
getAuthRecord,
|
getAuthRecord,
|
||||||
updateAuthRecord,
|
updateAuthRecord,
|
||||||
} from "db/dbAuth";
|
} from "db/dbAuth";
|
||||||
import kleur from "kleur";
|
|
||||||
import logger from "lib/logger";
|
import logger from "lib/logger";
|
||||||
|
|
||||||
async function initAuth(
|
async function initAuth(
|
||||||
@@ -26,15 +25,10 @@ async function initAuth(
|
|||||||
const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase();
|
const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase();
|
||||||
// Generate random state variable to prevent cross-site-scripting attacks
|
// Generate random state variable to prevent cross-site-scripting attacks
|
||||||
|
|
||||||
const instruction = `Visit this URL as ${kleur
|
const instruction = `Visit this URL as \x1b[3;4;1;95m${streamer ? "the streamer" : "the chatter"}\x1b[0;97m to authenticate the bot.`;
|
||||||
.red()
|
|
||||||
.underline()
|
|
||||||
.italic(
|
|
||||||
streamer ? "the streamer" : "the chatter",
|
|
||||||
)} to authenticate the bot.`;
|
|
||||||
logger.info(instruction);
|
logger.info(instruction);
|
||||||
logger.info(
|
logger.info(
|
||||||
`https://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join("+")}&state=${state}`,
|
`\x1b[3;4;1;95mhttps://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join("+")}&state=${state}\x1b[0;97m`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const createCodePromise = () => {
|
const createCodePromise = () => {
|
||||||
@@ -61,7 +55,7 @@ async function initAuth(
|
|||||||
return new Response("Successfully authenticated!");
|
return new Response("Successfully authenticated!");
|
||||||
} else {
|
} else {
|
||||||
return new Response(
|
return new Response(
|
||||||
`Authentication attempt unsuccessful, please make sure the redirect url in the twitch developer console is set to ${redirectURL} and that the bot is listening to that url & port.`,
|
`Authentication attempt unsuccessful, please make sure the redirect url in the twitch developer console is set to \x1b[3;4;1;95m${redirectURL}\x1b[0;97m and that the bot is listening to that url & port.`,
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -112,13 +106,15 @@ export async function createAuthProvider(
|
|||||||
});
|
});
|
||||||
|
|
||||||
authData.onRefresh(async (user, token) => {
|
authData.onRefresh(async (user, token) => {
|
||||||
logger.ok(`Successfully refreshed auth for user ${user}`);
|
logger.ok(
|
||||||
|
`Successfully refreshed auth for user \x1b[3;4;1;95m${user}\x1b[0;97m`,
|
||||||
|
);
|
||||||
await updateAuthRecord(user, token);
|
await updateAuthRecord(user, token);
|
||||||
});
|
});
|
||||||
|
|
||||||
authData.onRefreshFailure((user, err) => {
|
authData.onRefreshFailure((user, err) => {
|
||||||
logger.err(
|
logger.err(
|
||||||
`Failed to refresh auth for user ${user}: ${err.name} ${err.message}`,
|
`Failed to refresh auth for user \x1b[3;4;1;95m${user}\x1b[0;97m: ${err.name} ${err.message}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,7 +145,7 @@ export async function createAuthProvider(
|
|||||||
await authData.refreshAccessTokenForUser(user.userId);
|
await authData.refreshAccessTokenForUser(user.userId);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
logger.err(
|
logger.err(
|
||||||
`Failed to refresh user ${user.userId}. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`,
|
`Failed to refresh user \x1b[3;4;1;95m${user.userId}\x1b[0;97m. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`,
|
||||||
);
|
);
|
||||||
await deleteAuthRecord(user.userId);
|
await deleteAuthRecord(user.userId);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Cheer, handleNoTarget } from "cheers";
|
import { Cheer, handleNoTarget } from "cheers";
|
||||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
import {
|
||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
createCompensatedItemCheer,
|
||||||
|
createTimeoutEventCheer,
|
||||||
|
} from "db/CheerEvents";
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||||
@@ -30,7 +32,8 @@ export default new Cheer({
|
|||||||
}
|
}
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
await sendMessage("No vulnerable chatters");
|
await sendMessage("No vulnerable chatters");
|
||||||
await handleNoTarget(msg, user, ITEMNAME, true);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
target = users[Math.floor(Math.random() * users.length)]!;
|
target = users[Math.floor(Math.random() * users.length)]!;
|
||||||
@@ -45,7 +48,8 @@ export default new Cheer({
|
|||||||
target = await User.initUsername(args[0].toLowerCase());
|
target = await User.initUsername(args[0].toLowerCase());
|
||||||
}
|
}
|
||||||
if (!target) {
|
if (!target) {
|
||||||
await handleNoTarget(msg, user, ITEMNAME, false);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await getUserRecord(target);
|
await getUserRecord(target);
|
||||||
@@ -60,8 +64,7 @@ export default new Cheer({
|
|||||||
sendMessage(
|
sendMessage(
|
||||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||||
),
|
),
|
||||||
createTimeoutRecord(user, target, ITEMNAME),
|
createTimeoutEventCheer(user, target, "execute"),
|
||||||
createCheerEventRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "userExecution",
|
name: "userExecution",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
@@ -69,7 +72,8 @@ export default new Cheer({
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
else {
|
else {
|
||||||
await handleNoTarget(msg, user, ITEMNAME);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
switch (result.reason) {
|
switch (result.reason) {
|
||||||
case "banned":
|
case "banned":
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Cheer, handleNoTarget } from "cheers";
|
import { Cheer, handleNoTarget } from "cheers";
|
||||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
import {
|
||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
createCompensatedItemCheer,
|
||||||
|
createTimeoutEventCheer,
|
||||||
|
} from "db/CheerEvents";
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import { redis } from "lib/redis";
|
import { redis } from "lib/redis";
|
||||||
@@ -18,7 +20,8 @@ export default new Cheer({
|
|||||||
const targets = await redis.keys(`user:*:vulnerable`);
|
const targets = await redis.keys(`user:*:vulnerable`);
|
||||||
if (targets.length === 0) {
|
if (targets.length === 0) {
|
||||||
await sendMessage("No vulnerable chatters to blow up!", msg.messageId);
|
await sendMessage("No vulnerable chatters to blow up!", msg.messageId);
|
||||||
await handleNoTarget(msg, user, ITEMNAME);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selection = targets[Math.floor(Math.random() * targets.length)]!;
|
const selection = targets[Math.floor(Math.random() * targets.length)]!;
|
||||||
@@ -32,8 +35,7 @@ export default new Cheer({
|
|||||||
sendMessage(
|
sendMessage(
|
||||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
||||||
),
|
),
|
||||||
createTimeoutRecord(user, target!, ITEMNAME),
|
createTimeoutEventCheer(user, target!, "grenade"),
|
||||||
createCheerEventRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "grenadeExplosion",
|
name: "grenadeExplosion",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
|
export type cheers =
|
||||||
|
| "execute"
|
||||||
|
| "grenade"
|
||||||
|
| "tnt"
|
||||||
|
| "timeout"
|
||||||
|
| "superloot"
|
||||||
|
| "realsilverbullet";
|
||||||
|
|
||||||
type cheerOptions = {
|
type cheerOptions = {
|
||||||
name: string;
|
name: cheers;
|
||||||
amount: number;
|
amount: number;
|
||||||
isItem: boolean;
|
isItem: boolean;
|
||||||
execute: (
|
execute: (
|
||||||
@@ -12,7 +20,7 @@ type cheerOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class Cheer {
|
export class Cheer {
|
||||||
public readonly name: string;
|
public readonly name: cheers;
|
||||||
public readonly amount: number;
|
public readonly amount: number;
|
||||||
public readonly execute: (
|
public readonly execute: (
|
||||||
msg: EventSubChannelChatMessageEvent,
|
msg: EventSubChannelChatMessageEvent,
|
||||||
@@ -20,7 +28,7 @@ export class Cheer {
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
public readonly isItem: boolean;
|
public readonly isItem: boolean;
|
||||||
constructor(options: cheerOptions) {
|
constructor(options: cheerOptions) {
|
||||||
this.name = options.name.toLowerCase();
|
this.name = options.name;
|
||||||
this.amount = options.amount;
|
this.amount = options.amount;
|
||||||
this.execute = options.execute;
|
this.execute = options.execute;
|
||||||
this.isItem = options.isItem;
|
this.isItem = options.isItem;
|
||||||
@@ -55,13 +63,13 @@ export async function handleNoTarget(
|
|||||||
user: User,
|
user: User,
|
||||||
itemname: items,
|
itemname: items,
|
||||||
silent = true,
|
silent = true,
|
||||||
) {
|
): Promise<boolean> {
|
||||||
if (await user.itemLock()) {
|
if (await user.itemLock()) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`Cannot give ${user.displayName} a ${itemname} (itemlock)`,
|
`Cannot give ${user.displayName} a ${itemname} (itemlock)`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
await user.setLock();
|
await user.setLock();
|
||||||
const userRecord = await getUserRecord(user);
|
const userRecord = await getUserRecord(user);
|
||||||
@@ -72,4 +80,5 @@ export async function handleNoTarget(
|
|||||||
);
|
);
|
||||||
await changeItemCount(user, userRecord, itemname, 1);
|
await changeItemCount(user, userRecord, itemname, 1);
|
||||||
await user.clearLock();
|
await user.clearLock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
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 { Cheer } from "cheers";
|
||||||
import { createGetLootRecord } from "db/dbGetLoot";
|
import { createSuperLootEvent } from "db/CheerEvents";
|
||||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||||
import itemMap, { type inventory, type items } from "items";
|
import itemMap, { type inventory, type items } from "items";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
@@ -11,10 +11,10 @@ export default new Cheer({
|
|||||||
amount: 150,
|
amount: 150,
|
||||||
isItem: true,
|
isItem: true,
|
||||||
async execute(msg, user) {
|
async execute(msg, user) {
|
||||||
if (!(await redis.exists("streamIsLive"))) {
|
// if (!(await redis.exists("streamIsLive"))) {
|
||||||
await sendMessage(`No loot while stream is offline`, msg.messageId);
|
// await sendMessage(`No loot while stream is offline`, msg.messageId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
if (await user.itemLock()) {
|
if (await user.itemLock()) {
|
||||||
await sendMessage(`Cannot get loot (itemlock)`, msg.messageId);
|
await sendMessage(`Cannot get loot (itemlock)`, msg.messageId);
|
||||||
return;
|
return;
|
||||||
@@ -80,7 +80,7 @@ export default new Cheer({
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
updateUserRecord(user, userData),
|
updateUserRecord(user, userData),
|
||||||
sendMessage(message, msg.messageId),
|
sendMessage(message, msg.messageId),
|
||||||
createGetLootRecord(user, gainedqbucks, itemDiff, "superloot"),
|
createSuperLootEvent(user, gainedqbucks, itemDiff),
|
||||||
user.clearLock(),
|
user.clearLock(),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Cheer, handleNoTarget } from "cheers";
|
import { Cheer, handleNoTarget } from "cheers";
|
||||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
import {
|
||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
createTimeoutEventCheer,
|
||||||
|
createCompensatedItemCheer,
|
||||||
|
} from "db/CheerEvents";
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||||
@@ -17,12 +19,14 @@ export default new Cheer({
|
|||||||
async execute(msg, user) {
|
async execute(msg, user) {
|
||||||
const args = parseCheerArgs(msg.messageText);
|
const args = parseCheerArgs(msg.messageText);
|
||||||
if (!args[0]) {
|
if (!args[0]) {
|
||||||
await handleNoTarget(msg, user, ITEMNAME, false);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = await User.initUsername(args[0].toLowerCase());
|
const target = await User.initUsername(args[0].toLowerCase());
|
||||||
if (!target) {
|
if (!target) {
|
||||||
await handleNoTarget(msg, user, ITEMNAME, false);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await getUserRecord(target);
|
await getUserRecord(target);
|
||||||
@@ -37,8 +41,7 @@ export default new Cheer({
|
|||||||
sendMessage(
|
sendMessage(
|
||||||
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
||||||
),
|
),
|
||||||
createTimeoutRecord(user, target, ITEMNAME),
|
createTimeoutEventCheer(user, target, "timeout"),
|
||||||
createCheerEventRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "userBlast",
|
name: "userBlast",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
@@ -46,7 +49,8 @@ export default new Cheer({
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
else {
|
else {
|
||||||
await handleNoTarget(msg, user, ITEMNAME);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
switch (result.reason) {
|
switch (result.reason) {
|
||||||
case "banned":
|
case "banned":
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Cheer, handleNoTarget } from "cheers";
|
import { Cheer, handleNoTarget } from "cheers";
|
||||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
import {
|
||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
createTimeoutEventCheer,
|
||||||
|
createCompensatedItemCheer,
|
||||||
|
} from "db/CheerEvents";
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
import { getTNTTargets } from "items/tnt";
|
import { getTNTTargets } from "items/tnt";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
@@ -21,7 +23,8 @@ export default new Cheer({
|
|||||||
.then((a) => a.map((b) => b.slice(5, -11)));
|
.then((a) => a.map((b) => b.slice(5, -11)));
|
||||||
if (vulntargets.length === 0) {
|
if (vulntargets.length === 0) {
|
||||||
await sendMessage("No vulnerable chatters to blow up", msg.messageId);
|
await sendMessage("No vulnerable chatters to blow up", msg.messageId);
|
||||||
await handleNoTarget(msg, user, ITEMNAME);
|
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||||
|
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targets = getTNTTargets(vulntargets);
|
const targets = getTNTTargets(vulntargets);
|
||||||
@@ -36,13 +39,12 @@ export default new Cheer({
|
|||||||
sendMessage(
|
sendMessage(
|
||||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
||||||
),
|
),
|
||||||
createTimeoutRecord(user, target!, ITEMNAME),
|
|
||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
createCheerEventRecord(user, ITEMNAME),
|
createTimeoutEventCheer(user, targets, "tnt"),
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "tntExplosion",
|
name: "tntExplosion",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||||
|
import { emptyInventory } from "items";
|
||||||
import { Command, sendMessage } from "lib/commandUtils";
|
import { Command, sendMessage } from "lib/commandUtils";
|
||||||
import { addInvuln } from "lib/invuln";
|
import { addInvuln } from "lib/invuln";
|
||||||
import parseCommandArgs from "lib/parseCommandArgs";
|
import parseCommandArgs from "lib/parseCommandArgs";
|
||||||
@@ -20,12 +22,16 @@ export default new Command({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = await addInvuln(target.id);
|
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(
|
await sendMessage(
|
||||||
`${target.displayName} is now an invuln`,
|
`${target.displayName} is now an invuln. Their inventory and wallet have been wiped`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
else
|
} else
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`${target.displayName} is already an invuln`,
|
`${target.displayName} is already an invuln`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default new Command({
|
|||||||
const selection = namedRedeems.get(args[0]);
|
const selection = namedRedeems.get(args[0]);
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`,
|
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
22
src/commands/economy.ts
Normal file
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]);
|
const selection = namedRedeems.get(args[0]);
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`,
|
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default new Command({
|
|||||||
const args = parseCommandArgs(msg.messageText);
|
const args = parseCommandArgs(msg.messageText);
|
||||||
if (!args[0]) {
|
if (!args[0]) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`A full list of cheers can be found here: https://github.com/qwerinope/qweribot#cheers`,
|
`A full list of cheers can be found here: https://gitlab.com/qwerinope/qweribot#cheers`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default new Command({
|
|||||||
const args = parseCommandArgs(msg.messageText);
|
const args = parseCommandArgs(msg.messageText);
|
||||||
if (!args[0]) {
|
if (!args[0]) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`A full list of commands can be found here: https://github.com/qwerinope/qweribot#commands-1`,
|
`A full list of commands can be found here: https://gitlab.com/qwerinope/qweribot#commands-1`,
|
||||||
msg.messageId,
|
msg.messageId,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createGetLootRecord } from "db/dbGetLoot";
|
|
||||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||||
|
import { createGetLootEvent } from "db/LootEvents";
|
||||||
import itemMap, { type inventory, type items } from "items";
|
import itemMap, { type inventory, type items } from "items";
|
||||||
import { Command, sendMessage } from "lib/commandUtils";
|
import { Command, sendMessage } from "lib/commandUtils";
|
||||||
import { buildTimeString } from "lib/dateManager";
|
import { buildTimeString } from "lib/dateManager";
|
||||||
@@ -132,8 +132,11 @@ export default new Command({
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
updateUserRecord(user, userData),
|
updateUserRecord(user, userData),
|
||||||
sendMessage(message, msg.messageId),
|
sendMessage(message, msg.messageId),
|
||||||
createGetLootRecord(user, gainedqbucks, itemDiff, "getloot"),
|
createGetLootEvent(user, gainedqbucks, itemDiff, "getloot"),
|
||||||
user.clearLock(),
|
user.clearLock(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (itemstrings.length === 0 && gainedqbucks < 100)
|
||||||
|
await sendMessage("YEOP THAT'S A SCAMBOX YEOP");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
import items, { changeItemCount } from "items";
|
import items, { changeItemCount } from "items";
|
||||||
import { Command, sendMessage } from "lib/commandUtils";
|
import { Command, sendMessage } from "lib/commandUtils";
|
||||||
|
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||||
import logger from "lib/logger";
|
import logger from "lib/logger";
|
||||||
import parseCommandArgs from "lib/parseCommandArgs";
|
import parseCommandArgs from "lib/parseCommandArgs";
|
||||||
|
import { timeout } from "lib/timeout";
|
||||||
import User from "user";
|
import User from "user";
|
||||||
|
|
||||||
export default new Command({
|
export default new Command({
|
||||||
@@ -10,6 +12,11 @@ export default new Command({
|
|||||||
aliases: ["give"],
|
aliases: ["give"],
|
||||||
usertype: "chatter",
|
usertype: "chatter",
|
||||||
execution: async (msg, user) => {
|
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);
|
const args = parseCommandArgs(msg.messageText);
|
||||||
if (!args[0]) {
|
if (!args[0]) {
|
||||||
await sendMessage("Please specify a user", msg.messageId);
|
await sendMessage("Please specify a user", msg.messageId);
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export default new Command({
|
|||||||
}
|
}
|
||||||
const selection = items.get(messagequery[0].toLowerCase());
|
const selection = items.get(messagequery[0].toLowerCase());
|
||||||
if (messagequery[0].toLowerCase() === "lootbox") {
|
if (messagequery[0].toLowerCase() === "lootbox") {
|
||||||
|
if (await redis.sismember("disabledcommands", "getloot")) {
|
||||||
|
await sendMessage("Lootboxes are currently disabled", msg.messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await getloot.execute(msg, user);
|
await getloot.execute(msg, user);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export async function connectionCheck() {
|
|||||||
redisstatus = true;
|
redisstatus = true;
|
||||||
} catch {}
|
} catch {}
|
||||||
logger.info(
|
logger.info(
|
||||||
`Currently using the "${process.env.NODE_ENV ?? "production"}" database`,
|
`Currently using the \x1b[3;4;1;95m"${process.env.NODE_ENV ?? "production"}"\x1b[0;97m database`,
|
||||||
);
|
);
|
||||||
pgstatus
|
pgstatus
|
||||||
? logger.ok(`Postgresql status: good`)
|
? logger.ok(`Postgresql status: good`)
|
||||||
|
|||||||
151
src/db/CheerEvents.ts
Normal file
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 db from "db/connection";
|
||||||
import { cheerEvents } from "db/schema";
|
import { cheerEvents } from "db/schema";
|
||||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||||
import type { items } from "items";
|
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
export async function createCheerEventRecord(
|
|
||||||
user: User,
|
|
||||||
cheer: items,
|
|
||||||
): Promise<void> {
|
|
||||||
await db
|
|
||||||
.insert(cheerEvents)
|
|
||||||
.values({ user: parseInt(user.id, 10), event: cheer });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCheerEvents(user: User, monthData?: string) {
|
export async function getCheerEvents(user: User, monthData?: string) {
|
||||||
let condition: SQL<unknown> | undefined = eq(
|
let condition: SQL<unknown> | undefined = eq(
|
||||||
cheerEvents.user,
|
cheerEvents.user,
|
||||||
|
|||||||
@@ -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 db from "db/connection";
|
||||||
import { timeouts } from "db/schema";
|
import { timeouts } from "db/schema";
|
||||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||||
import type { items } from "items";
|
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
export async function createTimeoutRecord(
|
|
||||||
user: User,
|
|
||||||
target: User,
|
|
||||||
item: items,
|
|
||||||
): Promise<void> {
|
|
||||||
await db.insert(timeouts).values({
|
|
||||||
user: parseInt(user.id, 10),
|
|
||||||
target: parseInt(target.id, 10),
|
|
||||||
item,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getTimeoutsAsUser(user: User, monthData?: string) {
|
export async function getTimeoutsAsUser(user: User, monthData?: string) {
|
||||||
let condition: SQL<unknown> | undefined = eq(
|
let condition: SQL<unknown> | undefined = eq(
|
||||||
timeouts.user,
|
timeouts.user,
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
import db from "db/connection";
|
import db from "db/connection";
|
||||||
import { usedItems } from "db/schema";
|
import { usedItems } from "db/schema";
|
||||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||||
import type { items } from "items";
|
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
export async function createUsedItemRecord(
|
|
||||||
user: User,
|
|
||||||
item: items,
|
|
||||||
): Promise<void> {
|
|
||||||
await db.insert(usedItems).values({ user: parseInt(user.id, 10), item });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getItemsUsed(user: User, monthData?: string) {
|
export async function getItemsUsed(user: User, monthData?: string) {
|
||||||
let condition: SQL<unknown> | undefined = eq(
|
let condition: SQL<unknown> | undefined = eq(
|
||||||
usedItems.user,
|
usedItems.user,
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import {
|
|||||||
type SQL,
|
type SQL,
|
||||||
sql,
|
sql,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { itemarray } from "items";
|
import { itemarray, type items } from "items";
|
||||||
|
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
/** Use this function to both ensure existance and to retreive data */
|
/** Use this function to both ensure existance and to retreive data */
|
||||||
@@ -120,3 +121,25 @@ export async function getKDLeaderboard(monthData?: string) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ItemCounts = Record<items, number>;
|
||||||
|
|
||||||
|
export async function getTotalItemCounts(): Promise<ItemCounts> {
|
||||||
|
const allUsers = await db
|
||||||
|
.select({ username: users.username, inventory: users.inventory })
|
||||||
|
.from(users);
|
||||||
|
|
||||||
|
const filteredUsers = allUsers.filter(
|
||||||
|
(user) =>
|
||||||
|
!Array.from<string>(ANIVNAMES).includes(user.username.toLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
|
const counts = itemarray.reduce((acc, item) => {
|
||||||
|
acc[item] = filteredUsers.reduce((sum, user) => {
|
||||||
|
return sum + (user.inventory[item] || 0);
|
||||||
|
}, 0);
|
||||||
|
return acc;
|
||||||
|
}, {} as ItemCounts);
|
||||||
|
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { AccessToken } from "@twurple/auth";
|
import type { AccessToken } from "@twurple/auth";
|
||||||
|
import type { cheers as cheertypes } from "cheers";
|
||||||
import { relations } from "drizzle-orm";
|
import { relations } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
@@ -32,6 +33,7 @@ export const usersRelations = relations(users, ({ many }) => ({
|
|||||||
cheers: many(cheers),
|
cheers: many(cheers),
|
||||||
anivTimeouts: many(anivTimeouts),
|
anivTimeouts: many(anivTimeouts),
|
||||||
getLoots: many(getLoots),
|
getLoots: many(getLoots),
|
||||||
|
events: many(events),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const timeouts = pgTable("timeouts", {
|
export const timeouts = pgTable("timeouts", {
|
||||||
@@ -42,8 +44,10 @@ export const timeouts = pgTable("timeouts", {
|
|||||||
target: integer()
|
target: integer()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id),
|
.references(() => users.id),
|
||||||
item: varchar().$type<items>().notNull(),
|
item: varchar().$type<items | cheertypes>().notNull(),
|
||||||
created: timestamp().defaultNow().notNull(),
|
created: timestamp().defaultNow().notNull(),
|
||||||
|
cheer: uuid().references(() => cheerEvents.id),
|
||||||
|
usedItem: uuid().references(() => usedItems.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const timeoutsRelations = relations(timeouts, ({ one }) => ({
|
export const timeoutsRelations = relations(timeouts, ({ one }) => ({
|
||||||
@@ -57,6 +61,14 @@ export const timeoutsRelations = relations(timeouts, ({ one }) => ({
|
|||||||
references: [users.id],
|
references: [users.id],
|
||||||
relationName: "target",
|
relationName: "target",
|
||||||
}),
|
}),
|
||||||
|
cheer: one(cheerEvents, {
|
||||||
|
fields: [timeouts.cheer],
|
||||||
|
references: [cheerEvents.id],
|
||||||
|
}),
|
||||||
|
usedItem: one(usedItems, {
|
||||||
|
fields: [timeouts.usedItem],
|
||||||
|
references: [usedItems.id],
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const usedItems = pgTable("usedItems", {
|
export const usedItems = pgTable("usedItems", {
|
||||||
@@ -68,27 +80,36 @@ export const usedItems = pgTable("usedItems", {
|
|||||||
created: timestamp().defaultNow().notNull(),
|
created: timestamp().defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const usedItemsRelations = relations(usedItems, ({ one }) => ({
|
export const usedItemsRelations = relations(usedItems, ({ one, many }) => ({
|
||||||
user: one(users, {
|
user: one(users, {
|
||||||
fields: [usedItems.user],
|
fields: [usedItems.user],
|
||||||
references: [users.id],
|
references: [users.id],
|
||||||
}),
|
}),
|
||||||
|
timeouts: many(timeouts),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "success" when everything works
|
||||||
|
* "compensated" when the user gets an item in their inventory for a cheer
|
||||||
|
*/
|
||||||
|
export type cheerEventStatus = "success" | "compensated";
|
||||||
|
|
||||||
export const cheerEvents = pgTable("cheerEvents", {
|
export const cheerEvents = pgTable("cheerEvents", {
|
||||||
id: uuid().defaultRandom().primaryKey(),
|
id: uuid().defaultRandom().primaryKey(),
|
||||||
user: integer()
|
user: integer()
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id),
|
.references(() => users.id),
|
||||||
event: varchar().$type<items>().notNull(),
|
event: varchar().$type<items | cheertypes>().notNull(),
|
||||||
|
status: varchar().$type<cheerEventStatus>().default("success").notNull(),
|
||||||
created: timestamp().defaultNow().notNull(),
|
created: timestamp().defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const cheerEventsRelations = relations(cheerEvents, ({ one }) => ({
|
export const cheerEventsRelations = relations(cheerEvents, ({ one, many }) => ({
|
||||||
user: one(users, {
|
user: one(users, {
|
||||||
fields: [cheerEvents.user],
|
fields: [cheerEvents.user],
|
||||||
references: [users.id],
|
references: [users.id],
|
||||||
}),
|
}),
|
||||||
|
timeouts: many(timeouts),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const cheers = pgTable("cheers", {
|
export const cheers = pgTable("cheers", {
|
||||||
@@ -145,3 +166,33 @@ export const getLootsRelations = relations(getLoots, ({ one }) => ({
|
|||||||
references: [users.id],
|
references: [users.id],
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const events = pgTable("events", {
|
||||||
|
id: uuid().defaultRandom().primaryKey(),
|
||||||
|
user: integer()
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
created: timestamp().defaultNow().notNull(),
|
||||||
|
usedItem: uuid().references(() => usedItems.id),
|
||||||
|
cheer: uuid().references(() => cheerEvents.id),
|
||||||
|
getLoot: uuid().references(() => getLoots.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const eventsRelations = relations(events, ({ one }) => ({
|
||||||
|
user: one(users, {
|
||||||
|
fields: [events.user],
|
||||||
|
references: [users.id],
|
||||||
|
}),
|
||||||
|
usedItem: one(usedItems, {
|
||||||
|
fields: [events.usedItem],
|
||||||
|
references: [usedItems.id],
|
||||||
|
}),
|
||||||
|
cheer: one(cheerEvents, {
|
||||||
|
fields: [events.cheer],
|
||||||
|
references: [cheerEvents.id],
|
||||||
|
}),
|
||||||
|
getLoot: one(getLoots, {
|
||||||
|
fields: [events.getLoot],
|
||||||
|
references: [getLoots.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ eventSub.onChannelRedemptionAdd(streamerId, async (msg) => {
|
|||||||
const user = await User.initUsername(msg.userName);
|
const user = await User.initUsername(msg.userName);
|
||||||
try {
|
try {
|
||||||
await selection.execute(msg, user!);
|
await selection.execute(msg, user!);
|
||||||
if (process.env.NODE_ENV === "production")
|
|
||||||
await msg.updateStatus("FULFILLED"); // only on prod
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`,
|
`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`,
|
||||||
|
|||||||
@@ -1,34 +1,33 @@
|
|||||||
import { api, eventSub } from "index";
|
import { api, eventSub } from "index";
|
||||||
import kleur from "kleur";
|
|
||||||
import logger from "lib/logger";
|
import logger from "lib/logger";
|
||||||
|
|
||||||
eventSub.onRevoke((event) => {
|
eventSub.onRevoke((event) => {
|
||||||
logger.ok(
|
logger.ok(
|
||||||
`Successfully revoked EventSub subscription: ${kleur.underline(event.id)}`,
|
`Successfully revoked EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSub.onSubscriptionCreateSuccess((event) => {
|
eventSub.onSubscriptionCreateSuccess((event) => {
|
||||||
logger.ok(
|
logger.ok(
|
||||||
`Successfully created EventSub subscription: ${kleur.underline(event.id)}`,
|
`Successfully created EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSub.onSubscriptionCreateFailure((event) => {
|
eventSub.onSubscriptionCreateFailure((event) => {
|
||||||
logger.err(
|
logger.err(
|
||||||
`Failed to create EventSub subscription: ${kleur.underline(event.id)}`,
|
`Failed to create EventSub subscription: \x1b[3;4;4;95m${event.id}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSub.onSubscriptionDeleteSuccess((event) => {
|
eventSub.onSubscriptionDeleteSuccess((event) => {
|
||||||
logger.ok(
|
logger.ok(
|
||||||
`Successfully deleted EventSub subscription: ${kleur.underline(event.id)}`,
|
`Successfully deleted EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventSub.onSubscriptionDeleteFailure((event) => {
|
eventSub.onSubscriptionDeleteFailure((event) => {
|
||||||
logger.err(
|
logger.err(
|
||||||
`Failed to delete EventSub subscription: ${kleur.underline(event.id)}`,
|
`Failed to delete EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
|
|||||||
) {
|
) {
|
||||||
// The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
|
// The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
|
||||||
const message = await sendMessage(
|
const message = await sendMessage(
|
||||||
`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`,
|
`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://gitlab.com/qwerinope/qweribot/#qweribot`,
|
||||||
);
|
);
|
||||||
await redis.set(`user:${user?.id}:haschatted`, "1");
|
await redis.set(`user:${user?.id}:haschatted`, "1");
|
||||||
await redis.set(`user:${user?.id}:welcomemessageid`, message.id);
|
await redis.set(`user:${user?.id}:welcomemessageid`, message.id);
|
||||||
@@ -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
|
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!);
|
if (!msg.isCheer && !msg.isRedemption) await handleChatMessage(msg, user!);
|
||||||
else if (msg.isCheer && !msg.isRedemption)
|
else if (msg.isCheer && !msg.isRedemption)
|
||||||
await handleCheer(msg, msg.bits, user!);
|
await handleCheer(msg, msg.bits, user!);
|
||||||
|
|||||||
@@ -5,16 +5,23 @@ import { streamerId } from "main";
|
|||||||
import { sendDiscordMessage } from "web/discordConnection";
|
import { sendDiscordMessage } from "web/discordConnection";
|
||||||
|
|
||||||
eventSub.onStreamOnline(streamerId, async (msg) => {
|
eventSub.onStreamOnline(streamerId, async (msg) => {
|
||||||
await redis.set("streamIsLive", "1");
|
await Promise.all([
|
||||||
await sendMessage(
|
redis.set("streamIsLive", "1"),
|
||||||
|
sendMessage(
|
||||||
`${msg.broadcasterDisplayName.toUpperCase()} IS LIVE! START DIGGING!`,
|
`${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) => {
|
eventSub.onStreamOffline(streamerId, async (msg) => {
|
||||||
await redis.del("streamIsLive");
|
await Promise.all([
|
||||||
await sendMessage(
|
redis.del("streamIsLive"),
|
||||||
|
sendMessage(
|
||||||
`${msg.broadcasterDisplayName.toUpperCase()} IS OFFLINE! NO MORE FREE LOOT!`,
|
`${msg.broadcasterDisplayName.toUpperCase()} IS OFFLINE! NO MORE FREE LOOT!`,
|
||||||
);
|
),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ eventSub.onChannelSubscription(streamerId, async (msg) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
eventSub.onChannelSubscriptionGift(streamerId, async (msg) => {
|
eventSub.onChannelSubscriptionGift(streamerId, async (msg) => {
|
||||||
if (msg.isAnonymous) {
|
if (msg.gifterName === null) {
|
||||||
switch (msg.tier) {
|
switch (msg.tier) {
|
||||||
case "1000":
|
case "1000":
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
|
|||||||
if (!msg.messageText.startsWith(commandPrefix)) {
|
if (!msg.messageText.startsWith(commandPrefix)) {
|
||||||
await whisper(
|
await whisper(
|
||||||
msg.senderUserId,
|
msg.senderUserId,
|
||||||
`Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`,
|
`Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ eventSub.onUserWhisperMessage(chatterId, async (msg) => {
|
|||||||
case "h":
|
case "h":
|
||||||
await whisper(
|
await whisper(
|
||||||
msg.senderUserId,
|
msg.senderUserId,
|
||||||
`All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`,
|
`All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "ghostwhisper":
|
case "ghostwhisper":
|
||||||
|
|||||||
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
|
const banned = await api.moderation
|
||||||
.getBannedUsers(streamerId)
|
.getBannedUsers(streamerId)
|
||||||
.then((a) => a.data);
|
.then((a) => a.data);
|
||||||
@@ -156,7 +161,7 @@ for (const ban of banned) {
|
|||||||
Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1,
|
Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1,
|
||||||
);
|
);
|
||||||
logger.info(
|
logger.info(
|
||||||
`Set the timeout of ${ban.userDisplayName} in the Redis/Valkey database.`,
|
`Set the timeout of \x1b[3;4;1;95m${ban.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +170,7 @@ const mods = await api.moderation.getModerators(streamerId).then((a) => a.data);
|
|||||||
for (const mod of mods) {
|
for (const mod of mods) {
|
||||||
await redis.set(`user:${mod.userId}:mod`, "1");
|
await redis.set(`user:${mod.userId}:mod`, "1");
|
||||||
logger.info(
|
logger.info(
|
||||||
`Set the mod status of ${mod.userDisplayName} in the Redis/Valkey database.`,
|
`Set the mod status of \x1b[3;4;1;95m${mod.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +185,7 @@ for (const remod of bannedmods) {
|
|||||||
duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
|
duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
|
||||||
remodMod(target!, duration);
|
remodMod(target!, duration);
|
||||||
logger.info(
|
logger.info(
|
||||||
`Set the remod timer for ${target?.displayName} to ${duration} seconds.`,
|
`Set the remod timer for \x1b[3;4;1;95m${target?.displayName}\x1b[0;97m to \x1b[3;4;1;95m${duration}\x1b[0;97m seconds.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
|
||||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
|
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||||
import { changeItemCount, Item } from "items";
|
import { changeItemCount, Item } from "items";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import parseCommandArgs from "lib/parseCommandArgs";
|
import parseCommandArgs from "lib/parseCommandArgs";
|
||||||
@@ -28,7 +27,7 @@ export default new Item({
|
|||||||
}
|
}
|
||||||
const target = await User.initUsername(messagequery[0].toLowerCase());
|
const target = await User.initUsername(messagequery[0].toLowerCase());
|
||||||
if (!target) {
|
if (!target) {
|
||||||
await sendMessage(`${messagequery[0]} doesn't exist`);
|
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await getUserRecord(target); // make sure the user record exist in the database
|
await getUserRecord(target); // make sure the user record exist in the database
|
||||||
@@ -57,8 +56,7 @@ export default new Item({
|
|||||||
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
||||||
),
|
),
|
||||||
changeItemCount(user, userObj, ITEMNAME),
|
changeItemCount(user, userObj, ITEMNAME),
|
||||||
createTimeoutRecord(user, target, ITEMNAME),
|
createTimeoutEventItem(user, target, ITEMNAME),
|
||||||
createUsedItemRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "userBlast",
|
name: "userBlast",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
|
||||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
|
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||||
import { changeItemCount, Item } from "items";
|
import { changeItemCount, Item } from "items";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import { redis } from "lib/redis";
|
import { redis } from "lib/redis";
|
||||||
@@ -47,8 +46,7 @@ export default new Item({
|
|||||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
||||||
),
|
),
|
||||||
changeItemCount(user, userObj, ITEMNAME),
|
changeItemCount(user, userObj, ITEMNAME),
|
||||||
createTimeoutRecord(user, target!, ITEMNAME),
|
createTimeoutEventItem(user, target!, ITEMNAME),
|
||||||
createUsedItemRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "grenadeExplosion",
|
name: "grenadeExplosion",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
|
|||||||
@@ -1,49 +1,34 @@
|
|||||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
import { Command, type commandOptions } from "lib/commandUtils";
|
||||||
import type { specialExecuteArgs, userType } from "lib/commandUtils";
|
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
|
||||||
type itemOptions = {
|
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
||||||
|
|
||||||
|
interface itemOptions extends Omit<commandOptions, "usertype"> {
|
||||||
name: items;
|
name: items;
|
||||||
aliases: string[];
|
|
||||||
prettyName: string;
|
prettyName: string;
|
||||||
plural: string;
|
plural: string;
|
||||||
description: string;
|
description: string;
|
||||||
execution: (
|
|
||||||
message: EventSubChannelChatMessageEvent,
|
|
||||||
sender: User,
|
|
||||||
args?: specialExecuteArgs,
|
|
||||||
) => Promise<void>;
|
|
||||||
specialaliases?: string[];
|
|
||||||
price: number;
|
price: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export class Item {
|
export class Item extends Command {
|
||||||
public readonly name: items;
|
public readonly name: items = "blaster";
|
||||||
public readonly prettyName: string;
|
public readonly prettyName: string;
|
||||||
public readonly plural: string;
|
public readonly plural: string;
|
||||||
public readonly description: string;
|
public readonly description: string;
|
||||||
public readonly aliases: string[];
|
|
||||||
public readonly specialaliases: string[];
|
|
||||||
public readonly usertype: userType;
|
|
||||||
public readonly price: number;
|
public readonly price: number;
|
||||||
public readonly execute: (
|
|
||||||
message: EventSubChannelChatMessageEvent,
|
|
||||||
sender: User,
|
|
||||||
args?: specialExecuteArgs,
|
|
||||||
) => Promise<void>;
|
|
||||||
public readonly disableable: boolean;
|
|
||||||
|
|
||||||
/** Creates an item object */
|
/** Creates an item object */
|
||||||
constructor(options: itemOptions) {
|
constructor(options: itemOptions) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
usertype: "chatter", // Everyone can use items
|
||||||
|
disableable: true, // Items can always be disabled
|
||||||
|
});
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
this.prettyName = options.prettyName;
|
this.prettyName = options.prettyName;
|
||||||
this.plural = options.plural;
|
this.plural = options.plural;
|
||||||
this.description = options.description;
|
this.description = options.description;
|
||||||
this.aliases = options.aliases;
|
|
||||||
this.usertype = "chatter"; // Items are usable by everyone
|
|
||||||
this.execute = options.execution;
|
|
||||||
this.disableable = true;
|
|
||||||
this.specialaliases = options.specialaliases ?? [];
|
|
||||||
this.price = options.price;
|
this.price = options.price;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +63,6 @@ for (const file of files) {
|
|||||||
export default itemAliasMap;
|
export default itemAliasMap;
|
||||||
export { emptyInventory, itemarray, specialAliasItems, itemObjectArray };
|
export { emptyInventory, itemarray, specialAliasItems, itemObjectArray };
|
||||||
|
|
||||||
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
|
||||||
export type inventory = {
|
export type inventory = {
|
||||||
[key in items]?: number;
|
[key in items]?: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
|
||||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
|
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||||
import { changeItemCount, Item } from "items";
|
import { changeItemCount, Item } from "items";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import parseCommandArgs from "lib/parseCommandArgs";
|
import parseCommandArgs from "lib/parseCommandArgs";
|
||||||
@@ -18,7 +17,7 @@ export default new Item({
|
|||||||
plural: "s",
|
plural: "s",
|
||||||
description: "Times targeted or random vulnerable user out for 30 minutes",
|
description: "Times targeted or random vulnerable user out for 30 minutes",
|
||||||
aliases: ["execute", "silverbullet"],
|
aliases: ["execute", "silverbullet"],
|
||||||
specialaliases: ["blastin"],
|
specialaliases: ["blastin", "fuck"],
|
||||||
price: 666,
|
price: 666,
|
||||||
execution: async (msg, user, specialargs) => {
|
execution: async (msg, user, specialargs) => {
|
||||||
const messagequery = parseCommandArgs(
|
const messagequery = parseCommandArgs(
|
||||||
@@ -67,7 +66,7 @@ export default new Item({
|
|||||||
}
|
}
|
||||||
if (!target) {
|
if (!target) {
|
||||||
await user.clearLock();
|
await user.clearLock();
|
||||||
await sendMessage(`${messagequery[0]} doesn't exist`);
|
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,21 +77,24 @@ export default new Item({
|
|||||||
`You got blasted by ${user.displayName}!`,
|
`You got blasted by ${user.displayName}!`,
|
||||||
60 * 30,
|
60 * 30,
|
||||||
);
|
);
|
||||||
if (result.status)
|
if (result.status) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
sendMessage(
|
sendMessage(
|
||||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||||
),
|
),
|
||||||
changeItemCount(user, userObj, ITEMNAME),
|
|
||||||
createTimeoutRecord(user, target, ITEMNAME),
|
|
||||||
createUsedItemRecord(user, ITEMNAME),
|
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "userExecution",
|
name: "userExecution",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
target: target.displayName,
|
target: target.displayName,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
else {
|
if (user.id !== streamerId || process.env.NODE_ENV === "development")
|
||||||
|
// streamer doesn't consume bullets and doesn't count for timeouts
|
||||||
|
await Promise.all([
|
||||||
|
changeItemCount(user, userObj, ITEMNAME),
|
||||||
|
createTimeoutEventItem(user, target, ITEMNAME),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
switch (result.reason) {
|
switch (result.reason) {
|
||||||
case "banned":
|
case "banned":
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
|
||||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
|
||||||
import { getUserRecord } from "db/dbUser";
|
import { getUserRecord } from "db/dbUser";
|
||||||
|
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||||
import { changeItemCount, Item } from "items";
|
import { changeItemCount, Item } from "items";
|
||||||
import { sendMessage } from "lib/commandUtils";
|
import { sendMessage } from "lib/commandUtils";
|
||||||
import { redis } from "lib/redis";
|
import { redis } from "lib/redis";
|
||||||
@@ -49,13 +48,12 @@ export default new Item({
|
|||||||
sendMessage(
|
sendMessage(
|
||||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
||||||
),
|
),
|
||||||
createTimeoutRecord(user, target!, ITEMNAME),
|
|
||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
createUsedItemRecord(user, ITEMNAME),
|
createTimeoutEventItem(user, targets, ITEMNAME),
|
||||||
playAlert({
|
playAlert({
|
||||||
name: "tntExplosion",
|
name: "tntExplosion",
|
||||||
user: user.displayName,
|
user: user.displayName,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type specialExecuteArgs = {
|
|||||||
activation?: string;
|
activation?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type commandOptions = {
|
export interface commandOptions {
|
||||||
name: string;
|
name: string;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
usertype: userType;
|
usertype: userType;
|
||||||
@@ -20,7 +20,7 @@ export type commandOptions = {
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
disableable?: boolean;
|
disableable?: boolean;
|
||||||
specialaliases?: string[];
|
specialaliases?: string[];
|
||||||
};
|
}
|
||||||
|
|
||||||
/** The Command class represents a command */
|
/** The Command class represents a command */
|
||||||
export class Command {
|
export class Command {
|
||||||
|
|||||||
@@ -59,8 +59,11 @@ export async function getItemStats(target: User, thismonth: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const cheer of cheers) {
|
for (const cheer of cheers) {
|
||||||
if (!returnObj[cheer.event]) returnObj[cheer.event] = 0;
|
if (cheer.event in returnObj) {
|
||||||
returnObj[cheer.event]! += 1;
|
if (!returnObj[cheer.event as keyof inventory])
|
||||||
|
returnObj[cheer.event as keyof inventory] = 0;
|
||||||
|
returnObj[cheer.event as keyof inventory]! += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnObj;
|
return returnObj;
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import { sendMessage } from "lib/commandUtils";
|
|||||||
import { redis } from "lib/redis";
|
import { redis } from "lib/redis";
|
||||||
import { timeout } from "lib/timeout";
|
import { timeout } from "lib/timeout";
|
||||||
import type User from "user";
|
import type User from "user";
|
||||||
|
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 = {
|
type anivMessageStore = {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
@@ -43,6 +44,7 @@ export default async function handleMessage(
|
|||||||
user: User,
|
user: User,
|
||||||
) {
|
) {
|
||||||
if (ANIVNAMES.map((a) => a.toLowerCase()).includes(user.username)) {
|
if (ANIVNAMES.map((a) => a.toLowerCase()).includes(user.username)) {
|
||||||
|
await playMSTTS({ text: msg.messageText });
|
||||||
const data: anivMessageStore = await redis
|
const data: anivMessageStore = await redis
|
||||||
.get("anivmessages")
|
.get("anivmessages")
|
||||||
.then((a) => (a === null ? {} : JSON.parse(a)));
|
.then((a) => (a === null ? {} : JSON.parse(a)));
|
||||||
|
|||||||
@@ -1,18 +1,39 @@
|
|||||||
import kleur from "kleur";
|
|
||||||
|
|
||||||
const logger = {
|
const logger = {
|
||||||
err: (arg: string) =>
|
err: (arg: string) =>
|
||||||
console.error(
|
console.error(
|
||||||
kleur.red().bold().italic("[ERROR] ") + kleur.red().bold(arg),
|
Bun.wrapAnsi(
|
||||||
|
`\x1b[1;91m[ERROR]: \x1b[0;97m${arg}\x1b[0m`,
|
||||||
|
process.stdout.columns,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
warn: (arg: string) =>
|
warn: (arg: string) =>
|
||||||
console.warn(
|
console.warn(
|
||||||
kleur.yellow().bold().italic("[WARN] ") + kleur.yellow().bold(arg),
|
Bun.wrapAnsi(
|
||||||
|
`\x1b[1;93m[WARN]: \x1b[0;97m${arg}\x1b[0m`,
|
||||||
|
process.stdout.columns,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
info: (arg: string) =>
|
info: (arg: string) =>
|
||||||
console.info(kleur.white().bold().italic("[INFO] ") + kleur.white(arg)),
|
console.info(
|
||||||
ok: (arg: string) => console.info(kleur.green().bold(arg)),
|
Bun.wrapAnsi(
|
||||||
enverr: (arg: string) => logger.err(`Please provide a ${arg} in the .env`),
|
`\x1b[37;1m[INFO]: \x1b[0;97m${arg}\x1b[0m`,
|
||||||
|
process.stdout.columns,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ok: (arg: string) =>
|
||||||
|
console.info(
|
||||||
|
Bun.wrapAnsi(
|
||||||
|
`\x1b[1;92m[OK]: \x1b[0;97m${arg}\x1b[0m`,
|
||||||
|
process.stdout.columns,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enverr: (arg: string) =>
|
||||||
|
logger.err(
|
||||||
|
Bun.wrapAnsi(
|
||||||
|
`Please provide a \x1b[4;3;93m${arg}\x1b[0;97m in the .env`,
|
||||||
|
process.stdout.columns,
|
||||||
|
),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default logger;
|
export default logger;
|
||||||
|
|||||||
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,
|
backgroundColor: redeem.color,
|
||||||
userInputRequired: redeem.input,
|
userInputRequired: redeem.input,
|
||||||
});
|
});
|
||||||
logger.ok(`Created custom point redeem ${redeem.title}`);
|
logger.ok(
|
||||||
|
`Created custom point redeem \x1b[3;4;1;95m${redeem.title}\x1b[0;97m`,
|
||||||
|
);
|
||||||
idMap.set(redeem.name, creation.id);
|
idMap.set(redeem.name, creation.id);
|
||||||
activeRedeems.set(creation.id, redeem);
|
activeRedeems.set(creation.id, redeem);
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ for (const [_, redeem] of Array.from(namedRedeems)) {
|
|||||||
Array.from(currentRedeems).map(async ([title, redeem]) => {
|
Array.from(currentRedeems).map(async ([title, redeem]) => {
|
||||||
if (process.env.NODE_ENV !== "production") return;
|
if (process.env.NODE_ENV !== "production") return;
|
||||||
await api.channelPoints.deleteCustomReward(streamerId, redeem);
|
await api.channelPoints.deleteCustomReward(streamerId, redeem);
|
||||||
logger.ok(`Deleted custom point redeem ${title}`);
|
logger.ok(`Deleted custom point redeem \x1b[3;4;1;95m${title}\x1b[0;97m`);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.ok("Successfully synced all custom point redeems");
|
logger.ok("Successfully synced all custom point redeems");
|
||||||
@@ -108,7 +110,7 @@ export async function enableRedeem(redeem: PointRedeem, id: string) {
|
|||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
});
|
});
|
||||||
activeRedeems.set(id, redeem);
|
activeRedeems.set(id, redeem);
|
||||||
logger.ok(`Enabled the ${redeem.name} point redeem`);
|
logger.ok(`Enabled the \x1b[3;4;1;95m${redeem.name}\x1b[0;97m point redeem`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function disableRedeem(redeem: PointRedeem, id: string) {
|
export async function disableRedeem(redeem: PointRedeem, id: string) {
|
||||||
@@ -117,7 +119,7 @@ export async function disableRedeem(redeem: PointRedeem, id: string) {
|
|||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
});
|
});
|
||||||
activeRedeems.delete(id);
|
activeRedeems.delete(id);
|
||||||
logger.ok(`Disabled the ${redeem.name} point redeem`);
|
logger.ok(`Disabled the \x1b[3;4;1;95m${redeem.name}\x1b[0;97m point redeem`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { activeRedeems, idMap };
|
export { activeRedeems, idMap };
|
||||||
|
|||||||
17
src/pointRedeems/sfxFactorioAlert.ts
Normal file
17
src/pointRedeems/sfxFactorioAlert.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import PointRedeem from "pointRedeems";
|
||||||
|
import { playAlert } from "web/alerts/serverFunctions";
|
||||||
|
|
||||||
|
export default new PointRedeem({
|
||||||
|
name: "sfxFactorioAlert",
|
||||||
|
title: "Factorio Building Destroyed",
|
||||||
|
cost: 100,
|
||||||
|
color: "#A020F0",
|
||||||
|
prompt: 'Play the Factorio "Building Destroyed" sound effect',
|
||||||
|
sfxredeem: true,
|
||||||
|
execution: async (msg) =>
|
||||||
|
await playAlert({
|
||||||
|
name: "sound",
|
||||||
|
user: msg.userDisplayName,
|
||||||
|
sound: "factorioalert",
|
||||||
|
}),
|
||||||
|
});
|
||||||
17
src/pointRedeems/sfxFail.ts
Normal file
17
src/pointRedeems/sfxFail.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import PointRedeem from "pointRedeems";
|
||||||
|
import { playAlert } from "web/alerts/serverFunctions";
|
||||||
|
|
||||||
|
export default new PointRedeem({
|
||||||
|
name: "sfxFail",
|
||||||
|
title: "Fail",
|
||||||
|
cost: 100,
|
||||||
|
color: "#A020F0",
|
||||||
|
prompt: "failure horn or trombone meme sound",
|
||||||
|
sfxredeem: true,
|
||||||
|
execution: async (msg) =>
|
||||||
|
await playAlert({
|
||||||
|
name: "sound",
|
||||||
|
user: msg.userDisplayName,
|
||||||
|
sound: Math.random() > 0.5 ? "fail1" : "fail2",
|
||||||
|
}),
|
||||||
|
});
|
||||||
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"),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
14
src/web/alerts/TTSTypes.ts
Normal file
14
src/web/alerts/TTSTypes.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export type MSTTS = {
|
||||||
|
type: "microsoft";
|
||||||
|
text: string;
|
||||||
|
voice: string;
|
||||||
|
pitch: string;
|
||||||
|
speed: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DecTalk = {
|
||||||
|
type: "dectalk";
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TTS = MSTTS | DecTalk;
|
||||||
@@ -11,3 +11,39 @@ export async function playAlert(alert: alert) {
|
|||||||
alert,
|
alert,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MSTTSOptions = {
|
||||||
|
text: string;
|
||||||
|
voice?: string;
|
||||||
|
pitch?: number;
|
||||||
|
speed?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function playMSTTS(options: MSTTSOptions) {
|
||||||
|
await sendAlertEvent({
|
||||||
|
function: "playTTS",
|
||||||
|
tts: {
|
||||||
|
type: "microsoft",
|
||||||
|
voice: options.voice ?? "Sam",
|
||||||
|
pitch: options.pitch ? options.pitch.toString() : "100",
|
||||||
|
speed: options.speed ? options.speed.toString() : "150",
|
||||||
|
text: options.text,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function playDecTalk(input: string) {
|
||||||
|
await sendAlertEvent({
|
||||||
|
function: "playTTS",
|
||||||
|
tts: {
|
||||||
|
type: "dectalk",
|
||||||
|
text: input,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopTTS() {
|
||||||
|
await sendAlertEvent({
|
||||||
|
function: "cancelTTS",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { serverNotificationEvent } from "web/serverTypes";
|
import type { serverNotificationEvent } from "web/serverTypes";
|
||||||
|
import type { TTS } from "./TTSTypes";
|
||||||
|
|
||||||
type alertBase<name extends string> = {
|
type alertBase<name extends string> = {
|
||||||
name: name;
|
name: name;
|
||||||
@@ -21,7 +22,13 @@ export type tntExplosionAlert = alertBase<"tntExplosion"> & {
|
|||||||
targets: string[];
|
targets: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type soundAlerts = "mrockmadhouse" | "eddiescream" | "ripbozo";
|
export type soundAlerts =
|
||||||
|
| "mrockmadhouse"
|
||||||
|
| "eddiescream"
|
||||||
|
| "ripbozo"
|
||||||
|
| "factorioalert"
|
||||||
|
| "fail1"
|
||||||
|
| "fail2";
|
||||||
|
|
||||||
export type soundAlert = alertBase<"sound"> & {
|
export type soundAlert = alertBase<"sound"> & {
|
||||||
sound: soundAlerts;
|
sound: soundAlerts;
|
||||||
@@ -45,4 +52,17 @@ type playAlertEvent = {
|
|||||||
alert: alert;
|
alert: alert;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type alertEventData = playAlertEvent | serverNotificationEvent;
|
type playTTS = {
|
||||||
|
function: "playTTS";
|
||||||
|
tts: TTS;
|
||||||
|
};
|
||||||
|
|
||||||
|
type cancelTTS = {
|
||||||
|
function: "cancelTTS";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type alertEventData =
|
||||||
|
| playAlertEvent
|
||||||
|
| playTTS
|
||||||
|
| cancelTTS
|
||||||
|
| serverNotificationEvent;
|
||||||
|
|||||||
BIN
src/web/alerts/www/assets/explosions/factorioexplosion1.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorioexplosion1.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factorioexplosion2.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorioexplosion2.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factorioexplosion3.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorioexplosion3.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factorioexplosion4.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorioexplosion4.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factorioexplosion5.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorioexplosion5.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factoriolargeexplosion1.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factoriolargeexplosion1.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factoriolargeexplosion2.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factoriolargeexplosion2.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/explosions/factorionuke.ogg
Executable file
BIN
src/web/alerts/www/assets/explosions/factorionuke.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/factorioalert.ogg
Executable file
BIN
src/web/alerts/www/assets/factorioalert.ogg
Executable file
Binary file not shown.
BIN
src/web/alerts/www/assets/fail1.ogg
Normal file
BIN
src/web/alerts/www/assets/fail1.ogg
Normal file
Binary file not shown.
BIN
src/web/alerts/www/assets/fail2.ogg
Normal file
BIN
src/web/alerts/www/assets/fail2.ogg
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
55
src/web/alerts/www/src/TTSManager.ts
Normal file
55
src/web/alerts/www/src/TTSManager.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import type { TTS } from "web/alerts/TTSTypes";
|
||||||
|
|
||||||
|
class TTSManager {
|
||||||
|
private busy: boolean;
|
||||||
|
private ttsQueue: TTS[];
|
||||||
|
private audio: HTMLAudioElement;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.busy = false;
|
||||||
|
this.ttsQueue = [];
|
||||||
|
this.audio = new Audio();
|
||||||
|
document.body.appendChild(this.audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async playAudio(url: string) {
|
||||||
|
return new Promise((res) => {
|
||||||
|
this.audio.src = url;
|
||||||
|
this.audio.load();
|
||||||
|
this.audio.play();
|
||||||
|
this.audio.onended = res;
|
||||||
|
this.audio.onpause = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async playTTS(tts: TTS) {
|
||||||
|
this.busy = true;
|
||||||
|
switch (tts.type) {
|
||||||
|
case "microsoft":
|
||||||
|
await this.playAudio(
|
||||||
|
`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();
|
||||||
|
if (!newTTS) {
|
||||||
|
this.busy = false;
|
||||||
|
return;
|
||||||
|
} else this.playTTS(newTTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public queueTTS(input: TTS) {
|
||||||
|
if (this.busy) this.ttsQueue.push(input);
|
||||||
|
else this.playTTS(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public endTTS() {
|
||||||
|
this.audio.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TTSManager();
|
||||||
@@ -5,7 +5,7 @@ function easeOutQuad(t: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function execute(alert: blastinRoulette) {
|
export default async function execute(alert: blastinRoulette) {
|
||||||
const audio = new Audio("/alerts/public/mariokartbox.ogg");
|
const audio = new Audio("/alerts/assets/mariokartbox.ogg");
|
||||||
audio.play();
|
audio.play();
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("blastin-roulette");
|
div.classList.add("blastin-roulette");
|
||||||
|
|||||||
@@ -2,18 +2,19 @@ import type { grenadeExplosionAlert } from "web/alerts/types";
|
|||||||
import type { AlertRunner } from "./index";
|
import type { AlertRunner } from "./index";
|
||||||
|
|
||||||
const duration = 1000;
|
const duration = 1000;
|
||||||
const volume = 0.1;
|
const volume = 1;
|
||||||
|
|
||||||
export default async function execute(
|
export default async function execute(
|
||||||
alert: grenadeExplosionAlert,
|
alert: grenadeExplosionAlert,
|
||||||
): Promise<AlertRunner> {
|
): Promise<AlertRunner> {
|
||||||
const audio = new Audio("/alerts/public/explosion2.ogg");
|
const audioPath = `/alerts/assets/explosions/factorioexplosion${Math.ceil(Math.random() * 5)}.ogg`;
|
||||||
|
const audio = new Audio(audioPath);
|
||||||
audio.volume = volume;
|
audio.volume = volume;
|
||||||
|
|
||||||
const parentDiv = document.createElement("div");
|
const parentDiv = document.createElement("div");
|
||||||
parentDiv.className = "grenadeExplosionAlert";
|
parentDiv.className = "grenadeExplosionAlert";
|
||||||
parentDiv.innerHTML = `
|
parentDiv.innerHTML = `
|
||||||
<img src="/alerts/public/getrekt.jpg">
|
<img src="/alerts/assets/getrekt.jpg">
|
||||||
<span class="thrower">
|
<span class="thrower">
|
||||||
${alert.user}
|
${alert.user}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { soundAlert } from "web/alerts/types";
|
|||||||
import type { AlertRunner } from "./index";
|
import type { AlertRunner } from "./index";
|
||||||
|
|
||||||
export default async function execute(alert: soundAlert): Promise<AlertRunner> {
|
export default async function execute(alert: soundAlert): Promise<AlertRunner> {
|
||||||
const audio = new Audio(`/alerts/public/${alert.sound}.ogg`);
|
const audio = new Audio(`/alerts/assets/${alert.sound}.ogg`);
|
||||||
audio.play();
|
audio.play();
|
||||||
return {
|
return {
|
||||||
blocking: false,
|
blocking: false,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default async function execute(
|
|||||||
const video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
|
|
||||||
video.volume = volume;
|
video.volume = volume;
|
||||||
video.src = "/alerts/public/tnt.mp4";
|
video.src = "/alerts/assets/tnt.mp4";
|
||||||
video.autoplay = true;
|
video.autoplay = true;
|
||||||
video.height = 800;
|
video.height = 800;
|
||||||
video.width = 450;
|
video.width = 450;
|
||||||
|
|||||||
@@ -2,18 +2,19 @@ import type { userBlastAlert } from "web/alerts/types";
|
|||||||
import type { AlertRunner } from "./index";
|
import type { AlertRunner } from "./index";
|
||||||
|
|
||||||
const duration = 1000;
|
const duration = 1000;
|
||||||
const volume = 0.1;
|
const volume = 1;
|
||||||
|
|
||||||
export default async function execute(
|
export default async function execute(
|
||||||
alert: userBlastAlert,
|
alert: userBlastAlert,
|
||||||
): Promise<AlertRunner> {
|
): Promise<AlertRunner> {
|
||||||
const audio = new Audio("/alerts/public/explosion1.ogg");
|
const audioPath = `/alerts/assets/explosions/factorioexplosion${Math.ceil(Math.random() * 5)}.ogg`;
|
||||||
|
const audio = new Audio(audioPath);
|
||||||
audio.volume = volume;
|
audio.volume = volume;
|
||||||
|
|
||||||
const parentDiv = document.createElement("div");
|
const parentDiv = document.createElement("div");
|
||||||
parentDiv.className = "userBlastAlert";
|
parentDiv.className = "userBlastAlert";
|
||||||
parentDiv.innerHTML = `
|
parentDiv.innerHTML = `
|
||||||
<img src="/alerts/public/getrekt.jpg">
|
<img src="/alerts/assets/getrekt.jpg">
|
||||||
<span class="shooter">
|
<span class="shooter">
|
||||||
${alert.user}
|
${alert.user}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { userExecutionAlert } from "web/alerts/types";
|
|||||||
import type { AlertRunner } from "./index";
|
import type { AlertRunner } from "./index";
|
||||||
|
|
||||||
const duration = 3000;
|
const duration = 3000;
|
||||||
const volume = 0.1;
|
const volume = 1;
|
||||||
|
|
||||||
export default async function execute(
|
export default async function execute(
|
||||||
alert: userExecutionAlert,
|
alert: userExecutionAlert,
|
||||||
@@ -10,7 +10,7 @@ export default async function execute(
|
|||||||
const parentDiv = document.createElement("div");
|
const parentDiv = document.createElement("div");
|
||||||
parentDiv.className = "userExecutionAlert";
|
parentDiv.className = "userExecutionAlert";
|
||||||
parentDiv.innerHTML = `
|
parentDiv.innerHTML = `
|
||||||
<img src="/alerts/public/getrekt.jpg">
|
<img src="/alerts/assets/getrekt.jpg">
|
||||||
<span class="shooter">
|
<span class="shooter">
|
||||||
${alert.user}
|
${alert.user}
|
||||||
</span>
|
</span>
|
||||||
@@ -56,9 +56,13 @@ export default async function execute(
|
|||||||
const randomX = Math.floor(Math.random() * (window.innerWidth - 800));
|
const randomX = Math.floor(Math.random() * (window.innerWidth - 800));
|
||||||
const randomY = Math.floor(Math.random() * (window.innerHeight - 800));
|
const randomY = Math.floor(Math.random() * (window.innerHeight - 800));
|
||||||
|
|
||||||
const audio1 = new Audio("/alerts/public/explosion1.ogg");
|
const audio1 = new Audio(
|
||||||
const audio2 = new Audio("/alerts/public/explosion2.ogg");
|
"/alerts/assets/explosions/factoriolargeexplosion1.ogg",
|
||||||
const audio3 = new Audio("/alerts/public/explosion3.ogg");
|
);
|
||||||
|
const audio2 = new Audio(
|
||||||
|
"/alerts/assets/explosions/factoriolargeexplosion1.ogg",
|
||||||
|
);
|
||||||
|
const audio3 = new Audio("/alerts/assets/explosions/factorionuke.ogg");
|
||||||
|
|
||||||
audio1.volume = volume;
|
audio1.volume = volume;
|
||||||
audio2.volume = volume;
|
audio2.volume = volume;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { alertEventData } from "web/alerts/types";
|
import type { alertEventData } from "web/alerts/types";
|
||||||
import type { serverInstruction } from "web/serverTypes";
|
import type { serverInstruction } from "web/serverTypes";
|
||||||
import alertManager from "./alertManager";
|
import AlertManager from "./AlertManager";
|
||||||
|
// @ts-ignore
|
||||||
import "@fontsource/jersey-15";
|
import "@fontsource/jersey-15";
|
||||||
|
import TTSManager from "./TTSManager";
|
||||||
|
|
||||||
const wsAddress = `ws${location.protocol === "https:" ? "s" : ""}://${location.host}`;
|
const wsAddress = `ws${location.protocol === "https:" ? "s" : ""}://${location.host}`;
|
||||||
|
|
||||||
@@ -19,7 +21,13 @@ socket.onmessage = (event) => {
|
|||||||
const data: alertEventData = JSON.parse(event.data);
|
const data: alertEventData = JSON.parse(event.data);
|
||||||
switch (data.function) {
|
switch (data.function) {
|
||||||
case "playAlert":
|
case "playAlert":
|
||||||
alertManager.queueAlert(data.alert);
|
AlertManager.queueAlert(data.alert);
|
||||||
|
break;
|
||||||
|
case "playTTS":
|
||||||
|
TTSManager.queueTTS(data.tts);
|
||||||
|
break;
|
||||||
|
case "cancelTTS":
|
||||||
|
TTSManager.endTTS();
|
||||||
break;
|
break;
|
||||||
case "serverNotification":
|
case "serverNotification":
|
||||||
console.log(data.message);
|
console.log(data.message);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
// @ts-ignore
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
// @ts-ignore
|
||||||
import "@fontsource/jersey-15";
|
import "@fontsource/jersey-15";
|
||||||
|
|
||||||
import type { twitchEventData } from "web/chatWidget/websockettypes";
|
import type { twitchEventData } from "web/chatWidget/websockettypes";
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ export default Bun.serve({
|
|||||||
"/chat/getEmotes": getExternalEmotes,
|
"/chat/getEmotes": getExternalEmotes,
|
||||||
|
|
||||||
"/alerts": alerts,
|
"/alerts": alerts,
|
||||||
"/alerts/public/:filename": async (req) => {
|
"/alerts/assets/*": async (req) => {
|
||||||
const target = req.params.filename;
|
const url = new URL(req.url);
|
||||||
const file = Bun.file(`${import.meta.dir}/alerts/www/public/${target}`);
|
const path = url.pathname.slice("/alerts/assets/".length);
|
||||||
|
const file = Bun.file(`${import.meta.dir}/alerts/www/assets/${path}`);
|
||||||
if (!(await file.exists()))
|
if (!(await file.exists()))
|
||||||
return new Response(`${target} not found`, { status: 404 });
|
return new Response(`404: ${url.pathname} not found`, {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
return new Response(file);
|
return new Response(file);
|
||||||
},
|
},
|
||||||
"/": async (req, srv) => {
|
"/": async (req, srv) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"files": [],
|
"files": [],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"noEmit": true,
|
||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"lib/*": ["./lib/*"],
|
"lib/*": ["./lib/*"],
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "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