cidlookup/index.js
2025-05-28 11:03:24 -06:00

229 lines
8.8 KiB
JavaScript

require("dotenv").config()
const Discord = require("discord.js")
const Client = new Discord.Client({
intents: [
"DirectMessages"
]
})
const {
REST,
Routes
} = require('discord.js');
const rest = new REST({
version: '10'
}).setToken(process.env.TOKEN);
const axios = require("axios");
const jsondb = require("node-json-db")
const db = new jsondb.JsonDB(new jsondb.Config("cache", true, true, "/", true))
db.push
const commands = [
{
name: "cid",
description: "Lookup a CallerID",
contexts: [0, 1, 2],
integration_types: [0, 1],
options: [
{
name: "number",
description: "The number to look up (11 digit US only!)",
type: Discord.ApplicationCommandOptionType.String,
required: true
},
{
name: "show",
description: "Whether to show the response to everyone or not.",
type: Discord.ApplicationCommandOptionType.Boolean,
}
]
},
{
name: "lrn",
description: "Lookup a Local Routing Number",
contexts: [0, 1, 2],
integration_types: [0, 1],
options: [
{
name: "did",
description: "The number to look up (11 digit US only!)",
type: Discord.ApplicationCommandOptionType.String,
required: true
},
{
name: "ani",
description: "The ANI to use for the lookup. i.e the caller's number (for routing info) (11 digit US only!)",
type: Discord.ApplicationCommandOptionType.String,
required: false
},
{
name: "show",
description: "Whether to show the response to everyone or not.",
type: Discord.ApplicationCommandOptionType.Boolean,
required: false
}
]
}
]
Client.on("ready", async () => {
console.log(`Logged in as ${Client.user.displayName}`);
await (async () => {
try {
//Global
console.log(`Registering global commands`);
await rest.put(Routes.applicationCommands(Client.user.id), { body: commands })
} catch (error) {
console.error(error);
}
})();
})
/**
* Converts a T9-style alphanumeric phone number to its numeric equivalent.
* Example: "1610LITENET" => "16105483638"
* @param {string} input
* @returns {string}
*/
function t9ToNumber(input) {
input = String(input).replace(/[^a-zA-Z0-9]/g, '');
const t9 = {
A: '2', B: '2', C: '2',
D: '3', E: '3', F: '3',
G: '4', H: '4', I: '4',
J: '5', K: '5', L: '5',
M: '6', N: '6', O: '6',
P: '7', Q: '7', R: '7', S: '7',
T: '8', U: '8', V: '8',
W: '9', X: '9', Y: '9', Z: '9'
};
let output = String(input).toUpperCase().split('').map(c =>
t9[c] || (/\d/.test(c) ? c : '')
).join('');
return output.length === 11 ? output : null; // Ensure it's 11 digits
}
Client.on("interactionCreate", async (interaction) => {
switch (interaction.type) {
case Discord.InteractionType.ApplicationCommand:
switch (interaction.commandName) {
case "cid":
let show = !interaction.options.getBoolean("show");
if (show == null) show = true;
let number = t9ToNumber(interaction.options.getString("number"))
if (!/^\d{11}$/.test(number)) return interaction.reply({ ephemeral: true, content: "<:invalid:1305271064993071185> That's not a valid number! (11 digit US only)" });
if (await db.exists(`/cache/${number}`)) {
cached = await db.getData(`/cache/${number}`);
} else {
cached = {}
}
if (!cached.cnam || new Date() > new Date(cached?.expires)) {
// Old, get from API
interaction.reply({ ephemeral: show, content: `<:checking:1305271116134092842> Looking up \`${number}\`` }).then(() => {
axios.get(process.env.CNAM_API_URL.replaceAll("%n", number)).then((response) => {
db.push(`/cache/${number}`, {
timestamp: new Date(),
expires: new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000),
cnam: response.data
});
interaction.editReply({ ephemeral: show, content: `<:success:1305271084806963220> CNAM for \`${number}\` is \`${response.data}\`` })
}).catch((err) => {
interaction.editReply({ ephemeral: show, content: `<:server_error:1305271074551758868> There was a server error when trying to get that!` })
})
});
} else {
// Spit out cached version
interaction.reply({ ephemeral: show, content: `<:cached:1305276187966443612> CNAM for \`${number}\` is \`${cached.cnam}\`\n-# This was cached <t:${Math.floor(cached.timestamp / 1000)}:f>` })
}
break;
case "lrn":
let showLrn = !interaction.options.getBoolean("show");
if (showLrn == null) showLrn = true;
let did = t9ToNumber(interaction.options.getString("did"))
if (!/^\d{11}$/.test(did)) return interaction.reply({ ephemeral: true, content: "<:invalid:1305271064993071185> That's not a valid number! (11 digit US only)" });
let ani = t9ToNumber(interaction.options.getString("ani")) || null;
if (ani && !/^\d{11}$/.test(ani)) {
return interaction.reply({ ephemeral: true, content: "<:invalid:1305271064993071185> That's not a valid ANI! (11 digit US only)" });
}
if (await db.exists(`/lrncache/${did}`)) {
cached = await db.getData(`/lrncache/${did}`);
} else {
cached = {}
}
if (!cached.lrn || new Date() > new Date(cached?.expires) || ani) {
// Old, get from API
interaction.reply({ ephemeral: showLrn, content: `<:checking:1305271116134092842> Looking up \`${did}\`` }).then(() => {
axios.get(process.env.LRN_API_URL.replaceAll("%did", did).replaceAll("%ani", ani || "")).then((response) => {
let lrn = response.data;
/*lrn data:
{"tn":"15809197945","lrn":"15809197945","ocn":"5813","lata":"536","city":"LAWTON","ratecenter":"LAWTON","province":"OK","jurisdiction":"INDETERMINATE","local":"INDETERMINATE","lec":"CELLCO PARTNERSHIP DBA VERIZON WIRELESS - OK","lectype":"WIRELESS","spid":"6006","activation":"1241582400"}
*/
db.push(`/lrncache/${did}`, {
timestamp: new Date(),
expires: new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000), // 7 days
lrn
});
interaction.editReply({
content: `<:success:1305271084806963220> LRN for \`${did}\` is \`${lrn.lrn}\``,
ephemeral: showLrn, embeds: [{
title: `LRN for ${did}`,
fields: [
{ name: "LRN", value: `\`${lrn.lrn}\`` || "`NULL`", inline: true },
{ name: "OCN", value: `\`${lrn.ocn}\`` || "`NULL`", inline: true },
{ name: "LATA", value: `\`${lrn.lata}\`` || "`NULL`", inline: true },
{ name: "City", value: `\`${lrn.city}\`` || "`NULL`", inline: true },
{ name: "Rate Center", value: `\`${lrn.ratecenter}\`` || "`NULL`", inline: true },
{ name: "Province", value: `\`${lrn.province}\`` || "`NULL`", inline: true },
{ name: "Jurisdiction", value: `\`${lrn.jurisdiction}\`` || "`NULL`", inline: true },
{ name: "Local", value: `\`${lrn.local}\`` || "`NULL`", inline: true },
{ name: "LEC", value: `\`${lrn.lec}\`` || "`NULL`", inline: true },
{ name: "LEC Type", value: `\`${lrn.lectype}\`` || "`NULL`", inline: true },
{ name: "SPID", value: `\`${lrn.spid}\`` || "`NULL`", inline: true },
{ name: "Activation Date", value: `<t:${Math.floor(lrn.activation)}>` }
],
footer: { text: "This data is provided by the LRN API" },
color: 0x00FF00
}]
})
}).catch((err) => {
console.error(err);
interaction.editReply({ ephemeral: showLrn, content: `<:server_error:1305271074551758868> There was a server error when trying to get that!` })
})
});
} else {
// Spit out cached version
let lrn = cached.lrn;
interaction.reply({
content: `<:cached:1305276187966443612> LRN for \`${did}\``,
ephemeral: showLrn, embeds: [{
title: `LRN for ${did}`,
fields: [
{ name: "LRN", value: `\`${lrn.lrn}\`` || "`NULL`", inline: true },
{ name: "OCN", value: `\`${lrn.ocn}\`` || "`NULL`", inline: true },
{ name: "LATA", value: `\`${lrn.lata}\`` || "`NULL`", inline: true },
{ name: "City", value: `\`${lrn.city}\`` || "`NULL`", inline: true },
{ name: "Rate Center", value: `\`${lrn.ratecenter}\`` || "`NULL`", inline: true },
{ name: "Province", value: `\`${lrn.province}\`` || "`NULL`", inline: true },
{ name: "Jurisdiction", value: `\`${lrn.jurisdiction}\`` || "`NULL`", inline: true },
{ name: "Local", value: `\`${lrn.local}\`` || "`NULL`", inline: true },
{ name: "LEC", value: `\`${lrn.lec}\`` || "`NULL`", inline: true },
{ name: "LEC Type", value: `\`${lrn.lectype}\`` || "`NULL`", inline: true },
{ name: "SPID", value: `\`${lrn.spid}\`` || "`NULL`", inline: true },
{ name: "Activation Date", value: `<t:${Math.floor(lrn.activation)}>` }
],
footer: { text: "This is cached data. Cached" },
timestamp: new Date(cached.timestamp).toISOString(),
color: 0x00FF00
}]
})
}
break;
}
}
});
Client.login(process.env.TOKEN)