Working concept part 2
This commit is contained in:
parent
21b9041351
commit
70978819ee
193
bvs.js
Normal file
193
bvs.js
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
async function getPremiumDIDs() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`https://portal.bulkvs.com/api/v1.0/tnRecord?Status=Active&Trunk%20Group=${process.env.TRUNK_GROUP}`, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error fetching DIDs: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
// data.forEach(record => {
|
||||
// console.log(record.ReferenceID);
|
||||
// });
|
||||
// data is array of objects. If object.ReferenceID.split(";;")[0] is premDID, then [1] is the user id, and anything after is a note
|
||||
const dids = data.filter(record => record.ReferenceID && record.ReferenceID.startsWith(";;")).map(record => {
|
||||
// Get all flags (just keep looking at the next split item until its not a valid flag, after that, it's all a custom note, most likely to be used fo)
|
||||
let validFlags = ["bypassNitroReq"];
|
||||
let flags = [];
|
||||
let note = "";
|
||||
let splitReference = record.ReferenceID.split(";;");
|
||||
for (let i = 1; i < splitReference.length; i++) {
|
||||
if (validFlags.includes(splitReference[i])) {
|
||||
flags.push(splitReference[i]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
did: record.TN,
|
||||
userId: record.ReferenceID.split(";;")[1],
|
||||
flags: flags,
|
||||
rawRef: record.ReferenceID
|
||||
}
|
||||
});
|
||||
resolve(dids);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching DIDs:", error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getAllDIDs() {
|
||||
// Use the same BVS API endpoint, but just get all the results, not just premium ones.
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`https://portal.bulkvs.com/api/v1.0/tnRecord?Status=Active&Trunk%20Group=${process.env.TRUNK_GROUP}`, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error fetching DIDs: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
const dids = data.map(record => record.TN);
|
||||
resolve(dids);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching DIDs:", error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getAccountInfo() {
|
||||
const res = await fetch("https://portal.bulkvs.com/api/v1.0/accountDetail", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`BVS_TOKEN is invalid: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function searchDIDs(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
|
||||
// Validate query w regex
|
||||
if (!/^\d{3}$/.test(query) && !/^\d{6}$/.test(query)) {
|
||||
throw new Error("Query must be either NPA (3 digits) or NPANXX (6 digits)");
|
||||
}
|
||||
|
||||
const res = await fetch(`https://portal.bulkvs.com/api/v1.0/orderTn?Npa=${query.slice(0, 3)}&Nxx=${query.slice(3, 6)}&Lca=true&Limit=100`, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error searching DIDs: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function searchPurchasableDIDs(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");
|
||||
// console.log(purchasableDIDs)
|
||||
// Shuffle the purchasableDIDs array, then take the first 6
|
||||
let shuffled = [...purchasableDIDs];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
shuffled = shuffled.sort(() => 0.5 - Math.random());
|
||||
}
|
||||
// shuffle more
|
||||
|
||||
return shuffled.slice(0, 6);
|
||||
}
|
||||
|
||||
async function purchaseDID(did, userId) {
|
||||
// POST /orderTn with body:
|
||||
/*
|
||||
{
|
||||
"TN": "did to purchase",
|
||||
"Lidb": "LITENET",
|
||||
"Portout Pin": `${random 12 digit number}`,
|
||||
"ReferenceID": `;;${userId}`,
|
||||
"Trunk Group": `${process.env.TRUNK_GROUP}`,
|
||||
"Sms": false,
|
||||
"Mms": false,
|
||||
"Webhook": "Default"
|
||||
}
|
||||
*/
|
||||
let portoutPin = "";
|
||||
const digits = "0123456789";
|
||||
for (let i = 0; i < 12; i++) {
|
||||
portoutPin += digits[Math.floor(Math.random() * digits.length)];
|
||||
}
|
||||
const res = await fetch("https://portal.bulkvs.com/api/v1.0/orderTn", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"TN": did,
|
||||
"Lidb": "LITENET",
|
||||
"Portout Pin": portoutPin,
|
||||
"ReferenceID": `;;${userId}`,
|
||||
"Trunk Group": `${process.env.TRUNK_GROUP}`,
|
||||
"Sms": false,
|
||||
"Mms": false,
|
||||
"Webhook": "Default"
|
||||
})
|
||||
});
|
||||
// If response is 404, check body.json().Description, return the json body.
|
||||
if (res.status === 404) {
|
||||
const errorData = await res.json();
|
||||
throw new Error(`Error purchasing DID: ${res.status} ${res.statusText} - ${errorData.Description}`);
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error purchasing DID: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function formatPhoneNumber(input) {
|
||||
if (/^\d{10}$/.test(input)) {
|
||||
input = `1${input}`;
|
||||
}
|
||||
// Regex to make sure it's a valid 11 digit NA phone number.
|
||||
if (!/^\d{11}$/.test(input)) {
|
||||
throw new Error("Phone number must be 10 or 11 digits, including country code (1 for US/Canada).");
|
||||
}
|
||||
// Format to human readable format: +1 (310) 906-1234
|
||||
return `+${input[0]} (${input.slice(1, 4)}) ${input.slice(4, 7)}-${input.slice(7)}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllDIDs,
|
||||
getAccountInfo,
|
||||
getPremiumDIDs,
|
||||
searchDIDs,
|
||||
searchPurchasableDIDs,
|
||||
purchaseDID,
|
||||
formatPhoneNumber
|
||||
}
|
||||
40
bvsTest.js
Normal file
40
bvsTest.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
require("dotenv").config({quiet: true});
|
||||
const bvs = require("./bvs");
|
||||
|
||||
// bvs.getAccountInfo().then(accountInfo => {
|
||||
// console.log(`Validated BulkVS Token. Account Contact is ${accountInfo["Main Contact"].Name}`)
|
||||
// }).catch(error => {
|
||||
// console.error("Error validating BVS_TOKEN:", error);
|
||||
// process.exit(1);
|
||||
// })
|
||||
|
||||
// bvs.getAllDIDs().then(dids => {
|
||||
// console.log(`Fetched ${dids.length} DIDs from BulkVS.`);
|
||||
// console.log(dids);
|
||||
// }).catch(error => {
|
||||
// console.error("Error fetching DIDs:", error);
|
||||
// });
|
||||
|
||||
bvs.getPremiumDIDs().then(dids => {
|
||||
console.log(`Fetched ${dids.length} premium DIDs from BulkVS.`);
|
||||
console.log(dids);
|
||||
}).catch(error => {
|
||||
console.error("Error fetching premium DIDs:", error);
|
||||
});
|
||||
|
||||
bvs.searchPurchasableDIDs("910").then(dids => {
|
||||
console.log(`Fetched ${dids.length} DIDs from BulkVS with user ID 610548.`);
|
||||
console.log(dids.map(did => did.TN))
|
||||
// console.log(dids)
|
||||
// Test purchase the first one, if there is one
|
||||
if (dids.length > 0) {
|
||||
// bvs.purchaseDID(dids[0].TN, 8949849456165).then((data) => {
|
||||
// console.log(`Successfully purchased DID ${dids[0].TN}`);
|
||||
// console.log(data)
|
||||
// }).catch(error => {
|
||||
// console.error(`Error purchasing DID ${dids[0].TN}:`, error);
|
||||
// });
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("Error fetching DIDs with user ID 610548:", error);
|
||||
});
|
||||
42
commands.js
42
commands.js
|
|
@ -2,15 +2,37 @@ const Discord = require('discord.js');
|
|||
|
||||
module.exports = [
|
||||
{
|
||||
name: 'checkmember',
|
||||
description: 'Check if a member is boosting the guild.',
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
description: 'The user to check. If not provided, checks the user who ran the command.',
|
||||
type: Discord.ApplicationCommandOptionType.User, // USER type
|
||||
required: false,
|
||||
}
|
||||
]
|
||||
name: 'mynumber',
|
||||
description: 'Get your associated DID, if you have one.',
|
||||
},
|
||||
{
|
||||
name: 'searchnumbers',
|
||||
description: 'Search for DIDs, and purchase one if you have an active server boost.',
|
||||
options: [
|
||||
{
|
||||
name: 'area_code',
|
||||
description: 'The area code to search for (e.g. 610).',
|
||||
type: Discord.ApplicationCommandOptionType.Number,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'office_code',
|
||||
description: 'The office code to search for (e.g. 548).',
|
||||
type: Discord.ApplicationCommandOptionType.Number,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'getnumber',
|
||||
description: 'Purchase a DID from the latest search results of /searchnumbers.',
|
||||
options: [
|
||||
{
|
||||
name: 'choice',
|
||||
description: 'The choice of DID to purchase from the latest search results.',
|
||||
type: Discord.ApplicationCommandOptionType.Number,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
67
index.js
67
index.js
|
|
@ -7,49 +7,12 @@ const client = new Discord.Client({
|
|||
]
|
||||
})
|
||||
|
||||
const bvs = require("./bvs");
|
||||
|
||||
|
||||
const { REST, Routes } = require("discord.js");
|
||||
const rest = new REST({ version: "10" }).setToken(process.env.DISCORD_TOKEN);
|
||||
|
||||
async function getAllDIDs() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`https://portal.bulkvs.com/api/v1.0/tnRecord?Status=Active&Trunk%20Group=${process.env.TRUNK_GROUP}`, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Error fetching DIDs: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
data.forEach(record => {
|
||||
console.log(record.ReferenceID);
|
||||
});
|
||||
// data is array of objects. If object.ReferenceID.split(";;")[0] is premDID, then [1] is the user id, and anything after is a note
|
||||
const dids = data.filter(record => record.ReferenceID && record.ReferenceID.startsWith(";;")).map(record => {
|
||||
// Get all flags (just keep looking at the next split item until its not a valid flag, after that, it's all a custom note, most likely to be used fo)
|
||||
return {
|
||||
did: record.TN,
|
||||
userId: record.ReferenceID.split(";;")[1],
|
||||
}
|
||||
});
|
||||
resolve(dids);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching DIDs:", error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getAllDIDs().then(dids => {
|
||||
console.log(`Fetched ${dids.length} DIDs from BulkVS.`);
|
||||
console.log(dids)
|
||||
}).catch(error => {
|
||||
console.error("Error fetching DIDs:", error);
|
||||
});
|
||||
|
||||
client.on("clientReady", () => {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
|
|
@ -69,28 +32,6 @@ client.on("clientReady", () => {
|
|||
rest.put(Routes.applicationGuildCommands(client.user.id, process.env.HOME_GUILD), { body: commands })
|
||||
.then(() => console.log("Successfully registered application commands."))
|
||||
.catch(console.error);
|
||||
|
||||
// Check that we have a valid BVS_TOKEN with https://portal.bulkvs.com/api/v1.0/accountInfo (Bearer token in Authorization header)
|
||||
if (!process.env.BVS_TOKEN) {
|
||||
console.error("BVS_TOKEN environment variable is not set. Please set it to a valid token from https://portal.bulkvs.com/api/v1.0/accountInfo (Bearer token in Authorization header).");
|
||||
process.exit(1);
|
||||
}
|
||||
fetch("https://portal.bulkvs.com/api/v1.0/accountDetail", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${process.env.BVS_TOKEN}`,
|
||||
}
|
||||
}).then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`BVS_TOKEN is invalid: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
// Get raw text
|
||||
return res.json();
|
||||
}).then(data => {
|
||||
console.log(`Validated BulkVS Token. Account Contact is ${data["Main Contact"].Name}`)
|
||||
}).catch(error => {
|
||||
console.error("Error validating BVS_TOKEN:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async interaction => {
|
||||
|
|
@ -98,7 +39,7 @@ client.on('interactionCreate', async interaction => {
|
|||
if (interaction.isChatInputCommand()) {
|
||||
try {
|
||||
const handler = require(`./interactions/chatCommand/${interaction.commandName}.js`);
|
||||
await handler(interaction, client);
|
||||
await handler(interaction, client, bvs);
|
||||
} catch (error) {
|
||||
console.error(`Error handling interaction ${interaction.id}:`, error);
|
||||
}
|
||||
|
|
|
|||
21
interactions/chatCommand/getnumber.js
Normal file
21
interactions/chatCommand/getnumber.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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 });
|
||||
}
|
||||
|
||||
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];
|
||||
return interaction.reply({ content: `Attempting to purchase DID \`${bvs.formatPhoneNumber(didToPurchase)}\`...`, ephemeral: true }).then(() => {
|
||||
// wait, we are in dev phase, dont buy lol
|
||||
});
|
||||
}
|
||||
23
interactions/chatCommand/mynumber.js
Normal file
23
interactions/chatCommand/mynumber.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
module.exports = (interaction, client, bvs) => {
|
||||
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;
|
||||
}
|
||||
bvs.getPremiumDIDs().then(dids => {
|
||||
const userDIDs = dids.filter(did => did.userId === interaction.user.id);
|
||||
if (userDIDs.length === 0) {
|
||||
if (member.premiumSince) {
|
||||
interaction.reply({ content: `You don't have a DID associated with your account. But with your currently active server boost, you can get one with \`/getnumber\`!`, ephemeral: true });
|
||||
} else {
|
||||
interaction.reply({ content: `You don't have a DID associated with your account. Boost the server to request one!`, 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 });
|
||||
});
|
||||
}
|
||||
25
interactions/chatCommand/searchnumbers.js
Normal file
25
interactions/chatCommand/searchnumbers.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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 });
|
||||
}
|
||||
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 <choice>\` 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 });
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue