@zerospacegg/vynthra
Version:
Discord bot for ZeroSpace.gg data
191 lines ⢠7.86 kB
JavaScript
const Grell = require("@zerospacegg/iolin/zerospace/faction/grell");
const Legion = require("@zerospacegg/iolin/zerospace/faction/legion");
const Protectorate = require("@zerospacegg/iolin/zerospace/faction/protectorate");
// oo-ascii-tree will be required dynamically inside functions
/**
* Split a message into chunks that fit within Discord's character limit
*/
function splitMessage(message, maxLength) {
if (message.length <= maxLength) {
return [message];
}
const chunks = [];
let currentChunk = "";
const lines = message.split("\n");
for (const line of lines) {
if (currentChunk.length + line.length + 1 > maxLength) {
if (currentChunk.trim()) {
chunks.push(currentChunk.trim());
currentChunk = "";
}
if (line.length > maxLength) {
// Split long lines at word boundaries
const words = line.split(" ");
let currentLine = "";
for (const word of words) {
if (currentLine.length + word.length + 1 > maxLength) {
if (currentLine.trim()) {
chunks.push(currentLine.trim());
currentLine = word;
}
else {
// Single word too long, just truncate it
chunks.push(word.substring(0, maxLength - 3) + "...");
currentLine = "";
}
}
else {
currentLine += (currentLine ? " " : "") + word;
}
}
if (currentLine.trim()) {
currentChunk = currentLine;
}
}
else {
currentChunk = line;
}
}
else {
currentChunk += (currentChunk ? "\n" : "") + line;
}
}
if (currentChunk.trim()) {
chunks.push(currentChunk.trim());
}
return chunks;
}
/**
* Generate tech tree for a faction using the beautiful ASCII tree format
*/
function generateTechTree(factionName) {
try {
// Dynamic require for oo-ascii-tree
const { AsciiTree } = require("oo-ascii-tree");
// Get the faction instance (handle both ESM and CJS import styles)
let faction;
switch (factionName.toLowerCase()) {
case "grell":
const GrellClass = Grell.default || Grell;
faction = new GrellClass();
break;
case "protectorate":
const ProtectorateClass = Protectorate.default || Protectorate;
faction = new ProtectorateClass();
break;
case "legion":
const LegionClass = Legion.default || Legion;
faction = new LegionClass();
break;
default:
return `ā ERROR: Faction '${factionName}' not found. Available: grell, protectorate, legion`;
}
// Get the tech tree data
const techTreeData = faction.techTree;
if (!techTreeData || !techTreeData.tree) {
return `ā ERROR: No tech tree data found for faction '${factionName}'`;
}
// Helper to convert entity name to display format
function getDisplayName(node) {
if (!node || !node.name)
return "Unknown";
return node.name;
}
// Convert faction tech tree to oo-ascii-tree format
function buildAsciiTree(node, visited = new Set()) {
if (!node || visited.has(node.id)) {
return null; // Avoid infinite loops
}
const currentVisited = new Set(visited);
currentVisited.add(node.id);
const asciiNode = new AsciiTree(getDisplayName(node));
if (node.children && node.children.length > 0) {
// Use iolin's perfect sorting as-is (upgrades already filtered out at source)
for (const child of node.children) {
const childNode = buildAsciiTree(child, currentVisited);
if (childNode) {
asciiNode.add(childNode);
}
}
}
return asciiNode;
}
// Get faction emoji
const factionEmojis = {
grell: "š§¬",
protectorate: "šļø",
legion: "š„",
};
const emoji = factionEmojis[factionName.toLowerCase()] || "ā”";
const factionTitle = factionName.toUpperCase();
// Generate the tree with proper markdown structure
const factionUrl = `${faction.zsggPath}?tab=techTree`;
// Build the ASCII tree from the root
const rootTree = buildAsciiTree(techTreeData.tree);
let output = `## ${emoji} ${factionTitle} TECH TREE ${emoji}\n\n\`\`\`\n`;
if (rootTree) {
output += rootTree.toString();
}
else {
output += "No tech tree structure found.";
}
output += `\n\`\`\`\n\nš **View detailed tech tree on ZeroSpace.gg:** <${factionUrl}>`;
return output;
}
catch (error) {
console.error("Tech tree generation error:", error);
return `ā ERROR: Failed to generate tech tree for ${factionName}. ${error instanceof Error ? error.message : "Unknown error"}`;
}
}
const techTreeSubcommand = {
name: "tech-tree",
description: "Show tech tree progression for a faction",
builder: subcommand => subcommand
.setName("tech-tree")
.setDescription("Show tech tree progression for a faction")
.addStringOption(option => option
.setName("faction")
.setDescription("Faction to show tech tree for")
.setRequired(true)
.addChoices({ name: "𧬠Grell", value: "grell" }, { name: "šļø Protectorate", value: "protectorate" }, { name: "š„ Legion", value: "legion" }))
.addBooleanOption(option => option.setName("public").setDescription("Show results to everyone (default: private)").setRequired(false)),
async execute(interaction) {
const faction = interaction.options.getString("faction", true);
const isPublic = interaction.options.getBoolean("public") ?? false;
try {
// Defer reply
await interaction.deferReply({ ephemeral: !isPublic });
// Generate tech tree
const techTreeResult = generateTechTree(faction);
// Check if generation failed
if (techTreeResult.startsWith("ā")) {
await interaction.editReply(techTreeResult);
return;
}
// Discord has a 2000 character limit
if (techTreeResult.length <= 2000) {
await interaction.editReply(techTreeResult);
}
else {
// Split the message
const chunks = splitMessage(techTreeResult, 2000);
await interaction.editReply(chunks[0]);
for (let i = 1; i < chunks.length; i++) {
await interaction.followUp({
content: chunks[i],
ephemeral: !isPublic,
});
}
}
}
catch (error) {
console.error("Tech tree command failed:", error);
await interaction.editReply({
content: `ā Error generating tech tree: ${error instanceof Error ? error.message : "Unknown error"}`,
});
}
},
};
//# sourceMappingURL=tech-tree.js.map
exports.generateTechTree = generateTechTree;
exports.techTreeSubcommand = techTreeSubcommand;