move secrets to env variables add palworld commands
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 37s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 37s
This commit is contained in:
250
Commands/PalworldCommands.cs
Normal file
250
Commands/PalworldCommands.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Discord;
|
||||
using Discord.Interactions;
|
||||
using TentacleBot.Services;
|
||||
|
||||
namespace TentacleBot.Commands;
|
||||
|
||||
[Group("palworld", "Palworld server status commands")]
|
||||
public class PalworldCommands : InteractionModuleBase<SocketInteractionContext>
|
||||
{
|
||||
private readonly PalworldApiService _palworldApi;
|
||||
|
||||
public PalworldCommands(PalworldApiService palworldApi)
|
||||
{
|
||||
_palworldApi = palworldApi;
|
||||
}
|
||||
|
||||
[SlashCommand("status", "Show a combined server status dashboard")]
|
||||
public async Task HandleStatusAsync()
|
||||
{
|
||||
await DeferAsync();
|
||||
|
||||
var infoTask = _palworldApi.GetInfoAsync();
|
||||
var metricsTask = _palworldApi.GetMetricsAsync();
|
||||
var playersTask = _palworldApi.GetPlayersAsync();
|
||||
|
||||
var info = await infoTask;
|
||||
var metrics = await metricsTask;
|
||||
var players = await playersTask;
|
||||
|
||||
if (info is null && metrics is null && players is null)
|
||||
{
|
||||
await FollowupAsync(embed: new EmbedBuilder()
|
||||
.WithTitle("Palworld Server Status")
|
||||
.WithDescription("Failed to connect to the Palworld server. Check that the server is running and the REST API is enabled.")
|
||||
.WithColor(Color.Red)
|
||||
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||
.Build());
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Palworld Server Status")
|
||||
.WithColor(Color.DarkGreen)
|
||||
.WithTimestamp(DateTimeOffset.UtcNow);
|
||||
|
||||
if (info is not null)
|
||||
{
|
||||
var root = info.RootElement;
|
||||
var name = GetString(root, "servername");
|
||||
var version = GetString(root, "version");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
embed.AddField("Server Name", name, true);
|
||||
if (!string.IsNullOrWhiteSpace(version))
|
||||
embed.AddField("Version", version, true);
|
||||
}
|
||||
|
||||
if (metrics is not null)
|
||||
{
|
||||
var root = metrics.RootElement;
|
||||
embed.AddField("Players", $"{GetInt(root, "currentplayernum")} / {GetInt(root, "maxplayernum")}", true);
|
||||
embed.AddField("FPS", $"{GetDouble(root, "serverfps")}", true);
|
||||
|
||||
var uptime = GetInt(root, "uptime");
|
||||
embed.AddField("Uptime", FormatUptime(uptime), true);
|
||||
|
||||
var days = GetInt(root, "days");
|
||||
embed.AddField("In-Game Days", $"{days}", true);
|
||||
|
||||
var camps = GetInt(root, "basecampnum");
|
||||
embed.AddField("Base Camps", $"{camps}", true);
|
||||
}
|
||||
|
||||
if (players is not null)
|
||||
{
|
||||
var playerList = players.RootElement.GetProperty("players");
|
||||
var count = playerList.GetArrayLength();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var limit = Math.Min(count, 15);
|
||||
for (var i = 0; i < limit; i++)
|
||||
{
|
||||
var p = playerList[i];
|
||||
var name = GetString(p, "name");
|
||||
var level = GetInt(p, "level");
|
||||
sb.AppendLine($"**{name}** (Lv.{level})");
|
||||
}
|
||||
if (count > 15)
|
||||
sb.AppendLine($"*...and {count - 15} more*");
|
||||
embed.AddField($"Online Players ({count})", sb.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.AddField("Online Players", "None");
|
||||
}
|
||||
}
|
||||
|
||||
await FollowupAsync(embed: embed.Build());
|
||||
}
|
||||
|
||||
[SlashCommand("info", "Get Palworld server information")]
|
||||
public async Task HandleInfoAsync()
|
||||
{
|
||||
await DeferAsync();
|
||||
|
||||
var data = await _palworldApi.GetInfoAsync();
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
await FollowupAsync(embed: ErrorEmbed("info"));
|
||||
return;
|
||||
}
|
||||
|
||||
var root = data.RootElement;
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Server Info")
|
||||
.WithColor(Color.Blue)
|
||||
.AddField("Server Name", GetString(root, "servername") ?? "N/A", true)
|
||||
.AddField("Version", GetString(root, "version") ?? "N/A", true)
|
||||
.AddField("Description", GetString(root, "description") ?? "N/A")
|
||||
.AddField("World GUID", GetString(root, "worldguid") ?? "N/A")
|
||||
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||
.Build();
|
||||
|
||||
await FollowupAsync(embed: embed);
|
||||
}
|
||||
|
||||
[SlashCommand("players", "Get the list of online players")]
|
||||
public async Task HandlePlayersAsync()
|
||||
{
|
||||
await DeferAsync();
|
||||
|
||||
var data = await _palworldApi.GetPlayersAsync();
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
await FollowupAsync(embed: ErrorEmbed("players"));
|
||||
return;
|
||||
}
|
||||
|
||||
var players = data.RootElement.GetProperty("players");
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Online Players")
|
||||
.WithColor(Color.Green)
|
||||
.WithTimestamp(DateTimeOffset.UtcNow);
|
||||
|
||||
var count = players.GetArrayLength();
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
embed.WithDescription("No players are currently online.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var limit = Math.Min(count, 20);
|
||||
for (var i = 0; i < limit; i++)
|
||||
{
|
||||
var p = players[i];
|
||||
var name = GetString(p, "name");
|
||||
var level = GetInt(p, "level");
|
||||
var ping = GetDouble(p, "ping");
|
||||
sb.AppendLine($"{i + 1}. **{name}** — Lv.{level} — {ping:F0}ms ping");
|
||||
}
|
||||
if (count > 20)
|
||||
sb.AppendLine($"*...and {count - 20} more*");
|
||||
embed.WithDescription(sb.ToString());
|
||||
}
|
||||
|
||||
await FollowupAsync(embed: embed.Build());
|
||||
}
|
||||
|
||||
[SlashCommand("metrics", "Get Palworld server performance metrics")]
|
||||
public async Task HandleMetricsAsync()
|
||||
{
|
||||
await DeferAsync();
|
||||
|
||||
var data = await _palworldApi.GetMetricsAsync();
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
await FollowupAsync(embed: ErrorEmbed("metrics"));
|
||||
return;
|
||||
}
|
||||
|
||||
var root = data.RootElement;
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Server Metrics")
|
||||
.WithColor(Color.Orange)
|
||||
.AddField("FPS", $"{GetDouble(root, "serverfps"):F1}", true)
|
||||
.AddField("Frame Time", $"{GetDouble(root, "serverframetime"):F2}ms", true)
|
||||
.AddField("Players", $"{GetInt(root, "currentplayernum")} / {GetInt(root, "maxplayernum")}", true)
|
||||
.AddField("Uptime", FormatUptime(GetInt(root, "uptime")), true)
|
||||
.AddField("In-Game Days", $"{GetInt(root, "days")}", true)
|
||||
.AddField("Base Camps", $"{GetInt(root, "basecampnum")}", true)
|
||||
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||
.Build();
|
||||
|
||||
await FollowupAsync(embed: embed);
|
||||
}
|
||||
|
||||
private static string FormatUptime(int seconds)
|
||||
{
|
||||
if (seconds <= 0) return "N/A";
|
||||
|
||||
var ts = TimeSpan.FromSeconds(seconds);
|
||||
var parts = new List<string>();
|
||||
if (ts.Days > 0) parts.Add($"{ts.Days}d");
|
||||
if (ts.Hours > 0) parts.Add($"{ts.Hours}h");
|
||||
if (ts.Minutes > 0) parts.Add($"{ts.Minutes}m");
|
||||
if (parts.Count == 0) parts.Add($"{ts.Seconds}s");
|
||||
return string.Join(" ", parts);
|
||||
}
|
||||
|
||||
private static Embed ErrorEmbed(string endpoint)
|
||||
{
|
||||
return new EmbedBuilder()
|
||||
.WithTitle($"Failed to fetch {endpoint}")
|
||||
.WithDescription("Could not connect to the Palworld server. Verify the server is running and REST API is enabled.")
|
||||
.WithColor(Color.Red)
|
||||
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private static string? GetString(JsonElement element, string property)
|
||||
{
|
||||
return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.String
|
||||
? prop.GetString()
|
||||
: null;
|
||||
}
|
||||
|
||||
private static int GetInt(JsonElement element, string property)
|
||||
{
|
||||
return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.Number
|
||||
? prop.GetInt32()
|
||||
: 0;
|
||||
}
|
||||
|
||||
private static double GetDouble(JsonElement element, string property)
|
||||
{
|
||||
return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.Number
|
||||
? prop.GetDouble()
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user