This commit is contained in:
Hirzi
2022-05-29 19:28:04 +07:00
committed by GitHub
parent c8df400a8a
commit 7c9ab231bc
8 changed files with 529 additions and 428 deletions

81
Indo.md Normal file
View File

@@ -0,0 +1,81 @@
<div align="center">
![PteroStats Banner](https://cdn.discordapp.com/attachments/626755594526916629/978478722489393153/20220524_090325.png)
## Bahasa / Language
[[Indonesia]](https://github.com/HirziDevs/PteroStats/blob/dev/Indo.MD) | [[Inggris]](https://github.com/HirziDevs/PteroStats/blob/dev/README.md)
</div>
## Pengenalan
PteroStats adalah bot yang dirancang untuk memeriksa status panel pterodactyl dan dikirim ke server discord
## Instalasi
### Mendapatkan apikey dari pterodactyl
- Pergi ke `panel admin pterodactyl` dan pergi ke `Application API`
![Idk](https://usercontent.catto.pictures/hirzi/d5225df9-7395-491b-a214-dcd110b12308.png)
- Klik tombol `Create New`
![Admin Panel](https://usercontent.catto.pictures/hirzi/5ac33e25-ac37-416a-99a6-46d860a51645.png)
- Set semua permission ke `read` dan untuk description kamu bisa mengisi apa saja
![Application API](https://usercontent.catto.pictures/hirzi/a0c4a721-e1eb-483f-9a36-0c2aaa213186.png)
- Copy apikey-nya.
![Copy the apikey](https://usercontent.catto.pictures/hirzi/086111e0-0ffa-48ee-8839-801e0c3678cc.png)
### Membuat Discord Bot
Kalian bisa cek [website ini](https://discordjs.guide/preparations/setting-up-a-bot-application.html)
### Menginvite Discord Bot
Kalian bisa cek [website ini](https://discordjs.guide/preparations/adding-your-bot-to-servers.html)
### Mendapatkan Channel ID
1. Aktifkan `Developer Mode` di settings discord kamu
![User Settings](https://usercontent.catto.pictures/hirzi/c5e825d1-c323-4b19-a11b-e2f004d4906e.png)
2. Copy Channel ID-nya
![Channel ID](https://usercontent.catto.pictures/hirzi/e5fa4f62-b28f-45fd-a544-429f23899edb.png)
### Memulai Bot
- Masukan `token` bot discord di `line token` yang terdapat di file `config.yml`
- Copy `id channel` diserver discord kamu dan masukan ke line `channel` di file `config.yml`
- Masukan `apikey` dan `url` pterodactyl di `line panel` di file `config.yml`
- Jalankan command `npm install` di folder yang berisi file bot
- Jalankan command `node index` dan kamu selesai!
Jika kamu mendapat masalah bisa dm `Hirzi#8701` didiscord atau join [server support kami](https://discord.gg/zv6maQRah3)
### Mengunakan custom emoji
1. ketik `\` di server yang ada custom emojinya
![Idk](https://usercontent.catto.pictures/hirzi/1f59b255-7c5d-48f2-ab93-5358429cec83.png)
2. Pilih custom emoji yang kamu mau
![Idk](https://usercontent.catto.pictures/hirzi/38098261-7257-4e4d-8945-4ac5c252c952.png)
3. Copy textnya!
![Idk](https://usercontent.catto.pictures/hirzi/33800ccf-9ed5-4d54-9747-2983b23e1755.png)
## Permission apikey
Pilih Aktifkan `read` di semua opsi permission, jika tetap error pilih `read & write` di semua opsi permission
![Admin Apikey Permission](https://media.discordapp.net/attachments/819757140155564062/876320084992331816/Screenshot_2021-08-15-11-20-05-56.jpg)
## Links
- [Pterodactyl Panel](https://pterodactyl.io)
- [Pterodactyl Api Documentation](https://dashflo/docs/api/pterodactyl/v1)
- [Pterodactyl Discord Server]((https://discord.gg/pterodactyl))
- [PteroBot Support Server](https://discord.gg/zv6maQRah3)
- [PteroBot Support Server (Indonesia)](https://discord.gg/EYaFB7WSg6)

136
README.md
View File

@@ -1,115 +1,81 @@
# THIS BUILD HASN'T BEEN TESTED WITH NODE THAT WINGS DOWN
<div align="center">
# PteroStats
![PteroStats Banner](https://cdn.discordapp.com/attachments/626755594526916629/978478722489393153/20220524_090325.png)
![GitHub](https://img.shields.io/github/package-json/v/HirziDevs/PteroStats?style=flat-square)
![GitHub](https://img.shields.io/github/license/HirziDevs/PteroStats?style=flat-square)
## Language / Bahasa
[[English]](https://github.com/HirziDevs/PteroStats/blob/dev/README.md) | [[Indonesia]](https://github.com/HirziDevs/PteroStats/blob/dev/Indo.MD)
![img](https://media.discordapp.net/attachments/796259732683227157/861126504246411264/20210704_130856.jpg)
</div>
## Introduction
PteroStats is a bot designed to check Pterodactyl Panel and Nodes status and post it to your discord server
- Written in Javascript, CloudServer is faster and more stable.
- PteroControl can be used with any server on Pterodactyl, irregardless of whether it's on shared hosting or your own hosted panel
PteroStats is still **under development** and we welcome contributions.
### How it works?
PteroStats checks [pterodactyl](https://pterodactyl.io) nodes wings with [axios](https://www.npmjs.com/package/axios) to get nodes wings status, if the api didn't reply that mean the node is having [wings/daemon](https://pterodactyl.io/wings/1.0/installing.html) down and mark the node as offline
### Screenshot
- [**ItzyStore**](https://discord.gg/PS4Mf6DBzt) (No screenshot for resource becouse it will be long image)
![img](https://media.discordapp.net/attachments/796259732683227157/863359897210060820/IMG_20210710_164939.jpg)
- [**SpaceCloud**](https://discord.gg/28z8CYmPEY)
![img](https://media.discordapp.net/attachments/586738538448420881/866624597171372032/IMG_20210719_171633.jpg)
## Installation
- `fill in the required informations in the config.yml file`
- `Run npm install in the root directory of the bot files`
- `Run node index.js and you are done`
### Getting apikey from pterodactyl
- Go to your `pterodactyl admin page` and go to `Application API`.
if you need help contact me on discord `Hirzi#8701` or join [our discord server here](https://discord.gg/zv6maQRah3)
![Idk](https://usercontent.catto.pictures/hirzi/d5225df9-7395-491b-a214-dcd110b12308.png)
### Setuping Config
- Click on the `Create New` button
You need to put right config to make the bot work at [config.yml](https://github.com/HirziDevs/PteroStats/blob/main/config.yml) file
```
# PteroStats config
# If you need help, join our discord server: https://discord.gg/zv6maQRah3
![Idk](https://usercontent.catto.pictures/hirzi/5ac33e25-ac37-416a-99a6-46d860a51645.png)
# Bot Info's
token: 'BOT TOKEN' # Put bot token here, check https://discord.dev to create and get bot token
botstatus:
enable: false # Enable Custom Status (MUST BE "true" OR "false")
text: 'Hosting Panel' # Bot Status Message
type: 'WATCHING' # Bot Status Type. Ex: PLAYING, WATCHING, LISTENING, STREAMING
- Set all options permission to `read` and for description you can put whatever you want
# Channel and RefreshTime Configuration
channel: 'CHANNEL ID' # Put channel id here where the embed will be sended
refreshtime: 60 # Time when the embed edited/refreshed (MUST BE A SECONDS) (RECOMMENDED MORE THAN 20 SECONDS)
![Idk](https://usercontent.catto.pictures/hirzi/a0c4a721-e1eb-483f-9a36-0c2aaa213186.png)
# Panel Info's
panel:
url: 'HOST PANEL LINK' # Put panel url here. Example: https://panel.purenodes.net
adminkey: 'ADMIN APIKEY' # Put Admin Apikey here. check https://your.host.url/admin/api (your.host.url is an example link) to get the Admin ApiKey
- Copy the apikey.
# Embed Configuration
embed:
title: 'EMBED TITLE' # Embed Title here. Ex: PureNodes Stats
color: 'E5BE11' # Embed Hex color here.
description:
enable: false # Enable Embed Description (MUST BE "true" OR "false")
text: 'EMBED DESCRIPTION' # Embed Description
footer:
enable: true # Enable Embed Footer (MUST BE "true" OR "false")
text: 'By Hirzi#8701' # Embed Footer
timestamp: true # Enable Embed TimeStamp (MUST BE "true" OR "false")
![Idk](https://usercontent.catto.pictures/hirzi/086111e0-0ffa-48ee-8839-801e0c3678cc.png)
# Status Message Configuration
status:
online: ':green_circle: Online' # Message if the status is Online
offline: ':red_circle: Offline' # Message if the status is Offline
check: ':orange_circle: Checking' # Message if the status is Checking
### Creating Discord Bot
Please refer to [this website](https://discordjs.guide/preparations/setting-up-a-bot-application.html)
# Node Resource
resource:
enable: false # Enable resource option ex [Ram: 2gb/5gb] bellow node name (MUST BE "true" OR "false")
servers: true # Enable Total server on the node to resource text (MUST BE "true" OR "false")
allocations: true # Enable Total Allocation on the node to resource text (MUST BE "true" OR "false")
location: true # Enable location short name on the node to resource text (MUST BE "true" OR "false")
unit: 'gb' # Must be 'mb', 'gb', or 'percent'
### Inviting Discord Bot
Please refer to [this website](https://discordjs.guide/preparations/adding-your-bot-to-servers.html)
# Developers feature
debug: false # Enable and Disable debug log to console
debugaxios: false #Enable and Disable axios error logs
```
### Getting Channel ID
1. Enable Developer Feature at your discord settings
## Other
### FAQ
![Idk](https://usercontent.catto.pictures/hirzi/c5e825d1-c323-4b19-a11b-e2f004d4906e.png)
Q: Can i use pterodactyl v0.7?
2. Copy Channel ID
A: No, the pterodactyl v0.7 is not supported
![Idk](https://usercontent.catto.pictures/hirzi/e5fa4f62-b28f-45fd-a544-429f23899edb.png)
-
### Starting bot
- Put discord bot token in `config.yml` at `token line`.
- Put your pterodactyl `apikey` and `url` in `config.yml` at `panel line`.
- Copy `channel id` from your discord server and put it in `config.yml` file at `channel line`.
- Run `npm install` in the root directory of the bot files.
- Run `node index` and you are done.
Q: How much nodes can i add?
if you need help contact me on discord `Hirzi#8701` or join [our discord support server](https://discord.gg/zv6maQRah3)
A: You can add as much your panel have
### Using Custom Emoji
1. type `\` in guild that has custom emoji you want
-
![Idk](https://usercontent.catto.pictures/hirzi/1f59b255-7c5d-48f2-ab93-5358429cec83.png)
Q: How i can get support?
2. Select custom emoji you want
A: You can join our [discord server](https://discord.gg/zv6maQRah3)
![Idk](https://usercontent.catto.pictures/hirzi/38098261-7257-4e4d-8945-4ac5c252c952.png)
### Links
3. Copy the text!
* __[PteroBot Discord](https://discord.gg/zv6maQRah3)__
* __[Pterodactyl Panel](https://pterodactyl.io)__
* __[Pterodactyl API](https://dashflo.net/docs/api/pterodactyl/v1)__
![Idk](https://usercontent.catto.pictures/hirzi/33800ccf-9ed5-4d54-9747-2983b23e1755.png)
## Admin Apikey Permission
enable `read` on all options, if still didn't work enable `read & write` on all options
![Admin Apikey Permission](https://media.discordapp.net/attachments/819757140155564062/876320084992331816/Screenshot_2021-08-15-11-20-05-56.jpg)
## Links
- [Pterodactyl Panel](https://pterodactyl.io)
- [Pterodactyl Api Documentation](https://dashflo/docs/api/pterodactyl/v1)
- [Pterodactyl Discord Server]((https://discord.gg/pterodactyl))
- [PteroBot Support Server](https://discord.gg/zv6maQRah3)
- [PteroBot Support Server (Indonesia)](https://discord.gg/EYaFB7WSg6)

View File

@@ -1,48 +1,65 @@
# PteroStats config
# If you need help, join our discord server here: https://discord.gg/zv6maQRah3
# Bot Info's
token: 'BOT TOKEN' # Put bot token here, check https://discord.dev to create and get bot token
botstatus:
enable: false # Enable Custom Status (MUST BE "true" OR "false")
text: 'Hosting Panel' # Bot Status Message
type: 'WATCHING' # Bot Status Type. Ex: PLAYING, WATCHING, LISTENING, STREAMING
# Bot Configuration
token: 'Put bot token here'
bot_status:
enable: false
text: 'Hosting Panel'
type: 'WATCHING' # can be 'WATCHING', 'PLAYING', 'LISTENING', or 'COMPETING'. for 'STREAMING' is not working for now
# Channel and RefreshTime Configuration
channel: 'CHANNEL ID' # Put channel id here where the embed will be sended
refreshtime: 60 # Time when the embed edited/refreshed (MUST BE A SECONDS) (RECOMMENDED MORE THAN 20 SECONDS)
# Discord Channel and Refresh Time Configuration
channel: 'Put channel id here'
refresh: 60
# Panel Info's
# Panel Configuration
panel:
url: 'HOST PANEL LINK' # Put panel url here. Example: https://panel.purenodes.net
adminkey: 'ADMIN APIKEY' # Put Admin Apikey here. check https://your.host.url/admin/api (your.host.url is an example link) to get the Admin ApiKey
url: 'Put panel url here'
key: 'Put panel apikey here'
# Embed Configuration
# set to false if you want to disable embed option
embed:
title: 'EMBED TITLE' # Embed Title here. Ex: PureNodes Stats
color: 'E5BE11' # Embed Hex color here.
description:
enable: false # Enable Embed Description (MUST BE "true" OR "false")
text: 'EMBED DESCRIPTION' # Embed Description
footer:
enable: true # Enable Embed Footer (MUST BE "true" OR "false")
text: 'By Hirzi#8701' # Embed Footer
timestamp: true # Enable Embed TimeStamp (MUST BE "true" OR "false")
title: 'PteroStats'
color: '5865F2'
description: 'Next update {{time}}' # You can use {{time}} to make "in X seconds" time format
footer: 'By Hirzi#8701'
timestamp: true
thumbnail: false
image: false
field:
enable: false
title: 'Custom Field'
description: 'Custom Field' # You can use {{time}} to make "in X seconds" time format
# Message Button Configuration
button:
enable: true
btn1:
label: 'PteroStats'
url: 'https://github.com/HirziDevs/PteroStats'
btn2:
label: ''
url: ''
btn3:
label: ''
url: ''
btn4:
label: ''
url: ''
btn5:
label: ''
url: ''
# Status Message Configuration
status:
online: ':green_circle: Online' # Message if the status is Online
offline: ':red_circle: Offline' # Message if the status is Offline
check: ':orange_circle: Checking' # Message if the status is Checking
online: ':green_circle: Online' # TODO how to use custom emoji
offline: ':red_circle: Offline'
# Node Resource
resource:
enable: true # Enable resource option ex [Ram: 2gb/5gb] bellow node name (MUST BE "true" OR "false")
servers: true # Enable Total server on the node to resource text (MUST BE "true" OR "false")
allocations: true # Enable Total Allocation on the node to resource text (MUST BE "true" OR "false")
location: true # Enable location short name on the node to resource text (MUST BE "true" OR "false")
unit: 'gb' # Must be 'mb', 'gb', or 'percent'
# Developers feature
debug: false # Enable and Disable debug log to console
debugaxios: false #Enable and Disable axios error logs
enable: false
servers: true
location: true
allocations: true
unit: 'gb' # You can use "gb", "mb", or "percent"

View File

@@ -1,295 +1,29 @@
module.exports = client => {
const chalk = require('chalk')
const checkStatus = require('../handlers/checkStatus')
//Code are very sensitive, please changes things on config.yml instead
const { MessageEmbed } = require('discord.js')
const axios = require('axios')
const db = require('quick.db')
const nodetable = new db.table('node')
const paneltable = new db.table('panel')
const chalk = require('chalk')
const config = client.config
module.exports = {
name: 'ready',
once: true,
async execute(client) {
console.log(chalk.cyan('[PteroStats]') + chalk.green(' Bot is up!'))
console.log(chalk.cyan('[PteroStats]') + chalk.green(' If you need support you can join our discord server https://discord.gg/zv6maQRah3'))
let enablecs = config.botstatus.enable
let cs = config.botstatus.text
let stype = config.botstatus.type
let ch = client.channels.cache.find(cn => cn.id === config.channel)
let time = config.refreshtime
if (client.guilds.cache.size === 0) return console.log(chalk.cyan('[PteroStats]') + chalk.red(' There is bot is not in servers, please invite the bot first!'))
let hosturl = config.panel.url
let adminapikey = config.panel.adminkey
let statusonline = config.status.online
let statusoffline = config.status.offline
let checking = config.status.check
let resource = config.resource.enable
let serverres = config.resource.servers
let serverport = config.resource.allocations
let serverloc = config.resource.location
let unit = config.resource.unit
let title = config.embed.title
let color = config.embed.color
let desc = config.embed.description.text
let footer = config.embed.footer.text
let enablets = config.embed.timestamp
let enabledesc = config.embed.description.enable
let enablef = config.embed.footer.enable
let debug = config.debug
let debugerror = config.debugaxios
if (debug === true) {
console.log(chalk.red('=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+='))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Debug Mode: ') + chalk.cyan('true'))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Debug Axios Mode: ') + chalk.cyan(debugerror))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Resource: ') + chalk.cyan(resource))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Custom Status: ') + chalk.cyan(enablecs))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Enable Timestamp: ') + chalk.cyan(enablets))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Enable Description: ') + chalk.cyan(enabledesc))
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.green('Enable Footer: ') + chalk.cyan(enablef))
}
if (!hosturl.includes('http')) hosturl = 'http://' + hosturl
let unapi = hosturl + '/api'
let api = unapi.replace('//api', '/api')
if (enablecs === true) {
client.user.setActivity(cs, { type: stype })
} else {
client.user.setActivity(title + ' Panel Stats', { type: 'WATCHING' })
}
console.log(chalk.red('=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+='))
console.log(chalk.green('Name: ') + chalk.cyan('PteroStats'))
console.log(chalk.green('Version: ') + chalk.cyan('Stable pre-v1.5.0'))
console.log(chalk.green('Refresh Time: ') + chalk.cyan(time + ' Seconds'))
console.log(chalk.green('Bot Status: ') + chalk.cyan('Online'))
console.log(chalk.green('Support: ') + chalk.cyan('https://discord.gg/zv6maQRah3'))
console.log(chalk.red('=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+='))
console.log(chalk.red('You are using dev build, something not wanted might be happen!'))
if(paneltable.get('URL') === null) console.log(chalk.cyan('It seems you are using our bot for first time, thank you for choosing our bot, if you need help you can join our support server!'))
if(paneltable.get('URL') !== api) console.log(chalk.cyan('Panel url changed, please allow the bot to check the nodes status for ' + time + ' seconds'))
paneltable.set('URL',api)
setInterval(() => {
if (isNaN(time)) return console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red(time + ' is not a number!'))
if (!hosturl.includes('.')) return console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red(hosturl + ' is invalid url!'))
if (adminapikey.length < 48) return console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red('Invalid Admin Apikey!!'))
let list = []
axios(api + '/application/nodes/', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + adminapikey
}
}).then(response => {
let data = response.data.data
data.forEach(nodes => {
let id = nodes.attributes.id
axios(api + '/application/nodes/' + id + '?include=servers,location,allocations', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + adminapikey
}
}).then(node => {
axios(api + '/application/nodes/' + id + '/configuration', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + adminapikey
if (client.config.bot_status.enable) {
if (!['PLAYING', 'WATCHING', 'LISTENING', 'COMPETING'].includes(client.config.status.type)) {
console.log('Invalid Status Type!, Can be "WATCHING", "PLAYING", "LISTENING", or "COMPETING"')
} else {
client.user.setActivity(client.config.status.text, { type: client.config.status.type })
}
}).then(data => {
axios(node.data.attributes.scheme + '://' + node.data.attributes.fqdn + ':' + node.data.attributes.daemon_listen + '/api/servers', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + data.data.token
}
}).then(status => {
let ram = 'temp'
let disk = 'temp'
const mode = node.data.attributes.maintenance_mode
const loc = '[Locations: ' + node.data.attributes.relationships.location.attributes.short + ']'
const port = '[Allocations: ' + node.data.attributes.relationships.allocations.data.length + ']'
const servers = '[Servers: ' + node.data.attributes.relationships.servers.data.length + ']'
const rampercent = '[Ram: ' + Math.floor(node.data.attributes.allocated_resources.memory / node.data.attributes.memory * 100) + '%/100%]'
const diskpercent = '[Disk: ' + Math.floor(node.data.attributes.allocated_resources.disk / node.data.attributes.disk * 100) + '%/100%]'
const rammega = '[Ram: ' + node.data.attributes.allocated_resources.memory + 'MB/' + node.data.attributes.memory + 'MB]'
const diskmega = '[Disk: ' + node.data.attributes.allocated_resources.disk + 'MB/' + node.data.attributes.disk + 'MB]'
const ramgiga = '[Ram: ' + Math.floor(node.data.attributes.allocated_resources.memory / 1000) + 'GB/' + Math.floor(node.data.attributes.memory / 1000) + 'GB]'
const diskgiga = '[Disk: ' + Math.floor(node.data.attributes.allocated_resources.disk / 1000) + 'GB/' + Math.floor(node.data.attributes.disk / 1000) + 'GB]'
if (unit === 'mb') {
disk = diskmega
ram = rammega
}
if (unit === 'gb') {
disk = diskgiga
ram = ramgiga
}
if (unit === 'percent') {
disk = diskpercent
ram = rampercent
}
nodetable.set('node' + id, {
ram: ram,
disk: disk,
status: true,
servers: servers,
location: loc,
port: port,
mode: mode
})
}).catch((err) => {
let servers = '[Servers: N/A]'
let loc = '[Location: N/A]'
let port = '[Allocations: N/A]'
let ram = '[Ram: N/A]'
let disk = '[Disk: N/A]'
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red(node.data.attributes.name + ' is down!'))
if (debugerror === true) console.log(chalk.magenta('[PteroStats Debug] ') + chalk.red(err) + chalk.cyan(' Need Support? https://discord.gg/zv6maQRah3'))
nodetable.set('node' + id, {
ram: ram,
disk: disk,
status: false,
servers: servers,
location: loc,
port: port,
mode: false
})
})
}).catch((err) => {
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.red(err) + chalk.cyan(' Need Support? https://discord.gg/zv6maQRah3'))
})
}).catch((err) => {
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.red(err) + chalk.cyan(' Need Support? https://discord.gg/zv6maQRah3'))
})
let stats = nodetable.get('node' + id)
let msgStats = ''
if (stats === null) msgStats = '**' + nodes.attributes.name + '**: ' + checking
if (stats) {
let statsname = '**' + nodes.attributes.name + '**: '
if (stats.status === true) statsname = statsname + statusonline
if (stats.status === false) statsname = statsname + statusoffline
if (stats.mode === true) statsname = statsname + ' [Maintance]'
if (resource === true) statsname = statsname + '\n```\n' + stats.ram + '\n' + stats.disk
if (serverloc === true) statsname = statsname + '\n' + stats.location
if (serverport === true) statsname = statsname + '\n' + stats.port
if (serverres === true) statsname = statsname + '\n' + stats.servers
if (resource === false) statsname = statsname + '\n'
if (resource === true) msgStats = statsname + '```\n'
}
if (debug === true) console.log(chalk.magenta('[PteroStats Debug] ') + chalk.blue(nodes.attributes.name + ': ' + stats.status))
list.push(msgStats)
})
if (client.config.refresh < 10) console.log('Refresh below 10 seconds is not recommended!')
axios(api + '/application/servers', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + adminapikey
}
}).then(ser => {
let res = ser.data.meta.pagination.total
paneltable.set('serverCount', res)
}).catch((err) => {
paneltable.set('serverCount', 'N/A')
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red('Panel is down'))
if (debugerror === true) console.log(chalk.magenta('[PteroStats Debug] ') + err)
})
checkStatus(client)
axios(api + '/application/users', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + adminapikey
}
}).then(usr => {
let res = usr.data.meta.pagination.total
paneltable.set('userCount', res)
}).catch((err) => {
paneltable.set('userCount', 'N/A')
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red('Panel is down!'))
if (debugerror === true) console.log(chalk.magenta('[PteroStats Debug] ') + err)
})
let userCount = paneltable.get('userCount')
let serverCount = paneltable.get('serverCount')
if (userCount === null) userCount = checking
if (serverCount === null) serverCount = checking
if (userCount !== 'N/A') paneltable.set('panel', '**Panel**: ' + statusonline)
if (userCount === 'N/A') {
paneltable.set('panel', '**Panel**: ' + statusoffline)
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.red('panel is down!'))
}
if (userCount === checking) paneltable.set('panel', '**Panel**: ' + checking)
let panel = paneltable.get('panel') + '\n\nUsers: ' + userCount + '\nServers: ' + serverCount
if (panel === null) panel = '**Panel**: ' + checking + '\n\nUsers: ' + userCount + '\nServers: ' + serverCount
let nodes
list.forEach((d) => {
if (!nodes) return nodes = d
nodes = nodes + d
})
console.log(chalk.cyan(['[PteroStats Checker] ']) + chalk.green('Connected to ' + list.length + ' nodes')
let nodeCount = '[Total ' + list.length + ']'
if (debug === true) console.log(chalk.magenta('[PteroStats Debug] ') + chalk.blue(nodes))
if (nodes === undefined) {
nodes = checking + ' Please wait ' + time + ' seconds'
console.log(chalk.cyan(['[PteroStats Checker] ']) + chalk.yellow(checking + ' Please wait ' + time + ' seconds'))
}
let embedfooter = 'Updated every ' + time + ' seconds'
if (enablef === true) embedfooter = 'Updated every ' + time + ' seconds | ' + footer
let embed = new MessageEmbed()
.setTitle(title)
.setColor(color)
.addField('Panel Stats', panel)
.setFooter(embedfooter)
.setThumbnail(client.user.avatarURL())
if (enablets === true) {
embed.setTimestamp()
}
if (enabledesc === true) {
embed.setDescription(desc + '\n**Nodes Stats' + nodeCount + '**\n' + nodes)
} else {
embed.setDescription('\n**Nodes Stats' + nodeCount + '**\n' + nodes)
}
ch.send(embed).then(msg => { msg.delete({ timeout: time + '000' }) })
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.green('Posted Stats'))
if (panel !== null) console.log(chalk.cyan('[PteroStats Checker] ') + chalk.green('Stats Updated'))
console.log(chalk.cyan('[PteroStats Checker] ') + chalk.green('Updating Stats in ' + time + ' Seconds'))
}).catch((err) => {
console.log(chalk.magenta('[PteroStats Debug] ') + chalk.red(err) + chalk.cyan(' Need Support? https://discord.gg/zv6maQRah3'))
})
}, time + '000')
}
setInterval(async () => {
checkStatus(client)
}, client.config.refresh * 1000)
}
}

125
handlers/checkStatus.js Normal file
View File

@@ -0,0 +1,125 @@
const axios = require('axios')
const chalk = require('chalk')
const postStatus = require('./postStatus')
module.exports = async function checkStatus(client) {
const nodes = []
const panel = {
id: 1,
status: false,
total_servers: -1,
total_users: -1,
}
console.log(chalk.cyan('[PteroStats]') + chalk.green(' Getting nodes stats'))
const panelStats = new Promise((resolve, reject) => {
axios(client.config.panel.url + '/api/application/users', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + client.config.panel.key
}
}).then(usr => {
axios(client.config.panel.url + '/api/application/servers', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + client.config.panel.key
}
}).then(async (ser) => {
panel.total_users = usr.data.meta.pagination.total
panel.total_servers = ser.data.meta.pagination.total
panel.status = true
resolve()
})
}).catch(async (err) => {
if (err.response) {
if (err.response.status === 403) {
console.log('[PteroStats] Err! Invalid apikey')
console.log('[PteroStats] 1. Make sure the apikey is from admin page not account page')
console.log('[PteroStats] 2. Make sure the apikey has read permission on all options')
console.log('[PteroStats] 3. Make sure the apikey is exist')
} else if (err.response.status === 404) {
console.log('[PteroStats] Err! Invalid URL Panel')
console.log('[PteroStats] 1. Make sure the panel url is like "https://panel.example.com"')
} else {
console.log(err)
}
} else {
console.log('[PteroStats] ' + err)
}
resolve()
})
})
const nodeStats = new Promise((resolve, reject) => {
axios(client.config.panel.url + '/api/application/nodes?include=servers,location,allocations', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + client.config.panel.key
}
}).then(async (res) => {
res.data.data.forEach(async (node, i) => {
axios(client.config.panel.url + '/api/application/nodes/' + node.attributes.id + '/configuration', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + client.config.panel.key
}
}).then(async (data) => {
const body = {
id: node.attributes.id,
name: node.attributes.name,
location: node.attributes.relationships.location.attributes.short,
allocations: node.attributes.relationships.allocations.data.length,
status: true,
maintenance: node.attributes.maintenance_mode,
total_servers: node.attributes.relationships.servers.data.length,
memory_min: node.attributes.allocated_resources.memory,
memory_max: node.attributes.memory,
disk_min: node.attributes.allocated_resources.disk,
disk_max: node.attributes.disk,
}
const stats = new Promise((statsResolve, statsReject) => {
axios(node.attributes.scheme + '://' + node.attributes.fqdn + ':' + node.attributes.daemon_listen + '/api/servers', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + data.data.token
}
}).then(async (status) => {
statsResolve()
}).catch(async (err) => {
body.status = false
statsResolve()
})
})
stats.then(() => {
nodes.push(body)
resolve()
})
}).catch(async (err) => {
resolve()
})
})
}).catch(async (err) => {
resolve()
})
})
panelStats.then(() => {
nodeStats.then(() => {
postStatus(client, panel, nodes)
})
})
}

153
handlers/postStatus.js Normal file
View File

@@ -0,0 +1,153 @@
const { MessageEmbed, Formatters, MessageActionRow, MessageButton } = require('discord.js')
const chalk = require('chalk')
module.exports = async function postStatus(client, panel, nodes) {
const channel = await client.channels.cache.get(client.config.channel)
if (!channel) return console.log('Invalid Channel ID')
let messages = await channel.messages.fetch({ limit: 10 })
messages = messages.filter(m => m.author.id === client.user.id).last();
if (messages && messages.embeds.length === 0) {
messages.delete()
messages = null
}
const format = await Formatters.time(new Date(Date.now() + client.config.refresh * 1000), 'R')
const embed = new MessageEmbed()
let text = ''
let desc = ''
if (client.config.embed.title) embed.setTitle(client.config.embed.title)
if (client.config.embed.description) desc = client.config.embed.description.replaceAll('{{time}}', format) + '\n'
if (client.config.embed.color) embed.setColor(client.config.embed.color)
if (client.config.embed.footer) embed.setFooter({ text: client.config.embed.footer })
if (client.config.embed.thumbnail) embed.setThumbnail(client.config.embed.thumbnail)
if (client.config.embed.image) embed.setImage(client.config.embed.image)
panel.total_users = panel.total_users.toLocaleString()
panel.total_servers = panel.total_servers.toLocaleString()
const stats = new Promise((resolve, reject) => {
if (nodes.length !== 0) {
nodes.forEach((data, i) => {
const title = data.name + ': ' + String(data.status).replace('true', client.config.status.online).replace('false', client.config.status.offline)
let description = '```' + '\n' +
'Status : ' + String(data.status).replace('true', client.config.status.online).replace('false', client.config.status.offline) + '\n'
switch (client.config.resource.unit) {
case 'gb':
description = description +
'Memory : ' + Math.floor(data.memory_min / 1000).toLocaleString() + ' Gb /' + Math.floor(data.memory_max / 1000).toLocaleString() + ' Gb\n' +
'Disk : ' + Math.floor(data.disk_min / 1000).toLocaleString() + 'Gb /' + Math.floor(data.disk_max / 1000).toLocaleString() + ' Gb\n'
break;
case 'percent':
description = description +
'Memory : ' + Math.floor(data.memory_min / data.memory_max * 100) + ' %\n' +
'Disk : ' + Math.floor(data.disk_min / data.disk_max * 100) + ' %\n'
break;
default:
description = description +
'Memory : ' + data.memory_min.toLocaleString() + ' Mb /' + data.memory_max.toLocaleString() + ' Mb\n' +
'Disk : ' + data.disk_min.toLocaleString() + 'Mb /' + data.disk_max.toLocaleString() + ' Mb\n'
}
if (client.config.resource.location) description = description + 'Location: ' + data.location + '\n'
if (client.config.resource.allocations) description = description + 'Servers : ' + data.allocations.toLocaleString() + '\n'
if (client.config.resource.servers) description = description + 'Servers : ' + data.total_servers.toLocaleString() + '\n'
description = description + '```'
if (client.config.resource.enable) {
text = text + '\n**' + title.replace(':', ':**') + '\n' + description
} else {
text = text + '\n**' + title.replace(':', ':**')
}
if (i + 1 === nodes.length) resolve()
})
} else if (nodes.length === 0) {
if (!messages) {
text = 'There is no nodes to display'
resolve()
} else {
text = messages.embeds[0].fields[0].value.replaceAll(client.config.status.online, client.config.status.offline)
if (!panel.status && String(String(messages.embeds[0].fields[1].value).split('\n')[2]).split('')[String(String(messages.embeds[0].fields[1].value).split('\n')[2]).length - 1] !== '`') {
panel.total_users = String(String(messages.embeds[0].fields[1].value).split('\n')[2]).split('')[String(String(messages.embeds[0].fields[1].value).split('\n')[2]).length - 1]
panel.total_servers = String(String(messages.embeds[0].fields[1].value).split('\n')[3]).split('')[String(String(messages.embeds[0].fields[1].value).split('\n')[3]).length - 1]
}
resolve()
}
}
})
stats.then(async () => {
embed.setDescription(desc + '\n**Nodes Stats [' + nodes.length + ']**' + text)
embed.addField('Panel Stats',
'**Status:** ' + String(panel.status).replace('true', client.config.status.online).replace('false', client.config.status.offline) + '\n\n' +
'Users: ' + String(panel.total_users).replace('-1', '`Unknown`') + '\n' +
'Servers: ' + String(panel.total_servers).replace('-1', '`Unknown`')
)
if (client.config.embed.field.enable) {
embed.addField(client.config.embed.field.title, client.config.embed.field.description.replaceAll('{{time}}', format))
}
embed.setTimestamp()
let row = []
if (client.config.button.enable) {
row = new MessageActionRow
if (client.config.button.btn1.label.length !== 0 && client.config.button.btn1.label.link.length !== 0) {
row.addComponents(
new MessageButton()
.setLabel(client.config.button.btn1.label)
.setStyle('LINK')
.setURL(client.config.button.btn1.url)
)
}
if (client.config.button.btn2.label.length !== 0 && client.config.button.btn2.label.link.length !== 0) {
row.addComponents(
new MessageButton()
.setLabel(client.config.button.btn2.label)
.setStyle('LINK')
.setURL(client.config.button.btn2.url)
)
}
if (client.config.button.btn3.label.length !== 0 && client.config.button.btn3.label.link.length !== 0) {
row.addComponents(
new MessageButton()
.setLabel(client.config.button.btn3.label)
.setStyle('LINK')
.setURL(client.config.button.btn3.url)
)
}
if (client.config.button.btn4.label.length !== 0 && client.config.button.btn4.label.link.length !== 0) {
row.addComponents(
new MessageButton()
.setLabel(client.config.button.btn4.label)
.setStyle('LINK')
.setURL(client.config.button.btn4.url)
)
}
if (client.config.button.btn5.label.length !== 0 && client.config.button.btn5.label.link.length !== 0) {
row.addComponents(
new MessageButton()
.setLabel(client.config.button.btn5.label)
.setStyle('LINK')
.setURL(client.config.button.btn5.url)
)
}
row = [row]
}
if (!messages) channel.send({ embeds: [embed] })
else messages.edit({ embeds: [embed], components: row })
console.log(chalk.cyan('[PteroStats]') + chalk.green(' stats posted!'))
})
}

View File

@@ -1,18 +1,44 @@
const { Client, Collection } = require('discord.js')
const fs = require('fs')
const client = new Client()
const yaml = require('js-yaml')
const config = yaml.load(fs.readFileSync('./config.yml', 'utf8'))
const fs = require('fs');
if (Number(process.version.split('.')[0]) < 16) {
console.log('Invalid NodeJS Version!, Please use NodeJS 16.x or upper')
process.exit()
}
if (fs.existsSync('./node_modules')) {
const check = require('./node_modules/discord.js/package.json')
if (Number(check.version.split('.')[0]) !== 13) {
console.log('Invalid Discord.JS Version!, Please use Discord.JS 13.x')
process.exit()
}
} else {
console.log('There is no node_modules!, please install the package first by using "npm i"')
process.exit()
}
const yaml = require('js-yaml');
const { Client, Intents } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
// Load Config
const config = yaml.load(fs.readFileSync('./config.yml', 'utf8'));
client.config = config
fs.readdir('./events/', (err, files) => {
if (err) return console.error(err)
files.forEach(file => {
const event = require(`./events/${file}`)
const eventName = file.split('.')[0]
client.on(eventName, event.bind(null, client))
})
})
// Read Events Files
const eventFiles = fs.readdirSync('./events').filter(file => file.endsWith('.js'));
if (config.token === 'BOT TOKEN') console.log(chalk.blue('[PteroStats Checker] ') + chalk.red('Invalid Token, Check ') + chalk.green('config.yml') + chalk.red(' file to change token'))
client.login(config.token)
for (const file of eventFiles) {
const event = require(`./events/${file}`);
if (event.once) {
client.once(event.name, (client) => event.execute(client));
} else {
client.on(event.name, (client) => event.execute(client));
}
}
// Login to bot
try {
client.login(config.token);
} catch (Err) {
console.log('Invalid discord bot token')
process.exit()
}

View File

@@ -1,25 +1,24 @@
{
"name": "pterostats",
"version": "v1.5.0",
"version": "v2.0.0",
"description": "PteroStats is a bot designed to check Panel, Nodes, and Databases status and post it to your discord server",
"license": "Apache-2.0",
"repository": "HirziDevs/PteroStats",
"homepage": "https://pterostats.hirzidevs.net",
"bugs": {
"email": "hirzigamingyt@gmail.com",
"email": "hirzidevs@gmail.com",
"url": "https://github.com/HirziDevs/PteroStats"
},
"scripts": {
"start": "node index.js"
},
"dependencies": {
"axios": "^0.21.1",
"chalk": "^4.1.1",
"js-yaml": "^4.1.0",
"discord.js": "^12.5.3",
"quick.db": "^7.1.3"
"axios": "^0.26.1",
"chalk": "4.1.1",
"discord.js": "^13.7.0",
"js-yaml": "^4.1.0"
},
"engines": {
"node": "^12.x"
"node": "16.x"
}
}