mirror of
https://gitlab.com/qwerinope/qweribot.git
synced 2026-02-04 11:06:59 +01:00
Compare commits
29 Commits
55ad3d3b1c
...
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
|
|||
|
c9273afa88
|
|||
|
530071a907
|
|||
|
af946e59b8
|
|||
|
253775a66e
|
|||
|
d9c9f0c217
|
30
README.md
30
README.md
@@ -19,6 +19,7 @@ Invulns don't need moderator or vip status in the channel.
|
||||
The chatterbot and streamer always are invuln and cannot be stripped of this status.
|
||||
Moderators can add and remove invulns.
|
||||
On your first message in chat you will recieve 10 minutes of invuln status.
|
||||
When a moderator adds you as invuln you will lose all items and qbucks.
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -121,6 +122,25 @@ The enable and disable redeem commands have a way to enable/disable all sound al
|
||||
|
||||
When running the development database and twitch api application, the redeems will not get created. This is because twitch only allows editing or deleting rewards when the same application created the reward. (fucking stupid)
|
||||
|
||||
### Welcome message
|
||||
|
||||
Welcome message in the context of this project means 2 things:
|
||||
- The message the bot says when it's the first time you're chatting in the channel.
|
||||
- The personalized message the bot says when you chat for the first time during a stream.
|
||||
|
||||
The personalized message can be set by using the channel point redemption. The message cannot be longer than 200 characters.
|
||||
|
||||
### TTS
|
||||
|
||||
There are 2 types of TTS supported:
|
||||
|
||||
- Microsoft Sam
|
||||
- DecTalk
|
||||
|
||||
Microsoft s(c)am is currently only used by `a_n_i_v` and `a_n_e_e_v`. Whenever the aniv clankers say something MS Sam will pronounce their message.
|
||||
|
||||
Dectalk is available for 25k channel points. If your message is longer than 2 minutes your message will get scammed. I'm not sorry, 100% deserved.
|
||||
|
||||
### Chatterbot/streamerbot
|
||||
|
||||
This depends on if the `CHATTER_IS_STREAMER` environment variable is set.
|
||||
@@ -150,6 +170,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
||||
`anivtimeouts`|Get the amount of timeouts, dodges and dodge percentage from aniv timeouts [(info)](#aniv-timeouts)|anyone|`anivtimeouts` `anivtimeout`|:white_check_mark:
|
||||
`racetime`|Get the racetime.gg room the streamer is currently in. Needs to have twitch linked to racetime account|anyone|`racetime` `raceroom`|:white_check_mark:
|
||||
`randomchatter`|Get a random chatter for whatever reason|moderators|`randomchatter`|:white_check_mark:
|
||||
`economy`|Get a count of all items in circulation|anyone|`economy` `eco`|:white_check_mark:
|
||||
|
||||
### Qweribucks/Item commands
|
||||
|
||||
@@ -186,7 +207,7 @@ COMMAND|FUNCTION|USER|ALIASES|DISABLEABLE
|
||||
`getadmins`|Get a list of every admin in the channel|anyone|`getadmins`|:x:
|
||||
`itemlock {target}`|Toggle the itemlock on the specified target|moderator|`itemlock`|:x:
|
||||
`testcheer {amount} [args]`|Create a fake cheering event|streamer/chatterbot|`testcheer`|:x:
|
||||
`addinvuln {target}`|Adds an invuln user|moderator|`addinvuln`|:x:
|
||||
`addinvuln {target}`|Adds an invuln user and wipes the user's inventory and wallet|moderator|`addinvuln`|:x:
|
||||
`removeinvuln {target}`|Removes an invuln user|moderator|`removeinvuln`|:x:
|
||||
`addbot {target}`|Adds bot status to a specific chatter|streamer/chatterbot|`addbot`|:x:
|
||||
`removebot {target}`|Removes bot status from a specific chatter|streamer/chatterbot|`removebot`|:x:
|
||||
@@ -205,7 +226,7 @@ NAME|COMMAND|FUNCTION|ALIASES|COST
|
||||
-|-|-|-|-
|
||||
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
|
||||
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
||||
Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
||||
Silver Bullet|`silverbullet [target]`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}` `{fuck}`|666
|
||||
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
||||
|
||||
## Cheers
|
||||
@@ -217,12 +238,17 @@ NAME|AMOUNT|USAGE|FUNCTION
|
||||
`superloot`|150|`cheer150`|Get superloot. Details and drop rates can be found [(here)](#lootbox).
|
||||
`execute`|666|`cheer666 [target]`|Times specified or random vulnerable user out for 30 minutes. On failure gives cheerer a silver bullet
|
||||
`tnt`|1000|`cheer1000`|Gives 5-10 random vulnerable chatters 60 second timeouts. On failure gives cheerer a TNT
|
||||
`realsilverbullet`|6666|`cheer6666 [target]`|Times specified or random vulnerable chatter out for 24 hours. On failure gives the user nothing. Get scammed.
|
||||
|
||||
## Point Redeems
|
||||
|
||||
NAME|COST|DESCRIPTION|INTERNALNAME
|
||||
-|-|-|-
|
||||
`Dectalk TTS`|25000|Play a custom dectalk TTS. If the sound is too long you WILL get scammed.|`dectalk`
|
||||
`Set welcome message`|15000|Set the message the bot will say when you first chat during a stream (character limit is 200)|`setwelcomemsg`
|
||||
`FREE MONEY`|1000|Get 100 qbucks|`qbucksredeem`
|
||||
`RIPBOZO`|500|Sound: Coffeezilla calls me a conman [(source)](https://www.youtube.com/watch?v=QRvEGn7i-wM)|`sfxripbozo`
|
||||
`Welcome to the Madhouse`|100|Sound: mrockstar20 says: "Welcome to the Madhouse"|`sfxmrockmadhouse`
|
||||
`Eddie Scream`|100|Sound: Eddie screams|`sfxeddiescream`
|
||||
`Factorio Building Destroyed`|100|Sound: Factorio Building Destroyed alert|`sfxfactorioalert`
|
||||
`Fail`|100|Sound: Either the sad trumpet or trombone meme sound|`sfxfail`
|
||||
|
||||
44
biome.json
Normal file
44
biome.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noNonNullAssertedOptionalChain": "off",
|
||||
"noExplicitAny": "off",
|
||||
"noControlCharactersInRegex": "off",
|
||||
"noTsIgnore": "off"
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"semicolons": "always"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
130
bun.lock
130
bun.lock
@@ -6,39 +6,51 @@
|
||||
"name": "qweribot",
|
||||
"dependencies": {
|
||||
"@fontsource/jersey-15": "^5.2.8",
|
||||
"@twurple/api": "7.4.0",
|
||||
"@twurple/auth": "^7.4.0",
|
||||
"@twurple/eventsub-http": "^7.4.0",
|
||||
"discord.js": "^14.24.0",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"kleur": "^4.1.5",
|
||||
"@twurple/api": "8.0.3",
|
||||
"@twurple/auth": "^8.0.3",
|
||||
"@twurple/eventsub-http": "^8.0.3",
|
||||
"discord.js": "^14.25.1",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
||||
"@biomejs/biome": "^2.3.13",
|
||||
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||
"@types/bun": "latest",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"pg": "^8.16.3",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"pg": "^8.17.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.3",
|
||||
"typescript": "^5.9.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@d-fischer/cache-decorators": ["@d-fischer/cache-decorators@4.0.1", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-HNYLBLWs/t28GFZZeqdIBqq8f37mqDIFO6xNPof94VjpKvuP6ROqCZGafx88dk5zZUlBfViV9jD8iNNlXfc4CA=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.3.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.13", "@biomejs/cli-darwin-x64": "2.3.13", "@biomejs/cli-linux-arm64": "2.3.13", "@biomejs/cli-linux-arm64-musl": "2.3.13", "@biomejs/cli-linux-x64": "2.3.13", "@biomejs/cli-linux-x64-musl": "2.3.13", "@biomejs/cli-win32-arm64": "2.3.13", "@biomejs/cli-win32-x64": "2.3.13" }, "bin": { "biome": "bin/biome" } }, "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA=="],
|
||||
|
||||
"@d-fischer/cross-fetch": ["@d-fischer/cross-fetch@5.0.5", "", { "dependencies": { "node-fetch": "^2.6.12" } }, "sha512-symjDUPInTrkfIsZc2n2mo9hiAJLcTJsZkNICjZajEWnWpJ3s3zn50/FY8xpNUAf5w3eFuQii2wxztTGpvG1Xg=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-AGr8OoemT/ejynbIu56qeil2+F2WLkIjn2d8jGK1JkchxnMUhYOfnqc9sVzcRxpG9Ycvw4weQ5sprRvtb7Yhcw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-xvOiFkrDNu607MPMBUQ6huHmBG1PZLOrqhtK6pXJW3GjfVqJg0Z/qpTdhXfcqWdSZHcT+Nct2fOgewZvytESkw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-TUdDCSY+Eo/EHjhJz7P2GnWwfqet+lFxBZzGHldrvULr59AgahamLs/N85SC4+bdF86EhqDuuw9rYLvLFWWlXA=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-s+YsZlgiXNq8XkgHs6xdvKDFOj/bwTEevqEY6rC2I3cBHbxXYU1LOZstH3Ffw9hE5tE1sqT7U23C00MzkXztMw=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.13", "", { "os": "linux", "cpu": "x64" }, "sha512-0bdwFVSbbM//Sds6OjtnmQGp4eUjOTt6kHvR/1P0ieR9GcTUAlPNvPC3DiavTqq302W34Ae2T6u5VVNGuQtGlQ=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-QweDxY89fq0VvrxME+wS/BXKmqMrOTZlN9SqQ79kQSIc3FrEwvW/PvUegQF6XIVaekncDykB5dzPqjbwSKs9DA=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.13", "", { "os": "win32", "cpu": "x64" }, "sha512-trDw2ogdM2lyav9WFQsdsfdVy1dvZALymRpgmWsvSez0BJzBjulhOT/t+wyKeh3pZWvwP3VMs1SoOKwO3wecMQ=="],
|
||||
|
||||
"@d-fischer/cache-decorators": ["@d-fischer/cache-decorators@4.0.1", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-HNYLBLWs/t28GFZZeqdIBqq8f37mqDIFO6xNPof94VjpKvuP6ROqCZGafx88dk5zZUlBfViV9jD8iNNlXfc4CA=="],
|
||||
|
||||
"@d-fischer/detect-node": ["@d-fischer/detect-node@3.0.1", "", {}, "sha512-0Rf3XwTzuTh8+oPZW9SfxTIiL+26RRJ0BRPwj5oVjZFyFKmsj9RGfN2zuTRjOuA3FCK/jYm06HOhwNK+8Pfv8w=="],
|
||||
|
||||
"@d-fischer/logger": ["@d-fischer/logger@4.2.3", "", { "dependencies": { "@d-fischer/detect-node": "^3.0.1", "@d-fischer/shared-utils": "^3.2.0", "tslib": "^2.0.3" } }, "sha512-mJUx9OgjrNVLQa4od/+bqnmD164VTCKnK5B4WOW8TX5y/3w2i58p+PMRE45gUuFjk2BVtOZUg55JQM3d619fdw=="],
|
||||
|
||||
"@d-fischer/qs": ["@d-fischer/qs@7.0.2", "", {}, "sha512-yAu3xDooiL+ef84Jo8nLjDjWBRk7RXk163Y6aTvRB7FauYd3spQD/dWvgT7R4CrN54Juhrrc3dMY7mc+jZGurQ=="],
|
||||
|
||||
"@d-fischer/rate-limiter": ["@d-fischer/rate-limiter@1.1.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.3", "@d-fischer/shared-utils": "^3.6.3", "tslib": "^2.6.2" } }, "sha512-O5HgACwApyCZhp4JTEBEtbv/W3eAwEkrARFvgWnEsDmXgCMWjIHwohWoHre5BW6IYXFSHBGsuZB/EvNL3942kQ=="],
|
||||
|
||||
"@d-fischer/raw-body": ["@d-fischer/raw-body@2.4.3", "", { "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.3", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-rtPTezQLROnTDdRij0Vo5OJ41aGvfKj9pQ7CkzFssQy+Jyc9BUVLV/DXLIGgvEGUaWt09Jq3im4WgvvPYqTomw=="],
|
||||
|
||||
"@d-fischer/shared-utils": ["@d-fischer/shared-utils@3.6.4", "", { "dependencies": { "tslib": "^2.4.1" } }, "sha512-BPkVLHfn2Lbyo/ENDBwtEB8JVQ+9OzkjJhUunLaxkw4k59YFlQxUUwlDBejVSFcpQT0t+D3CQlX+ySZnQj0wxw=="],
|
||||
|
||||
"@d-fischer/typed-event-emitter": ["@d-fischer/typed-event-emitter@3.3.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-OvSEOa8icfdWDqcRtjSEZtgJTFOFNgTjje7zaL0+nAtu2/kZtRCSK5wUMrI/aXtCH8o0Qz2vA8UqkhWUTARFQQ=="],
|
||||
@@ -47,11 +59,11 @@
|
||||
|
||||
"@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],
|
||||
|
||||
"@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="],
|
||||
"@discordjs/formatters": ["@discordjs/formatters@0.6.2", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ=="],
|
||||
|
||||
"@discordjs/rest": ["@discordjs/rest@2.6.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.16", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w=="],
|
||||
|
||||
"@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
"@discordjs/util": ["@discordjs/util@1.2.0", "", { "dependencies": { "discord-api-types": "^0.38.33" } }, "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg=="],
|
||||
|
||||
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||
|
||||
@@ -149,23 +161,23 @@
|
||||
|
||||
"@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="],
|
||||
|
||||
"@twurple/api": ["@twurple/api@7.4.0", "", { "dependencies": { "@d-fischer/cache-decorators": "^4.0.0", "@d-fischer/cross-fetch": "^5.0.1", "@d-fischer/detect-node": "^3.0.1", "@d-fischer/logger": "^4.2.1", "@d-fischer/rate-limiter": "^1.1.0", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "7.4.0", "@twurple/common": "7.4.0", "retry": "^0.13.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/auth": "7.4.0" } }, "sha512-RlXLs4ZvS8n0+iIk7YyVDwrjhlwpn+N+h7fX5Q61HoxlmzoCShmnnFo03abYw9i8Cc3deGpbQATOSVmigXM4qg=="],
|
||||
"@twurple/api": ["@twurple/api@8.0.3", "", { "dependencies": { "@d-fischer/cache-decorators": "^4.0.0", "@d-fischer/detect-node": "^3.0.1", "@d-fischer/logger": "^4.2.1", "@d-fischer/rate-limiter": "^1.1.0", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "8.0.3", "@twurple/common": "8.0.3", "retry": "^0.13.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/auth": "8.0.3" } }, "sha512-vnqVi9YlNDbCqgpUUvTIq4sDitKCY0dkTw9zPluZvRNqUB1eCsuoaRNW96HQDhKtA9P4pRzwZ8xU7v/1KU2ytg=="],
|
||||
|
||||
"@twurple/api-call": ["@twurple/api-call@7.4.0", "", { "dependencies": { "@d-fischer/cross-fetch": "^5.0.1", "@d-fischer/qs": "^7.0.2", "@d-fischer/shared-utils": "^3.6.1", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-WNxvjp/hMqZREElbvE4rHMyUIrHdGY5cbG8xbqgSM9CESFvJ1wm5BubhyANOyKd1TxABacLddbfbO//Fz9YHgA=="],
|
||||
"@twurple/api-call": ["@twurple/api-call@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-/5DBTqFjpYB+qqOkkFzoTWE79a7+I8uLXmBIIIYjGoq/CIPxKcHnlemXlU8cQhTr87PVa3th8zJXGYiNkpRx8w=="],
|
||||
|
||||
"@twurple/auth": ["@twurple/auth@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "7.4.0", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-WAQV6nJGkfY7r2BkRYhnzUpdfozLvjNsCxkyNVprl4dCWdJzccnTvqkKTdDRJc5ZJxDVaB9Drzwx9/fCp/gRDA=="],
|
||||
"@twurple/auth": ["@twurple/auth@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.1", "@twurple/api-call": "8.0.3", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-Xlv+WNXmGQir4aBXYeRCqdno5XurA6jzYTIovSEHa7FZf3AMHMFqtzW7yqTCUn4iOahfUSA2TIIxmxFM0wis0g=="],
|
||||
|
||||
"@twurple/common": ["@twurple/common@7.4.0", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "klona": "^2.0.4", "tslib": "^2.0.3" } }, "sha512-lX5cVkYar6jGvni6iLmMYjhxH1oPSl2v7XVeZ4C7U1GbLz/Jwk0L0uldQNGUIf9gpRHPY+TXRlk0UIpz2yo8DA=="],
|
||||
"@twurple/common": ["@twurple/common@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "klona": "^2.0.4", "tslib": "^2.0.3" } }, "sha512-JQ2lb5qSFT21Y9qMfIouAILb94ppedLHASq49Fe/AP8oq0k3IC9Q7tX2n6tiMzGWqn+n8MnONUpMSZ6FhulMXA=="],
|
||||
|
||||
"@twurple/eventsub-base": ["@twurple/eventsub-base@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/api": "7.4.0", "@twurple/auth": "7.4.0", "@twurple/common": "7.4.0", "tslib": "^2.0.3" } }, "sha512-Umx0kNZKxBUTF2/MHAlnnCuNPs8Tl1Aw8EzDJI2AW10tOiWvgeCR889fKCFBPlHXvcMYSEvsItkX+pXeZ8GkeQ=="],
|
||||
"@twurple/eventsub-base": ["@twurple/eventsub-base@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/api": "8.0.3", "@twurple/auth": "8.0.3", "@twurple/common": "8.0.3", "tslib": "^2.0.3" } }, "sha512-59G5xJbHWLTSO6NAgwtkHPfIlmdjrABgiEumFnHhNusMbLM9qdA+kLcW5NB2NImNliytl6zZtqY92FInzUE6NA=="],
|
||||
|
||||
"@twurple/eventsub-http": ["@twurple/eventsub-http@7.4.0", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/raw-body": "^2.4.3", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/auth": "7.4.0", "@twurple/common": "7.4.0", "@twurple/eventsub-base": "7.4.0", "@types/express-serve-static-core": "^4.17.24", "httpanda": "^0.4.6", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "7.4.0" } }, "sha512-Ua8cP4OPfgyUxlNG2BSJ2Ck02Axk3YXIBoQqoTURlSI0wix8+kTK0X4QuDvxicPxn9iRV1prNimOSvt4HXSkrQ=="],
|
||||
"@twurple/eventsub-http": ["@twurple/eventsub-http@8.0.3", "", { "dependencies": { "@d-fischer/logger": "^4.2.1", "@d-fischer/shared-utils": "^3.6.1", "@d-fischer/typed-event-emitter": "^3.3.0", "@twurple/auth": "8.0.3", "@twurple/common": "8.0.3", "@twurple/eventsub-base": "8.0.3", "@types/express-serve-static-core": "^5.1.0", "httpanda": "^0.4.6", "raw-body": "^3.0.2", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "8.0.3" } }, "sha512-ds8l01GfsIC0hhILepv/UUn/Ix8s0wLg9aGy10xWaG9/Hlfe82NPI8gAg0LYsmlCsOADPwJZSckMTGPJrpw1Iw=="],
|
||||
|
||||
"@twurple/eventsub-ngrok": ["@twurple/eventsub-ngrok@7.4.0", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@ngrok/ngrok": "^0.5.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "7.4.0", "@twurple/eventsub-http": "7.4.0" } }, "sha512-YPk4TtYmCFQwBuIUgEpd6D29wqhJJQq6fxjWG83E86lp3vfcaY6aq7kE39kSqTz8TDkx62xnSH9lsMmZImIQ0w=="],
|
||||
"@twurple/eventsub-ngrok": ["@twurple/eventsub-ngrok@8.0.3", "", { "dependencies": { "@d-fischer/shared-utils": "^3.6.1", "@ngrok/ngrok": "^0.5.1", "tslib": "^2.0.3" }, "peerDependencies": { "@twurple/api": "8.0.3", "@twurple/eventsub-http": "8.0.3" } }, "sha512-wt4keLIivnEpv0EpQw1zgBD6tinaDmVf5VhvQqr8NABCpL4TuZNQAIveIUelHmY+phlISIX/42mvXqHNfmMTwg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
||||
"@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="],
|
||||
|
||||
"@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg=="],
|
||||
"@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="],
|
||||
|
||||
"@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="],
|
||||
|
||||
@@ -173,8 +185,6 @@
|
||||
|
||||
"@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||
|
||||
"@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
@@ -183,23 +193,21 @@
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
||||
"bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="],
|
||||
|
||||
"bytes": ["bytes@3.1.0", "", {}, "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
"discord-api-types": ["discord-api-types@0.38.36", "", {}, "sha512-qrbUbjjwtyeBg5HsAlm1C859epfOyiLjPqAOzkdWlCNsZCWJrertnETF/NwM8H+waMFU58xGSc5eXUfXah+WTQ=="],
|
||||
|
||||
"discord.js": ["discord.js@14.24.0", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.31", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-KNq/ekT8bsmT3ZAfVre8cPbl+DfVYSdlLnDmGZPoz7Cw21LYeWHllRA9MivqNq5b1GPGAxGvyUN1vxbTb/PQWw=="],
|
||||
"discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="],
|
||||
|
||||
"drizzle-kit": ["drizzle-kit@0.31.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg=="],
|
||||
"drizzle-kit": ["drizzle-kit@0.31.8", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="],
|
||||
|
||||
"drizzle-orm": ["drizzle-orm@0.44.6", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ=="],
|
||||
"drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="],
|
||||
|
||||
@@ -209,16 +217,14 @@
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
||||
|
||||
"http-errors": ["http-errors@1.7.3", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } }, "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw=="],
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"httpanda": ["httpanda@0.4.7", "", { "dependencies": { "@types/node": "^14.11.2", "tslib": "^2.0.3" } }, "sha512-NieTiR7kfOheL9OeEi6+JKFmJ2JP9ZRqUQ4tiXZ9J+EMMKxApHUQlEM5l4gZ+l67lxE9Er6oigZnujmhlodNCg=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||
|
||||
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
@@ -229,19 +235,17 @@
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
"pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="],
|
||||
|
||||
"pg": ["pg@8.16.3", "", { "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", "pg-protocol": "^1.10.3", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.7" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw=="],
|
||||
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
|
||||
|
||||
"pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="],
|
||||
|
||||
"pg-connection-string": ["pg-connection-string@2.9.1", "", {}, "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="],
|
||||
"pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
|
||||
|
||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||
|
||||
"pg-pool": ["pg-pool@3.10.1", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg=="],
|
||||
"pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="],
|
||||
|
||||
"pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="],
|
||||
"pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
|
||||
|
||||
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
@@ -255,13 +259,15 @@
|
||||
|
||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.1.1", "", {}, "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="],
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
@@ -269,11 +275,9 @@
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||
|
||||
@@ -287,18 +291,28 @@
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||
|
||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"@discordjs/builders/@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="],
|
||||
|
||||
"@discordjs/builders/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/builders/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@discordjs/rest/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/rest/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@discordjs/ws/@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/ws/discord-api-types": ["discord-api-types@0.38.31", "", {}, "sha512-kC94ANsk8ackj8ENTuO8joTNEL0KtymVhHy9dyEC/s4QAZ7GCx40dYEzQaadyo8w+oP0X8QydE/nzAWRylTGtQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"httpanda/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
in {
|
||||
devShells."${system}" = {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [ bun nodejs deno ];
|
||||
packages = with pkgs; [ bun biome ];
|
||||
shellHook = ''
|
||||
echo Loaded the qweribot dev shell
|
||||
'';
|
||||
|
||||
19
package.json
19
package.json
@@ -2,12 +2,14 @@
|
||||
"name": "qweribot",
|
||||
"module": "src/index.ts",
|
||||
"devDependencies": {
|
||||
"@twurple/eventsub-ngrok": "^7.4.0",
|
||||
"@biomejs/biome": "^2.3.13",
|
||||
"@twurple/eventsub-ngrok": "^8.0.3",
|
||||
"@types/bun": "latest",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"pg": "^8.16.3"
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"pg": "^8.17.2"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "biome check && tsc -b",
|
||||
"start": "NODE_ENV=production bun src/index.ts",
|
||||
"start-dev": "NODE_ENV=development bun src/index.ts",
|
||||
"start-discord": "NODE_ENV=production bun src/discord/index.ts",
|
||||
@@ -24,11 +26,10 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource/jersey-15": "^5.2.8",
|
||||
"@twurple/api": "7.4.0",
|
||||
"@twurple/auth": "^7.4.0",
|
||||
"@twurple/eventsub-http": "^7.4.0",
|
||||
"discord.js": "^14.24.0",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"kleur": "^4.1.5"
|
||||
"@twurple/api": "8.0.3",
|
||||
"@twurple/auth": "^8.0.3",
|
||||
"@twurple/eventsub-http": "^8.0.3",
|
||||
"discord.js": "^14.25.1",
|
||||
"drizzle-orm": "^0.45.1"
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
]);
|
||||
});
|
||||
});
|
||||
115
src/auth.ts
115
src/auth.ts
@@ -1,26 +1,43 @@
|
||||
import { RefreshingAuthProvider, exchangeCode, type AccessToken } from "@twurple/auth";
|
||||
import { createAuthRecord, deleteAuthRecord, getAuthRecord, updateAuthRecord } from "db/dbAuth";
|
||||
|
||||
import {
|
||||
type AccessToken,
|
||||
exchangeCode,
|
||||
RefreshingAuthProvider,
|
||||
} from "@twurple/auth";
|
||||
import {
|
||||
createAuthRecord,
|
||||
deleteAuthRecord,
|
||||
getAuthRecord,
|
||||
updateAuthRecord,
|
||||
} from "db/dbAuth";
|
||||
import logger from "lib/logger";
|
||||
import kleur from "kleur";
|
||||
|
||||
async function initAuth(userId: string, clientId: string, clientSecret: string, requestedIntents: string[], streamer: boolean): Promise<AccessToken> {
|
||||
const port = process.env.REDIRECT_PORT ?? 3456
|
||||
async function initAuth(
|
||||
userId: string,
|
||||
clientId: string,
|
||||
clientSecret: string,
|
||||
requestedIntents: string[],
|
||||
streamer: boolean,
|
||||
): Promise<AccessToken> {
|
||||
const port = process.env.REDIRECT_PORT ?? 3456;
|
||||
const redirectURL = process.env.REDIRECT_URL ?? `http://localhost:${port}`;
|
||||
// Set the default url and port to http://localhost:3456
|
||||
|
||||
const state = Bun.randomUUIDv7().replace(/-/g, "").slice(0, 32).toUpperCase();
|
||||
// Generate random state variable to prevent cross-site-scripting attacks
|
||||
|
||||
const instruction = `Visit this URL as ${kleur.red().underline().italic(streamer ? 'the streamer' : 'the chatter')} to authenticate the bot.`
|
||||
const instruction = `Visit this URL as \x1b[3;4;1;95m${streamer ? "the streamer" : "the chatter"}\x1b[0;97m to authenticate the bot.`;
|
||||
logger.info(instruction);
|
||||
logger.info(`https://id.twitch.tv/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectURL}&response_type=code&scope=${requestedIntents.join('+')}&state=${state}`);
|
||||
logger.info(
|
||||
`\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 = () => {
|
||||
let resolver: (code: string) => void;
|
||||
const promise = new Promise<string>((resolve) => { resolver = resolve; });
|
||||
const promise = new Promise<string>((resolve) => {
|
||||
resolver = resolve;
|
||||
});
|
||||
return { promise, resolver: resolver! };
|
||||
}
|
||||
};
|
||||
|
||||
const { promise: codepromise, resolver } = createCodePromise();
|
||||
|
||||
@@ -28,14 +45,21 @@ async function initAuth(userId: string, clientId: string, clientSecret: string,
|
||||
port,
|
||||
fetch(request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
if (searchParams.has('code') && searchParams.has('state') && searchParams.get('state') === state) {
|
||||
if (
|
||||
searchParams.has("code") &&
|
||||
searchParams.has("state") &&
|
||||
searchParams.get("state") === state
|
||||
) {
|
||||
// Check if the required fields exist on the params and validate the state
|
||||
resolver(searchParams.get('code')!);
|
||||
resolver(searchParams.get("code")!);
|
||||
return new Response("Successfully authenticated!");
|
||||
} else {
|
||||
return new Response(`Authentication attempt unsuccessful, please make sure the redirect url in the twitch developer console is set to ${redirectURL} and that the bot is listening to that url & port.`, { status: 400 });
|
||||
}
|
||||
return new Response(
|
||||
`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 },
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await deleteAuthRecord(userId);
|
||||
@@ -43,12 +67,17 @@ async function initAuth(userId: string, clientId: string, clientSecret: string,
|
||||
const code = await codepromise;
|
||||
server.stop(false);
|
||||
logger.info(`Authentication code received.`);
|
||||
const tokenData = await exchangeCode(clientId, clientSecret, code, redirectURL);
|
||||
const tokenData = await exchangeCode(
|
||||
clientId,
|
||||
clientSecret,
|
||||
code,
|
||||
redirectURL,
|
||||
);
|
||||
logger.info(`Successfully authenticated code.`);
|
||||
await createAuthRecord(tokenData, userId);
|
||||
logger.ok(`Successfully saved auth data in the database.`);
|
||||
return tokenData;
|
||||
};
|
||||
}
|
||||
|
||||
export type authProviderInstructions = {
|
||||
userId: string;
|
||||
@@ -56,48 +85,72 @@ export type authProviderInstructions = {
|
||||
streamer: boolean;
|
||||
};
|
||||
|
||||
export async function createAuthProvider(data: authProviderInstructions[]): Promise<RefreshingAuthProvider> {
|
||||
export async function createAuthProvider(
|
||||
data: authProviderInstructions[],
|
||||
): Promise<RefreshingAuthProvider> {
|
||||
const clientId = process.env.CLIENT_ID;
|
||||
if (!clientId) { logger.enverr("CLIENT_ID"); process.exit(1); };
|
||||
if (!clientId) {
|
||||
logger.enverr("CLIENT_ID");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const clientSecret = process.env.CLIENT_SECRET;
|
||||
if (!clientSecret) { logger.enverr("CLIENT_SECRET"); process.exit(1); };
|
||||
if (!clientSecret) {
|
||||
logger.enverr("CLIENT_SECRET");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const authData = new RefreshingAuthProvider({
|
||||
clientId,
|
||||
clientSecret
|
||||
clientSecret,
|
||||
});
|
||||
|
||||
authData.onRefresh(async (user, token) => {
|
||||
logger.ok(`Successfully refreshed auth for user ${user}`);
|
||||
logger.ok(
|
||||
`Successfully refreshed auth for user \x1b[3;4;1;95m${user}\x1b[0;97m`,
|
||||
);
|
||||
await updateAuthRecord(user, token);
|
||||
});
|
||||
|
||||
authData.onRefreshFailure((user, err) => {
|
||||
logger.err(`Failed to refresh auth for user ${user}: ${err.name} ${err.message}`);
|
||||
logger.err(
|
||||
`Failed to refresh auth for user \x1b[3;4;1;95m${user}\x1b[0;97m: ${err.name} ${err.message}`,
|
||||
);
|
||||
});
|
||||
|
||||
for (const user of data) {
|
||||
const authRecord = await getAuthRecord(user.userId, user.intents);
|
||||
|
||||
const token = authRecord ? authRecord.accesstoken : await initAuth(user.userId, clientId, clientSecret, user.intents, user.streamer);
|
||||
const token = authRecord
|
||||
? authRecord.accesstoken
|
||||
: await initAuth(
|
||||
user.userId,
|
||||
clientId,
|
||||
clientSecret,
|
||||
user.intents,
|
||||
user.streamer,
|
||||
);
|
||||
|
||||
try {
|
||||
await authData.addUserForToken(token, user.intents);
|
||||
} catch (err) {
|
||||
logger.err(`Failed to setup user auth. Please restart the bot and re-authenticate.`);
|
||||
} catch (_err) {
|
||||
logger.err(
|
||||
`Failed to setup user auth. Please restart the bot and re-authenticate.`,
|
||||
);
|
||||
await deleteAuthRecord(user.userId);
|
||||
process.exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
await authData.refreshAccessTokenForUser(user.userId);
|
||||
} catch (err) {
|
||||
logger.err(`Failed to refresh user ${user.userId}. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`);
|
||||
} catch (_err) {
|
||||
logger.err(
|
||||
`Failed to refresh user \x1b[3;4;1;95m${user.userId}\x1b[0;97m. Please restart the bot and re-authenticate it. Make sure the user that auths the bot and the user that's defined in .env are the same.`,
|
||||
);
|
||||
await deleteAuthRecord(user.userId);
|
||||
process.exit(1);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return authData;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,71 +1,96 @@
|
||||
import { Cheer, handleNoTarget } from "cheers";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import {
|
||||
createCompensatedItemCheer,
|
||||
createTimeoutEventCheer,
|
||||
} from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import User from "user";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'silverbullet';
|
||||
const ITEMNAME = "silverbullet";
|
||||
|
||||
export default new Cheer('execute', 666, async (msg, user) => {
|
||||
export default new Cheer({
|
||||
name: "execute",
|
||||
amount: 666,
|
||||
isItem: true,
|
||||
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 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'); await handleNoTarget(msg, user, ITEMNAME, true); return; };
|
||||
}
|
||||
if (users.length === 0) {
|
||||
await sendMessage("No vulnerable chatters");
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: 'blastinRoulette',
|
||||
name: "blastinRoulette",
|
||||
user: user.displayName,
|
||||
targets: users.map(a => a.displayName),
|
||||
finaltarget: target.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 handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||
}
|
||||
if (!target) {
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
await getUserRecord(target);
|
||||
|
||||
const result = await timeout(target, `You got executed by ${user.displayName}!`, 60 * 30);
|
||||
if (result.status) await Promise.all([
|
||||
sendMessage(`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`),
|
||||
createTimeoutRecord(user, target, ITEMNAME),
|
||||
createCheerEventRecord(user, ITEMNAME),
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got executed by ${user.displayName}!`,
|
||||
60 * 30,
|
||||
);
|
||||
if (result.status)
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||
),
|
||||
createTimeoutEventCheer(user, target, "execute"),
|
||||
playAlert({
|
||||
name: 'userExecution',
|
||||
name: "userExecution",
|
||||
user: user.displayName,
|
||||
target: target.displayName
|
||||
})
|
||||
target: target.displayName,
|
||||
}),
|
||||
]);
|
||||
else {
|
||||
await handleNoTarget(msg, user, ITEMNAME);
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
switch (result.reason) {
|
||||
case "banned":
|
||||
await sendMessage(`${target.displayName} is already timed out/banned`, msg.messageId);
|
||||
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)
|
||||
timeout(user, "nah", 60),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage('Something went wrong...', msg.messageId);
|
||||
await sendMessage("Something went wrong...", msg.messageId);
|
||||
break;
|
||||
};
|
||||
};
|
||||
}, true);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Cheer, handleNoTarget } from "cheers";
|
||||
import {
|
||||
createCompensatedItemCheer,
|
||||
createTimeoutEventCheer,
|
||||
} from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||
import { Cheer, handleNoTarget } from "cheers";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'grenade';
|
||||
const ITEMNAME = "grenade";
|
||||
|
||||
export default new Cheer('grenade', 99, async (msg, user) => {
|
||||
export default new Cheer({
|
||||
name: "grenade",
|
||||
amount: 99,
|
||||
isItem: true,
|
||||
async execute(msg, user) {
|
||||
const targets = await redis.keys(`user:*:vulnerable`);
|
||||
if (targets.length === 0) { await sendMessage('No vulnerable chatters to blow up!', msg.messageId); await handleNoTarget(msg, user, ITEMNAME); return; };
|
||||
if (targets.length === 0) {
|
||||
await sendMessage("No vulnerable chatters to blow up!", msg.messageId);
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
const selection = targets[Math.floor(Math.random() * targets.length)]!;
|
||||
const target = await User.initUserId(selection.slice(5, -11));
|
||||
|
||||
@@ -21,13 +32,15 @@ export default new Cheer('grenade', 99, async (msg, user) => {
|
||||
await Promise.all([
|
||||
timeout(target!, `You got hit by ${user.displayName}'s grenade!`, 60),
|
||||
redis.del(selection),
|
||||
sendMessage(`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`),
|
||||
createTimeoutRecord(user, target!, ITEMNAME),
|
||||
createCheerEventRecord(user, ITEMNAME),
|
||||
sendMessage(
|
||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
||||
),
|
||||
createTimeoutEventCheer(user, target!, "grenade"),
|
||||
playAlert({
|
||||
name: 'grenadeExplosion',
|
||||
name: "grenadeExplosion",
|
||||
user: user.displayName,
|
||||
target: target?.displayName!
|
||||
})
|
||||
target: target?.displayName!,
|
||||
}),
|
||||
]);
|
||||
}, true);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,47 +1,84 @@
|
||||
import User from 'user';
|
||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import type User from "user";
|
||||
|
||||
export class Cheer {
|
||||
public readonly name: string;
|
||||
public readonly amount: number;
|
||||
public readonly execute: (msg: EventSubChannelChatMessageEvent, sender: User) => Promise<void>;
|
||||
public readonly isItem: boolean;
|
||||
constructor(name: string, amount: number, execution: (msg: EventSubChannelChatMessageEvent, sender: User) => Promise<void>, isItem = false) {
|
||||
this.name = name.toLowerCase();
|
||||
this.amount = amount;
|
||||
this.execute = execution;
|
||||
this.isItem = isItem;
|
||||
};
|
||||
export type cheers =
|
||||
| "execute"
|
||||
| "grenade"
|
||||
| "tnt"
|
||||
| "timeout"
|
||||
| "superloot"
|
||||
| "realsilverbullet";
|
||||
|
||||
type cheerOptions = {
|
||||
name: cheers;
|
||||
amount: number;
|
||||
isItem: boolean;
|
||||
execute: (
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
sender: User,
|
||||
) => Promise<void>;
|
||||
};
|
||||
|
||||
import { readdir } from 'node:fs/promises';
|
||||
const cheers = new Map<number, Cheer>;
|
||||
const namedcheers = new Map<string, Cheer>;
|
||||
export class Cheer {
|
||||
public readonly name: cheers;
|
||||
public readonly amount: number;
|
||||
public readonly execute: (
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
sender: User,
|
||||
) => Promise<void>;
|
||||
public readonly isItem: boolean;
|
||||
constructor(options: cheerOptions) {
|
||||
this.name = options.name;
|
||||
this.amount = options.amount;
|
||||
this.execute = options.execute;
|
||||
this.isItem = options.isItem;
|
||||
}
|
||||
}
|
||||
|
||||
import { readdir } from "node:fs/promises";
|
||||
|
||||
const cheers = new Map<number, Cheer>();
|
||||
const namedcheers = new Map<string, Cheer>();
|
||||
|
||||
const files = await readdir(import.meta.dir);
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
if (!file.endsWith(".ts")) continue;
|
||||
if (file === import.meta.file) continue;
|
||||
const cheer: Cheer = await import(import.meta.dir + '/' + file.slice(0, -3)).then(a => a.default);
|
||||
const cheer: Cheer = await import(
|
||||
`${import.meta.dir}/${file.slice(0, -3)}`
|
||||
).then((a) => a.default);
|
||||
cheers.set(cheer.amount, cheer);
|
||||
namedcheers.set(cheer.name, cheer);
|
||||
};
|
||||
}
|
||||
|
||||
export default cheers;
|
||||
export { namedcheers };
|
||||
|
||||
import { sendMessage } from 'lib/commandUtils';
|
||||
import { getUserRecord } from 'db/dbUser';
|
||||
import { changeItemCount, type items } from 'items';
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { changeItemCount, type items } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
|
||||
export async function handleNoTarget(msg: EventSubChannelChatMessageEvent, user: User, itemname: items, silent = true) {
|
||||
export async function handleNoTarget(
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
user: User,
|
||||
itemname: items,
|
||||
silent = true,
|
||||
): Promise<boolean> {
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage(`Cannot give ${user.displayName} a ${itemname} (itemlock)`, msg.messageId);
|
||||
return;
|
||||
};
|
||||
await sendMessage(
|
||||
`Cannot give ${user.displayName} a ${itemname} (itemlock)`,
|
||||
msg.messageId,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
await user.setLock();
|
||||
const userRecord = await getUserRecord(user);
|
||||
if (!silent) await sendMessage(`No (valid) target specified. You got a ${itemname}!`, msg.messageId);
|
||||
if (!silent)
|
||||
await sendMessage(
|
||||
`No (valid) target specified. You got a ${itemname}!`,
|
||||
msg.messageId,
|
||||
);
|
||||
await changeItemCount(user, userRecord, itemname, 1);
|
||||
await user.clearLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
86
src/cheers/realsilverbullet.ts
Normal file
86
src/cheers/realsilverbullet.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Cheer } from "cheers";
|
||||
import { createTimeoutEventCheer } from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
export default new Cheer({
|
||||
name: "realsilverbullet",
|
||||
amount: 6666,
|
||||
isItem: false,
|
||||
async execute(msg, user) {
|
||||
const args = parseCheerArgs(msg.messageText);
|
||||
|
||||
let target: User | null;
|
||||
if (!args[0]) {
|
||||
const vulnsids = await redis.keys("user:*:vulnerable");
|
||||
const baseusers = vulnsids.map((a) => User.initUserId(a.slice(5, -11)));
|
||||
const users: User[] = [];
|
||||
for (const user of baseusers) {
|
||||
const a = await user;
|
||||
if (!a) continue;
|
||||
users.push(a);
|
||||
}
|
||||
if (users.length === 0) {
|
||||
await sendMessage("No vulnerable chatters, -6666 KEKPOINT");
|
||||
return;
|
||||
}
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: "blastinRoulette",
|
||||
user: user.displayName,
|
||||
targets: users.map((a) => a.displayName),
|
||||
finaltarget: target.displayName,
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 4000));
|
||||
} else {
|
||||
target = await User.initUsername(args[0].toLowerCase());
|
||||
}
|
||||
if (!target) {
|
||||
await sendMessage("dumbass wasted 6666 bits KEKPOINT", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await getUserRecord(target);
|
||||
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got fucking DELETED by ${user.displayName}!`,
|
||||
60 * 60 * 24,
|
||||
);
|
||||
if (result.status)
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||
),
|
||||
createTimeoutEventCheer(user, target, "realsilverbullet"),
|
||||
playAlert({
|
||||
name: "userExecution",
|
||||
user: user.displayName,
|
||||
target: target.displayName,
|
||||
}),
|
||||
]);
|
||||
else {
|
||||
switch (result.reason) {
|
||||
case "banned":
|
||||
await sendMessage(
|
||||
`${target.displayName} is already timed out/banned`,
|
||||
msg.messageId,
|
||||
);
|
||||
break;
|
||||
case "illegal":
|
||||
await Promise.all([
|
||||
sendMessage(`${user.displayName} Nou Nou Nou`),
|
||||
timeout(user, "nah", 60 * 60 * 24),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage("Something went wrong... oops", msg.messageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,28 +1,40 @@
|
||||
import { Cheer } from "cheers";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { createSuperLootEvent } from "db/CheerEvents";
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import itemMap, { type inventory, type items } from "items";
|
||||
import { createGetLootRecord } from "db/dbGetLoot";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
|
||||
export default new Cheer('superloot', 150, async (msg, user) => {
|
||||
if (!await redis.exists('streamIsLive')) { await sendMessage(`No loot while stream is offline`, msg.messageId); return; };
|
||||
if (await user.itemLock()) { await sendMessage(`Cannot get loot (itemlock)`, msg.messageId); return; };
|
||||
export default new Cheer({
|
||||
name: "superloot",
|
||||
amount: 150,
|
||||
isItem: true,
|
||||
async execute(msg, user) {
|
||||
// if (!(await redis.exists("streamIsLive"))) {
|
||||
// await sendMessage(`No loot while stream is offline`, msg.messageId);
|
||||
// return;
|
||||
// }
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage(`Cannot get loot (itemlock)`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
const userData = await getUserRecord(user);
|
||||
|
||||
await sendMessage("HOLD");
|
||||
await new Promise(res => setTimeout(res, 1000 * 5));
|
||||
await new Promise((res) => setTimeout(res, 1000 * 5));
|
||||
|
||||
if (Math.random() > 0.5) {
|
||||
await Promise.all([
|
||||
sendMessage(`SUPERLOOT FAILED!!! KEKPOINT KEKPOINT KEKPOINT ${msg.chatterDisplayName.toUpperCase()} SEE YOU IN 5 MINUTES!!!`),
|
||||
sendMessage(
|
||||
`SUPERLOOT FAILED!!! KEKPOINT KEKPOINT KEKPOINT ${msg.chatterDisplayName.toUpperCase()} SEE YOU IN 5 MINUTES!!!`,
|
||||
),
|
||||
timeout(user, `RIP BOZO! NO SUPERLOOT FOR YOU`, 60 * 5),
|
||||
user.clearLock()
|
||||
user.clearLock(),
|
||||
]);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const gainedqbucks = Math.floor(Math.random() * 250) + 150; // range from 150 to 400
|
||||
userData.balance += gainedqbucks;
|
||||
@@ -31,7 +43,7 @@ export default new Cheer('superloot', 150, async (msg, user) => {
|
||||
grenade: 0,
|
||||
blaster: 0,
|
||||
tnt: 0,
|
||||
silverbullet: 0
|
||||
silverbullet: 0,
|
||||
};
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
@@ -39,12 +51,15 @@ export default new Cheer('superloot', 150, async (msg, user) => {
|
||||
if (Math.floor(Math.random() * 5) === 0) itemDiff.blaster! += 1; // 1 in 5
|
||||
if (Math.floor(Math.random() * 50) === 0) itemDiff.tnt! += 1; // 1 in 50
|
||||
if (Math.floor(Math.random() * 50) === 0) itemDiff.silverbullet! += 1; // 1 in 50
|
||||
};
|
||||
}
|
||||
|
||||
for (const [item, amount] of Object.entries(itemDiff) as [items, number][]) {
|
||||
for (const [item, amount] of Object.entries(itemDiff) as [
|
||||
items,
|
||||
number,
|
||||
][]) {
|
||||
if (userData.inventory[item]) userData.inventory[item] += amount;
|
||||
else userData.inventory[item] = amount;
|
||||
};
|
||||
}
|
||||
|
||||
const itemstrings: string[] = [`${gainedqbucks} qbucks`];
|
||||
|
||||
@@ -52,17 +67,21 @@ export default new Cheer('superloot', 150, async (msg, user) => {
|
||||
if (amount === 0) continue;
|
||||
const selection = itemMap.get(item);
|
||||
if (!selection) continue;
|
||||
itemstrings.push(`${amount} ${selection.prettyName + (amount === 1 ? '' : selection.plural)}`);
|
||||
};
|
||||
itemstrings.push(
|
||||
`${amount} ${selection.prettyName + (amount === 1 ? "" : selection.plural)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const last = itemstrings.pop();
|
||||
const itemstring = itemstrings.length === 0 ? last : itemstrings.join(', ') + " and " + last;
|
||||
const itemstring =
|
||||
itemstrings.length === 0 ? last : `${itemstrings.join(", ")} and ${last}`;
|
||||
const message = `You got ${itemstring}`;
|
||||
|
||||
await Promise.all([
|
||||
updateUserRecord(user, userData),
|
||||
sendMessage(message, msg.messageId),
|
||||
createGetLootRecord(user, gainedqbucks, itemDiff, 'superloot'),
|
||||
user.clearLock()
|
||||
createSuperLootEvent(user, gainedqbucks, itemDiff),
|
||||
user.clearLock(),
|
||||
]);
|
||||
}, true);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,49 +1,73 @@
|
||||
import { Cheer, handleNoTarget } from "cheers";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import {
|
||||
createTimeoutEventCheer,
|
||||
createCompensatedItemCheer,
|
||||
} from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import User from "user";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'blaster';
|
||||
const ITEMNAME = "blaster";
|
||||
|
||||
export default new Cheer('timeout', 100, async (msg, user) => {
|
||||
export default new Cheer({
|
||||
name: "timeout",
|
||||
amount: 100,
|
||||
isItem: true,
|
||||
async execute(msg, user) {
|
||||
const args = parseCheerArgs(msg.messageText);
|
||||
if (!args[0]) { await handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||
if (!args[0]) {
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||
if (!target) {
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME, false);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
await getUserRecord(target);
|
||||
|
||||
const result = await timeout(target, `You got blasted by ${user.displayName}!`, 60);
|
||||
if (result.status) await Promise.all([
|
||||
sendMessage(`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`),
|
||||
createTimeoutRecord(user, target, ITEMNAME),
|
||||
createCheerEventRecord(user, ITEMNAME),
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got blasted by ${user.displayName}!`,
|
||||
60,
|
||||
);
|
||||
if (result.status)
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
||||
),
|
||||
createTimeoutEventCheer(user, target, "timeout"),
|
||||
playAlert({
|
||||
name: 'userBlast',
|
||||
name: "userBlast",
|
||||
user: user.displayName,
|
||||
target: target.displayName
|
||||
})
|
||||
|
||||
target: target.displayName,
|
||||
}),
|
||||
]);
|
||||
else {
|
||||
await handleNoTarget(msg, user, ITEMNAME);
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
switch (result.reason) {
|
||||
case "banned":
|
||||
await sendMessage(`${target.displayName} is already timed out/banned`, msg.messageId);
|
||||
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)
|
||||
timeout(user, "nah", 60),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage('Something went wrong...', msg.messageId);
|
||||
await sendMessage("Something went wrong...", msg.messageId);
|
||||
break;
|
||||
};
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,40 +1,59 @@
|
||||
import { Cheer, handleNoTarget } from "cheers";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import {
|
||||
createTimeoutEventCheer,
|
||||
createCompensatedItemCheer,
|
||||
} from "db/CheerEvents";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import User from "user";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||
import { getTNTTargets } from "items/tnt";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'tnt';
|
||||
const ITEMNAME = "tnt";
|
||||
|
||||
export default new Cheer('tnt', 1000, async (msg, user) => {
|
||||
const vulntargets = await redis.keys('user:*:vulnerable').then(a => a.map(b => b.slice(5, -11)));
|
||||
if (vulntargets.length === 0) { await sendMessage('No vulnerable chatters to blow up', msg.messageId); await handleNoTarget(msg, user, ITEMNAME); return; };
|
||||
export default new Cheer({
|
||||
name: "tnt",
|
||||
amount: 1000,
|
||||
isItem: true,
|
||||
async execute(msg, user) {
|
||||
const vulntargets = await redis
|
||||
.keys("user:*:vulnerable")
|
||||
.then((a) => a.map((b) => b.slice(5, -11)));
|
||||
if (vulntargets.length === 0) {
|
||||
await sendMessage("No vulnerable chatters to blow up", msg.messageId);
|
||||
const compensated = await handleNoTarget(msg, user, ITEMNAME, true);
|
||||
if (compensated) await createCompensatedItemCheer(user, ITEMNAME);
|
||||
return;
|
||||
}
|
||||
const targets = getTNTTargets(vulntargets);
|
||||
|
||||
await Promise.all(targets.map(async targetid => {
|
||||
await Promise.all(
|
||||
targets.map(async (targetid) => {
|
||||
const target = await User.initUserId(targetid);
|
||||
await getUserRecord(target!); // make sure the user record exist in the database
|
||||
await Promise.all([
|
||||
timeout(target!, `You got hit by ${user.displayName}'s TNT!`, 60),
|
||||
redis.del(`user:${targetid}:vulnerable`),
|
||||
sendMessage(`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`),
|
||||
createTimeoutRecord(user, target!, ITEMNAME)
|
||||
sendMessage(
|
||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
||||
),
|
||||
]);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
createCheerEventRecord(user, ITEMNAME),
|
||||
createTimeoutEventCheer(user, targets, "tnt"),
|
||||
playAlert({
|
||||
name: 'tntExplosion',
|
||||
name: "tntExplosion",
|
||||
user: user.displayName,
|
||||
targets
|
||||
})
|
||||
targets,
|
||||
}),
|
||||
]);
|
||||
|
||||
await sendMessage(`RIPBOZO ${user.displayName} exploded ${targets.length} chatter${targets.length === 1 ? '' : 's'} with their TNT RIPBOZO`);
|
||||
}, true);
|
||||
await sendMessage(
|
||||
`RIPBOZO ${user.displayName} exploded ${targets.length} chatter${targets.length === 1 ? "" : "s"} with their TNT RIPBOZO`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { addAdmin } from "lib/admins";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'addadmin',
|
||||
aliases: ['addadmin'],
|
||||
usertype: 'streamer',
|
||||
name: "addadmin",
|
||||
aliases: ["addadmin"],
|
||||
usertype: "streamer",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
const data = await addAdmin(target.id);
|
||||
if (data === "OK") await sendMessage(`${target.displayName} is now an admin`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} is already an admin`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const data = await addAdmin(target.id);
|
||||
if (data === "OK")
|
||||
await sendMessage(`${target.displayName} is now an admin`, msg.messageId);
|
||||
else
|
||||
await sendMessage(
|
||||
`${target.displayName} is already an admin`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,23 +1,40 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerUsers } from "main";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'addbot',
|
||||
aliases: ['addbot'],
|
||||
usertype: 'streamer',
|
||||
name: "addbot",
|
||||
aliases: ["addbot"],
|
||||
usertype: "streamer",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (streamerUsers.includes(target.id)) { await sendMessage(`Cannot change bot status of qweribot managed user`, msg.messageId); return; };
|
||||
const data = await redis.set(`user:${target.id}:bot`, '1');
|
||||
await target.clearVulnerable();
|
||||
if (data === "OK") await sendMessage(`${target.displayName} is now a bot`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} is already a bot`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (streamerUsers.includes(target.id)) {
|
||||
await sendMessage(
|
||||
`Cannot change bot status of qweribot managed user`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const data = await redis.set(`user:${target.id}:bot`, "1");
|
||||
await target.clearVulnerable();
|
||||
if (data === "OK")
|
||||
await sendMessage(`${target.displayName} is now a bot`, msg.messageId);
|
||||
else
|
||||
await sendMessage(
|
||||
`${target.displayName} is already a bot`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
import { addInvuln } from "lib/invuln";
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import { emptyInventory } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { addInvuln } from "lib/invuln";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'addinvuln',
|
||||
aliases: ['addinvuln'],
|
||||
usertype: 'moderator',
|
||||
name: "addinvuln",
|
||||
aliases: ["addinvuln"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
const data = await addInvuln(target.id);
|
||||
if (data === "OK") await sendMessage(`${target.displayName} is now an invuln`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} is already an invuln`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const data = await addInvuln(target.id);
|
||||
if (data === "OK") {
|
||||
const userRecord = await getUserRecord(target);
|
||||
userRecord.inventory = emptyInventory;
|
||||
userRecord.balance = 0;
|
||||
await updateUserRecord(target, userRecord);
|
||||
await sendMessage(
|
||||
`${target.displayName} is now an invuln. Their inventory and wallet have been wiped`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else
|
||||
await sendMessage(
|
||||
`${target.displayName} is already an invuln`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,30 +1,54 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { changeBalance } from "lib/changeBalance";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'admindonate',
|
||||
aliases: ['admindonate'],
|
||||
usertype: 'admin',
|
||||
execution: async msg => {
|
||||
name: "admindonate",
|
||||
aliases: ["admindonate"],
|
||||
usertype: "admin",
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a user', msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a user", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const userRecord = await getUserRecord(target);
|
||||
if (!args[1]) { await sendMessage('Please specify the amount qweribucks you want to give', msg.messageId); return; };
|
||||
const amount = parseInt(args[1]);
|
||||
if (isNaN(amount)) { await sendMessage(`'${args[1]}' is not a valid amount`); return; };
|
||||
if (await target.itemLock()) { await sendMessage('Cannot give qweribucks (itemlock)', msg.messageId); return; };
|
||||
if (!args[1]) {
|
||||
await sendMessage(
|
||||
"Please specify the amount qweribucks you want to give",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const amount = parseInt(args[1], 10);
|
||||
if (Number.isNaN(amount)) {
|
||||
await sendMessage(`'${args[1]}' is not a valid amount`);
|
||||
return;
|
||||
}
|
||||
if (await target.itemLock()) {
|
||||
await sendMessage("Cannot give qweribucks (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await target.setLock();
|
||||
const data = await changeBalance(target, userRecord, amount);
|
||||
if (!data) {
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} qweribuck${amount === 1 ? '' : 's'}`, msg.messageId);
|
||||
await sendMessage(
|
||||
`Failed to give ${target.displayName} ${amount} qweribuck${amount === 1 ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else {
|
||||
await sendMessage(`${target.displayName} now has ${data.balance} qweribuck${data.balance === 1 ? '' : 's'}`, msg.messageId);
|
||||
};
|
||||
await target.clearLock();
|
||||
await sendMessage(
|
||||
`${target.displayName} now has ${data.balance} qweribuck${data.balance === 1 ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
}
|
||||
await target.clearLock();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,34 +1,64 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import items, { changeItemCount } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'admingive',
|
||||
aliases: ['admingive'],
|
||||
usertype: 'admin',
|
||||
execution: async msg => {
|
||||
name: "admingive",
|
||||
aliases: ["admingive"],
|
||||
usertype: "admin",
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a user', msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a user", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const userRecord = await getUserRecord(target);
|
||||
if (!args[1]) { await sendMessage('Please specify an item to give', msg.messageId); return; };
|
||||
if (!args[1]) {
|
||||
await sendMessage("Please specify an item to give", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const item = items.get(args[1].toLowerCase());
|
||||
if (!item) { await sendMessage(`Item ${args[1]} doesn't exist`, msg.messageId); return; };
|
||||
if (!args[2]) { await sendMessage('Please specify the amount of the item you want to give', msg.messageId); return; };
|
||||
const amount = parseInt(args[2]);
|
||||
if (isNaN(amount)) { await sendMessage(`'${args[2]}' is not a valid amount`); return; };
|
||||
if (await target.itemLock()) { await sendMessage('Cannot give item (itemlock)', msg.messageId); return; };
|
||||
if (!item) {
|
||||
await sendMessage(`Item ${args[1]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (!args[2]) {
|
||||
await sendMessage(
|
||||
"Please specify the amount of the item you want to give",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const amount = parseInt(args[2], 10);
|
||||
if (Number.isNaN(amount)) {
|
||||
await sendMessage(`'${args[2]}' is not a valid amount`);
|
||||
return;
|
||||
}
|
||||
if (await target.itemLock()) {
|
||||
await sendMessage("Cannot give item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await target.setLock();
|
||||
const data = await changeItemCount(target, userRecord, item.name, amount);
|
||||
if (data) {
|
||||
const newamount = data.inventory[item.name]!;
|
||||
await sendMessage(`${target.displayName} now has ${newamount} ${item.prettyName + (newamount === 1 ? '' : item.plural)}`, msg.messageId);
|
||||
await sendMessage(
|
||||
`${target.displayName} now has ${newamount} ${item.prettyName + (newamount === 1 ? "" : item.plural)}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else {
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} ${item.prettyName + (amount === 1 ? '' : item.plural)}`, msg.messageId);
|
||||
};
|
||||
await target.clearLock();
|
||||
await sendMessage(
|
||||
`Failed to give ${target.displayName} ${amount} ${item.prettyName + (amount === 1 ? "" : item.plural)}`,
|
||||
msg.messageId,
|
||||
);
|
||||
}
|
||||
await target.clearLock();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getKDLeaderboard } from "db/dbUser";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import User from "user";
|
||||
|
||||
type KD = { user: User; kd: number; };
|
||||
type KD = { user: User; kd: number };
|
||||
|
||||
export default new Command({
|
||||
name: 'alltimekdleaderboard',
|
||||
aliases: ['alltimeleaderboard', 'alltimekdleaderboard'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
name: "alltimekdleaderboard",
|
||||
aliases: ["alltimeleaderboard", "alltimekdleaderboard"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const rawKD = await getKDLeaderboard();
|
||||
if (rawKD.length === 0) {
|
||||
await sendMessage(`No users on leaderboard yet!`, msg.messageId);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const userKDs: KD[] = [];
|
||||
await Promise.all(rawKD.map(async userRecord => {
|
||||
await Promise.all(
|
||||
rawKD.map(async (userRecord) => {
|
||||
const user = await User.initUserId(userRecord.userId.toString());
|
||||
if (!user) return;
|
||||
userKDs.push({ user, kd: userRecord.KD })
|
||||
}));
|
||||
userKDs.push({ user, kd: userRecord.KD });
|
||||
}),
|
||||
);
|
||||
|
||||
userKDs.sort((a, b) => b.kd - a.kd);
|
||||
|
||||
const txt: string[] = [];
|
||||
for (let i = 0; i < (userKDs.length < 10 ? userKDs.length : 10); i++) {
|
||||
txt.push(`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`);
|
||||
};
|
||||
|
||||
await sendMessage(`Alltime leaderboard: ${txt.join(' | ')}`, msg.messageId);
|
||||
txt.push(
|
||||
`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`,
|
||||
);
|
||||
}
|
||||
|
||||
await sendMessage(`Alltime leaderboard: ${txt.join(" | ")}`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,33 +1,45 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getTimeoutStats, getItemStats } from "lib/getStats";
|
||||
import { getItemStats, getTimeoutStats } from "lib/getStats";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'alltimestats',
|
||||
aliases: ['alltime', 'alltimestats'],
|
||||
usertype: 'chatter',
|
||||
name: "alltimestats",
|
||||
aliases: ["alltime", "alltimestats"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
let target: User | null = user;
|
||||
if (args[0]) {
|
||||
target = await User.initUsername(args[0]);
|
||||
if (!target) { await sendMessage(`User ${args[0]} doesn't exist!`, msg.messageId); return; };
|
||||
};
|
||||
if (!target) {
|
||||
await sendMessage(`User ${args[0]} doesn't exist!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const [timeout, item] = await Promise.all([getTimeoutStats(target, false), getItemStats(target, false)]);
|
||||
if (!timeout || !item) { await sendMessage(`ERROR: Something went wrong!`, msg.messageId); return; };
|
||||
const [timeout, item] = await Promise.all([
|
||||
getTimeoutStats(target, false),
|
||||
getItemStats(target, false),
|
||||
]);
|
||||
if (!timeout || !item) {
|
||||
await sendMessage(`ERROR: Something went wrong!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
const KD = timeout.shot.blaster / timeout.hit.blaster;
|
||||
|
||||
await sendMessage(`
|
||||
await sendMessage(
|
||||
`
|
||||
Alltime: stats of ${target.displayName}:
|
||||
Users blasted: ${timeout.shot.blaster},
|
||||
Blasted by others: ${timeout.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||
Blasted by others: ${timeout.hit.blaster} (${Number.isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||
Grenades lobbed: ${item.grenade},
|
||||
TNT exploded: ${item.tnt}.
|
||||
Silver bullets fired: ${timeout.shot.silverbullet},
|
||||
Silver bullets taken: ${timeout.hit.silverbullet}.
|
||||
`, msg.messageId);
|
||||
}
|
||||
`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getAnivTimeouts } from "db/dbAnivTimeouts";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'anivtimeouts',
|
||||
aliases: ['anivtimeouts', 'anivtimeout'],
|
||||
usertype: 'chatter',
|
||||
name: "anivtimeouts",
|
||||
aliases: ["anivtimeouts", "anivtimeout"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
const target = args[0] ? await User.initUsername(args[0].toLowerCase()) : user;
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist!`, msg.messageId); return; };
|
||||
const { dodge, dead } = await getAnivTimeouts(target);
|
||||
const percentage = ((dodge / (dead + dodge)) * 100);
|
||||
const message = `Aniv timeouts of ${target.displayName}: Dodge: ${dodge}, Timeout: ${dead}. Dodge percentage: ${isNaN(percentage) ? "0" : percentage.toFixed(1)}%`;
|
||||
await sendMessage(message, msg.messageId);
|
||||
const target = args[0]
|
||||
? await User.initUsername(args[0].toLowerCase())
|
||||
: user;
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const { dodge, dead } = await getAnivTimeouts(target);
|
||||
const percentage = (dodge / (dead + dodge)) * 100;
|
||||
const message = `Aniv timeouts of ${target.displayName}: Dodge: ${dodge}, Timeout: ${dead}. Dodge percentage: ${Number.isNaN(percentage) ? "0" : percentage.toFixed(1)}%`;
|
||||
await sendMessage(message, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import User from "user";
|
||||
import { redis } from "lib/redis";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'backshot',
|
||||
aliases: ['backshot'],
|
||||
usertype: 'chatter',
|
||||
name: "backshot",
|
||||
aliases: ["backshot"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const targets = await redis.keys(`user:*:haschatted`);
|
||||
const selection = targets[Math.floor(Math.random() * targets.length)]!;
|
||||
const target = await User.initUserId(selection.slice(5, -11));
|
||||
await sendMessage(`${user.displayName} backshotted ${target?.displayName}`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`${user.displayName} backshotted ${target?.displayName}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,37 +1,62 @@
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import items from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import items from "items";
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
|
||||
export default new Command({
|
||||
name: 'buyitem',
|
||||
aliases: ['buyitem', 'buy', 'purchase'],
|
||||
usertype: 'chatter',
|
||||
name: "buyitem",
|
||||
aliases: ["buyitem", "buy", "purchase"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage(`Specify the item you'd like to buy`, msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage(`Specify the item you'd like to buy`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selecteditem = items.get(args[0].toLowerCase());
|
||||
if (!selecteditem) { await sendMessage(`'${args[0]}' is not a valid item`, msg.messageId); return; };
|
||||
const amount = args[1] ? parseInt(args[1]) : 1;
|
||||
if (isNaN(amount) || amount < 1) { await sendMessage(`'${args[1]}' is not a valid amount to buy`, msg.messageId); return; };
|
||||
if (!selecteditem) {
|
||||
await sendMessage(`'${args[0]}' is not a valid item`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const amount = args[1] ? parseInt(args[1], 10) : 1;
|
||||
if (Number.isNaN(amount) || amount < 1) {
|
||||
await sendMessage(
|
||||
`'${args[1]}' is not a valid amount to buy`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const totalcost = amount * selecteditem.price;
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot buy item (itemlock)', msg.messageId); return; };
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage("Cannot buy item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
|
||||
const userRecord = await getUserRecord(user);
|
||||
if (userRecord.balance < totalcost) { await sendMessage(`You don't have enough qbucks to buy ${amount} ${selecteditem.prettyName}${amount === 1 ? '' : selecteditem.plural}! You have ${userRecord.balance}, need ${totalcost}`); await user.clearLock(); return; };
|
||||
if (userRecord.balance < totalcost) {
|
||||
await sendMessage(
|
||||
`You don't have enough qbucks to buy ${amount} ${selecteditem.prettyName}${amount === 1 ? "" : selecteditem.plural}! You have ${userRecord.balance}, need ${totalcost}`,
|
||||
);
|
||||
await user.clearLock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (userRecord.inventory[selecteditem.name]) userRecord.inventory[selecteditem.name]! += amount
|
||||
if (userRecord.inventory[selecteditem.name])
|
||||
userRecord.inventory[selecteditem.name]! += amount;
|
||||
else userRecord.inventory[selecteditem.name] = amount;
|
||||
|
||||
userRecord.balance -= totalcost;
|
||||
|
||||
await Promise.all([
|
||||
updateUserRecord(user, userRecord),
|
||||
sendMessage(`${user.displayName} bought ${amount} ${selecteditem.prettyName}${amount === 1 ? '' : selecteditem.plural} for ${totalcost} qbucks. They now have ${userRecord.inventory[selecteditem.name]} ${selecteditem.prettyName}${userRecord.inventory[selecteditem.name] === 1 ? '' : selecteditem.plural} and ${userRecord.balance} qbucks`, msg.messageId)
|
||||
sendMessage(
|
||||
`${user.displayName} bought ${amount} ${selecteditem.prettyName}${amount === 1 ? "" : selecteditem.plural} for ${totalcost} qbucks. They now have ${userRecord.inventory[selecteditem.name]} ${selecteditem.prettyName}${userRecord.inventory[selecteditem.name] === 1 ? "" : selecteditem.plural} and ${userRecord.balance} qbucks`,
|
||||
msg.messageId,
|
||||
),
|
||||
]);
|
||||
|
||||
await user.clearLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { namedcheers } from "cheers";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { namedcheers } from "cheers";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'disablecheer',
|
||||
aliases: ['disablecheer'],
|
||||
usertype: 'moderator',
|
||||
name: "disablecheer",
|
||||
aliases: ["disablecheer"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a cheer to disable', msg.messageId); return; };
|
||||
const selection = namedcheers.get(args[0].toLowerCase());
|
||||
if (!selection) { await sendMessage(`There is no ${args[0]} cheer`, msg.messageId); return; };
|
||||
const result = await redis.sadd('disabledcheers', selection.name);
|
||||
if (result === 0) { await sendMessage(`The ${selection.name} cheer is already disabled`, msg.messageId); return; };
|
||||
await sendMessage(`Successfully disabled the ${selection.name} cheer`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a cheer to disable", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selection = namedcheers.get(args[0].toLowerCase());
|
||||
if (!selection) {
|
||||
await sendMessage(`There is no ${args[0]} cheer`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const result = await redis.sadd("disabledcheers", selection.name);
|
||||
if (result === 0) {
|
||||
await sendMessage(
|
||||
`The ${selection.name} cheer is already disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Successfully disabled the ${selection.name} cheer`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
import { redis } from "lib/redis";
|
||||
import commands from "commands";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'disablecommand',
|
||||
aliases: ['disablecommand'],
|
||||
usertype: 'moderator',
|
||||
name: "disablecommand",
|
||||
aliases: ["disablecommand"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a command to disable', msg.messageId); return; };
|
||||
const selection = commands.get(args[0].toLowerCase());
|
||||
if (!selection) { await sendMessage(`There is no ${args[0]} command`, msg.messageId); return; };
|
||||
if (!selection.disableable) { await sendMessage(`Cannot disable ${selection.name} as the command is not disableable`, msg.messageId); return; };
|
||||
const result = await redis.sadd('disabledcommands', selection.name);
|
||||
if (result === 0) { await sendMessage(`The ${selection.name} command is already disabled`, msg.messageId); return; };
|
||||
await sendMessage(`Successfully disabled the ${selection.name} command`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a command to disable", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selection = commands.get(args[0].toLowerCase());
|
||||
if (!selection) {
|
||||
await sendMessage(`There is no ${args[0]} command`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (!selection.disableable) {
|
||||
await sendMessage(
|
||||
`Cannot disable ${selection.name} as the command is not disableable`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const result = await redis.sadd("disabledcommands", selection.name);
|
||||
if (result === 0) {
|
||||
await sendMessage(
|
||||
`The ${selection.name} command is already disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Successfully disabled the ${selection.name} command`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,30 +1,55 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { disableRedeem, idMap, namedRedeems, sfxRedeems } from "pointRedeems";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import logger from "lib/logger";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
|
||||
export default new Command({
|
||||
name: 'disableRedeem',
|
||||
aliases: ['disableredeem'],
|
||||
usertype: 'moderator',
|
||||
name: "disableRedeem",
|
||||
aliases: ["disableredeem"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage("Please specify a point redemption to disable", msg.messageId); return; };
|
||||
if (args[0] === 'sfx' || args[0] === 'sound') {
|
||||
sfxRedeems.forEach(async redeem => {
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
"Please specify a point redemption to disable",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (args[0] === "sfx" || args[0] === "sound") {
|
||||
sfxRedeems.forEach(async (redeem) => {
|
||||
const id = idMap.get(redeem.name);
|
||||
if (!id) { await sendMessage(`Failed to find the ID for redeem ${redeem.name}`, msg.messageId); logger.err(`Failed to find the ID for ${redeem.name} while enabling`); return; };
|
||||
if (!id) {
|
||||
await sendMessage(
|
||||
`Failed to find the ID for redeem ${redeem.name}`,
|
||||
msg.messageId,
|
||||
);
|
||||
logger.err(`Failed to find the ID for ${redeem.name} while enabling`);
|
||||
return;
|
||||
}
|
||||
await disableRedeem(redeem, id);
|
||||
});
|
||||
await sendMessage(`Disabled all sound (sfx) channel point redemptions`, msg.messageId);
|
||||
await sendMessage(
|
||||
`Disabled all sound (sfx) channel point redemptions`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const selection = namedRedeems.get(args[0]);
|
||||
if (!selection) { await sendMessage(`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`, msg.messageId); return; };
|
||||
if (!selection) {
|
||||
await sendMessage(
|
||||
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const id = idMap.get(selection.name);
|
||||
await disableRedeem(selection, id!);
|
||||
await sendMessage(`The ${selection.name} point redeem is now disabled`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`The ${selection.name} point redeem is now disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,52 +1,81 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { changeBalance } from "lib/changeBalance";
|
||||
import User from "user";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import logger from "lib/logger";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'donate',
|
||||
aliases: ['donate'],
|
||||
usertype: 'chatter',
|
||||
name: "donate",
|
||||
aliases: ["donate"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a user', msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a user", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (target.username === user.username) { await sendMessage("You can't give yourself qweribucks", msg.messageId); return; };
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (target.username === user.username) {
|
||||
await sendMessage("You can't give yourself qweribucks", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const targetRecord = await getUserRecord(target);
|
||||
if (!args[1]) { await sendMessage('Please specify the amount of the item you want to give', msg.messageId); return; };
|
||||
const amount = parseInt(args[1]);
|
||||
if (isNaN(amount) || amount < 1) { await sendMessage(`'${args[1]}' is not a valid amount`); return; };
|
||||
if (!args[1]) {
|
||||
await sendMessage(
|
||||
"Please specify the amount of the item you want to give",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const amount = parseInt(args[1], 10);
|
||||
if (Number.isNaN(amount) || amount < 1) {
|
||||
await sendMessage(`'${args[1]}' is not a valid amount`);
|
||||
return;
|
||||
}
|
||||
|
||||
const userRecord = await getUserRecord(user);
|
||||
if (userRecord.balance < amount) { await sendMessage(`You can't give qweribucks you don't have!`, msg.messageId); return; };
|
||||
if (userRecord.balance < amount) {
|
||||
await sendMessage(
|
||||
`You can't give qweribucks you don't have!`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await user.itemLock() || await target.itemLock()) { await sendMessage('Cannot give qweribucks (itemlock)', msg.messageId); return; };
|
||||
if ((await user.itemLock()) || (await target.itemLock())) {
|
||||
await sendMessage("Cannot give qweribucks (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
user.setLock(),
|
||||
target.setLock()
|
||||
]);
|
||||
await Promise.all([user.setLock(), target.setLock()]);
|
||||
|
||||
const data = await Promise.all([
|
||||
await changeBalance(target, targetRecord, amount),
|
||||
await changeBalance(user, userRecord, -amount)
|
||||
await changeBalance(user, userRecord, -amount),
|
||||
]);
|
||||
|
||||
if (data[0] !== false && data[1] !== false) {
|
||||
const { balance: newamount } = data[0];
|
||||
await sendMessage(`${user.displayName} gave ${amount} qweribuck${amount === 1 ? '' : 's'} to ${target.displayName}. They now have ${newamount} qweribuck${newamount === 1 ? '' : 's'}`, msg.messageId);
|
||||
await sendMessage(
|
||||
`${user.displayName} gave ${amount} qweribuck${amount === 1 ? "" : "s"} to ${target.displayName}. They now have ${newamount} qweribuck${newamount === 1 ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else {
|
||||
// TODO: Rewrite this section
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} qbuck${(amount === 1 ? '' : 's')}`, msg.messageId);
|
||||
logger.err(`WARNING: Qweribucks donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`);
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
user.clearLock(),
|
||||
target.clearLock()
|
||||
]);
|
||||
await sendMessage(
|
||||
`Failed to give ${target.displayName} ${amount} qbuck${amount === 1 ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
logger.err(
|
||||
`WARNING: Qweribucks donation failed: target success: ${data[0] !== false}, donator success: ${data[1] !== false}`,
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all([user.clearLock(), target.clearLock()]);
|
||||
},
|
||||
});
|
||||
|
||||
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);
|
||||
},
|
||||
});
|
||||
@@ -1,20 +1,35 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { namedcheers } from "cheers";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { namedcheers } from "cheers";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'enablecheer',
|
||||
aliases: ['enablecheer'],
|
||||
usertype: 'moderator',
|
||||
name: "enablecheer",
|
||||
aliases: ["enablecheer"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a cheer to enable', msg.messageId); return; };
|
||||
const selection = namedcheers.get(args[0].toLowerCase());
|
||||
if (!selection) { await sendMessage(`There is no ${args[0]} cheer`, msg.messageId); return; };
|
||||
const result = await redis.srem('disabledcheers', selection.name);
|
||||
if (result === 0) { await sendMessage(`The ${selection.name} cheer isn't disabled`, msg.messageId); return; };
|
||||
await sendMessage(`Successfully enabled the ${selection.name} cheer`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a cheer to enable", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selection = namedcheers.get(args[0].toLowerCase());
|
||||
if (!selection) {
|
||||
await sendMessage(`There is no ${args[0]} cheer`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const result = await redis.srem("disabledcheers", selection.name);
|
||||
if (result === 0) {
|
||||
await sendMessage(
|
||||
`The ${selection.name} cheer isn't disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Successfully enabled the ${selection.name} cheer`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import commands from "commands";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'enablecommand',
|
||||
aliases: ['enablecommand'],
|
||||
usertype: 'moderator',
|
||||
name: "enablecommand",
|
||||
aliases: ["enablecommand"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a command to enable', msg.messageId); return; };
|
||||
const selection = commands.get(args[0].toLowerCase());
|
||||
if (!selection) { await sendMessage(`There is no ${args[0]} command`, msg.messageId); return; };
|
||||
const result = await redis.srem('disabledcommands', selection.name);
|
||||
if (result === 0) { await sendMessage(`The ${selection.name} command isn't disabled`, msg.messageId); return; };
|
||||
await sendMessage(`Successfully enabled the ${selection.name} command`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a command to enable", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selection = commands.get(args[0].toLowerCase());
|
||||
if (!selection) {
|
||||
await sendMessage(`There is no ${args[0]} command`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const result = await redis.srem("disabledcommands", selection.name);
|
||||
if (result === 0) {
|
||||
await sendMessage(
|
||||
`The ${selection.name} command isn't disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Successfully enabled the ${selection.name} command`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,29 +1,54 @@
|
||||
import { enableRedeem, idMap, namedRedeems, sfxRedeems } from "pointRedeems";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import logger from "lib/logger";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { enableRedeem, idMap, namedRedeems, sfxRedeems } from "pointRedeems";
|
||||
|
||||
export default new Command({
|
||||
name: 'enableRedeem',
|
||||
aliases: ['enableredeem'],
|
||||
usertype: 'moderator',
|
||||
name: "enableRedeem",
|
||||
aliases: ["enableredeem"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage("Please specify a point redemption to enable", msg.messageId); return; };
|
||||
if (args[0] === 'sfx' || args[0] === 'sound') {
|
||||
sfxRedeems.forEach(async redeem => {
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
"Please specify a point redemption to enable",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (args[0] === "sfx" || args[0] === "sound") {
|
||||
sfxRedeems.forEach(async (redeem) => {
|
||||
const id = idMap.get(redeem.name);
|
||||
if (!id) { await sendMessage(`Failed to find the ID for redeem ${redeem.name}`, msg.messageId); logger.err(`Failed to find the ID for ${redeem.name} while enabling`); return; };
|
||||
if (!id) {
|
||||
await sendMessage(
|
||||
`Failed to find the ID for redeem ${redeem.name}`,
|
||||
msg.messageId,
|
||||
);
|
||||
logger.err(`Failed to find the ID for ${redeem.name} while enabling`);
|
||||
return;
|
||||
}
|
||||
await enableRedeem(redeem, id);
|
||||
});
|
||||
await sendMessage(`Enabled all sound (sfx) channel point redemptions`, msg.messageId);
|
||||
await sendMessage(
|
||||
`Enabled all sound (sfx) channel point redemptions`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
const selection = namedRedeems.get(args[0]);
|
||||
if (!selection) { await sendMessage(`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://github.com/qwerinope/qweribot#point-redeems`, msg.messageId); return; };
|
||||
if (!selection) {
|
||||
await sendMessage(
|
||||
`Redeem ${args[0]} doesn't exist. The internal names for redeems are here: https://gitlab.com/qwerinope/qweribot#point-redeems`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const id = idMap.get(selection.name);
|
||||
await enableRedeem(selection, id!);
|
||||
await sendMessage(`The ${selection.name} point redeem is now enabled`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`The ${selection.name} point redeem is now enabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,13 +2,13 @@ import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { timeout } from "lib/timeout";
|
||||
|
||||
export default new Command({
|
||||
name: 'fakemodme',
|
||||
aliases: ['modme', 'mod'],
|
||||
usertype: 'chatter',
|
||||
name: "fakemodme",
|
||||
aliases: ["modme", "mod"],
|
||||
usertype: "chatter",
|
||||
execution: async (_msg, user) => {
|
||||
await Promise.all([
|
||||
timeout(user, "NO MODME", 60),
|
||||
sendMessage(`NO MODME COMMAND!!! UltraMad UltraMad UltraMad`)
|
||||
sendMessage(`NO MODME COMMAND!!! UltraMad UltraMad UltraMad`),
|
||||
]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getAdmins } from "lib/admins";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'getadmins',
|
||||
aliases: ['getadmins'],
|
||||
usertype: 'chatter',
|
||||
name: "getadmins",
|
||||
aliases: ["getadmins"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
const admins = await getAdmins()
|
||||
execution: async (msg) => {
|
||||
const admins = await getAdmins();
|
||||
const adminnames: string[] = [];
|
||||
for (const id of admins) {
|
||||
const admin = await User.initUserId(id);
|
||||
adminnames.push(admin?.displayName!);
|
||||
};
|
||||
await sendMessage(`Current admins: ${adminnames.join(', ')}`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`Current admins: ${adminnames.join(", ")}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'getbalance',
|
||||
aliases: ['getbalance', 'balance', 'qbucks', 'qweribucks', 'wallet', 'getwallet'],
|
||||
usertype: 'chatter',
|
||||
name: "getbalance",
|
||||
aliases: [
|
||||
"getbalance",
|
||||
"balance",
|
||||
"qbucks",
|
||||
"qweribucks",
|
||||
"wallet",
|
||||
"getwallet",
|
||||
],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
const target = args[0] ? await User.initUsername(args[0].toLowerCase()) : user;
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist!`, msg.messageId); return; };
|
||||
const data = await getUserRecord(target);
|
||||
await sendMessage(`${target.displayName} has ${data.balance} qbuck${data.balance === 1 ? '' : 's'}`, msg.messageId);
|
||||
const target = args[0]
|
||||
? await User.initUsername(args[0].toLowerCase())
|
||||
: user;
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const data = await getUserRecord(target);
|
||||
await sendMessage(
|
||||
`${target.displayName} has ${data.balance} qbuck${data.balance === 1 ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,39 +1,63 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { namedcheers } from "cheers";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { namedcheers } from "cheers";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'getcheers',
|
||||
aliases: ['getcheers', 'getcheer'],
|
||||
usertype: 'chatter',
|
||||
name: "getcheers",
|
||||
aliases: ["getcheers", "getcheer"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage(`A full list of cheers can be found here: https://github.com/qwerinope/qweribot#cheers`, msg.messageId); return; };
|
||||
const disabledcheers = await redis.smembers('disabledcheers');
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
`A full list of cheers can be found here: https://gitlab.com/qwerinope/qweribot#cheers`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const disabledcheers = await redis.smembers("disabledcheers");
|
||||
const cheerstrings: string[] = [];
|
||||
|
||||
if (args[0].toLowerCase() === "enabled") {
|
||||
for (const [name, cheer] of Array.from(namedcheers.entries())) {
|
||||
if (disabledcheers.includes(name)) continue;
|
||||
cheerstrings.push(`${cheer.amount}: ${name}`);
|
||||
};
|
||||
}
|
||||
|
||||
const last = cheerstrings.pop();
|
||||
if (!last) { await sendMessage("No enabled cheers", msg.messageId); return; };
|
||||
await sendMessage(cheerstrings.length === 0 ? last : cheerstrings.join(', ') + " and " + last, msg.messageId);
|
||||
|
||||
if (!last) {
|
||||
await sendMessage("No enabled cheers", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
cheerstrings.length === 0
|
||||
? last
|
||||
: `${cheerstrings.join(", ")} and ${last}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else if (args[0].toLowerCase() === "disabled") {
|
||||
for (const [name, cheer] of Array.from(namedcheers.entries())) {
|
||||
if (!disabledcheers.includes(name)) continue;
|
||||
cheerstrings.push(`${cheer.amount}: ${name}`);
|
||||
};
|
||||
}
|
||||
|
||||
const last = cheerstrings.pop();
|
||||
if (!last) { await sendMessage("No disabled cheers", msg.messageId); return; };
|
||||
await sendMessage(cheerstrings.length === 0 ? last : cheerstrings.join(', ') + " and " + last, msg.messageId);
|
||||
|
||||
} else await sendMessage('Please specify if you want the enabled or disabled cheers', msg.messageId);
|
||||
if (!last) {
|
||||
await sendMessage("No disabled cheers", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
cheerstrings.length === 0
|
||||
? last
|
||||
: `${cheerstrings.join(", ")} and ${last}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else
|
||||
await sendMessage(
|
||||
"Please specify if you want the enabled or disabled cheers",
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,30 +1,51 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { basecommands } from "commands";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'getcommands',
|
||||
aliases: ['getcommands', 'getc', 'commands'],
|
||||
usertype: 'chatter',
|
||||
name: "getcommands",
|
||||
aliases: ["getcommands", "getc", "commands"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage(`A full list of commands can be found here: https://github.com/qwerinope/qweribot#commands-1`, msg.messageId); return; };
|
||||
const disabledcommands = await redis.smembers('disabledcommands');
|
||||
if (args[0].toLowerCase() === 'enabled') {
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
`A full list of commands can be found here: https://gitlab.com/qwerinope/qweribot#commands-1`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const disabledcommands = await redis.smembers("disabledcommands");
|
||||
if (args[0].toLowerCase() === "enabled") {
|
||||
const commandnames: string[] = [];
|
||||
for (const [name, command] of Array.from(basecommands.entries())) {
|
||||
if (command.usertype !== 'chatter') continue; // Admin only commands should be somewhat hidden
|
||||
if (command.usertype !== "chatter") continue; // Admin only commands should be somewhat hidden
|
||||
if (disabledcommands.includes(name)) continue;
|
||||
commandnames.push(name);
|
||||
};
|
||||
if (commandnames.length === 0) await sendMessage('No commands besides non-disableable commands are enabled', msg.messageId);
|
||||
else await sendMessage(`Currently enabled commands: ${commandnames.join(', ')}`, msg.messageId);
|
||||
} else if (args[0].toLowerCase() === 'disabled') {
|
||||
if (disabledcommands.length === 0) await sendMessage('No commands are disabled', msg.messageId);
|
||||
else await sendMessage(`Currently disabled commands: ${disabledcommands.join(', ')}`);
|
||||
}
|
||||
else await sendMessage('Please specify if you want the enabled or disabled commands', msg.messageId);
|
||||
}
|
||||
if (commandnames.length === 0)
|
||||
await sendMessage(
|
||||
"No commands besides non-disableable commands are enabled",
|
||||
msg.messageId,
|
||||
);
|
||||
else
|
||||
await sendMessage(
|
||||
`Currently enabled commands: ${commandnames.join(", ")}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else if (args[0].toLowerCase() === "disabled") {
|
||||
if (disabledcommands.length === 0)
|
||||
await sendMessage("No commands are disabled", msg.messageId);
|
||||
else
|
||||
await sendMessage(
|
||||
`Currently disabled commands: ${disabledcommands.join(", ")}`,
|
||||
);
|
||||
} else
|
||||
await sendMessage(
|
||||
"Please specify if you want the enabled or disabled commands",
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,31 +1,42 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import items from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
import items from "items";
|
||||
|
||||
export default new Command({
|
||||
name: 'inventory',
|
||||
aliases: ['inv', 'inventory', 'pocket'],
|
||||
usertype: 'chatter',
|
||||
name: "inventory",
|
||||
aliases: ["inv", "inventory", "pocket"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
let target: User = user;
|
||||
if (args[0]) {
|
||||
const obj = await User.initUsername(args[0].toLowerCase());
|
||||
if (!obj) { await sendMessage(`User ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (!obj) {
|
||||
await sendMessage(`User ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
target = obj;
|
||||
};
|
||||
}
|
||||
|
||||
const data = await getUserRecord(target);
|
||||
const messagedata: string[] = [];
|
||||
for (const [key, amount] of Object.entries(data.inventory)) {
|
||||
if (amount === 0) continue;
|
||||
const itemselection = items.get(key);
|
||||
messagedata.push(`${itemselection?.prettyName}${amount === 1 ? '' : itemselection?.plural}: ${amount}`);
|
||||
};
|
||||
|
||||
if (messagedata.length === 0) { await sendMessage(`${target.displayName} has no items`, msg.messageId); return; };
|
||||
await sendMessage(`Inventory of ${target.displayName}: ${messagedata.join(', ')}`, msg.messageId);
|
||||
messagedata.push(
|
||||
`${itemselection?.prettyName}${amount === 1 ? "" : itemselection?.plural}: ${amount}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (messagedata.length === 0) {
|
||||
await sendMessage(`${target.displayName} has no items`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Inventory of ${target.displayName}: ${messagedata.join(", ")}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,17 +3,20 @@ import { getInvulns } from "lib/invuln";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'getinvulns',
|
||||
aliases: ['getinvulns'],
|
||||
usertype: 'chatter',
|
||||
name: "getinvulns",
|
||||
aliases: ["getinvulns"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
const invulns = await getInvulns()
|
||||
execution: async (msg) => {
|
||||
const invulns = await getInvulns();
|
||||
const invulnnames: string[] = [];
|
||||
for (const id of invulns) {
|
||||
const invuln = await User.initUserId(id);
|
||||
invulnnames.push(invuln?.displayName!);
|
||||
};
|
||||
await sendMessage(`Current invulnerable chatters: ${invulnnames.join(', ')}`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`Current invulnerable chatters: ${invulnnames.join(", ")}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,48 +1,93 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import { createGetLootEvent } from "db/LootEvents";
|
||||
import itemMap, { type inventory, type items } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { buildTimeString } from "lib/dateManager";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { isInvuln, removeInvuln } from "lib/invuln";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { streamerUsers } from "main";
|
||||
import { createGetLootRecord } from "db/dbGetLoot";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const COOLDOWN = 10 * 60 * 1000; // 10 mins (ms)
|
||||
const COOLDOWN = 10 * 60; // 10 mins (s)
|
||||
|
||||
export default new Command({
|
||||
name: 'getloot',
|
||||
aliases: ['getloot', 'dig', 'loot', 'mine'],
|
||||
usertype: 'chatter',
|
||||
name: "getloot",
|
||||
aliases: ["getloot", "dig", "loot", "mine"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
if (!await redis.exists('streamIsLive')) { await sendMessage(`No loot while stream is offline`, msg.messageId); return; };
|
||||
if (await isInvuln(msg.chatterId) && !streamerUsers.includes(msg.chatterId)) { await sendMessage(`You're no longer an invuln because used a lootbox.`, msg.messageId); await removeInvuln(msg.chatterId); };
|
||||
if (await user.itemLock()) { await sendMessage(`Cannot get loot (itemlock)`, msg.messageId); return; };
|
||||
if (!(await redis.exists("streamIsLive"))) {
|
||||
await sendMessage(`No loot while stream is offline`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(await isInvuln(msg.chatterId)) &&
|
||||
!streamerUsers.includes(msg.chatterId)
|
||||
) {
|
||||
await sendMessage(
|
||||
`You're no longer an invuln because used a lootbox.`,
|
||||
msg.messageId,
|
||||
);
|
||||
await removeInvuln(msg.chatterId);
|
||||
}
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage(`Cannot get loot (itemlock)`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
const userData = await getUserRecord(user);
|
||||
const lastlootbox = userData.lastlootbox.getTime();
|
||||
const now = Date.now();
|
||||
if ((lastlootbox + COOLDOWN) > now) {
|
||||
const timeData =
|
||||
(await redis.expiretime(`user:${user.id}:lootboxcooldown`)) * 1000;
|
||||
if (timeData > Date.now()) {
|
||||
await user.clearLock();
|
||||
if (await user.greedy()) {
|
||||
await Promise.all([
|
||||
sendMessage(`${user.displayName} STOP BEING GREEDY!!! UltraMad UltraMad UltraMad`),
|
||||
timeout(user, `Wait ${buildTimeString(now - COOLDOWN, lastlootbox)}`, 60)
|
||||
sendMessage(
|
||||
`${user.displayName} STOP BEING GREEDY!!! UltraMad UltraMad UltraMad`,
|
||||
),
|
||||
timeout(
|
||||
user,
|
||||
`Wait ${buildTimeString(timeData, Date.now())} for another lootbox`,
|
||||
60,
|
||||
),
|
||||
]);
|
||||
return;
|
||||
} else {
|
||||
await Promise.all([
|
||||
user.setGreed(),
|
||||
sendMessage(`Wait ${buildTimeString(now - COOLDOWN, lastlootbox)} for another lootbox.`, msg.messageId)
|
||||
sendMessage(
|
||||
`Wait ${buildTimeString(timeData, Date.now())} for another lootbox.`,
|
||||
msg.messageId,
|
||||
),
|
||||
]);
|
||||
return;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await user.clearGreed();
|
||||
|
||||
userData.lastlootbox = new Date(now);
|
||||
await redis.set(`user:${user.id}:lootboxcooldown`, "1");
|
||||
await redis.expire(`user:${user.id}:lootboxcooldown`, COOLDOWN);
|
||||
|
||||
if (
|
||||
!(await redis.exists(`user:${user.id}:subbed`)) &&
|
||||
Math.random() < 0.1
|
||||
) {
|
||||
await Promise.all([
|
||||
user.clearLock(),
|
||||
updateUserRecord(user, userData),
|
||||
timeout(user, "THE LOOTBOX WAS TRAPPED!!!", 60),
|
||||
sendMessage(
|
||||
`wybuh wybuh ${user.displayName.toUpperCase()} FOUND A TRAPPED LOOTBOX!!! wybuh wybuh`,
|
||||
),
|
||||
playAlert({
|
||||
name: "grenadeExplosion",
|
||||
user: "trapped lootbox",
|
||||
target: user.displayName,
|
||||
}),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
const gainedqbucks = Math.floor(Math.random() * 100) + 50; // range from 50 to 150
|
||||
userData.balance += gainedqbucks;
|
||||
|
||||
@@ -50,7 +95,7 @@ export default new Command({
|
||||
grenade: 0,
|
||||
blaster: 0,
|
||||
tnt: 0,
|
||||
silverbullet: 0
|
||||
silverbullet: 0,
|
||||
};
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
@@ -58,12 +103,15 @@ export default new Command({
|
||||
if (Math.floor(Math.random() * 5) === 0) itemDiff.blaster! += 1; // 1 in 5
|
||||
if (Math.floor(Math.random() * 50) === 0) itemDiff.tnt! += 1; // 1 in 50
|
||||
if (Math.floor(Math.random() * 50) === 0) itemDiff.silverbullet! += 1; // 1 in 50
|
||||
};
|
||||
}
|
||||
|
||||
for (const [item, amount] of Object.entries(itemDiff) as [items, number][]) {
|
||||
for (const [item, amount] of Object.entries(itemDiff) as [
|
||||
items,
|
||||
number,
|
||||
][]) {
|
||||
if (userData.inventory[item]) userData.inventory[item] += amount;
|
||||
else userData.inventory[item] = amount;
|
||||
};
|
||||
}
|
||||
|
||||
const itemstrings: string[] = [`${gainedqbucks} qbucks`];
|
||||
|
||||
@@ -71,18 +119,24 @@ export default new Command({
|
||||
if (amount === 0) continue;
|
||||
const selection = itemMap.get(item);
|
||||
if (!selection) continue;
|
||||
itemstrings.push(`${amount} ${selection.prettyName + (amount === 1 ? '' : selection.plural)}`);
|
||||
};
|
||||
itemstrings.push(
|
||||
`${amount} ${selection.prettyName + (amount === 1 ? "" : selection.plural)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const last = itemstrings.pop();
|
||||
const itemstring = itemstrings.length === 0 ? last : itemstrings.join(', ') + " and " + last;
|
||||
const itemstring =
|
||||
itemstrings.length === 0 ? last : `${itemstrings.join(", ")} and ${last}`;
|
||||
const message = `You got ${itemstring}`;
|
||||
|
||||
await Promise.all([
|
||||
updateUserRecord(user, userData),
|
||||
sendMessage(message, msg.messageId),
|
||||
createGetLootRecord(user, gainedqbucks, itemDiff, 'getloot'),
|
||||
user.clearLock()
|
||||
createGetLootEvent(user, gainedqbucks, itemDiff, "getloot"),
|
||||
user.clearLock(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (itemstrings.length === 0 && gainedqbucks < 100)
|
||||
await sendMessage("YEOP THAT'S A SCAMBOX YEOP");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { itemObjectArray } from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
|
||||
export default new Command({
|
||||
name: 'getprices',
|
||||
aliases: ['getprices', 'prices', 'shop'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
const txt = itemObjectArray.toSorted((a, b) => a.price - b.price).map(item => `${item.prettyName}: ${item.price}`);
|
||||
await sendMessage(`Prices: ${txt.join(' | ')}`, msg.messageId);
|
||||
}
|
||||
name: "getprices",
|
||||
aliases: ["getprices", "prices", "shop"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const txt = itemObjectArray
|
||||
.toSorted((a, b) => a.price - b.price)
|
||||
.map((item) => `${item.prettyName}: ${item.price}`);
|
||||
await sendMessage(`Prices: ${txt.join(" | ")}`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { buildTimeString } from "lib/dateManager";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
import { timeoutDuration } from "lib/timeout";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'gettimeout',
|
||||
aliases: ['gett', 'gettimeout', 'releasetime'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
name: "gettimeout",
|
||||
aliases: ["gett", "gettimeout", "releasetime"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
const data = await timeoutDuration(target);
|
||||
if (data === false) { await sendMessage(`Chatter ${target.displayName} isn't timed out`, msg.messageId); return; };
|
||||
if (data) { await sendMessage(`${target.displayName} is still timed out for ${buildTimeString(data * 1000, Date.now())}`, msg.messageId); return; };
|
||||
await sendMessage(`${target.displayName} is permanently banned`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
const data = await timeoutDuration(target);
|
||||
if (data === false) {
|
||||
await sendMessage(
|
||||
`Chatter ${target.displayName} isn't timed out`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
await sendMessage(
|
||||
`${target.displayName} is still timed out for ${buildTimeString(data * 1000, Date.now())}`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`${target.displayName} is permanently banned`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,52 +1,94 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import items, { changeItemCount } from "items";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||
import logger from "lib/logger";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'give',
|
||||
aliases: ['give'],
|
||||
usertype: 'chatter',
|
||||
name: "give",
|
||||
aliases: ["give"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
if (Array.from<string>(ANIVNAMES).includes(msg.chatterName)) {
|
||||
await sendMessage("CLANKERS CAN'T GIVE ITEMS UltraMad UltraMad UltraMad");
|
||||
await timeout(user, "STUPID CLANKER", 30);
|
||||
return;
|
||||
}
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a user', msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a user", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (target.username === user.username) { await sendMessage("You can't give yourself items", msg.messageId); return; };
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (target.username === user.username) {
|
||||
await sendMessage("You can't give yourself items", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const targetRecord = await getUserRecord(target);
|
||||
if (!args[1]) { await sendMessage('Please specify an item to give', msg.messageId); return; };
|
||||
if (!args[1]) {
|
||||
await sendMessage("Please specify an item to give", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const item = items.get(args[1].toLowerCase());
|
||||
if (!item) { await sendMessage(`Item ${args[1]} doesn't exist`, msg.messageId); return; };
|
||||
if (!args[2]) { await sendMessage('Please specify the amount of the item you want to give', msg.messageId); return; };
|
||||
const amount = parseInt(args[2]);
|
||||
if (isNaN(amount) || amount < 1) { await sendMessage(`'${args[2]}' is not a valid amount`); return; };
|
||||
if (!item) {
|
||||
await sendMessage(`Item ${args[1]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (!args[2]) {
|
||||
await sendMessage(
|
||||
"Please specify the amount of the item you want to give",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const amount = parseInt(args[2], 10);
|
||||
if (Number.isNaN(amount) || amount < 1) {
|
||||
await sendMessage(`'${args[2]}' is not a valid amount`);
|
||||
return;
|
||||
}
|
||||
const userRecord = await getUserRecord(user);
|
||||
if (userRecord.inventory[item.name]! < amount) { await sendMessage(`You can't give items you don't have!`, msg.messageId); return; };
|
||||
if (userRecord.inventory[item.name]! < amount) {
|
||||
await sendMessage(`You can't give items you don't have!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await user.itemLock() || await target.itemLock()) { await sendMessage('Cannot give item (itemlock)', msg.messageId); return; };
|
||||
if ((await user.itemLock()) || (await target.itemLock())) {
|
||||
await sendMessage("Cannot give item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
user.setLock(),
|
||||
target.setLock()
|
||||
]);
|
||||
await Promise.all([user.setLock(), target.setLock()]);
|
||||
|
||||
const data = await Promise.all([
|
||||
await changeItemCount(target, targetRecord, item.name, amount),
|
||||
await changeItemCount(user, userRecord, item.name, -amount)
|
||||
await changeItemCount(user, userRecord, item.name, -amount),
|
||||
]);
|
||||
|
||||
if (data[0] !== false && data[1] !== false) {
|
||||
const tempdata = data[0];
|
||||
const newamount = tempdata.inventory[item.name]!;
|
||||
await sendMessage(`${user.displayName} gave ${amount} ${item.prettyName + (amount === 1 ? '' : item.plural)} to ${target.displayName}. They now have ${newamount} ${item.prettyName + (newamount === 1 ? '' : item.plural)}`, msg.messageId);
|
||||
await sendMessage(
|
||||
`${user.displayName} gave ${amount} ${item.prettyName + (amount === 1 ? "" : item.plural)} to ${target.displayName}. They now have ${newamount} ${item.prettyName + (newamount === 1 ? "" : item.plural)}`,
|
||||
msg.messageId,
|
||||
);
|
||||
} else {
|
||||
// TODO: Rewrite this section
|
||||
await sendMessage(`Failed to give ${target.displayName} ${amount} ${item.prettyName + (amount === 1 ? '' : item.plural)}`, msg.messageId);
|
||||
logger.warn(`WARNING: Item donation failed: target success: ${data[0] !== false ? "yes" : "no"}, donator success: ${data[1] !== false ? "yes" : "no"}`);
|
||||
};
|
||||
await sendMessage(
|
||||
`Failed to give ${target.displayName} ${amount} ${item.prettyName + (amount === 1 ? "" : item.plural)}`,
|
||||
msg.messageId,
|
||||
);
|
||||
logger.warn(
|
||||
`WARNING: Item donation failed: target success: ${data[0] !== false ? "yes" : "no"}, donator success: ${data[1] !== false ? "yes" : "no"}`,
|
||||
);
|
||||
}
|
||||
await user.clearLock();
|
||||
await target.clearLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
import { Command } from 'lib/commandUtils';
|
||||
import { readdir } from 'node:fs/promises';
|
||||
const commands = new Map<string, Command>; // This map has all command/item aliases mapped to commands/items (many-to-one)
|
||||
const specialAliasCommands = new Map<string, Command>; // This map has all special command/item aliases mapped to commands/items (just like commands map)
|
||||
const basecommands = new Map<string, Command>; // This map has all command names mapped to commands (one-to-one) (no items)
|
||||
import { readdir } from "node:fs/promises";
|
||||
import type { Command } from "lib/commandUtils";
|
||||
|
||||
const commands = new Map<string, Command>(); // This map has all command/item aliases mapped to commands/items (many-to-one)
|
||||
const specialAliasCommands = new Map<string, Command>(); // This map has all special command/item aliases mapped to commands/items (just like commands map)
|
||||
const basecommands = new Map<string, Command>(); // This map has all command names mapped to commands (one-to-one) (no items)
|
||||
|
||||
const files = await readdir(import.meta.dir);
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
if (!file.endsWith(".ts")) continue;
|
||||
if (file === import.meta.file) continue;
|
||||
const command: Command = await import(import.meta.dir + '/' + file.slice(0, -3)).then(a => a.default);
|
||||
const command: Command = await import(
|
||||
`${import.meta.dir}/${file.slice(0, -3)}`
|
||||
).then((a) => a.default);
|
||||
basecommands.set(command.name, command);
|
||||
for (const alias of command.aliases) {
|
||||
commands.set(alias, command); // Since it's not a primitive type the map is filled with references to the command, not the actual object
|
||||
};
|
||||
}
|
||||
for (const alias of command.specialaliases) {
|
||||
specialAliasCommands.set(alias, command);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
import items, { specialAliasItems } from "items";
|
||||
|
||||
for (const [name, item] of Array.from(items)) {
|
||||
commands.set(name, item); // As Item is basically just Command but with more parameters, this should work fine
|
||||
};
|
||||
}
|
||||
for (const [alias, item] of Array.from(specialAliasItems)) {
|
||||
specialAliasCommands.set(alias, item);
|
||||
};
|
||||
}
|
||||
|
||||
export default commands;
|
||||
export { specialAliasCommands, basecommands };
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import items from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
|
||||
export default new Command({
|
||||
name: 'iteminfo',
|
||||
aliases: ['iteminfo', 'itemhelp', 'info'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
const messagequery = parseCommandArgs(msg.messageText).join(' ');
|
||||
if (!messagequery) { await sendMessage('Please specify an item you would like to get info about', msg.messageId); return; };
|
||||
const selection = items.get(messagequery.toLowerCase());
|
||||
if (!selection) { await sendMessage(`'${messagequery}' is not an item`, msg.messageId); return; };
|
||||
await sendMessage(`Name: ${selection.prettyName}, Description: ${selection.description}, Aliases: ${selection.aliases.join(', ')}`, msg.messageId);
|
||||
name: "iteminfo",
|
||||
aliases: ["iteminfo", "itemhelp", "info"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const messagequery = parseCommandArgs(msg.messageText).join(" ");
|
||||
if (!messagequery) {
|
||||
await sendMessage(
|
||||
"Please specify an item you would like to get info about",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const selection = items.get(messagequery.toLowerCase());
|
||||
if (!selection) {
|
||||
await sendMessage(`'${messagequery}' is not an item`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
await sendMessage(
|
||||
`Name: ${selection.prettyName}, Description: ${selection.description}, Aliases: ${selection.aliases.join(", ")}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,17 +3,29 @@ import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'itemlock',
|
||||
aliases: ['itemlock'],
|
||||
usertype: 'moderator',
|
||||
name: "itemlock",
|
||||
aliases: ["itemlock"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a chatter to toggle the lock for', msg.messageId); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
"Please specify a chatter to toggle the lock for",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage('Targeted user does not exist', msg.messageId); return; };
|
||||
if (!target) {
|
||||
await sendMessage("Targeted user does not exist", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const status = await target.itemLock();
|
||||
status ? await target.clearLock() : await target.setLock();
|
||||
await sendMessage(`Successfully ${status ? 'cleared' : 'set'} the item lock on ${target.displayName}`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`Successfully ${status ? "cleared" : "set"} the item lock on ${target.displayName}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getKDLeaderboard } from "db/dbUser";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import User from "user";
|
||||
|
||||
type KD = { user: User; kd: number; };
|
||||
type KD = { user: User; kd: number };
|
||||
|
||||
export default new Command({
|
||||
name: 'monthlykdleaderboard',
|
||||
aliases: ['monthlyleaderboard', 'kdleaderboard', 'leaderboard'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
name: "monthlykdleaderboard",
|
||||
aliases: ["monthlyleaderboard", "kdleaderboard", "leaderboard"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const monthdata = new Date().toISOString().slice(0, 7);
|
||||
|
||||
const rawKD = await getKDLeaderboard(monthdata);
|
||||
if (rawKD.length === 0) {
|
||||
await sendMessage(`No users on leaderboard yet!`, msg.messageId);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const userKDs: KD[] = [];
|
||||
await Promise.all(rawKD.map(async userRecord => {
|
||||
await Promise.all(
|
||||
rawKD.map(async (userRecord) => {
|
||||
const user = await User.initUserId(userRecord.userId.toString());
|
||||
if (!user) return;
|
||||
userKDs.push({ user, kd: userRecord.KD })
|
||||
}));
|
||||
userKDs.push({ user, kd: userRecord.KD });
|
||||
}),
|
||||
);
|
||||
|
||||
userKDs.sort((a, b) => b.kd - a.kd);
|
||||
|
||||
const txt: string[] = [];
|
||||
for (let i = 0; i < (userKDs.length < 10 ? userKDs.length : 10); i++) {
|
||||
txt.push(`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`);
|
||||
};
|
||||
|
||||
await sendMessage(`Monthly leaderboard: ${txt.join(' | ')}`, msg.messageId);
|
||||
txt.push(
|
||||
`${i + 1}. ${userKDs[i]?.user.displayName}: ${userKDs[i]?.kd.toFixed(2)}`,
|
||||
);
|
||||
}
|
||||
|
||||
await sendMessage(`Monthly leaderboard: ${txt.join(" | ")}`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,33 +1,45 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getTimeoutStats, getItemStats } from "lib/getStats";
|
||||
import { getItemStats, getTimeoutStats } from "lib/getStats";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'monthlystats',
|
||||
aliases: ['stats', 'monthlystats'],
|
||||
usertype: 'chatter',
|
||||
name: "monthlystats",
|
||||
aliases: ["stats", "monthlystats"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
let target: User | null = user;
|
||||
if (args[0]) {
|
||||
target = await User.initUsername(args[0]);
|
||||
if (!target) { await sendMessage(`User ${args[0]} doesn't exist!`, msg.messageId); return; };
|
||||
};
|
||||
if (!target) {
|
||||
await sendMessage(`User ${args[0]} doesn't exist!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const [timeout, item] = await Promise.all([getTimeoutStats(target, true), getItemStats(target, true)]);
|
||||
if (!timeout || !item) { await sendMessage(`ERROR: Something went wrong!`, msg.messageId); return; };
|
||||
const [timeout, item] = await Promise.all([
|
||||
getTimeoutStats(target, true),
|
||||
getItemStats(target, true),
|
||||
]);
|
||||
if (!timeout || !item) {
|
||||
await sendMessage(`ERROR: Something went wrong!`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
const KD = timeout.shot.blaster / timeout.hit.blaster;
|
||||
|
||||
await sendMessage(`
|
||||
await sendMessage(
|
||||
`
|
||||
This month: stats of ${target.displayName}:
|
||||
Users blasted: ${timeout.shot.blaster},
|
||||
Blasted by others: ${timeout.hit.blaster} (${isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||
Blasted by others: ${timeout.hit.blaster} (${Number.isNaN(KD) ? 0 : KD.toFixed(2)} K/D).
|
||||
Grenades lobbed: ${item.grenade},
|
||||
TNT exploded: ${item.tnt}.
|
||||
Silver bullets fired: ${timeout.shot.silverbullet},
|
||||
Silver bullets taken: ${timeout.hit.silverbullet}.
|
||||
`, msg.messageId);
|
||||
}
|
||||
`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { getBalanceLeaderboard } from "db/dbUser";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'qbucksleaderboard',
|
||||
aliases: ['qbucksleaderboard', 'baltop', 'moneyleaderboard'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
name: "qbucksleaderboard",
|
||||
aliases: ["qbucksleaderboard", "baltop", "moneyleaderboard"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const data = await getBalanceLeaderboard();
|
||||
if (!data) return;
|
||||
|
||||
@@ -18,8 +18,8 @@ export default new Command({
|
||||
if (!user) continue;
|
||||
txt.push(`${index}. ${user.displayName}: ${userRecord.balance}`);
|
||||
index++;
|
||||
};
|
||||
|
||||
await sendMessage(`Balance leaderboard: ${txt.join(' | ')}`, msg.messageId);
|
||||
}
|
||||
|
||||
await sendMessage(`Balance leaderboard: ${txt.join(" | ")}`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,29 +4,40 @@ import { streamerId } from "main";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'racetime',
|
||||
aliases: ['racetime', 'raceroom'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
try { // this might be some of the worst http code ever
|
||||
name: "racetime",
|
||||
aliases: ["racetime", "raceroom"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
try {
|
||||
// this might be some of the worst http code ever
|
||||
const streamer = await User.initUserId(streamerId);
|
||||
|
||||
const races = await fetch(`https://racetime.gg/smr/data`).then(a => a.json() as any);
|
||||
if (races.current_races.length < 1) { await sendMessage(`No Super Metroid Randomizer races active`, msg.messageId); return; };
|
||||
const races = await fetch(`https://racetime.gg/smr/data`).then(
|
||||
(a) => a.json() as any,
|
||||
);
|
||||
if (races.current_races.length < 1) {
|
||||
await sendMessage(
|
||||
`No Super Metroid Randomizer races active`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const race of races.current_races) {
|
||||
const data = await fetch(`https://racetime.gg${race.data_url}`).then(a => a.json() as any);
|
||||
const data = await fetch(`https://racetime.gg${race.data_url}`).then(
|
||||
(a) => a.json() as any,
|
||||
);
|
||||
for (const racer of data.entrants) {
|
||||
if (racer.user.twitch_name === streamer?.username) {
|
||||
await sendMessage(`https://racetime.gg${data.url}`, msg.messageId);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
await sendMessage('Streamer is not in a racetime race.', msg.messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
await sendMessage("Streamer is not in a racetime race.", msg.messageId);
|
||||
} catch (err) {
|
||||
await sendMessage("Failed to get racetime status", msg.messageId);
|
||||
logger.err(err as string);
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,19 +4,19 @@ import { streamerId } from "main";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
export default new Command({
|
||||
name: 'randomchatter',
|
||||
aliases: ['randomchatter'],
|
||||
usertype: 'moderator',
|
||||
name: "randomchatter",
|
||||
aliases: ["randomchatter"],
|
||||
usertype: "moderator",
|
||||
execution: async (msg) => {
|
||||
const data = await api.chat.getChatters(streamerId).then(a => a.data);
|
||||
const data = await api.chat.getChatters(streamerId).then((a) => a.data);
|
||||
const target = data[Math.floor(Math.random() * data.length)];
|
||||
await playAlert({
|
||||
name: 'blastinRoulette',
|
||||
name: "blastinRoulette",
|
||||
user: msg.chatterName,
|
||||
targets: data.map(a => a.userDisplayName),
|
||||
finaltarget: target?.userDisplayName
|
||||
targets: data.map((a) => a.userDisplayName),
|
||||
finaltarget: target?.userDisplayName,
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 4000));
|
||||
await sendMessage(`${target?.userDisplayName}`, msg.messageId);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { streamerUsers } from "main";
|
||||
import { removeAdmin } from "lib/admins";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { streamerUsers } from "main";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'removeadmin',
|
||||
aliases: ['removeadmin'],
|
||||
usertype: 'streamer',
|
||||
name: "removeadmin",
|
||||
aliases: ["removeadmin"],
|
||||
usertype: "streamer",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (streamerUsers.includes(target.id)) { await sendMessage(`Can't remove admin ${target.displayName} as they are managed by the bot program`, msg.messageId); return; };
|
||||
const data = await removeAdmin(target.id);
|
||||
if (data === 1) await sendMessage(`${target.displayName} is no longer an admin`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} isn't an admin`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (streamerUsers.includes(target.id)) {
|
||||
await sendMessage(
|
||||
`Can't remove admin ${target.displayName} as they are managed by the bot program`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const data = await removeAdmin(target.id);
|
||||
if (data === 1)
|
||||
await sendMessage(
|
||||
`${target.displayName} is no longer an admin`,
|
||||
msg.messageId,
|
||||
);
|
||||
else
|
||||
await sendMessage(`${target.displayName} isn't an admin`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { streamerUsers } from "main";
|
||||
import { redis } from "lib/redis";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerUsers } from "main";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'removebot',
|
||||
aliases: ['removebot'],
|
||||
usertype: 'streamer',
|
||||
name: "removebot",
|
||||
aliases: ["removebot"],
|
||||
usertype: "streamer",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (streamerUsers.includes(target.id)) { await sendMessage(`Cannot change bot status of qweribot managed user`, msg.messageId); return; };
|
||||
const data = await redis.del(`user:${target.id}:bot`);
|
||||
if (data === 1) await sendMessage(`${target.displayName} is no longer a bot`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} isn't a bot`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (streamerUsers.includes(target.id)) {
|
||||
await sendMessage(
|
||||
`Cannot change bot status of qweribot managed user`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const data = await redis.del(`user:${target.id}:bot`);
|
||||
if (data === 1)
|
||||
await sendMessage(
|
||||
`${target.displayName} is no longer a bot`,
|
||||
msg.messageId,
|
||||
);
|
||||
else await sendMessage(`${target.displayName} isn't a bot`, msg.messageId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,22 +1,42 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { streamerUsers } from "main";
|
||||
import { removeInvuln } from "lib/invuln";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { streamerUsers } from "main";
|
||||
import User from "user";
|
||||
|
||||
export default new Command({
|
||||
name: 'removeinvuln',
|
||||
aliases: ['removeinvuln'],
|
||||
usertype: 'moderator',
|
||||
name: "removeinvuln",
|
||||
aliases: ["removeinvuln"],
|
||||
usertype: "moderator",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify a target', msg.messageId); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId); return; };
|
||||
if (streamerUsers.includes(target.id)) { await sendMessage(`Can't remove invulnerability from ${target.displayName} as they are managed by the bot program`, msg.messageId); return; };
|
||||
const data = await removeInvuln(target.id);
|
||||
if (data === 1) await sendMessage(`${target.displayName} is no longer invulnerable`, msg.messageId);
|
||||
else await sendMessage(`${target.displayName} isn't invulnerable`, msg.messageId);
|
||||
if (!args[0]) {
|
||||
await sendMessage("Please specify a target", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
if (!target) {
|
||||
await sendMessage(`Chatter ${args[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
if (streamerUsers.includes(target.id)) {
|
||||
await sendMessage(
|
||||
`Can't remove invulnerability from ${target.displayName} as they are managed by the bot program`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const data = await removeInvuln(target.id);
|
||||
if (data === 1)
|
||||
await sendMessage(
|
||||
`${target.displayName} is no longer invulnerable`,
|
||||
msg.messageId,
|
||||
);
|
||||
else
|
||||
await sendMessage(
|
||||
`${target.displayName} isn't invulnerable`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,21 +5,24 @@ import { timeout } from "lib/timeout";
|
||||
const barrelCount = 6;
|
||||
|
||||
export default new Command({
|
||||
name: 'roulette',
|
||||
aliases: ['roulette'],
|
||||
usertype: 'chatter',
|
||||
name: "roulette",
|
||||
aliases: ["roulette"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
if (!await redis.exists('rouletteCount')) await redis.set('rouletteCount', "0");
|
||||
const currentChamber = Number(await redis.get('rouletteCount'));
|
||||
if (!(await redis.exists("rouletteCount")))
|
||||
await redis.set("rouletteCount", "0");
|
||||
const currentChamber = Number(await redis.get("rouletteCount"));
|
||||
const shot = Math.random() < 1 / (barrelCount - currentChamber);
|
||||
if (!shot) await Promise.all([
|
||||
redis.incr('rouletteCount'),
|
||||
sendMessage("SWEAT Click SWEAT", msg.messageId)
|
||||
if (!shot)
|
||||
await Promise.all([
|
||||
redis.incr("rouletteCount"),
|
||||
sendMessage("SWEAT Click SWEAT", msg.messageId),
|
||||
]);
|
||||
else await Promise.all([
|
||||
redis.set('rouletteCount', "0"),
|
||||
else
|
||||
await Promise.all([
|
||||
redis.set("rouletteCount", "0"),
|
||||
sendMessage("wybuh BANG!! wybuh"),
|
||||
timeout(user, "You lost at russian roulette!", 5 * 60)
|
||||
timeout(user, "You lost at russian roulette!", 5 * 60),
|
||||
]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,19 +2,28 @@ import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { timeout } from "lib/timeout";
|
||||
|
||||
export default new Command({
|
||||
name: 'seiso',
|
||||
aliases: ['seiso'],
|
||||
usertype: 'chatter',
|
||||
name: "seiso",
|
||||
aliases: ["seiso"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const rand = Math.floor(Math.random() * 101);
|
||||
if (rand > 75) await sendMessage(`${rand}% seiso YAAAA`, msg.messageId);
|
||||
else if (rand > 50) await sendMessage(`${rand}% seiso POGGERS`, msg.messageId);
|
||||
else if (rand === 50) await sendMessage(`${rand}% seiso ok`, msg.messageId);
|
||||
else if (rand > 30) await sendMessage(`${rand}% seiso SWEAT`, msg.messageId);
|
||||
else if (rand > 10) await sendMessage(`${rand}% seiso catErm`, msg.messageId);
|
||||
else await Promise.all([
|
||||
sendMessage(`${rand}% seiso RIPBOZO`),
|
||||
timeout(user, 'TOO YABAI!', 60)
|
||||
else if (rand === 67)
|
||||
await Promise.all([
|
||||
sendMessage(`KOKPEG 67 KOKPEG`),
|
||||
timeout(user, "SIX SEVEN", 67),
|
||||
]);
|
||||
}
|
||||
else if (rand > 50)
|
||||
await sendMessage(`${rand}% seiso POGGERS`, msg.messageId);
|
||||
else if (rand === 50) await sendMessage(`${rand}% seiso ok`, msg.messageId);
|
||||
else if (rand > 30)
|
||||
await sendMessage(`${rand}% seiso SWEAT`, msg.messageId);
|
||||
else if (rand > 10)
|
||||
await sendMessage(`${rand}% seiso catErm`, msg.messageId);
|
||||
else
|
||||
await Promise.all([
|
||||
sendMessage(`${rand}% seiso RIPBOZO`),
|
||||
timeout(user, "TOO YABAI!", 60),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { isAdmin } from "lib/admins";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'stacking',
|
||||
aliases: ['stacking'],
|
||||
usertype: 'chatter',
|
||||
name: "stacking",
|
||||
aliases: ["stacking"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
execution: async msg => {
|
||||
execution: async (msg) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0] || !await isAdmin(msg.chatterId)) { await sendMessage(`Timeout stacking is currently ${await redis.exists('timeoutStacking') ? "on" : "off"}`, msg.messageId); return; };
|
||||
if (!args[0] || !(await isAdmin(msg.chatterId))) {
|
||||
await sendMessage(
|
||||
`Timeout stacking is currently ${(await redis.exists("timeoutStacking")) ? "on" : "off"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Only admins can reach this part of code
|
||||
switch (args[0]) {
|
||||
case 'enable':
|
||||
case 'on':
|
||||
await redis.set('timeoutStacking', '1');
|
||||
await sendMessage('Timeout stacking is now on')
|
||||
case "enable":
|
||||
case "on":
|
||||
await redis.set("timeoutStacking", "1");
|
||||
await sendMessage("Timeout stacking is now on");
|
||||
break;
|
||||
case 'disable':
|
||||
case 'off':
|
||||
await redis.del('timeoutStacking');
|
||||
await sendMessage('Timeout stacking is now off')
|
||||
case "disable":
|
||||
case "off":
|
||||
await redis.del("timeoutStacking");
|
||||
await sendMessage("Timeout stacking is now off");
|
||||
break;
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { handleCheer } from "events/message";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
|
||||
export default new Command({
|
||||
name: 'testcheer',
|
||||
aliases: ['testcheer'],
|
||||
usertype: 'streamer',
|
||||
name: "testcheer",
|
||||
aliases: ["testcheer"],
|
||||
usertype: "streamer",
|
||||
disableable: false,
|
||||
execution: async (msg, user) => {
|
||||
const args = parseCommandArgs(msg.messageText);
|
||||
if (!args[0]) { await sendMessage('Please specify the amount of fake bits you want to send', msg.messageId); return; };
|
||||
if (isNaN(parseInt(args[0]))) { await sendMessage(`${args[0]} is not a valid amout of bits`); return; };
|
||||
if (!args[0]) {
|
||||
await sendMessage(
|
||||
"Please specify the amount of fake bits you want to send",
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (Number.isNaN(parseInt(args[0], 10))) {
|
||||
await sendMessage(`${args[0]} is not a valid amout of bits`);
|
||||
return;
|
||||
}
|
||||
const bits = Number(args.shift()); // we shift it so the amount of bits isn't part of the handleCheer message, we already know that args[0] can be parsed as a number so this is fine.
|
||||
await handleCheer(msg, bits, user);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,25 +1,60 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import items from "items";
|
||||
import { isInvuln, removeInvuln } from "lib/invuln";
|
||||
import { streamerUsers } from "main";
|
||||
import getloot from "commands/getloot";
|
||||
import items from "items";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { isInvuln, removeInvuln } from "lib/invuln";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerUsers } from "main";
|
||||
|
||||
export default new Command({
|
||||
name: 'use',
|
||||
aliases: ['use'],
|
||||
usertype: 'chatter',
|
||||
name: "use",
|
||||
aliases: ["use"],
|
||||
usertype: "chatter",
|
||||
disableable: false,
|
||||
specialaliases: ['i'],
|
||||
specialaliases: ["i"],
|
||||
execution: async (msg, user, specialargs) => {
|
||||
const messagequery = msg.messageText.trim().split(' ').slice(1); // This selects the item, so on "i blast mrockstar20" it would pick ["blast", "mrockstar20"]
|
||||
const silent = msg.messageText.toLowerCase().startsWith('i');
|
||||
if (!messagequery[0]) { if (!silent) { await sendMessage('Please specify an item you would like to use', msg.messageId); }; return; };
|
||||
const selection = items.get(messagequery[0].toLowerCase());
|
||||
if (messagequery[0].toLowerCase() === "lootbox") { await getloot.execute(msg, user); return; };
|
||||
if (!selection) { if (!silent) { await sendMessage(`'${messagequery[0]}' is not an item`, msg.messageId); }; return; };
|
||||
if (await redis.sismember('disabledcommands', selection.name)) { await sendMessage(`The ${selection.prettyName} item is disabled`, msg.messageId); return; };
|
||||
if (await isInvuln(msg.chatterId) && !streamerUsers.includes(msg.chatterId)) { await sendMessage(`You're no longer an invuln because you used an item.`, msg.messageId); await removeInvuln(msg.chatterId); };
|
||||
await selection.execute(msg, user, specialargs);
|
||||
const messagequery = msg.messageText.trim().split(" ").slice(1); // This selects the item, so on "i blast mrockstar20" it would pick ["blast", "mrockstar20"]
|
||||
const silent = msg.messageText.toLowerCase().startsWith("i");
|
||||
if (!messagequery[0]) {
|
||||
if (!silent) {
|
||||
await sendMessage(
|
||||
"Please specify an item you would like to use",
|
||||
msg.messageId,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const selection = items.get(messagequery[0].toLowerCase());
|
||||
if (messagequery[0].toLowerCase() === "lootbox") {
|
||||
if (await redis.sismember("disabledcommands", "getloot")) {
|
||||
await sendMessage("Lootboxes are currently disabled", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await getloot.execute(msg, user);
|
||||
return;
|
||||
}
|
||||
if (!selection) {
|
||||
if (!silent) {
|
||||
await sendMessage(`'${messagequery[0]}' is not an item`, msg.messageId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (await redis.sismember("disabledcommands", selection.name)) {
|
||||
await sendMessage(
|
||||
`The ${selection.prettyName} item is disabled`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(await isInvuln(msg.chatterId)) &&
|
||||
!streamerUsers.includes(msg.chatterId)
|
||||
) {
|
||||
await sendMessage(
|
||||
`You're no longer an invuln because you used an item.`,
|
||||
msg.messageId,
|
||||
);
|
||||
await removeInvuln(msg.chatterId);
|
||||
}
|
||||
await selection.execute(msg, user, specialargs);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export default new Command({
|
||||
name: 'vulnchatters',
|
||||
aliases: ['vulnchatters', 'vulnc', 'vc'],
|
||||
usertype: 'chatter',
|
||||
execution: async msg => {
|
||||
const data = await redis.keys('user:*:vulnerable');
|
||||
name: "vulnchatters",
|
||||
aliases: ["vulnchatters", "vulnc", "vc"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg) => {
|
||||
const data = await redis.keys("user:*:vulnerable");
|
||||
const one = data.length === 1;
|
||||
await sendMessage(`There ${one ? 'is' : 'are'} ${data.length} vulnerable chatter${one ? '' : 's'}`, msg.messageId);
|
||||
}
|
||||
await sendMessage(
|
||||
`There ${one ? "is" : "are"} ${data.length} vulnerable chatter${one ? "" : "s"}`,
|
||||
msg.messageId,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,18 +3,24 @@ import { timeout } from "lib/timeout";
|
||||
|
||||
// Remake of the !yabai command in ttv/kiara_tv
|
||||
export default new Command({
|
||||
name: 'yabai',
|
||||
aliases: ['yabai', 'goon'],
|
||||
usertype: 'chatter',
|
||||
name: "yabai",
|
||||
aliases: ["yabai", "goon"],
|
||||
usertype: "chatter",
|
||||
execution: async (msg, user) => {
|
||||
const rand = Math.floor(Math.random() * 101);
|
||||
if (rand < 25) sendMessage(`${rand}% yabai! GIGACHAD`, msg.messageId);
|
||||
else if (rand < 50) sendMessage(`${rand}% yabai POGGERS`, msg.messageId);
|
||||
else if (rand === 50) sendMessage(`${rand}% yabai ok`, msg.messageId);
|
||||
else if (rand < 90) sendMessage(`${rand}% yabai AINTNOWAY`, msg.messageId);
|
||||
else await Promise.all([
|
||||
sendMessage(`${msg.chatterDisplayName} is ${rand}% yabai CAUGHT`),
|
||||
timeout(user, "TOO YABAI!", 60)
|
||||
else if (rand === 67)
|
||||
await Promise.all([
|
||||
sendMessage(`KOKPEG 67 KOKPEG`),
|
||||
timeout(user, "SIX SEVEN", 67),
|
||||
]);
|
||||
}
|
||||
else if (rand < 90) sendMessage(`${rand}% yabai AINTNOWAY`, msg.messageId);
|
||||
else
|
||||
await Promise.all([
|
||||
sendMessage(`${msg.chatterDisplayName} is ${rand}% yabai CAUGHT`),
|
||||
timeout(user, "TOO YABAI!", 60),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function connectionCheck() {
|
||||
try {
|
||||
await db.select().from(users); // The query doesn't matter, only that it fails. This also fails if the migration hasn't taken place
|
||||
pgstatus = true;
|
||||
} catch { };
|
||||
} catch {}
|
||||
const tempclient = new RedisClient(undefined, {
|
||||
connectionTimeout: 200,
|
||||
maxRetries: 1,
|
||||
@@ -17,9 +17,15 @@ export async function connectionCheck() {
|
||||
try {
|
||||
await tempclient.connect();
|
||||
redisstatus = true;
|
||||
} catch { };
|
||||
logger.info(`Currently using the "${process.env.NODE_ENV ?? "production"}" database`);
|
||||
pgstatus ? logger.ok(`Postgresql status: good`) : logger.err(`Postgresql status: bad`);
|
||||
redisstatus ? logger.ok(`Redis/Valkey status: good`) : logger.err(`Redis/Valkey status: bad`);
|
||||
} catch {}
|
||||
logger.info(
|
||||
`Currently using the \x1b[3;4;1;95m"${process.env.NODE_ENV ?? "production"}"\x1b[0;97m database`,
|
||||
);
|
||||
pgstatus
|
||||
? logger.ok(`Postgresql status: good`)
|
||||
: logger.err(`Postgresql status: bad`);
|
||||
redisstatus
|
||||
? logger.ok(`Redis/Valkey status: good`)
|
||||
: logger.err(`Redis/Valkey status: bad`);
|
||||
if (!pgstatus || !redisstatus) process.exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
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";
|
||||
@@ -6,5 +6,5 @@ export const password = process.env.POSTGRES_PASSWORD ?? "";
|
||||
export const database = process.env.POSTGRES_DB ?? "";
|
||||
export const url = `postgresql://${user}:${password}@${host}/${database}`;
|
||||
|
||||
import { drizzle } from 'drizzle-orm/bun-sql';
|
||||
export default drizzle(url, { schema })
|
||||
import { drizzle } from "drizzle-orm/bun-sql";
|
||||
export default drizzle(url, { schema });
|
||||
|
||||
@@ -1,32 +1,55 @@
|
||||
import db from "db/connection";
|
||||
import User from "user";
|
||||
import { anivTimeouts } from "db/schema";
|
||||
import { type anivBots } from "lib/handleAnivMessage";
|
||||
import { count, eq, and } from "drizzle-orm";
|
||||
import { and, count, eq } from "drizzle-orm";
|
||||
import type { anivBots } from "lib/handleAnivMessage";
|
||||
import type User from "user";
|
||||
|
||||
/** To create a dodge record, set the duration to 0 */
|
||||
export async function createAnivTimeoutRecord(message: string, anivBot: anivBots, user: User, duration: number) {
|
||||
export async function createAnivTimeoutRecord(
|
||||
message: string,
|
||||
anivBot: anivBots,
|
||||
user: User,
|
||||
duration: number,
|
||||
) {
|
||||
await db.insert(anivTimeouts).values({
|
||||
message,
|
||||
anivBot,
|
||||
user: parseInt(user.id),
|
||||
user: parseInt(user.id, 10),
|
||||
duration,
|
||||
timeout: duration !== 0
|
||||
timeout: duration !== 0,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAnivTimeouts(user: User) {
|
||||
let [dodge, dead] = await Promise.all([
|
||||
db.select({
|
||||
dodge: count()
|
||||
}).from(anivTimeouts).where(and(eq(anivTimeouts.user, parseInt(user.id)), eq(anivTimeouts.timeout, false))).then(a => a[0]?.dodge),
|
||||
db.select({
|
||||
dead: count()
|
||||
}).from(anivTimeouts).where(and(eq(anivTimeouts.user, parseInt(user.id)), eq(anivTimeouts.timeout, true))).then(a => a[0]?.dead)
|
||||
db
|
||||
.select({
|
||||
dodge: count(),
|
||||
})
|
||||
.from(anivTimeouts)
|
||||
.where(
|
||||
and(
|
||||
eq(anivTimeouts.user, parseInt(user.id, 10)),
|
||||
eq(anivTimeouts.timeout, false),
|
||||
),
|
||||
)
|
||||
.then((a) => a[0]?.dodge),
|
||||
db
|
||||
.select({
|
||||
dead: count(),
|
||||
})
|
||||
.from(anivTimeouts)
|
||||
.where(
|
||||
and(
|
||||
eq(anivTimeouts.user, parseInt(user.id, 10)),
|
||||
eq(anivTimeouts.timeout, true),
|
||||
),
|
||||
)
|
||||
.then((a) => a[0]?.dead),
|
||||
]);
|
||||
|
||||
if (!dodge) dodge = 0;
|
||||
if (!dead) dead = 0;
|
||||
|
||||
return { dodge, dead };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,24 +5,30 @@ import { eq } from "drizzle-orm";
|
||||
|
||||
export async function createAuthRecord(token: AccessToken, userId: string) {
|
||||
await db.insert(auth).values({
|
||||
id: parseInt(userId),
|
||||
accesstoken: token
|
||||
id: parseInt(userId, 10),
|
||||
accesstoken: token,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAuthRecord(userId: string, requiredIntents: string[]) {
|
||||
const data = await db.query.auth.findFirst({
|
||||
where: eq(auth.id, parseInt(userId))
|
||||
where: eq(auth.id, parseInt(userId, 10)),
|
||||
});
|
||||
if (!data) return undefined;
|
||||
if (!requiredIntents.every(intent => data.accesstoken.scope.includes(intent))) return undefined;
|
||||
if (
|
||||
!requiredIntents.every((intent) => data.accesstoken.scope.includes(intent))
|
||||
)
|
||||
return undefined;
|
||||
return { accesstoken: data.accesstoken };
|
||||
};
|
||||
}
|
||||
|
||||
export async function updateAuthRecord(userId: string, newtoken: AccessToken) {
|
||||
await db.update(auth).set({ accesstoken: newtoken }).where(eq(auth.id, parseInt(userId)));
|
||||
};
|
||||
await db
|
||||
.update(auth)
|
||||
.set({ accesstoken: newtoken })
|
||||
.where(eq(auth.id, parseInt(userId, 10)));
|
||||
}
|
||||
|
||||
export async function deleteAuthRecord(userId: string): Promise<void> {
|
||||
await db.delete(auth).where(eq(auth.id, parseInt(userId)));
|
||||
};
|
||||
await db.delete(auth).where(eq(auth.id, parseInt(userId, 10)));
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import db from "db/connection";
|
||||
import { cheerEvents } from "db/schema";
|
||||
import { and, between, eq, SQL } from "drizzle-orm";
|
||||
import type { items } from "items";
|
||||
import User from "user";
|
||||
|
||||
export async function createCheerEventRecord(user: User, cheer: items): Promise<void> {
|
||||
await db.insert(cheerEvents).values({ user: parseInt(user.id), event: cheer });
|
||||
};
|
||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||
import type User from "user";
|
||||
|
||||
export async function getCheerEvents(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(cheerEvents.user, parseInt(user.id));
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
cheerEvents.user,
|
||||
parseInt(user.id, 10),
|
||||
);
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(cheerEvents.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(cheerEvents.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
const data = await db.select().from(cheerEvents).where(condition);
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import db from "db/connection";
|
||||
import { cheers } from "db/schema";
|
||||
import User from "user";
|
||||
import { and, between, eq, SQL } from "drizzle-orm";
|
||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||
import type User from "user";
|
||||
|
||||
export async function createCheerRecord(user: User, amount: number): Promise<void> {
|
||||
await db.insert(cheers).values({ user: parseInt(user.id), amount });
|
||||
};
|
||||
export async function createCheerRecord(
|
||||
user: User,
|
||||
amount: number,
|
||||
): Promise<void> {
|
||||
await db.insert(cheers).values({ user: parseInt(user.id, 10), amount });
|
||||
}
|
||||
|
||||
export async function getCheers(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(cheers.user, parseInt(user.id));
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
cheers.user,
|
||||
parseInt(user.id, 10),
|
||||
);
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(cheers.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(cheers.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
const data = await db.select().from(cheers).where(condition);
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +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),
|
||||
qbucks: qbucks,
|
||||
items: inventory,
|
||||
trigger
|
||||
});
|
||||
};
|
||||
@@ -1,35 +1,38 @@
|
||||
import db from "db/connection";
|
||||
import { timeouts } from "db/schema";
|
||||
import User from "user";
|
||||
import type { items } from "items";
|
||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||
|
||||
export async function createTimeoutRecord(user: User, target: User, item: items): Promise<void> {
|
||||
await db.insert(timeouts).values({
|
||||
user: parseInt(user.id),
|
||||
target: parseInt(target.id),
|
||||
item
|
||||
});
|
||||
};
|
||||
import type User from "user";
|
||||
|
||||
export async function getTimeoutsAsUser(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(timeouts.user, parseInt(user.id));
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
timeouts.user,
|
||||
parseInt(user.id, 10),
|
||||
);
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(timeouts.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(timeouts.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
const data = await db.select().from(timeouts).where(condition);
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getTimeoutsAsTarget(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(timeouts.target, parseInt(user.id));
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
timeouts.target,
|
||||
parseInt(user.id, 10),
|
||||
);
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(timeouts.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(timeouts.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
const data = await db.select().from(timeouts).where(condition);
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import db from "db/connection";
|
||||
import { usedItems } from "db/schema";
|
||||
import User from "user";
|
||||
import type { items } from "items";
|
||||
import { and, between, eq, type SQL } from "drizzle-orm";
|
||||
|
||||
export async function createUsedItemRecord(user: User, item: items): Promise<void> {
|
||||
await db.insert(usedItems).values({ user: parseInt(user.id), item });
|
||||
};
|
||||
import type User from "user";
|
||||
|
||||
export async function getItemsUsed(user: User, monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = eq(usedItems.user, parseInt(user.id));
|
||||
let condition: SQL<unknown> | undefined = eq(
|
||||
usedItems.user,
|
||||
parseInt(user.id, 10),
|
||||
);
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(usedItems.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(usedItems.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
const data = await db.select().from(usedItems).where(condition);
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
121
src/db/dbUser.ts
121
src/db/dbUser.ts
@@ -1,57 +1,86 @@
|
||||
import db from "db/connection";
|
||||
import { timeouts, users } from "db/schema";
|
||||
import { itemarray } from "items";
|
||||
import {
|
||||
and,
|
||||
between,
|
||||
count,
|
||||
desc,
|
||||
eq,
|
||||
type InferSelectModel,
|
||||
inArray,
|
||||
ne,
|
||||
type SQL,
|
||||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { itemarray, type items } from "items";
|
||||
import { ANIVNAMES } from "lib/handleAnivMessage";
|
||||
import type User from "user";
|
||||
import { count, desc, eq, inArray, sql, ne, between, and, SQL, type InferSelectModel } from "drizzle-orm";
|
||||
|
||||
/** Use this function to both ensure existance and to retreive data */
|
||||
export async function getUserRecord(user: User) {
|
||||
const data = await db.query.users.findFirst({ where: eq(users.id, parseInt(user.id)) });
|
||||
const data = await db.query.users.findFirst({
|
||||
where: eq(users.id, parseInt(user.id, 10)),
|
||||
});
|
||||
if (!data) return createUserRecord(user);
|
||||
|
||||
if (Object.keys(data.inventory).sort().toString() !== itemarray.sort().toString()) { // If the items in the user inventory are missing an item.
|
||||
itemarray.forEach(key => {
|
||||
if (
|
||||
Object.keys(data.inventory).sort().toString() !==
|
||||
itemarray.sort().toString()
|
||||
) {
|
||||
// If the items in the user inventory are missing an item.
|
||||
itemarray.forEach((key) => {
|
||||
if (!(key in data.inventory)) data.inventory[key] = 0;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllUserRecords() {
|
||||
return await db.select().from(users);
|
||||
};
|
||||
}
|
||||
|
||||
async function createUserRecord(user: User) {
|
||||
return await db.insert(users).values({
|
||||
id: parseInt(user.id),
|
||||
username: user.username
|
||||
}).returning().then(a => {
|
||||
if (!a[0]) throw Error('Something went horribly wrong');
|
||||
return a[0]
|
||||
return await db
|
||||
.insert(users)
|
||||
.values({
|
||||
id: parseInt(user.id, 10),
|
||||
username: user.username,
|
||||
})
|
||||
.returning()
|
||||
.then((a) => {
|
||||
if (!a[0]) throw Error("Something went horribly wrong");
|
||||
return a[0];
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export type UserRecord = InferSelectModel<typeof users>;
|
||||
|
||||
export async function updateUserRecord(user: User, newData: UserRecord) {
|
||||
await db.update(users).set(newData).where(eq(users.id, parseInt(user.id)));
|
||||
await db
|
||||
.update(users)
|
||||
.set(newData)
|
||||
.where(eq(users.id, parseInt(user.id, 10)));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getBalanceLeaderboard() {
|
||||
return await db.select().from(users).orderBy(desc(users.balance)).limit(10);
|
||||
};
|
||||
}
|
||||
|
||||
export async function getKDLeaderboard(monthData?: string) {
|
||||
let condition: SQL<unknown> | undefined = ne(timeouts.item, 'silverbullet');
|
||||
let condition: SQL<unknown> | undefined = ne(timeouts.item, "silverbullet");
|
||||
if (monthData) {
|
||||
const begin = Date.parse(monthData);
|
||||
const end = new Date(begin).setMonth(new Date(begin).getMonth() + 1);
|
||||
condition = and(condition, between(timeouts.created, new Date(begin), new Date(end)));
|
||||
};
|
||||
condition = and(
|
||||
condition,
|
||||
between(timeouts.created, new Date(begin), new Date(end)),
|
||||
);
|
||||
}
|
||||
|
||||
const usersGotShot = await db.select({
|
||||
const usersGotShot = await db
|
||||
.select({
|
||||
userId: users.id,
|
||||
amount: count(timeouts.target),
|
||||
})
|
||||
@@ -61,9 +90,10 @@ export async function getKDLeaderboard(monthData?: string) {
|
||||
.having(sql`count(${timeouts.id}) > 5`)
|
||||
.where(condition);
|
||||
|
||||
const usersThatShot = await db.select({
|
||||
const usersThatShot = await db
|
||||
.select({
|
||||
userId: users.id,
|
||||
amount: count(timeouts.user)
|
||||
amount: count(timeouts.user),
|
||||
})
|
||||
.from(users)
|
||||
.innerJoin(timeouts, eq(users.id, timeouts.user))
|
||||
@@ -71,20 +101,45 @@ export async function getKDLeaderboard(monthData?: string) {
|
||||
.where(
|
||||
and(
|
||||
condition,
|
||||
inArray(users.id, usersGotShot.map(a => a.userId))
|
||||
)
|
||||
inArray(
|
||||
users.id,
|
||||
usersGotShot.map((a) => a.userId),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const lookup = new Map(usersThatShot.map(a => [a.userId, a.amount]));
|
||||
const result = usersGotShot.map(user => ({
|
||||
const lookup = new Map(usersThatShot.map((a) => [a.userId, a.amount]));
|
||||
const result = usersGotShot.map((user) => ({
|
||||
userId: user.userId,
|
||||
KD: lookup.get(user.userId)! / user.amount
|
||||
KD: lookup.get(user.userId)! / user.amount,
|
||||
}));
|
||||
|
||||
result.map(user => {
|
||||
if (isNaN(user.KD)) user.KD = 0;
|
||||
return user
|
||||
result.map((user) => {
|
||||
if (Number.isNaN(user.KD)) user.KD = 0;
|
||||
return user;
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
162
src/db/schema.ts
162
src/db/schema.ts
@@ -1,20 +1,28 @@
|
||||
import type { AccessToken } from "@twurple/auth";
|
||||
import type { inventory, items } from "items";
|
||||
import { boolean, integer, jsonb, pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
|
||||
import type { anivBots } from "lib/handleAnivMessage";
|
||||
import type { cheers as cheertypes } from "cheers";
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
timestamp,
|
||||
uuid,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import type { inventory, items } from "items";
|
||||
import type { anivBots } from "lib/handleAnivMessage";
|
||||
|
||||
export const auth = pgTable('auth', {
|
||||
export const auth = pgTable("auth", {
|
||||
id: integer().primaryKey(),
|
||||
accesstoken: jsonb().$type<AccessToken>().notNull()
|
||||
accesstoken: jsonb().$type<AccessToken>().notNull(),
|
||||
});
|
||||
|
||||
export const users = pgTable('users', {
|
||||
export const users = pgTable("users", {
|
||||
id: integer().primaryKey().notNull(),
|
||||
username: varchar().notNull(),
|
||||
balance: integer().default(0).notNull(),
|
||||
inventory: jsonb().$type<inventory>().default({}).notNull(),
|
||||
lastlootbox: timestamp().default(new Date(0)).notNull()
|
||||
});
|
||||
|
||||
export const usersRelations = relations(users, ({ many }) => ({
|
||||
@@ -24,103 +32,167 @@ export const usersRelations = relations(users, ({ many }) => ({
|
||||
cheerEvents: many(cheerEvents),
|
||||
cheers: many(cheers),
|
||||
anivTimeouts: many(anivTimeouts),
|
||||
getLoots: many(getLoots)
|
||||
getLoots: many(getLoots),
|
||||
events: many(events),
|
||||
}));
|
||||
|
||||
export const timeouts = pgTable('timeouts', {
|
||||
export const timeouts = pgTable("timeouts", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
target: integer().notNull().references(() => users.id),
|
||||
item: varchar().$type<items>().notNull(),
|
||||
created: timestamp().defaultNow().notNull()
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
target: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
item: varchar().$type<items | cheertypes>().notNull(),
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
cheer: uuid().references(() => cheerEvents.id),
|
||||
usedItem: uuid().references(() => usedItems.id),
|
||||
});
|
||||
|
||||
export const timeoutsRelations = relations(timeouts, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [timeouts.user],
|
||||
references: [users.id],
|
||||
relationName: 'shooter'
|
||||
relationName: "shooter",
|
||||
}),
|
||||
target: one(users, {
|
||||
fields: [timeouts.target],
|
||||
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", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
item: varchar().$type<items>().notNull(),
|
||||
created: timestamp().defaultNow().notNull()
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const usedItemsRelations = relations(usedItems, ({ one }) => ({
|
||||
export const usedItemsRelations = relations(usedItems, ({ one, many }) => ({
|
||||
user: one(users, {
|
||||
fields: [usedItems.user],
|
||||
references: [users.id]
|
||||
})
|
||||
references: [users.id],
|
||||
}),
|
||||
timeouts: many(timeouts),
|
||||
}));
|
||||
|
||||
export const cheerEvents = pgTable('cheerEvents', {
|
||||
/**
|
||||
* "success" when everything works
|
||||
* "compensated" when the user gets an item in their inventory for a cheer
|
||||
*/
|
||||
export type cheerEventStatus = "success" | "compensated";
|
||||
|
||||
export const cheerEvents = pgTable("cheerEvents", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
event: varchar().$type<items>().notNull(),
|
||||
created: timestamp().defaultNow().notNull()
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
event: varchar().$type<items | cheertypes>().notNull(),
|
||||
status: varchar().$type<cheerEventStatus>().default("success").notNull(),
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const cheerEventsRelations = relations(cheerEvents, ({ one }) => ({
|
||||
export const cheerEventsRelations = relations(cheerEvents, ({ one, many }) => ({
|
||||
user: one(users, {
|
||||
fields: [cheerEvents.user],
|
||||
references: [users.id]
|
||||
})
|
||||
references: [users.id],
|
||||
}),
|
||||
timeouts: many(timeouts),
|
||||
}));
|
||||
|
||||
export const cheers = pgTable('cheers', {
|
||||
export const cheers = pgTable("cheers", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
amount: integer().notNull(),
|
||||
created: timestamp().defaultNow().notNull()
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const cheersRelations = relations(cheers, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [cheers.user],
|
||||
references: [users.id]
|
||||
})
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const anivTimeouts = pgTable('anivTimeouts', {
|
||||
export const anivTimeouts = pgTable("anivTimeouts", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
message: varchar().notNull(),
|
||||
anivBot: varchar().$type<anivBots>().notNull(),
|
||||
duration: integer(),
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
timeout: boolean().default(true)
|
||||
timeout: boolean().default(true),
|
||||
});
|
||||
|
||||
export const anivTimeoutsRelations = relations(anivTimeouts, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [anivTimeouts.user],
|
||||
references: [users.id]
|
||||
})
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type lootTriggers = "getloot" | "superloot";
|
||||
|
||||
export const getLoots = pgTable('getLoots', {
|
||||
export const getLoots = pgTable("getLoots", {
|
||||
id: uuid().defaultRandom().primaryKey(),
|
||||
user: integer().notNull().references(() => users.id),
|
||||
user: integer()
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
qbucks: integer().notNull(),
|
||||
items: jsonb().$type<inventory>().notNull(),
|
||||
trigger: varchar().$type<lootTriggers>().notNull(),
|
||||
created: timestamp().defaultNow().notNull()
|
||||
created: timestamp().defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const getLootsRelations = relations(getLoots, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [getLoots.user],
|
||||
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],
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { api, eventSub } from "index";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerId } from "main";
|
||||
import { deleteBannedUserMessagesFromChatWidget } from "web/chatWidget/message";
|
||||
import { eventSub, api } from "index";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
eventSub.onChannelBan(streamerId, async msg => {
|
||||
eventSub.onChannelBan(streamerId, async (msg) => {
|
||||
deleteBannedUserMessagesFromChatWidget(msg);
|
||||
const welcomemessageid = await redis.get(`user:${msg.userId}:welcomemessageid`);
|
||||
if (welcomemessageid) { await api.moderation.deleteChatMessages(streamerId, welcomemessageid); await redis.del(`user:${msg.userId}:welcomemessageid`); };
|
||||
await redis.set(`user:${msg.userId}:timeout`, '1');
|
||||
if (msg.endDate) await redis.expire(`user:${msg.userId}:timeout`, Math.floor((msg.endDate.getTime() - Date.now()) / 1000));
|
||||
const welcomemessageid = await redis.get(
|
||||
`user:${msg.userId}:welcomemessageid`,
|
||||
);
|
||||
if (welcomemessageid) {
|
||||
await api.moderation.deleteChatMessages(streamerId, welcomemessageid);
|
||||
await redis.del(`user:${msg.userId}:welcomemessageid`);
|
||||
}
|
||||
await redis.set(`user:${msg.userId}:timeout`, "1");
|
||||
if (msg.endDate)
|
||||
await redis.expire(
|
||||
`user:${msg.userId}:timeout`,
|
||||
Math.floor((msg.endDate.getTime() - Date.now()) / 1000),
|
||||
);
|
||||
});
|
||||
|
||||
eventSub.onChannelUnban(streamerId, async msg => {
|
||||
eventSub.onChannelUnban(streamerId, async (msg) => {
|
||||
await redis.del(`user:${msg.userId}:timeout`);
|
||||
await redis.del(`user:${msg.userId}:remod`);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { activeRedeems } from "pointRedeems";
|
||||
import { eventSub } from "index";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import logger from "lib/logger";
|
||||
import { streamerId } from "main";
|
||||
import { activeRedeems } from "pointRedeems";
|
||||
import User from "user";
|
||||
|
||||
eventSub.onChannelRedemptionAdd(streamerId, async msg => {
|
||||
eventSub.onChannelRedemptionAdd(streamerId, async (msg) => {
|
||||
const selection = activeRedeems.get(msg.rewardId);
|
||||
if (!selection) { logger.warn(`Can't find the ${msg.rewardTitle} redeem`); return; };
|
||||
if (!selection) {
|
||||
logger.warn(`Can't find the ${msg.rewardTitle} redeem`);
|
||||
return;
|
||||
}
|
||||
const user = await User.initUsername(msg.userName);
|
||||
try {
|
||||
await selection.execute(msg, user!);
|
||||
if (process.env.NODE_ENV === 'production') await msg.updateStatus('FULFILLED'); // only on prod
|
||||
} catch (err) {
|
||||
await sendMessage(`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`);
|
||||
await sendMessage(
|
||||
`[ERROR]: Something went wrong with ${user?.displayName}'s redeem!`,
|
||||
);
|
||||
logger.err(err as string);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,6 @@ import { eventSub } from "index";
|
||||
import { chatterId, streamerId } from "main";
|
||||
import { deleteMessageFromChatWidget } from "web/chatWidget/message";
|
||||
|
||||
eventSub.onChannelChatMessageDelete(streamerId, chatterId, async msg => {
|
||||
eventSub.onChannelChatMessageDelete(streamerId, chatterId, async (msg) => {
|
||||
deleteMessageFromChatWidget(msg);
|
||||
});
|
||||
|
||||
@@ -1,35 +1,45 @@
|
||||
import { api, eventSub } from "index";
|
||||
import kleur from "kleur";
|
||||
import logger from "lib/logger";
|
||||
|
||||
eventSub.onRevoke(event => {
|
||||
logger.ok(`Successfully revoked EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
eventSub.onRevoke((event) => {
|
||||
logger.ok(
|
||||
`Successfully revoked EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||
);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionCreateSuccess(event => {
|
||||
logger.ok(`Successfully created EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
eventSub.onSubscriptionCreateSuccess((event) => {
|
||||
logger.ok(
|
||||
`Successfully created EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||
);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionCreateFailure(event => {
|
||||
logger.err(`Failed to create EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
eventSub.onSubscriptionCreateFailure((event) => {
|
||||
logger.err(
|
||||
`Failed to create EventSub subscription: \x1b[3;4;4;95m${event.id}`,
|
||||
);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionDeleteSuccess(event => {
|
||||
logger.ok(`Successfully deleted EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
eventSub.onSubscriptionDeleteSuccess((event) => {
|
||||
logger.ok(
|
||||
`Successfully deleted EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||
);
|
||||
});
|
||||
|
||||
eventSub.onSubscriptionDeleteFailure(event => {
|
||||
logger.err(`Failed to delete EventSub subscription: ${kleur.underline(event.id)}`);
|
||||
eventSub.onSubscriptionDeleteFailure((event) => {
|
||||
logger.err(
|
||||
`Failed to delete EventSub subscription: \x1b[3;4;1;95m${event.id}`,
|
||||
);
|
||||
});
|
||||
|
||||
await api.eventSub.deleteAllSubscriptions();
|
||||
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { readdir } from "node:fs/promises";
|
||||
|
||||
const files = await readdir(import.meta.dir);
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
if (!file.endsWith(".ts")) continue;
|
||||
if (file === import.meta.file) continue;
|
||||
await import(import.meta.dir + '/' + file.slice(0, -3));
|
||||
};
|
||||
await import(`${import.meta.dir}/${file.slice(0, -3)}`);
|
||||
}
|
||||
|
||||
eventSub.start();
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base"
|
||||
import { streamerId, commandPrefix, streamerUsers, chatterId } from "main";
|
||||
import User from "user";
|
||||
import commands, { specialAliasCommands } from "commands";
|
||||
import { Command, sendMessage } from "lib/commandUtils";
|
||||
import { isAdmin } from "lib/admins";
|
||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import cheers from "cheers";
|
||||
import logger from "lib/logger";
|
||||
import { addMessageToChatWidget } from "web/chatWidget/message";
|
||||
import { isInvuln, removeInvuln, setTemporaryInvuln } from "lib/invuln";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import commands, { specialAliasCommands } from "commands";
|
||||
import { createCheerRecord } from "db/dbCheers";
|
||||
import handleAnivMessage from "lib/handleAnivMessage";
|
||||
import { Item } from "items";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { eventSub } from "index";
|
||||
import { Item } from "items";
|
||||
import { isAdmin } from "lib/admins";
|
||||
import { type Command, sendMessage } from "lib/commandUtils";
|
||||
import handleAnivMessage from "lib/handleAnivMessage";
|
||||
import { isInvuln, removeInvuln, setTemporaryInvuln } from "lib/invuln";
|
||||
import logger from "lib/logger";
|
||||
import { redis } from "lib/redis";
|
||||
import { chatterId, commandPrefix, streamerId, streamerUsers } from "main";
|
||||
import User from "user";
|
||||
import { addMessageToChatWidget } from "web/chatWidget/message";
|
||||
|
||||
eventSub.onChannelChatMessage(streamerId, chatterId, parseChatMessage);
|
||||
|
||||
@@ -32,21 +32,43 @@ async function parseChatMessage(msg: EventSubChannelChatMessageEvent) {
|
||||
|
||||
if (await redis.exists(`user:${user?.id}:bot`)) return; // Ignore all bot commands
|
||||
|
||||
if (!await redis.exists(`user:${user?.id}:haschatted`) && !msg.sourceMessageId) { // The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
|
||||
const message = await sendMessage(`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://github.com/qwerinope/qweribot/#qweribot`);
|
||||
if (
|
||||
!(await redis.exists(`user:${user?.id}:haschatted`)) &&
|
||||
!msg.sourceMessageId
|
||||
) {
|
||||
// The msg.sourceMessageId checks if the message is from shared chat. shared chat should be ignored
|
||||
const message = await sendMessage(
|
||||
`Welcome ${user?.displayName}. Please note: This chat has PvP, if you get timed out that's part of the qwerinope experience. You have 10 minutes of invincibility. A full list of commands and items can be found here: https://gitlab.com/qwerinope/qweribot/#qweribot`,
|
||||
);
|
||||
await redis.set(`user:${user?.id}:haschatted`, "1");
|
||||
await redis.set(`user:${user?.id}:welcomemessageid`, message.id);
|
||||
await redis.expire(`user:${user?.id}:welcomemessageid`, 600);
|
||||
if (!await isInvuln(msg.chatterId)) await setTemporaryInvuln(user?.id!); // This would set the invuln expiration lmao
|
||||
};
|
||||
if (!(await isInvuln(msg.chatterId))) await setTemporaryInvuln(user?.id!); // This would set the invuln expiration lmao
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (!msg.isCheer && !msg.isRedemption) await handleChatMessage(msg, user!)
|
||||
else if (msg.isCheer && !msg.isRedemption) await handleCheer(msg, msg.bits, user!);
|
||||
};
|
||||
// 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);
|
||||
|
||||
async function handleChatMessage(msg: EventSubChannelChatMessageEvent, user: User) {
|
||||
await redis.set(`user:${user?.id}:haschattedthisstream`, "1");
|
||||
|
||||
if (!msg.isCheer && !msg.isRedemption) await handleChatMessage(msg, user!);
|
||||
else if (msg.isCheer && !msg.isRedemption)
|
||||
await handleCheer(msg, msg.bits, user!);
|
||||
}
|
||||
|
||||
async function handleChatMessage(
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
user: User,
|
||||
) {
|
||||
// Aniv message filter
|
||||
handleAnivMessage(msg, user);
|
||||
|
||||
@@ -54,62 +76,109 @@ async function handleChatMessage(msg: EventSubChannelChatMessageEvent, user: Use
|
||||
const selected = selectCommand(msg.messageText);
|
||||
if (!selected) return;
|
||||
const { cmd: selection, activation, isitem } = selected;
|
||||
if (await redis.sismember('disabledcommands', selection.name)) return;
|
||||
if (isitem && await isInvuln(msg.chatterId) && !streamerUsers.includes(msg.chatterId)) { await sendMessage(`You're no longer an invuln because you used an item.`, msg.messageId); await removeInvuln(msg.chatterId); };
|
||||
if (await redis.sismember("disabledcommands", selection.name)) return;
|
||||
if (
|
||||
isitem &&
|
||||
(await isInvuln(msg.chatterId)) &&
|
||||
!streamerUsers.includes(msg.chatterId)
|
||||
) {
|
||||
await sendMessage(
|
||||
`You're no longer an invuln because you used an item.`,
|
||||
msg.messageId,
|
||||
);
|
||||
await removeInvuln(msg.chatterId);
|
||||
}
|
||||
|
||||
switch (selection.usertype) {
|
||||
case "admin":
|
||||
if (!await isAdmin(user.id)) return;
|
||||
if (!(await isAdmin(user.id))) return;
|
||||
break;
|
||||
case "streamer":
|
||||
if (!streamerUsers.includes(msg.chatterId)) return;
|
||||
break;
|
||||
case "moderator":
|
||||
if (!(await redis.exists(`user:${user.id}:mod`) || await isAdmin(user.id))) return;
|
||||
if (
|
||||
!(
|
||||
(await redis.exists(`user:${user.id}:mod`)) ||
|
||||
(await isAdmin(user.id))
|
||||
)
|
||||
)
|
||||
return;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
await selection.execute(msg, user, { activation });
|
||||
} catch (err) {
|
||||
logger.err(err as string);
|
||||
await sendMessage('ERROR: Something went wrong', msg.messageId);
|
||||
await sendMessage("ERROR: Something went wrong", msg.messageId);
|
||||
await user.clearLock();
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type selectedCommand = {
|
||||
cmd: Command;
|
||||
activation: string;
|
||||
activation?: string;
|
||||
isitem: boolean;
|
||||
};
|
||||
|
||||
function selectCommand(message: string): selectedCommand | false {
|
||||
const specialcmdselector = message.trim().toLowerCase().split(' ')[0]!;
|
||||
const specialcmdselector = message.trim().toLowerCase().split(" ")[0]!;
|
||||
const specialcmd = specialAliasCommands.get(specialcmdselector);
|
||||
if (specialcmd) return { cmd: specialcmd, activation: specialcmdselector, isitem: specialcmd instanceof Item };
|
||||
if (specialcmd)
|
||||
return {
|
||||
cmd: specialcmd,
|
||||
activation: specialcmdselector,
|
||||
isitem: specialcmd instanceof Item,
|
||||
};
|
||||
if (!message.startsWith(commandPrefix)) return false;
|
||||
const commandSelector = message.slice(commandPrefix.length).trim().toLowerCase().split(' ')[0]!;
|
||||
const commandSelector = message
|
||||
.slice(commandPrefix.length)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(" ")[0]!;
|
||||
const normalcmd = commands.get(commandSelector);
|
||||
if (normalcmd) return { cmd: normalcmd, activation: commandPrefix + commandSelector, isitem: normalcmd instanceof Item };
|
||||
if (normalcmd)
|
||||
return {
|
||||
cmd: normalcmd,
|
||||
activation: undefined,
|
||||
isitem: normalcmd instanceof Item,
|
||||
};
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleCheer(msg: EventSubChannelChatMessageEvent, bits: number, user: User) {
|
||||
export async function handleCheer(
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
bits: number,
|
||||
user: User,
|
||||
) {
|
||||
if (msg.isCheer) {
|
||||
await getUserRecord(user); // ensure they exist in the database
|
||||
await createCheerRecord(user, bits);
|
||||
}; // If this is not triggered it's because of the testcheer command. these fake bits should not be added to the database
|
||||
} // If this is not triggered it's because of the testcheer command. these fake bits should not be added to the database
|
||||
|
||||
const selection = cheers.get(bits);
|
||||
if (!selection) return;
|
||||
|
||||
if (await redis.sismember('disabledcheers', selection.name)) { await sendMessage(`The ${selection.name} cheer is disabled! Sorry!`, msg.messageId); return; };
|
||||
if (selection.isItem && await isInvuln(user.id) && !streamerUsers.includes(user.id)) { await sendMessage(`${user.displayName} Is no longer an invuln`); await removeInvuln(user.id); };
|
||||
if (await redis.sismember("disabledcheers", selection.name)) {
|
||||
await sendMessage(
|
||||
`The ${selection.name} cheer is disabled! Sorry!`,
|
||||
msg.messageId,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
selection.isItem &&
|
||||
(await isInvuln(user.id)) &&
|
||||
!streamerUsers.includes(user.id)
|
||||
) {
|
||||
await sendMessage(`${user.displayName} Is no longer an invuln`);
|
||||
await removeInvuln(user.id);
|
||||
}
|
||||
try {
|
||||
await selection.execute(msg, user);
|
||||
} catch (err) {
|
||||
await sendMessage(`[ERROR]: Something went wrong with cheer execution`);
|
||||
logger.err(err as string);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { eventSub } from "index";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerId } from "main";
|
||||
|
||||
eventSub.onChannelModeratorAdd(streamerId, async mod => {
|
||||
await redis.set(`user:${mod.userId}:mod`, '1');
|
||||
eventSub.onChannelModeratorAdd(streamerId, async (mod) => {
|
||||
await redis.set(`user:${mod.userId}:mod`, "1");
|
||||
});
|
||||
|
||||
eventSub.onChannelModeratorRemove(streamerId, async mod => {
|
||||
eventSub.onChannelModeratorRemove(streamerId, async (mod) => {
|
||||
await redis.del(`user:${mod.userId}:mod`);
|
||||
});
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { eventSub, api } from "index";
|
||||
import { api, eventSub } from "index";
|
||||
import { changeItemCount } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import logger from "lib/logger";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerId } from "main";
|
||||
import User from "user";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
eventSub.onChannelRaidTo(streamerId, async msg => {
|
||||
if (await redis.exists(`user:${msg.raidingBroadcasterId}:recentraid`)) { await sendMessage(`Another raid from ${msg.raidedBroadcasterDisplayName}??? SMH`); return; };
|
||||
await redis.set(`user:${msg.raidingBroadcasterId}:recentraid`, '1');
|
||||
eventSub.onChannelRaidTo(streamerId, async (msg) => {
|
||||
if (await redis.exists(`user:${msg.raidingBroadcasterId}:recentraid`)) {
|
||||
await sendMessage(
|
||||
`Another raid from ${msg.raidedBroadcasterDisplayName}??? SMH`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await redis.set(`user:${msg.raidingBroadcasterId}:recentraid`, "1");
|
||||
await redis.expire(`user:${msg.raidingBroadcasterId}:recentraid`, 60 * 30); // raid cooldown is 30 minutes
|
||||
await sendMessage(`Ty for raiding ${msg.raidingBroadcasterDisplayName}. You get 3 pieces of TNT. Enjoy!`);
|
||||
await sendMessage(
|
||||
`Ty for raiding ${msg.raidingBroadcasterDisplayName}. You get 3 pieces of TNT. Enjoy!`,
|
||||
);
|
||||
try {
|
||||
await api.chat.shoutoutUser(streamerId, msg.raidingBroadcasterId);
|
||||
} catch (e) {
|
||||
logger.warn(`Failed to give automatic shoutout to ${msg.raidingBroadcasterDisplayName}`);
|
||||
};
|
||||
} catch (_e) {
|
||||
logger.warn(
|
||||
`Failed to give automatic shoutout to ${msg.raidingBroadcasterDisplayName}`,
|
||||
);
|
||||
}
|
||||
const raider = await User.initUsername(msg.raidingBroadcasterName);
|
||||
const result = await changeItemCount(raider!, await getUserRecord(raider!), 'tnt', 3);
|
||||
const result = await changeItemCount(
|
||||
raider!,
|
||||
await getUserRecord(raider!),
|
||||
"tnt",
|
||||
3,
|
||||
);
|
||||
if (!result) await sendMessage("oopsies, no tnt for you!");
|
||||
});
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { eventSub } from "index";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerId } from "main";
|
||||
import { sendDiscordMessage } from "web/discordConnection";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
eventSub.onStreamOnline(streamerId, async msg => {
|
||||
await redis.set('streamIsLive', '1');
|
||||
await sendMessage(`${msg.broadcasterDisplayName.toUpperCase()} IS LIVE! START DIGGING!`);
|
||||
await sendDiscordMessage({ message: 'live' });
|
||||
eventSub.onStreamOnline(streamerId, async (msg) => {
|
||||
await Promise.all([
|
||||
redis.set("streamIsLive", "1"),
|
||||
sendMessage(
|
||||
`${msg.broadcasterDisplayName.toUpperCase()} IS LIVE! START DIGGING!`,
|
||||
),
|
||||
sendDiscordMessage({ message: "live" }),
|
||||
redis
|
||||
.keys("user:*:haschattedthisstream")
|
||||
.then((a) => a.map(async (b) => await redis.del(b))),
|
||||
]);
|
||||
});
|
||||
|
||||
eventSub.onStreamOffline(streamerId, async msg => {
|
||||
await redis.del('streamIsLive');
|
||||
await sendMessage(`${msg.broadcasterDisplayName.toUpperCase()} IS OFFLINE! NO MORE FREE LOOT!`);
|
||||
eventSub.onStreamOffline(streamerId, async (msg) => {
|
||||
await Promise.all([
|
||||
redis.del("streamIsLive"),
|
||||
sendMessage(
|
||||
`${msg.broadcasterDisplayName.toUpperCase()} IS OFFLINE! NO MORE FREE LOOT!`,
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { getUserRecord, updateUserRecord } from "db/dbUser";
|
||||
import { eventSub } from "index";
|
||||
import { changeBalance } from "lib/changeBalance";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { streamerId } from "main";
|
||||
import User from "user";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
eventSub.onChannelSubscription(streamerId, async msg => {
|
||||
eventSub.onChannelSubscription(streamerId, async (msg) => {
|
||||
await redis.set(`user:${msg.userId}:subbed`, msg.tier.slice(0, 1));
|
||||
if (msg.isGift) return;
|
||||
const user = await User.initUsername(msg.userName);
|
||||
@@ -14,46 +14,60 @@ eventSub.onChannelSubscription(streamerId, async msg => {
|
||||
switch (msg.tier) {
|
||||
case "1000":
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE SUB ${msg.userDisplayName}! YOU GET 500 QBUCKS`),
|
||||
changeBalance(user!, userRecord, 500)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE SUB ${msg.userDisplayName}! YOU GET 500 QBUCKS`,
|
||||
),
|
||||
changeBalance(user!, userRecord, 500),
|
||||
]);
|
||||
break;
|
||||
case "2000":
|
||||
userRecord.balance += 1500;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += 1
|
||||
else userRecord.inventory.silverbullet = 1
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += 1;
|
||||
else userRecord.inventory.silverbullet = 1;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE TIER 2 SUB ${msg.userDisplayName}! YOU GET 1500 QBUCKS AND A SILVER BULLET`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE TIER 2 SUB ${msg.userDisplayName}! YOU GET 1500 QBUCKS AND A SILVER BULLET`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
case "3000":
|
||||
userRecord.balance += 3000;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += 2
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += 2;
|
||||
else userRecord.inventory.silverbullet = 2;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE TIER 3 SUB ${msg.userDisplayName}! YOU GET 3000 QBUCKS AND 2 SILVER BULLETS`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE TIER 3 SUB ${msg.userDisplayName}! YOU GET 3000 QBUCKS AND 2 SILVER BULLETS`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
eventSub.onChannelSubscriptionGift(streamerId, async msg => {
|
||||
if (msg.isAnonymous) {
|
||||
eventSub.onChannelSubscriptionGift(streamerId, async (msg) => {
|
||||
if (msg.gifterName === null) {
|
||||
switch (msg.tier) {
|
||||
case "1000":
|
||||
await sendMessage(`YO THANKS ANON FOR THE SCAM SUB${msg.amount === 1 ? '' : 'S'}`);
|
||||
await sendMessage(
|
||||
`YO THANKS ANON FOR THE SCAM SUB${msg.amount === 1 ? "" : "S"}`,
|
||||
);
|
||||
break;
|
||||
case "2000":
|
||||
await sendMessage(`YO THANKS ANON FOR THE ${msg.amount} TIER 2 SCAM SUB${msg.amount === 1 ? '' : 'S'}`);
|
||||
await sendMessage(
|
||||
`YO THANKS ANON FOR THE ${msg.amount} TIER 2 SCAM SUB${msg.amount === 1 ? "" : "S"}`,
|
||||
);
|
||||
break;
|
||||
case "3000":
|
||||
await sendMessage(`YO THANKS ANON FOR THE ${msg.amount} TIER 3 SCAM SUB${msg.amount === 1 ? '' : 'S'}`);
|
||||
await sendMessage(
|
||||
`YO THANKS ANON FOR THE ${msg.amount} TIER 3 SCAM SUB${msg.amount === 1 ? "" : "S"}`,
|
||||
);
|
||||
break;
|
||||
};
|
||||
}
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const user = await User.initUsername(msg.gifterName);
|
||||
const amount = msg.amount;
|
||||
@@ -61,63 +75,79 @@ eventSub.onChannelSubscriptionGift(streamerId, async msg => {
|
||||
switch (msg.tier) {
|
||||
case "1000":
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE SCAM GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 500} QBUCKS`),
|
||||
changeBalance(user!, userRecord, amount * 500)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE SCAM GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 500} QBUCKS`,
|
||||
),
|
||||
changeBalance(user!, userRecord, amount * 500),
|
||||
]);
|
||||
break;
|
||||
case "2000":
|
||||
userRecord.balance += 1500 * amount;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += amount
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += amount;
|
||||
else userRecord.inventory.silverbullet = amount;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE SCAM TIER 2 GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 1500} QBUCKS AND ${amount} SILVER BULLET${amount === 1 ? '' : 'S'}`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE SCAM TIER 2 GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 1500} QBUCKS AND ${amount} SILVER BULLET${amount === 1 ? "" : "S"}`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
case "3000":
|
||||
userRecord.balance += 3000 * amount;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += amount * 2
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += amount * 2;
|
||||
else userRecord.inventory.silverbullet = amount * 2;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE SCAM TIER 3 GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 3000} QBUCKS AND ${amount * 2} SILVER BULLETS`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE SCAM TIER 3 GIFTS ${msg.gifterDisplayName}! YOU GET ${amount * 3000} QBUCKS AND ${amount * 2} SILVER BULLETS`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
eventSub.onChannelSubscriptionEnd(streamerId, async msg => {
|
||||
eventSub.onChannelSubscriptionEnd(streamerId, async (msg) => {
|
||||
await redis.del(`user:${msg.userId}:subbed`);
|
||||
});
|
||||
|
||||
eventSub.onChannelSubscriptionMessage(streamerId, async msg => {
|
||||
eventSub.onChannelSubscriptionMessage(streamerId, async (msg) => {
|
||||
await redis.set(`user:${msg.userId}:subbed`, msg.tier.slice(0, 1));
|
||||
const user = await User.initUsername(msg.userName);
|
||||
const userRecord = await getUserRecord(user!);
|
||||
switch (msg.tier) {
|
||||
case "1000":
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE RESUB ${msg.userDisplayName}! YOU GET 500 QBUCKS`),
|
||||
changeBalance(user!, userRecord, 500)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE RESUB ${msg.userDisplayName}! YOU GET 500 QBUCKS`,
|
||||
),
|
||||
changeBalance(user!, userRecord, 500),
|
||||
]);
|
||||
break;
|
||||
case "2000":
|
||||
userRecord.balance += 1500;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += 1
|
||||
else userRecord.inventory.silverbullet = 1
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += 1;
|
||||
else userRecord.inventory.silverbullet = 1;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE TIER 2 RESUB ${msg.userDisplayName}! YOU GET 1500 QBUCKS AND A SILVER BULLET`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE TIER 2 RESUB ${msg.userDisplayName}! YOU GET 1500 QBUCKS AND A SILVER BULLET`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
case "3000":
|
||||
userRecord.balance += 3000;
|
||||
if (userRecord.inventory.silverbullet) userRecord.inventory.silverbullet += 2
|
||||
if (userRecord.inventory.silverbullet)
|
||||
userRecord.inventory.silverbullet += 2;
|
||||
else userRecord.inventory.silverbullet = 2;
|
||||
await Promise.all([
|
||||
sendMessage(`YO THANKS FOR THE TIER 3 RESUB ${msg.userDisplayName}! YOU GET 3000 QBUCKS AND 2 SILVER BULLETS`),
|
||||
updateUserRecord(user!, userRecord)
|
||||
sendMessage(
|
||||
`YO THANKS FOR THE TIER 3 RESUB ${msg.userDisplayName}! YOU GET 3000 QBUCKS AND 2 SILVER BULLETS`,
|
||||
),
|
||||
updateUserRecord(user!, userRecord),
|
||||
]);
|
||||
break;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,36 +1,80 @@
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { api, eventSub } from "index";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { buildTimeString } from "lib/dateManager";
|
||||
import { chatterId, commandPrefix } from "main";
|
||||
import { redis } from "lib/redis";
|
||||
import { chatterId, commandPrefix } from "main";
|
||||
|
||||
const WHISPERCOOLDOWN = 60 * 5; // 5 minutes
|
||||
|
||||
eventSub.onUserWhisperMessage(chatterId, async msg => {
|
||||
if (!msg.messageText.startsWith(commandPrefix)) { await whisper(msg.senderUserId, `Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`); return; };
|
||||
const cmd = msg.messageText.slice(commandPrefix.length).trim().toLowerCase().split(' ')[0]!;
|
||||
eventSub.onUserWhisperMessage(chatterId, async (msg) => {
|
||||
if (!msg.messageText.startsWith(commandPrefix)) {
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
`Whisper commands start with '${commandPrefix}'. All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const cmd = msg.messageText
|
||||
.slice(commandPrefix.length)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(" ")[0]!;
|
||||
|
||||
switch (cmd) {
|
||||
case 'help':
|
||||
case 'h':
|
||||
await whisper(msg.senderUserId, `All whisper commands can be found here: https://github.com/qwerinope/qweribot#whisper-commands-1`);
|
||||
case "help":
|
||||
case "h":
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
`All whisper commands can be found here: https://gitlab.com/qwerinope/qweribot#whisper-commands-1`,
|
||||
);
|
||||
break;
|
||||
case 'ghostwhisper':
|
||||
case 'ghost':
|
||||
case 'g':
|
||||
if (await redis.ttl(`user:${msg.senderUserId}:timeout`) < 0) { await whisper(msg.senderUserId, 'Cannot send ghost whisper while not timed out'); return; };
|
||||
const cooldown = await redis.expiretime(`user:${msg.senderUserId}:whispercooldown`);
|
||||
case "ghostwhisper":
|
||||
case "ghost":
|
||||
case "g": {
|
||||
if ((await redis.ttl(`user:${msg.senderUserId}:timeout`)) < 0) {
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
"Cannot send ghost whisper while not timed out",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const cooldown = await redis.expiretime(
|
||||
`user:${msg.senderUserId}:whispercooldown`,
|
||||
);
|
||||
if (cooldown < 0) {
|
||||
if (msg.messageText.length > 200) { await whisper(msg.senderUserId, `Message too long. Please send a shorter one.`); return; };
|
||||
await redis.set(`user:${msg.senderUserId}:whispercooldown`, '1');
|
||||
await redis.expire(`user:${msg.senderUserId}:whispercooldown`, WHISPERCOOLDOWN);
|
||||
await sendMessage(`The ghost of ${msg.senderUserDisplayName} whispered: ${msg.messageText.split(' ').slice(1).join(' ').replaceAll(/cheer[0-9]+/gi, '')}`);
|
||||
await whisper(msg.senderUserId, `Message sent. You can send another ghost whisper in ${Math.floor(WHISPERCOOLDOWN / 60)} minutes.`);
|
||||
if (msg.messageText.length > 200) {
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
`Message too long. Please send a shorter one.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await redis.set(`user:${msg.senderUserId}:whispercooldown`, "1");
|
||||
await redis.expire(
|
||||
`user:${msg.senderUserId}:whispercooldown`,
|
||||
WHISPERCOOLDOWN,
|
||||
);
|
||||
await sendMessage(
|
||||
`The ghost of ${msg.senderUserDisplayName} whispered: ${msg.messageText
|
||||
.split(" ")
|
||||
.slice(1)
|
||||
.join(" ")
|
||||
.replaceAll(/cheer[0-9]+/gi, "")}`,
|
||||
);
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
`Message sent. You can send another ghost whisper in ${Math.floor(WHISPERCOOLDOWN / 60)} minutes.`,
|
||||
);
|
||||
} else {
|
||||
await whisper(msg.senderUserId, `Wait another ${buildTimeString(cooldown * 1000, Date.now())} before sending another ghost whisper.`);
|
||||
};
|
||||
await whisper(
|
||||
msg.senderUserId,
|
||||
`Wait another ${buildTimeString(cooldown * 1000, Date.now())} before sending another ghost whisper.`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const whisper = async (target: string, message: string) => await api.whispers.sendWhisper(chatterId, target, message);
|
||||
const whisper = async (target: string, message: string) =>
|
||||
await api.whispers.sendWhisper(chatterId, target, message);
|
||||
|
||||
220
src/index.ts
220
src/index.ts
@@ -1,127 +1,217 @@
|
||||
import { chatterId, streamerId, singleUserMode, streamerUsers } from "main";
|
||||
import { ApiClient } from "@twurple/api";
|
||||
import { connectionCheck } from "connectionCheck";
|
||||
import { ApiClient } from "@twurple/api";
|
||||
import {
|
||||
type ConnectionAdapter,
|
||||
EventSubHttpListener,
|
||||
ReverseProxyAdapter,
|
||||
} from "@twurple/eventsub-http";
|
||||
import { NgrokAdapter } from "@twurple/eventsub-ngrok";
|
||||
import { type authProviderInstructions, createAuthProvider } from "auth";
|
||||
import { database, host, password, user } from "db/connection";
|
||||
import logger from "lib/logger";
|
||||
import { redis } from "lib/redis";
|
||||
import { createAuthProvider, type authProviderInstructions } from "auth";
|
||||
import { user, password, database, host } from "db/connection";
|
||||
import { ConnectionAdapter, EventSubHttpListener, ReverseProxyAdapter } from "@twurple/eventsub-http";
|
||||
import { NgrokAdapter } from "@twurple/eventsub-ngrok";
|
||||
import { chatterId, singleUserMode, streamerId, streamerUsers } from "main";
|
||||
|
||||
if (chatterId === "") { logger.enverr('CHATTER_ID'); process.exit(1); };
|
||||
if (streamerId === "") { logger.enverr('STREAMER_ID'); process.exit(1); };
|
||||
if (!user) { logger.enverr("POSTGRES_USER"); process.exit(1); };
|
||||
if (!password) { logger.enverr("POSTGRES_USER"); process.exit(1); };
|
||||
if (!database) { logger.enverr("POSTGRES_DB"); process.exit(1); };
|
||||
if (!host) { logger.enverr("POSTGRES_HOST"); process.exit(1); };
|
||||
|
||||
const eventSubHostName = process.env.EVENTSUB_HOSTNAME ?? (() => {
|
||||
logger.enverr('EVENTSUB_HOSTNAME');
|
||||
if (chatterId === "") {
|
||||
logger.enverr("CHATTER_ID");
|
||||
process.exit(1);
|
||||
})();
|
||||
|
||||
const eventSubPort = process.env.EVENTSUB_PORT ?? (() => {
|
||||
logger.enverr('EVENTSUB_PORT');
|
||||
}
|
||||
if (streamerId === "") {
|
||||
logger.enverr("STREAMER_ID");
|
||||
process.exit(1);
|
||||
})();
|
||||
|
||||
const eventSubSecret = process.env.EVENTSUB_SECRET ?? (() => {
|
||||
logger.enverr('EVENTSUB_SECRET');
|
||||
}
|
||||
if (!user) {
|
||||
logger.enverr("POSTGRES_USER");
|
||||
process.exit(1);
|
||||
})();
|
||||
}
|
||||
if (!password) {
|
||||
logger.enverr("POSTGRES_USER");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!database) {
|
||||
logger.enverr("POSTGRES_DB");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!host) {
|
||||
logger.enverr("POSTGRES_HOST");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const eventSubHostName =
|
||||
process.env.EVENTSUB_HOSTNAME ??
|
||||
(() => {
|
||||
logger.enverr("EVENTSUB_HOSTNAME");
|
||||
process.exit(1);
|
||||
})();
|
||||
|
||||
const eventSubPort =
|
||||
process.env.EVENTSUB_PORT ??
|
||||
(() => {
|
||||
logger.enverr("EVENTSUB_PORT");
|
||||
process.exit(1);
|
||||
})();
|
||||
|
||||
const eventSubSecret =
|
||||
process.env.EVENTSUB_SECRET ??
|
||||
(() => {
|
||||
logger.enverr("EVENTSUB_SECRET");
|
||||
process.exit(1);
|
||||
})();
|
||||
|
||||
const eventSubPath = process.env.EVENTSUB_PATH;
|
||||
|
||||
await connectionCheck();
|
||||
|
||||
const CHATTERINTENTS = ["user:read:chat", "user:write:chat", "user:bot", "user:manage:whispers"];
|
||||
const STREAMERINTENTS = ["channel:bot", "user:read:chat", "moderation:read", "moderator:read:chatters", "channel:manage:moderators", "moderator:manage:chat_messages", "moderator:manage:banned_users", "bits:read", "channel:moderate", "moderator:manage:shoutouts", "channel:read:subscriptions", "channel:manage:redemptions"];
|
||||
const CHATTERINTENTS = [
|
||||
"user:read:chat",
|
||||
"user:write:chat",
|
||||
"user:bot",
|
||||
"user:manage:whispers",
|
||||
];
|
||||
const STREAMERINTENTS = [
|
||||
"channel:bot",
|
||||
"user:read:chat",
|
||||
"moderation:read",
|
||||
"moderator:read:chatters",
|
||||
"channel:manage:moderators",
|
||||
"moderator:manage:chat_messages",
|
||||
"moderator:manage:banned_users",
|
||||
"bits:read",
|
||||
"channel:moderate",
|
||||
"moderator:manage:shoutouts",
|
||||
"channel:read:subscriptions",
|
||||
"channel:manage:redemptions",
|
||||
];
|
||||
|
||||
const users: authProviderInstructions[] = [
|
||||
{
|
||||
userId: streamerId,
|
||||
intents: singleUserMode ? CHATTERINTENTS.concat(STREAMERINTENTS) : STREAMERINTENTS,
|
||||
streamer: true
|
||||
}
|
||||
intents: singleUserMode
|
||||
? CHATTERINTENTS.concat(STREAMERINTENTS)
|
||||
: STREAMERINTENTS,
|
||||
streamer: true,
|
||||
},
|
||||
];
|
||||
|
||||
if (!singleUserMode) users.push({
|
||||
if (!singleUserMode)
|
||||
users.push({
|
||||
userId: chatterId,
|
||||
intents: CHATTERINTENTS,
|
||||
streamer: false
|
||||
});
|
||||
streamer: false,
|
||||
});
|
||||
|
||||
const adapter: ConnectionAdapter = process.env.NODE_ENV === 'development' ? new NgrokAdapter({
|
||||
const adapter: ConnectionAdapter =
|
||||
process.env.NODE_ENV === "development"
|
||||
? new NgrokAdapter({
|
||||
ngrokConfig: {
|
||||
authtoken: process.env.EVENTSUB_NGROK_TOKEN ?? (() => {
|
||||
logger.enverr('EVENTSUB_NGROK_TOKEN');
|
||||
authtoken:
|
||||
process.env.EVENTSUB_NGROK_TOKEN ??
|
||||
(() => {
|
||||
logger.enverr("EVENTSUB_NGROK_TOKEN");
|
||||
process.exit(1);
|
||||
})()
|
||||
}
|
||||
}) : new ReverseProxyAdapter({
|
||||
pathPrefix: eventSubPath, hostName: eventSubHostName, port: parseInt(eventSubPort)
|
||||
});
|
||||
})(),
|
||||
},
|
||||
})
|
||||
: new ReverseProxyAdapter({
|
||||
pathPrefix: eventSubPath,
|
||||
hostName: eventSubHostName,
|
||||
port: parseInt(eventSubPort, 10),
|
||||
});
|
||||
|
||||
const authProvider = await createAuthProvider(users);
|
||||
|
||||
export const api = new ApiClient({ authProvider });
|
||||
|
||||
export const eventSub = new EventSubHttpListener({ apiClient: api, secret: eventSubSecret, adapter });
|
||||
export const eventSub = new EventSubHttpListener({
|
||||
apiClient: api,
|
||||
secret: eventSubSecret,
|
||||
adapter,
|
||||
});
|
||||
|
||||
if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, '1');
|
||||
if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, "1");
|
||||
|
||||
import { addAdmin } from "lib/admins";
|
||||
import { addInvuln } from "lib/invuln";
|
||||
import User from "user";
|
||||
import { remodMod, timeoutDuration } from "lib/timeout";
|
||||
import User from "user";
|
||||
|
||||
streamerUsers.forEach(async id => await Promise.all([addAdmin(id), addInvuln(id), redis.set(`user:${id}:mod`, '1')]));
|
||||
streamerUsers.forEach(
|
||||
async (id) =>
|
||||
await Promise.all([
|
||||
addAdmin(id),
|
||||
addInvuln(id),
|
||||
redis.set(`user:${id}:mod`, "1"),
|
||||
]),
|
||||
);
|
||||
|
||||
const banned = await api.moderation.getBannedUsers(streamerId).then(a => a.data);
|
||||
// Deleting all timeouts to prevent ghosts while bot was off
|
||||
await redis
|
||||
.keys("user:*:timeout")
|
||||
.then(async (a) => a.map(async (b) => await redis.del(b)));
|
||||
|
||||
const banned = await api.moderation
|
||||
.getBannedUsers(streamerId)
|
||||
.then((a) => a.data);
|
||||
for (const ban of banned) {
|
||||
await redis.set(`user:${ban.userId}:timeout`, '1');
|
||||
await redis.set(`user:${ban.userId}:timeout`, "1");
|
||||
const banlength = ban.expiryDate;
|
||||
if (banlength) {
|
||||
redis.expire(`user:${ban.userId}:timeout`, Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1);
|
||||
logger.info(`Set the timeout of ${ban.userDisplayName} in the Redis/Valkey database.`);
|
||||
};
|
||||
};
|
||||
redis.expire(
|
||||
`user:${ban.userId}:timeout`,
|
||||
Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1,
|
||||
);
|
||||
logger.info(
|
||||
`Set the timeout of \x1b[3;4;1;95m${ban.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mods = await api.moderation.getModerators(streamerId).then(a => a.data);
|
||||
const mods = await api.moderation.getModerators(streamerId).then((a) => a.data);
|
||||
for (const mod of mods) {
|
||||
await redis.set(`user:${mod.userId}:mod`, '1');
|
||||
logger.info(`Set the mod status of ${mod.userDisplayName} in the Redis/Valkey database.`);
|
||||
};
|
||||
await redis.set(`user:${mod.userId}:mod`, "1");
|
||||
logger.info(
|
||||
`Set the mod status of \x1b[3;4;1;95m${mod.userDisplayName}\x1b[0;97m in the Redis/Valkey database.`,
|
||||
);
|
||||
}
|
||||
|
||||
const bannedmods = await redis.keys('user:*:remod').then(a => Array.from(a).map(b => b.slice(5, -6)));
|
||||
const bannedmods = await redis
|
||||
.keys("user:*:remod")
|
||||
.then((a) => Array.from(a).map((b) => b.slice(5, -6)));
|
||||
for (const remod of bannedmods) {
|
||||
const target = await User.initUserId(remod);
|
||||
const durationdata = await timeoutDuration(target!);
|
||||
let duration = 0;
|
||||
if (durationdata) duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
|
||||
if (durationdata)
|
||||
duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
|
||||
remodMod(target!, duration);
|
||||
logger.info(`Set the remod timer for ${target?.displayName} to ${duration} seconds.`);
|
||||
};
|
||||
logger.info(
|
||||
`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.`,
|
||||
);
|
||||
}
|
||||
|
||||
const subs = await api.subscriptions.getSubscriptions(streamerId).then(a => a.data);
|
||||
const redisSubs = await redis.keys('user:*:subbed').then(a => a.map(b => b.slice(5, -7)));
|
||||
const subs = await api.subscriptions
|
||||
.getSubscriptions(streamerId)
|
||||
.then((a) => a.data);
|
||||
const redisSubs = await redis
|
||||
.keys("user:*:subbed")
|
||||
.then((a) => a.map((b) => b.slice(5, -7)));
|
||||
for (const sub of subs) {
|
||||
if (redisSubs.includes(sub.userId)) {
|
||||
const index = redisSubs.indexOf(sub.userId);
|
||||
redisSubs.splice(index, 1);
|
||||
continue;
|
||||
};
|
||||
}
|
||||
await redis.set(`user:${sub.userId}:subbed`, sub.tier.slice(0, 1));
|
||||
};
|
||||
}
|
||||
|
||||
redisSubs.map(async a => await redis.del(`user:${a}:subbed`));
|
||||
redisSubs.map(async (a) => await redis.del(`user:${a}:subbed`));
|
||||
|
||||
const streamdata = await api.streams.getStreamByUserId(streamerId);
|
||||
if (streamdata) await redis.set('streamIsLive', '1');
|
||||
if (streamdata) await redis.set("streamIsLive", "1");
|
||||
else await redis.del("streamIsLive");
|
||||
|
||||
await import("./events");
|
||||
|
||||
await import("./pointRedeems");
|
||||
|
||||
await import("./web");
|
||||
|
||||
|
||||
@@ -1,63 +1,87 @@
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'blaster';
|
||||
const ITEMNAME = "blaster";
|
||||
|
||||
export default new Item({
|
||||
name: ITEMNAME,
|
||||
prettyName: 'Blaster',
|
||||
plural: 's',
|
||||
description: 'Times a specific person out for 60 seconds',
|
||||
aliases: ['blaster', 'blast'],
|
||||
prettyName: "Blaster",
|
||||
plural: "s",
|
||||
description: "Times a specific person out for 60 seconds",
|
||||
aliases: ["blaster", "blast"],
|
||||
price: 100,
|
||||
execution: async (msg, user, specialargs) => {
|
||||
const messagequery = parseCommandArgs(msg.messageText, specialargs?.activation);
|
||||
if (!messagequery[0]) { await sendMessage('Please specify a target'); return; };
|
||||
const messagequery = parseCommandArgs(
|
||||
msg.messageText,
|
||||
specialargs?.activation,
|
||||
);
|
||||
if (!messagequery[0]) {
|
||||
await sendMessage("Please specify a target");
|
||||
return;
|
||||
}
|
||||
const target = await User.initUsername(messagequery[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`${messagequery[0]} doesn't exist`); return; };
|
||||
if (!target) {
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
await getUserRecord(target); // make sure the user record exist in the database
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage("Cannot use an item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
|
||||
const userObj = await getUserRecord(user);
|
||||
if (userObj.inventory[ITEMNAME]! < 1) { await sendMessage(`You don't have any blasters!`, msg.messageId); await user.clearLock(); return; };
|
||||
if (userObj.inventory[ITEMNAME]! < 1) {
|
||||
await sendMessage(`You don't have any blasters!`, msg.messageId);
|
||||
await user.clearLock();
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await timeout(target, `You got blasted by ${user.displayName}!`, 60);
|
||||
if (result.status) await Promise.all([
|
||||
sendMessage(`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`),
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got blasted by ${user.displayName}!`,
|
||||
60,
|
||||
);
|
||||
if (result.status)
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`GOTTEM ${target.displayName} got BLASTED by ${user.displayName} GOTTEM`,
|
||||
),
|
||||
changeItemCount(user, userObj, ITEMNAME),
|
||||
createTimeoutRecord(user, target, ITEMNAME),
|
||||
createUsedItemRecord(user, ITEMNAME),
|
||||
createTimeoutEventItem(user, target, ITEMNAME),
|
||||
playAlert({
|
||||
name: 'userBlast',
|
||||
name: "userBlast",
|
||||
user: user.displayName,
|
||||
target: target.displayName
|
||||
})
|
||||
target: target.displayName,
|
||||
}),
|
||||
]);
|
||||
else {
|
||||
switch (result.reason) {
|
||||
case "banned":
|
||||
await sendMessage(`${target.displayName} is already timed out/banned`, msg.messageId);
|
||||
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)
|
||||
timeout(user, "nah", 60),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage('Something went wrong...', msg.messageId);
|
||||
await sendMessage("Something went wrong...", msg.messageId);
|
||||
break;
|
||||
};
|
||||
};
|
||||
await user.clearLock();
|
||||
}
|
||||
}
|
||||
await user.clearLock();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,48 +1,58 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import User from "user";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
||||
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'grenade';
|
||||
const ITEMNAME = "grenade";
|
||||
|
||||
export default new Item({
|
||||
name: ITEMNAME,
|
||||
prettyName: 'Grenade',
|
||||
plural: 's',
|
||||
description: 'Give a random chatter a 60s timeout',
|
||||
aliases: ['grenade'],
|
||||
prettyName: "Grenade",
|
||||
plural: "s",
|
||||
description: "Give a random chatter a 60s timeout",
|
||||
aliases: ["grenade"],
|
||||
price: 99,
|
||||
execution: async (msg, user) => {
|
||||
const targets = await redis.keys(`user:*:vulnerable`);
|
||||
if (targets.length === 0) { await sendMessage('No vulnerable chatters to blow up', msg.messageId); return; };
|
||||
if (targets.length === 0) {
|
||||
await sendMessage("No vulnerable chatters to blow up", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const selection = targets[Math.floor(Math.random() * targets.length)]!;
|
||||
const target = await User.initUserId(selection.slice(5, -11));
|
||||
|
||||
await getUserRecord(target!); // make sure the user record exist in the database
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage("Cannot use an item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
|
||||
const userObj = await getUserRecord(user);
|
||||
if (userObj.inventory[ITEMNAME]! < 1) { await sendMessage(`You don't have any grenades!`, msg.messageId); await user.clearLock(); return; };
|
||||
if (userObj.inventory[ITEMNAME]! < 1) {
|
||||
await sendMessage(`You don't have any grenades!`, msg.messageId);
|
||||
await user.clearLock();
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
timeout(target!, `You got hit by ${user.displayName}'s grenade!`, 60),
|
||||
sendMessage(`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`),
|
||||
sendMessage(
|
||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s grenade wybuh`,
|
||||
),
|
||||
changeItemCount(user, userObj, ITEMNAME),
|
||||
createTimeoutRecord(user, target!, ITEMNAME),
|
||||
createUsedItemRecord(user, ITEMNAME),
|
||||
createTimeoutEventItem(user, target!, ITEMNAME),
|
||||
playAlert({
|
||||
name: 'grenadeExplosion',
|
||||
name: "grenadeExplosion",
|
||||
user: user.displayName,
|
||||
target: target?.displayName!
|
||||
})
|
||||
target: target?.displayName!,
|
||||
}),
|
||||
]);
|
||||
await user.clearLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import User from "user";
|
||||
import { type userType, type specialExecuteArgs } from "lib/commandUtils";
|
||||
import { Command, type commandOptions } from "lib/commandUtils";
|
||||
import type User from "user";
|
||||
|
||||
type itemOptions = {
|
||||
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
||||
|
||||
interface itemOptions extends Omit<commandOptions, "usertype"> {
|
||||
name: items;
|
||||
aliases: string[];
|
||||
prettyName: string;
|
||||
plural: string;
|
||||
description: string;
|
||||
execution: (message: EventSubChannelChatMessageEvent, sender: User, args?: specialExecuteArgs) => Promise<void>;
|
||||
specialaliases?: string[];
|
||||
price: number;
|
||||
};
|
||||
}
|
||||
|
||||
export class Item {
|
||||
public readonly name: items;
|
||||
export class Item extends Command {
|
||||
public readonly name: items = "blaster";
|
||||
public readonly prettyName: string;
|
||||
public readonly plural: string;
|
||||
public readonly description: string;
|
||||
public readonly aliases: string[];
|
||||
public readonly specialaliases: string[];
|
||||
public readonly usertype: userType;
|
||||
public readonly price: number;
|
||||
public readonly execute: (message: EventSubChannelChatMessageEvent, sender: User, args?: specialExecuteArgs) => Promise<void>;
|
||||
public readonly disableable: boolean;
|
||||
|
||||
/** Creates an item object */
|
||||
constructor(options: itemOptions) {
|
||||
super({
|
||||
...options,
|
||||
usertype: "chatter", // Everyone can use items
|
||||
disableable: true, // Items can always be disabled
|
||||
});
|
||||
this.name = options.name;
|
||||
this.prettyName = options.prettyName;
|
||||
this.plural = options.plural;
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { updateUserRecord, type UserRecord } from "db/dbUser";
|
||||
const itemAliasMap = new Map<string, Item>;
|
||||
const itemObjectArray: Item[] = []
|
||||
const specialAliasItems = new Map<string, Item>;
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { type UserRecord, updateUserRecord } from "db/dbUser";
|
||||
|
||||
const itemAliasMap = new Map<string, Item>();
|
||||
const itemObjectArray: Item[] = [];
|
||||
const specialAliasItems = new Map<string, Item>();
|
||||
const emptyInventory: inventory = {};
|
||||
const itemarray: items[] = [];
|
||||
|
||||
const files = await readdir(import.meta.dir);
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
if (!file.endsWith(".ts")) continue;
|
||||
if (file === import.meta.file) continue;
|
||||
const item: Item = await import(import.meta.dir + '/' + file.slice(0, -3)).then(a => a.default);
|
||||
const item: Item = await import(
|
||||
`${import.meta.dir}/${file.slice(0, -3)}`
|
||||
).then((a) => a.default);
|
||||
emptyInventory[item.name] = 0;
|
||||
itemarray.push(item.name);
|
||||
itemObjectArray.push(item);
|
||||
for (const alias of item.aliases) {
|
||||
itemAliasMap.set(alias, item); // Since it's not a primitive type the map is filled with references to the item, not the actual object
|
||||
};
|
||||
}
|
||||
for (const alias of item.specialaliases) {
|
||||
specialAliasItems.set(alias, item);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default itemAliasMap;
|
||||
export { emptyInventory, itemarray, specialAliasItems, itemObjectArray };
|
||||
|
||||
export type items = "blaster" | "silverbullet" | "grenade" | "tnt";
|
||||
export type inventory = {
|
||||
[key in items]?: number;
|
||||
};
|
||||
|
||||
export async function changeItemCount(user: User, userRecord: UserRecord, itemname: items, amount = -1): Promise<false | UserRecord> {
|
||||
export async function changeItemCount(
|
||||
user: User,
|
||||
userRecord: UserRecord,
|
||||
itemname: items,
|
||||
amount = -1,
|
||||
): Promise<false | UserRecord> {
|
||||
userRecord.inventory[itemname] = userRecord.inventory[itemname]! += amount;
|
||||
if (userRecord.inventory[itemname] < 0) return false;
|
||||
await updateUserRecord(user, userRecord);
|
||||
return userRecord;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,88 +1,118 @@
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
import User from "user";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { streamerId } from "main";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'silverbullet';
|
||||
const ITEMNAME = "silverbullet";
|
||||
|
||||
export default new Item({
|
||||
name: ITEMNAME,
|
||||
prettyName: 'Silver bullet',
|
||||
plural: 's',
|
||||
description: 'Times targeted or random vulnerable user out for 30 minutes',
|
||||
aliases: ['execute', 'silverbullet'],
|
||||
specialaliases: ['blastin'],
|
||||
prettyName: "Silver bullet",
|
||||
plural: "s",
|
||||
description: "Times targeted or random vulnerable user out for 30 minutes",
|
||||
aliases: ["execute", "silverbullet"],
|
||||
specialaliases: ["blastin", "fuck"],
|
||||
price: 666,
|
||||
execution: async (msg, user, specialargs) => {
|
||||
const messagequery = parseCommandArgs(msg.messageText, specialargs?.activation);
|
||||
const messagequery = parseCommandArgs(
|
||||
msg.messageText,
|
||||
specialargs?.activation,
|
||||
);
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage("Cannot use an item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
|
||||
const userObj = await getUserRecord(user);
|
||||
if (userObj.inventory[ITEMNAME]! < 1 && user.id !== streamerId) { await sendMessage(`You don't have any silver bullets!`, msg.messageId); await user.clearLock(); return; };
|
||||
if (userObj.inventory[ITEMNAME]! < 1 && user.id !== streamerId) {
|
||||
await sendMessage(`You don't have any silver bullets!`, msg.messageId);
|
||||
await user.clearLock();
|
||||
return;
|
||||
}
|
||||
|
||||
let target: User | null;
|
||||
if (!messagequery[0]) {
|
||||
const vulnsids = await redis.keys('user:*:vulnerable');
|
||||
const baseusers = vulnsids.map(a => User.initUserId(a.slice(5, -11)));
|
||||
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 user.clearLock(); await sendMessage('No vulnerable chatters', msg.messageId); return; };
|
||||
}
|
||||
if (users.length === 0) {
|
||||
await user.clearLock();
|
||||
await sendMessage("No vulnerable chatters", msg.messageId);
|
||||
return;
|
||||
}
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: 'blastinRoulette',
|
||||
name: "blastinRoulette",
|
||||
user: user.displayName,
|
||||
targets: users.map(a => a.displayName),
|
||||
finaltarget: target.displayName
|
||||
targets: users.map((a) => a.displayName),
|
||||
finaltarget: target.displayName,
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 4000));
|
||||
} else {
|
||||
target = await User.initUsername(messagequery[0].toLowerCase());
|
||||
};
|
||||
if (!target) { await user.clearLock(); await sendMessage(`${messagequery[0]} doesn't exist`); return; };
|
||||
}
|
||||
if (!target) {
|
||||
await user.clearLock();
|
||||
await sendMessage(`${messagequery[0]} doesn't exist`, msg.messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
await getUserRecord(target); // make sure the user record exist in the database
|
||||
|
||||
const result = await timeout(target, `You got blasted by ${user.displayName}!`, 60 * 30);
|
||||
if (result.status) await Promise.all([
|
||||
sendMessage(`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`),
|
||||
changeItemCount(user, userObj, ITEMNAME),
|
||||
createTimeoutRecord(user, target, ITEMNAME),
|
||||
createUsedItemRecord(user, ITEMNAME),
|
||||
const result = await timeout(
|
||||
target,
|
||||
`You got blasted by ${user.displayName}!`,
|
||||
60 * 30,
|
||||
);
|
||||
if (result.status) {
|
||||
await Promise.all([
|
||||
sendMessage(
|
||||
`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`,
|
||||
),
|
||||
playAlert({
|
||||
name: 'userExecution',
|
||||
name: "userExecution",
|
||||
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) {
|
||||
case "banned":
|
||||
await sendMessage(`${target.displayName} is already timed out/banned`, msg.messageId);
|
||||
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)
|
||||
timeout(user, "nah", 60),
|
||||
]);
|
||||
break;
|
||||
case "unknown":
|
||||
await sendMessage('Something went wrong...', msg.messageId);
|
||||
await sendMessage("Something went wrong...", msg.messageId);
|
||||
break;
|
||||
};
|
||||
};
|
||||
await user.clearLock();
|
||||
}
|
||||
}
|
||||
await user.clearLock();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,64 +1,80 @@
|
||||
import { redis } from "lib/redis";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import User from "user";
|
||||
import { getUserRecord } from "db/dbUser";
|
||||
import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { createUsedItemRecord } from "db/dbUsedItems";
|
||||
import { createTimeoutEventItem } from "db/ItemEvents";
|
||||
import { changeItemCount, Item } from "items";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
import User from "user";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
|
||||
const ITEMNAME = 'tnt';
|
||||
const ITEMNAME = "tnt";
|
||||
|
||||
export default new Item({
|
||||
name: ITEMNAME,
|
||||
prettyName: 'TNT',
|
||||
plural: 's',
|
||||
description: 'Give 5-10 random chatters 60 second timeouts',
|
||||
aliases: ['tnt'],
|
||||
prettyName: "TNT",
|
||||
plural: "s",
|
||||
description: "Give 5-10 random chatters 60 second timeouts",
|
||||
aliases: ["tnt"],
|
||||
price: 1000,
|
||||
execution: async (msg, user) => {
|
||||
const vulntargets = await redis.keys('user:*:vulnerable').then(a => a.map(b => b.slice(5, -11)));
|
||||
if (vulntargets.length === 0) { await sendMessage('No vulnerable chatters to blow up', msg.messageId); return; };
|
||||
const vulntargets = await redis
|
||||
.keys("user:*:vulnerable")
|
||||
.then((a) => a.map((b) => b.slice(5, -11)));
|
||||
if (vulntargets.length === 0) {
|
||||
await sendMessage("No vulnerable chatters to blow up", msg.messageId);
|
||||
return;
|
||||
}
|
||||
const targets = getTNTTargets(vulntargets);
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||
if (await user.itemLock()) {
|
||||
await sendMessage("Cannot use an item (itemlock)", msg.messageId);
|
||||
return;
|
||||
}
|
||||
await user.setLock();
|
||||
|
||||
const userObj = await getUserRecord(user);
|
||||
if (userObj.inventory[ITEMNAME]! < 1) { await sendMessage(`You don't have any TNTs!`, msg.messageId); await user.clearLock(); return; };
|
||||
if (userObj.inventory[ITEMNAME]! < 1) {
|
||||
await sendMessage(`You don't have any TNTs!`, msg.messageId);
|
||||
await user.clearLock();
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(targets.map(async targetid => {
|
||||
await Promise.all(
|
||||
targets.map(async (targetid) => {
|
||||
const target = await User.initUserId(targetid);
|
||||
await getUserRecord(target!); // make sure the user record exist in the database
|
||||
await Promise.all([
|
||||
timeout(target!, `You got hit by ${user.displayName}'s TNT!`, 60),
|
||||
sendMessage(`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`),
|
||||
createTimeoutRecord(user, target!, ITEMNAME),
|
||||
sendMessage(
|
||||
`wybuh ${target?.displayName} got hit by ${user.displayName}'s TNT wybuh`,
|
||||
),
|
||||
]);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
createUsedItemRecord(user, ITEMNAME),
|
||||
createTimeoutEventItem(user, targets, ITEMNAME),
|
||||
playAlert({
|
||||
name: 'tntExplosion',
|
||||
name: "tntExplosion",
|
||||
user: user.displayName,
|
||||
targets
|
||||
targets,
|
||||
}),
|
||||
changeItemCount(user, userObj, ITEMNAME)
|
||||
changeItemCount(user, userObj, ITEMNAME),
|
||||
]);
|
||||
|
||||
await user.clearLock();
|
||||
await sendMessage(`RIPBOZO ${user.displayName} exploded ${targets.length} chatter${targets.length === 1 ? '' : 's'} with their TNT RIPBOZO`);
|
||||
}
|
||||
await sendMessage(
|
||||
`RIPBOZO ${user.displayName} exploded ${targets.length} chatter${targets.length === 1 ? "" : "s"} with their TNT RIPBOZO`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export function getTNTTargets<T>(arr: T[]): T[] {
|
||||
if (arr.length <= 5) {
|
||||
return arr;
|
||||
};
|
||||
}
|
||||
|
||||
const count = Math.floor(Math.random() * 6) + 5; // Random number between 5 and 10
|
||||
const shuffled = [...arr].sort(() => 0.5 - Math.random()); // Shuffle array
|
||||
return shuffled.slice(0, Math.min(count, arr.length)); // Return up to `count` entries
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
export async function getAdmins() {
|
||||
const data = await redis.keys('user:*:admin');
|
||||
return data.map(a => a.slice(5, -6));
|
||||
};
|
||||
const data = await redis.keys("user:*:admin");
|
||||
return data.map((a) => a.slice(5, -6));
|
||||
}
|
||||
export async function isAdmin(userid: string) {
|
||||
return await redis.exists(`user:${userid}:admin`);
|
||||
};
|
||||
}
|
||||
export async function addAdmin(userid: string) {
|
||||
return await redis.set(`user:${userid}:admin`, '1');
|
||||
};
|
||||
return await redis.set(`user:${userid}:admin`, "1");
|
||||
}
|
||||
export async function removeAdmin(userid: string) {
|
||||
return await redis.del(`user:${userid}:admin`);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { updateUserRecord, type UserRecord } from "db/dbUser";
|
||||
import User from "user";
|
||||
import { type UserRecord, updateUserRecord } from "db/dbUser";
|
||||
import type User from "user";
|
||||
|
||||
export async function changeBalance(user: User, userRecord: UserRecord, amount: number): Promise<false | UserRecord> {
|
||||
export async function changeBalance(
|
||||
user: User,
|
||||
userRecord: UserRecord,
|
||||
amount: number,
|
||||
): Promise<false | UserRecord> {
|
||||
userRecord.balance = userRecord.balance += amount;
|
||||
if (userRecord.balance < 0) return false;
|
||||
await updateUserRecord(user, userRecord);
|
||||
return userRecord;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import User from "user";
|
||||
import { chatterId, streamerId } from "main";
|
||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import { api } from "index";
|
||||
import { chatterId, streamerId } from "main";
|
||||
import type User from "user";
|
||||
|
||||
export type userType = 'chatter' | 'admin' | 'streamer' | 'moderator';
|
||||
export type userType = "chatter" | "admin" | "streamer" | "moderator";
|
||||
|
||||
export type specialExecuteArgs = {
|
||||
activation?: string;
|
||||
};
|
||||
|
||||
export type commandOptions = {
|
||||
export interface commandOptions {
|
||||
name: string;
|
||||
aliases: string[];
|
||||
usertype: userType;
|
||||
execution: (message: EventSubChannelChatMessageEvent, sender: User, args?: specialExecuteArgs) => Promise<void>;
|
||||
execution: (
|
||||
message: EventSubChannelChatMessageEvent,
|
||||
sender: User,
|
||||
args?: specialExecuteArgs,
|
||||
) => Promise<void>;
|
||||
disableable?: boolean;
|
||||
specialaliases?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
/** The Command class represents a command */
|
||||
export class Command {
|
||||
@@ -25,7 +29,11 @@ export class Command {
|
||||
public readonly usertype: userType;
|
||||
public readonly disableable: boolean;
|
||||
public readonly specialaliases: string[];
|
||||
public readonly execute: (message: EventSubChannelChatMessageEvent, sender: User, args?: specialExecuteArgs) => Promise<void>;
|
||||
public readonly execute: (
|
||||
message: EventSubChannelChatMessageEvent,
|
||||
sender: User,
|
||||
args?: specialExecuteArgs,
|
||||
) => Promise<void>;
|
||||
constructor(options: commandOptions) {
|
||||
this.name = options.name.toLowerCase();
|
||||
this.aliases = options.aliases;
|
||||
@@ -33,14 +41,19 @@ export class Command {
|
||||
this.execute = options.execution;
|
||||
this.disableable = options.disableable ?? true;
|
||||
this.specialaliases = options.specialaliases ?? [];
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper function to send a message to the stream */
|
||||
export const sendMessage = async (message: string, replyParentMessageId?: string) => {
|
||||
export const sendMessage = async (
|
||||
message: string,
|
||||
replyParentMessageId?: string,
|
||||
) => {
|
||||
try {
|
||||
return await api.chat.sendChatMessageAsApp(chatterId, streamerId, message, { replyParentMessageId });
|
||||
} catch (e) {
|
||||
return await api.chat.sendChatMessageAsApp(chatterId, streamerId, message, {
|
||||
replyParentMessageId,
|
||||
});
|
||||
} catch (_e) {
|
||||
return await api.chat.sendChatMessageAsApp(chatterId, streamerId, message);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,14 +4,16 @@ export function buildTimeString(time1: number, time2: number): string {
|
||||
day: Math.floor(diff / (1000 * 60 * 60 * 24)),
|
||||
hour: Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
|
||||
minute: Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)),
|
||||
second: Math.floor((diff % (1000 * 60)) / 1000)
|
||||
second: Math.floor((diff % (1000 * 60)) / 1000),
|
||||
};
|
||||
const stringarray: string[] = [];
|
||||
for (const [unit, value] of Object.entries(timeobj)) {
|
||||
if (value === 0) continue;
|
||||
if (unit === 'second' && timeobj.day > 0) continue;
|
||||
stringarray.push(`${value} ${unit}${value === 1 ? '' : 's'}`);
|
||||
};
|
||||
if (unit === "second" && timeobj.day > 0) continue;
|
||||
stringarray.push(`${value} ${unit}${value === 1 ? "" : "s"}`);
|
||||
}
|
||||
const last = stringarray.pop();
|
||||
return stringarray.length === 0 ? last! : stringarray.join(', ') + " and " + last;
|
||||
};
|
||||
return stringarray.length === 0
|
||||
? last!
|
||||
: `${stringarray.join(", ")} and ${last}`;
|
||||
}
|
||||
|
||||
@@ -5,38 +5,44 @@ import type { inventory } from "items";
|
||||
import type User from "user";
|
||||
|
||||
export async function getTimeoutStats(target: User, thismonth: boolean) {
|
||||
const monthdata = thismonth ? new Date().toISOString().slice(0, 7) : undefined;
|
||||
const monthdata = thismonth
|
||||
? new Date().toISOString().slice(0, 7)
|
||||
: undefined;
|
||||
|
||||
const [shot, hit] = await Promise.all([
|
||||
getTimeoutsAsUser(target, monthdata),
|
||||
getTimeoutsAsTarget(target, monthdata)
|
||||
getTimeoutsAsTarget(target, monthdata),
|
||||
]);
|
||||
|
||||
if (!shot || !hit) return;
|
||||
|
||||
const blasterhit = hit.filter(item => item.item !== 'silverbullet').length;
|
||||
const blasterhit = hit.filter((item) => item.item !== "silverbullet").length;
|
||||
const silverbullethit = hit.length - blasterhit;
|
||||
const blastershot = shot.filter(item => item.item !== 'silverbullet').length;
|
||||
const blastershot = shot.filter(
|
||||
(item) => item.item !== "silverbullet",
|
||||
).length;
|
||||
const silverbulletshot = shot.length - blastershot;
|
||||
|
||||
return {
|
||||
hit: {
|
||||
blaster: blasterhit,
|
||||
silverbullet: silverbullethit
|
||||
silverbullet: silverbullethit,
|
||||
},
|
||||
shot: {
|
||||
blaster: blastershot,
|
||||
silverbullet: silverbulletshot
|
||||
}
|
||||
silverbullet: silverbulletshot,
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export async function getItemStats(target: User, thismonth: boolean) {
|
||||
const monthdata = thismonth ? new Date().toISOString().slice(0, 7) : undefined;
|
||||
const monthdata = thismonth
|
||||
? new Date().toISOString().slice(0, 7)
|
||||
: undefined;
|
||||
|
||||
const [items, cheers] = await Promise.all([
|
||||
getItemsUsed(target, monthdata),
|
||||
getCheerEvents(target, monthdata)
|
||||
getCheerEvents(target, monthdata),
|
||||
]);
|
||||
if (!items || !cheers) return;
|
||||
|
||||
@@ -50,12 +56,15 @@ export async function getItemStats(target: User, thismonth: boolean) {
|
||||
for (const item of items) {
|
||||
if (!returnObj[item.item]) returnObj[item.item] = 0;
|
||||
returnObj[item.item]! += 1;
|
||||
};
|
||||
}
|
||||
|
||||
for (const cheer of cheers) {
|
||||
if (!returnObj[cheer.event]) returnObj[cheer.event] = 0;
|
||||
returnObj[cheer.event]! += 1
|
||||
};
|
||||
if (cheer.event in returnObj) {
|
||||
if (!returnObj[cheer.event as keyof inventory])
|
||||
returnObj[cheer.event as keyof inventory] = 0;
|
||||
returnObj[cheer.event as keyof inventory]! += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return returnObj;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base"
|
||||
import { redis } from "lib/redis";
|
||||
import type User from "user";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import type { EventSubChannelChatMessageEvent } from "@twurple/eventsub-base";
|
||||
import { createAnivTimeoutRecord } from "db/dbAnivTimeouts";
|
||||
import { sendMessage } from "lib/commandUtils";
|
||||
import { redis } from "lib/redis";
|
||||
import { timeout } from "lib/timeout";
|
||||
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 = {
|
||||
[key: string]: string;
|
||||
@@ -21,40 +22,52 @@ type isNotAnivMessage = {
|
||||
isAnivMessage: false;
|
||||
};
|
||||
|
||||
export type anivBots = 'a_n_i_v' | 'a_n_e_e_v';
|
||||
export type anivBots = "a_n_i_v" | "a_n_e_e_v";
|
||||
|
||||
type anivMessageResult = IsAnivMessage | isNotAnivMessage;
|
||||
|
||||
async function isAnivMessage(message: string): Promise<anivMessageResult> {
|
||||
const data: anivMessageStore = await redis.get('anivmessages').then(a => a === null ? {} : JSON.parse(a));
|
||||
const data: anivMessageStore = await redis
|
||||
.get("anivmessages")
|
||||
.then((a) => (a === null ? {} : JSON.parse(a)));
|
||||
for (const clanker of ANIVNAMES) {
|
||||
const anivmessage = data[clanker];
|
||||
if (!anivmessage) continue;
|
||||
if (anivmessage === message) return { isAnivMessage: true, message, anivbot: clanker };
|
||||
};
|
||||
if (anivmessage === message)
|
||||
return { isAnivMessage: true, message, anivbot: clanker };
|
||||
}
|
||||
return { isAnivMessage: false };
|
||||
};
|
||||
}
|
||||
|
||||
export default async function handleMessage(msg: EventSubChannelChatMessageEvent, user: User) {
|
||||
if (ANIVNAMES.map(a => a.toLowerCase()).includes(user.username)) {
|
||||
const data: anivMessageStore = await redis.get('anivmessages').then(a => a === null ? {} : JSON.parse(a));
|
||||
export default async function handleMessage(
|
||||
msg: EventSubChannelChatMessageEvent,
|
||||
user: User,
|
||||
) {
|
||||
if (ANIVNAMES.map((a) => a.toLowerCase()).includes(user.username)) {
|
||||
await playMSTTS({ text: msg.messageText });
|
||||
const data: anivMessageStore = await redis
|
||||
.get("anivmessages")
|
||||
.then((a) => (a === null ? {} : JSON.parse(a)));
|
||||
data[user.displayName] = msg.messageText;
|
||||
await redis.set('anivmessages', JSON.stringify(data));
|
||||
await redis.set("anivmessages", JSON.stringify(data));
|
||||
} else {
|
||||
const data = await isAnivMessage(msg.messageText);
|
||||
if (data.isAnivMessage) {
|
||||
if (Math.random() > 0.5) { // 1/2 chance to dodge aniv timeout
|
||||
await createAnivTimeoutRecord(msg.messageText, data.anivbot, user, 0)
|
||||
if (Math.random() > 0.5) {
|
||||
// 1/2 chance to dodge aniv timeout
|
||||
await createAnivTimeoutRecord(msg.messageText, data.anivbot, user, 0);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const duration = Math.floor(Math.random() * 30) + 30 // minimum timeout of 30 sec, maximum of 60 sec
|
||||
const duration = Math.floor(Math.random() * 30) + 30; // minimum timeout of 30 sec, maximum of 60 sec
|
||||
|
||||
await Promise.all([
|
||||
timeout(user, 'copied an aniv message', 30),
|
||||
sendMessage(`${user.displayName} got timed out for copying an ${data.anivbot} message`),
|
||||
createAnivTimeoutRecord(msg.messageText, data.anivbot, user, duration)
|
||||
timeout(user, "copied an aniv message", 30),
|
||||
sendMessage(
|
||||
`${user.displayName} got timed out for copying an ${data.anivbot} message`,
|
||||
),
|
||||
createAnivTimeoutRecord(msg.messageText, data.anivbot, user, duration),
|
||||
]);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@ import { redis } from "lib/redis";
|
||||
import { streamerUsers } from "main";
|
||||
|
||||
export async function getInvulns() {
|
||||
const data = await redis.keys('user:*:invulnerable');
|
||||
return data.map(a => a.slice(5, -13));
|
||||
};
|
||||
const data = await redis.keys("user:*:invulnerable");
|
||||
return data.map((a) => a.slice(5, -13));
|
||||
}
|
||||
export async function isInvuln(userid: string) {
|
||||
return await redis.exists(`user:${userid}:invulnerable`);
|
||||
};
|
||||
}
|
||||
export async function addInvuln(userid: string) {
|
||||
await redis.del(`user:${userid}:vulnerable`);
|
||||
if (await redis.ttl(`user:${userid}:invulnerable`) > 0) await redis.del(`user:${userid}:invulnerable`);
|
||||
return await redis.set(`user:${userid}:invulnerable`, '1');
|
||||
};
|
||||
if ((await redis.ttl(`user:${userid}:invulnerable`)) > 0)
|
||||
await redis.del(`user:${userid}:invulnerable`);
|
||||
return await redis.set(`user:${userid}:invulnerable`, "1");
|
||||
}
|
||||
export async function removeInvuln(userid: string) {
|
||||
if (streamerUsers.includes(userid)) return;
|
||||
return await redis.del(`user:${userid}:invulnerable`);
|
||||
};
|
||||
}
|
||||
export async function setTemporaryInvuln(userid: string, duration = 600) {
|
||||
await redis.set(`user:${userid}:invulnerable`, '1');
|
||||
await redis.set(`user:${userid}:invulnerable`, "1");
|
||||
await redis.expire(`user:${userid}:invulnerable`, duration);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,39 @@
|
||||
import kleur from "kleur";
|
||||
|
||||
const logger = {
|
||||
err: (arg: string) => console.error(kleur.red().bold().italic('[ERROR] ') + kleur.red().bold(arg)),
|
||||
warn: (arg: string) => console.warn(kleur.yellow().bold().italic('[WARN] ') + kleur.yellow().bold(arg)),
|
||||
info: (arg: string) => console.info(kleur.white().bold().italic('[INFO] ') + kleur.white(arg)),
|
||||
ok: (arg: string) => console.info(kleur.green().bold(arg)),
|
||||
enverr: (arg: string) => logger.err(`Please provide a ${arg} in the .env`)
|
||||
err: (arg: string) =>
|
||||
console.error(
|
||||
Bun.wrapAnsi(
|
||||
`\x1b[1;91m[ERROR]: \x1b[0;97m${arg}\x1b[0m`,
|
||||
process.stdout.columns,
|
||||
),
|
||||
),
|
||||
warn: (arg: string) =>
|
||||
console.warn(
|
||||
Bun.wrapAnsi(
|
||||
`\x1b[1;93m[WARN]: \x1b[0;97m${arg}\x1b[0m`,
|
||||
process.stdout.columns,
|
||||
),
|
||||
),
|
||||
info: (arg: string) =>
|
||||
console.info(
|
||||
Bun.wrapAnsi(
|
||||
`\x1b[37;1m[INFO]: \x1b[0;97m${arg}\x1b[0m`,
|
||||
process.stdout.columns,
|
||||
),
|
||||
),
|
||||
ok: (arg: string) =>
|
||||
console.info(
|
||||
Bun.wrapAnsi(
|
||||
`\x1b[1;92m[OK]: \x1b[0;97m${arg}\x1b[0m`,
|
||||
process.stdout.columns,
|
||||
),
|
||||
),
|
||||
enverr: (arg: string) =>
|
||||
logger.err(
|
||||
Bun.wrapAnsi(
|
||||
`Please provide a \x1b[4;3;93m${arg}\x1b[0;97m in the .env`,
|
||||
process.stdout.columns,
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
export default logger
|
||||
export default logger;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user