Trade: Add TradeStore and trade flow
Adding a TradeStore to keep track of active trades and their states. Also implements the core trade flow and embed logic.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||||
const { Card, User } = require("../models");
|
const { Card, User, Character } = require("../models");
|
||||||
const { UserUtils, CardUtils } = require("../util");
|
const { UserUtils, CardUtils } = require("../util");
|
||||||
const { TradeStore } = require("../stores");
|
const { TradeStore } = require("../stores");
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ module.exports = {
|
|||||||
),
|
),
|
||||||
permissionLevel: 0,
|
permissionLevel: 0,
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply();
|
||||||
let user1 = await UserUtils.getUserByDiscordId(interaction.member.id);
|
let user1 = await UserUtils.getUserByDiscordId(interaction.member.id);
|
||||||
let trade = await TradeStore.getTradeByUser(user1.id);
|
let trade = await TradeStore.getTradeByUser(user1.id);
|
||||||
|
|
||||||
switch (interaction.options.getSubcommand()) {
|
switch (interaction.options.getSubcommand()) {
|
||||||
case "start":
|
case "start":
|
||||||
await interaction.deferReply();
|
|
||||||
let user2 = await UserUtils.getUserByDiscordId(interaction.options.getUser("user").id);
|
let user2 = await UserUtils.getUserByDiscordId(interaction.options.getUser("user").id);
|
||||||
//Attach usernames for convenience
|
//Attach usernames for convenience
|
||||||
user2.name = interaction.options.getUser("user").username;
|
user2.name = interaction.options.getUser("user").username;
|
||||||
@@ -50,23 +50,22 @@ module.exports = {
|
|||||||
this.startTrade(interaction, user1, user2);
|
this.startTrade(interaction, user1, user2);
|
||||||
break;
|
break;
|
||||||
case "view":
|
case "view":
|
||||||
await interaction.deferReply();
|
|
||||||
this.viewTrade(interaction, trade);
|
this.viewTrade(interaction, trade);
|
||||||
break;
|
break;
|
||||||
case "add":
|
case "add":
|
||||||
if (!trade) {
|
if (!trade) {
|
||||||
await interaction.reply({ content: "You don't have an active trade", ephemeral: true });
|
await interaction.editReply({ content: "You don't have an active trade", ephemeral: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await interaction.deferReply();
|
|
||||||
let card = await Card.findOne({
|
let card = await Card.findOne({
|
||||||
where:
|
where:
|
||||||
{ identifier: interaction.options.getString("card") , userId: user1.id }
|
{ identifier: interaction.options.getString("card") , userId: user1.id }
|
||||||
},
|
,
|
||||||
include: [
|
include: [
|
||||||
{ model: Character, include: [{ model: Band }] },
|
{ model: Character },
|
||||||
{ model: User}
|
{ model: User}
|
||||||
]);
|
]}
|
||||||
|
);
|
||||||
this.addCardToTrade(interaction, trade, card);
|
this.addCardToTrade(interaction, trade, card);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -100,94 +99,206 @@ module.exports = {
|
|||||||
//find card by identifier if owned by user
|
//find card by identifier if owned by user
|
||||||
|
|
||||||
if (!card) {
|
if (!card) {
|
||||||
await interaction.reply({ content: "You don't own this card", ephemeral: true });
|
await interaction.editReply({ content: "You don't own this card", ephemeral: true });
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (card.userId !== trade.user1.id) {
|
|
||||||
await interaction.editReply({ content: "You don't own this Card!" });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trade.user1.id === interaction.member.id) {
|
if (trade.user1.id === card.userId) {
|
||||||
trade.user1Cards.push(card);
|
trade.user1Cards.push(card);
|
||||||
} else {
|
}
|
||||||
|
if (trade.user2.id === card.userId) {
|
||||||
trade.user2Cards.push(card);
|
trade.user2Cards.push(card);
|
||||||
}
|
}
|
||||||
this.viewTrade(interaction, trade);
|
|
||||||
|
await interaction.editReply({ content: `User ${interaction.member.user.username} added ${card.identifier} to the trade` });
|
||||||
|
await this.viewTrade(interaction, trade);
|
||||||
},
|
},
|
||||||
|
|
||||||
async viewTrade(interaction, trade) {
|
async viewTrade(interaction, trade) {
|
||||||
if (!trade) {
|
if (!trade) {
|
||||||
await interaction.editReply({ content: "This Trade does not exist!" });
|
await interaction.editReply({ content: "No active Trade" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//delete existing trade message
|
|
||||||
if (trade.embed) {
|
|
||||||
await trade.embed.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
let user1Cards = "No cards"
|
let user1Cards = "";
|
||||||
let user2Cards = "No cards"
|
let user2Cards = "";
|
||||||
|
|
||||||
// for each of user1's cards in the trade
|
// for each of user1's cards in the trade
|
||||||
for (card of trade.user1Cards) {
|
for (card of trade.user1Cards) {
|
||||||
//get the card object
|
let cardStr = CardUtils.getShortString(card);
|
||||||
let card = await Card.findOne({
|
user1Cards += `\n${cardStr}`;
|
||||||
where: {
|
|
||||||
identifier: cardIdentifier
|
|
||||||
},
|
|
||||||
include: [
|
|
||||||
{ model: Character, include: [{ model: Band }] },
|
|
||||||
{ model: User}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
//add it to the list
|
|
||||||
user1Cards += `${card.identifier}\n`;
|
|
||||||
}
|
}
|
||||||
// for each of user2's cards in the trade
|
// for each of user2's cards in the trade
|
||||||
for (card of trade.user2Cards) {
|
for (card of trade.user2Cards) {
|
||||||
//get the card object
|
let cardStr = CardUtils.getShortString(card);
|
||||||
let card = await Card.findOne({
|
user2Cards += cardStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = 0xff000c;
|
||||||
|
let tradeLocked = trade.user1Locked && trade.user2Locked;
|
||||||
|
let tradeAccepted = trade.user1accepted && trade.user2accepted;
|
||||||
|
|
||||||
|
if (trade.state === TradeStore.States.LOCKED) {
|
||||||
|
//color orange
|
||||||
|
color = 0xffa500;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trade.state === TradeStore.States.ACCEPTED) {
|
||||||
|
//color green
|
||||||
|
color = 0x00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trade.state == TradeStore.States.CANCELLED || trade.state == TradeStore.States.EXPIRED) {
|
||||||
|
//color red
|
||||||
|
color = 0xff0000;
|
||||||
|
}
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(`Trade [${trade.id}] ${trade.user1.name} with ${trade.user2.name}`)
|
.setTitle(`Trade [${trade.id}] ${trade.user1.name} with ${trade.user2.name}`)
|
||||||
.setDescription("DUMMY DESCRIPTION")
|
.setDescription("DUMMY DESCRIPTION")
|
||||||
.addFields(
|
.addFields(
|
||||||
{ name: `${trade.user1.name}'s cards`, value: user1Cards },
|
{ name: `${trade.user1.name}'s cards ${trade.user1Locked ? '🔒 Locked' : '🔓'}`, value: user1Cards || "No cards" },
|
||||||
{ name: `${trade.user2.name}'s cards`, value: `DATA2` }
|
{ name: `${trade.user2.name}'s cards ${trade.user2Locked ? '🔒 Locked' : '🔓'}`, value: user2Cards || "No cards" }
|
||||||
)
|
)
|
||||||
.setColor(0x00ff00)
|
.setColor(color)
|
||||||
.setFooter({ text: `TRADE`, iconURL: 'https://cdn.discordapp.com/attachments/856904078754971658/1017431187234508820/fp.png' });
|
.setFooter({ text: `TRADE`, iconURL: 'https://cdn.discordapp.com/attachments/856904078754971658/1017431187234508820/fp.png' });
|
||||||
|
|
||||||
const row = new ActionRowBuilder();
|
let row = new ActionRowBuilder();
|
||||||
row.addComponents(
|
row = this.addComponentsToRow(row, trade);
|
||||||
new ButtonBuilder()
|
|
||||||
.setCustomId(`cancel-trade-${trade.id}`)
|
|
||||||
.setLabel('Cancel')
|
|
||||||
.setStyle(ButtonStyle.Danger)
|
|
||||||
);
|
|
||||||
let reply = await interaction.editReply({ embeds: [embed], components: [row] });
|
|
||||||
trade.embed = reply;
|
|
||||||
|
|
||||||
|
let reply;
|
||||||
|
if (trade.embed) {
|
||||||
|
reply = await trade.embed.edit({ embeds: [embed], components: [row] });
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
reply = await interaction.editReply({ embeds: [embed], components: [row] });
|
||||||
|
await this.attachCollectors(trade, reply, interaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
trade.embed = reply;
|
||||||
|
if (trade.cancelled || trade.expired) {
|
||||||
|
await TradeStore.removeTrade(trade);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addComponentsToRow(row, trade) {
|
||||||
|
//Anything before State.ACCEPTED can still be cancelled
|
||||||
|
if (trade.state < TradeStore.States.ACCEPTED) {
|
||||||
|
row.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`cancel-trade-${trade.id}`)
|
||||||
|
.setLabel('Cancel')
|
||||||
|
.setStyle(ButtonStyle.Danger)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Anything before State.LOCKED can still be locked
|
||||||
|
if (trade.state < TradeStore.States.LOCKED) {
|
||||||
|
row.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`lock-trade-${trade.id}`)
|
||||||
|
.setLabel('Lock')
|
||||||
|
.setStyle(ButtonStyle.Success)
|
||||||
|
.setEmoji('🔒')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let acceptCount = 0;
|
||||||
|
if (trade.user1accepted) {
|
||||||
|
acceptCount++;
|
||||||
|
}
|
||||||
|
if (trade.user2accepted) {
|
||||||
|
acceptCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only trades in state locked can be accepted
|
||||||
|
if (trade.state === TradeStore.States.LOCKED) {
|
||||||
|
row.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`accept-trade-${trade.id}`)
|
||||||
|
.setLabel(`Accept (${acceptCount}/2)`)
|
||||||
|
.setStyle(ButtonStyle.Success)
|
||||||
|
.setEmoji('✅')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if trade is accepted, add a button to finalize it
|
||||||
|
if (trade.state === TradeStore.States.ACCEPTED) {
|
||||||
|
row.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`trade-completed-${trade.id}`)
|
||||||
|
.setLabel('Completed')
|
||||||
|
.setStyle(ButtonStyle.Success)
|
||||||
|
.setDisabled(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
},
|
||||||
|
|
||||||
|
async attachCollectors(trade, reply, interaction) {
|
||||||
//button collector
|
//button collector
|
||||||
const filter = (button) => button.user.id === trade.user1.discordId || button.user.id === trade.user2.discordId;
|
const filter = (button) => button.user.id === trade.user1.discordId || button.user.id === trade.user2.discordId;
|
||||||
const collector = reply.createMessageComponentCollector({ filter, time: tradeTimeout });
|
const collector = reply.createMessageComponentCollector({ filter, time: tradeTimeout });
|
||||||
|
|
||||||
collector.on('collect', async (button) => {
|
collector.on('collect', async (button) => {
|
||||||
|
//if interaction member is neither user1 nor user2, ignore
|
||||||
|
if (button.user.id !== trade.user1.discordId && button.user.id !== trade.user2.discordId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (button.customId === `cancel-trade-${trade.id}`) {
|
if (button.customId === `cancel-trade-${trade.id}`) {
|
||||||
await TradeStore.removeTrade(trade);
|
|
||||||
collector.stop("cancel");
|
collector.stop("cancel");
|
||||||
}
|
}
|
||||||
|
if (button.customId === `lock-trade-${trade.id}`) {
|
||||||
|
//if interaction member is user1
|
||||||
|
if (button.user.id === trade.user1.discordId) {
|
||||||
|
trade.user1Locked = true;
|
||||||
|
}
|
||||||
|
//if interaction member is user2
|
||||||
|
|
||||||
|
if (button.user.id === trade.user2.discordId) {
|
||||||
|
trade.user2Locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trade.user1Locked && trade.user2Locked) {
|
||||||
|
trade.state = TradeStore.States.LOCKED;
|
||||||
|
}
|
||||||
|
await button.deferUpdate();
|
||||||
|
await this.viewTrade(interaction, trade);
|
||||||
|
}
|
||||||
|
if (button.customId === `accept-trade-${trade.id}`) {
|
||||||
|
//if interaction member is user1
|
||||||
|
if (button.user.id === trade.user1.discordId) {
|
||||||
|
trade.user1accepted = true;
|
||||||
|
}
|
||||||
|
//if interaction member is user2
|
||||||
|
|
||||||
|
if (button.user.id === trade.user2.discordId) {
|
||||||
|
trade.user2accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trade.user1accepted && trade.user2accepted) {
|
||||||
|
trade.state = TradeStore.States.ACCEPTED;
|
||||||
|
collector.stop("accepted");
|
||||||
|
}
|
||||||
|
|
||||||
|
await button.deferUpdate();
|
||||||
|
await this.viewTrade(interaction, trade);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
collector.on('end', async (collected, reason) => {
|
collector.on('end', async (collected, reason) => {
|
||||||
console.log(`Collected ${collected.size} items, reason: ${reason}`);
|
console.log(`Collected ${collected.size} items, reason: ${reason}`);
|
||||||
if (reason === "time") {
|
if (reason === "time") {
|
||||||
await TradeStore.removeTrade(trade);
|
trade.state = TradeStore.States.EXPIRED;
|
||||||
await trade.embed.edit({ content: `Trade ${trade.id} expired`, embeds: [], components: [] });
|
|
||||||
}
|
}
|
||||||
if (reason === "cancel") {
|
if (reason === "cancel") {
|
||||||
await trade.embed.edit({ content: `Trade ${trade.id} cancelled`, embeds: [], components: [] });
|
trade.state = TradeStore.States.CANCELLED;
|
||||||
}
|
}
|
||||||
|
if (reason === "accepted") {
|
||||||
|
//TODO: perform trade on database
|
||||||
|
}
|
||||||
|
await this.viewTrade(interaction, trade);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,15 @@ const QUALITY_NAMES = {
|
|||||||
6 : "Shiny"
|
6 : "Shiny"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QUALITY_SYMBOLS = {
|
||||||
|
1 : "<a:shyyOrcaTrain:1005123718542020729><:shyyDead:859552234697916426><:shyyDead:859552234697916426><:shyyDead:859552234697916426><:shyyDead:859552234697916426>",
|
||||||
|
2 : "<a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><:shyyDead:859552234697916426><:shyyDead:859552234697916426><:shyyDead:859552234697916426>",
|
||||||
|
3 : "<a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><:shyyDead:859552234697916426><:shyyDead:859552234697916426>",
|
||||||
|
4 : "<a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><:shyyDead:859552234697916426>",
|
||||||
|
5 : "<a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729><a:shyyOrcaTrain:1005123718542020729>",
|
||||||
|
6 : "⭐⭐⭐⭐⭐"
|
||||||
|
}
|
||||||
|
|
||||||
const CURRENCY_SYMBOLS = {
|
const CURRENCY_SYMBOLS = {
|
||||||
1 : "🎶",
|
1 : "🎶",
|
||||||
2 : "💎"
|
2 : "💎"
|
||||||
@@ -56,5 +65,6 @@ const QUALITY_VALUES = {
|
|||||||
exports.QUALITY = QUALITY;
|
exports.QUALITY = QUALITY;
|
||||||
exports.QUALITY_NAMES = QUALITY_NAMES;
|
exports.QUALITY_NAMES = QUALITY_NAMES;
|
||||||
exports.CURRENCY_SYMBOLS = CURRENCY_SYMBOLS;
|
exports.CURRENCY_SYMBOLS = CURRENCY_SYMBOLS;
|
||||||
|
exports.QUALITY_SYMBOLS = QUALITY_SYMBOLS;
|
||||||
exports.CURRENCY_NAMES = CURRENCY_NAMES;
|
exports.CURRENCY_NAMES = CURRENCY_NAMES;
|
||||||
exports.QUALITY_VALUES = QUALITY_VALUES;
|
exports.QUALITY_VALUES = QUALITY_VALUES;
|
||||||
@@ -17,6 +17,14 @@ module.exports = {
|
|||||||
async getTradeByUser(userId) {
|
async getTradeByUser(userId) {
|
||||||
return this.activeTrades.find(trade => trade.user1.id === userId || trade.user2.id === userId);
|
return this.activeTrades.find(trade => trade.user1.id === userId || trade.user2.id === userId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
States: {
|
||||||
|
OPEN: 0,
|
||||||
|
LOCKED: 1,
|
||||||
|
ACCEPTED: 2,
|
||||||
|
CANCELLED: 3,
|
||||||
|
EXPIRED: 4
|
||||||
|
},
|
||||||
|
|
||||||
Trade: class Trade {
|
Trade: class Trade {
|
||||||
constructor(id, user1, user2) {
|
constructor(id, user1, user2) {
|
||||||
@@ -26,8 +34,11 @@ module.exports = {
|
|||||||
this.embed = null;
|
this.embed = null;
|
||||||
this.user1Cards = [];
|
this.user1Cards = [];
|
||||||
this.user2Cards = [];
|
this.user2Cards = [];
|
||||||
this.user1Accept = false;
|
this.user1Locked = false;
|
||||||
this.user2Accept = false;
|
this.user2Locked = false;
|
||||||
|
this.user1Accepted = false;
|
||||||
|
this.user2Accepted = false;
|
||||||
|
this.state = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user