Trade: Persist trades in the database
using atomic transactions. If a card happens to not be owned by the trading user, the entire trade is being rolled back. Rollback behaviour is a bit weird though, since we have to rollback manually even in a managed transaction.
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||||
const { Card, User, Character } = require("../models");
|
const { Card, User, Character } = require("../models");
|
||||||
const { UserUtils, CardUtils } = require("../util");
|
const { UserUtils, CardUtils, DbUtils, GeneralUtils } = require("../util");
|
||||||
const { TradeStore } = require("../stores");
|
const { TradeStore } = require("../stores");
|
||||||
|
|
||||||
const tradeExpiry = 920000; //When an active trade expires automatically
|
const tradeExpiry = 920000; //When an active trade expires automatically
|
||||||
const tradeTimeout = 120000; //Time until user can start a new trade
|
const tradeTimeout = 120000; //Time until user can start a new trade
|
||||||
|
const db = DbUtils.getDb();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
@@ -316,8 +317,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trade.user1accepted && trade.user2accepted) {
|
if (trade.user1accepted && trade.user2accepted) {
|
||||||
trade.state = TradeStore.States.ACCEPTED;
|
|
||||||
collector.stop("accepted");
|
collector.stop("accepted");
|
||||||
|
trade.state = TradeStore.States.ACCEPTED;
|
||||||
|
await this.transactCards(trade, interaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
await button.deferUpdate();
|
await button.deferUpdate();
|
||||||
@@ -334,13 +336,52 @@ module.exports = {
|
|||||||
trade.state = TradeStore.States.CANCELLED;
|
trade.state = TradeStore.States.CANCELLED;
|
||||||
}
|
}
|
||||||
if (reason === "accepted") {
|
if (reason === "accepted") {
|
||||||
//TODO: perform trade on database
|
trade.state = TradeStore.States.ACCEPTED;
|
||||||
}
|
}
|
||||||
await this.viewTrade(interaction, trade);
|
await this.viewTrade(interaction, trade);
|
||||||
await this.endTrade(trade);
|
await this.endTrade(trade);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async transactCards(trade, interaction) {
|
||||||
|
try {
|
||||||
|
const result = await db.sequelize.transaction(async (trx) => {
|
||||||
|
//check and update user1 cards
|
||||||
|
for (let i = 0; i < trade.user1Cards.length; i++) {
|
||||||
|
const card = trade.user1Cards[i];
|
||||||
|
let result = await Card.update(
|
||||||
|
{ userId: trade.user2.id },
|
||||||
|
{ where: { id: card.id, userId: trade.user1.id } },
|
||||||
|
{ transaction: trx }
|
||||||
|
)
|
||||||
|
if (result[0] == 0) {
|
||||||
|
await interaction.channel.send(`Card ${card.id} is not owned by ${trade.user1.discordId}! That's not supposed to happen, transaction rolled back!`);
|
||||||
|
trx.rollback();
|
||||||
|
throw new Error(`Card ${card.id} is not owned by ${trade.user1.discordId}!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//check and update user1 cards
|
||||||
|
for (let i = 0; i < trade.user2Cards.length; i++) {
|
||||||
|
const card = trade.user2Cards[i];
|
||||||
|
let result = await Card.update(
|
||||||
|
{ userId: trade.user1.id },
|
||||||
|
{ where: { id: card.id, userId: trade.user2.id } },
|
||||||
|
{ transaction: trx }
|
||||||
|
)
|
||||||
|
if (!result) {
|
||||||
|
await interaction.channel.send(`Card ${card.id} is not owned by ${trade.user2.discordId}! That's not supposed to happen, transaction rolled back!`);
|
||||||
|
trx.rollback();
|
||||||
|
throw new Error(`Card ${card.id} is not owned by ${trade.user2.discordId}!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
let logID = await GeneralUtils.generateLogID();
|
||||||
|
console.log(`[${logID}] ${error} : ${error.stack}`);
|
||||||
|
await interaction.channel.send(`Transaction failed! Please contact an admin with log ID ${logID}!`);
|
||||||
|
}
|
||||||
|
},
|
||||||
async endTrade(trade) {
|
async endTrade(trade) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await TradeStore.removeTrade(trade);
|
await TradeStore.removeTrade(trade);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
const { Bot } = require("../models");
|
const { Bot } = require("../models");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const { ReactionUserManager } = require("discord.js");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: "GeneralUtils",
|
name: "GeneralUtils",
|
||||||
@@ -11,5 +13,9 @@ module.exports = {
|
|||||||
let bot = await Bot.findOne();
|
let bot = await Bot.findOne();
|
||||||
bot[property] = value;
|
bot[property] = value;
|
||||||
await bot.save();
|
await bot.save();
|
||||||
|
},
|
||||||
|
|
||||||
|
generateLogID: async function() {
|
||||||
|
return crypto.randomBytes(4).toString("hex");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user