Compare commits

..

3 Commits

Author SHA1 Message Date
DeSqBlocki c76680293e rai if you are reading this im getting you a monster 2026-08-14 22:54:03 +02:00
DeSqBlocki 912b1b6ceb rewrote cookiesgit add commands/applications/random.js 2026-08-14 22:52:44 +02:00
DeSqBlocki ad7b3c6359 added local assets for classes/jobs 2026-08-14 22:52:24 +02:00
80 changed files with 2035 additions and 3338 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

+40
View File
@@ -0,0 +1,40 @@
require("dotenv").config();
const { REST, Routes } = require("discord.js");
const rest = new REST({ version: "10" })
.setToken(process.env.D_TOKEN);
async function clearCommands() {
try {
if (!process.env.D_TOKEN)
throw new Error("Missing D_TOKEN environment variable.");
if (!process.env.D_ID)
throw new Error("Missing D_ID environment variable.");
if (!process.env.D_GID)
throw new Error("Missing G_GID environment variable.");
console.log("Clearing guild application commands...");
await rest.put(
Routes.applicationGuildCommands(
process.env.D_ID,
process.env.D_GID
),
{
body: []
}
);
console.log("Successfully cleared all guild application commands.");
} catch (error) {
console.error("Failed to clear application commands:");
console.error(error);
process.exitCode = 1;
}
}
clearCommands();
+844
View File
@@ -0,0 +1,844 @@
const {
SlashCommandBuilder,
EmbedBuilder
} = require("discord.js");
const { mClient } = require("../..");
const DATABASE = "neo-db";
const COOKIE_COLLECTION = "items_cookies";
const COOLDOWN_COLLECTION = "cooldowns_cookies";
const COLLECT_COOLDOWN = 60 * 60; // 1 hour
const STEAL_COOLDOWN = 15 * 60; // 15 minutes
const STEAL_SUCCESS_CHANCE = 0.50;
const ABRA_ID = "378135175059603457";
const ABRA_MULTIPLIER = 1.5;
module.exports = {
data: new SlashCommandBuilder()
.setName("cookies")
.setDescription("The ShaiBot cookie economy.")
.addSubcommand(subcommand =>
subcommand
.setName("collect")
.setDescription("Collect a fresh batch of cookies.")
)
.addSubcommand(subcommand =>
subcommand
.setName("steal")
.setDescription("Attempt to steal another user's cookies.")
.addUserOption(option =>
option
.setName("target")
.setDescription("The unfortunate victim.")
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName("check")
.setDescription("Check a cookie balance.")
.addUserOption(option =>
option
.setName("target")
.setDescription("The user whose cookies you want to check.")
)
)
.addSubcommand(subcommand =>
subcommand
.setName("give")
.setDescription("Give some of your cookies to another user.")
.addUserOption(option =>
option
.setName("target")
.setDescription("The recipient.")
.setRequired(true)
)
.addIntegerOption(option =>
option
.setName("amount")
.setDescription("How many cookies to give.")
.setRequired(true)
.setMinValue(1)
)
)
.addSubcommand(subcommand =>
subcommand
.setName("leaderboard")
.setDescription("Show the richest cookie collectors.")
),
async execute(interaction) {
switch (interaction.options.getSubcommand()) {
case "collect":
return collect(interaction);
case "steal":
return steal(interaction);
case "check":
return check(interaction);
case "give":
return give(interaction);
case "leaderboard":
return leaderboard(interaction);
default:
return interaction.reply({
content: "Unknown subcommand.",
ephemeral: true
});
}
}
};
/*
* DATABASE
*/
function getCollections() {
const db = mClient.db(DATABASE);
return {
cookies: db.collection(COOKIE_COLLECTION),
cooldowns: db.collection(COOLDOWN_COLLECTION)
};
}
/*
* COOKIE HELPERS
*/
async function getCookieBalance(userId) {
const { cookies } = getCollections();
const result = await cookies.findOne(
{
userId
},
{
projection: {
_id: 0,
cookies: 1
}
}
);
return result?.cookies ?? 0;
}
async function changeCookies(userId, amount, session = undefined) {
const { cookies } = getCollections();
return cookies.updateOne(
{
userId
},
{
$inc: {
cookies: amount
},
$setOnInsert: {
userId
}
},
{
upsert: true,
session
}
);
}
/*
* COOKIE TRANSFER
*
* This uses a transaction so both balances update together.
*
* MongoDB transactions require Atlas or a replica set.
*/
async function transferCookies(fromUserId, toUserId, amount) {
const { cookies } = getCollections();
const session = mClient.startSession();
try {
await session.withTransaction(async () => {
/*
* Only debit if the sender still has enough cookies.
*
* This protects against two commands being executed
* at almost the same time.
*/
const debit = await cookies.updateOne(
{
userId: fromUserId,
cookies: {
$gte: amount
}
},
{
$inc: {
cookies: -amount
}
},
{
session
}
);
if (debit.modifiedCount !== 1) {
throw new Error("INSUFFICIENT_COOKIES");
}
await cookies.updateOne(
{
userId: toUserId
},
{
$inc: {
cookies: amount
},
$setOnInsert: {
userId: toUserId
}
},
{
upsert: true,
session
}
);
});
} finally {
await session.endSession();
}
}
/*
* COOLDOWN HELPERS
*
* cooldowns_cookies:
*
* {
* userId: "...",
* guildId: "...",
* collect: 1786741200,
* steal: 1786738500
* }
*/
async function getCooldown(userId, guildId, type) {
const { cooldowns } = getCollections();
const result = await cooldowns.findOne(
{
userId,
guildId
},
{
projection: {
_id: 0,
[type]: 1
}
}
);
return result?.[type] ?? 0;
}
async function setCooldown(userId, guildId, type, duration) {
const { cooldowns } = getCollections();
const now = getUnixTime();
const next = now + duration;
await cooldowns.updateOne(
{
userId,
guildId
},
{
$set: {
[type]: next
},
$setOnInsert: {
userId,
guildId
}
},
{
upsert: true
}
);
return next;
}
/*
* /cookies collect
*/
async function collect(interaction) {
const userId = interaction.user.id;
const guildId = interaction.guildId;
const now = getUnixTime();
const cooldown = await getCooldown(
userId,
guildId,
"collect"
);
if (cooldown > now) {
return interaction.reply({
content:
`The batch is still cooking. Come back <t:${cooldown}:R> 🍪`,
ephemeral: true
});
}
let amount = randomInteger(1, 20);
/*
* Legacy Abra bonus.
*/
if (userId === ABRA_ID) {
amount = Math.ceil(
amount * ABRA_MULTIPLIER
);
}
await changeCookies(
userId,
amount
);
await setCooldown(
userId,
guildId,
"collect",
COLLECT_COOLDOWN
);
return interaction.reply({
content: randomCollectResponse(
amount,
interaction.user
)
});
}
/*
* /cookies steal
*/
async function steal(interaction) {
const user = interaction.user;
const target = interaction.options.getUser(
"target",
true
);
/*
* Can't rob yourself.
*/
if (target.id === user.id) {
return interaction.reply({
content:
"C'mon, don't embarrass yourself. You can't steal from yourself.",
ephemeral: true
});
}
/*
* Don't let people farm bot accounts.
*/
if (target.bot) {
return interaction.reply({
content:
"Bots have highly sophisticated cookie security systems.",
ephemeral: true
});
}
const now = getUnixTime();
/*
* Check steal cooldown before doing anything expensive.
*/
const cooldown = await getCooldown(
user.id,
interaction.guildId,
"steal"
);
if (cooldown > now) {
return interaction.reply({
content:
`Lay low for a while. You can steal again <t:${cooldown}:R>.`,
ephemeral: true
});
}
/*
* Fetch both balances concurrently.
*/
const [
yourCookies,
theirCookies
] = await Promise.all([
getCookieBalance(user.id),
getCookieBalance(target.id)
]);
if (yourCookies < 1) {
return interaction.reply({
content:
"You need at least **1 Cookie** to attempt a robbery.",
ephemeral: true
});
}
if (theirCookies < 1) {
return interaction.reply({
content:
`Shame on you! ${target} doesn't have any Cookies. D:`,
ephemeral: true
});
}
/*
* Chaos mode:
* fixed 50% steal chance.
*/
const success =
Math.random() < STEAL_SUCCESS_CHANCE;
/*
* Successful robbery.
*/
if (success) {
const maxSteal = Math.max(
1,
Math.ceil(theirCookies / 4)
);
const amount = Math.min(
theirCookies,
randomInteger(
1,
maxSteal
)
);
try {
await transferCookies(
target.id,
user.id,
amount
);
} catch (error) {
if (error.message === "INSUFFICIENT_COOKIES") {
return interaction.reply({
content:
`${target}'s Cookie balance changed before you could finish the robbery.`,
ephemeral: true
});
}
throw error;
}
const nextSteal = await setCooldown(
user.id,
interaction.guildId,
"steal",
STEAL_COOLDOWN
);
return interaction.reply({
content:
`${randomStealSuccessResponse(amount, target)}\n` +
`**Success Chance:** ${formatPercentage(STEAL_SUCCESS_CHANCE)} • ` +
`Next robbery <t:${nextSteal}:R>`
});
}
/*
* Failed robbery.
*
* Lose between 1 and roughly 1/6 of your balance.
*/
const maxPenalty = Math.max(
1,
Math.ceil(yourCookies / 6)
);
const penalty = Math.min(
yourCookies,
randomInteger(
1,
maxPenalty
)
);
try {
await transferCookies(
user.id,
target.id,
penalty
);
} catch (error) {
if (error.message === "INSUFFICIENT_COOKIES") {
return interaction.reply({
content:
"Your Cookie balance changed before the robbery could finish.",
ephemeral: true
});
}
throw error;
}
const nextSteal = await setCooldown(
user.id,
interaction.guildId,
"steal",
STEAL_COOLDOWN
);
return interaction.reply({
content:
`${randomStealFailResponse(penalty, target)}\n` +
`**Success Chance:** ${formatPercentage(STEAL_SUCCESS_CHANCE)} • ` +
`Next robbery <t:${nextSteal}:R>`
});
}
/*
* /cookies check
*/
async function check(interaction) {
const target =
interaction.options.getUser("target")
?? interaction.user;
const balance =
await getCookieBalance(target.id);
const mood =
balance === 0
? "😔"
: "🍪";
return interaction.reply({
content:
`${target} has **${balance.toLocaleString()} Cookie${balance === 1 ? "" : "s"}**! ${mood}`
});
}
/*
* /cookies give
*/
async function give(interaction) {
const user = interaction.user;
const target = interaction.options.getUser(
"target",
true
);
const amount = interaction.options.getInteger(
"amount",
true
);
if (target.id === user.id) {
return interaction.reply({
content:
"You cannot give Cookies to yourself.",
ephemeral: true
});
}
if (target.bot) {
return interaction.reply({
content:
"Bots don't need Cookies. Allegedly.",
ephemeral: true
});
}
try {
await transferCookies(
user.id,
target.id,
amount
);
} catch (error) {
if (error.message === "INSUFFICIENT_COOKIES") {
return interaction.reply({
content:
"We don't run a charity here. You don't have enough Cookies!",
ephemeral: true
});
}
throw error;
}
return interaction.reply({
content:
`${user} shared **${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}** with ${target} 🍪`
});
}
/*
* /cookies leaderboard
*/
async function leaderboard(interaction) {
const { cookies } = getCollections();
const results = await cookies
.find({
cookies: {
$gt: 0
}
})
.sort({
cookies: -1
})
.limit(10)
.toArray();
const description = results.length
? results
.map((record, index) => {
const medal = getLeaderboardMedal(index);
return (
`${medal} <@${record.userId}> — ` +
`**${record.cookies.toLocaleString()}** 🍪`
);
})
.join("\n")
: "Nobody has collected any Cookies yet.";
const embed = new EmbedBuilder()
.setTitle("🍪 Cookie Leaderboard")
.setDescription(description)
.setTimestamp();
if (interaction.guild?.iconURL()) {
embed.setThumbnail(
interaction.guild.iconURL({
size: 256
})
);
}
return interaction.reply({
embeds: [embed]
});
}
/*
* COLLECT RESPONSES
*/
function randomCollectResponse(amount, user) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`${user}, I made you ${cookies} 🍪`,
`${user}, here's your ${cookies} 🍪 I ate the rest, sorry >.<`,
`Only ${cookies} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a glass of milk with your ${cookies}? 🥛`,
`${cookies} 🍪 Careful, ${user}! They're still warm :3`,
`${cookies} 🍪 Best batch yet!`,
`${user}, these ${cookies} I bequeath unto thine own self upon this day.`,
`${user}, your ${cookies} are fresh out of the oven. Try not to burn your tongue!`,
`${user}, I bribed the Cookie Monster to spare these ${cookies} just for you.`,
`${user}, Santa came early! You got ${cookies}. Milk not included.`,
`${user}, your ${cookies} are artisanal, gluten-free, and slightly cursed.`,
`A wild baker appeared! They hand ${user} ${cookies} 🍪`,
`${user}, Abra said these ${cookies} are "totally not stolen"... 🤔`,
`${user}, here's your ${cookies} — watch out, they may be booby-trapped.`
];
return randomItem(responses);
}
/*
* STEAL SUCCESS RESPONSES
*/
function randomStealSuccessResponse(amount, target) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`You've successfully stolen ${cookies} from ${target}!`,
`Ninja mode activated. You snatched ${cookies} from ${target} without leaving a crumb behind.`,
`Abra taught you well — ${cookies} from ${target} are now yours!`,
`Like a phantom in the night, you took ${cookies} from ${target}.`,
`You just pulled the sweetest heist: ${cookies} from ${target}!`,
`Mission complete. ${target} is ${cookies} poorer.`,
`You distracted ${target} with a dance and swiped ${cookies}.`,
`Clean getaway! ${target} won't notice the missing ${cookies} until it's too late.`,
`The Cookie Vault has been breached. ${cookies} taken from ${target}.`,
`You emerged from the shadows carrying ${cookies} from ${target}.`
];
return randomItem(responses);
}
/*
* STEAL FAILURE RESPONSES
*/
function randomStealFailResponse(amount, target) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`You were caught red-handed by ${target} and lost ${cookies} as a penalty!`,
`Fail! ${target} caught you mid-bite — you dropped ${cookies}.`,
`Not your best moment... You lost ${cookies} to ${target} in the struggle.`,
`Stealth level: 0. You handed ${cookies} to ${target} as an apology.`,
`The Cookie jar was booby-trapped! You gave ${cookies} to ${target} while running away.`,
`Ouch. The guard dog barked, and now ${target} has your ${cookies}.`,
`You tripped over your own shoelace — ${target} picked up ${cookies}.`,
`Caught by the Cookie Police! ${cookies} confiscated and given to ${target}.`,
`Your pockets had holes... ${target} found the ${cookies} you dropped.`
];
return randomItem(responses);
}
/*
* GENERIC HELPERS
*/
function getUnixTime() {
return Math.floor(
Date.now() / 1000
);
}
function randomInteger(min, max) {
return Math.floor(
Math.random() * (max - min + 1)
) + min;
}
function randomItem(items) {
return items[
Math.floor(
Math.random() * items.length
)
];
}
function formatPercentage(value) {
return `${Math.round(value * 100)}%`;
}
function getLeaderboardMedal(index) {
switch (index) {
case 0:
return "🥇";
case 1:
return "🥈";
case 2:
return "🥉";
default:
return `**${index + 1}.**`;
}
}
+557
View File
@@ -0,0 +1,557 @@
const {
SlashCommandBuilder,
EmbedBuilder,
AttachmentBuilder
} = require("discord.js");
const { mClient } = require("../..");
const path = require("node:path");
const jobIconDirectory = path.resolve(
__dirname,
"../../assets/jobs"
);
const expansions = [
{ name: "A Realm Reborn", value: 2 },
{ name: "Heavensward", value: 3 },
{ name: "Stormblood", value: 4 },
{ name: "Shadowbringers", value: 5 },
{ name: "Endwalker", value: 6 },
{ name: "Dawntrail", value: 7 }
];
const dutyTypes = [
{ name: "Dungeon", value: "Dungeon" },
{ name: "Trial", value: "Trial" },
{ name: "Raid", value: "Raid" },
{ name: "Alliance Raid", value: "AllianceRaid" },
{ name: "Guildhest", value: "Guildhest" },
{ name: "Variant Dungeon", value: "VariantDungeon" },
{ name: "Criterion", value: "Criterion" }
];
const dutyDifficulties = [
{ name: "Normal", value: "Normal" },
{ name: "Hard", value: "Hard" },
{ name: "Extreme", value: "Extreme" },
{ name: "Unreal", value: "Unreal" },
{ name: "Savage", value: "Savage" },
{ name: "Ultimate", value: "Ultimate" },
{ name: "Chaotic", value: "Chaotic" }
];
const roulettes = [
{ name: "Leveling", value: "Leveling" },
{ name: "High-level Dungeons", value: "High-level Dungeons" },
{ name: "Level Cap Dungeons", value: "Level Cap Dungeons" },
{ name: "Trials", value: "Trials" },
{ name: "Normal Raids", value: "Normal Raids" },
{ name: "Alliance Raids", value: "Alliance Raids" },
{ name: "Main Scenario", value: "Main Scenario" },
{ name: "Guildhests", value: "Guildhests" },
{ name: "Mentor", value: "Mentor" }
];
const roles = [
{ name: "Tank", value: "Tank" },
{ name: "Healer", value: "Healer" },
{ name: "DPS", value: "DPS" }
];
const roleTypes = [
{ name: "Tank", value: "Tank" },
{ name: "Pure Healer", value: "Pure" },
{ name: "Shield Healer", value: "Shield" },
{ name: "Melee DPS", value: "Melee" },
{ name: "Physical Ranged DPS", value: "PhysicalRanged" },
{ name: "Magical Ranged DPS", value: "MagicalRanged" }
];
const damageTypes = [
{ name: "Physical", value: "Physical" },
{ name: "Magical", value: "Magical" }
];
function getJobIcon(job) {
const fileName = `${job.Name.replace(/\s+/g, "")}.png`;
return path.join(
jobIconDirectory,
fileName
);
}
const dutyIcons = {
Guildhest:
"https://ffxiv.consolegameswiki.com/mediawiki/images/6/61/Guildhest.png",
Dungeon:
"https://ffxiv.consolegameswiki.com/mediawiki/images/a/ab/Dungeon.png",
Trial:
"https://ffxiv.consolegameswiki.com/mediawiki/images/0/0d/Trial.png",
Raid:
"https://ffxiv.consolegameswiki.com/mediawiki/images/d/d6/Raid.png",
AllianceRaid:
"https://ffxiv.consolegameswiki.com/mediawiki/images/d/d6/Raid.png",
VariantDungeon:
"https://ffxiv.consolegameswiki.com/mediawiki/images/5/5e/Variant_and_criterion_dungeons_icon1.png",
Criterion:
"https://ffxiv.consolegameswiki.com/mediawiki/images/5/5e/Variant_and_criterion_dungeons_icon1.png"
};
const dutyDifficultyIcons = {
Ultimate:
"https://ffxiv.consolegameswiki.com/mediawiki/images/8/8a/Ultimate_Raid.png",
Chaotic:
"https://ffxiv.consolegameswiki.com/mediawiki/images/a/a9/Chaotic_Alliance_Raid.png"
};
module.exports = {
data: new SlashCommandBuilder()
.setName("random")
.setDescription("Randomly select FFXIV content.")
.addSubcommand(subcommand =>
subcommand
.setName("duty")
.setDescription("Select a random duty to complete.")
.addIntegerOption(option =>
option
.setName("level")
.setDescription("Your current level.")
.setRequired(true)
.setMinValue(10)
.setMaxValue(100)
)
.addStringOption(option =>
option
.setName("type")
.setDescription("Only select a specific duty type.")
.addChoices(...dutyTypes)
)
.addStringOption(option =>
option
.setName("difficulty")
.setDescription("Only select a specific difficulty.")
.addChoices(...dutyDifficulties)
)
.addIntegerOption(option =>
option
.setName("expansion")
.setDescription("Only select duties from an expansion.")
.addChoices(...expansions)
)
.addStringOption(option =>
option
.setName("roulette")
.setDescription("Only select duties belonging to a roulette.")
.addChoices(...roulettes)
)
.addIntegerOption(option =>
option
.setName("party-size")
.setDescription("Only select duties with this party size.")
.addChoices(
{ name: "4 Players", value: 4 },
{ name: "8 Players", value: 8 },
{ name: "24 Players", value: 24 }
)
)
.addBooleanOption(option =>
option
.setName("duty-support")
.setDescription("Filter by Duty Support availability.")
)
.addBooleanOption(option =>
option
.setName("trusts")
.setDescription("Filter by Trust availability.")
)
)
.addSubcommand(subcommand =>
subcommand
.setName("class")
.setDescription("Select a random class or job.")
.addStringOption(option =>
option
.setName("role")
.setDescription("Only select a specific role.")
.addChoices(...roles)
)
.addStringOption(option =>
option
.setName("role-type")
.setDescription("Only select a specific role subtype.")
.addChoices(...roleTypes)
)
.addStringOption(option =>
option
.setName("damage-type")
.setDescription("Filter by physical or magical damage.")
.addChoices(...damageTypes)
)
.addIntegerOption(option =>
option
.setName("expansion")
.setDescription("Only select classes/jobs from an expansion.")
.addChoices(...expansions)
)
.addBooleanOption(option =>
option
.setName("jobstone")
.setDescription("True for jobs, false for base classes.")
)
.addBooleanOption(option =>
option
.setName("limited")
.setDescription("Filter by limited-job status.")
)
.addIntegerOption(option =>
option
.setName("max-starting-level")
.setDescription("Maximum starting level of the class/job.")
.setMinValue(1)
.setMaxValue(100)
)
),
async execute(interaction) {
switch (interaction.options.getSubcommand()) {
case "duty":
return randomDuty(interaction);
case "class":
return randomClass(interaction);
}
}
};
async function randomDuty(interaction) {
const collection = mClient
.db("neo-db")
.collection("items_duties");
const level = interaction.options.getInteger("level");
const type = interaction.options.getString("type");
const difficulty = interaction.options.getString("difficulty");
const expansion = interaction.options.getInteger("expansion");
const roulette = interaction.options.getString("roulette");
const partySize = interaction.options.getInteger("party-size");
const dutySupport = interaction.options.getBoolean("duty-support");
const trusts = interaction.options.getBoolean("trusts");
const query = {
MinimumLevel: {
$lte: level
}
};
if (type !== null)
query.Type = type;
if (difficulty !== null)
query.Difficulty = difficulty;
if (expansion !== null)
query.Expansion = expansion;
if (roulette !== null)
query.Roulettes = roulette;
if (partySize !== null)
query["Party.Size"] = partySize;
if (dutySupport !== null)
query["Party.CanUseDutySupport"] = dutySupport;
if (trusts !== null)
query["Party.CanUseTrusts"] = trusts;
const [duty] = await collection.aggregate([
{
$match: query
},
{
$sample: {
size: 1
}
}
]).toArray();
if (!duty) {
return interaction.reply({
content: "No duties matched those filters.",
ephemeral: true
});
}
const embed = new EmbedBuilder()
.setTitle(duty.Name)
.setThumbnail(getDutyIcon(duty))
.setDescription(
[
`**${formatDutyType(duty.Type)}**`,
`${duty.Difficulty} Difficulty`,
`${getExpansionName(duty.Expansion)} • Patch ${formatPatch(duty)}`
].join("\n")
)
.addFields(
{
name: "Level",
value: `${duty.MinimumLevel}`,
inline: true
},
{
name: "Level Sync",
value: `${duty.LevelSync}`,
inline: true
},
{
name: "Minimum Item Level",
value: duty.MinimumItemLevel
? `${duty.MinimumItemLevel}`
: "None",
inline: true
},
{
name: "Party",
value: formatParty(duty.Party)
},
{
name: "Roulettes",
value: duty.Roulettes?.length
? duty.Roulettes.join(", ")
: "None"
},
{
name: "NPC Support",
value: [
`Duty Support: ${duty.Party.CanUseDutySupport ? "Yes" : "No"}`,
`Trusts: ${duty.Party.CanUseTrusts ? "Yes" : "No"}`
].join("\n")
}
);
return interaction.reply({
embeds: [embed]
});
}
async function randomClass(interaction) {
const collection = mClient
.db("neo-db")
.collection("items_classes");
const role = interaction.options.getString("role");
const roleType = interaction.options.getString("role-type");
const damageType = interaction.options.getString("damage-type");
const expansion = interaction.options.getInteger("expansion");
const hasJobStone = interaction.options.getBoolean("jobstone");
const limited = interaction.options.getBoolean("limited");
const maxStartingLevel =
interaction.options.getInteger("max-starting-level");
const query = {};
if (role !== null)
query.Role = role;
if (roleType !== null)
query.RoleType = roleType;
if (damageType !== null)
query.DamageType = damageType;
if (expansion !== null)
query.Expansion = expansion;
if (hasJobStone !== null)
query.HasJobStone = hasJobStone;
if (limited !== null)
query.Limited = limited;
if (maxStartingLevel !== null) {
query.StartingLevel = {
$lte: maxStartingLevel
};
}
const [job] = await collection.aggregate([
{
$match: query
},
{
$sample: {
size: 1
}
}
]).toArray();
if (!job) {
return interaction.reply({
content: "No classes or jobs matched those filters.",
ephemeral: true
});
}
const icon = new AttachmentBuilder(
getJobIcon(job),
{
name: `${job.Abbreviation}.png`
}
);
const embed = new EmbedBuilder()
.setTitle(`${job.Name} (${job.Abbreviation})`)
.setThumbnail(`attachment://${job.Abbreviation}.png`)
.setDescription(
[
`**${job.Role} • ${formatRoleType(job.RoleType)}**`,
`${job.DamageType} Damage`,
`${getExpansionName(job.Expansion)} • Patch ${formatPatch(job)}`
].join("\n")
)
.addFields(
{
name: "Starting Level",
value: `${job.StartingLevel}`,
inline: true
},
{
name: "Job Stone",
value: job.HasJobStone ? "Yes" : "No",
inline: true
},
{
name: "Limited Job",
value: job.Limited ? "Yes" : "No",
inline: true
},
{
name: "Base Class",
value: job.Class ?? "None",
inline: true
}
);
return interaction.reply({
embeds: [embed],
files: [icon]
});
}
function getDutyIcon(duty) {
return dutyDifficultyIcons[duty.Difficulty]
?? dutyIcons[duty.Type]
?? null;
}
function getExpansionName(expansion) {
return expansions.find(entry =>
entry.value === expansion
)?.name ?? `Expansion ${expansion}`;
}
function formatPatch(item) {
if (item.MinorPatch) {
return `${item.Expansion}.${item.MajorPatch}${item.MinorPatch}`;
}
return `${item.Expansion}.${item.MajorPatch}`;
}
function formatDutyType(type) {
switch (type) {
case "AllianceRaid":
return "Alliance Raid";
case "VariantDungeon":
return "Variant Dungeon";
default:
return type;
}
}
function formatRoleType(roleType) {
switch (roleType) {
case "PhysicalRanged":
return "Physical Ranged";
case "MagicalRanged":
return "Magical Ranged";
case "Pure":
return "Pure Healer";
case "Shield":
return "Shield Healer";
default:
return roleType;
}
}
function formatParty(party) {
const roles = [];
if (party.Tanks > 0) {
roles.push(
`${party.Tanks} Tank${party.Tanks !== 1 ? "s" : ""}`
);
}
if (party.Healers > 0) {
roles.push(
`${party.Healers} Healer${party.Healers !== 1 ? "s" : ""}`
);
}
if (party.DPS > 0) {
roles.push(`${party.DPS} DPS`);
}
if (!roles.length) {
return `${party.Size} Player${party.Size !== 1 ? "s" : ""}`;
}
return `${party.Size} Players\n${roles.join(" • ")}`;
}
-312
View File
@@ -1,312 +0,0 @@
// cookies.js — rewritten & rebalanced
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const cookieSchema = require("../schemas/cookieSchema"); // expected to support findOne, findOneAndUpdate, updateOne, find
const cooldownSchema = require("../schemas/cooldownSchema"); // expected to support findOne, findOneAndUpdate
const Discord = require('discord.js');
const URI = process.env.URI;
if (!URI) throw new Error("Missing process.env.URI for MongoDB");
// single client reused
const client = new MongoClient(URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
client.connect().catch(err => {
console.error("Mongo connect error:", err);
});
// util
const msToTime = (duration) => {
const ms = Math.floor((duration % 1000) / 100);
const s = Math.floor((duration / 1000) % 60);
const m = Math.floor((duration / (1000 * 60)) % 60);
const h = Math.floor((duration / (1000 * 60 * 60)) % 24);
const pad = n => (n < 10 ? "0" + n : "" + n);
return `${pad(h)}:${pad(m)}:${pad(s)}`;
};
const delay = ms => new Promise(r => setTimeout(r, ms));
// --- Helpers ---
async function getCookies(userId) {
const rec = await cookieSchema.findOne({ _id: userId });
return rec && typeof rec.cookies === 'number' ? rec.cookies : 0;
}
async function changeCookies(userId, delta, username = null) {
// upsert and set username if provided
const update = { $inc: { cookies: delta } };
if (username) update.$set = { user: username };
return cookieSchema.findOneAndUpdate(
{ _id: userId },
update,
{ upsert: true, returnDocument: 'after' }
);
}
async function getCooldown(uniqueId) {
const rec = await cooldownSchema.findOne({ _id: uniqueId });
return rec && rec.cooldown ? rec.cooldown : 0;
}
async function setCooldown(uniqueId, command, subcommand, msTimestamp) {
await cooldownSchema.findOneAndUpdate(
{ _id: uniqueId },
{ _id: uniqueId, command, subcommand, cooldown: msTimestamp },
{ upsert: true }
);
}
function cooldownId(guildId, userId, command, subcommand) {
return `${guildId}-${userId}-${command}-${subcommand}`;
}
// --- Game logic & rebalances ---
// Notes about balancing:
// - Collect: 1..20 cookies (consistent reward); Abra (special user id) gets 1.5x.
// - Steal:
// * successChance = attacker / (attacker + defender), clamped to [0.10, 0.90]
// * steal amount: random 1..ceil(defender/4)
// * failure penalty: attacker loses random 1..ceil(attacker/6) (transferred to defender)
// - Cooldowns: collect = 1 hour, steal = 15 minutes
const ABRA_ID = "378135175059603457"; // kept from original for bonus
async function collect(interaction, user) {
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
if (existing > now) {
return interaction.reply({
content: `The batch is still cooking, come back in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
// set cooldown (1 hour)
const cooldownUntil = now + 60 * 60 * 1000;
await setCooldown(id, interaction.commandName, sub, cooldownUntil);
// balanced collect amount: 1..20
let amount = Math.floor(Math.random() * 20) + 1;
if (user.id === ABRA_ID) amount = Math.ceil(amount * 1.5);
// update DB
await changeCookies(user.id, amount, user.username);
// playful responses (kept / adapted)
const collectResponses = (amount, user) => {
const s = amount === 1 ? 'Cookie' : 'Cookies';
return [
`${user}, I made you **${amount}** ${s} 🍪`,
`${user}, here's your **${amount}** ${s} 🍪 I ate the rest, sorry >.<`,
`Only **${amount}** ${s} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a Glass of Milk with your **${amount}** ${s}? 🍪`,
`**${amount}** ${s} 🍪 Careful, ${user}! They're still warm :3`,
`**${amount}** ${s} 🍪 Best Batch yet!`,
`**${amount}** ${s} 🍪 These, I bequeath unto thine own self upon this day. Thou must guard them safely.`,
`${user}, your **${amount}** ${s} 🍪 are fresh out of the oven... try not to burn your tongue!`,
`${user}, I bribed the Cookie Monster to spare these **${amount}** ${s} 🍪 just for you.`,
`${user}, Santa came early! You got **${amount}** ${s} 🍪 (milk not included).`,
`${user}, your **${amount}** ${s} 🍪 are artisanal, gluten-free, and slightly cursed.`,
`A wild baker appeared! They hand you **${amount}** ${s} 🍪`,
`${user}, Abra said these **${amount}** ${s} 🍪 are "totally not stolen"... 🤔`,
`${user}, here’s your **${amount}** ${s} 🍪 — watch out, they may be booby-trapped.`
];
};
const responses = collectResponses(amount, user);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: responses[i] });
}
async function steal(interaction, user) {
const target = interaction.options.getUser('target');
if (!target) {
return interaction.reply({ content: `Pick someone to steal from.`, ephemeral: true });
}
if (target.id === user.id) {
return interaction.reply({
content: `C'mon, don't embarrass yourself! We all know you couldn't do that, even if you tried.`,
ephemeral: true
});
}
// pull balances
const your = await getCookies(user.id);
const their = await getCookies(target.id);
if (your <= 0) {
return interaction.reply({ content: `You need at least 1 Cookie to attempt a Robbery`, ephemeral: true });
}
if (their <= 0) {
return interaction.reply({ content: `Shame on you! ${target.username} doesn't have any Cookies D:`, ephemeral: true });
}
// steal cooldown (15 minutes)
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
const chaos = true // 50% Stela Chance Mode
if (existing > now) {
return interaction.reply({
content: `Hold up — try stealing again in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
await setCooldown(id, interaction.commandName, sub, now + 15 * 60 * 1000);
// success chance and clamping
let successChance = your / (your + their);
if (isNaN(successChance) || !isFinite(successChance)) successChance = 0.2;
if (chaos) {
successChance = 0.5
} else {
successChance = Math.max(0.10, Math.min(0.90, successChance));
}
const success = Math.random() < successChance;
if (success) {
const stealSuccessResponses = (amount, target) => {
const s = amount === 1 ? 'Cookie' : 'Cookies';
return [
`You've successfully stolen **${amount}** ${s} from **${target.username}**!`,
`Ninja mode: activated. You snatched **${amount}** ${s} from **${target.username}** without a crumb left behind.`,
`Abra taught you well — **${amount}** ${s} from **${target.username}** are now yours!`,
`Like a phantom in the night, you took **${amount}** ${s} from **${target.username}**.`,
`You just pulled the sweetest heist: **${amount}** ${s} from **${target.username}**!`,
`Mission complete. **${target.username}** is **${amount}** ${s} poorer, you are richer.`,
`You distracted **${target.username}** with a dance and swiped **${amount}** ${s}.`,
`Clean getaway! **${target.username}** won’t know about the missing **${amount}** ${s} until it’s too late.`,
`The Cookie Vault has been breached. **${amount}** ${s} taken from **${target.username}**.`,
`You emerged from the shadows with **${amount}** ${s} from **${target.username}**.`
];
};
const maxSteal = Math.max(1, Math.ceil(their / 4)); // steal up to ~25% of victim
let amount = Math.floor(Math.random() * maxSteal) + 1;
amount = Math.min(amount, their);
// transfer
await Promise.all([
changeCookies(user.id, amount, user.username),
changeCookies(target.id, -amount, target.username)
]);
const responses = stealSuccessResponses(amount, target);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: `${responses[i]} (Chance: ${Math.round(successChance * 100, 2)}%)` });
} else {
const stealFailResponses = (penalty, target) => {
const s = penalty === 1 ? 'Cookie' : 'Cookies';
return [
`You were caught red-handed by **${target.username}** and lost **${penalty}** ${s} as a penalty!`,
`Fail! **${target.username}** caught you mid-bite — you dropped **${penalty}** ${s}.`,
`Not your best moment... You lost **${penalty}** ${s} to **${target.username}** in the struggle.`,
`Stealth level: 0. You handed **${penalty}** ${s} to **${target.username}** as an apology.`,
`The cookie jar was booby-trapped! You gave **${penalty}** ${s} to **${target.username}** while running away.`,
`Ouch. The guard dog barked, and now **${target.username}** has your **${penalty}** ${s}.`,
`You tripped over your own shoelace — **${target.username}** reclaimed **${penalty}** ${s}.`,
`Caught by the Cookie Police! **${penalty}** ${s} confiscated and given to **${target.username}**.`,
`Your pockets had holes... **${target.username}** picked up **${penalty}** ${s} you dropped.`
];
};
// failure penalty: attacker loses a little (1..ceil(your/6)) to the victim
const maxPenalty = Math.max(1, Math.ceil(your / 6));
const penalty = Math.min(your, Math.floor(Math.random() * maxPenalty) + 1);
await Promise.all([
changeCookies(user.id, -penalty, user.username),
changeCookies(target.id, +penalty, target.username)
]);
const responses = stealFailResponses(penalty, target);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: `${responses[i]} (Chance: ${Math.round(successChance * 100, 2)}%)` });
}
}
async function check(interaction, user) {
let target = interaction.options.getUser('target');
if (!target) target = user;
const cookies = await getCookies(target.id);
const mood = cookies === 0 ? '😔' : '😲';
return interaction.reply({ content: `**${target.username}** has **${cookies}** Cookies! ${mood}` });
}
async function give(interaction, user) {
const target = interaction.options.getUser('target');
const amount = interaction.options.getInteger('amount');
if (!target) return interaction.reply({ content: `You need to specify a target.`, ephemeral: true });
if (target.id === user.id) return interaction.reply({ content: 'You cannot give yourself Cookies ._."', ephemeral: true });
if (!Number.isInteger(amount) || amount < 1) {
return interaction.reply({ content: `**${user}** shared *checks notes* **${amount}** Cookie(s) with ${target}....wait a minute!!`, ephemeral: true });
}
const your = await getCookies(user.id);
if (your < amount) return interaction.reply({ content: `We don't run a charity here, get more Cookies!`, ephemeral: true });
await Promise.all([
changeCookies(user.id, -amount, user.username),
changeCookies(target.id, +amount, target.username)
]);
// small delay to mimic previous behavior
await delay(200);
return interaction.reply({ content: `**${user}** shared **${amount}** Cookie(s) with ${target}` });
}
async function leader(interaction) {
const results = await cookieSchema.find().sort({ cookies: -1 }).limit(10);
let cookieSorted = '';
results.forEach((r, i) => {
cookieSorted += `[${i + 1}] <@${r._id}> (${r.cookies})\n`;
});
const embed = new Discord.MessageEmbed()
.setColor(0x0099FF)
.setThumbnail(interaction.guild.iconURL())
.setTimestamp()
.addFields({ name: `-Cookie Leaderboard-`, value: cookieSorted || 'No cookie data yet.' });
return interaction.reply({ embeds: [embed] });
}
module.exports = {
slash: true,
description: 'collect a cookie',
category: 'Misc',
options: [
{ name: "collect", description: "want some?", type: 1 },
{ name: "steal", description: "try your luck at a lala robbery!", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }] },
{ name: "check", description: "check your cookie stash", type: 1, options: [{ name: "target", description: "someone", type: 6 }] },
{ name: "give", description: "share your cookies with someone", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }, { name: "amount", description: "cookies", required: true, type: 4 }] },
{ name: "leader", description: "show Top 10 Cookie Clickers", type: 1 }
],
callback: ({ interaction, user }) => {
const sub = interaction.options._subcommand;
switch (sub) {
case "collect": return collect(interaction, user);
case "steal": return steal(interaction, user);
case "check": return check(interaction, user);
case "give": return give(interaction, user);
case "leader": return leader(interaction);
default: return interaction.reply({ content: 'Unknown subcommand', ephemeral: true });
}
}
};
-448
View File
@@ -1,448 +0,0 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const uri = process.env.URI
const cookieSchema = require("../schemas/cookieSchema")
const cooldownSchema = require("../schemas/cooldownSchema")
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const Discord = require('discord.js')
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
function msToTime(duration) {
var milliseconds = parseInt((duration % 1000) / 100),
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + ":" + minutes + ":" + seconds;
}
async function collect(interaction, user) {
client.connect(async (err, db) => {
var dbo = db.db('test')
let id = `${interaction.guildId}-${interaction.user.id}-${interaction.commandName}-${interaction.options._subcommand}`
dbo.collection('cooldowns').findOne({
_id: id
}, async function (err, results) {
let now = Date.now()
let time = now + 3600000 // plus one hour in ms
if (results) {
if (results.cooldown > now) {
time = new Date(results.cooldown).toUTCString()
return interaction.reply({
content: `The batch is still cooking, come back in **${msToTime(results.cooldown - now)}**`,
ephemeral: true
})
}
}
addCooldown(interaction.user.id, interaction.guildId, interaction.commandName, interaction.options._subcommand, time)
var amount = Math.floor(Math.random() * 30)
//random amount between 1 and 29
if (user.id == "378135175059603457") {
//Abra Bonus
amount = Math.ceil(amount * 1.5)
}
var responses = [
`${user}, I made you **${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 `,
`${user}, Here's your **${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 I ate the rest, sorry >.<`,
`Only **${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a Glass of Milk with your **${amount}** ${amount == 1?`Cookie`: `Cookies`}? 🍪 `,
`**${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 Careful, ${user}! They're still warm :3`,
`**${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 Best Batch yet!`,
`**${amount}** ${amount == 1?`Cookie`: `Cookies`} 🍪 These, I bequeath unto thine own self upon this day. Thou must guard them safely.`,
]
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: +amount }
}, {
upsert: true
})
if(amount === 0){
interaction.reply({
content: `${user}, someone ate all your Cookies already D:`
})
} else {
var i = Math.floor(Math.random() * responses.length)
interaction.reply({
content: responses[i]
})
}
// choose response
})
})
}
/*
function steal(interaction, user) {
client.connect(async (err, db) => {
if (err) throw err;
let target = interaction.options.getUser('target')
let dbo = db.db("test")
if (target.id == user.id) {
return interaction.reply({
content: `C'mon, don't embarass yourself! We all know you couldn't do that, even if you tried.`,
ephemeral: true,
})
}
// get your amount of cookies
let your = await dbo.collection('cookies').findOne({
_id: user.id
})
// get their amount of cookies
let their = await dbo.collection('cookies').findOne({
_id: target.id
})
try {
if (!your.cookies) {
return interaction.reply({
content: `You need at least 1 Cookie to attempt a Robbery`,
ephemeral: true,
})
}
if (!their.cookies) {
return interaction.reply({
content: `Shame on you! They don't even have any Cookies D:`,
ephemeral: true,
})
}
} catch (error) {
return interaction.reply({
content: `How awkward...I don't know some of you!`,
ephemeral: true,
})
}
let difficulty = []
for (var i = 0; i < their.cookies; i++) {
if (i < your.cookies) {
difficulty.push(true)
} else {
difficulty.push(false)
}
}
let result = Math.floor(Math.random() * their.cookies) + 1
let response
if (!difficulty[result]) {
// author lost
response = `You were unsuccessful at robbing **${target.username}** and lost your chance at **${result}** ${result == 1?`Cookie`: `Cookies`}! `
if (your.cookies - result <= 0) {
result = your.cookies
response += `That's all your Cookies gone.`
}
result *= -1
} else {
//author won
response = `You've successfully stolen **${result<their.cookies?result:their.cookies}** ${result == 1?`Cookie`: `Cookies`} from **${target.username}**! `
if (their.cookies - result <= 0) {
result = their.cookies
response += `They're completely dry now.`
}
}
interaction.reply({
content: response
})
//update author's cookies
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: +result } //will decrease if it's +negativeInteger
}, {
upsert: true
})
await cookieSchema.findOneAndUpdate({
_id: target.id
}, {
_id: target.id,
user: target.username,
$inc: { cookies: -result } //will decrease if it's +negativeInteger
}, {
upsert: true
})
})
}
*/
async function steal(interaction, user) {
const target = interaction.options.getUser('target');
if (!target || target.id === user.id) {
return interaction.reply({
content: `You can't steal from yourself!`,
ephemeral: true
});
}
const dbo = client.db("test");
const [yourCookies, theirCookies] = await Promise.all([
dbo.collection('cookies').findOne({ _id: user.id }) || { cookies: 0 },
dbo.collection('cookies').findOne({ _id: target.id }) || { cookies: 0 }
]);
if (yourCookies.cookies <= 0) {
return interaction.reply({
content: `You need at least 1 cookie to attempt a robbery.`,
ephemeral: true
});
}
if (theirCookies.cookies <= 0) {
return interaction.reply({
content: `${target.username} has no cookies to steal!`,
ephemeral: true
});
}
// Fair success chance: attackerCookies / (attackerCookies + defenderCookies)
const successChance = yourCookies.cookies / (yourCookies.cookies + theirCookies.cookies);
const success = Math.random() < successChance;
// Random steal amount between 1 and 1/3 of victim's cookies
const maxSteal = Math.max(1, Math.floor(theirCookies.cookies / 3));
let stealAmount = Math.floor(Math.random() * maxSteal) + 1;
let message;
if (success) {
stealAmount = Math.min(stealAmount, theirCookies.cookies);
await Promise.all([
cookieSchema.updateOne({ _id: user.id }, { $inc: { cookies: stealAmount }, $set: { user: user.username } }, { upsert: true }),
cookieSchema.updateOne({ _id: target.id }, { $inc: { cookies: -stealAmount }, $set: { user: target.username } }, { upsert: true })
]);
message = `You successfully stole **${stealAmount}** 🍪 from **${target.username}**!`;
} else {
// Penalty: lose up to half of attempted amount
stealAmount = Math.min(stealAmount, yourCookies.cookies);
await Promise.all([
cookieSchema.updateOne({ _id: user.id }, { $inc: { cookies: -stealAmount }, $set: { user: user.username } }, { upsert: true }),
cookieSchema.updateOne({ _id: target.id }, { $inc: { cookies: stealAmount }, $set: { user: target.username } }, { upsert: true })
]);
message = `You failed to steal from **${target.username}** and lost **${stealAmount}** 🍪 as punishment!`;
}
return interaction.reply({ content: message });
}
function check(interaction, user) {
client.connect(async function (err, db) {
let target = interaction.options.getUser('target')
if (!target) {
target = user
}
let response = `**${target.username}** has `
let dbo = db.db("test")
dbo.collection("cookies").findOne({
_id: target.id
}, function (err, results) {
let cookies
if (!results) {
cookies = 0
response += `**${cookies}** Cookies! 😔`
} else {
cookies = results.cookies
response += `**${cookies}** Cookies! 😲`
}
interaction.reply({
content: response
})
})
})
}
async function give(interaction, user) {
client.connect(async function (err, db) {
let target = interaction.options.getUser('target')
let amount = interaction.options.getInteger('amount')
let dbo = db.db('test')
//check if you have enough cookies
let your = await dbo.collection('cookies').findOne({
_id: user.id
})
if (target.id == user.id) {
return interaction.reply({
content: 'You cannot give yourself Cookies ._."'
})
}
if (your.cookies < amount) {
return interaction.reply({
content: `We don't run a charity here, get more Cookies!`
})
}
if (amount < 1) {
return interaction.reply({
content: `**${user}** shared *checks notes* **${amount}** Cookie(s) with ${target}....wait a minute!!`,
})
}
//update user's cookies
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: -amount }
}, {
upsert: true
})
//update target's cookies
await cookieSchema.findOneAndUpdate({
_id: target.id
}, {
_id: target.id,
user: target.username,
$inc: { cookies: +amount }
}, {
upsert: true
})
await delay(200)
interaction.reply({
content: `**${user}** shared **${amount}** Cookie(s) with ${target}`
})
})
}
async function addCooldown(userId, guildId, command, subcommand, ms) {
let id = `${guildId}-${userId}-${command}-${subcommand}`
await cooldownSchema.findOneAndUpdate({
_id: id
}, {
_id: id,
command: command,
subcommand: subcommand,
cooldown: ms
}, {
upsert: true
})
}
async function leader(interaction) {
const results = await cookieSchema.find().sort({
cookies: -1
}).limit(10)
let cookieSorted = ''
let i = 0
results.forEach((result) => {
cookieSorted += `[${i + 1}] <@${result._id}> (${result.cookies})\n`
i++;
})
var embed = new Discord.MessageEmbed()
.setColor(0x0099FF)
.setThumbnail(interaction.guild.iconURL())
.setTimestamp()
.addFields({ name: `-Cookie Leaderboard-`, value: cookieSorted })
interaction.reply({
embeds: [embed]
})
}
module.exports = {
slash: true,
description: 'collect a cookie',
category: 'Misc',
options: [
//collect
{
name: "collect",
description: "want some?",
type: 1 // SubCommand Type
},
//steal
{
name: "steal",
description: "try your luck at a lala robbery!",
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
required: true,
type: 6 // User Type
}
]
},
//check
{
name: "check",
description: "check your cookie stash",
required: false,
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
type: 6 // User Type
}
]
},
//give
{
name: "give",
description: "share your cookies with someone",
required: false,
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
required: true,
type: 6 // User Type
}, {
name: "amount",
description: "cookies",
required: true,
type: 4 // Integer Type
}
]
},
//leader
{
name: "leader",
description: "show Top 10 Cookie Clickers",
type: 1, // SubCommand Type
},
],
callback: ({ interaction, user }) => {
switch (interaction.options._subcommand) {
case "collect":
collect(interaction, user)
break;
case "steal":
steal(interaction, user)
break;
case "check":
check(interaction, user)
break;
case "give":
give(interaction, user)
break;
case "leader":
leader(interaction)
break;
default:
break;
}
}
}
-262
View File
@@ -1,262 +0,0 @@
// cookies.js — rewritten & rebalanced
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const cookieSchema = require("../schemas/cookieSchema"); // expected to support findOne, findOneAndUpdate, updateOne, find
const cooldownSchema = require("../schemas/cooldownSchema"); // expected to support findOne, findOneAndUpdate
const Discord = require('discord.js');
const URI = process.env.URI;
if (!URI) throw new Error("Missing process.env.URI for MongoDB");
// single client reused
const client = new MongoClient(URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
client.connect().catch(err => {
console.error("Mongo connect error:", err);
});
// util
const msToTime = (duration) => {
const ms = Math.floor((duration % 1000) / 100);
const s = Math.floor((duration / 1000) % 60);
const m = Math.floor((duration / (1000 * 60)) % 60);
const h = Math.floor((duration / (1000 * 60 * 60)) % 24);
const pad = n => (n < 10 ? "0" + n : "" + n);
return `${pad(h)}:${pad(m)}:${pad(s)}`;
};
const delay = ms => new Promise(r => setTimeout(r, ms));
// --- Helpers ---
async function getCookies(userId) {
const rec = await cookieSchema.findOne({ _id: userId });
return rec && typeof rec.cookies === 'number' ? rec.cookies : 0;
}
async function changeCookies(userId, delta, username = null) {
// upsert and set username if provided
const update = { $inc: { cookies: delta } };
if (username) update.$set = { user: username };
return cookieSchema.findOneAndUpdate(
{ _id: userId },
update,
{ upsert: true, returnDocument: 'after' }
);
}
async function getCooldown(uniqueId) {
const rec = await cooldownSchema.findOne({ _id: uniqueId });
return rec && rec.cooldown ? rec.cooldown : 0;
}
async function setCooldown(uniqueId, command, subcommand, msTimestamp) {
await cooldownSchema.findOneAndUpdate(
{ _id: uniqueId },
{ _id: uniqueId, command, subcommand, cooldown: msTimestamp },
{ upsert: true }
);
}
function cooldownId(guildId, userId, command, subcommand) {
return `${guildId}-${userId}-${command}-${subcommand}`;
}
// --- Game logic & rebalances ---
// Notes about balancing:
// - Collect: 1..20 cookies (consistent reward); Abra (special user id) gets 1.5x.
// - Steal:
// * successChance = attacker / (attacker + defender), clamped to [0.10, 0.90]
// * steal amount: random 1..ceil(defender/4)
// * failure penalty: attacker loses random 1..ceil(attacker/6) (transferred to defender)
// - Cooldowns: collect = 1 hour, steal = 15 minutes
const ABRA_ID = "378135175059603457"; // kept from original for bonus
async function collect(interaction, user) {
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
if (existing > now) {
return interaction.reply({
content: `The batch is still cooking, come back in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
// set cooldown (1 hour)
const cooldownUntil = now + 60 * 60 * 1000;
await setCooldown(id, interaction.commandName, sub, cooldownUntil);
// balanced collect amount: 1..20
let amount = Math.floor(Math.random() * 20) + 1;
if (user.id === ABRA_ID) amount = Math.ceil(amount * 1.5);
// update DB
await changeCookies(user.id, amount, user.username);
// playful responses (kept / adapted)
const responses = [
`${user}, I made you **${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪`,
`${user}, here's your **${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪 I ate the rest, sorry >.<`,
`Only **${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a Glass of Milk with your **${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'}? 🍪`,
`**${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪 Careful, ${user}! They're still warm :3`,
`**${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪 Best Batch yet!`,
`**${amount}** ${amount === 1 ? 'Cookie' : 'Cookies'} 🍪 These, I bequeath unto thine own self upon this day. Thou must guard them safely.`
];
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: responses[i] });
}
async function steal(interaction, user) {
const target = interaction.options.getUser('target');
if (!target) {
return interaction.reply({ content: `Pick someone to steal from.`, ephemeral: true });
}
if (target.id === user.id) {
return interaction.reply({
content: `C'mon, don't embarrass yourself! We all know you couldn't do that, even if you tried.`,
ephemeral: true
});
}
// steal cooldown (15 minutes)
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
if (existing > now) {
return interaction.reply({
content: `Hold up — try stealing again in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
await setCooldown(id, interaction.commandName, sub, now + 15 * 60 * 1000);
// pull balances
const your = await getCookies(user.id);
const their = await getCookies(target.id);
if (your <= 0) {
return interaction.reply({ content: `You need at least 1 Cookie to attempt a Robbery`, ephemeral: true });
}
if (their <= 0) {
return interaction.reply({ content: `Shame on you! ${target.username} doesn't have any Cookies D:`, ephemeral: true });
}
// success chance and clamping
let successChance = your / (your + their);
if (isNaN(successChance) || !isFinite(successChance)) successChance = 0.2;
successChance = Math.max(0.50, Math.min(0.90, successChance)); // made it 50% for chaos
const success = Math.random() < successChance;
if (success) {
const maxSteal = Math.max(1, Math.ceil(their / 4)); // steal up to ~25% of victim
let amount = Math.floor(Math.random() * maxSteal) + 1;
amount = Math.min(amount, their);
// transfer
await Promise.all([
changeCookies(user.id, amount, user.username),
changeCookies(target.id, -amount, target.username)
]);
const s = amount === 1 ? 'Cookie' : 'Cookies';
return interaction.reply({
content: `You've successfully stolen **${amount}** ${s} from **${target.username}**!`
});
} else {
// failure penalty: attacker loses a little (1..ceil(your/6)) to the victim
const maxPenalty = Math.max(1, Math.ceil(your / 6));
const penalty = Math.min(your, Math.floor(Math.random() * maxPenalty) + 1);
await Promise.all([
changeCookies(user.id, -penalty, user.username),
changeCookies(target.id, +penalty, target.username)
]);
const s = penalty === 1 ? 'Cookie' : 'Cookies';
return interaction.reply({
content: `You were unsuccessful at robbing **${target.username}** and lost **${penalty}** ${s} as a penalty!`
});
}
}
async function check(interaction, user) {
let target = interaction.options.getUser('target');
if (!target) target = user;
const cookies = await getCookies(target.id);
const mood = cookies === 0 ? '😔' : '😲';
return interaction.reply({ content: `**${target.username}** has **${cookies}** Cookies! ${mood}` });
}
async function give(interaction, user) {
const target = interaction.options.getUser('target');
const amount = interaction.options.getInteger('amount');
if (!target) return interaction.reply({ content: `You need to specify a target.`, ephemeral: true });
if (target.id === user.id) return interaction.reply({ content: 'You cannot give yourself Cookies ._."', ephemeral: true });
if (!Number.isInteger(amount) || amount < 1) {
return interaction.reply({ content: `**${user}** shared *checks notes* **${amount}** Cookie(s) with ${target}....wait a minute!!`, ephemeral: true });
}
const your = await getCookies(user.id);
if (your < amount) return interaction.reply({ content: `We don't run a charity here, get more Cookies!`, ephemeral: true });
await Promise.all([
changeCookies(user.id, -amount, user.username),
changeCookies(target.id, +amount, target.username)
]);
// small delay to mimic previous behavior
await delay(200);
return interaction.reply({ content: `**${user}** shared **${amount}** Cookie(s) with ${target}` });
}
async function leader(interaction) {
const results = await cookieSchema.find().sort({ cookies: -1 }).limit(10);
let cookieSorted = '';
results.forEach((r, i) => {
cookieSorted += `[${i + 1}] <@${r._id}> (${r.cookies})\n`;
});
const embed = new Discord.MessageEmbed()
.setColor(0x0099FF)
.setThumbnail(interaction.guild.iconURL())
.setTimestamp()
.addFields({ name: `-Cookie Leaderboard-`, value: cookieSorted || 'No cookie data yet.' });
return interaction.reply({ embeds: [embed] });
}
module.exports = {
slash: true,
description: 'collect a cookie',
category: 'Misc',
options: [
{ name: "collect", description: "want some?", type: 1 },
{ name: "steal", description: "try your luck at a lala robbery!", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }] },
{ name: "check", description: "check your cookie stash", type: 1, options: [{ name: "target", description: "someone", type: 6 }] },
{ name: "give", description: "share your cookies with someone", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }, { name: "amount", description: "cookies", required: true, type: 4 }] },
{ name: "leader", description: "show Top 10 Cookie Clickers", type: 1 }
],
callback: ({ interaction, user }) => {
const sub = interaction.options._subcommand;
switch (sub) {
case "collect": return collect(interaction, user);
case "steal": return steal(interaction, user);
case "check": return check(interaction, user);
case "give": return give(interaction, user);
case "leader": return leader(interaction);
default: return interaction.reply({ content: 'Unknown subcommand', ephemeral: true });
}
}
};
-307
View File
@@ -1,307 +0,0 @@
// cookies.js — rewritten & rebalanced
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const cookieSchema = require("../schemas/cookieSchema"); // expected to support findOne, findOneAndUpdate, updateOne, find
const cooldownSchema = require("../schemas/cooldownSchema"); // expected to support findOne, findOneAndUpdate
const Discord = require('discord.js');
const URI = process.env.URI;
if (!URI) throw new Error("Missing process.env.URI for MongoDB");
// single client reused
const client = new MongoClient(URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
client.connect().catch(err => {
console.error("Mongo connect error:", err);
});
// util
const msToTime = (duration) => {
const ms = Math.floor((duration % 1000) / 100);
const s = Math.floor((duration / 1000) % 60);
const m = Math.floor((duration / (1000 * 60)) % 60);
const h = Math.floor((duration / (1000 * 60 * 60)) % 24);
const pad = n => (n < 10 ? "0" + n : "" + n);
return `${pad(h)}:${pad(m)}:${pad(s)}`;
};
const delay = ms => new Promise(r => setTimeout(r, ms));
// --- Helpers ---
async function getCookies(userId) {
const rec = await cookieSchema.findOne({ _id: userId });
return rec && typeof rec.cookies === 'number' ? rec.cookies : 0;
}
async function changeCookies(userId, delta, username = null) {
// upsert and set username if provided
const update = { $inc: { cookies: delta } };
if (username) update.$set = { user: username };
return cookieSchema.findOneAndUpdate(
{ _id: userId },
update,
{ upsert: true, returnDocument: 'after' }
);
}
async function getCooldown(uniqueId) {
const rec = await cooldownSchema.findOne({ _id: uniqueId });
return rec && rec.cooldown ? rec.cooldown : 0;
}
async function setCooldown(uniqueId, command, subcommand, msTimestamp) {
await cooldownSchema.findOneAndUpdate(
{ _id: uniqueId },
{ _id: uniqueId, command, subcommand, cooldown: msTimestamp },
{ upsert: true }
);
}
function cooldownId(guildId, userId, command, subcommand) {
return `${guildId}-${userId}-${command}-${subcommand}`;
}
// --- Game logic & rebalances ---
// Notes about balancing:
// - Collect: 1..20 cookies (consistent reward); Abra (special user id) gets 1.5x.
// - Steal:
// * successChance = attacker / (attacker + defender), clamped to [0.10, 0.90]
// * steal amount: random 1..ceil(defender/4)
// * failure penalty: attacker loses random 1..ceil(attacker/6) (transferred to defender)
// - Cooldowns: collect = 1 hour, steal = 15 minutes
const ABRA_ID = "378135175059603457"; // kept from original for bonus
async function collect(interaction, user) {
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
if (existing > now) {
return interaction.reply({
content: `The batch is still cooking, come back in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
// set cooldown (1 hour)
const cooldownUntil = now + 60 * 60 * 1000;
await setCooldown(id, interaction.commandName, sub, cooldownUntil);
// balanced collect amount: 1..20
let amount = Math.floor(Math.random() * 20) + 1;
if (user.id === ABRA_ID) amount = Math.ceil(amount * 1.5);
// update DB
await changeCookies(user.id, amount, user.username);
// playful responses (kept / adapted)
const collectResponses = (amount, user) => {
const s = amount === 1 ? 'Cookie' : 'Cookies';
return [
`${user}, I made you **${amount}** ${s} 🍪`,
`${user}, here's your **${amount}** ${s} 🍪 I ate the rest, sorry >.<`,
`Only **${amount}** ${s} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a Glass of Milk with your **${amount}** ${s}? 🍪`,
`**${amount}** ${s} 🍪 Careful, ${user}! They're still warm :3`,
`**${amount}** ${s} 🍪 Best Batch yet!`,
`**${amount}** ${s} 🍪 These, I bequeath unto thine own self upon this day. Thou must guard them safely.`,
`${user}, your **${amount}** ${s} 🍪 are fresh out of the oven... try not to burn your tongue!`,
`${user}, I bribed the Cookie Monster to spare these **${amount}** ${s} 🍪 just for you.`,
`${user}, Santa came early! You got **${amount}** ${s} 🍪 (milk not included).`,
`${user}, your **${amount}** ${s} 🍪 are artisanal, gluten-free, and slightly cursed.`,
`A wild baker appeared! They hand you **${amount}** ${s} 🍪`,
`${user}, Abra said these **${amount}** ${s} 🍪 are "totally not stolen"... 🤔`,
`${user}, here’s your **${amount}** ${s} 🍪 — watch out, they may be booby-trapped.`
];
};
const responses = collectResponses(amount, user);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: responses[i] });
}
async function steal(interaction, user) {
const target = interaction.options.getUser('target');
if (!target) {
return interaction.reply({ content: `Pick someone to steal from.`, ephemeral: true });
}
if (target.id === user.id) {
return interaction.reply({
content: `C'mon, don't embarrass yourself! We all know you couldn't do that, even if you tried.`,
ephemeral: true
});
}
// steal cooldown (15 minutes)
const sub = interaction.options._subcommand;
const id = cooldownId(interaction.guildId, interaction.user.id, interaction.commandName, sub);
const now = Date.now();
const existing = await getCooldown(id);
if (existing > now) {
return interaction.reply({
content: `Hold up — try stealing again in **${msToTime(existing - now)}**`,
ephemeral: true
});
}
await setCooldown(id, interaction.commandName, sub, now + 15 * 60 * 1000);
// pull balances
const your = await getCookies(user.id);
const their = await getCookies(target.id);
if (your <= 0) {
return interaction.reply({ content: `You need at least 1 Cookie to attempt a Robbery`, ephemeral: true });
}
if (their <= 0) {
return interaction.reply({ content: `Shame on you! ${target.username} doesn't have any Cookies D:`, ephemeral: true });
}
// success chance and clamping
let successChance = your / (your + their);
if (isNaN(successChance) || !isFinite(successChance)) successChance = 0.2;
successChance = Math.max(0.10, Math.min(0.90, successChance));
const success = Math.random() < successChance;
if (success) {
const stealSuccessResponses = (amount, target) => {
const s = amount === 1 ? 'Cookie' : 'Cookies';
return [
`You've successfully stolen **${amount}** ${s} from **${target.username}**!`,
`Ninja mode: activated. You snatched **${amount}** ${s} from **${target.username}** without a crumb left behind.`,
`Abra taught you well — **${amount}** ${s} from **${target.username}** are now yours!`,
`Like a phantom in the night, you took **${amount}** ${s} from **${target.username}**.`,
`You just pulled the sweetest heist: **${amount}** ${s} from **${target.username}**!`,
`Mission complete. **${target.username}** is **${amount}** ${s} poorer, you are richer.`,
`You distracted **${target.username}** with a dance and swiped **${amount}** ${s}.`,
`Clean getaway! **${target.username}** won’t know about the missing **${amount}** ${s} until it’s too late.`,
`The Cookie Vault has been breached. **${amount}** ${s} taken from **${target.username}**.`,
`You emerged from the shadows with **${amount}** ${s} from **${target.username}**.`
];
};
const maxSteal = Math.max(1, Math.ceil(their / 4)); // steal up to ~25% of victim
let amount = Math.floor(Math.random() * maxSteal) + 1;
amount = Math.min(amount, their);
// transfer
await Promise.all([
changeCookies(user.id, amount, user.username),
changeCookies(target.id, -amount, target.username)
]);
const responses = stealSuccessResponses(amount, target);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: responses[i] });
} else {
const stealFailResponses = (penalty, target) => {
const s = penalty === 1 ? 'Cookie' : 'Cookies';
return [
`You were caught red-handed by **${target.username}** and lost **${penalty}** ${s} as a penalty!`,
`Fail! **${target.username}** caught you mid-bite — you dropped **${penalty}** ${s}.`,
`Not your best moment... You lost **${penalty}** ${s} to **${target.username}** in the struggle.`,
`Stealth level: 0. You handed **${penalty}** ${s} to **${target.username}** as an apology.`,
`The cookie jar was booby-trapped! You gave **${penalty}** ${s} to **${target.username}** while running away.`,
`Ouch. The guard dog barked, and now **${target.username}** has your **${penalty}** ${s}.`,
`You tripped over your own shoelace — **${target.username}** reclaimed **${penalty}** ${s}.`,
`Caught by the Cookie Police! **${penalty}** ${s} confiscated and given to **${target.username}**.`,
`Your pockets had holes... **${target.username}** picked up **${penalty}** ${s} you dropped.`
];
};
// failure penalty: attacker loses a little (1..ceil(your/6)) to the victim
const maxPenalty = Math.max(1, Math.ceil(your / 6));
const penalty = Math.min(your, Math.floor(Math.random() * maxPenalty) + 1);
await Promise.all([
changeCookies(user.id, -penalty, user.username),
changeCookies(target.id, +penalty, target.username)
]);
const responses = stealFailResponses(penalty, target);
const i = Math.floor(Math.random() * responses.length);
return interaction.reply({ content: responses[i] });
}
}
async function check(interaction, user) {
let target = interaction.options.getUser('target');
if (!target) target = user;
const cookies = await getCookies(target.id);
const mood = cookies === 0 ? '😔' : '😲';
return interaction.reply({ content: `**${target.username}** has **${cookies}** Cookies! ${mood}` });
}
async function give(interaction, user) {
const target = interaction.options.getUser('target');
const amount = interaction.options.getInteger('amount');
if (!target) return interaction.reply({ content: `You need to specify a target.`, ephemeral: true });
if (target.id === user.id) return interaction.reply({ content: 'You cannot give yourself Cookies ._."', ephemeral: true });
if (!Number.isInteger(amount) || amount < 1) {
return interaction.reply({ content: `**${user}** shared *checks notes* **${amount}** Cookie(s) with ${target}....wait a minute!!`, ephemeral: true });
}
const your = await getCookies(user.id);
if (your < amount) return interaction.reply({ content: `We don't run a charity here, get more Cookies!`, ephemeral: true });
await Promise.all([
changeCookies(user.id, -amount, user.username),
changeCookies(target.id, +amount, target.username)
]);
// small delay to mimic previous behavior
await delay(200);
return interaction.reply({ content: `**${user}** shared **${amount}** Cookie(s) with ${target}` });
}
async function leader(interaction) {
const results = await cookieSchema.find().sort({ cookies: -1 }).limit(10);
let cookieSorted = '';
results.forEach((r, i) => {
cookieSorted += `[${i + 1}] <@${r._id}> (${r.cookies})\n`;
});
const embed = new Discord.MessageEmbed()
.setColor(0x0099FF)
.setThumbnail(interaction.guild.iconURL())
.setTimestamp()
.addFields({ name: `-Cookie Leaderboard-`, value: cookieSorted || 'No cookie data yet.' });
return interaction.reply({ embeds: [embed] });
}
module.exports = {
slash: true,
description: 'collect a cookie',
category: 'Misc',
options: [
{ name: "collect", description: "want some?", type: 1 },
{ name: "steal", description: "try your luck at a lala robbery!", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }] },
{ name: "check", description: "check your cookie stash", type: 1, options: [{ name: "target", description: "someone", type: 6 }] },
{ name: "give", description: "share your cookies with someone", type: 1, options: [{ name: "target", description: "someone", required: true, type: 6 }, { name: "amount", description: "cookies", required: true, type: 4 }] },
{ name: "leader", description: "show Top 10 Cookie Clickers", type: 1 }
],
callback: ({ interaction, user }) => {
const sub = interaction.options._subcommand;
switch (sub) {
case "collect": return collect(interaction, user);
case "steal": return steal(interaction, user);
case "check": return check(interaction, user);
case "give": return give(interaction, user);
case "leader": return leader(interaction);
default: return interaction.reply({ content: 'Unknown subcommand', ephemeral: true });
}
}
};
-383
View File
@@ -1,383 +0,0 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const uri = process.env.URI
const cookieSchema = require("../schemas/cookieSchema")
const cooldownSchema = require("../schemas/cooldownSchema")
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const Discord = require('discord.js')
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
function msToTime(duration) {
var milliseconds = parseInt((duration % 1000) / 100),
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return hours + ":" + minutes + ":" + seconds;
}
async function collect(interaction, user) {
client.connect(async (err, db) => {
var dbo = db.db('test')
let id = `${interaction.guildId}-${interaction.user.id}-${interaction.commandName}-${interaction.options._subcommand}`
dbo.collection('cooldowns').findOne({
_id: id
}, async function (err, results) {
let now = Date.now()
let time = now + 3600000 // plus one hour in ms
if (results) {
if (results.cooldown > now) {
time = new Date(results.cooldown).toUTCString()
return interaction.reply({
content: `The batch is still cooking, come back in **${msToTime(results.cooldown - now)}**`,
ephemeral: true
})
}
}
addCooldown(interaction.user.id, interaction.guildId, interaction.commandName, interaction.options._subcommand, time)
var ftext = [
"I ate the rest, sorry >.<",
"Blame Abra",
"Would you like some Milk with that?",
"Careful! They're still warm :3",
"Best Batch yet!",
"These, I bequeath unto thine own self upon this day. Thou must guard them safely.",
""
]
var i = Math.floor(Math.random() * ftext.length)
//funny text at the end
var amount = Math.floor(Math.random() * 30)
//random amount between 1 and 29
if (user.id == "378135175059603457") {
//Abra Bonus
amount *= 1.25
}
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: +amount }
}, {
upsert: true
})
var response = `<@${user.id}>, 🍪 you collected ${amou*nt} cookies!! 🍪 ${ftext[i]}`
interaction.reply({
content: response
})
})
})
}
function steal(interaction, user) {
client.connect(async (err, db) => {
if (err) throw err;
let target = interaction.options.getUser('target')
let dbo = db.db("test")
if (target.id == user.id) {
return interaction.reply({
content: `C'mon, don't embarass yourself! We all know you couldn't do that, even if you tried.`,
ephemeral: true,
})
}
// get your amount of cookies
let your = await dbo.collection('cookies').findOne({
_id: user.id
})
// get their amount of cookies
let their = await dbo.collection('cookies').findOne({
_id: target.id
})
try {
if (!your.cookies) {
return interaction.reply({
content: `You need at least 1 Cookie to attempt a Robbery`,
ephemeral: true,
})
}
if (!their.cookies) {
return interaction.reply({
content: `Shame on you! They don't even have any Cookies D:`,
ephemeral: true,
})
}
} catch (error) {
return interaction.reply({
content: `How awkward...I don't know some of you!`,
ephemeral: true,
})
}
let difficulty = []
for (var i = 0; i < their.cookies; i++) {
if (i < your.cookies) {
difficulty.push(false)
} else {
difficulty.push(true)
}
}
let result = Math.floor(Math.random() * their.cookies) + 1
let response
if (!difficulty[result]) {
// author lost
response = `You were unsuccessful at robbing **${target.username}** and lost **${result}** Cookie(s)! `
if (your.cookies - result <= 0) {
result = your.cookies
response += `That's all your Cookies gone.`
}
result *= -1
} else {
//author won
response = `You've successfully stolen **${result}** Cookie(s) from **${target.username}**! `
if (their.cookies - result <= 0) {
result = their.cookies
response += `They're completely dry now.`
}
}
interaction.reply({
content: response
})
//update author's cookies
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: +result } //will decrease if it's +negativeInteger
}, {
upsert: true
})
await cookieSchema.findOneAndUpdate({
_id: target.id
}, {
_id: target.id,
user: target.username,
$inc: { cookies: -result } //will decrease if it's +negativeInteger
}, {
upsert: true
})
})
}
function check(interaction, user) {
client.connect(async function (err, db) {
let target = interaction.options.getUser('target')
if (!target) {
target = user
}
let response = `**${target.username}** has `
let dbo = db.db("test")
dbo.collection("cookies").findOne({
_id: target.id
}, function (err, results) {
let cookies
if (!results) {
cookies = 0
response += `**${cookies}** Cookies! 😔`
} else {
cookies = results.cookies
response += `**${cookies}** Cookies! 😲`
}
interaction.reply({
content: response
})
})
})
}
async function give(interaction, user) {
client.connect(async function (err, db) {
let target = interaction.options.getUser('target')
let amount = interaction.options.getInteger('amount')
let dbo = db.db('test')
//check if you have enough cookies
let your = await dbo.collection('cookies').findOne({
_id: user.id
})
if (target.id == user.id) {
return interaction.reply({
content: 'You cannot give yourself Cookies ._."'
})
}
if (your.cookies < amount) {
return interaction.reply({
content: `We don't run a charity here, get more Cookies!`
})
}
if (amount < 1) {
return interaction.reply({
content: `**${user}** shared *checks notes* **${amount}** Cookie(s) with ${target}....wait a minute!!`,
})
}
//update user's cookies
await cookieSchema.findOneAndUpdate({
_id: user.id
}, {
_id: user.id,
user: user.username,
$inc: { cookies: -amount }
}, {
upsert: true
})
//update target's cookies
await cookieSchema.findOneAndUpdate({
_id: target.id
}, {
_id: target.id,
user: target.username,
$inc: { cookies: +amount }
}, {
upsert: true
})
await delay(200)
interaction.reply({
content: `**${user}** shared **${amount}** Cookie(s) with ${target}`
})
})
}
async function addCooldown(userId, guildId, command, subcommand, ms) {
let id = `${guildId}-${userId}-${command}-${subcommand}`
await cooldownSchema.findOneAndUpdate({
_id: id
}, {
_id: id,
command: command,
subcommand: subcommand,
cooldown: ms
}, {
upsert: true
})
}
async function leader(interaction) {
const results = await cookieSchema.find().sort({
cookies: -1
}).limit(10)
let cookieSorted = ''
let i = 0
results.forEach((result) => {
cookieSorted += `[${i + 1}] <@${result._id}> (${result.cookies})\n`
i++;
})
var embed = new Discord.MessageEmbed()
.setColor(0x0099FF)
.setThumbnail(interaction.guild.iconURL())
.setTimestamp()
.addFields({ name: `-Cookie Leaderboard-`, value: cookieSorted })
interaction.reply({
embeds: [embed]
})
}
module.exports = {
slash: true,
description: 'collect a cookie',
category: 'Misc',
options: [
//collect
{
name: "collect",
description: "want some?",
type: 1 // SubCommand Type
},
//steal
{
name: "steal",
description: "try your luck at a lala robbery!",
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
required: true,
type: 6 // User Type
}
]
},
//check
{
name: "check",
description: "check your cookie stash",
required: false,
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
type: 6 // User Type
}
]
},
//give
{
name: "give",
description: "share your cookies with someone",
required: false,
type: 1, // SubCommand Type
options: [
{
name: "target",
description: "someone",
required: true,
type: 6 // User Type
}, {
name: "amount",
description: "cookies",
required: true,
type: 4 // Integer Type
}
]
},
//leader
{
name: "leader",
description: "show Top 10 Cookie Clickers",
type: 1, // SubCommand Type
},
],
callback: ({ interaction, user }) => {
switch (interaction.options._subcommand) {
case "collect":
collect(interaction, user)
break;
case "steal":
steal(interaction, user)
break;
case "check":
check(interaction, user)
break;
case "give":
give(interaction, user)
break;
case "leader":
leader(interaction)
break;
default:
break;
}
}
}
-31
View File
@@ -1,31 +0,0 @@
module.exports = {
slash: true,
description: 'surprise!',
category: 'meme',
options: [
{
name: 'pwned',
description: "Told you not to click on this",
required: true,
type: 3,
choices: [
{ "name": 'never', "value": "never" },
{ "name": 'gonna', "value": "gonna" },
{ "name": 'give', "value": "give" },
{ "name": 'you', "value": "you" },
{ "name": 'up', "value": "up" },
{ "name": 'never', "value": "never" },
{ "name": 'gonna', "value": "gonna" },
{ "name": 'let', "value": "let" },
{ "name": 'you', "value": "you" },
{ "name": 'down', "value": "down" },
],
},
],
callback: async ({ interaction }) => {
interaction.reply({
content: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
ephemeral: true,
})
}
}
-209
View File
@@ -1,209 +0,0 @@
const { MongoClient, ServerApiVersion } = require('mongodb');
require('dotenv').config();
const uri = process.env.URI
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1
});
const expansions = new Map([
["ARR", { "min": 0, "max": 50 }],
["HW", { "min": 51, "max": 60 }],
["SB", { "min": 61, "max": 70 }],
["ShB", { "min": 71, "max": 80 }],
["EW", { "min": 81, "max": 90 }],
])
async function getDutyData(interaction, database, user) {
var parameters = {
"type": interaction.options.getString('type'),
"min": interaction.options.getInteger('minlevel'),
"max": interaction.options.getInteger('maxlevel'),
"expansion": interaction.options.getString('expansion')
}
if (!parameters.type) {
//no type specified, randomizing...
database.listCollections().toArray(function (err, results) {
if (err) throw err;
let random = Math.floor(Math.random() * results.length)
parameters.type = results[random].name
})
}
if (parameters.expansion) {
//check if paramters are set correctly
if (!parameters.min) {
parameters.min = expansions.get(parameters.expansion).min
}
if (parameters.min < expansions.get(parameters.expansion).min) {
return console.log("mismatch min level and expansion")
}
if (!parameters.max) {
parameters.max = expansions.get(parameters.expansion).max
}
if (parameters.max > expansions.get(parameters.expansion).max) {
return console.log("mismatch max level and expansion")
}
} else {
if (!parameters.min) { parameters.min = 0 }
if (!parameters.max) { parameters.max = 90 }
}
if (parameters.type == 'pvp') {
parameters.expansion = 'ARR'
parameters.min = expansions.get(parameters.expansion).min
parameters.max = expansions.get(parameters.expansion).max
}
await delay(500) //wait for updates
database.collection(parameters.type).find({
level: { '$gte': parameters.min, '$lte': parameters.max }
}).toArray(async function (err, results2) {
let random = Math.floor(Math.random() * results2.length)
try {
let duty = results2[random]
let reply = `<@${user.id}>, Thou shalt playeth...${duty.name} [Level: ${duty.level}, Type: ${parameters.type}`
if (duty.expansion) {
reply += `, Expansion: ${duty.expansion}]`
} else {
reply += ']'
}
interaction.reply({
content: reply
})
} catch (error) {
interaction.reply({
content: `WAA!! (Impossible Combination found)`
})
}
})
}
async function getClassData(interaction, database, user) {
var collection = interaction.options.getString('type')
if (!collection) {
//no type specified
database.listCollections().toArray(function (err, cResults) {
let rdm = Math.floor(Math.random() * cResults.length)
collection = cResults[rdm].name
})
}
await delay(200) //wait for database.listCollections()
database.collection(collection).find({}).toArray(async function (err, results) {
if (err) throw err;
var response
try {
let rdm = Math.floor(Math.random() * results.length)
response = `<@${user.id}>, Thou shalt playeth...${results[rdm].job}`
} catch (error) {
response = 'Invalid Type specified!'
}
interaction.reply({
content: response,
})
})
}
module.exports = {
category: 'Random',
description: 'randomized helper', // Required for slash commands
slash: true, // Create both a slash and legacy command
options: [
{
name: "duty",
type: 1,
description: "random duty",
options: [
{
name: 'type',
description: 'what kind of duty would you like?',
required: false,
type: 3,
choices: [
{ "name": 'Dungeons', "value": "dungeons" },
{ "name": 'Guildhests', "value": "guildhests" },
{ "name": 'PvP', "value": "pvp" },
{ "name": 'Normal Raids', "value": "nraids" },
{ "name": 'Alliance Raids', "value": "araids" },
{ "name": 'Trials', "value": "trials" },
{ "name": 'Extreme', "value": "extreme" },
{ "name": 'Savage', "value": "savage" },
{ "name": 'Ultimates', "value": "ultimates" },
{ "name": 'Roulettes', "value": "roulettes" },
],
},
{
name: 'minlevel',
description: 'Start of Level Range, assuming 0 if not specified',
required: false,
type: 4,
},
{
name: 'maxlevel',
description: 'End of Level Range, assuming 90 if not specified',
required: false,
type: 4,
},
{
name: 'expansion',
description: 'which expansion would you like?',
required: false,
type: 3,
choices: [
{ "name": 'A Realm Reborn', "value": "ARR" },
{ "name": 'Heavensward', "value": "HW" },
{ "name": 'Stormblood', "value": "SB" },
{ "name": 'Shadowbringers', "value": "ShB" },
{ "name": 'Endwalker', "value": "EW" },
],
}
],
},
{
name: "class",
type: 1,
description: "random class",
options: [
{
name: 'type',
description: 'specify class',
required: false,
type: 3,
choices: [
{ "name": 'Healer', "value": "healer" },
{ "name": 'Tank', "value": "tank" },
{ "name": 'Melee', "value": "melee" },
{ "name": 'Ranged', "value": "ranged" },
{ "name": 'Caster', "value": "caster" },
],
}
]
}
],
callback: ({ interaction, user }) => {
switch (interaction.options._subcommand) {
case "duty":
client.connect(function (err, db) {
if (err) throw err;
var dbDuties = db.db("Duties")
getDutyData(interaction, dbDuties, user)
})
break;
case "class":
client.connect(async function (err, db) {
if (err) throw err;
var dbClasses = db.db("Classes")
getClassData(interaction, dbClasses, user)
})
default:
break;
}
},
}
-10
View File
@@ -1,10 +0,0 @@
module.exports = {
slash: true,
description: 'sussy baka',
category: 'meme',
callback: async ({ interaction }) => {
interaction.reply({
content: "ඞ"
})
}
}
-29
View File
@@ -1,29 +0,0 @@
module.exports = {
slash: true,
description: '',
category: '',
options: [
{
name: 'victim',
description: 'pick anyone >:D',
required: true,
type: 9,
options: [
{
name: 'type',
description: 'what kind of duty would you like?',
required: false,
type: 3, //String
choices: [
{ "name": 'choice', "value": "value" },
],
autocomplete: true
},
],
},
],
callback: async ({ interaction }) => {
}
}
-26
View File
@@ -1,26 +0,0 @@
module.exports = {
slash: true,
description: 'waa! at someone',
category: 'Misc',
cooldown: '3h',
options: [
{
name: 'victim',
description: 'pick anyone >:D',
required: true,
type: 9,
},
],
callback: async ({ interaction, args, client }) => {
var reply = 'waa!'
const member = client.users.cache.get(args[0]);
if(args[0]){
reply+= ` <@${member.id}>`
}
interaction.reply({
content: reply,
tts: true
})
}
}
-15
View File
@@ -1,15 +0,0 @@
[
{"name": "The Labyrinth of the Ancients", "level": 50, "expansion": "ARR"},
{"name": "Syrcus Tower", "level": 50, "expansion": "ARR"},
{"name": "The World of Darkness", "level": 50, "expansion": "ARR"},
{"name": "The Void Ark", "level": 60, "expansion": "HW"},
{"name": "The Weeping City of Mhach", "level": 60, "expansion": "HW"},
{"name": "Dun Scaith", "level": 60, "expansion": "HW"},
{"name": "The Royal City of Rabanastre", "level": 70, "expansion": "SB"},
{"name": "The Ridorana Lighthouse", "level": 70, "expansion": "SB"},
{"name": "The Orbonne Monastery", "level": 70, "expansion": "SB"},
{"name": "The Copied Factory", "level": 80, "expansion": "ShB"},
{"name": "The Puppets' Bunker", "level": 80, "expansion": "ShB"},
{"name": "The Tower at Paradigm's Breach", "level": 80, "expansion": "ShB"},
{"name": "Aglaia", "level": 90, "expansion": "EW"}
]
-88
View File
@@ -1,88 +0,0 @@
[
{"name": "Sastasha", "level": 15, "expansion": "ARR"},
{"name": "The Tam-Tara Deepcroft", "level": 16, "expansion": "ARR"},
{"name": "Copperbell Mines", "level": 17, "expansion": "ARR"},
{"name": "Halatali", "level": 20, "expansion": "ARR"},
{"name": "The Thousand Maws of Toto-Rak", "level": 24, "expansion": "ARR"},
{"name": "Haukke Manor", "level": 28, "expansion": "ARR"},
{"name": "Brayflox's Longstop", "level": 32, "expansion": "ARR"},
{"name": "The Sunken Temple of Quarn", "level": 35, "expansion": "ARR"},
{"name": "Cutter's Cry", "level": 41, "expansion": "ARR"},
{"name": "The Stone Vigil", "level": 44, "expansion": "ARR"},
{"name": "Dzemael Darkhold", "level": 47, "expansion": "ARR"},
{"name": "The Aurum Vale", "level": 50, "expansion": "ARR"},
{"name": "The Wanderer's Palace", "level": 50, "expansion": "ARR"},
{"name": "Castrum Meridianum", "level": 50, "expansion": "ARR"},
{"name": "The Praetorium", "level": 50, "expansion": "ARR"},
{"name": "Amdapor Keep", "level": 50, "expansion": "ARR"},
{"name": "Pharos Sirius", "level": 50, "expansion": "ARR"},
{"name": "Copperbell Mines (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Haukke Manor (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Lost City of Amdapor", "level": 50, "expansion": "ARR"},
{"name": "Halatali (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Brayflox's Longstop (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Hullbreaker Isle", "level": 50, "expansion": "ARR"},
{"name": "The Tam-Tara Deepcroft (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Stone Vigil (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Snowcloak", "level": 50, "expansion": "ARR"},
{"name": "Sastasha (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Sunken Temple of Quarn (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Keeper of the Lake", "level": 50, "expansion": "ARR"},
{"name": "The Wanderer's Palace (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Amdapor Keep (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Dusk Vigil", "level": 51, "expansion": "HW"},
{"name": "Sohm Al", "level": 53, "expansion": "HW"},
{"name": "The Aery", "level": 55, "expansion": "HW"},
{"name": "The Vault", "level": 57, "expansion": "HW"},
{"name": "The Great Gubal Library", "level": 59, "expansion": "HW"},
{"name": "The Aetherochemical Research Facility", "level": 60, "expansion": "HW"},
{"name": "Neverreap", "level": 60, "expansion": "HW"},
{"name": "The Fractal Continuum", "level": 60, "expansion": "HW"},
{"name": "Saint Mocianne's Arboretum", "level": 60, "expansion": "HW"},
{"name": "Pharos Sirius (Hard)", "level": 60, "expansion": "HW"},
{"name": "The Antitower", "level": 60, "expansion": "HW"},
{"name": "The Lost City of Amdapor (Hard)", "level": 60, "expansion": "HW"},
{"name": "Sohr Khai", "level": 60, "expansion": "HW"},
{"name": "Hullbreaker Isle (Hard)", "level": 60, "expansion": "HW"},
{"name": "Xelphatol", "level": 60, "expansion": "HW"},
{"name": "The Great Gubal Library (Hard)", "level": 60, "expansion": "HW"},
{"name": "Baelsar's Wall", "level": 60, "expansion": "HW"},
{"name": "Sohm Al (Hard)", "level": 60, "expansion": "HW"},
{"name": "The Sirensong Sea", "level": 61, "expansion": "SB"},
{"name": "Shisui of the Violet Tides", "level": 63, "expansion": "SB"},
{"name": "Bardam's Mettle", "level": 65, "expansion": "SB"},
{"name": "Doma Castle", "level": 67, "expansion": "SB"},
{"name": "Castrum Abania", "level": 69, "expansion": "SB"},
{"name": "Ala Mhigo", "level": 70, "expansion": "SB"},
{"name": "Kugane Castle", "level": 70, "expansion": "SB"},
{"name": "The Temple of the Fist", "level": 70, "expansion": "SB"},
{"name": "The Drowned City of Skalla", "level": 70, "expansion": "SB"},
{"name": "Hell's Lid", "level": 70, "expansion": "SB"},
{"name": "The Fractal Continuum (Hard)", "level": 70, "expansion": "SB"},
{"name": "The Swallow's Compass", "level": 70, "expansion": "SB"},
{"name": "The Burn", "level": 70, "expansion": "SB"},
{"name": "Saint Mocianne's Arboretum (Hard)", "level": 70, "expansion": "SB"},
{"name": "The Ghimlyt Dark", "level": 70, "expansion": "SB"},
{"name": "Holminster", "level": 71, "expansion": "ShB"},
{"name": "Dohn Mheg", "level": 73, "expansion": "ShB"},
{"name": "The Qitana Ravel", "level": 75, "expansion": "ShB"},
{"name": "Malikah's Well", "level": 77, "expansion": "ShB"},
{"name": "Mt. Gulg", "level": 79, "expansion": "ShB"},
{"name": "Amaurot", "level": 80, "expansion": "ShB"},
{"name": "The Twinning", "level": 80, "expansion": "ShB"},
{"name": "Akadaemia Anyder", "level": 80, "expansion": "ShB"},
{"name": "The Grand Cosmos", "level": 80, "expansion": "ShB"},
{"name": "The Heroes' Gauntlet", "level": 80, "expansion": "ShB"},
{"name": "Matoya's Relict", "level": 80, "expansion": "ShB"},
{"name": "Paglth'an", "level": 80, "expansion": "ShB"},
{"name": "The Tower of Zot", "level": 81, "expansion": "EW"},
{"name": "The Tower of Babel", "level": 83, "expansion": "EW"},
{"name": "Vanaspati", "level": 85, "expansion": "EW"},
{"name": "Ktisis Hyperboreia", "level": 87, "expansion": "EW"},
{"name": "The Aitiascope", "level": 89, "expansion": "EW"},
{"name": "The Dead Ends", "level": 90, "expansion": "EW"},
{"name": "Smileton", "level": 90, "expansion": "EW"},
{"name": "The Stigma Dreamscape", "level": 90, "expansion": "EW"},
{"name": "Alzadaal's Legacy", "level": 90, "expansion": "EW"},
{"name": "The Fell Court of Troia", "level": 90, "expansion": "EW"}
]
-37
View File
@@ -1,37 +0,0 @@
[
{"name": "The Howling Eye (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Navel (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Bowl of Embers (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "Thornmarch (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Whorleater (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Striking Tree (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Akh Afah Amphitheatre (Extreme)", "level": 50, "expansion": "ARR"},
{"name": "The Limitless Blue (Extreme)", "level": 60, "expansion": "HW"},
{"name": "Thok ast Thok (Extreme)", "level": 60, "expansion": "HW"},
{"name": "The Minstrel's Ballad: Thordan's Reign", "level": 60, "expansion": "HW"},
{"name": "The Minstrel's Ballad: Nidhogg's Rage", "level": 60, "expansion": "HW"},
{"name": "Containment Bay S1T7 (Extreme)", "level": 60, "expansion": "HW"},
{"name": "Containment Bay P1T6 (Extreme)", "level": 60, "expansion": "HW"},
{"name": "Containment Bay Z1T9 (Extreme)", "level": 60, "expansion": "HW"},
{"name": "The Pool of Tribute (Extreme)", "level": 70, "expansion": "SB"},
{"name": "Emanation (Extreme)", "level": 70, "expansion": "SB"},
{"name": "The Minstrel's Ballad: Shinryu's Domain", "level": 70, "expansion": "SB"},
{"name": "The Minstrel's Ballad: Tsukuyomi's Pain", "level": 70, "expansion": "SB"},
{"name": "The Great Hunt (Extreme)", "level": 70, "expansion": "SB"},
{"name": "The Jade Stoa (Extreme)", "level": 70, "expansion": "SB"},
{"name": "Hells' Kier (Extreme)", "level": 70, "expansion": "SB"},
{"name": "The Wreath of Snakes (Extreme)", "level": 70, "expansion": "SB"},
{"name": "The Dancing Plague (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "The Crown of the Immaculate (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "The Minstrel's Ballad: Hades's Elegy", "level": 80, "expansion": "ShB"},
{"name": "Cinder Drift (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "Memoria Misera (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "The Seat of Sacrifice (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "Castrum Marinum (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "The Cloud Deck (Extreme)", "level": 80, "expansion": "ShB"},
{"name": "The Minstrel's Ballad: Zodiark's Fall", "level": 90, "expansion": "EW"},
{"name": "The Minstrel's Ballad: Hydaelyn's Call", "level": 90, "expansion": "EW"},
{"name": "The Minstrel's Ballad: Endsinger's Aria", "level": 90, "expansion": "EW"},
{"name": "Containment Bay S1T7 (Unreal)", "level": 90, "expansion": "EW"},
{"name": "Storm's Crown (Extreme)", "level": 90, "expansion": "EW"}
]
-16
View File
@@ -1,16 +0,0 @@
[
{"name": "Basic Training: Enemy Parties", "level": 10, "expansion": "ARR"},
{"name": "Under the Armor", "level": 10, "expansion": "ARR"},
{"name": "Basic Training: Enemy Strongholds", "level": 15, "expansion": "ARR"},
{"name": "Hero on the Half Shell", "level": 15, "expansion": "ARR"},
{"name": "Pulling Poison Posies", "level": 20, "expansion": "ARR"},
{"name": "Stinging Back", "level": 20, "expansion": "ARR"},
{"name": "All's Well that Ends in the Well", "level": 25, "expansion": "ARR"},
{"name": "Flicking Sticks and Taking Names", "level": 25, "expansion": "ARR"},
{"name": "More than a Feeler", "level": 30, "expansion": "ARR"},
{"name": "Annoy the Void", "level": 30, "expansion": "ARR"},
{"name": "Shadow and Claw", "level": 35, "expansion": "ARR"},
{"name": "Long Live the Queen", "level": 35, "expansion": "ARR"},
{"name": "Ward Up", "level": 40, "expansion": "ARR"},
{"name": "Solemn Trinity", "level": 40, "expansion": "ARR"}
]
-59
View File
@@ -1,59 +0,0 @@
[
{"name": "The Binding Coil of Bahamut - Turn 1", "level": 50, "expansion": "ARR"},
{"name": "The Binding Coil of Bahamut - Turn 2", "level": 50, "expansion": "ARR"},
{"name": "The Binding Coil of Bahamut - Turn 3", "level": 50, "expansion": "ARR"},
{"name": "The Binding Coil of Bahamut - Turn 4", "level": 50, "expansion": "ARR"},
{"name": "The Binding Coil of Bahamut - Turn 5", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut - Turn 1", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut - Turn 2", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut - Turn 3", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut - Turn 4", "level": 50, "expansion": "ARR"},
{"name": "The Final Coil of Bahamut - Turn 1", "level": 50, "expansion": "ARR"},
{"name": "The Final Coil of Bahamut - Turn 2", "level": 50, "expansion": "ARR"},
{"name": "The Final Coil of Bahamut - Turn 3", "level": 50, "expansion": "ARR"},
{"name": "The Final Coil of Bahamut - Turn 4", "level": 50, "expansion": "ARR"},
{"name": "Alexander - The Fist of the Father", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Cuff of the Father", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Arm of the Father", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Burden of the Father", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Fist of the Son", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Cuff of the Son", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Arm of the Son", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Burden of the Son", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Eyes of the Creator", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Breath of the Creator", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Heart of the Creator", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Soul of the Creator", "level": 60, "expansion": "HW"},
{"name": "Deltascape V1.0", "level": 70, "expansion": "SB"},
{"name": "Deltascape V2.0", "level": 70, "expansion": "SB"},
{"name": "Deltascape V3.0", "level": 70, "expansion": "SB"},
{"name": "Deltascape V4.0", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V1.0", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V2.0", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V3.0", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V4.0", "level": 70, "expansion": "SB"},
{"name": "Alphascape V1.0", "level": 70, "expansion": "SB"},
{"name": "Alphascape V2.0", "level": 70, "expansion": "SB"},
{"name": "Alphascape V3.0", "level": 70, "expansion": "SB"},
{"name": "Alphascape V4.0", "level": 70, "expansion": "SB"},
{"name": "Eden's Gate: Resurrection", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Descent", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Inundation", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Sepulture", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Fulmination", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Furor", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Iconoclasm", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Refulgence", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Umbra", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Litany", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Anamorphosis", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Eternity", "level": 80, "expansion": "ShB"},
{"name": "Asphodelos: The First Circle", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Second Circle", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Third Circle", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Fourth Circle", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Fifth Circle", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Sixth Circle", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Seventh Circle", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Eighth Circle", "level": 90, "expansion": "EW"}
]
-13
View File
@@ -1,13 +0,0 @@
[
{"name": "Crystalline Conflict (Casual Match)", "level": 30, "expansion": "ARR"},
{"name": "Crystalline Conflict (Ranked Match)", "level": 30, "expansion": "ARR"},
{"name": "Crystalline Conflict (Custom Match - The Palaistra)", "level": 30, "expansion": "ARR"},
{"name": "Crystalline Conflict (Custom Match - The Volcanic Heart)", "level": 30, "expansion": "ARR"},
{"name": "Crystalline Conflict (Custom Match - Cloud Nine)", "level": 30, "expansion": "ARR"},
{"name": "The Borderland Ruins (Secure)", "level": 30, "expansion": "ARR"},
{"name": "Seal Rock (Seize)", "level": 30, "expansion": "ARR"},
{"name": "The Fields of Glory (Shatter)", "level": 30, "expansion": "ARR"},
{"name": "Onsal Hakair (Danshig Naadam)", "level": 30, "expansion": "ARR"},
{"name": "Astragalos", "level": 30, "expansion": "ARR"},
{"name": "Hidden Gorge", "level": 30, "expansion": "ARR"}
]
-13
View File
@@ -1,13 +0,0 @@
[
{"name": "Expert", "level": 90},
{"name": "Level 90 Dungeons", "level": 90},
{"name": "Level 50/60/70/80", "level": 50},
{"name": "Leveling", "level": 16},
{"name": "Trials", "level": 50},
{"name": "Main Scenario", "level": 50},
{"name": "Guildhests", "level": 10},
{"name": "Alliance Raid", "level": 50},
{"name": "Normal Raid", "level": 60},
{"name": "Mentor", "level": 90},
{"name": "Frontline", "level": 30}
]
-50
View File
@@ -1,50 +0,0 @@
[
{"name": "The Second Coil of Bahamut (Savage) - Turn 1", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut (Savage) - Turn 2", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut (Savage) - Turn 3", "level": 50, "expansion": "ARR"},
{"name": "The Second Coil of Bahamut (Savage) - Turn 4", "level": 50, "expansion": "ARR"},
{"name": "Alexander - The Fist of the Father (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Cuff of the Father (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Arm of the Father (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Burden of the Father (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Fist of the Son (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Cuff of the Son (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Arm of the Son (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Burden of the Son (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Eyes of the Creator (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Breath of the Creator (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Heart of the Creator (Savage)", "level": 60, "expansion": "HW"},
{"name": "Alexander - The Soul of the Creator (Savage)", "level": 60, "expansion": "HW"},
{"name": "Deltascape V1.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Deltascape V2.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Deltascape V3.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Deltascape V4.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V1.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V2.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V3.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Sigmascape V4.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Alphascape V1.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Alphascape V2.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Alphascape V3.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Alphascape V4.0 (Savage)", "level": 70, "expansion": "SB"},
{"name": "Eden's Gate: Resurrection (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Descent (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Inundation (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Gate: Sepulture (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Fulmination (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Furor (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Iconoclasm (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Verse: Refulgence (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Umbra (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Litany (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Anamorphosis (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Eden's Promise: Eternity (Savage)", "level": 80, "expansion": "ShB"},
{"name": "Asphodelos: The First Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Second Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Third Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Asphodelos: The Fourth Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Fifth Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Sixth Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Seventh Circle (Savage)", "level": 90, "expansion": "EW"},
{"name": "Abyssos: The Eighth Circle (Savage)", "level": 90, "expansion": "EW"}
]
-48
View File
@@ -1,48 +0,0 @@
[
{"name": "The Bowl of Embers", "level": 20, "expansion": "ARR"},
{"name": "The Navel", "level": 34, "expansion": "ARR"},
{"name": "The Howling Eye", "level": 44, "expansion": "ARR"},
{"name": "The Porta Decumana", "level": 50, "expansion": "ARR"},
{"name": "The Chrysalis", "level": 50, "expansion": "ARR"},
{"name": "A Relic Reborn: the Chimera", "level": 50, "expansion": "ARR"},
{"name": "A Relic Reborn: the Hydra", "level": 50, "expansion": "ARR"},
{"name": "Battle on the Big Bridge", "level": 50, "expansion": "ARR"},
{"name": "The Dragon's Neck", "level": 50, "expansion": "ARR"},
{"name": "Battle in the Big Keep", "level": 50, "expansion": "ARR"},
{"name": "The Bowl of Embers (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Howling Eye (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Navel (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Thornmarch (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Whorleater (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Striking Tree (Hard)", "level": 50, "expansion": "ARR"},
{"name": "The Akh Afah Amphitheatre (Hard)", "level": 50, "expansion": "ARR"},
{"name": "Urth's Fount", "level": 50, "expansion": "ARR"},
{"name": "The Minstrel's Ballad: Ultima's Bane", "level": 50, "expansion": "ARR"},
{"name": "Thok ast Thok (Hard)", "level": 53, "expansion": "HW"},
{"name": "The Limitless Blue (Hard)", "level": 57, "expansion": "HW"},
{"name": "The Singularity Reactor", "level": 60, "expansion": "HW"},
{"name": "The Final Steps of Faith", "level": 60, "expansion": "HW"},
{"name": "Containment Bay S1T7", "level": 60, "expansion": "HW"},
{"name": "Containment Bay P1T6", "level": 60, "expansion": "HW"},
{"name": "Containment Bay Z1T9", "level": 60, "expansion": "HW"},
{"name": "The Pool of Tribute", "level": 63, "expansion": "SB"},
{"name": "Emanation", "level": 67, "expansion": "SB"},
{"name": "The Royal Menagerie", "level": 70, "expansion": "SB"},
{"name": "Castrum Fluminis", "level": 70, "expansion": "SB"},
{"name": "Kugane Ohashi", "level": 70, "expansion": "SB"},
{"name": "The Great Hunt", "level": 70, "expansion": "SB"},
{"name": "The Jade Stoa", "level": 70, "expansion": "SB"},
{"name": "Hells' Kier", "level": 70, "expansion": "SB"},
{"name": "The Wreath of Snakes", "level": 70, "expansion": "SB"},
{"name": "The Dancing Plague", "level": 73, "expansion": "ShB"},
{"name": "The Crown of the Immaculate", "level": 79, "expansion": "ShB"},
{"name": "The Dying Gasp", "level": 80, "expansion": "ShB"},
{"name": "Cinder Drift", "level": 80, "expansion": "ShB"},
{"name": "The Seat of Sacrifice", "level": 80, "expansion": "ShB"},
{"name": "Castrum Marinum", "level": 80, "expansion": "ShB"},
{"name": "The Cloud Deck", "level": 80, "expansion": "ShB"},
{"name": "The Dark Inside", "level": 83, "expansion": "EW"},
{"name": "The Mothercrystal", "level": 89, "expansion": "EW"},
{"name": "The Final Day", "level": 90, "expansion": "EW"},
{"name": "Storm's Crown", "level": 90, "expansion": "EW"}
]
-6
View File
@@ -1,6 +0,0 @@
[
{"name": "The Unending Coil of Bahamut (Ultimate)", "level": 70, "expansion": "SB"},
{"name": "The Weapon's Refrain (Ultimate)", "level": 70, "expansion": "SB"},
{"name": "The Epic of Alexander (Ultimate)", "level": 80, "expansion": "ShB"},
{"name": "Dragonsong's Reprise (Ultimate)", "level": 90, "expansion": "EW"}
]
+20
View File
@@ -0,0 +1,20 @@
const { Events, ActivityType } = require('discord.js');
const { mClient } = require('..');
module.exports = {
name: Events.ClientReady,
once: true,
async execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
// Set presence
client.user.setPresence({
activities: [{
name: 'Surely this time, right...?',
type: ActivityType.Streaming,
url: 'https://twitch.tv/desq_blocki'
}],
status: 'online'
});
}
};
+98
View File
@@ -0,0 +1,98 @@
const { Events, MessageFlags, Message } = require('discord.js')
const { mClient } = require('../index')
require('dotenv').config()
module.exports = {
name: Events.InteractionCreate,
once: false,
async execute(interaction) {
if (interaction.isChatInputCommand()) { commandHandler(interaction) }
if (interaction.isButton()) { buttonHandler(interaction) }
if (interaction.isMentionableSelectMenu()) { selectMenuHandler(interaction) }
if (interaction.isUserSelectMenu()) { selectMenuHandler(interaction) }
if (interaction.isModalSubmit()) { modalHandler(interaction) }
}
}
async function commandHandler(interaction) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) { return console.error(`No command matching ${interaction.commandName} was found.`) }
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
} else {
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
}
}
}
async function buttonHandler(interaction) {
const button = interaction.client.buttons.get(interaction.customId)
if (!button) { return console.error(`No button registered matching ${interaction.customId}.`) }
try {
await button.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
} else {
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
}
}
}
async function selectMenuHandler(interaction) {
const selectMenu = interaction.client.selectMenus.get(interaction.customId)
if (!selectMenu) { return console.error(`No Select Menu registered matching ${interaction.customId}.`) }
try {
await selectMenu.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
} else {
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral
});
}
}
}
async function modalHandler(interaction) {
const modal = interaction.client.modals.get(interaction.customId);
if (!modal) { return console.error(`No Modal registered matching ${interaction.customId}.`); }
try {
await modal.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: 'There was an error while executing this modal!',
flags: MessageFlags.Ephemeral
});
} else {
await interaction.reply({
content: 'There was an error while executing this modal!',
flags: MessageFlags.Ephemeral
});
}
}
}
+20
View File
@@ -0,0 +1,20 @@
const fs = require('node:fs')
const path = require('node:path')
module.exports = (client) => {
const buttonsFoldersPath = path.join(__dirname, '../buttons');
const buttonsFolders = fs.readdirSync(buttonsFoldersPath);
let buttonCount = 0
for (const folder of buttonsFolders) {
const buttonsPath = path.join(buttonsFoldersPath, folder);
const buttonsFiles = fs.readdirSync(buttonsPath).filter(file => file.endsWith('.js'));
for (const file of buttonsFiles) {
buttonCount++
const filePath = path.join(buttonsPath, file);
const button = require(filePath);
if ('execute' in button) {
client.buttons.set(button.name, button);
}
}
}
console.log(`[${buttonCount}] Button(s) initialized`)
}
+41
View File
@@ -0,0 +1,41 @@
const { REST, Routes } = require('discord.js')
const fs = require('node:fs')
const path = require('node:path')
module.exports = (client) => {
let commands = [] // only used for REST API
const foldersPath = path.join(__dirname, '../commands');
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
client.commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
const rest = new REST().setToken(process.env.D_TOKEN);
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(process.env.D_ID, process.env.D_GID),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
}
+18
View File
@@ -0,0 +1,18 @@
const fs = require('node:fs')
const path = require('node:path')
module.exports = (client) => {
const eventsPath = path.join(__dirname, '../events')
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'))
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file)
const event = require(filePath)
if (event.once) {
client.once(event.name, (...args) => event.execute(...args, client))
// added client to commomerate global usage
} else {
client.on(event.name, (...args) => event.execute(...args, client))
// added client to commomerate global usage
}
}
}
+20
View File
@@ -0,0 +1,20 @@
const fs = require('node:fs')
const path = require('node:path')
module.exports = (client) => {
const selectMenusFoldersPath = path.join(__dirname, '../selectMenus');
const selectMenusFolders = fs.readdirSync(selectMenusFoldersPath);
let selectMenuCount = 0
for (const folder of selectMenusFolders) {
const Path = path.join(selectMenusFoldersPath, folder);
const Files = fs.readdirSync(Path).filter(file => file.endsWith('.js'));
for (const file of Files) {
selectMenuCount++
const filePath = path.join(Path, file);
const button = require(filePath);
if ('execute' in button) {
client.selectMenus.set(button.name, button);
}
}
}
console.log(`[${selectMenuCount}] Select Menu(s) initialized`)
}
+33 -71
View File
@@ -1,71 +1,33 @@
const DiscordJS = require('discord.js') const { Client, GatewayIntentBits, Collection, Partials } = require('discord.js')
const WOKCommands = require('wokcommands') const { MongoClient } = require('mongodb')
const path = require('path') require('dotenv').config()
require('dotenv').config(); const mClient = new MongoClient(process.env.M_URI)
exports.mClient = mClient;
const { Intents } = DiscordJS const fs = require('node:fs')
const client = new DiscordJS.Client({
intents: [ const client = new Client({
Intents.FLAGS.GUILDS, intents: [
Intents.FLAGS.GUILD_MESSAGES, GatewayIntentBits.Guilds,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS, GatewayIntentBits.GuildMembers,
], GatewayIntentBits.GuildMessages,
}) GatewayIntentBits.GuildPresences,
/* GatewayIntentBits.GuildMessageReactions,
To-Do: GatewayIntentBits.GuildVoiceStates,
- animal spawn time with weather condition check GatewayIntentBits.DirectMessages,
- lodestone lookup???? GatewayIntentBits.MessageContent,
- change database structure: duties in one, filter by type (e.g. Dungeon, Raid. etc); classes in one, filter by type (e.g. Tank, Healer, etc) ],
*/ partials: [Partials.Channel, Partials.Message, Partials.User, Partials.GuildMember, Partials.Reaction]
});
client.on('ready', async () => { exports.client = client
const dbOptions = {
keepAlive: true client.commands = new Collection() // slash commands collection
} client.legacyCommands = new Collection() // legacy commands collection
new WOKCommands(client, { client.aliases = new Collection() // list of command aliases
commandsDir: path.join(__dirname, 'commands'), client.buttons = new Collection() // list of buttons
featuresDir: path.join(__dirname, 'features'), client.selectMenus = new Collection() // list of selectMenus
messagesPath: '',
typeScript: false, fs.readdirSync('./handlers').forEach((handler) => {
showWarns: true, require(`./handlers/${handler}`)(client)
delErrMsgCooldown: -1, });
defaultLangauge: 'english',
ignoreBots: false, client.login(process.env.D_TOKEN)
ephemeral: false,
dbOptions: {
// These are the default options
keepAlive: true
},
testServers: ['848610258306072576'],
botOwners: ['140508899064283136'],
disabledDefaultCommands: [
// 'help',
// 'command',
// 'language',
// 'prefix',
// 'requiredrole',
// 'channelonly'
],
mongoUri: process.env.URI,
debug: false
})
.setDefaultPrefix('$')
// Used for the color of embeds sent by WOKCommands
.setColor(0xff0000)
})
client.login(process.env.TOKEN)
/*
SUB_COMMAND 1
SUB_COMMAND_GROUP 2
STRING 3
INTEGER 4 Any integer between -2^53 and 2^53
BOOLEAN 5
USER 6
CHANNEL 7 Includes all channel types + categories
ROLE 8
MENTIONABLE 9 Includes users and roles
NUMBER 10 Any double between -2^53 and 2^53
ATTACHMENT 11 attachment object
*/
+335 -843
View File
File diff suppressed because it is too large Load Diff
+9 -5
View File
@@ -1,18 +1,22 @@
{ {
"name": "shaitest", "name": "shaibot",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": {
"type": "git",
"url": "https://git.desq-gaming.de/admin_rb/ShaiBot.git"
},
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"type": "commonjs",
"dependencies": { "dependencies": {
"dotenv": "^16.0.2", "discord.js": "^14.27.0",
"mongodb": "^4.9.1", "dotenv": "^17.4.2",
"mongoose": "^6.6.0", "mongodb": "^7.5.0"
"wokcommands": "^1.5.3"
} }
} }
-18
View File
@@ -1,18 +0,0 @@
const { default: mongoose } = require("mongoose");
const reqString = {
type: String,
required: true,
}
const reqInteger = {
type: Number,
required: true,
}
const classSchema = new mongoose.Schema({
_id: reqInteger,
job: reqString
})
module.exports = mongoose.model('classes', classSchema)
-19
View File
@@ -1,19 +0,0 @@
const mongoose = require('mongoose')
const reqString = {
type: String,
required: true,
}
const reqInteger = {
type: Number,
required: true,
}
const cookieSchema = new mongoose.Schema({
//Guild ID
_id: reqString,
user: reqString,
cookies: reqInteger
})
module.exports = mongoose.model('Cookies', cookieSchema)
-20
View File
@@ -1,20 +0,0 @@
const { default: mongoose } = require("mongoose");
const reqString = {
type: String,
required: true,
}
const reqInteger = {
type: Number,
required: true,
}
const cooldownSchema = new mongoose.Schema({
_id: reqString,
command: reqString,
subcommand: reqString,
cooldown: reqInteger
})
module.exports = mongoose.model('cooldowns', cooldownSchema)