fix(bot): refresh roll-call date changes
This commit is contained in:
parent
055db8d0f0
commit
f6d42399d4
2 changed files with 26 additions and 17 deletions
|
|
@ -66,7 +66,10 @@ export const handleButtonInteraction = async (
|
|||
await setMissionAttendance(payload, { missionId, userId: user.id, response });
|
||||
|
||||
const summary = await getMissionAttendance(payload, missionId);
|
||||
const hash = attendanceHash(await fetchAttendanceDocs(payload, missionId));
|
||||
const hash = attendanceHash(
|
||||
await fetchAttendanceDocs(payload, missionId),
|
||||
mission.classification.startDateTime,
|
||||
);
|
||||
|
||||
await interaction.message.edit({
|
||||
embeds: [renderAttendanceEmbed(mission, summary)],
|
||||
|
|
|
|||
|
|
@ -28,12 +28,6 @@ const missionTitle = (mission: Mission): string =>
|
|||
|
||||
const memberName = (user: User): string => user.displayName ?? user.username;
|
||||
|
||||
const isStillFuture = (mission: Mission): boolean => {
|
||||
const start = new Date(mission.classification.startDateTime).getTime();
|
||||
const end = start + mission.classification.estimatedDuration * 60_000;
|
||||
return end > Date.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* Roll call is sent once: as soon as the mission is Ready, or three days
|
||||
* before a Scheduled mission, whichever happens first.
|
||||
|
|
@ -59,17 +53,18 @@ const operationTypeLabel = (type: Mission["operationType"]): string => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Deterministic fingerprint of the attendance state so reconcile can skip
|
||||
* embeds that haven't changed (anti-loop).
|
||||
* Deterministic fingerprint of the attendance state and scheduled start time
|
||||
* so reconcile can skip embeds that haven't changed (anti-loop), while still
|
||||
* detecting mission date changes.
|
||||
*/
|
||||
export const attendanceHash = (docs: MissionAttendance[]): string => {
|
||||
export const attendanceHash = (docs: MissionAttendance[], startDateTime?: string): string => {
|
||||
const rows = docs
|
||||
.map((doc) => {
|
||||
const userId = typeof doc.user === "object" ? doc.user.id : doc.user;
|
||||
return [userId, doc.response] as const;
|
||||
})
|
||||
.sort((a, b) => a[0] - b[0] || a[1].localeCompare(b[1]));
|
||||
return JSON.stringify(rows);
|
||||
return JSON.stringify({ rows, startDateTime: startDateTime ?? null });
|
||||
};
|
||||
|
||||
export const fetchAttendanceDocs = async (
|
||||
|
|
@ -97,17 +92,23 @@ export const getEligibleMissions = async (payload: Payload): Promise<Mission[]>
|
|||
limit: MAX_ELIGIBLE_MISSIONS,
|
||||
where: {
|
||||
and: [
|
||||
{ "classification.startDateTime": { greater_than: new Date().toISOString() } },
|
||||
{ "ownershipAndStatus.visibility": { equals: "unit" } },
|
||||
{
|
||||
or: [
|
||||
{ "ownershipAndStatus.status": { equals: "Ready" } },
|
||||
{
|
||||
and: [
|
||||
{ "classification.startDateTime": { greater_than: new Date().toISOString() } },
|
||||
{ "ownershipAndStatus.status": { equals: "Ready" } },
|
||||
],
|
||||
},
|
||||
{
|
||||
and: [
|
||||
{ "classification.startDateTime": { greater_than: new Date().toISOString() } },
|
||||
{ "ownershipAndStatus.status": { equals: "Scheduled" } },
|
||||
{ "classification.startDateTime": { less_than_equal: rollCallCutoff } },
|
||||
],
|
||||
},
|
||||
{ discordMessageId: { exists: true } },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -200,7 +201,10 @@ const postEmbed = async (
|
|||
mission: Mission,
|
||||
): Promise<void> => {
|
||||
const summary = await getMissionAttendance(payload, mission.id);
|
||||
const hash = attendanceHash(await fetchAttendanceDocs(payload, mission.id));
|
||||
const hash = attendanceHash(
|
||||
await fetchAttendanceDocs(payload, mission.id),
|
||||
mission.classification.startDateTime,
|
||||
);
|
||||
const channel = await getOpsChannel(client, opsChannelId);
|
||||
const message = await channel.send({
|
||||
embeds: [renderAttendanceEmbed(mission, summary)],
|
||||
|
|
@ -257,7 +261,10 @@ const manageMission = async (
|
|||
return;
|
||||
}
|
||||
|
||||
const hash = attendanceHash(await fetchAttendanceDocs(payload, mission.id));
|
||||
const hash = attendanceHash(
|
||||
await fetchAttendanceDocs(payload, mission.id),
|
||||
mission.classification.startDateTime,
|
||||
);
|
||||
if (hash === mission.discordAttendanceHash) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -297,7 +304,6 @@ export const reconcileMission = async (
|
|||
if (
|
||||
mission.ownershipAndStatus.visibility !== "unit" ||
|
||||
!["Ready", "Scheduled"].includes(mission.ownershipAndStatus.status) ||
|
||||
!isStillFuture(mission) ||
|
||||
(!isRollCallDue(mission) && !mission.discordMessageId)
|
||||
) {
|
||||
return;
|
||||
|
|
@ -353,7 +359,7 @@ export const reconcile = async (client: Client, payload: Payload): Promise<void>
|
|||
}
|
||||
|
||||
const eligible = (await getEligibleMissions(payload)).filter(
|
||||
(mission) => isStillFuture(mission) && (isRollCallDue(mission) || !!mission.discordMessageId),
|
||||
(mission) => isRollCallDue(mission) || !!mission.discordMessageId,
|
||||
);
|
||||
for (const mission of eligible) {
|
||||
try {
|
||||
|
|
|
|||
Loading…
Reference in a new issue