diff --git a/bvs.js b/bvs.js index 685e11b..f6009d7 100644 --- a/bvs.js +++ b/bvs.js @@ -83,6 +83,7 @@ async function getAccountInfo() { } async function searchDIDs(query) { + // console.log(`q is ${query}`) // Query will either be NPA, or NPANXX. Validate with regex, then use the API to find some. // API Example: GET https://portal.bulkvs.com/api/v1.0/orderTn?Npa=310&Nxx=906&Lca=true&Limit=100 query = query.replace(/\D/g, ""); // Remove all non-digit characters @@ -105,13 +106,14 @@ async function searchDIDs(query) { } async function searchPurchasableDIDs(query) { + // console.log(`Searching purchasable DIDs with query ${query}`); // Use searchDIDs, then filter down to 6 random from the results where the Nrc value == "0.05" and Mrc == "0.06" const dids = await searchDIDs(query).catch(error => { console.error("Error searching DIDs:", error); return []; }); // console.log(dids) - const purchasableDIDs = dids.filter(did => did.Nrc <= "0.50" && did.Mrc === "0.06"); + const purchasableDIDs = dids.filter(did => did.Nrc <= "0.50" && did.Mrc <= "0.10"); // console.log(purchasableDIDs) // Shuffle the purchasableDIDs array, then take the first 6 let shuffled = [...purchasableDIDs]; @@ -182,6 +184,22 @@ function formatPhoneNumber(input) { return `+${input[0]} (${input.slice(1, 4)}) ${input.slice(4, 7)}-${input.slice(7)}`; } +function deleteDID(did) { + // API to delete msg, DELETE https://portal.bulkvs.com/api/v1.0/tnRecord?Number=did + return fetch(`https://portal.bulkvs.com/api/v1.0/tnRecord?Number=${did}`, { + method: "DELETE", + headers: { + "Authorization": `Bearer ${process.env.BVS_TOKEN}`, + } + }) + .then(res => { + if (!res.ok) { + throw new Error(`Error deleting DID: ${res.status} ${res.statusText}`); + } + return res.json(); + }); +} + module.exports = { getAllDIDs, getAccountInfo, diff --git a/interactions/chatCommand/getnumber.js b/interactions/chatCommand/getnumber.js index 89dfaea..6ff67bd 100644 --- a/interactions/chatCommand/getnumber.js +++ b/interactions/chatCommand/getnumber.js @@ -1,21 +1,60 @@ module.exports = (interaction, client, bvs) => { - const choice = interaction.options.getNumber('choice'); - if (!choice) { - return interaction.reply({ content: `You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); + + const member = interaction.guild.members.cache.get(interaction.user.id); + + if (!member) { + interaction.reply({ content: `Could not find member with ID ${user.id}.`, ephemeral: true }); + return; } - if (!global.tempPurchasableDIDs || !global.tempPurchasableDIDs[interaction.user.id]) { - return interaction.reply({ content: `You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); + if (!member.premiumSince) { + return interaction.reply({ content: `You must be boosting the server to use this command.`, ephemeral: true }); } - const dids = global.tempPurchasableDIDs[interaction.user.id]; - const index = parseInt(choice) - 1; - if (isNaN(index) || index < 0 || index >= dids.length) { - return interaction.reply({ content: `Invalid choice. You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); - } + // Check that the member doesn't already have a number. + bvs.getPremiumDIDs().then(dids => { + const userDIDs = dids.filter(did => did.userId === interaction.user.id); + if (userDIDs.length === 0) { - const didToPurchase = dids[index]; - return interaction.reply({ content: `Attempting to purchase DID \`${bvs.formatPhoneNumber(didToPurchase)}\`...`, ephemeral: true }).then(() => { - // wait, we are in dev phase, dont buy lol + const choice = interaction.options.getNumber('choice'); + if (!choice) { + return interaction.reply({ content: `You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); + } + + if (global.purchaseConfirmations && global.purchaseConfirmations[interaction.user.id]) { + if (choice === 1) { + // proceed with purchase + const didToPurchase = global.purchaseConfirmations[interaction.user.id]; + delete global.purchaseConfirmations[interaction.user.id]; + delete global.tempPurchasableDIDs[interaction.user.id]; + return interaction.reply({ content: `Purchasing DID \`${bvs.formatPhoneNumber(didToPurchase)}\`...`, ephemeral: true }) + // todo: impl actually buying did + } else { + delete global.purchaseConfirmations[interaction.user.id]; + return interaction.reply({ content: `Cancelled. You can either choose a different DID from the latest run of \`/searchnumbers\` or start a new search by running \`/searchnumbers\` again.`, ephemeral: true }); + } + } + + if (!global.tempPurchasableDIDs || !global.tempPurchasableDIDs[interaction.user.id]) { + return interaction.reply({ content: `You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); + } + + const dids = global.tempPurchasableDIDs[interaction.user.id]; + const index = parseInt(choice) - 1; + if (isNaN(index) || index < 0 || index >= dids.length) { + return interaction.reply({ content: `Invalid choice. You must provide a choice from the latest run of \`/searchnumbers\`. Use \`/searchnumbers\` to see available DIDs and their corresponding choices.`, ephemeral: true }); + } + + const didToPurchase = dids[index]; + // Store the choice in a global temp variable with the user id so the user can confirm their purchase by running the command again with the same choice. This is a bit janky but it works for now. + global.purchaseConfirmations = global.purchaseConfirmations || {}; + global.purchaseConfirmations[interaction.user.id] = didToPurchase; + return interaction.reply({ content: `You have selected DID \`${bvs.formatPhoneNumber(didToPurchase)}\` for purchase. If you want to proceed with purchasing this DID, run \`/getnumber\` with choice \`1\`. If you want to cancel, run the command again with a different choice or run \`/searchnumbers\` again to start a new search.`, ephemeral: true }); + } else { + interaction.reply({ content: `Your DID is \`${bvs.formatPhoneNumber(userDIDs[0].did)}\``, ephemeral: true }); + } + }).catch(error => { + console.error("Error fetching premium DIDs:", error); + interaction.reply({ content: `There was an error fetching your DID. Please try again later.`, ephemeral: true }); }); } \ No newline at end of file diff --git a/interactions/chatCommand/searchnumbers.js b/interactions/chatCommand/searchnumbers.js index 2c9949e..7396f3d 100644 --- a/interactions/chatCommand/searchnumbers.js +++ b/interactions/chatCommand/searchnumbers.js @@ -1,25 +1,55 @@ module.exports = (interaction, client, bvs) => { - const areaCode = interaction.options.getNumber('area_code'); - const officeCode = interaction.options.getNumber('office_code'); - // Validate input - if (!/^\d{3}$/.test(areaCode)) { - return interaction.reply({ content: `Area code must be 3 digits.`, ephemeral: true }); + const member = interaction.guild.members.cache.get(interaction.user.id); + + if (!member) { + interaction.reply({ content: `Could not find member with ID ${user.id}.`, ephemeral: true }); + return; } - if (officeCode && !/^\d{3}$/.test(officeCode)) { - return interaction.reply({ content: `Office code must be 3 digits.`, ephemeral: true }); + + if (!member.premiumSince) { + return interaction.reply({ content: `You must be boosting the server to use this command.`, ephemeral: true }); } - bvs.searchPurchasableDIDs(areaCode + (officeCode || "")).then(dids => { - if (dids.length === 0) { - return interaction.reply({ content: `No results for search query. Try again.`, ephemeral: true }); + + bvs.getPremiumDIDs().then(dids => { + const userDIDs = dids.filter(did => did.userId === interaction.user.id); + if (userDIDs.length === 0) { + const areaCode = interaction.options.getNumber('area_code'); + const officeCode = interaction.options.getNumber('office_code'); + + // Clear purchase confirmation and temp purchasable DIDs for the user, since they're starting a new search. This prevents confusion where a user searches for DIDs, then tries to purchase one, but the purchasable DIDs from the previous search are still stored and they accidentally purchase a DID from the previous search results instead of the new ones. + if (global.purchaseConfirmations) { + delete global.purchaseConfirmations[interaction.user.id]; + } + if (global.tempPurchasableDIDs) { + delete global.tempPurchasableDIDs[interaction.user.id]; + } + + // Validate input + if (!/^\d{3}$/.test(areaCode)) { + return interaction.reply({ content: `Area code must be 3 digits.`, ephemeral: true }); + } + if (officeCode && !/^\d{3}$/.test(officeCode)) { + return interaction.reply({ content: `Office code must be 3 digits.`, ephemeral: true }); + } + bvs.searchPurchasableDIDs(`${areaCode}${officeCode || ""}`).then(dids => { + if (dids.length === 0) { + return interaction.reply({ content: `No results for search query. Try again.`, ephemeral: true }); + } + // console.log(dids) + interaction.reply({ content: `Found the following DIDs\n\`\`\`\n${dids.map((did, index) => `${index + 1}. ${bvs.formatPhoneNumber(did.TN)}`).join("\n")}\n\`\`\`\nUse \`/getnumber \` to purchase one of these DIDs! (Choice is one of the numbers listed above by index, not phone number)`, ephemeral: true }); + // Store the DIDs in a global temp variable with the user id so the user can purchase by 1, 2, 3, etc. in the /getnumber command. This is a bit janky but it works for now. + global.tempPurchasableDIDs = global.tempPurchasableDIDs || {}; + global.tempPurchasableDIDs[interaction.user.id] = dids.map(did => did.TN); + // console.log(global.tempPurchasableDIDs) + }).catch(error => { + console.error("Error searching DIDs:", error); + interaction.reply({ content: `There was an error searching for DIDs. Please try again later.`, ephemeral: true }); + }); + } else { + interaction.reply({ content: `Your DID is \`${bvs.formatPhoneNumber(userDIDs[0].did)}\``, ephemeral: true }); } - console.log(dids) - interaction.reply({ content: `Found the following DIDs\n\`\`\`\n${dids.map((did, index) => `${index + 1}. ${bvs.formatPhoneNumber(did.TN)}`).join("\n")}\n\`\`\`\nUse \`/getnumber \` to purchase one of these DIDs! (Choice is one of the numbers listed above by index, not phone number)`, ephemeral: true }); - // Store the DIDs in a global temp variable with the user id so the user can purchase by 1, 2, 3, etc. in the /getnumber command. This is a bit janky but it works for now. - global.tempPurchasableDIDs = global.tempPurchasableDIDs || {}; - global.tempPurchasableDIDs[interaction.user.id] = dids.map(did => did.TN); - console.log(global.tempPurchasableDIDs) }).catch(error => { - console.error("Error searching DIDs:", error); - interaction.reply({ content: `There was an error searching for DIDs. Please try again later.`, ephemeral: true }); + console.error("Error fetching premium DIDs:", error); + interaction.reply({ content: `There was an error fetching your DID. Please try again later.`, ephemeral: true }); }); } \ No newline at end of file