Force Push, guh
This commit is contained in:
parent
9d9f0e2e91
commit
700ba6b1c9
113
commands.js
113
commands.js
|
@ -103,6 +103,119 @@ module.exports = {
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "createproduct",
|
||||||
|
"description": "Create a product",
|
||||||
|
"default_member_permissions": 0,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"description": "The name of the product",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deleteproduct",
|
||||||
|
description: "Delete a product",
|
||||||
|
default_member_permissions: 0,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
description: "The name of the product",
|
||||||
|
type: Discord.ApplicationCommandOptionType.String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "update",
|
||||||
|
description: "Update a product",
|
||||||
|
default_member_permissions: 0,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
description: "The name of the product",
|
||||||
|
type: Discord.ApplicationCommandOptionType.String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "field",
|
||||||
|
description: "The field to update",
|
||||||
|
type: Discord.ApplicationCommandOptionType.String,
|
||||||
|
required: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: "Name",
|
||||||
|
value: "name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Description",
|
||||||
|
value: "description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dev Product ID",
|
||||||
|
value: "devProductId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Image ID",
|
||||||
|
value: "imageId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "File",
|
||||||
|
value: "file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Stocking Info",
|
||||||
|
value: "stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Category",
|
||||||
|
value: "category"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "forcelink",
|
||||||
|
description: "Force link a Roblox account to a Discord user",
|
||||||
|
default_member_permissions: 0,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "roblox-id",
|
||||||
|
description: "The Roblox ID of the user",
|
||||||
|
type: Discord.ApplicationCommandOptionType.Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "discord-id",
|
||||||
|
description: "The Discord ID of the user",
|
||||||
|
type: Discord.ApplicationCommandOptionType.User,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "forceunlink",
|
||||||
|
description: "Force unlink a Roblox account from a Discord user",
|
||||||
|
default_member_permissions: 0,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "roblox-id",
|
||||||
|
description: "The Roblox ID of the user",
|
||||||
|
type: Discord.ApplicationCommandOptionType.Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "discord-id",
|
||||||
|
description: "The Discord ID of the user",
|
||||||
|
type: Discord.ApplicationCommandOptionType.User,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
admin: []
|
admin: []
|
||||||
|
|
47
commands/createproduct.js
Normal file
47
commands/createproduct.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const client = global.discord_client
|
||||||
|
const pool = global.db_pool;
|
||||||
|
const createProdHandler = require('../messageHandlers/create_prod.js');
|
||||||
|
|
||||||
|
if (!global.productCreationData) global.productCreationData = {};
|
||||||
|
|
||||||
|
const execute = async (interaction) => {
|
||||||
|
console.log("Checking if user is already creating a product");
|
||||||
|
if (global.productCreationData[interaction.user.id]) return interaction.reply({ content: "You are already creating a product!", ephemeral: true });
|
||||||
|
global.productCreationData[interaction.user.id] = {
|
||||||
|
name: interaction.options.getString("name")
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const productName = global.productCreationData[interaction.user.id].name;
|
||||||
|
const productResult = await pool.query(`SELECT * FROM products WHERE UPPER(name) = UPPER(?)`, [productName]);
|
||||||
|
if (productResult.length > 0) {
|
||||||
|
delete global.productCreationData[interaction.user.id];
|
||||||
|
return interaction.reply({ content: "A product with this name already exists!", ephemeral: true });
|
||||||
|
}
|
||||||
|
console.log("Checking guild hub");
|
||||||
|
const guildId = interaction.guildId;
|
||||||
|
const hubResult = await pool.query(`SELECT * FROM hubs WHERE discordGuild = ?`, [guildId]);
|
||||||
|
if (hubResult.length === 0) {
|
||||||
|
console.log("No hub found");
|
||||||
|
delete global.productCreationData[interaction.user.id];
|
||||||
|
return interaction.reply({ content: "This guild does not have a hub set up!", ephemeral: true });
|
||||||
|
}
|
||||||
|
console.log("Hub found");
|
||||||
|
// Proceed with creation
|
||||||
|
await interaction.reply({ ephemeral: true, content: "Getting things ready..." });
|
||||||
|
await interaction.user.send({ content: `Creating product: \`${productName}\`` });
|
||||||
|
await interaction.user.send({ content: "Please provide a description for the product. Say `cancel` to exit." });
|
||||||
|
interaction.editReply({ephemeral: true, content: "Check your DMs!"});
|
||||||
|
global.productCreationData[interaction.user.id] = {
|
||||||
|
name: productName,
|
||||||
|
step: 1,
|
||||||
|
hub: hubResult[0].id
|
||||||
|
};
|
||||||
|
global.dmHandlers[interaction.user.id] = createProdHandler;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
delete global.productCreationData[interaction.user.id];
|
||||||
|
return interaction.editReply({ content: "An error occurred during the product creation process.", ephemeral: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { execute }
|
34
commands/forcelink.js
Normal file
34
commands/forcelink.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const client = global.discord_client
|
||||||
|
const pool = global.db_pool;
|
||||||
|
|
||||||
|
const execute = async (interaction) => {
|
||||||
|
const robloxId = interaction.options.getNumber("roblox-id");
|
||||||
|
const discordID = interaction.options.getUser("discord-id")?.id;
|
||||||
|
if (!discordID) return interaction.reply({ content: "You must provide a Discord User", ephemeral: true });
|
||||||
|
if (!robloxId) return interaction.reply({ content: "You must provide a Roblox ID", ephemeral: true });
|
||||||
|
try {
|
||||||
|
const connection = await pool.getConnection();
|
||||||
|
const [rows] = await connection.query("SELECT * FROM users WHERE robloxId = ?", [robloxId]);
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
await connection.query("INSERT INTO users (robloxId, discordId) VALUES (?, ?)", [robloxId, discordID]);
|
||||||
|
} else {
|
||||||
|
const user = rows[0];
|
||||||
|
if (!user.discordId) {
|
||||||
|
await connection.query("UPDATE users SET discordId = ? WHERE robloxId = ?", [discordID, robloxId]);
|
||||||
|
} else if (user.discordId !== discordID) {
|
||||||
|
await connection.query("UPDATE users SET discordId = NULL WHERE discordId = ?", [discordID]);
|
||||||
|
await connection.query("UPDATE users SET discordId = ? WHERE robloxId = ?", [discordID, robloxId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.release();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return interaction.reply({ content: "An error occurred while linking your account", ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return interaction.reply({ content: "Successfully linked your account", ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { execute }
|
|
@ -15,6 +15,11 @@ const execute = async (interaction) => {
|
||||||
if (!row) return interaction.reply({ content: "User not found", ephemeral: true });
|
if (!row) return interaction.reply({ content: "User not found", ephemeral: true });
|
||||||
robloxID = row.robloxId;
|
robloxID = row.robloxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the hub for the guild
|
||||||
|
const guildID = interaction.guild.id;
|
||||||
|
const [hub] = await pool.query('SELECT * FROM hubs WHERE discordGuild = ?', [guildID]);
|
||||||
|
if (!hub) return interaction.reply({ content: "Hub not found for this guild", ephemeral: true });
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
||||||
|
@ -22,14 +27,39 @@ const execute = async (interaction) => {
|
||||||
|
|
||||||
const productName = interaction.options.getString("product-name");
|
const productName = interaction.options.getString("product-name");
|
||||||
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
||||||
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ?', [`%${productName.toUpperCase()}%`]);
|
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ? AND hubId = ?', [`%${productName.toUpperCase()}%`, hub.id]);
|
||||||
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
||||||
|
|
||||||
// Check if the user already owns the product
|
// Check if the user already owns the product
|
||||||
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
||||||
if (purchase) return interaction.reply({ content: "User already owns this product", ephemeral: true });
|
if (purchase) return interaction.reply({ content: "User already owns this product", ephemeral: true });
|
||||||
|
|
||||||
// Insert purchase into database
|
// Insert purchase into database
|
||||||
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [robloxID, product.id, product.hubId]);
|
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [robloxID, product.id, product.hubId]);
|
||||||
|
try {
|
||||||
|
// Assuming you have a function to send a message to the user
|
||||||
|
dscUser = await client.users.fetch(user.discordId);
|
||||||
|
dscUser.send(`You have been givena copy of ${product.name}!\nUse \`/retrive ${product.name}\` in the Discord server to download it!`);
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, user has privacy settings enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hub.logChannel != null) {
|
||||||
|
try {
|
||||||
|
chan = await client.channels.fetch(hub.logChannel);
|
||||||
|
chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `Product Given`,
|
||||||
|
color: 0x00ff00,
|
||||||
|
description: `**Roblox ID:** ${user.robloxId}\n**Discord User:** <@${user.discordId}>\n**Product:** ${product.name}\n**Type:** Give`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, channel was deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
return interaction.reply({ content: `Gave \`${product.name}\` to ${robloxID}`, ephemeral: true });
|
return interaction.reply({ content: `Gave \`${product.name}\` to ${robloxID}`, ephemeral: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,12 @@ const execute = async (interaction) => {
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
if (!robloxID) return interaction.reply({ content: "User not found", ephemeral: true });
|
if (!robloxID) return interaction.reply({ content: "User not found", ephemeral: true });
|
||||||
|
|
||||||
|
const [hub] = await pool.query('SELECT * FROM hubs WHERE discordGuild = ?', [interaction.guild.id]);
|
||||||
|
if (!hub) return interaction.reply({ content: "Hub not found for this guild", ephemeral: true });
|
||||||
|
|
||||||
const productName = interaction.options.getString("product-name");
|
const productName = interaction.options.getString("product-name");
|
||||||
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
||||||
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ?', [`%${productName.toUpperCase()}%`]);
|
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ? AND hubId = ?', [`%${productName.toUpperCase()}%`, hub.id]);
|
||||||
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
||||||
// Check if the user already owns the product
|
// Check if the user already owns the product
|
||||||
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
||||||
|
@ -28,7 +31,7 @@ const execute = async (interaction) => {
|
||||||
type: 2,
|
type: 2,
|
||||||
label: 'Download',
|
label: 'Download',
|
||||||
style: 5,
|
style: 5,
|
||||||
url: `https://cdn.example.com/${product.file}/${authToken}`
|
url: `${process.env.BASE_URL}/cdn/${product.file}/${authToken}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -47,7 +50,7 @@ const execute = async (interaction) => {
|
||||||
type: 2,
|
type: 2,
|
||||||
label: 'Download',
|
label: 'Download',
|
||||||
style: 5,
|
style: 5,
|
||||||
url: `https://cdn.example.com/${product.file}/${authToken}`
|
url: `${process.env.BASE_URL}/cdn/${product.file}/${authToken}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,14 @@ const execute = async (interaction) => {
|
||||||
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
||||||
if (!user) return interaction.reply({ content: "User not found", ephemeral: true });
|
if (!user) return interaction.reply({ content: "User not found", ephemeral: true });
|
||||||
|
|
||||||
|
// Get the hub for the guild
|
||||||
|
const guildID = interaction.guild.id;
|
||||||
|
const [hub] = await pool.query('SELECT * FROM hubs WHERE discordGuild = ?', [guildID]);
|
||||||
|
if (!hub) return interaction.reply({ content: "Hub not found for this guild", ephemeral: true });
|
||||||
|
|
||||||
const productName = interaction.options.getString("product-name");
|
const productName = interaction.options.getString("product-name");
|
||||||
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
// try catch try and find the product based on partial product name, parse everything in uppercase to make things easier
|
||||||
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ?', [`%${productName.toUpperCase()}%`]);
|
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ? AND hubId = ?', [`%${productName.toUpperCase()}%`, hub.id]);
|
||||||
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
||||||
// Check if the user already owns the product
|
// Check if the user already owns the product
|
||||||
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
||||||
|
@ -30,6 +35,23 @@ const execute = async (interaction) => {
|
||||||
|
|
||||||
// Remove purchase from database
|
// Remove purchase from database
|
||||||
await pool.query('DELETE FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
await pool.query('DELETE FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
||||||
|
|
||||||
|
if (hub.logChannel != null) {
|
||||||
|
try {
|
||||||
|
chan = await client.channels.fetch(hub.logChannel);
|
||||||
|
chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `Product Revoked`,
|
||||||
|
color: 0xff0000,
|
||||||
|
description: `**Roblox ID:** ${user.robloxId}\n**Discord User:** <@${user.discordId}>\n**Product:** ${product.name}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, channel was deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
return interaction.reply({ content: `Removed \`${product.name}\` from ${robloxID}`, ephemeral: true });
|
return interaction.reply({ content: `Removed \`${product.name}\` from ${robloxID}`, ephemeral: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,6 @@ const execute = async (interaction) => {
|
||||||
return interaction.reply({ content: "Successfully unlinked your account. Please use the hub game to get a new pairing code!", ephemeral: true });
|
return interaction.reply({ content: "Successfully unlinked your account. Please use the hub game to get a new pairing code!", ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { execute }
|
const noop = () => { };
|
||||||
|
|
||||||
|
module.exports = { execute: noop }
|
61
commands/update.js
Normal file
61
commands/update.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
const client = global.discord_client
|
||||||
|
const pool = global.db_pool;
|
||||||
|
const updateProductHandler = require('../messageHandlers/update_prod.js');
|
||||||
|
|
||||||
|
if (!global.productUpdateData) global.productUpdateData = {};
|
||||||
|
|
||||||
|
const execute = async (interaction) => {
|
||||||
|
if (!interaction.guild) return interaction.reply({ content: "This command must be used in a server!", ephemeral: true });
|
||||||
|
if (global.productUpdateData[interaction.user.id]) return interaction.reply({ content: "You are already updating a product!", ephemeral: true });
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get the hub for the guild
|
||||||
|
const guildID = interaction.guild.id;
|
||||||
|
const [hub] = await pool.query('SELECT * FROM hubs WHERE discordGuild = ?', [guildID]);
|
||||||
|
if (!hub) return interaction.reply({ content: "Hub not found for this guild", ephemeral: true });
|
||||||
|
|
||||||
|
const productName = interaction.options.getString("name");
|
||||||
|
const [product] = await pool.query('SELECT * FROM products WHERE UPPER(name) LIKE ? AND hubId = ?', [`%${productName.toUpperCase()}%`, hub.id]);
|
||||||
|
if (!product) return interaction.reply({ content: "Product not found", ephemeral: true });
|
||||||
|
// Proceed with creation
|
||||||
|
await interaction.reply({ ephemeral: true, content: "Getting things ready..." });
|
||||||
|
await interaction.user.send({ content: `Updating product: \`${product.name}\`` });
|
||||||
|
switch (interaction.options.getString("field")) {
|
||||||
|
case "name":
|
||||||
|
await interaction.user.send({ content: "Please provide a new name for the product. Say `cancel` to exit." });
|
||||||
|
break;
|
||||||
|
case "description":
|
||||||
|
await interaction.user.send({ content: "Please provide a new description for the product. Say `cancel` to exit." });
|
||||||
|
break;
|
||||||
|
case "devProductId":
|
||||||
|
await interaction.user.send({ content: "Please provide a new developer product ID for the product. Say `cancel` to exit." });
|
||||||
|
break;
|
||||||
|
case "imageId":
|
||||||
|
await interaction.user.send({ content: "Please provide a new image ID for the product. Say `cancel` to exit." });
|
||||||
|
break;
|
||||||
|
case "file":
|
||||||
|
await interaction.user.send({ content: "Please provide a new file for the product. Say `cancel` to exit." });
|
||||||
|
break;
|
||||||
|
case "stock":
|
||||||
|
await interaction.user.send({ content: "Please provide a new stock quantity for the product. Say `cancel` to exit. Set `-1` to disable." });
|
||||||
|
break;
|
||||||
|
case "category":
|
||||||
|
await interaction.user.send({ content: "Please provide a new category for the product. Say `cancel` to exit. Set to `~none` to remove." });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return interaction.editReply({ content: "Invalid field provided.", ephemeral: true });
|
||||||
|
}
|
||||||
|
interaction.editReply({ ephemeral: true, content: "Check your DMs!" });
|
||||||
|
global.productUpdateData[interaction.user.id] = {
|
||||||
|
id: product.id,
|
||||||
|
type: interaction.options.getString("field")
|
||||||
|
};
|
||||||
|
global.dmHandlers[interaction.user.id] = updateProductHandler;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
delete global.productCreationData[interaction.user.id];
|
||||||
|
return interaction.editReply({ content: "An error occurred during the product creation process.", ephemeral: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { execute }
|
11
index.js
11
index.js
|
@ -9,7 +9,7 @@ const app = express();
|
||||||
global.log = log;
|
global.log = log;
|
||||||
|
|
||||||
const Discord = require("discord.js")
|
const Discord = require("discord.js")
|
||||||
const client = new Discord.Client({intents: ["Guilds", "DirectMessages"]})
|
const client = new Discord.Client({intents: ["Guilds", "DirectMessages", "MessageContent", "GuildMembers"]})
|
||||||
|
|
||||||
const pool = MariaDB.createPool({
|
const pool = MariaDB.createPool({
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
|
@ -86,6 +86,15 @@ client.on("interactionCreate", async (interaction) => {
|
||||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
global.dmHandlers = {}
|
||||||
|
client.on("messageCreate", async (message) => {
|
||||||
|
if (message.author.bot) return;
|
||||||
|
if (!message.channel.isDMBased()) return;
|
||||||
|
if (!global.dmHandlers[message.author.id]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.dmHandlers[message.author.id](message);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const port = process.env.SERVER_PORT || 3000;
|
const port = process.env.SERVER_PORT || 3000;
|
||||||
|
|
136
messageHandlers/create_prod.js
Normal file
136
messageHandlers/create_prod.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
const client = global.discord_client
|
||||||
|
const pool = global.db_pool;
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
|
||||||
|
const { pipeline } = require('stream');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
|
||||||
|
const streamPipeline = promisify(pipeline);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a file from a URL to a specified destination.
|
||||||
|
* @param {string} url - The URL to download the file from.
|
||||||
|
* @param {string} dest - The destination file path to save the file.
|
||||||
|
* @param {function} cb - Callback function called on completion or error.
|
||||||
|
*/
|
||||||
|
async function download(url, dest, cb) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
await streamPipeline(response.body, fs.createWriteStream(dest));
|
||||||
|
cb(null); // Signal success
|
||||||
|
} catch (error) {
|
||||||
|
cb(error); // Pass error to callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = (user) => {
|
||||||
|
delete global.productCreationData[user.id];
|
||||||
|
delete global.dmHandlers[user.id];
|
||||||
|
user.send('Product creation cancelled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const execute = async (message) => {
|
||||||
|
if (!interaction.guild) return interaction.reply({ content: "This command must be used in a server!", ephemeral: true });
|
||||||
|
switch (global.productCreationData[message.author.id].step) {
|
||||||
|
case 1: // Description
|
||||||
|
const description = message.content.trim();
|
||||||
|
if (description.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.productCreationData[message.author.id].description = description;
|
||||||
|
global.productCreationData[message.author.id].step = 2;
|
||||||
|
message.channel.send('Description set. Please provide the dev product ID. Say `cancel` to cancel creation.');
|
||||||
|
break;
|
||||||
|
case 2: // Dev product ID
|
||||||
|
const devProductId = message.content.trim();
|
||||||
|
|
||||||
|
if (devProductId.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d{1,15}$/.test(newDevId)) {
|
||||||
|
message.channel.send('Invalid Dev Product ID. It must be a number between 1 and 15 digits.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.productCreationData[message.author.id].devProductId = devProductId;
|
||||||
|
global.productCreationData[message.author.id].step = 3;
|
||||||
|
message.channel.send('Dev product ID set. Please provide the decal ID. Say `skip` to not set one, or `cancel` to cancel creation.');
|
||||||
|
break;
|
||||||
|
case 3: // Image ID
|
||||||
|
const imageId = message.content.trim();
|
||||||
|
if (imageId.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageId.toLowerCase() === "skip") {
|
||||||
|
global.productCreationData[message.author.id].imageId = null;
|
||||||
|
global.productCreationData[message.author.id].step = 4;
|
||||||
|
message.channel.send('Image ID skipped. Please upload the product file. Say `cancel` to cancel creation.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d{1,15}$/.test(newImageId)) {
|
||||||
|
message.channel.send('Invalid Image ID. It must be a number between 1 and 15 digits.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.productCreationData[message.author.id].imageId = imageId;
|
||||||
|
global.productCreationData[message.author.id].step = 4;
|
||||||
|
message.channel.send('Image ID set. Please upload the product file.');
|
||||||
|
break;
|
||||||
|
case 4: // File
|
||||||
|
if (message.content.trim().toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.attachments.size > 0) {
|
||||||
|
const attachment = message.attachments.first();
|
||||||
|
const validExtensions = ['zip', 'rbxm', 'rbxmx'];
|
||||||
|
const fileExtension = attachment.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
|
if (!validExtensions.includes(fileExtension)) {
|
||||||
|
message.channel.send('Invalid file type. Only zip, rbxm, and rbxmx files are allowed.');
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = fs.createWriteStream(`./productFiles/${crypto.randomBytes(8).toString('hex')}`);
|
||||||
|
const prodId = crypto.randomUUID();
|
||||||
|
download(attachment.url, file.path, (err) => {
|
||||||
|
if (err) {
|
||||||
|
message.channel.send('Failed to download the file.');
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.productCreationData[message.author.id].filePath = file.path.split('/').pop();
|
||||||
|
let fileType = attachment.name.split('.').pop();
|
||||||
|
global.productCreationData[message.author.id].step = 5;
|
||||||
|
message.channel.send('File uploaded successfully. Product creation complete.');
|
||||||
|
pool.query(`INSERT INTO products (id, name, description, devProductId, decalId, file, fileType, hubId) VALUES (?, ?, ?, ?, ?, ?, ?, ?);`, [prodId, global.productCreationData[message.author.id].name, global.productCreationData[message.author.id].description, global.productCreationData[message.author.id].devProductId, global.productCreationData[message.author.id].imageId, global.productCreationData[message.author.id].filePath, fileType, global.productCreationData[message.author.id].hub], (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
message.channel.send('An error occurred while creating the product.');
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message.author.send('Product created successfully.');
|
||||||
|
delete global.productCreationData[message.author.id];
|
||||||
|
delete global.dmHandlers[message.author.id];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.channel.send('No file attached. Please upload the product file.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message.channel.send('Invalid step.');
|
||||||
|
cancel(message.author);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = execute
|
143
messageHandlers/update_prod.js
Normal file
143
messageHandlers/update_prod.js
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
const client = global.discord_client
|
||||||
|
const pool = global.db_pool;
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
|
||||||
|
const { pipeline } = require('stream');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
|
||||||
|
const streamPipeline = promisify(pipeline);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a file from a URL to a specified destination.
|
||||||
|
* @param {string} url - The URL to download the file from.
|
||||||
|
* @param {string} dest - The destination file path to save the file.
|
||||||
|
* @param {function} cb - Callback function called on completion or error.
|
||||||
|
*/
|
||||||
|
async function download(url, dest, cb) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
await streamPipeline(response.body, fs.createWriteStream(dest));
|
||||||
|
cb(null); // Signal success
|
||||||
|
} catch (error) {
|
||||||
|
cb(error); // Pass error to callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = (user) => {
|
||||||
|
delete global.productUpdateData[user.id];
|
||||||
|
delete global.dmHandlers[user.id];
|
||||||
|
user.send('Product update cancelled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const execute = async (message) => {
|
||||||
|
switch (global.productUpdateData[message.author.id].type) {
|
||||||
|
case "name": // Name
|
||||||
|
const newName = message.content.trim();
|
||||||
|
if (newName.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET name = ? WHERE id = ?', [newName, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Product name updated!');
|
||||||
|
break;
|
||||||
|
case "description": // Description
|
||||||
|
const newDesc = message.content.trim();
|
||||||
|
if (newDesc.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET description = ? WHERE id = ?', [newDesc, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Product description updated!');
|
||||||
|
break;
|
||||||
|
case "devProductId": // Dev Product ID
|
||||||
|
const newDevId = message.content.trim();
|
||||||
|
if (newDevId.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d{1,15}$/.test(newDevId)) {
|
||||||
|
message.channel.send('Invalid Dev Product ID. It must be a number between 1 and 15 digits.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET devProductID = ? WHERE id = ?', [newDevId, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Dev Product ID updated!');
|
||||||
|
break;
|
||||||
|
case "imageId": // Image ID
|
||||||
|
const newImageId = message.content.trim();
|
||||||
|
if (newImageId.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d{1,15}$/.test(newImageId)) {
|
||||||
|
message.channel.send('Invalid Image ID. It must be a number between 1 and 15 digits.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET imageId = ? WHERE id = ?', [newImageId, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Image ID updated!');
|
||||||
|
break;
|
||||||
|
case "file": // File
|
||||||
|
if (message.content.trim().toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.attachments.size > 0) {
|
||||||
|
const attachment = message.attachments.first();
|
||||||
|
const validExtensions = ['zip', 'rbxm', 'rbxmx'];
|
||||||
|
const fileExtension = attachment.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
|
if (!validExtensions.includes(fileExtension)) {
|
||||||
|
message.channel.send('Invalid file type. Only zip, rbxm, and rbxmx files are allowed.');
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = fs.createWriteStream(`./productFiles/${crypto.randomBytes(8).toString('hex')}`);
|
||||||
|
download(attachment.url, file.path, async (err) => {
|
||||||
|
if (err) {
|
||||||
|
message.channel.send('Failed to download the file.');
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileType = attachment.name.split('.').pop();
|
||||||
|
await pool.query('UPDATE products SET file = ?, fileType = ? WHERE id = ?', [file.path.split('/').pop(), fileType, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('File updated successfully!');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.channel.send('No file attached. Please upload the product file.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "stock": // Stocking info
|
||||||
|
const newStock = parseInt(message.content.trim());
|
||||||
|
if (isNaN(newStock)) {
|
||||||
|
message.channel.send('Invalid stock quantity. It must be a number.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET stock = ? WHERE id = ?', [newStock, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Stock quantity updated!');
|
||||||
|
break;
|
||||||
|
case "category": // Category
|
||||||
|
const newCategory = message.content.trim();
|
||||||
|
if (newCategory.toLowerCase() === 'cancel') {
|
||||||
|
cancel(message.author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newCategory.toLowerCase() === '~none') {
|
||||||
|
newCategory = null;
|
||||||
|
}
|
||||||
|
await pool.query('UPDATE products SET category = ? WHERE id = ?', [newCategory, global.productUpdateData[message.author.id].id]);
|
||||||
|
message.channel.send('Category updated!');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message.channel.send('Invalid type. Somehow?');
|
||||||
|
cancel(message.author);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete global.productUpdateData[message.author.id];
|
||||||
|
delete global.dmHandlers[message.author.id];
|
||||||
|
}
|
||||||
|
module.exports = execute
|
3
migrations/006_update_hubs_add_logChannel
Normal file
3
migrations/006_update_hubs_add_logChannel
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE hubs
|
||||||
|
ADD COLUMN logChannel VARCHAR(255);
|
||||||
|
-- Uhh
|
|
@ -25,6 +25,37 @@ router.get('/getSession', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const products = await pool.query('SELECT * FROM products WHERE hubId = ?', [hub.id]);
|
const products = await pool.query('SELECT * FROM products WHERE hubId = ?', [hub.id]);
|
||||||
const purchases = await pool.query('SELECT * FROM purchases WHERE hubId = ?', [hub.id]);
|
const purchases = await pool.query('SELECT * FROM purchases WHERE hubId = ?', [hub.id]);
|
||||||
|
// Generate bestSeller object from purchases. Find what product id has the most sales, then make the object
|
||||||
|
const purchaseCounts = purchases.reduce((acc, purchase) => {
|
||||||
|
console.log(acc)
|
||||||
|
console.log(purchase)
|
||||||
|
acc[purchase.productId] = (acc[purchase.productId] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const bestSellerProductId = Object.keys(purchaseCounts).reduce((a, b) => purchaseCounts[a] > purchaseCounts[b] ? a : b, null);
|
||||||
|
const bestSellerProduct = products.find(product => product.id == bestSellerProductId);
|
||||||
|
const bestSeller = bestSellerProduct ? {
|
||||||
|
productID: bestSellerProduct.id,
|
||||||
|
name: bestSellerProduct.name,
|
||||||
|
description: bestSellerProduct.description,
|
||||||
|
devproduct_id: (bestSellerProduct.stock == 0) ? 0 : bestSellerProduct.devProductID,
|
||||||
|
decalID: bestSellerProduct.decalId || "0",
|
||||||
|
stock: bestSellerProduct.stock == -1 ? false : bestSellerProduct.stock,
|
||||||
|
onsale: bestSellerProduct.stock == -1 || bestSellerProduct.stock > 0 ? true : false,
|
||||||
|
category: bestSellerProduct.stock == -1 || bestSellerProduct.stock > 0 ? bestSellerProduct.category : "Out of Stock",
|
||||||
|
rating: {
|
||||||
|
currentScore: 0,
|
||||||
|
maxScore: 0,
|
||||||
|
amountOfReviews: 0
|
||||||
|
},
|
||||||
|
tags: [],
|
||||||
|
playerData: {
|
||||||
|
robloxId: pid,
|
||||||
|
ownsProduct: purchases.some(purchase => purchase.productId == bestSellerProduct.id && purchase.robloxId == pid)
|
||||||
|
}
|
||||||
|
} : {};
|
||||||
|
|
||||||
// generate array of products
|
// generate array of products
|
||||||
const respData = JSON.stringify({
|
const respData = JSON.stringify({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
@ -51,15 +82,16 @@ router.get('/getSession', async (req, res) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
productsData: {
|
productsData: {
|
||||||
|
bestsellerProduct: bestSeller,
|
||||||
allProducts: products.map(product => ({
|
allProducts: products.map(product => ({
|
||||||
productID: product.id,
|
productID: product.id,
|
||||||
name: product.name,
|
name: product.name,
|
||||||
description: product.description,
|
description: product.description,
|
||||||
devproduct_id: product.devProductID,
|
devproduct_id: (product.stock == 0) ? 0 : product.devProductID,
|
||||||
decalID: product.decalId || "0",
|
decalID: product.decalId || "0",
|
||||||
stock: product.stock > 0 ? product.stock : false,
|
stock: (product.stock == -1 || purchases.some(purchase => purchase.productId == product.id && purchase.robloxId == pid)) ? false : product.stock,
|
||||||
onsale: true,
|
onsale: product.stock == -1 || product.stock > 0 ? true : false,
|
||||||
category: product.category,
|
category: product.stock == -1 || product.stock > 0 ? product.category : "Out of Stock",
|
||||||
rating: {
|
rating: {
|
||||||
currentScore: 0,
|
currentScore: 0,
|
||||||
maxScore: 0,
|
maxScore: 0,
|
||||||
|
@ -78,6 +110,7 @@ router.get('/getSession', async (req, res) => {
|
||||||
requestedAt: new Date().toISOString(),
|
requestedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
}, (_, v) => typeof v === 'bigint' ? v.toString() : v)
|
}, (_, v) => typeof v === 'bigint' ? v.toString() : v)
|
||||||
|
|
||||||
return res.status(200).send(respData)
|
return res.status(200).send(respData)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const pool = global.db_pool;
|
const pool = global.db_pool;
|
||||||
|
const client = global.discord_client;
|
||||||
|
|
||||||
|
|
||||||
// Main payment processor
|
// Main payment processor
|
||||||
|
@ -13,8 +14,8 @@ router.post("/external/hub/order/complete", async (req, res) => {
|
||||||
const { robloxID, productID } = req.body;
|
const { robloxID, productID } = req.body;
|
||||||
if (!robloxID || !productID) return res.status(400).json({ status: "400", message: 'Missing Roblox ID or Product ID' });
|
if (!robloxID || !productID) return res.status(400).json({ status: "400", message: 'Missing Roblox ID or Product ID' });
|
||||||
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
const [user] = await pool.query('SELECT * FROM users WHERE robloxId = ?', [robloxID]);
|
||||||
const [product] = await pool.query('SELECT * FROM products WHERE id = ?', [productID]);
|
const [product] = await pool.query('SELECT * FROM products WHERE id = ? AND hubId = ?', [productID, hub.id]);
|
||||||
|
|
||||||
// Check if user and product exists
|
// Check if user and product exists
|
||||||
if (!user || !product) return res.status(404).json({ status: "404", message: 'User or Product not found' });
|
if (!user || !product) return res.status(404).json({ status: "404", message: 'User or Product not found' });
|
||||||
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
const [purchase] = await pool.query('SELECT * FROM purchases WHERE robloxId = ? AND productId = ?', [robloxID, product.id]);
|
||||||
|
@ -24,6 +25,36 @@ router.post("/external/hub/order/complete", async (req, res) => {
|
||||||
|
|
||||||
// Insert purchase into database
|
// Insert purchase into database
|
||||||
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [robloxID, product.id, hub.id]);
|
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [robloxID, product.id, hub.id]);
|
||||||
|
if (product.stock !== -1 && product.stock !== 0) {
|
||||||
|
await pool.query('UPDATE products SET stock = stock - 1 WHERE id = ?', [product.id]);
|
||||||
|
}
|
||||||
|
res.status(200).json({ status: "200", message: 'Purchased product' });
|
||||||
|
|
||||||
|
// Handle logging
|
||||||
|
try {
|
||||||
|
// Assuming you have a function to send a message to the user
|
||||||
|
dscUser = await client.users.fetch(user.discordId);
|
||||||
|
dscUser.send(`You have successfully purchased ${product.name}!\nUse \`/retrive ${product.name}\` in the Discord server to download it!`);
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, user has privacy settings enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hub.logChannel != null) {
|
||||||
|
try {
|
||||||
|
chan = await client.channels.fetch(hub.logChannel);
|
||||||
|
chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `New Purchase`,
|
||||||
|
color: 0x00ff00,
|
||||||
|
description: `**Roblox ID:** ${user.robloxId}\n**Discord User:** <@${user.discordId}>\n**Product:** ${product.name}\n**Type:** Normal`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, channel was deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gift validator
|
// Gift validator
|
||||||
|
@ -48,7 +79,6 @@ router.post("/external/hub/gift/validate", async (req, res) => {
|
||||||
|
|
||||||
// Check if purchase already exists
|
// Check if purchase already exists
|
||||||
if (purchase) return res.status(409).json({ status: "409", message: 'User already owns product', data: {userExists: true, ownsProduct: true} });
|
if (purchase) return res.status(409).json({ status: "409", message: 'User already owns product', data: {userExists: true, ownsProduct: true} });
|
||||||
|
|
||||||
// All good!
|
// All good!
|
||||||
return res.status(200).json({ status: "200", message: 'User does not own product', data: {userExists: true, ownsProduct: false} });
|
return res.status(200).json({ status: "200", message: 'User does not own product', data: {userExists: true, ownsProduct: false} });
|
||||||
});
|
});
|
||||||
|
@ -74,6 +104,36 @@ router.post("/external/hub/gift/complete", async (req, res) => {
|
||||||
|
|
||||||
// Insert purchase into database
|
// Insert purchase into database
|
||||||
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [recipientID, product.id, hub.id]);
|
await pool.query('INSERT INTO purchases (robloxId, productId, hubId) VALUES (?, ?, ?)', [recipientID, product.id, hub.id]);
|
||||||
|
if (product.stock !== -1 && product.stock !== 0) {
|
||||||
|
await pool.query('UPDATE products SET stock = stock - 1 WHERE id = ?', [product.id]);
|
||||||
|
}
|
||||||
|
res.status(200).json({ status: "200", message: 'Gifted product to user' });
|
||||||
|
|
||||||
|
// Handle logging
|
||||||
|
try {
|
||||||
|
// Assuming you have a function to send a message to the user
|
||||||
|
dscUser = await client.users.fetch(user.discordId);
|
||||||
|
dscUser.send(`You have successfully purchased ${product.name}!\nUse \`/retrive ${product.name}\` in the Discord server to download it!`);
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, user has privacy settings enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hub.logChannel != null) {
|
||||||
|
try {
|
||||||
|
chan = await client.channels.fetch(hub.logChannel);
|
||||||
|
chan.send({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `New Purchase`,
|
||||||
|
color: 0x00ff00,
|
||||||
|
description: `**Roblox ID:** ${user.robloxId}\n**Discord User:** <@${user.discordId}>\n**Product:** ${product.name}\n**Type:** Gift`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing, channel was deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
Loading…
Reference in a new issue