Compare commits
49 Commits
d2619420ef
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e3e1620d1 | |||
| dd4d6e4817 | |||
| b441de286c | |||
| 2ca18aae06 | |||
| 1ef2295c07 | |||
| 6342c4569b | |||
| c61e34c241 | |||
| 82cef1b34a | |||
| 136a5902c0 | |||
| 903a2f3081 | |||
| 995a2317b9 | |||
| e3dbb11e12 | |||
| 14a059034c | |||
| 9710fc65bf | |||
| 42fc848f8c | |||
| c2ac7737c0 | |||
| 6a519f3a2d | |||
| 338b32430d | |||
| 9f1a539501 | |||
| 425686ec40 | |||
| 4d858554be | |||
| 9c867b1adb | |||
| 030b4b8883 | |||
| 08d1a8c5cd | |||
| 9e0870c1ed | |||
| ca823726e3 | |||
| ae35f61522 | |||
| 985f481fae | |||
| e437c051b9 | |||
| c630543f9e | |||
| 9847e476b8 | |||
| 8aeab3fb5d | |||
| 81ef97370c | |||
| 1fd73538fa | |||
| 32ce20bd72 | |||
| 0d512960a7 | |||
| 0407f4d7f1 | |||
| 0f48f09bd9 | |||
| b9a887259a | |||
| 24d5b395ca | |||
| 8bdd6868e8 | |||
| 5df3430242 | |||
| 832863c1e4 | |||
| 7c49e05aec | |||
| dafa81e2a6 | |||
| 9e94e99874 | |||
| c76680293e | |||
| 912b1b6ceb | |||
| ad7b3c6359 |
@@ -1,2 +1,5 @@
|
||||
node_modules
|
||||
.env
|
||||
backup/
|
||||
*.bak
|
||||
tests/
|
||||
@@ -0,0 +1,289 @@
|
||||
# ShaiBot
|
||||
|
||||
ShaiBot is a modular Discord community bot built around a persistent
|
||||
**Cookie economy**, interactive games, player progression, and
|
||||
multiplayer activities.
|
||||
|
||||
The bot's main entertainment hub is the **Crumb Saucer**, where players
|
||||
can collect and wager Cookies, play casino-style games, attempt Vault
|
||||
Heists, recover through Bankruptcy, and sit down at a multiplayer Texas
|
||||
Hold'em table.
|
||||
|
||||
ShaiBot also includes persistent player profiles, achievements,
|
||||
guild-unique badges, cooperative encounters, raid planning, and lighter
|
||||
community features.
|
||||
|
||||
> **Status:** Active development and stabilization. The major economy,
|
||||
> profile, achievement, and Poker overhaul is live; current work is
|
||||
> focused primarily on testing, balancing, and polish.
|
||||
|
||||
## Highlights
|
||||
|
||||
- 🍪 Persistent Cookie economy
|
||||
- 🎰 Crumb Saucer game hub
|
||||
- 🎰 Slots, Blackjack, Roulette, Vault Heist, and Bankruptcy
|
||||
- ♠️ Persistent 2--6 player Texas Hold'em
|
||||
- 🏴 Player-to-player Cookie stealing
|
||||
- 👤 Lifetime player profiles and statistics
|
||||
- 🏆 Personal achievements
|
||||
- 🎖️ Guild-unique global badges
|
||||
- 🔁 All-In and Run It Back progression
|
||||
- ⚔️ Cooperative Crumble Strike encounters
|
||||
- 📅 Raid planning and community utilities
|
||||
- 💾 Mongo-backed persistence and recovery
|
||||
|
||||
## The Crumb Saucer
|
||||
|
||||
The **Crumb Saucer** is the main home for ShaiBot's Cookie-powered
|
||||
games.
|
||||
|
||||
### 🍪 Cookie Bakery
|
||||
|
||||
Collect Cookies using the same shared collection system as
|
||||
`/cookies collect`.
|
||||
|
||||
### 🎰 Slots
|
||||
|
||||
Spin for symbol combinations, jackpots, All-In wagers, and Run It Back
|
||||
streaks.
|
||||
|
||||
### 🃏 Blackjack
|
||||
|
||||
Includes Hit, Stand, Double, natural Blackjack, pushes, All-In, and Run
|
||||
It Back.
|
||||
|
||||
### 🎡 Roulette
|
||||
|
||||
A compact European Roulette game using numbers `0–36`, with Red, Black,
|
||||
Odd, Even, and Zero bets.
|
||||
|
||||
### 🏦 Vault Heist
|
||||
|
||||
Attempt to steal from ShaiBot's house vault using different risk/reward
|
||||
approaches.
|
||||
|
||||
### 🆘 Bankruptcy
|
||||
|
||||
A recovery mechanic for players who need a fresh start, with profile
|
||||
progression for rebuilding afterward.
|
||||
|
||||
### ♠️ Shai Poker
|
||||
|
||||
A persistent multiplayer Texas Hold'em game for **2--6 players**.
|
||||
|
||||
Current defaults:
|
||||
|
||||
``` text
|
||||
Buy-in: 1,000 Cookies
|
||||
Blinds: 10 / 20
|
||||
Turn timer: 90 seconds
|
||||
Pot model: single pot
|
||||
```
|
||||
|
||||
Poker supports private hole cards, public community cards,
|
||||
Fold/Check/Call/Bet/Raise/All-In, automatic timeout folding, restart
|
||||
recovery, host cancellation, safe buy-in/cash-out settlement, Poker
|
||||
statistics, achievements, and guild-first badges.
|
||||
|
||||
Side pots, tournaments, bots, rake, and player-facing equity assistance
|
||||
are intentionally outside the current V1 design.
|
||||
|
||||
## Player Profiles
|
||||
|
||||
Profiles provide the permanent history behind ShaiBot's progression
|
||||
systems.
|
||||
|
||||
They track things such as:
|
||||
|
||||
- Game wins and losses
|
||||
- Per-game statistics
|
||||
- Cookie gains and losses
|
||||
- Giving and stealing
|
||||
- Daily activity and streaks
|
||||
- Social progression
|
||||
- Run It Back streaks
|
||||
- Poker history and hand accomplishments
|
||||
- Achievements
|
||||
- Global badges
|
||||
|
||||
Completed multiplayer game documents do not need to become permanent
|
||||
history when the meaningful result already belongs in the profile
|
||||
system.
|
||||
|
||||
## Achievements & Global Badges
|
||||
|
||||
**Achievements** are personal progression. Any eligible player can earn
|
||||
them.
|
||||
|
||||
**Global badges** are guild-unique distinctions, usually awarded to the
|
||||
first player in a guild to accomplish something notable.
|
||||
|
||||
Progression spans the Cookie economy, stealing, Crumb Saucer games,
|
||||
activity, social interactions, Poker, and special cross-game challenges.
|
||||
|
||||
## Cookie Stealing
|
||||
|
||||
Player-to-player stealing is separate from Vault Heist.
|
||||
|
||||
The current system accounts for wealth differences, recent pressure on
|
||||
the victim, percentage-based loot, hard caps, and an hourly exposure
|
||||
budget.
|
||||
|
||||
Detailed balancing and mechanics are documented in
|
||||
[`COOKIE_LOGIC.md`](COOKIE_LOGIC.md).
|
||||
|
||||
## ⚔️ Crumble Strike
|
||||
|
||||
Crumble Strike is ShaiBot's cooperative boss-battle system.
|
||||
|
||||
Players recruit a party, choose combat roles, commit Cookies, and work
|
||||
together using actions such as Strike, Defend, Heal, Revive, and Limit
|
||||
Break.
|
||||
|
||||
Encounters use persistent state because they can span many players and
|
||||
interactions.
|
||||
|
||||
## 📅 Raid Planner & Community Features
|
||||
|
||||
ShaiBot also includes raid-planning tools and smaller social/community
|
||||
commands alongside its economy and progression systems.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
# Installation
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node.js
|
||||
- MongoDB
|
||||
- A Discord bot application/token
|
||||
- Dependencies listed in `package.json`
|
||||
|
||||
Clone and install:
|
||||
|
||||
``` bash
|
||||
git clone https://git.desq-gaming.de/Shaiwase/ShaiBot.git
|
||||
cd ShaiBot
|
||||
npm install
|
||||
```
|
||||
|
||||
Create `.env` in the repository root:
|
||||
|
||||
``` env
|
||||
D_TOKEN=YOUR_DISCORD_BOT_TOKEN
|
||||
M_URI=YOUR_MONGODB_CONNECTION_STRING
|
||||
M_DB=YOUR_DATABASE_NAME
|
||||
|
||||
# Optional
|
||||
BOT_AUTHOR_ID=YOUR_DISCORD_USER_ID
|
||||
PROFILE_TIMEZONE=Europe/Berlin
|
||||
```
|
||||
|
||||
Never commit `.env`.
|
||||
|
||||
Start normally:
|
||||
|
||||
``` bash
|
||||
node index.js
|
||||
```
|
||||
|
||||
Or with PM2:
|
||||
|
||||
``` bash
|
||||
pm2 start index.js --name ShaiBot
|
||||
```
|
||||
|
||||
A typical update cycle is:
|
||||
|
||||
``` bash
|
||||
node --check path/to/changed-file.js
|
||||
pm2 restart ShaiBot
|
||||
pm2 logs ShaiBot --lines 100
|
||||
```
|
||||
|
||||
## MongoDB
|
||||
|
||||
ShaiBot does not require MongoDB multi-document transactions for its
|
||||
core economy.
|
||||
|
||||
The bot instead relies on atomic updates, unique indexes, optimistic
|
||||
concurrency, idempotent operations, and recovery logic where
|
||||
appropriate.
|
||||
|
||||
If your Mongo deployment does not support retryable writes, your
|
||||
connection string may require:
|
||||
|
||||
``` text
|
||||
retryWrites=false
|
||||
```
|
||||
|
||||
Never commit database credentials.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
# Documentation
|
||||
|
||||
The central README intentionally stays high-level. Detailed
|
||||
implementation rules live in focused manuals:
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
Document Purpose
|
||||
------------------------------------------------ -----------------------------------
|
||||
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) Runtime structure, handlers,
|
||||
utilities, Mongo persistence,
|
||||
concurrency, and recovery
|
||||
|
||||
[`docs/ECONOMY.md`](docs/ECONOMY.md) Cookie balances, transfers,
|
||||
cooldowns, house vault, settlement,
|
||||
and idempotency
|
||||
|
||||
[`docs/PROGRESSION.md`](docs/PROGRESSION.md) Profiles, achievements, badges,
|
||||
activity, social progression, and
|
||||
maintenance
|
||||
|
||||
[`docs/GAMES.md`](docs/GAMES.md) Crumb Saucer games, Poker, Crumble
|
||||
Strike, and important game rules
|
||||
|
||||
[`COOKIE_LOGIC.md`](COOKIE_LOGIC.md) Detailed PvP Cookie stealing rules
|
||||
and balancing
|
||||
|
||||
[`AGENTS.md`](AGENTS.md) Repository-wide safety and
|
||||
architecture guide for AI coding
|
||||
agents
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
The current repository code remains the final source of truth.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
# Development Principles
|
||||
|
||||
ShaiBot generally favors:
|
||||
|
||||
- Shared utilities instead of duplicated game/economy logic
|
||||
- Thin Discord command/button wrappers
|
||||
- Persistent authoritative state where recovery matters
|
||||
- Atomic and idempotent economy operations
|
||||
- Centralized profile progression
|
||||
- Public game experiences with private sensitive information
|
||||
- Small explicit solutions over unnecessary abstraction
|
||||
|
||||
For stateful changes, always consider duplicate interactions,
|
||||
simultaneous users, insufficient-balance races, and bot restarts during
|
||||
settlement.
|
||||
|
||||
## Security
|
||||
|
||||
Keep at least the following out of version control:
|
||||
|
||||
``` gitignore
|
||||
node_modules/
|
||||
.env
|
||||
*.log
|
||||
```
|
||||
|
||||
Rotate Discord or MongoDB credentials immediately if they are exposed.
|
||||
|
||||
## License
|
||||
|
||||
See the repository's current license information, if present.
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
@@ -0,0 +1,11 @@
|
||||
const {
|
||||
closeInteractionSession
|
||||
} = require("../../utils/interactionSession");
|
||||
|
||||
module.exports = {
|
||||
name: "sessionClose",
|
||||
|
||||
async execute(interaction) {
|
||||
return closeInteractionSession(interaction);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
showCrumbSaucer
|
||||
} = require(
|
||||
"../../utils/crumbSaucer"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerBack",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return showCrumbSaucer(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
MessageFlags
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
declareBankruptcy
|
||||
} = require("../../utils/bankruptcy");
|
||||
|
||||
const {
|
||||
requireInteractionSession
|
||||
} = require("../../utils/interactionSession");
|
||||
|
||||
module.exports = {
|
||||
name: "crumbSaucerBankruptcy",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireInteractionSession(interaction);
|
||||
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await declareBankruptcy(interaction);
|
||||
|
||||
if (!result.ok) {
|
||||
if (result.reason === "COOLDOWN") {
|
||||
return interaction.reply({
|
||||
content:
|
||||
`Bankruptcy assistance is unavailable until <t:${result.nextAvailableAt}:R>.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
if (result.reason === "BALANCE_NOT_ZERO") {
|
||||
return interaction.reply({
|
||||
content:
|
||||
`Bankruptcy can only be declared at exactly **0 Cookies**. Your current balance is **${result.balance.toLocaleString()}**.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
return interaction.reply({
|
||||
content: "Bankruptcy could not be declared.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("🍪 Bankruptcy Assistance")
|
||||
.setDescription(
|
||||
[
|
||||
`${interaction.user} declared bankruptcy and received a starter kit.`,
|
||||
"",
|
||||
`**+${result.amount.toLocaleString()} Cookies**`,
|
||||
"",
|
||||
"Spend them wisely. Or at least memorably."
|
||||
].join("\n")
|
||||
)
|
||||
.addFields({
|
||||
name: "Next Eligibility",
|
||||
value: `<t:${result.nextAvailableAt}:R>`
|
||||
})
|
||||
.setFooter({
|
||||
text: `crumbSaucer:${interaction.user.id}`
|
||||
});
|
||||
|
||||
const row = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("crumbSaucerBack")
|
||||
.setLabel("Back to Crumb Saucer")
|
||||
.setEmoji("◀️")
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId("sessionClose")
|
||||
.setLabel("Close")
|
||||
.setEmoji("✖️")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return interaction.update({
|
||||
embeds: [embed],
|
||||
components: [row]
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
showBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerBlackjack",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return showBlackjack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
startBlackjackAllIn
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackAllIn",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return startBlackjackAllIn(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
startBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackBet10",
|
||||
|
||||
async execute(interaction) {
|
||||
return startBlackjack(
|
||||
interaction,
|
||||
10
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
startBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackBet100",
|
||||
|
||||
async execute(interaction) {
|
||||
return startBlackjack(
|
||||
interaction,
|
||||
100
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
startBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackBet1000",
|
||||
|
||||
async execute(interaction) {
|
||||
return startBlackjack(
|
||||
interaction,
|
||||
1000
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
startBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackBet50",
|
||||
|
||||
async execute(interaction) {
|
||||
return startBlackjack(
|
||||
interaction,
|
||||
50
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
startBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackBet500",
|
||||
|
||||
async execute(interaction) {
|
||||
return startBlackjack(
|
||||
interaction,
|
||||
500
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
doubleBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackDouble",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return doubleBlackjack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
hitBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackHit",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return hitBlackjack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
runBlackjackBack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackRunItBack",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return runBlackjackBack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
standBlackjack
|
||||
} = require(
|
||||
"../../utils/crumbBlackjack"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbBlackjackStand",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return standBlackjack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
const {
|
||||
EmbedBuilder
|
||||
} = require(
|
||||
"discord.js"
|
||||
);
|
||||
|
||||
const {
|
||||
temporaryEphemeralReply
|
||||
} = require(
|
||||
"../../utils/cookieEconomy"
|
||||
);
|
||||
|
||||
const {
|
||||
getCrumbSaucerSessionOwner
|
||||
} = require(
|
||||
"../../utils/crumbSaucer"
|
||||
);
|
||||
|
||||
|
||||
function delay(
|
||||
ms
|
||||
) {
|
||||
return new Promise(
|
||||
resolve =>
|
||||
setTimeout(
|
||||
resolve,
|
||||
ms
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerClose",
|
||||
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const ownerId =
|
||||
getCrumbSaucerSessionOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This isn't your Crumb Saucer session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"Closed"
|
||||
)
|
||||
.setDescription(
|
||||
"Closing Crumb Saucer..."
|
||||
);
|
||||
|
||||
|
||||
await interaction.update({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components: []
|
||||
});
|
||||
|
||||
|
||||
await delay(
|
||||
1500
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
await interaction.deleteReply();
|
||||
}
|
||||
|
||||
catch {}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const {
|
||||
collectCookiesFromSaucer
|
||||
} = require(
|
||||
"../../utils/crumbCollect"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerCollect",
|
||||
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return collectCookiesFromSaucer(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const {
|
||||
showCollectPage
|
||||
} = require(
|
||||
"../../utils/crumbCollect"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerCollectMenu",
|
||||
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return showCollectPage(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,301 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
getCookieBalance,
|
||||
getCooldown,
|
||||
getUnixTime,
|
||||
temporaryEphemeralReply
|
||||
} = require(
|
||||
"../../utils/cookieEconomy"
|
||||
);
|
||||
|
||||
const {
|
||||
HEIST_COOLDOWN
|
||||
} = require(
|
||||
"../../utils/cookieHeist"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerHeist",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
/*
|
||||
* Verify ownership of the Crumb Saucer menu.
|
||||
*/
|
||||
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const [
|
||||
type,
|
||||
ownerId
|
||||
] =
|
||||
footer?.split(":")
|
||||
?? [];
|
||||
|
||||
|
||||
if (
|
||||
type !== "crumbSaucer" ||
|
||||
ownerId !== interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This isn't your Crumb Saucer session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ====================================================
|
||||
* SHARED HEIST COOLDOWN PRE-CHECK
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
const now =
|
||||
getUnixTime();
|
||||
|
||||
|
||||
const cooldown =
|
||||
await getCooldown(
|
||||
interaction.user.id,
|
||||
interaction.guildId,
|
||||
"heist"
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
cooldown >
|
||||
now
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
`🏦 The ShaiBot Vault is still on alert.\nYou can attempt another heist <t:${cooldown}:R>.`
|
||||
},
|
||||
6000
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Balances
|
||||
*/
|
||||
|
||||
const vaultId =
|
||||
interaction.client.user.id;
|
||||
|
||||
|
||||
const [
|
||||
vault,
|
||||
player
|
||||
] =
|
||||
await Promise.all([
|
||||
getCookieBalance(
|
||||
vaultId
|
||||
),
|
||||
|
||||
getCookieBalance(
|
||||
interaction.user.id
|
||||
)
|
||||
]);
|
||||
|
||||
|
||||
if (
|
||||
player <
|
||||
1
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"You need at least **1 Cookie** before attempting a vault heist."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
vault <
|
||||
1
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"The ShaiBot Vault is currently empty."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Heist menu
|
||||
*/
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🏦 ShaiBot Cookie Vault"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
`**Vault Balance:** ${vault.toLocaleString()} 🍪`,
|
||||
`**Security:** ${getSecurityLabel(vault)}`,
|
||||
"",
|
||||
"Choose your approach.",
|
||||
"",
|
||||
"🥷 **Sneak**",
|
||||
"High chance • Small haul • Small fine",
|
||||
"",
|
||||
"💻 **Hack**",
|
||||
"Medium chance • Medium haul • Medium fine",
|
||||
"",
|
||||
"💥 **Smash**",
|
||||
"Low chance • Large haul • Large fine"
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${interaction.user.id}`
|
||||
});
|
||||
|
||||
|
||||
const heistButtons =
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"cookieHeistSneak"
|
||||
)
|
||||
.setLabel(
|
||||
"Sneak"
|
||||
)
|
||||
.setEmoji(
|
||||
"🥷"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"cookieHeistHack"
|
||||
)
|
||||
.setLabel(
|
||||
"Hack"
|
||||
)
|
||||
.setEmoji(
|
||||
"💻"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"cookieHeistSmash"
|
||||
)
|
||||
.setLabel(
|
||||
"Smash"
|
||||
)
|
||||
.setEmoji(
|
||||
"💥"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Danger
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
const backButton =
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Back to Crumb Saucer"
|
||||
)
|
||||
.setEmoji(
|
||||
"◀️"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return interaction.update({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components: [
|
||||
heistButtons,
|
||||
backButton
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* SECURITY LABEL
|
||||
*/
|
||||
|
||||
function getSecurityLabel(
|
||||
vault
|
||||
) {
|
||||
if (
|
||||
vault <
|
||||
1000
|
||||
) {
|
||||
return "🟢 Low";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
vault <
|
||||
10000
|
||||
) {
|
||||
return "🟡 Moderate";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
vault <
|
||||
50000
|
||||
) {
|
||||
return "🟠 High";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
vault <
|
||||
250000
|
||||
) {
|
||||
return "🔴 Very High";
|
||||
}
|
||||
|
||||
|
||||
return "🟣 Maximum";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
resolveCookieHeist
|
||||
} = require(
|
||||
"../../utils/cookieHeist"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"cookieHeistHack",
|
||||
|
||||
async execute(interaction) {
|
||||
return resolveCookieHeist(
|
||||
interaction,
|
||||
"Hack"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
resolveCookieHeist
|
||||
} = require(
|
||||
"../../utils/cookieHeist"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"cookieHeistSmash",
|
||||
|
||||
async execute(interaction) {
|
||||
return resolveCookieHeist(
|
||||
interaction,
|
||||
"Smash"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
resolveCookieHeist
|
||||
} = require(
|
||||
"../../utils/cookieHeist"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"cookieHeistSneak",
|
||||
|
||||
async execute(interaction) {
|
||||
return resolveCookieHeist(
|
||||
interaction,
|
||||
"Sneak"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
const {
|
||||
buildPokerHubEmbed,
|
||||
buildPokerHubComponents,
|
||||
deferPokerReply
|
||||
} = require("../../utils/pokerDiscord");
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerPoker",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
await deferPokerReply(
|
||||
interaction
|
||||
);
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
buildPokerHubEmbed()
|
||||
],
|
||||
components:
|
||||
buildPokerHubComponents()
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
const {
|
||||
buildProfileOverview,
|
||||
buildProfileButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
|
||||
const {
|
||||
temporaryEphemeralReply
|
||||
} = require("../../utils/cookieEconomy");
|
||||
|
||||
module.exports = {
|
||||
name: "crumbSaucerProfile",
|
||||
|
||||
async execute(interaction) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
const [
|
||||
type,
|
||||
ownerId
|
||||
] = footer?.split(":") ?? [];
|
||||
|
||||
|
||||
if (
|
||||
type !== "crumbSaucer" ||
|
||||
ownerId !== interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This isn't your Crumb Saucer session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const embed =
|
||||
await buildProfileOverview(
|
||||
interaction.guildId,
|
||||
interaction.user,
|
||||
interaction.user.id
|
||||
);
|
||||
|
||||
|
||||
return interaction.update({
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"overview",
|
||||
0,
|
||||
1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
const {
|
||||
showRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerRoulette",
|
||||
|
||||
async execute(interaction) {
|
||||
return showRoulette(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
startRouletteAllIn
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteAllIn",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return startRouletteAllIn(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
chooseRouletteBet
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBet10",
|
||||
|
||||
async execute(interaction) {
|
||||
return chooseRouletteBet(
|
||||
interaction,
|
||||
10
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
chooseRouletteBet
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBet100",
|
||||
|
||||
async execute(interaction) {
|
||||
return chooseRouletteBet(
|
||||
interaction,
|
||||
100
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
chooseRouletteBet
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBet1000",
|
||||
|
||||
async execute(interaction) {
|
||||
return chooseRouletteBet(
|
||||
interaction,
|
||||
1000
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
chooseRouletteBet
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBet50",
|
||||
|
||||
async execute(interaction) {
|
||||
return chooseRouletteBet(
|
||||
interaction,
|
||||
50
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
chooseRouletteBet
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBet500",
|
||||
|
||||
async execute(interaction) {
|
||||
return chooseRouletteBet(
|
||||
interaction,
|
||||
500
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
playRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteBlack",
|
||||
|
||||
async execute(interaction) {
|
||||
return playRoulette(
|
||||
interaction,
|
||||
"Black"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
playRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteEven",
|
||||
|
||||
async execute(interaction) {
|
||||
return playRoulette(
|
||||
interaction,
|
||||
"Even"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
playRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteOdd",
|
||||
|
||||
async execute(interaction) {
|
||||
return playRoulette(
|
||||
interaction,
|
||||
"Odd"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
playRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteRed",
|
||||
|
||||
async execute(interaction) {
|
||||
return playRoulette(
|
||||
interaction,
|
||||
"Red"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
runRouletteBack
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteRunItBack",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return runRouletteBack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
playRoulette
|
||||
} = require(
|
||||
"../../utils/crumbRoulette"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbRouletteZero",
|
||||
|
||||
async execute(interaction) {
|
||||
return playRoulette(
|
||||
interaction,
|
||||
"Zero"
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
showSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return showSlots(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
playSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots10",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return playSlots(
|
||||
interaction,
|
||||
10
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
playSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots100",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return playSlots(
|
||||
interaction,
|
||||
100
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
playSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots1000",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return playSlots(
|
||||
interaction,
|
||||
1000
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
playSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots50",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return playSlots(
|
||||
interaction,
|
||||
50
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
playSlots
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlots500",
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
return playSlots(
|
||||
interaction,
|
||||
500
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const {
|
||||
runSlotsBack
|
||||
} = require(
|
||||
"../../utils/crumbSlots.js"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbSaucerSlotsRunItBack",
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
return runSlotsBack(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const {
|
||||
performCrumbleStrikeAction
|
||||
} = require(
|
||||
"../../utils/crumbleStrike"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
"crumbleStrikeDefend",
|
||||
|
||||
async execute(interaction) {
|
||||
return performCrumbleStrikeAction(
|
||||
interaction,
|
||||
"Defend"
|
||||
);
|
||||
}
|
||||
};
|
||||