snapshot migration phase 5
This commit is contained in:
+352
-4
@@ -706,6 +706,14 @@ async function trackProfileEvent(
|
||||
updatedStats
|
||||
);
|
||||
|
||||
|
||||
await evaluateEventAchievements(
|
||||
interaction,
|
||||
userId,
|
||||
event
|
||||
);
|
||||
|
||||
|
||||
await evaluateBadges(
|
||||
interaction,
|
||||
userId,
|
||||
@@ -713,6 +721,7 @@ async function trackProfileEvent(
|
||||
event
|
||||
);
|
||||
|
||||
|
||||
return updatedStats;
|
||||
}
|
||||
|
||||
@@ -977,8 +986,12 @@ async function evaluateAchievements(
|
||||
const normalDefinitions =
|
||||
definitions.filter(
|
||||
definition =>
|
||||
definition.measure !==
|
||||
"achievementPercentage"
|
||||
![
|
||||
"achievementPercentage",
|
||||
"event"
|
||||
].includes(
|
||||
definition.measure
|
||||
)
|
||||
);
|
||||
|
||||
for (const definition of normalDefinitions) {
|
||||
@@ -2837,6 +2850,9 @@ function isStatBackfillableAchievement(
|
||||
if (
|
||||
definition.measure ===
|
||||
"achievementPercentage"
|
||||
||
|
||||
definition.measure ===
|
||||
"event"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -2849,7 +2865,8 @@ function isStatBackfillableAchievement(
|
||||
return (
|
||||
Array.isArray(
|
||||
definition.conditions
|
||||
) &&
|
||||
)
|
||||
&&
|
||||
definition.conditions.length > 0
|
||||
);
|
||||
}
|
||||
@@ -2937,6 +2954,331 @@ function getCompositeProgress(
|
||||
conditions.length
|
||||
};
|
||||
}
|
||||
function buildEventContext(
|
||||
interaction,
|
||||
event
|
||||
) {
|
||||
const activity =
|
||||
getActivityDateContext();
|
||||
|
||||
return {
|
||||
...event,
|
||||
|
||||
localDate:
|
||||
activity.dateKey,
|
||||
|
||||
localWeekday:
|
||||
activity.weekday,
|
||||
|
||||
localHour:
|
||||
activity.hour,
|
||||
|
||||
localWeekKey:
|
||||
activity.weekKey,
|
||||
|
||||
localTimeBucket:
|
||||
activity.timeBucket,
|
||||
|
||||
isWeekend:
|
||||
activity.weekday ===
|
||||
"saturday"
|
||||
||
|
||||
activity.weekday ===
|
||||
"sunday",
|
||||
|
||||
isWeekday:
|
||||
![
|
||||
"saturday",
|
||||
"sunday"
|
||||
].includes(
|
||||
activity.weekday
|
||||
)
|
||||
};
|
||||
}
|
||||
function readEventPath(
|
||||
event,
|
||||
path
|
||||
) {
|
||||
if (!path) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return String(path)
|
||||
.split(".")
|
||||
.reduce(
|
||||
(current, key) =>
|
||||
current?.[key],
|
||||
event
|
||||
);
|
||||
}
|
||||
|
||||
function evaluateEventCondition(
|
||||
event,
|
||||
condition
|
||||
) {
|
||||
if (
|
||||
!condition ||
|
||||
!condition.field
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const actual =
|
||||
readEventPath(
|
||||
event,
|
||||
condition.field
|
||||
);
|
||||
|
||||
const expected =
|
||||
condition.value;
|
||||
|
||||
const operator =
|
||||
String(
|
||||
condition.operator ??
|
||||
"eq"
|
||||
).toLowerCase();
|
||||
|
||||
|
||||
switch (operator) {
|
||||
case "eq":
|
||||
return (
|
||||
actual ===
|
||||
expected
|
||||
);
|
||||
|
||||
|
||||
case "neq":
|
||||
return (
|
||||
actual !==
|
||||
expected
|
||||
);
|
||||
|
||||
|
||||
case "gt":
|
||||
return (
|
||||
Number(actual ?? 0) >
|
||||
Number(expected ?? 0)
|
||||
);
|
||||
|
||||
|
||||
case "gte":
|
||||
return (
|
||||
Number(actual ?? 0) >=
|
||||
Number(expected ?? 0)
|
||||
);
|
||||
|
||||
|
||||
case "lt":
|
||||
return (
|
||||
Number(actual ?? 0) <
|
||||
Number(expected ?? 0)
|
||||
);
|
||||
|
||||
|
||||
case "lte":
|
||||
return (
|
||||
Number(actual ?? 0) <=
|
||||
Number(expected ?? 0)
|
||||
);
|
||||
|
||||
|
||||
case "in":
|
||||
return (
|
||||
Array.isArray(
|
||||
expected
|
||||
)
|
||||
&&
|
||||
expected.includes(
|
||||
actual
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
case "not_in":
|
||||
return (
|
||||
Array.isArray(
|
||||
expected
|
||||
)
|
||||
&&
|
||||
!expected.includes(
|
||||
actual
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
case "truthy":
|
||||
return Boolean(
|
||||
actual
|
||||
);
|
||||
|
||||
|
||||
case "falsy":
|
||||
return !Boolean(
|
||||
actual
|
||||
);
|
||||
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function evaluateEventAchievement(
|
||||
event,
|
||||
definition
|
||||
) {
|
||||
const conditions =
|
||||
Array.isArray(
|
||||
definition.eventConditions
|
||||
)
|
||||
? definition.eventConditions
|
||||
: [];
|
||||
|
||||
|
||||
if (!conditions.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const mode =
|
||||
String(
|
||||
definition.eventMode ??
|
||||
"all"
|
||||
).toLowerCase();
|
||||
|
||||
|
||||
if (mode === "any") {
|
||||
return conditions.some(
|
||||
condition =>
|
||||
evaluateEventCondition(
|
||||
event,
|
||||
condition
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return conditions.every(
|
||||
condition =>
|
||||
evaluateEventCondition(
|
||||
event,
|
||||
condition
|
||||
)
|
||||
);
|
||||
}
|
||||
async function evaluateEventAchievements(
|
||||
interaction,
|
||||
userId,
|
||||
event
|
||||
) {
|
||||
const guildId =
|
||||
interaction.guildId;
|
||||
|
||||
|
||||
if (
|
||||
!guildId ||
|
||||
!event?.type
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const {
|
||||
achievements,
|
||||
achievementConfig
|
||||
} =
|
||||
getProfileCollections();
|
||||
|
||||
|
||||
await ensureAchievementDocument(
|
||||
userId,
|
||||
guildId
|
||||
);
|
||||
|
||||
|
||||
const definitions =
|
||||
await achievementConfig
|
||||
.find({
|
||||
active: {
|
||||
$ne: false
|
||||
},
|
||||
|
||||
measure:
|
||||
"event"
|
||||
})
|
||||
.toArray();
|
||||
|
||||
|
||||
if (!definitions.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const achievementDocument =
|
||||
await achievements.findOne({
|
||||
userId,
|
||||
guildId
|
||||
});
|
||||
|
||||
|
||||
const unlocked =
|
||||
achievementDocument
|
||||
?.unlocked ?? {};
|
||||
|
||||
|
||||
const eventContext =
|
||||
buildEventContext(
|
||||
interaction,
|
||||
event
|
||||
);
|
||||
|
||||
|
||||
for (
|
||||
const definition
|
||||
of definitions
|
||||
) {
|
||||
if (
|
||||
unlocked[
|
||||
definition.achievementId
|
||||
]
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Optional fast filter.
|
||||
*
|
||||
* This avoids evaluating Roulette conditions
|
||||
* against a Cookie Bakery event, for example.
|
||||
*/
|
||||
|
||||
if (
|
||||
definition.eventType &&
|
||||
definition.eventType !==
|
||||
eventContext.type
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
!evaluateEventAchievement(
|
||||
eventContext,
|
||||
definition
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
await unlockAchievement(
|
||||
interaction,
|
||||
userId,
|
||||
guildId,
|
||||
definition
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ensureProfileSystem,
|
||||
@@ -2967,5 +3309,11 @@ module.exports = {
|
||||
getProfileContext,
|
||||
getProfileContextFromFooter,
|
||||
getProfileTargetId,
|
||||
requireProfileOwner
|
||||
requireProfileOwner,
|
||||
|
||||
buildEventContext,
|
||||
readEventPath,
|
||||
evaluateEventCondition,
|
||||
evaluateEventAchievement,
|
||||
evaluateEventAchievements,
|
||||
};
|
||||
Reference in New Issue
Block a user