285 lines
6.7 KiB
JavaScript
285 lines
6.7 KiB
JavaScript
const { EmbedBuilder, MessageFlags } = require("discord.js");
|
|
const { mClient } = require("../index");
|
|
|
|
const DATABASE_NAME = process.env.M_DB;
|
|
const SESSION_COLLECTION = "items_sessions";
|
|
const DEFAULT_SESSION_LIFETIME_MS = 24 * 60 * 60 * 1000;
|
|
|
|
function getSessionCollection() {
|
|
if (!DATABASE_NAME) {
|
|
throw new Error("M_DB is not configured in .env");
|
|
}
|
|
|
|
return mClient.db(DATABASE_NAME).collection(SESSION_COLLECTION);
|
|
}
|
|
|
|
async function ensureInteractionSessionIndexes() {
|
|
const sessions = getSessionCollection();
|
|
|
|
await sessions.createIndex(
|
|
{ messageId: 1 },
|
|
{ unique: true }
|
|
);
|
|
|
|
await sessions.createIndex(
|
|
{ expiresAt: 1 },
|
|
{ expireAfterSeconds: 0 }
|
|
);
|
|
}
|
|
|
|
async function createInteractionSession(
|
|
interaction,
|
|
{
|
|
messageId = null,
|
|
ownerId = interaction.user.id,
|
|
kind = "menu",
|
|
targetId = null,
|
|
metadata = {},
|
|
lifetimeMs = DEFAULT_SESSION_LIFETIME_MS
|
|
} = {}
|
|
) {
|
|
const resolvedMessageId =
|
|
messageId ?? interaction.message?.id;
|
|
|
|
if (!resolvedMessageId) {
|
|
throw new Error("SESSION_MESSAGE_ID_REQUIRED");
|
|
}
|
|
|
|
const sessions = getSessionCollection();
|
|
const now = new Date();
|
|
const expiresAt = new Date(now.getTime() + lifetimeMs);
|
|
|
|
await sessions.updateOne(
|
|
{ messageId: resolvedMessageId },
|
|
{
|
|
$set: {
|
|
messageId: resolvedMessageId,
|
|
ownerId,
|
|
guildId: interaction.guildId ?? null,
|
|
channelId: interaction.channelId ?? null,
|
|
kind,
|
|
targetId,
|
|
metadata,
|
|
active: true,
|
|
updatedAt: now,
|
|
expiresAt
|
|
},
|
|
$setOnInsert: {
|
|
createdAt: now
|
|
}
|
|
},
|
|
{ upsert: true }
|
|
);
|
|
|
|
return sessions.findOne({ messageId: resolvedMessageId });
|
|
}
|
|
|
|
async function registerReplySession(interaction, options = {}) {
|
|
const message = await interaction.fetchReply();
|
|
|
|
return createInteractionSession(
|
|
interaction,
|
|
{
|
|
...options,
|
|
messageId: message.id
|
|
}
|
|
);
|
|
}
|
|
|
|
async function getInteractionSession(interaction) {
|
|
const messageId = interaction.message?.id;
|
|
|
|
if (!messageId) {
|
|
return null;
|
|
}
|
|
|
|
return getSessionCollection().findOne({
|
|
messageId,
|
|
active: true,
|
|
expiresAt: { $gt: new Date() }
|
|
});
|
|
}
|
|
|
|
function inferLegacyOwnerId(interaction) {
|
|
const footer = interaction.message?.embeds?.[0]?.footer?.text;
|
|
const parts = footer?.split(":") ?? [];
|
|
|
|
if (!parts.length) {
|
|
return null;
|
|
}
|
|
|
|
switch (parts[0]) {
|
|
case "profile":
|
|
case "crumbSaucer":
|
|
case "crumbSaucerCollect":
|
|
case "crumbSaucerSlots":
|
|
case "crumbSaucerSlotsResult":
|
|
case "crumbSaucerBlackjack":
|
|
case "crumbBlackjackResult":
|
|
case "crumbRouletteMenu":
|
|
case "crumbRouletteBet":
|
|
case "crumbRouletteResult":
|
|
case "cookieHeist":
|
|
return parts[1] ?? null;
|
|
|
|
case "crumbBlackjack":
|
|
return parts[2] ?? null;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function replySessionError(
|
|
interaction,
|
|
content = "This isn't your session."
|
|
) {
|
|
const payload = {
|
|
content,
|
|
flags: MessageFlags.Ephemeral
|
|
};
|
|
|
|
if (interaction.replied || interaction.deferred) {
|
|
return interaction.followUp(payload);
|
|
}
|
|
|
|
return interaction.reply(payload);
|
|
}
|
|
|
|
async function requireInteractionSession(
|
|
interaction,
|
|
{
|
|
kinds = null,
|
|
allowLegacy = true
|
|
} = {}
|
|
) {
|
|
const session = await getInteractionSession(interaction);
|
|
|
|
if (session) {
|
|
if (session.ownerId !== interaction.user.id) {
|
|
await replySessionError(interaction);
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
Array.isArray(kinds) &&
|
|
kinds.length > 0 &&
|
|
!kinds.includes(session.kind)
|
|
) {
|
|
await replySessionError(
|
|
interaction,
|
|
"This control belongs to a different session."
|
|
);
|
|
return null;
|
|
}
|
|
|
|
return session;
|
|
}
|
|
|
|
if (allowLegacy) {
|
|
const ownerId = inferLegacyOwnerId(interaction);
|
|
|
|
if (ownerId) {
|
|
if (ownerId !== interaction.user.id) {
|
|
await replySessionError(interaction);
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
messageId: interaction.message?.id ?? null,
|
|
ownerId,
|
|
guildId: interaction.guildId ?? null,
|
|
channelId: interaction.channelId ?? null,
|
|
kind: "legacy",
|
|
targetId: null,
|
|
metadata: {},
|
|
active: true,
|
|
legacy: true
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
messageId: interaction.message?.id ?? null,
|
|
ownerId: interaction.user.id,
|
|
kind: "unmanaged",
|
|
metadata: {},
|
|
active: true,
|
|
unmanaged: true
|
|
};
|
|
}
|
|
|
|
async function guardSessionInteraction(interaction) {
|
|
if (!interaction.isButton()) {
|
|
return true;
|
|
}
|
|
|
|
const session = await getInteractionSession(interaction);
|
|
|
|
if (session) {
|
|
if (session.ownerId === interaction.user.id) {
|
|
return true;
|
|
}
|
|
|
|
await replySessionError(interaction);
|
|
return false;
|
|
}
|
|
|
|
const ownerId = inferLegacyOwnerId(interaction);
|
|
|
|
if (!ownerId || ownerId === interaction.user.id) {
|
|
return true;
|
|
}
|
|
|
|
await replySessionError(interaction);
|
|
return false;
|
|
}
|
|
|
|
async function closeInteractionSession(interaction) {
|
|
const session = await requireInteractionSession(interaction);
|
|
|
|
if (!session) {
|
|
return false;
|
|
}
|
|
|
|
if (session.messageId && !session.unmanaged) {
|
|
await getSessionCollection().updateOne(
|
|
{ messageId: session.messageId },
|
|
{
|
|
$set: {
|
|
active: false,
|
|
closedAt: new Date(),
|
|
updatedAt: new Date()
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setTitle("Session Closed")
|
|
.setDescription("Closing menu. . .");
|
|
|
|
await interaction.update({
|
|
embeds: [embed],
|
|
components: []
|
|
});
|
|
|
|
setTimeout(() => {
|
|
interaction.deleteReply().catch(() => {});
|
|
}, 1200);
|
|
|
|
return true;
|
|
}
|
|
|
|
module.exports = {
|
|
ensureInteractionSessionIndexes,
|
|
createInteractionSession,
|
|
registerReplySession,
|
|
getInteractionSession,
|
|
requireInteractionSession,
|
|
guardSessionInteraction,
|
|
closeInteractionSession,
|
|
replySessionError,
|
|
inferLegacyOwnerId
|
|
};
|