updated profile and centralized functions
This commit is contained in:
@@ -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
|
||||
};
|
||||
Reference in New Issue
Block a user