here
This commit is contained in:
@@ -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 };
|
||||
Reference in New Issue
Block a user