updated profile and centralized functions

This commit is contained in:
DeSqBlocki
2026-08-22 23:33:20 +02:00
parent 08d1a8c5cd
commit 030b4b8883
18 changed files with 3747 additions and 1202 deletions
+403
View File
@@ -0,0 +1,403 @@
const {
getProfileCollections,
getProfileStats,
readStatValue
} = require("./profileSystem");
const {
getUnixTime
} = require("./cookieEconomy");
function getActiveDefinitions(
achievementConfig
) {
return achievementConfig
.find({
active: {
$ne: false
}
})
.toArray();
}
async function reconcileUserAchievements({
guildId,
userId,
dryRun = false
}) {
const {
achievements,
achievementConfig
} = getProfileCollections();
const [
statsDocument,
definitions,
achievementDocument
] = await Promise.all([
getProfileStats(
userId,
guildId
),
getActiveDefinitions(
achievementConfig
),
achievements.findOne({
userId,
guildId
})
]);
const unlocked = {
...(achievementDocument?.unlocked ?? {})
};
const additions = {};
let skippedEventOnly = 0;
const normalDefinitions =
definitions.filter(
definition =>
definition.measure !==
"achievementPercentage"
);
for (const definition of normalDefinitions) {
if (unlocked[definition.achievementId]) {
continue;
}
if (!definition.stat) {
skippedEventOnly++;
continue;
}
const currentValue =
readStatValue(
statsDocument,
definition
);
if (
currentValue <
Number(definition.target ?? 0)
) {
continue;
}
const timestamp =
getUnixTime();
additions[
definition.achievementId
] = timestamp;
unlocked[
definition.achievementId
] = timestamp;
}
const collectibleDefinitions =
definitions.filter(
definition =>
definition.measure !==
"achievementPercentage"
);
const unlockedCollectible =
collectibleDefinitions.filter(
definition =>
Boolean(
unlocked[
definition.achievementId
]
)
).length;
const percentage =
collectibleDefinitions.length > 0
? unlockedCollectible /
collectibleDefinitions.length
: 0;
for (
const definition
of definitions.filter(
item =>
item.measure ===
"achievementPercentage"
)
) {
if (
unlocked[
definition.achievementId
]
) {
continue;
}
if (
percentage <
Number(definition.target ?? 0)
) {
continue;
}
additions[
definition.achievementId
] = getUnixTime();
}
const additionEntries =
Object.entries(additions);
if (
!dryRun &&
additionEntries.length > 0
) {
const set = {};
for (
const [achievementId, timestamp]
of additionEntries
) {
set[
`unlocked.${achievementId}`
] = timestamp;
}
await achievements.updateOne(
{
userId,
guildId
},
{
$setOnInsert: {
userId,
guildId
},
$set: set
},
{
upsert: true
}
);
}
return {
userId,
checked:
definitions.length,
unlocked:
additionEntries.length,
skippedEventOnly,
dryRun,
achievementIds:
additionEntries.map(
([achievementId]) =>
achievementId
)
};
}
async function reconcileAllAchievements({
guildId,
dryRun = false
}) {
const {
stats
} = getProfileCollections();
const userIds =
await stats.distinct(
"userId",
{
guildId
}
);
const summary = {
users:
userIds.length,
unlocked:
0,
skippedEventOnly:
0,
results: []
};
for (const userId of userIds) {
const result =
await reconcileUserAchievements({
guildId,
userId,
dryRun
});
summary.unlocked +=
result.unlocked;
summary.skippedEventOnly +=
result.skippedEventOnly;
summary.results.push(
result
);
}
return summary;
}
async function auditProfileData(
guildId
) {
const {
stats,
achievements,
achievementConfig,
badges
} = getProfileCollections();
const [
statDocuments,
achievementDocuments,
activeAchievements,
badgeDocuments
] = await Promise.all([
stats.find({
guildId
}).toArray(),
achievements.find({
guildId
}).toArray(),
achievementConfig.countDocuments({
active: {
$ne: false
}
}),
badges.find({
guildId
}).toArray()
]);
const issues = [];
for (const document of statDocuments) {
if (!document.userId) {
issues.push(
"Stats document missing userId."
);
continue;
}
const total =
document.games?.total ?? {};
const played =
Number(total.played ?? 0);
const resolved =
Number(total.won ?? 0) +
Number(total.lost ?? 0) +
Number(total.pushed ?? 0);
if (played !== resolved) {
issues.push(
`${document.userId}: games.total.played (${played}) does not match W/L/P total (${resolved}).`
);
}
for (
const game
of [
"slots",
"blackjack",
"roulette",
"heist"
]
) {
const value =
document.games?.[game] ?? {};
const gamePlayed =
Number(value.played ?? 0);
const gameResolved =
Number(value.won ?? 0) +
Number(value.lost ?? 0) +
Number(value.pushed ?? 0);
if (
gamePlayed !==
gameResolved
) {
issues.push(
`${document.userId}: games.${game}.played (${gamePlayed}) does not match W/L/P total (${gameResolved}).`
);
}
}
const nonNegativeFields = [
document.economy?.cookiesObtainedTotal,
document.economy?.cookiesLostTotal,
document.economy?.cookiesGivenTotal,
document.economy?.stolenTotal,
document.economy?.stolenFromMeTotal,
document.economy?.vaultStolenTotal
];
if (
nonNegativeFields.some(
value =>
Number(value ?? 0) < 0
)
) {
issues.push(
`${document.userId}: one or more economy counters are negative.`
);
}
}
const statUsers =
new Set(
statDocuments.map(
document =>
document.userId
)
);
const orphanAchievementDocs =
achievementDocuments.filter(
document =>
!statUsers.has(
document.userId
)
).length;
const claimedBadges =
badgeDocuments.filter(
badge =>
Boolean(badge.ownerId)
).length;
return {
statDocuments:
statDocuments.length,
achievementDocuments:
achievementDocuments.length,
activeAchievements,
badgeDocuments:
badgeDocuments.length,
claimedBadges,
orphanAchievementDocs,
issueCount:
issues.length,
issues
};
}
module.exports = {
reconcileUserAchievements,
reconcileAllAchievements,
auditProfileData
};