mirror of
https://github.com/HirziDevs/PteroStats
synced 2026-08-22 07:06:21 +00:00
feat: installer
This commit is contained in:
+2
-1
@@ -1,4 +1,5 @@
|
||||
node_modules/
|
||||
config-dev.yml
|
||||
package-lock.json
|
||||
cache.json
|
||||
cache.json
|
||||
.env
|
||||
+5
-9
@@ -1,8 +1,8 @@
|
||||
# PteroStats config
|
||||
# If you need help, join our discord server here: https://discord.gg/zv6maQRah3
|
||||
version: 4 # Do not change this unless you know what you are doing!
|
||||
|
||||
# Bot Configuration
|
||||
token: "Put Bot Token Here"
|
||||
presence:
|
||||
enable: true
|
||||
text: "Hosting Panel"
|
||||
@@ -10,15 +10,9 @@ presence:
|
||||
status: "online" # can be 'online', 'idle', 'dnd', or 'invisible'
|
||||
|
||||
# Discord Channel and Refresh Time Configuration
|
||||
channel: "Put Channel ID Here"
|
||||
refresh: 10 # How much time the bot will refresh the stats
|
||||
timeout: 5 # How much time to wait for a node to respond to the bot (if you change this, it will add more time to refresh the stats)
|
||||
|
||||
# Panel Configuration
|
||||
panel:
|
||||
url: "Put Panel URL Here"
|
||||
key: "Put Panel APIKEY Here"
|
||||
|
||||
# Message and Embed Configuration
|
||||
# set the option as '' if you want to disable it
|
||||
message:
|
||||
@@ -28,10 +22,10 @@ message:
|
||||
embed:
|
||||
title_nodes: "Nodes Stats"
|
||||
title_panel: "Panel Stats"
|
||||
author: "PteroStats"
|
||||
author: "Hosting Panel"
|
||||
color: "5865F2"
|
||||
description: "Next update {{time}}" # You can use {{time}} to make "in X seconds" time format
|
||||
footer: "By Hirzi#8701"
|
||||
footer: "By @HirziDevs"
|
||||
timestamp: true
|
||||
thumbnail: ""
|
||||
image: ""
|
||||
@@ -42,6 +36,8 @@ button:
|
||||
row1:
|
||||
- label: "Panel"
|
||||
url: "https://panel.example.com"
|
||||
# - label: "Dashboard"
|
||||
# url: "https://dash.example.com"
|
||||
|
||||
# Status Message Configuration
|
||||
# How to use custom emoji: https://github.com/HirziDevs/PteroStats#using-custom-emoji
|
||||
|
||||
+10
-1
@@ -3,6 +3,8 @@ const fs = require("node:fs");
|
||||
const yaml = require("js-yaml");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Loading configuration..."))
|
||||
|
||||
let config = yaml.load(fs.readFileSync("./config.yml", "utf8"));
|
||||
if (fs.existsSync("config-dev.yml")) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Using development configuration..."))
|
||||
@@ -10,11 +12,18 @@ if (fs.existsSync("config-dev.yml")) {
|
||||
}
|
||||
|
||||
try {
|
||||
const testURL = new URL(config.panel.url);
|
||||
const testURL = new URL(process.env?.PanelURL);
|
||||
if (!testURL.protocol.startsWith("http")) throw new Error();
|
||||
} catch {
|
||||
console.error('Config Error | Invalid URL Format! Example Correct URL: "https://panel.example.com"');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
if (config.version !== 4) {
|
||||
console.error('Config Error | Invalid config version! The config has been updated, please get the new config from https://github.com/HirziDevs/PteroStats/blob/dev/config.yml');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Configuration loaded"))
|
||||
|
||||
module.exports = config
|
||||
@@ -12,7 +12,7 @@ module.exports = function convertUnits(value1, value2, unit) {
|
||||
return `${!percentage ? 0 : percentage}%`;
|
||||
} else if (units.includes(unit)) {
|
||||
const formatValue = (value) => Number.isInteger(value) ? value.toLocaleString() : value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
|
||||
|
||||
return `${formatValue(value1 * conversionFactors[unit])} ${unit}/${(value2 === 0 || value2 < 1) ? "Unlimited" : formatValue(value2 * conversionFactors[unit]) + " " + unit}`;
|
||||
} else {
|
||||
return `${value1.toLocaleString()} MB/${value2.toLocaleString()} MB`;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const config = require("./config.js");
|
||||
|
||||
module.exports = async function getNodeConfiguration(id) {
|
||||
return fetch(`${new URL(config.panel.url).origin}/api/application/nodes/${id}/configuration`, {
|
||||
return fetch(`${new URL(process.env?.PanelURL).origin}/api/application/nodes/${id}/configuration`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${config.panel.key}`
|
||||
"Authorization": `Bearer ${process.env?.PanelKEY}`
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
@@ -3,11 +3,11 @@ const config = require("./config.js");
|
||||
const axios = require("axios");
|
||||
|
||||
module.exports = async function getAllNodes() {
|
||||
return axios(`${new URL(config.panel.url).origin}/api/application/nodes?include=servers,location,allocations`, {
|
||||
return axios(`${new URL(process.env?.PanelURL).origin}/api/application/nodes?include=servers,location,allocations`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${config.panel.key}`
|
||||
"Authorization": `Bearer ${process.env?.PanelKEY}`
|
||||
},
|
||||
})
|
||||
.then((res) => res.data.data.filter((node) => !config.nodes_settings.blacklist.includes(node.attributes.id)))
|
||||
|
||||
@@ -2,12 +2,12 @@ const config = require("./config.js");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
module.exports = async function getServers() {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Getting panel servers..."))
|
||||
return fetch(`${new URL(config.panel.url).origin}/api/application/servers`, {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Retrieving panel servers..."))
|
||||
return fetch(`${new URL(process.env?.PanelURL).origin}/api/application/servers`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${config.panel.key}`
|
||||
"Authorization": `Bearer ${process.env?.PanelKEY}`
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
@@ -9,18 +9,18 @@ const promiseTimeout = require("./promiseTimeout.js");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
module.exports = async function getStats() {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Getting panel nodes..."))
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Retrieving panel nodes..."))
|
||||
const nodesStats = await getNodesDetails();
|
||||
if (!nodesStats) throw new Error("Failed to get nodes attributes");
|
||||
|
||||
const statusPromises = nodesStats.slice(0, config.nodes_settings.limit).map(async (node) => {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Getting "${node.attributes.name}" configuration...`))
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Fetching "${node.attributes.name}" configuration...`))
|
||||
const nodeConfig = await getNodeConfiguration(node.attributes.id);
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Getting "${node.attributes.name}" wings status...`))
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Checking "${node.attributes.name}" wings status...`))
|
||||
const nodeStatus = await promiseTimeout(getWingsStatus(node, nodeConfig.token), config.timeout * 1000);
|
||||
|
||||
if (!nodeStatus)
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright(`Node "${node.attributes.name}" is offline`))
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright(`Node "${node.attributes.name}" is currently offline`))
|
||||
return {
|
||||
attributes: {
|
||||
name: node.attributes.name,
|
||||
|
||||
@@ -2,12 +2,12 @@ const config = require("./config.js");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
module.exports = async function getUsers() {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Getting panel users..."))
|
||||
return fetch(`${new URL(config.panel.url).origin}/api/application/users`, {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Retrieving panel users..."))
|
||||
return fetch(`${new URL(process.env?.PanelURL).origin}/api/application/users`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${config.panel.key}`
|
||||
"Authorization": `Bearer ${process.env?.PanelKEY}`
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
const readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
const fs = require("fs")
|
||||
const cliColor = require("cli-color")
|
||||
|
||||
const questions = [
|
||||
"Please enter your panel name: ",
|
||||
"Please enter your panel URL: ",
|
||||
"Please enter your panel API key: ",
|
||||
"Please enter your bot token: ",
|
||||
"Please enter your channel ID: "
|
||||
];
|
||||
|
||||
const answers = [];
|
||||
|
||||
const isValidURL = (url) => {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function Installer() {
|
||||
console.log("Welcome! Please fill this question to start PteroStats.");
|
||||
|
||||
const askQuestion = (index) => {
|
||||
if (index < questions.length) {
|
||||
console.log(questions[index]);
|
||||
|
||||
readline.question('> ', answer => {
|
||||
let isValid = true;
|
||||
|
||||
if (index === 1 && !isValidURL(answer)) {
|
||||
console.log(cliColor.redBright("Invalid URL. Please enter a valid URL."));
|
||||
isValid = false;
|
||||
} else if (index === 2 && !/^(plcn_|ptlc_|peli_|ptla_)/.test(answer)) {
|
||||
console.log(cliColor.redBright("Invalid API key. It must start with 'plcn_' or 'ptlc_'."));
|
||||
isValid = false;
|
||||
} else if (index === 4 && !/^\d+$/.test(answer)) {
|
||||
console.log(cliColor.redBright("Invalid Channel ID. It must be a number."));
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (index === 2 && /^(peli_|ptla_)/.test(answer)) console.log(cliColor.yellow("The use of Application API keys are deprecated, you should use Client API keys"));
|
||||
|
||||
if (isValid) {
|
||||
answers.push(isValidURL(answer) ? new URL(answer).origin : answer);
|
||||
askQuestion(index + 1);
|
||||
} else {
|
||||
askQuestion(index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(" \n====================================================")
|
||||
console.log("Thank you! Here are the details you've provided:");
|
||||
console.log(`Panel Name : ${cliColor.cyanBright(answers[0])}`);
|
||||
console.log(`Panel URL : ${cliColor.cyanBright(answers[1])}`);
|
||||
console.log(`Panel API Key : ${cliColor.cyanBright(answers[2])}`);
|
||||
console.log(`Discord Bot Token : ${cliColor.cyanBright(answers[3])}`);
|
||||
console.log(`Discord Channel ID : ${cliColor.cyanBright(answers[4])}`);
|
||||
console.log("====================================================\n \n")
|
||||
|
||||
fs.writeFileSync(".env", `PanelURL=${answers[1]}\nPanelKEY=${answers[2]}\nDiscordBotToken=${answers[3]}\nDiscordChannel=${answers[4]}`, "utf8")
|
||||
|
||||
fs.writeFileSync("config.yml", fs.readFileSync("./config.yml", "utf8").replaceAll("Hosting Panel", answers[0]).replaceAll("https://panel.example.com", answers[1]), "utf-8")
|
||||
|
||||
console.log(cliColor.cyanBright("Please restart the bot to continue."))
|
||||
readline.close();
|
||||
}
|
||||
};
|
||||
|
||||
askQuestion(0);
|
||||
}
|
||||
@@ -1,11 +1,30 @@
|
||||
const { Client, GatewayIntentBits, EmbedBuilder, time, ActivityType } = require("discord.js");
|
||||
const config = require("./handlers/config.js");
|
||||
const convertUnits = require("./handlers/convertUnits.js");
|
||||
const getStats = require("./handlers/getStats.js");
|
||||
const { Client, GatewayIntentBits, EmbedBuilder, time, ActivityType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||
const cachePath = require('node:path').join(__dirname, "./cache.json");
|
||||
const fs = require("node:fs");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
console.log(`
|
||||
_${cliColor.blueBright.bold("Pterodactyl & Pelican")}___ ______ ______
|
||||
/\\ ___\\ /\\__ _\\ /\\ __ \\ /\\__ _\\ /\\ ___\\
|
||||
\\ \\___ \\ \\/_ \\ \\/ \\ \\ \\_\\ \\ \\/_/\\ \\/ \\ \\___ \\
|
||||
\\/\\_____\\ \\ \\_\\ \\ \\_\\ \\_\\ \\ \\_\\ \\/\\_____\\
|
||||
\\/_____/ \\/_/ \\/_/\\/_/ \\/_/ \\/_____/${cliColor.yellowBright.bold("4.0.0-dev")}`);
|
||||
|
||||
console.log(
|
||||
` \nCopyright © 2022 - ${new Date().getFullYear()} HirziDevs & Contributors` +
|
||||
" \n \nDiscord: https://discord.znproject.my.id" +
|
||||
" \n Source: https://github.com/HirziDevs/PteroStats" +
|
||||
" \nLicense: https://github.com/Hirzidevs/PteroStats/blob/main/LICENSE" +
|
||||
" \n \nPteroStats is a Discord Bot that designed to check Pterodactyl or Pelican Panel stats and post it to your Discord server. \n \n"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(".env")) return require("./handlers/installer.js")()
|
||||
|
||||
require("dotenv").config()
|
||||
const config = require("./handlers/config.js");
|
||||
const convertUnits = require("./handlers/convertUnits.js");
|
||||
const getStats = require("./handlers/getStats.js");
|
||||
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.greenBright("Starting bot..."));
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("You are using development build! some features may not work."));
|
||||
|
||||
@@ -141,7 +160,7 @@ async function createMessage({ cache, panel, nodes, servers, users }) {
|
||||
|
||||
if (!cache && !panel) {
|
||||
embeds[embeds.length - 1].setDescription(
|
||||
embeds[embeds.length - 1].data.description ? embeds[embeds.length - 1].data.description + "\n\nThere are no nodes to display!" : "There are no nodes to display!"
|
||||
embeds[embeds.length - 1].data.description ? embeds[embeds.length - 1].data.description + "\n\nThere are no nodes to be display!" : "There are no nodes to be display!"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,10 +178,13 @@ async function createMessage({ cache, panel, nodes, servers, users }) {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const channel = await client.channels.fetch(process.env?.DiscordChannel);
|
||||
const messages = await channel.messages.fetch({ limit: 10 });
|
||||
const botMessage = messages.find(msg => msg.author.id === client.user.id);
|
||||
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.greenBright("Panel stats have been posted to the channel!"));
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.greenBright(`Panel stats successfuly posted to the "${channel.name}" channel!`));
|
||||
|
||||
setTimeout(() => startGetStatus(), config.refresh * 1000);
|
||||
|
||||
@@ -192,7 +214,7 @@ function DiscordErrorHandler(error) {
|
||||
}
|
||||
|
||||
try {
|
||||
client.login(config.token);
|
||||
client.login(process.env?.DiscordBotToken);
|
||||
} catch {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error | Invalid Discord Bot Token! Make sure you have the correct token in the config!"));
|
||||
process.exit();
|
||||
|
||||
+7
-6
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "pterostats",
|
||||
"version": "v4.0.0",
|
||||
"description": "PteroStats is a bot designed to check Pterodactyl Panel and Nodes status and post it to your discord server",
|
||||
"version": "4.0.0",
|
||||
"description": "PteroStats is a Discord Bot that designed to check Pterodactyl or Pelican Panel stats and post it to your Discord server.",
|
||||
"license": "MIT",
|
||||
"repository": "HirziDevs/PteroStats",
|
||||
"homepage": "https://pterostats.hirzidevs.xyz",
|
||||
"homepage": "https://pterostats.znproject.my.id",
|
||||
"bugs": {
|
||||
"email": "hirzidevs@gmail.com",
|
||||
"url": "https://github.com/HirziDevs/PteroStats/issues"
|
||||
@@ -13,12 +13,13 @@
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"axios": "^1.7.7",
|
||||
"cli-color": "^2.0.4",
|
||||
"discord.js": "^14.15.3",
|
||||
"discord.js": "^14.16.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user