Skip to content

Commit

Permalink
Merge pull request #73 from winglessraven/web-panel
Browse files Browse the repository at this point in the history
Add Web Panel Option
  • Loading branch information
winglessraven authored Dec 20, 2023
2 parents e01e3f4 + 6c5c369 commit 3d927df
Show file tree
Hide file tree
Showing 8 changed files with 559 additions and 17 deletions.
206 changes: 189 additions & 17 deletions DiscordBotPlugin/PluginMain.cs

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions DiscordBotPlugin/ResourceReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DiscordBotPlugin
{
internal class ResourceReader
{
public string ReadResource(string resourceName)
{
var assembly = Assembly.GetExecutingAssembly();

// Resource name should include namespace
string resourcePath = $"{assembly.GetName().Name}.{resourceName}";

using (Stream stream = assembly.GetManifestResourceStream(resourcePath))
{
if (stream == null)
throw new FileNotFoundException($"Resource {resourceName} not found.");

using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}

}
}
3 changes: 3 additions & 0 deletions DiscordBotPlugin/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ public class DiscordBotSettings : SettingSectionStore
[WebSetting("Discord Debug Mode", "Enable verbose logging on the Discord bot for debugging", false)]
public bool DiscordDebugMode = false;

[WebSetting("Enable Web Panel", "Enable a web panel on the specified port for embedding onto a website", false)]
public bool EnableWebPanel = false;

public Dictionary<string, DateTime> LastSeen = new Dictionary<string, DateTime>();

}
Expand Down
64 changes: 64 additions & 0 deletions DiscordBotPlugin/panel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Info Dashboard</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>

<div class="server-info">
<div class="status">
<h2>Server Status</h2>
<p class="{{statusClass}}">{{status}}</p>
</div>
<div class="details">
<h2>Server Name</h2>
<code id="server-name" onclick="copyToClipboard('server-name')"></code>
</div>
<div class="details">
<h2>Server IP</h2>
<code id="server-ip" onclick="copyToClipboard('server-ip')"></code>
</div>

<div class="flex-container">
<div class="flex-item">
<h2>CPU Usage</h2>
<p id="cpu-usage"></p>
</div>
<div class="flex-item">
<h2>Memory Usage</h2>
<p id="memory-usage"></p>
</div>
<div class="flex-item">
<h2>Uptime</h2>
<p id="uptime"></p>
</div>
</div>

<!-- Containers for Online Players and Player Count -->
<div id="online-players-container" class="flex-container" style="display: none;">
<div class="flex-item">
<!-- Players will be added here by script.js -->
</div>
</div>

<div id="player-count-container" class="flex-container" style="display: none;">
<div class="flex-item">
<h2>Player Count</h2>
<p id="player-count"></p>
</div>
</div>

<div class="details">
<h2>Top 5 Players by Play Time</h2>
<ol id="playtime-leaderboard">
<!-- Playtime leaderboard entries will be added here -->
</ol>
</div>
</div>

<script src="script.js"></script>
</body>
</html>
16 changes: 16 additions & 0 deletions DiscordBotPlugin/panel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"ServerName": "",
"ServerIP": "",
"ServerStatus": "",
"ServerStatusClass": "",
"CPUUsage": "",
"MemoryUsage": "",
"Uptime": "",
"OnlinePlayers": [
""
],
"PlayerCount": "",
"PlaytimeLeaderBoard": [
""
]
}
156 changes: 156 additions & 0 deletions DiscordBotPlugin/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
function copyToClipboard(elementId) {
var textElement = document.getElementById(elementId);
var text = textElement.innerText;
navigator.clipboard.writeText(text).then(function () {
// Change text to "Copied to Clipboard"
textElement.textContent = 'Copied to Clipboard';
setTimeout(function () {
// Revert back to the original text
textElement.textContent = text;
}, 2000); // after 2 seconds
}, function (err) {
console.error('Could not copy text: ', err);
});
}

function incrementTime(timeString) {
let [days, hours, minutes, seconds] = timeString.split(':').map(Number);
seconds += 1;
if (seconds >= 60) {
seconds = 0;
minutes += 1;
if (minutes >= 60) {
minutes = 0;
hours += 1;
if (hours >= 24) {
hours = 0;
days += 1;
}
}
}
return [
days.toString().padStart(2, '0'),
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0')
].join(':');
}

function updateUptime() {
const statusElement = document.querySelector('.status p'); // Get the current status
const uptimeElement = document.getElementById('uptime');

// Check if the status text is "Ready" before incrementing the uptime
if (statusElement && statusElement.textContent.includes('Ready')) {
uptimeElement.textContent = incrementTime(uptimeElement.textContent);
}
}

setInterval(updateUptime, 1000); // Call updateUptime every second

let currentData = {}; // Variable to store the latest fetched data

function fetchDataAndUpdateUI() {
fetch('panel.json')
.then(response => response.json())
.then(data => {
currentData = data; // Store the latest data
updateUI(data);
})
.catch(error => console.error('Error fetching data:', error));
}

function updateUI(data) {
// Update elements based on JSON data
document.getElementById('server-name').textContent = data.ServerName;
document.getElementById('server-ip').textContent = data.ServerIP;
document.querySelector('.status p').className = data.ServerStatusClass;
document.querySelector('.status p').textContent = data.ServerStatus;
document.getElementById('cpu-usage').textContent = data.CPUUsage;
document.getElementById('memory-usage').textContent = data.MemoryUsage;
document.getElementById('uptime').textContent = data.Uptime;

console.log("Updating Online Players in UI");
const onlinePlayersContainer = document.getElementById('online-players-container');
if (data.OnlinePlayers && data.OnlinePlayers.length > 0) {
console.log(`Online players from JSON: ${data.OnlinePlayers.join(', ')}`);
onlinePlayersContainer.style.display = 'block';
const onlinePlayersList = onlinePlayersContainer.querySelector('.flex-item');
onlinePlayersList.innerHTML = '<h2>Online Players</h2>' + data.OnlinePlayers.map(player => `<p>${player}</p>`).join('');
} else {
onlinePlayersContainer.style.display = 'none';
}

// Update Player Count
const playerCountContainer = document.getElementById('player-count-container');
if (data.PlayerCount) {
playerCountContainer.style.display = 'block';
document.getElementById('player-count').textContent = data.PlayerCount;
} else {
playerCountContainer.style.display = 'none';
}

// Update Playtime Leaderboard
if (data.PlaytimeLeaderBoard && data.PlaytimeLeaderBoard.length > 0) {
const playtimeLeaderboardList = document.getElementById('playtime-leaderboard');
playtimeLeaderboardList.innerHTML = data.PlaytimeLeaderBoard.map(entry => `<li>${entry}</li>`).join('');
}
}

// Function to increment player playtime in 'Xd Xh Xm Xs' format
function incrementPlayerPlaytime(playtimeString) {
let [days, hours, minutes, seconds] = playtimeString.split(/d |h |m |s/).map(Number);
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
days++;
}
}
}
return `${days}d ${hours}h ${minutes}m ${seconds}s`;
}

// Function to increment playtime for online players
function incrementPlaytimeForOnlinePlayers() {
//console.log("incrementPlaytimeForOnlinePlayers function called");

if (!currentData.OnlinePlayers || currentData.OnlinePlayers.length === 0) {
//console.log("No online players found");
return;
}

//console.log(`Found ${currentData.OnlinePlayers.length} online players from JSON`);

currentData.OnlinePlayers.forEach(playerName => {
//console.log(`Processing player: ${playerName}`);
const playtimeElements = document.querySelectorAll('#playtime-leaderboard li');
//console.log(`Found ${playtimeElements.length} playtime elements`);

playtimeElements.forEach(playtimeElement => {
//console.log(`Checking playtime element: ${playtimeElement.textContent}`);

if (playtimeElement.textContent.includes(playerName)) {
//console.log(`Before Increment - Player: ${playerName}, Playtime: ${playtimeElement.textContent}`);
const parts = playtimeElement.textContent.split(' - ');
const incrementedPlaytime = incrementPlayerPlaytime(parts[1].trim());
playtimeElement.textContent = `${playerName} - ${incrementedPlaytime}`;
//console.log(`After Increment - Player: ${playerName}, Playtime: ${playtimeElement.textContent}`);
}
});
});
}

// Call this function periodically (e.g., every second)
setInterval(incrementPlaytimeForOnlinePlayers, 1000);

// Initial call to fetch and update data
fetchDataAndUpdateUI();

// Set up interval to update data every 10 seconds
setInterval(fetchDataAndUpdateUI, 10000);
90 changes: 90 additions & 0 deletions DiscordBotPlugin/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
/*background-color: #23272a;*/
color: #fff;
margin: 0; /* Removed padding to fit better in an iframe */
}

.server-info {
background-color: #23272a;
border-radius: 0px;
padding: 10px; /* Reduced padding for a more compact layout */
width: 100%; /* Adjust width to 100% for iframe */
max-width: 380px; /* Adjust max-width if necessary */
margin: 10px auto; /* Reduced vertical margin */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.server-info h2 {
color: #7289da;
font-size: 0.85rem; /* Slightly smaller font size for headings */
margin-bottom: 3px; /* Reduced margin-bottom */
}

.status .ready {
color: #43b581; /* Green for Ready */
}

.status .pending {
color: #ffbf00; /* Amber for Pending */
}

.status .stopped {
color: #f04747; /* Red for Stopped */
}

.details {
margin-bottom: 10px; /* Reduced margin between sections */
}

.details h2 {
margin-bottom: 3px; /* Reduced margin-bottom for headings */
}

.details code {
display: block;
background-color: #2c2f33;
padding: 3px; /* Reduced padding for code blocks */
margin-top: 3px; /* Reduced margin-top */
border-radius: 4px;
font-family: 'Courier New', Courier, monospace;
cursor: pointer;
}

.flex-container {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px; /* Reduced margin between flex-items */
}

.flex-item {
flex: 1;
padding: 0 5px; /* Maintain padding for separation */
text-align: left;
}

.flex-item h2 {
margin-bottom: 3px; /* Reduced margin-bottom for headings */
}

.flex-item p {
margin-top: 0;
margin-bottom: 0;
}

ol {
padding-left: 15px; /* Reduced padding-left for ordered lists */
margin-top: 0; /* Removed default top margin for ordered lists */
}

a {
color: #00b0f4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* If you find the layout still too wide for some iframes, consider adding a custom media query to adjust the max-width at that specific iframe width */
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ A full featured Discord bot plugin for [AMP by Cubecoders](https://cubecoders.co

**If you appreciate my work on this plugin, feel free to buy me a beer to keep me fueled up [here](https://www.paypal.com/donate/?business=JAYRTVPHT5CG8&no_recurring=0&currency_code=GBP)**

Discord Info Panel:

![Bot Info Example](https://github.com/winglessraven/AMP-Discord-Bot/assets/4540397/065570c1-df8b-45b0-bba8-0879d0c38795 "Bot Info Example")

Web Panel:

![Web Panel Example](https://github.com/winglessraven/AMP-Discord-Bot/assets/4540397/0c5a46d7-5d10-4b6d-a7e1-866e38df70ab)


# Command Reference
| Command | Description |
| ------------- | ------------------------------ |
Expand Down Expand Up @@ -107,6 +114,7 @@ Before the plugin can be used you need to configure AMP in a specific way. **NO
|Send Chat from Discord to Server|Attempt to send chat messages from Discord chat channel to the server (currently only supported for Minecraft)|
|Send Console to Discord|Send console output to a Discord channel|
|Console Discord Channel|Discord channel name to send console output to (if enabled)|
|Enable Web Panel|Enable the web panel. This will create a html file in a similar format to the Discord info panel for website embeds. Additional steps are required to map the html file to make it accessible. See the [Wiki](https://github.com/winglessraven/AMP-Discord-Bot/wiki/Configure-the-Web-Panel)|

## AMP Discord Bot Colours
The `Discord Bot Colours` section give you the ability to change the colour of your embedded messages in Discord. For each option you want to change insert the hex colour code required.
Expand Down

0 comments on commit 3d927df

Please sign in to comment.