added new logic to the new rewards
This commit is contained in:
@@ -1,51 +1,54 @@
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildAchievementsEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileAchievements",
|
||||
name:
|
||||
"profileAchievements",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildAchievementsEmbed(
|
||||
const embed =
|
||||
await buildAchievementsEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
0,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
0
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileAchievementsLeft",
|
||||
"profileAchievementsRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"achievements",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,53 +1,54 @@
|
||||
// buttons/profile/achievementsLeft.js
|
||||
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildAchievementsEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileAchievementsLeft",
|
||||
name:
|
||||
"profileAchievementsLeft",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildAchievementsEmbed(
|
||||
const embed =
|
||||
await buildAchievementsEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
session.page - 1,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
context.page - 1
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileAchievementsLeft",
|
||||
"profileAchievementsRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"achievements",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,53 +1,54 @@
|
||||
// buttons/profile/achievementsRight.js
|
||||
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildAchievementsEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileAchievementsRight",
|
||||
name:
|
||||
"profileAchievementsRight",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildAchievementsEmbed(
|
||||
const embed =
|
||||
await buildAchievementsEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
session.page + 1,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
context.page + 1
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileAchievementsLeft",
|
||||
"profileAchievementsRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"achievements",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
+30
-27
@@ -1,51 +1,54 @@
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildBadgesEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileBadges",
|
||||
name:
|
||||
"profileBadges",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildBadgesEmbed(
|
||||
const embed =
|
||||
await buildBadgesEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
0,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
0
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileBadgesLeft",
|
||||
"profileBadgesRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"badges",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,53 +1,54 @@
|
||||
// buttons/profile/badgesLeft.js
|
||||
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildBadgesEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileBadgesLeft",
|
||||
name:
|
||||
"profileBadgesLeft",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildBadgesEmbed(
|
||||
const embed =
|
||||
await buildBadgesEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
session.page - 1,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
context.page - 1
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileBadgesLeft",
|
||||
"profileBadgesRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"badges",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,53 +1,54 @@
|
||||
// buttons/profile/badgesRight.js
|
||||
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildBadgesEmbed,
|
||||
buildProfileButtons,
|
||||
buildProfilePaginationButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
getProfileContextFromFooter,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileBadgesRight",
|
||||
name:
|
||||
"profileBadgesRight",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const result = await buildBadgesEmbed(
|
||||
const embed =
|
||||
await buildBadgesEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
session.page + 1,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId,
|
||||
context.page + 1
|
||||
);
|
||||
|
||||
const components = [
|
||||
...buildProfileButtons()
|
||||
];
|
||||
|
||||
if (result.pageCount > 1) {
|
||||
components.push(
|
||||
buildProfilePaginationButtons(
|
||||
"profileBadgesLeft",
|
||||
"profileBadgesRight",
|
||||
result.page,
|
||||
result.pageCount
|
||||
)
|
||||
const pageContext =
|
||||
getProfileContextFromFooter(
|
||||
embed.data.footer?.text
|
||||
);
|
||||
}
|
||||
|
||||
return interaction.update({
|
||||
embeds: [result.embed],
|
||||
components
|
||||
embeds: [embed],
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"badges",
|
||||
pageContext?.page ?? 0,
|
||||
pageContext?.pageCount ?? 1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
+26
-13
@@ -1,34 +1,47 @@
|
||||
const {
|
||||
requireProfileOwner,
|
||||
buildProfileStatsEmbed,
|
||||
buildProfileButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
buildStatsEmbed,
|
||||
buildProfileButtons,
|
||||
requireProfileOwner
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
name: "profileStats",
|
||||
name:
|
||||
"profileStats",
|
||||
|
||||
async execute(interaction) {
|
||||
const session = await requireProfileOwner(
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const context =
|
||||
await requireProfileOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
if (!session) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await interaction.client.users.fetch(
|
||||
session.profileUserId
|
||||
const user =
|
||||
await interaction.client.users.fetch(
|
||||
context.profileUserId
|
||||
);
|
||||
|
||||
const embed = await buildProfileStatsEmbed(
|
||||
const embed =
|
||||
await buildStatsEmbed(
|
||||
interaction.guildId,
|
||||
user,
|
||||
session.sessionOwnerId
|
||||
context.sessionOwnerId
|
||||
);
|
||||
|
||||
return interaction.update({
|
||||
embeds: [embed],
|
||||
components: buildProfileButtons()
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"stats",
|
||||
0,
|
||||
1
|
||||
)
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,46 +1,63 @@
|
||||
const { SlashCommandBuilder } = require("discord.js");
|
||||
const {
|
||||
SlashCommandBuilder
|
||||
} = require(
|
||||
"discord.js"
|
||||
);
|
||||
|
||||
const {
|
||||
buildProfileOverview,
|
||||
buildProfileButtons
|
||||
} = require("../../utils/profileSystem");
|
||||
const {
|
||||
createInteractionSession
|
||||
} = require("../../utils/interactionSession");
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("profile")
|
||||
.setDescription("View a player profile")
|
||||
.addUserOption(option =>
|
||||
data:
|
||||
new SlashCommandBuilder()
|
||||
.setName(
|
||||
"profile"
|
||||
)
|
||||
.setDescription(
|
||||
"View a player profile"
|
||||
)
|
||||
.addUserOption(
|
||||
option =>
|
||||
option
|
||||
.setName("user")
|
||||
.setDescription("Player to view")
|
||||
.setName(
|
||||
"user"
|
||||
)
|
||||
.setDescription(
|
||||
"Player to view"
|
||||
)
|
||||
),
|
||||
|
||||
async execute(interaction) {
|
||||
const user = interaction.options.getUser("user") ?? interaction.user;
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
const user =
|
||||
interaction.options.getUser(
|
||||
"user"
|
||||
) ??
|
||||
interaction.user;
|
||||
|
||||
const embed = await buildProfileOverview(
|
||||
const sessionOwnerId =
|
||||
interaction.user.id;
|
||||
|
||||
const embed =
|
||||
await buildProfileOverview(
|
||||
interaction.guildId,
|
||||
user,
|
||||
interaction.user.id
|
||||
sessionOwnerId
|
||||
);
|
||||
|
||||
await interaction.reply({
|
||||
return interaction.reply({
|
||||
embeds: [embed],
|
||||
components: buildProfileButtons()
|
||||
components:
|
||||
buildProfileButtons(
|
||||
"overview",
|
||||
0,
|
||||
1
|
||||
)
|
||||
});
|
||||
|
||||
const message = await interaction.fetchReply();
|
||||
|
||||
await createInteractionSession(
|
||||
interaction,
|
||||
{
|
||||
messageId: message.id,
|
||||
ownerId: interaction.user.id,
|
||||
kind: "profile",
|
||||
targetId: user.id
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,415 +1,263 @@
|
||||
const {
|
||||
SlashCommandBuilder,
|
||||
MessageFlags
|
||||
} = require(
|
||||
"discord.js"
|
||||
);
|
||||
PermissionFlagsBits,
|
||||
MessageFlags,
|
||||
EmbedBuilder
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
requireAdminAccess
|
||||
reconcileUserAchievements,
|
||||
reconcileAllAchievements,
|
||||
auditProfileData
|
||||
} = require(
|
||||
"../../utils/permissions"
|
||||
"../../utils/profileMaintenance"
|
||||
);
|
||||
|
||||
const {
|
||||
reconcileProfileAchievements,
|
||||
auditProfileSystem
|
||||
} = require(
|
||||
"../../utils/profileSystem"
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* COMMAND
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
data:
|
||||
new SlashCommandBuilder()
|
||||
.setName(
|
||||
"profileadmin"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"Profile and achievement administration."
|
||||
"Administrative profile maintenance tools"
|
||||
)
|
||||
.setDefaultMemberPermissions(
|
||||
PermissionFlagsBits.Administrator
|
||||
)
|
||||
|
||||
/*
|
||||
* =================================================
|
||||
* RECONCILE
|
||||
* =================================================
|
||||
*/
|
||||
|
||||
.addSubcommand(
|
||||
subcommand =>
|
||||
subcommand
|
||||
.setName(
|
||||
"reconcile"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"Re-evaluate stored statistics for achievements."
|
||||
"Backfill database-trackable achievements"
|
||||
)
|
||||
|
||||
.addUserOption(
|
||||
option =>
|
||||
option
|
||||
.setName(
|
||||
"user"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"Only reconcile this user's profile."
|
||||
)
|
||||
|
||||
.setRequired(
|
||||
false
|
||||
"Only reconcile this user; omit for everyone"
|
||||
)
|
||||
)
|
||||
|
||||
.addBooleanOption(
|
||||
option =>
|
||||
option
|
||||
.setName(
|
||||
"dry-run"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"Show what would change without unlocking anything."
|
||||
)
|
||||
|
||||
.setRequired(
|
||||
false
|
||||
"Show what would change without writing it"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
/*
|
||||
* =================================================
|
||||
* AUDIT
|
||||
* =================================================
|
||||
*/
|
||||
|
||||
.addSubcommand(
|
||||
subcommand =>
|
||||
subcommand
|
||||
.setName(
|
||||
"audit"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"Audit the profile and achievement system."
|
||||
"Audit stored profile statistics for inconsistencies"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* EXECUTE
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
async execute(
|
||||
interaction
|
||||
) {
|
||||
/*
|
||||
* Runtime authorization.
|
||||
*
|
||||
* This intentionally replaces:
|
||||
*
|
||||
* .setDefaultMemberPermissions(
|
||||
* PermissionFlagsBits.Administrator
|
||||
* )
|
||||
*
|
||||
* so BOT_AUTHOR_ID can also invoke the command.
|
||||
*/
|
||||
|
||||
if (
|
||||
!await requireAdminAccess(
|
||||
interaction
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const subcommand =
|
||||
interaction.options
|
||||
.getSubcommand();
|
||||
|
||||
|
||||
switch (
|
||||
subcommand
|
||||
) {
|
||||
case "reconcile":
|
||||
|
||||
return runReconcile(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
case "audit":
|
||||
|
||||
return runAudit(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
default:
|
||||
|
||||
return interaction.reply({
|
||||
content:
|
||||
"Unknown profile administration action.",
|
||||
interaction.options.getSubcommand();
|
||||
|
||||
await interaction.deferReply({
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* RECONCILE
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function runReconcile(
|
||||
interaction
|
||||
if (
|
||||
subcommand ===
|
||||
"reconcile"
|
||||
) {
|
||||
const target =
|
||||
const user =
|
||||
interaction.options.getUser(
|
||||
"user"
|
||||
);
|
||||
|
||||
|
||||
const dryRun =
|
||||
interaction.options.getBoolean(
|
||||
"dry-run"
|
||||
)
|
||||
??
|
||||
false;
|
||||
) ?? false;
|
||||
|
||||
|
||||
/*
|
||||
* This can touch multiple Mongo documents,
|
||||
* so acknowledge the interaction first.
|
||||
*/
|
||||
|
||||
await interaction.deferReply({
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
if (user) {
|
||||
const result =
|
||||
await reconcileProfileAchievements(
|
||||
await reconcileUserAchievements({
|
||||
guildId:
|
||||
interaction.guildId,
|
||||
{
|
||||
userId:
|
||||
target?.id
|
||||
??
|
||||
null,
|
||||
|
||||
user.id,
|
||||
dryRun
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
const lines = [
|
||||
dryRun
|
||||
? "### 🔎 Profile Reconciliation — Dry Run"
|
||||
: "### ✅ Profile Reconciliation Complete",
|
||||
|
||||
"",
|
||||
|
||||
target
|
||||
? `Target: ${target}`
|
||||
: "Target: **Entire Guild**",
|
||||
|
||||
"",
|
||||
|
||||
`Profiles checked: **${result.profilesChecked ?? 0}**`,
|
||||
`Achievement unlocks found: **${result.unlocksFound ?? 0}**`
|
||||
];
|
||||
|
||||
|
||||
if (
|
||||
!dryRun
|
||||
) {
|
||||
lines.push(
|
||||
`Achievements unlocked: **${result.unlocksApplied ?? 0}**`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
result.skippedEventOnly !==
|
||||
undefined
|
||||
) {
|
||||
lines.push(
|
||||
`Event-only checks skipped: **${result.skippedEventOnly}**`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
Array.isArray(
|
||||
result.details
|
||||
)
|
||||
&&
|
||||
result.details.length >
|
||||
0
|
||||
) {
|
||||
const preview =
|
||||
result.details
|
||||
.slice(
|
||||
0,
|
||||
15
|
||||
)
|
||||
.map(
|
||||
entry => {
|
||||
if (
|
||||
typeof entry ===
|
||||
"string"
|
||||
) {
|
||||
return `• ${entry}`;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
`• <@${entry.userId}> — ` +
|
||||
`${entry.achievementName ?? entry.achievementId}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
lines.push(
|
||||
"",
|
||||
"### Changes",
|
||||
...preview
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
result.details.length >
|
||||
preview.length
|
||||
) {
|
||||
lines.push(
|
||||
"",
|
||||
`*...and ${result.details.length - preview.length} more.*`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
lines.join(
|
||||
"\n"
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🛠️ Profile Reconciliation"
|
||||
)
|
||||
.setDescription(
|
||||
`${dryRun ? "Dry run for" : "Reconciled"} ${user}.`
|
||||
)
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
"New unlocks",
|
||||
value:
|
||||
`**${result.unlocked}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Definitions checked",
|
||||
value:
|
||||
`**${result.checked}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Event-only skipped",
|
||||
value:
|
||||
`**${result.skippedEventOnly}**`,
|
||||
inline:
|
||||
true
|
||||
}
|
||||
)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[ProfileAdmin] Reconciliation failed:",
|
||||
error
|
||||
);
|
||||
|
||||
const summary =
|
||||
await reconcileAllAchievements({
|
||||
guildId:
|
||||
interaction.guildId,
|
||||
dryRun
|
||||
});
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"Profile reconciliation failed. Check the bot logs for details."
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🛠️ Profile Reconciliation"
|
||||
)
|
||||
.setDescription(
|
||||
`${dryRun ? "Dry run completed" : "Reconciliation completed"} for stored profile users.`
|
||||
)
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
"Users",
|
||||
value:
|
||||
`**${summary.users}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"New unlocks",
|
||||
value:
|
||||
`**${summary.unlocked}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Event-only checks skipped",
|
||||
value:
|
||||
`**${summary.skippedEventOnly}**`,
|
||||
inline:
|
||||
true
|
||||
}
|
||||
)
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* AUDIT
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function runAudit(
|
||||
interaction
|
||||
) {
|
||||
await interaction.deferReply({
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const result =
|
||||
await auditProfileSystem(
|
||||
const audit =
|
||||
await auditProfileData(
|
||||
interaction.guildId
|
||||
);
|
||||
|
||||
|
||||
const lines = [
|
||||
"### 🧾 Profile System Audit",
|
||||
"",
|
||||
|
||||
`Profile documents: **${result.profileDocuments ?? 0}**`,
|
||||
`Achievement documents: **${result.achievementDocuments ?? 0}**`,
|
||||
`Active achievement definitions: **${result.activeAchievements ?? 0}**`,
|
||||
`Legacy achievement definitions: **${result.legacyAchievements ?? 0}**`,
|
||||
`Guild badges: **${result.badges ?? 0}**`,
|
||||
`Claimed badges: **${result.claimedBadges ?? 0}**`,
|
||||
`Available badges: **${result.availableBadges ?? 0}**`
|
||||
];
|
||||
|
||||
|
||||
if (
|
||||
Array.isArray(
|
||||
result.warnings
|
||||
)
|
||||
&&
|
||||
result.warnings.length >
|
||||
0
|
||||
) {
|
||||
lines.push(
|
||||
"",
|
||||
"### ⚠️ Warnings",
|
||||
|
||||
...result.warnings.map(
|
||||
warning =>
|
||||
`• ${warning}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
else {
|
||||
lines.push(
|
||||
"",
|
||||
"✅ No obvious profile-system issues were found."
|
||||
);
|
||||
}
|
||||
|
||||
const issuePreview =
|
||||
audit.issues
|
||||
.slice(0, 10)
|
||||
.join("\n");
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
lines.join(
|
||||
"\n"
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🔎 Profile Data Audit"
|
||||
)
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[ProfileAdmin] Audit failed:",
|
||||
error
|
||||
);
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"Profile audit failed. Check the bot logs for details."
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
"Stats documents",
|
||||
value:
|
||||
`**${audit.statDocuments}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Achievement documents",
|
||||
value:
|
||||
`**${audit.achievementDocuments}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Active achievements",
|
||||
value:
|
||||
`**${audit.activeAchievements}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Global badges",
|
||||
value:
|
||||
`**${audit.badgeDocuments}** (${audit.claimedBadges} claimed)`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Orphan achievement docs",
|
||||
value:
|
||||
`**${audit.orphanAchievementDocs}**`,
|
||||
inline:
|
||||
true
|
||||
},
|
||||
{
|
||||
name:
|
||||
"Issues",
|
||||
value:
|
||||
`**${audit.issueCount}**`,
|
||||
inline:
|
||||
true
|
||||
}
|
||||
)
|
||||
.setDescription(
|
||||
issuePreview
|
||||
? `First issues:\n${issuePreview}`
|
||||
: "No structural inconsistencies found."
|
||||
)
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ module.exports = {
|
||||
"records"
|
||||
)
|
||||
.setDescription(
|
||||
"View Shai Cookie and Crumb Saucer records"
|
||||
"View Olymp Cookie and Crumb Saucer records"
|
||||
)
|
||||
.addStringOption(
|
||||
option =>
|
||||
|
||||
@@ -27,30 +27,30 @@ const HEIST_METHODS = {
|
||||
baseChance: 0.65,
|
||||
minStealPercent: 0.01,
|
||||
maxStealPercent: 0.03,
|
||||
maxStealAmount: 5000,
|
||||
maxStealAmount: 1500,
|
||||
minPenaltyPercent: 0.01,
|
||||
maxPenaltyPercent: 0.04,
|
||||
maxPenaltyAmount: 1500
|
||||
maxPenaltyAmount: 750
|
||||
},
|
||||
|
||||
Hack: {
|
||||
baseChance: 0.45,
|
||||
minStealPercent: 0.03,
|
||||
maxStealPercent: 0.06,
|
||||
maxStealAmount: 12500,
|
||||
maxStealAmount: 3500,
|
||||
minPenaltyPercent: 0.03,
|
||||
maxPenaltyPercent: 0.07,
|
||||
maxPenaltyAmount: 4000
|
||||
maxPenaltyAmount: 2000
|
||||
},
|
||||
|
||||
Smash: {
|
||||
baseChance: 0.25,
|
||||
minStealPercent: 0.06,
|
||||
maxStealPercent: 0.12,
|
||||
maxStealAmount: 25000,
|
||||
maxStealAmount: 7500,
|
||||
minPenaltyPercent: 0.06,
|
||||
maxPenaltyPercent: 0.12,
|
||||
maxPenaltyAmount: 10000
|
||||
maxPenaltyAmount: 5000
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+80
-10
@@ -18,7 +18,8 @@ const {
|
||||
} = require("./cookieEconomy");
|
||||
|
||||
const {
|
||||
recordGameOutcome
|
||||
recordGameOutcome,
|
||||
trackProfileEvent
|
||||
} = require("./profileSystem");
|
||||
|
||||
const GAME_COLLECTION =
|
||||
@@ -112,7 +113,9 @@ async function startBlackjack(
|
||||
interaction,
|
||||
bet,
|
||||
allowCustomBet = false,
|
||||
allIn = false
|
||||
source = "normal",
|
||||
previousAllIn = false,
|
||||
previousBet = 0
|
||||
) {
|
||||
if (
|
||||
!allowCustomBet &&
|
||||
@@ -196,6 +199,9 @@ async function startBlackjack(
|
||||
getCookieBalance(houseId)
|
||||
]);
|
||||
|
||||
const allIn =
|
||||
bet === playerBalance;
|
||||
|
||||
if (playerBalance < bet) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
@@ -276,6 +282,11 @@ async function startBlackjack(
|
||||
doubled:
|
||||
false,
|
||||
allIn,
|
||||
vaultAtBet:
|
||||
playerBalance,
|
||||
source,
|
||||
previousAllIn,
|
||||
previousBet,
|
||||
deck,
|
||||
playerCards,
|
||||
dealerCards,
|
||||
@@ -285,6 +296,20 @@ async function startBlackjack(
|
||||
|
||||
await games.insertOne(game);
|
||||
|
||||
await trackProfileEvent(
|
||||
interaction,
|
||||
playerId,
|
||||
{
|
||||
type: "game_start",
|
||||
game: "blackjack",
|
||||
source,
|
||||
bet,
|
||||
allIn,
|
||||
previousAllIn,
|
||||
previousBet
|
||||
}
|
||||
);
|
||||
|
||||
const playerBlackjack =
|
||||
isBlackjack(playerCards);
|
||||
|
||||
@@ -370,7 +395,7 @@ async function startBlackjackAllIn(
|
||||
interaction,
|
||||
balance,
|
||||
true,
|
||||
true
|
||||
"all_in"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -405,6 +430,9 @@ async function runBlackjackBack(
|
||||
const bet =
|
||||
Number(parts[2]);
|
||||
|
||||
const previousAllIn =
|
||||
parts[3] === "1";
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
@@ -435,7 +463,9 @@ async function runBlackjackBack(
|
||||
interaction,
|
||||
bet,
|
||||
true,
|
||||
false
|
||||
"run_it_back",
|
||||
previousAllIn,
|
||||
bet
|
||||
);
|
||||
}
|
||||
|
||||
@@ -803,6 +833,32 @@ async function finishBlackjack(
|
||||
game,
|
||||
result
|
||||
) {
|
||||
const { db } = getCollections();
|
||||
|
||||
const claim = await db
|
||||
.collection(GAME_COLLECTION)
|
||||
.updateOne(
|
||||
{
|
||||
gameId: game.gameId,
|
||||
state: "Playing"
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
state: "Resolving"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (claim.modifiedCount !== 1) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This Blackjack hand is already being resolved."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const houseId =
|
||||
interaction.client.user.id;
|
||||
|
||||
@@ -845,6 +901,23 @@ async function finishBlackjack(
|
||||
`[Blackjack] House cannot cover payout ${payout} for ${game.userId}`
|
||||
);
|
||||
|
||||
await db
|
||||
.collection(GAME_COLLECTION)
|
||||
.updateOne(
|
||||
{
|
||||
gameId: game.gameId,
|
||||
state: "Resolving"
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
state: "Finished",
|
||||
result: "PayoutError",
|
||||
payout: 0,
|
||||
finishedAt: getUnixTime()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return interaction.update({
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
@@ -856,7 +929,7 @@ async function finishBlackjack(
|
||||
)
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbBlackjackResult:${game.userId}:${game.originalBet}`
|
||||
`crumbBlackjackResult:${game.userId}:${game.originalBet}:${game.allIn ? 1 : 0}`
|
||||
})
|
||||
],
|
||||
components:
|
||||
@@ -871,9 +944,6 @@ async function finishBlackjack(
|
||||
);
|
||||
}
|
||||
|
||||
const { db } =
|
||||
getCollections();
|
||||
|
||||
await db
|
||||
.collection(
|
||||
GAME_COLLECTION
|
||||
@@ -883,7 +953,7 @@ async function finishBlackjack(
|
||||
gameId:
|
||||
game.gameId,
|
||||
state:
|
||||
"Playing"
|
||||
"Resolving"
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
@@ -1010,7 +1080,7 @@ async function finishBlackjack(
|
||||
)
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbBlackjackResult:${game.userId}:${game.originalBet}`
|
||||
`crumbBlackjackResult:${game.userId}:${game.originalBet}:${game.allIn ? 1 : 0}`
|
||||
});
|
||||
|
||||
return interaction.update({
|
||||
|
||||
+36
-8
@@ -12,7 +12,8 @@ const {
|
||||
} = require("./cookieEconomy");
|
||||
|
||||
const {
|
||||
recordGameOutcome
|
||||
recordGameOutcome,
|
||||
trackProfileEvent
|
||||
} = require("./profileSystem");
|
||||
|
||||
const ROULETTE_BETS = [
|
||||
@@ -206,7 +207,10 @@ async function startRouletteAllIn(
|
||||
async function showRouletteChoices(
|
||||
interaction,
|
||||
bet,
|
||||
allIn = false
|
||||
requestedAllIn = false,
|
||||
source = "normal",
|
||||
previousAllIn = false,
|
||||
previousBet = 0
|
||||
) {
|
||||
if (
|
||||
!Number.isSafeInteger(bet) ||
|
||||
@@ -262,7 +266,7 @@ async function showRouletteChoices(
|
||||
})
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteBet:${interaction.user.id}:${bet}:${allIn ? 1 : 0}`
|
||||
`crumbRouletteBet:${interaction.user.id}:${bet}:${requestedAllIn ? 1 : 0}:${source}:${previousAllIn ? 1 : 0}:${previousBet}`
|
||||
});
|
||||
|
||||
return interaction.update({
|
||||
@@ -303,6 +307,9 @@ async function runRouletteBack(
|
||||
const bet =
|
||||
Number(parts[2]);
|
||||
|
||||
const previousAllIn =
|
||||
parts[3] === "1";
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
@@ -383,8 +390,14 @@ async function playRoulette(
|
||||
const bet =
|
||||
Number(parts[2]);
|
||||
|
||||
const allIn =
|
||||
parts[3] === "1";
|
||||
const source =
|
||||
parts[4] || "normal";
|
||||
|
||||
const previousAllIn =
|
||||
parts[5] === "1";
|
||||
|
||||
const previousBet =
|
||||
Number(parts[6] ?? 0);
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
@@ -476,6 +489,23 @@ async function playRoulette(
|
||||
throw error;
|
||||
}
|
||||
|
||||
const allIn =
|
||||
bet === playerBalance;
|
||||
|
||||
await trackProfileEvent(
|
||||
interaction,
|
||||
playerId,
|
||||
{
|
||||
type: "game_start",
|
||||
game: "roulette",
|
||||
source,
|
||||
bet,
|
||||
allIn,
|
||||
previousAllIn,
|
||||
previousBet
|
||||
}
|
||||
);
|
||||
|
||||
await interaction.update({
|
||||
embeds: [
|
||||
buildSpinEmbed(
|
||||
@@ -594,8 +624,6 @@ async function playRoulette(
|
||||
won &&
|
||||
choiceKey ===
|
||||
"Zero",
|
||||
allIn:
|
||||
won &&
|
||||
allIn
|
||||
}
|
||||
);
|
||||
@@ -664,7 +692,7 @@ async function playRoulette(
|
||||
)
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteResult:${playerId}:${bet}`
|
||||
`crumbRouletteResult:${playerId}:${bet}:${allIn ? 1 : 0}`
|
||||
});
|
||||
|
||||
return interaction.editReply({
|
||||
|
||||
+21
-3
@@ -13,7 +13,8 @@ const {
|
||||
} = require("./cookieEconomy");
|
||||
|
||||
const {
|
||||
recordGameOutcome
|
||||
recordGameOutcome,
|
||||
trackProfileEvent
|
||||
} = require("./profileSystem");
|
||||
|
||||
const SLOT_BETS = [
|
||||
@@ -104,7 +105,8 @@ async function showSlots(interaction) {
|
||||
|
||||
async function playSlots(
|
||||
interaction,
|
||||
bet
|
||||
bet,
|
||||
source = "normal"
|
||||
) {
|
||||
const ownerId =
|
||||
getSlotsSessionOwner(
|
||||
@@ -197,6 +199,17 @@ async function playSlots(
|
||||
throw error;
|
||||
}
|
||||
|
||||
await trackProfileEvent(
|
||||
interaction,
|
||||
playerId,
|
||||
{
|
||||
type: "game_start",
|
||||
game: "slots",
|
||||
source,
|
||||
bet
|
||||
}
|
||||
);
|
||||
|
||||
await interaction.update({
|
||||
embeds: [
|
||||
buildSpinningEmbed(
|
||||
@@ -321,6 +334,10 @@ async function playSlots(
|
||||
? "loss"
|
||||
: "push",
|
||||
profit,
|
||||
triplet:
|
||||
outcome.type === "triple"
|
||||
? outcome.symbol
|
||||
: null,
|
||||
jackpot:
|
||||
outcome.type ===
|
||||
"triple" &&
|
||||
@@ -460,7 +477,8 @@ async function runSlotsBack(
|
||||
|
||||
return playSlots(
|
||||
interaction,
|
||||
bet
|
||||
bet,
|
||||
"run_it_back"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+912
-1934
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user