Compare commits
42 Commits
832863c1e4
...
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 |
@@ -1,2 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.env
|
.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.
|
||||||
@@ -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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
performCrumbleStrikeAction
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeHeal",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return performCrumbleStrikeAction(
|
||||||
|
interaction,
|
||||||
|
"Heal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobAstrologian",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Healer",
|
||||||
|
"Astrologian"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobBard",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"DPS",
|
||||||
|
"Bard"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobBlackMage",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"DPS",
|
||||||
|
"BlackMage"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobDarkKnight",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Tank",
|
||||||
|
"DarkKnight"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobDragoon",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"DPS",
|
||||||
|
"Dragoon"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobPaladin",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Tank",
|
||||||
|
"Paladin"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobScholar",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Healer",
|
||||||
|
"Scholar"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobWarrior",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Tank",
|
||||||
|
"Warrior"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeJob
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeJobWhiteMage",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeJob(
|
||||||
|
interaction,
|
||||||
|
"Healer",
|
||||||
|
"WhiteMage"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
performCrumbleStrikeAction
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeLimitBreak",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return performCrumbleStrikeAction(
|
||||||
|
interaction,
|
||||||
|
"LimitBreak"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
performCrumbleStrikeAction
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeRevive",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return performCrumbleStrikeAction(
|
||||||
|
interaction,
|
||||||
|
"Revive"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeRole
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeRoleDPS",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeRole(
|
||||||
|
interaction,
|
||||||
|
"DPS"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeRole
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeRoleHealer",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeRole(
|
||||||
|
interaction,
|
||||||
|
"Healer"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
chooseCrumbleStrikeRole
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeRoleTank",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return chooseCrumbleStrikeRole(
|
||||||
|
interaction,
|
||||||
|
"Tank"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
startCrumbleStrikeBattle
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeStart",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return startCrumbleStrikeBattle(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
performCrumbleStrikeAction
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeStrike",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return performCrumbleStrikeAction(
|
||||||
|
interaction,
|
||||||
|
"Strike"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
confirmCrumbleStrikeJoin
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeTierAdventurer",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return confirmCrumbleStrikeJoin(
|
||||||
|
interaction,
|
||||||
|
"Adventurer"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
confirmCrumbleStrikeJoin
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeTierSprout",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return confirmCrumbleStrikeJoin(
|
||||||
|
interaction,
|
||||||
|
"Sprout"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const {
|
||||||
|
confirmCrumbleStrikeJoin
|
||||||
|
} = require(
|
||||||
|
"../../utils/crumbleStrike"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"crumbleStrikeTierWarriorOfLight",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return confirmCrumbleStrikeJoin(
|
||||||
|
interaction,
|
||||||
|
"WarriorOfLight"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
executePokerAction
|
||||||
|
} = require("../../utils/pokerInteractions");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerAllIn",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return executePokerAction(
|
||||||
|
interaction,
|
||||||
|
{
|
||||||
|
type: "all_in"
|
||||||
|
},
|
||||||
|
"✅ All-In."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
buildJoinPicker
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerBrowse",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tables =
|
||||||
|
await getPokerService()
|
||||||
|
.listJoinableTables({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
channelId:
|
||||||
|
interaction.channelId
|
||||||
|
});
|
||||||
|
|
||||||
|
return interaction.editReply(
|
||||||
|
buildJoinPicker(
|
||||||
|
tables
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
executePokerAction
|
||||||
|
} = require("../../utils/pokerInteractions");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerCall",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return executePokerAction(
|
||||||
|
interaction,
|
||||||
|
{
|
||||||
|
type: "call"
|
||||||
|
},
|
||||||
|
"✅ Called."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
const {
|
||||||
|
EmbedBuilder,
|
||||||
|
ActionRowBuilder,
|
||||||
|
ButtonBuilder,
|
||||||
|
ButtonStyle
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
POKER_TABLE_FOOTER_PREFIX,
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerCancel",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await deferPokerReply(interaction);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(interaction);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content: "❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService().getTable({ gameId });
|
||||||
|
|
||||||
|
if (
|
||||||
|
String(game.data.ownerId) !==
|
||||||
|
String(interaction.user.id)
|
||||||
|
) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content: "❌ Only the table owner can cancel this Poker table.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content: "",
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle("🛑 Cancel Poker Table?")
|
||||||
|
.setDescription(
|
||||||
|
[
|
||||||
|
"All seated players will be safely cashed out.",
|
||||||
|
"",
|
||||||
|
"This cannot be undone."
|
||||||
|
].join("\n")
|
||||||
|
)
|
||||||
|
.setFooter({
|
||||||
|
text: `${POKER_TABLE_FOOTER_PREFIX}:${gameId}`
|
||||||
|
})
|
||||||
|
],
|
||||||
|
components: [
|
||||||
|
new ActionRowBuilder().addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId("pokerCancelConfirm")
|
||||||
|
.setLabel("Cancel Table")
|
||||||
|
.setEmoji("🛑")
|
||||||
|
.setStyle(ButtonStyle.Danger),
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId("pokerCancelKeep")
|
||||||
|
.setLabel("Keep Table")
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return editPokerError(interaction, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction,
|
||||||
|
refreshPokerTableMessage
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerCancelConfirm",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await deferPokerReply(interaction);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(interaction);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content: "❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService().cancelTable({
|
||||||
|
gameId,
|
||||||
|
userId: interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deleteReply();
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
return editPokerError(interaction, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
deferPokerReply
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerCancelKeep",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await deferPokerReply(interaction);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deleteReply();
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
const {
|
||||||
|
cardToString
|
||||||
|
} = require("../../utils/pokerEngine");
|
||||||
|
|
||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
POKER_CARD_RESPONSE_LIFETIME,
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerCards",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const service =
|
||||||
|
getPokerService();
|
||||||
|
|
||||||
|
const [
|
||||||
|
cards,
|
||||||
|
game
|
||||||
|
] =
|
||||||
|
await Promise.all([
|
||||||
|
service.viewCards({
|
||||||
|
gameId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
}),
|
||||||
|
service.getTable({
|
||||||
|
gameId
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
const hand =
|
||||||
|
game.data.hand;
|
||||||
|
|
||||||
|
const player =
|
||||||
|
game.data.players.find(
|
||||||
|
candidate =>
|
||||||
|
String(candidate.userId) ===
|
||||||
|
String(interaction.user.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentPlayer =
|
||||||
|
hand?.players?.find(
|
||||||
|
candidate =>
|
||||||
|
candidate.seat ===
|
||||||
|
hand.currentSeat
|
||||||
|
);
|
||||||
|
|
||||||
|
const isYourTurn =
|
||||||
|
Boolean(
|
||||||
|
player
|
||||||
|
&&
|
||||||
|
hand
|
||||||
|
&&
|
||||||
|
hand.currentSeat ===
|
||||||
|
player.seat
|
||||||
|
);
|
||||||
|
|
||||||
|
const communityCards =
|
||||||
|
hand?.communityCards ?? [];
|
||||||
|
|
||||||
|
const lines = [
|
||||||
|
"### 🃏 Your Cards",
|
||||||
|
`**${cards.map(cardToString).join(" ")}**`,
|
||||||
|
"",
|
||||||
|
"**Community:** " +
|
||||||
|
(communityCards.length
|
||||||
|
? communityCards
|
||||||
|
.map(cardToString)
|
||||||
|
.join(" ")
|
||||||
|
: "No community cards yet."),
|
||||||
|
""
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isYourTurn) {
|
||||||
|
lines.push(
|
||||||
|
"👉 **It's YOUR TURN.**",
|
||||||
|
game.data.turnExpiresAt
|
||||||
|
? `⏱️ Auto-folds <t:${game.data.turnExpiresAt}:R>.`
|
||||||
|
: "⏱️ Your turn timer is active."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (currentPlayer) {
|
||||||
|
lines.push(
|
||||||
|
`Waiting for <@${currentPlayer.userId}> to act.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
lines.push(
|
||||||
|
"The hand is not currently waiting on a player action."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
content:
|
||||||
|
lines.join("\n"),
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(
|
||||||
|
() => {
|
||||||
|
interaction
|
||||||
|
.deleteReply()
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
POKER_CARD_RESPONSE_LIFETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction,
|
||||||
|
refreshPokerTableMessage
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerCashOut",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result =
|
||||||
|
await getPokerService()
|
||||||
|
.cashOut({
|
||||||
|
gameId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
result.game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`✅ Cashed out **${result.amount.toLocaleString()} Cookies** back to your vault.`,
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
executePokerAction
|
||||||
|
} = require("../../utils/pokerInteractions");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerCheck",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return executePokerAction(
|
||||||
|
interaction,
|
||||||
|
{
|
||||||
|
type: "check"
|
||||||
|
},
|
||||||
|
"✅ Checked."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
publishPokerTable,
|
||||||
|
buildOpenTableLink
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerCreate",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const service =
|
||||||
|
getPokerService();
|
||||||
|
|
||||||
|
let game = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
game =
|
||||||
|
await service.createTable({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
channelId:
|
||||||
|
interaction.channelId,
|
||||||
|
ownerId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
const published =
|
||||||
|
await publishPokerTable(
|
||||||
|
interaction,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`✅ Poker table created. **${published.game.data.buyIn.toLocaleString()} Cookies** were moved into your Poker stack.`,
|
||||||
|
embeds: [],
|
||||||
|
components:
|
||||||
|
buildOpenTableLink(
|
||||||
|
published.game
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
if (game) {
|
||||||
|
try {
|
||||||
|
await service.cashOut({
|
||||||
|
gameId:
|
||||||
|
game.gameId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (refundError) {
|
||||||
|
console.error(
|
||||||
|
`[Poker] Failed to refund unpublished table ${game.gameId}:`,
|
||||||
|
refundError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const {
|
||||||
|
executePokerAction
|
||||||
|
} = require("../../utils/pokerInteractions");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerFold",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return executePokerAction(
|
||||||
|
interaction,
|
||||||
|
{
|
||||||
|
type: "fold"
|
||||||
|
},
|
||||||
|
"✅ Folded."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction,
|
||||||
|
refreshPokerTableMessage,
|
||||||
|
buildOpenTableLink
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerJoinTable",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService()
|
||||||
|
.joinTable({
|
||||||
|
gameId,
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`✅ Joined the Poker table for **${game.data.buyIn.toLocaleString()} Cookies**.`,
|
||||||
|
embeds: [],
|
||||||
|
components:
|
||||||
|
buildOpenTableLink(
|
||||||
|
game
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
refreshPokerTableMessage,
|
||||||
|
buildOpenTableLink
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerMyTable",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService()
|
||||||
|
.getMyTable({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ You are not currently seated at a Poker table.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"♠️ Your active Poker table:",
|
||||||
|
embeds: [],
|
||||||
|
components:
|
||||||
|
buildOpenTableLink(
|
||||||
|
game
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const {
|
||||||
|
executeMinimumRaise
|
||||||
|
} = require("../../utils/pokerInteractions");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "pokerRaise",
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return executeMinimumRaise(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction,
|
||||||
|
refreshPokerTableMessage
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerRefresh",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService()
|
||||||
|
.getTable({
|
||||||
|
gameId
|
||||||
|
});
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"✅ Poker table refreshed.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const {
|
||||||
|
getPokerService
|
||||||
|
} = require("../../utils/crumbPoker");
|
||||||
|
|
||||||
|
const {
|
||||||
|
deferPokerReply,
|
||||||
|
editPokerError,
|
||||||
|
getPokerGameIdFromInteraction,
|
||||||
|
refreshPokerTableMessage
|
||||||
|
} = require("../../utils/pokerDiscord");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"pokerStart",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
await deferPokerReply(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const gameId =
|
||||||
|
getPokerGameIdFromInteraction(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!gameId) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
"❌ This Poker table is no longer active.",
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const game =
|
||||||
|
await getPokerService()
|
||||||
|
.startTableHand({
|
||||||
|
gameId,
|
||||||
|
userId:
|
||||||
|
interaction.user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
await refreshPokerTableMessage(
|
||||||
|
interaction.client,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`✅ Hand **#${game.data.handNumber}** started. Cards have been dealt — use **🃏 Cards** to view your hand.`,
|
||||||
|
embeds: [],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
return editPokerError(
|
||||||
|
interaction,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildAchievementsEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileAchievements",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildAchievementsEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"achievements",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildAchievementsEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileAchievementsLeft",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildAchievementsEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
context.page - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"achievements",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildAchievementsEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileAchievementsRight",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildAchievementsEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
context.page + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"achievements",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildBadgesEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileBadges",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildBadgesEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"badges",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildBadgesEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileBadgesLeft",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildBadgesEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
context.page - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"badges",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const {
|
||||||
|
buildBadgesEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
getProfileContextFromFooter,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileBadgesRight",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildBadgesEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId,
|
||||||
|
context.page + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageContext =
|
||||||
|
getProfileContextFromFooter(
|
||||||
|
embed.data.footer?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"badges",
|
||||||
|
pageContext?.page ?? 0,
|
||||||
|
pageContext?.pageCount ?? 1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
const {
|
||||||
|
EmbedBuilder
|
||||||
|
} = require(
|
||||||
|
"discord.js"
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
function delay(
|
||||||
|
ms
|
||||||
|
) {
|
||||||
|
return new Promise(
|
||||||
|
resolve =>
|
||||||
|
setTimeout(
|
||||||
|
resolve,
|
||||||
|
ms
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileClose",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"Profile Closed"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Closing profile..."
|
||||||
|
)
|
||||||
|
],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
|
||||||
|
await delay(1200);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.deleteReply();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
const {
|
||||||
|
buildProfileOverview,
|
||||||
|
buildProfileButtons,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileOverview",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildProfileOverview(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"overview",
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
const {
|
||||||
|
buildStatsEmbed,
|
||||||
|
buildProfileButtons,
|
||||||
|
requireProfileOwner
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name:
|
||||||
|
"profileStats",
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const context =
|
||||||
|
await requireProfileOwner(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user =
|
||||||
|
await interaction.client.users.fetch(
|
||||||
|
context.profileUserId
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildStatsEmbed(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
context.sessionOwnerId
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.update({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"stats",
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder,
|
||||||
|
EmbedBuilder,
|
||||||
|
MessageFlags
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
declareBankruptcy
|
||||||
|
} = require("../../utils/bankruptcy");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("bankruptcy")
|
||||||
|
.setDescription(
|
||||||
|
"Declare bankruptcy at exactly 0 Cookies and receive a starter kit."
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
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:
|
||||||
|
`You may only declare bankruptcy 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 Declared")
|
||||||
|
.setDescription(
|
||||||
|
[
|
||||||
|
`${interaction.user} has been issued a fresh starter kit.`,
|
||||||
|
"",
|
||||||
|
`**+${result.amount.toLocaleString()} Cookies**`,
|
||||||
|
"",
|
||||||
|
"The Cookie economy believes in second chances."
|
||||||
|
].join("\n")
|
||||||
|
)
|
||||||
|
.addFields({
|
||||||
|
name: "Next Eligibility",
|
||||||
|
value: `<t:${result.nextAvailableAt}:R>`
|
||||||
|
});
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
embeds: [embed]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
+875
-438
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
showCrumbSaucer
|
||||||
|
} = require("../../utils/crumbSaucer");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("crumbsaucer")
|
||||||
|
.setDescription(
|
||||||
|
"Visit the Crumb Saucer."
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
return showCrumbSaucer(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder
|
||||||
|
} = require(
|
||||||
|
"discord.js"
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
buildProfileOverview,
|
||||||
|
buildProfileButtons
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileSystem"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data:
|
||||||
|
new SlashCommandBuilder()
|
||||||
|
.setName(
|
||||||
|
"profile"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"View a player profile"
|
||||||
|
)
|
||||||
|
.addUserOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"user"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Player to view"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const user =
|
||||||
|
interaction.options.getUser(
|
||||||
|
"user"
|
||||||
|
) ??
|
||||||
|
interaction.user;
|
||||||
|
|
||||||
|
const sessionOwnerId =
|
||||||
|
interaction.user.id;
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildProfileOverview(
|
||||||
|
interaction.guildId,
|
||||||
|
user,
|
||||||
|
sessionOwnerId
|
||||||
|
);
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
embeds: [embed],
|
||||||
|
components:
|
||||||
|
buildProfileButtons(
|
||||||
|
"overview",
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,629 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder,
|
||||||
|
MessageFlags,
|
||||||
|
EmbedBuilder
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
reconcileUserAchievements,
|
||||||
|
reconcileAllAchievements,
|
||||||
|
grantMaintenanceReward,
|
||||||
|
getMaintenanceRewardStatus,
|
||||||
|
auditProfileData,
|
||||||
|
DEFAULT_MAINTENANCE_REWARD_ID,
|
||||||
|
DEFAULT_MAINTENANCE_REWARD_AMOUNT
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileMaintenance"
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
requireAdminAccess
|
||||||
|
} = require(
|
||||||
|
"../../utils/permissions"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data:
|
||||||
|
new SlashCommandBuilder()
|
||||||
|
.setName(
|
||||||
|
"profileadmin"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Administrative profile maintenance tools"
|
||||||
|
)
|
||||||
|
.addSubcommand(
|
||||||
|
subcommand =>
|
||||||
|
subcommand
|
||||||
|
.setName(
|
||||||
|
"reconcile"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Backfill database-trackable achievements"
|
||||||
|
)
|
||||||
|
.addUserOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"user"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Only reconcile this user; omit for everyone"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addBooleanOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"dry-run"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Show what would change without writing it"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand(
|
||||||
|
subcommand =>
|
||||||
|
subcommand
|
||||||
|
.setName(
|
||||||
|
"audit"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Audit stored profile statistics for inconsistencies"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand(
|
||||||
|
subcommand =>
|
||||||
|
subcommand
|
||||||
|
.setName(
|
||||||
|
"maintenance-reward"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Grant an idempotent maintenance reward to stored profile users"
|
||||||
|
)
|
||||||
|
.addStringOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"reward-id"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
`Unique reward ID; defaults to ${DEFAULT_MAINTENANCE_REWARD_ID}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addIntegerOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"amount"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
`Cookies per player; defaults to ${DEFAULT_MAINTENANCE_REWARD_AMOUNT}`
|
||||||
|
)
|
||||||
|
.setMinValue(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addBooleanOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"dry-run"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Preview recipients without granting Cookies"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand(
|
||||||
|
subcommand =>
|
||||||
|
subcommand
|
||||||
|
.setName(
|
||||||
|
"maintenance-status"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Show maintenance reward claim status"
|
||||||
|
)
|
||||||
|
.addStringOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"reward-id"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
`Reward ID; defaults to ${DEFAULT_MAINTENANCE_REWARD_ID}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* Allows:
|
||||||
|
*
|
||||||
|
* - Discord Administrators
|
||||||
|
* - BOT_AUTHOR_ID from .env
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (
|
||||||
|
!await requireAdminAccess(
|
||||||
|
interaction
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const subcommand =
|
||||||
|
interaction.options.getSubcommand();
|
||||||
|
|
||||||
|
|
||||||
|
await interaction.deferReply({
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
subcommand ===
|
||||||
|
"reconcile"
|
||||||
|
) {
|
||||||
|
const user =
|
||||||
|
interaction.options.getUser(
|
||||||
|
"user"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const dryRun =
|
||||||
|
interaction.options.getBoolean(
|
||||||
|
"dry-run"
|
||||||
|
) ?? false;
|
||||||
|
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
const result =
|
||||||
|
await reconcileUserAchievements({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
|
||||||
|
userId:
|
||||||
|
user.id,
|
||||||
|
|
||||||
|
dryRun
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"🛠️ Profile Reconciliation"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
`${dryRun ? "Dry run for" : "Reconciled"} ${user}.`
|
||||||
|
)
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"New unlocks",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${result.unlocked}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Definitions checked",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${result.checked}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Event-only skipped",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${result.skippedEventOnly}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const summary =
|
||||||
|
await reconcileAllAchievements({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
|
||||||
|
dryRun
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"🛠️ Profile Reconciliation"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
`${dryRun ? "Dry run completed" : "Reconciliation completed"} for stored profile users.`
|
||||||
|
)
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Users",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.users}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"New unlocks",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.unlocked}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Event-only checks skipped",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.skippedEventOnly}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
subcommand ===
|
||||||
|
"maintenance-reward"
|
||||||
|
) {
|
||||||
|
const rewardId =
|
||||||
|
interaction.options.getString(
|
||||||
|
"reward-id"
|
||||||
|
)
|
||||||
|
??
|
||||||
|
DEFAULT_MAINTENANCE_REWARD_ID;
|
||||||
|
|
||||||
|
|
||||||
|
const amount =
|
||||||
|
interaction.options.getInteger(
|
||||||
|
"amount"
|
||||||
|
)
|
||||||
|
??
|
||||||
|
DEFAULT_MAINTENANCE_REWARD_AMOUNT;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Safer admin default:
|
||||||
|
*
|
||||||
|
* If omitted, maintenance-reward previews
|
||||||
|
* instead of paying immediately.
|
||||||
|
*
|
||||||
|
* Pass dry-run:false explicitly to execute.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const dryRun =
|
||||||
|
interaction.options.getBoolean(
|
||||||
|
"dry-run"
|
||||||
|
)
|
||||||
|
??
|
||||||
|
true;
|
||||||
|
|
||||||
|
|
||||||
|
const summary =
|
||||||
|
await grantMaintenanceReward({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
|
||||||
|
rewardId,
|
||||||
|
|
||||||
|
amount,
|
||||||
|
|
||||||
|
dryRun
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
dryRun
|
||||||
|
? "🧪 Maintenance Reward Dry Run"
|
||||||
|
: "🎁 Maintenance Reward"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
dryRun
|
||||||
|
? "No Cookies were granted. Run again with `dry-run:false` to execute this reward."
|
||||||
|
: `Maintenance compensation **${rewardId}** has been processed.`
|
||||||
|
)
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Reward ID",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`\`${summary.rewardId}\``,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Per player",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.amount.toLocaleString()} 🍪**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Stored profiles",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.users}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
dryRun
|
||||||
|
? "Would receive"
|
||||||
|
: "Granted",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${dryRun ? summary.eligible : summary.granted}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Already claimed",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.alreadyClaimed}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Failed",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${summary.failed}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
subcommand ===
|
||||||
|
"maintenance-status"
|
||||||
|
) {
|
||||||
|
const rewardId =
|
||||||
|
interaction.options.getString(
|
||||||
|
"reward-id"
|
||||||
|
)
|
||||||
|
??
|
||||||
|
DEFAULT_MAINTENANCE_REWARD_ID;
|
||||||
|
|
||||||
|
|
||||||
|
const status =
|
||||||
|
await getMaintenanceRewardStatus({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
|
||||||
|
rewardId
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"🎁 Maintenance Reward Status"
|
||||||
|
)
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Reward ID",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`\`${status.rewardId}\``,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Default amount",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${status.amount.toLocaleString()} 🍪**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Stored profiles",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${status.users}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Claimed",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${status.claimed}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Remaining",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${status.remaining}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const audit =
|
||||||
|
await auditProfileData(
|
||||||
|
interaction.guildId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const issuePreview =
|
||||||
|
audit.issues
|
||||||
|
.slice(
|
||||||
|
0,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"🔎 Profile Data Audit"
|
||||||
|
)
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Stats documents",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.statDocuments}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Achievement documents",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.achievementDocuments}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Active achievements",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.activeAchievements}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Global badges",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.badgeDocuments}** (${audit.claimedBadges} claimed)`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Orphan achievement docs",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.orphanAchievementDocs}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:
|
||||||
|
"Issues",
|
||||||
|
|
||||||
|
value:
|
||||||
|
`**${audit.issueCount}**`,
|
||||||
|
|
||||||
|
inline:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
issuePreview
|
||||||
|
? `First issues:\n${issuePreview}`
|
||||||
|
: "No structural inconsistencies found."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (error) {
|
||||||
|
console.error(
|
||||||
|
`[ProfileAdmin] ${subcommand} failed:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
embeds: [
|
||||||
|
new EmbedBuilder()
|
||||||
|
.setTitle(
|
||||||
|
"❌ Profile Admin Error"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"The maintenance action failed. Check the bot logs for details."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
const {
|
||||||
|
buildRecordsEmbed,
|
||||||
|
getRecordChoices
|
||||||
|
} = require(
|
||||||
|
"../../utils/profileRecords"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data:
|
||||||
|
new SlashCommandBuilder()
|
||||||
|
.setName(
|
||||||
|
"records"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"View Olymp Cookie and Crumb Saucer records"
|
||||||
|
)
|
||||||
|
.addStringOption(
|
||||||
|
option =>
|
||||||
|
option
|
||||||
|
.setName(
|
||||||
|
"category"
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
"Record category"
|
||||||
|
)
|
||||||
|
.setRequired(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.addChoices(
|
||||||
|
...getRecordChoices()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
interaction
|
||||||
|
) {
|
||||||
|
const category =
|
||||||
|
interaction.options.getString(
|
||||||
|
"category",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const embed =
|
||||||
|
await buildRecordsEmbed({
|
||||||
|
guildId:
|
||||||
|
interaction.guildId,
|
||||||
|
category,
|
||||||
|
botUserId:
|
||||||
|
interaction.client.user.id,
|
||||||
|
limit: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
embeds: [embed]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
const {
|
||||||
|
SlashCommandBuilder,
|
||||||
|
MessageFlags
|
||||||
|
} = require("discord.js");
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CONFIG
|
||||||
|
*/
|
||||||
|
|
||||||
|
const POKE_MAX_TIMES = 5;
|
||||||
|
const POKE_DELAY_MS = 750;
|
||||||
|
const POKE_COOLDOWN_MS = 30_000;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In-memory cooldowns.
|
||||||
|
*
|
||||||
|
* Key:
|
||||||
|
* Discord user ID of the person sending the poke.
|
||||||
|
*
|
||||||
|
* Value:
|
||||||
|
* Timestamp when they may poke again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const pokeCooldowns = new Map();
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("fun")
|
||||||
|
.setDescription("Fun commands.")
|
||||||
|
|
||||||
|
.addSubcommand(subcommand =>
|
||||||
|
subcommand
|
||||||
|
.setName("poke")
|
||||||
|
.setDescription(
|
||||||
|
"Poke another server member in DMs."
|
||||||
|
)
|
||||||
|
|
||||||
|
.addUserOption(option =>
|
||||||
|
option
|
||||||
|
.setName("member")
|
||||||
|
.setDescription(
|
||||||
|
"The member you want to poke."
|
||||||
|
)
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
|
||||||
|
.addIntegerOption(option =>
|
||||||
|
option
|
||||||
|
.setName("times")
|
||||||
|
.setDescription(
|
||||||
|
`How many times to poke them. Max ${POKE_MAX_TIMES}.`
|
||||||
|
)
|
||||||
|
.setMinValue(1)
|
||||||
|
.setMaxValue(
|
||||||
|
POKE_MAX_TIMES
|
||||||
|
)
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
switch (
|
||||||
|
interaction.options.getSubcommand()
|
||||||
|
) {
|
||||||
|
case "poke":
|
||||||
|
return poke(
|
||||||
|
interaction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* POKE
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function poke(interaction) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Guild only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!interaction.guild) {
|
||||||
|
return interaction.reply({
|
||||||
|
content:
|
||||||
|
"This command can only be used inside a server.",
|
||||||
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Options
|
||||||
|
*/
|
||||||
|
|
||||||
|
const user =
|
||||||
|
interaction.options.getUser(
|
||||||
|
"member",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const times =
|
||||||
|
interaction.options.getInteger(
|
||||||
|
"times"
|
||||||
|
)
|
||||||
|
?? 1;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't poke yourself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (
|
||||||
|
user.id ===
|
||||||
|
interaction.user.id
|
||||||
|
) {
|
||||||
|
return interaction.reply({
|
||||||
|
content:
|
||||||
|
"You can't poke yourself! 👉",
|
||||||
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't poke bots.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (user.bot) {
|
||||||
|
return interaction.reply({
|
||||||
|
content:
|
||||||
|
"You can't poke a bot!",
|
||||||
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the target is actually
|
||||||
|
* a member of this guild.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let member;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
member =
|
||||||
|
await interaction.guild
|
||||||
|
.members
|
||||||
|
.fetch(
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
return interaction.reply({
|
||||||
|
content:
|
||||||
|
"I couldn't find that member in this server.",
|
||||||
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* COOLDOWN
|
||||||
|
*/
|
||||||
|
|
||||||
|
const now =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
const cooldownUntil =
|
||||||
|
pokeCooldowns.get(
|
||||||
|
interaction.user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
cooldownUntil &&
|
||||||
|
cooldownUntil > now
|
||||||
|
) {
|
||||||
|
const remaining =
|
||||||
|
Math.ceil(
|
||||||
|
(
|
||||||
|
cooldownUntil -
|
||||||
|
now
|
||||||
|
)
|
||||||
|
/
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
content:
|
||||||
|
`You need to wait **${remaining}s** before poking someone again.`,
|
||||||
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defer before sending DMs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
await interaction.deferReply({
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set cooldown before sending.
|
||||||
|
*
|
||||||
|
* This prevents repeated invocations
|
||||||
|
* while the DM loop is running.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pokeCooldowns.set(
|
||||||
|
interaction.user.id,
|
||||||
|
now + POKE_COOLDOWN_MS
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sender name.
|
||||||
|
*
|
||||||
|
* Prefer their server display name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let senderName =
|
||||||
|
interaction.user.displayName;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const senderMember =
|
||||||
|
await interaction.guild
|
||||||
|
.members
|
||||||
|
.fetch(
|
||||||
|
interaction.user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
senderName =
|
||||||
|
senderMember.displayName;
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
/*
|
||||||
|
* interaction.user.displayName
|
||||||
|
* remains the fallback.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SEND POKES
|
||||||
|
*/
|
||||||
|
|
||||||
|
let sent =
|
||||||
|
0;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < times;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
|
||||||
|
await member.send({
|
||||||
|
content:
|
||||||
|
`👉 **${senderName} poked you!**\n` +
|
||||||
|
`From **${interaction.guild.name}**`
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
sent++;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't sleep after the final DM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (
|
||||||
|
i <
|
||||||
|
times - 1
|
||||||
|
) {
|
||||||
|
await sleep(
|
||||||
|
POKE_DELAY_MS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to poke ${user.tag}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no messages made it through,
|
||||||
|
* this is most likely closed DMs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (sent === 0) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`I couldn't poke ${user}. They may have DMs disabled.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some messages succeeded before
|
||||||
|
* Discord rejected a later one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
`I poked ${user} **${sent}/${times}** times before I couldn't send any more.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SUCCESS
|
||||||
|
*/
|
||||||
|
|
||||||
|
return interaction.editReply({
|
||||||
|
content:
|
||||||
|
times === 1
|
||||||
|
? `👉 Poked ${user}!`
|
||||||
|
: `👉 Poked ${user} **${times} times!**`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SLEEP
|
||||||
|
*/
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(
|
||||||
|
resolve =>
|
||||||
|
setTimeout(
|
||||||
|
resolve,
|
||||||
|
ms
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user