This commit is contained in:
2026-02-22 20:27:09 -05:00
parent 7b326a112e
commit 92024c8a64
37 changed files with 6785 additions and 3344 deletions
+147
View File
@@ -0,0 +1,147 @@
'use strict';
const express = require('express');
const { CJbot } = require('../model/minecraft');
const ACTIVITY_PLUGINS = ['Swing', 'Craft', 'GuardianFarm', 'GoldFarm', 'AutoEat'];
function createRouter() {
const router = express.Router();
router.get('/api/activity', (req, res) => {
try {
const result = {};
for (const [name, bot] of Object.entries(CJbot.bots)) {
const plugins = {};
for (const pluginName of ACTIVITY_PLUGINS) {
const instance = bot.plunginsLoaded[pluginName];
if (!instance) continue;
const cls = CJbot.plungins[pluginName];
if (cls && typeof cls.getStatus === 'function') {
plugins[pluginName] = cls.getStatus(instance);
} else {
plugins[pluginName] = { active: true };
}
}
if (Object.keys(plugins).length > 0) {
result[name] = { connected: bot.isReady, plugins };
}
}
res.json({ bots: result });
} catch (error) {
console.error('API Error /api/activity:', error);
res.status(500).json({ error: error.message });
}
});
return router;
}
const webUI = {
tabId: 'activity',
tabLabel: 'Activity',
tabOrder: 20,
html: `
<div style="margin-bottom:8px"><span class="last-updated" id="ts-activity"></span></div>
<div id="activityArea">
<div style="padding:20px;color:#6b7280;text-align:center">Loading activity...</div>
</div>
`,
css: `
.activity-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:16px}
.activity-card{background:#111827;border:1px solid #374151;border-radius:8px;padding:16px;transition:border-color .2s}
.activity-card:hover{border-color:#60a5fa}
.activity-card h3{font-size:1em;color:#60a5fa;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.activity-plugin{background:#1f2937;border:1px solid #374151;border-radius:6px;padding:10px 14px;margin-bottom:8px}
.activity-plugin:last-child{margin-bottom:0}
.activity-plugin-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}
.activity-plugin-name{font-size:.9em;font-weight:600;color:#e5e7eb}
.activity-badge{padding:2px 8px;border-radius:10px;font-size:.75em;font-weight:600}
.activity-badge.active{background:#059669;color:#fff}
.activity-detail{font-size:.8em;color:#9ca3af;margin-top:4px}
.hunger-bar{display:flex;gap:2px;margin-top:4px}
.hunger-pip{width:12px;height:12px;border-radius:2px;background:#374151}
.hunger-pip.filled{background:#f59e0b}
.hunger-pip.low{background:#ef4444}
.activity-empty{padding:40px 20px;color:#6b7280;text-align:center;font-size:.9em}
`,
onTabActive: 'onActivityTabActive',
js: `
let activityInterval=null;
function onActivityTabActive() {
loadActivity();
if (!activityInterval) activityInterval = setInterval(() => { if (currentTab === 'activity') loadActivity(); }, 5000);
}
async function loadActivity() {
try {
const r = await fetch('/api/activity');
if (!r.ok) { document.getElementById('activityArea').innerHTML='<div class="activity-empty">Failed to load activity</div>'; return; }
const d = await r.json();
renderActivity(d.bots || {});
updateTimestamp('ts-activity');
} catch(e) {
document.getElementById('activityArea').innerHTML='<div class="activity-empty">Failed to load activity</div>';
}
}
function renderActivity(bots) {
const area = document.getElementById('activityArea');
const names = Object.keys(bots);
if (names.length === 0) {
area.innerHTML='<div class="activity-empty">No active automation plugins</div>';
return;
}
area.innerHTML = '<div class="activity-grid">' + names.map(name => {
const bot = bots[name];
const pluginHtml = Object.entries(bot.plugins).map(([pName, status]) => {
let detail = '';
// Special case: AutoEat gets a hunger bar
if (pName === 'AutoEat' && status.hunger != null) {
let hungerBar = '<div class="hunger-bar">';
for (let i = 0; i < 20; i++) {
const filled = i < status.hunger;
const low = status.hunger < (status.threshold || 0);
hungerBar += '<div class="hunger-pip' + (filled ? (low ? ' low' : ' filled') : '') + '"></div>';
}
hungerBar += '</div>';
detail += hungerBar;
}
// Render all status keys generically
const skipKeys = new Set(['active']);
for (const [key, val] of Object.entries(status)) {
if (skipKeys.has(key)) continue;
if (val === null || val === undefined) continue;
const label = fmtName(key.replace(/([A-Z])/g, '_$1').toLowerCase());
let display;
if (typeof val === 'boolean') display = val ? 'Yes' : 'No';
else if (Array.isArray(val)) display = val.map(fmtName).join(', ') || 'None';
else display = escHtml(String(val));
detail += '<div class="activity-detail">' + escHtml(label) + ': ' + display + '</div>';
}
if (!detail) detail = '<div class="activity-detail">Running</div>';
return '<div class="activity-plugin">' +
'<div class="activity-plugin-header">' +
'<span class="activity-plugin-name">' + escHtml(pName) + '</span>' +
'<span class="activity-badge active">Active</span>' +
'</div>' +
detail +
'</div>';
}).join('');
return '<div class="activity-card">' +
'<h3><span class="bot-status ' + (bot.connected ? 'online' : 'offline') + '"></span> ' + escHtml(name) + '</h3>' +
pluginHtml +
'</div>';
}).join('') + '</div>';
}
`,
};
module.exports = { name: 'Activity', createRouter, webUI };