mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
community goal? never jnew her
This commit is contained in:
@@ -1,26 +1,26 @@
|
||||
// Discord Goal Command
|
||||
// Purpose: Handles community goal view/update/clear operations.
|
||||
// Purpose: Handles global objective view/update/clear operations.
|
||||
// Scope: Allows read by all and write by admins.
|
||||
function createGoalCommand({ getCommunityGoal, setCommunityGoal, clearCommunityGoal, isAdminUser, sanitizeMentions }) {
|
||||
function createGoalCommand({ getGlobalObjective, setGlobalObjective, clearGlobalObjective, isAdminUser, sanitizeMentions }) {
|
||||
return async function handleGoalCommand(message, tokens) {
|
||||
const query = tokens.join(' ').trim();
|
||||
const lower = query.toLowerCase();
|
||||
if (!query) {
|
||||
const goal = getCommunityGoal();
|
||||
await message.reply({ content: goal?.text ? `Community goal: ${sanitizeMentions(goal.text)}` : 'No community goal set.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
const goal = getGlobalObjective();
|
||||
await message.reply({ content: goal?.text ? `Global objective: ${sanitizeMentions(goal.text)}` : 'No global objective set.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
return;
|
||||
}
|
||||
if (!isAdminUser(message.author.id)) {
|
||||
await message.reply({ content: 'Only admins can update the community goal.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
await message.reply({ content: 'Only admins can update the global objective.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (lower === 'clear') {
|
||||
clearCommunityGoal({ by: message.author?.id || null });
|
||||
await message.reply({ content: 'Community goal cleared.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
clearGlobalObjective({ by: message.author?.id || null });
|
||||
await message.reply({ content: 'Global objective cleared.', allowedMentions: { parse: [], repliedUser: false } });
|
||||
} else {
|
||||
setCommunityGoal(query, { by: message.author?.id || null });
|
||||
await message.reply({ content: sanitizeMentions(`Community goal set: ${query}`), allowedMentions: { parse: [], repliedUser: false } });
|
||||
setGlobalObjective(query, { by: message.author?.id || null });
|
||||
await message.reply({ content: sanitizeMentions(`Global objective set: ${query}`), allowedMentions: { parse: [], repliedUser: false } });
|
||||
}
|
||||
} catch (err) {
|
||||
await message.reply({ content: sanitizeMentions(`Failed to update goal: ${err.message}`), allowedMentions: { parse: [], repliedUser: false } });
|
||||
|
||||
@@ -15,7 +15,7 @@ function formatHelp() {
|
||||
'`rs unlock <id>` — unlock a rover',
|
||||
'`rs mode <open|turns|admin|lockdown>` — change server mode',
|
||||
'`rs reason [text|clear]` — show or set admin mode reason',
|
||||
'`rs goal [text|clear]` — show or set community goal',
|
||||
'`rs goal [text|clear]` — show or set global objective',
|
||||
'`rs verify list|remove ...` — manage verified users',
|
||||
'`rs deter list|ban|unban ...` — manage deterred users',
|
||||
'`ts` — show time status',
|
||||
|
||||
@@ -16,7 +16,7 @@ const { sendExternalMessage, sendExternalTyping } = require('../chatService');
|
||||
const { buildReplayVideo, getReplaySources, getDefaultDiscordSources, validateSources, tryTriggerReplay } = require('../replayEngineV2');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { getCommunityGoal, setCommunityGoal, clearCommunityGoal } = require('../communityGoalService');
|
||||
const { getGlobalObjective, setGlobalObjective, clearGlobalObjective } = require('../globalObjectiveService');
|
||||
const { getAdminReason, setAdminReason, clearAdminReason } = require('../adminReasonService');
|
||||
const {
|
||||
getGuildConfig,
|
||||
@@ -108,7 +108,7 @@ const presence = createPresenceManager({
|
||||
client,
|
||||
logger,
|
||||
getMode,
|
||||
getCommunityGoal,
|
||||
getGlobalObjective,
|
||||
countReady,
|
||||
});
|
||||
|
||||
@@ -129,9 +129,9 @@ const commands = createCommandHandlers({
|
||||
getDefaultDiscordSources,
|
||||
validateSources,
|
||||
tryTriggerReplay,
|
||||
getCommunityGoal,
|
||||
setCommunityGoal,
|
||||
clearCommunityGoal,
|
||||
getGlobalObjective,
|
||||
setGlobalObjective,
|
||||
clearGlobalObjective,
|
||||
getAdminReason,
|
||||
setAdminReason,
|
||||
clearAdminReason,
|
||||
|
||||
@@ -98,8 +98,8 @@ function createBusEventHandler(deps) {
|
||||
}
|
||||
schedulePresenceRotation();
|
||||
break;
|
||||
case 'communityGoal.updated':
|
||||
announce({ channelId: channels.announcements, content: payload?.text ? `Community goal: ${payload.text}` : 'Community goal cleared.', color: 0x8bc34a, title: 'Community Goal', description: payload?.text || 'Community goal cleared.' });
|
||||
case 'globalObjective.updated':
|
||||
announce({ channelId: channels.announcements, content: payload?.text ? `Global objective: ${payload.text}` : 'Global objective cleared.', color: 0x8bc34a, title: 'Global Objective', description: payload?.text || 'Global objective cleared.' });
|
||||
schedulePresenceRotation();
|
||||
break;
|
||||
case 'rover.online':
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Discord Presence Module
|
||||
// Purpose: Owns rotating Discord presence text derived from rover readiness, mode, and community goals.
|
||||
// Purpose: Owns rotating Discord presence text derived from rover readiness, mode, and global objectives.
|
||||
// Scope: Handles presence update scheduling/state and exposes start/recompute controls for orchestration.
|
||||
const { ActivityType } = require('discord.js');
|
||||
|
||||
function createPresenceManager({ client, logger, getMode, getCommunityGoal, countReady }) {
|
||||
function createPresenceManager({ client, logger, getMode, getGlobalObjective, countReady }) {
|
||||
const PRESENCE_ROTATE_MS = 20000;
|
||||
let presenceInterval = null;
|
||||
let presenceShowGoal = false;
|
||||
let presenceShowObjective = false;
|
||||
|
||||
function truncatePresenceText(text, maxLength) {
|
||||
if (!text) return '';
|
||||
@@ -18,11 +18,11 @@ function createPresenceManager({ client, logger, getMode, getCommunityGoal, coun
|
||||
function buildPresenceName() {
|
||||
const { ready, total } = countReady();
|
||||
const mode = getMode();
|
||||
const goal = getCommunityGoal();
|
||||
const goalText = goal?.text ? String(goal.text).trim() : '';
|
||||
if (presenceShowGoal && goalText) {
|
||||
const trimmed = truncatePresenceText(goalText, 110);
|
||||
return `Goal: ${trimmed}`;
|
||||
const objective = getGlobalObjective();
|
||||
const objectiveText = objective?.text ? String(objective.text).trim() : '';
|
||||
if (presenceShowObjective && objectiveText) {
|
||||
const trimmed = truncatePresenceText(objectiveText, 110);
|
||||
return `Objective: ${trimmed}`;
|
||||
}
|
||||
return `${mode} · ${ready}/${total} Rovers Ready`;
|
||||
}
|
||||
@@ -44,16 +44,16 @@ function createPresenceManager({ client, logger, getMode, getCommunityGoal, coun
|
||||
clearInterval(presenceInterval);
|
||||
presenceInterval = null;
|
||||
}
|
||||
const goal = getCommunityGoal();
|
||||
if (!goal?.text) {
|
||||
presenceShowGoal = false;
|
||||
const objective = getGlobalObjective();
|
||||
if (!objective?.text) {
|
||||
presenceShowObjective = false;
|
||||
updatePresence();
|
||||
return;
|
||||
}
|
||||
presenceShowGoal = false;
|
||||
presenceShowObjective = false;
|
||||
updatePresence();
|
||||
presenceInterval = setInterval(() => {
|
||||
presenceShowGoal = !presenceShowGoal;
|
||||
presenceShowObjective = !presenceShowObjective;
|
||||
updatePresence();
|
||||
}, PRESENCE_ROTATE_MS);
|
||||
}
|
||||
|
||||
+27
-17
@@ -1,15 +1,16 @@
|
||||
// community Goal Service
|
||||
// Purpose: Defines the community Goal Service module and the helpers/state used by this service unit.
|
||||
// Global Objective Service
|
||||
// Purpose: Defines the global objective service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fs = require('fs');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('communityGoalService');
|
||||
const logger = require('../../globals/logger').child('globalObjectiveService');
|
||||
const { isAdmin } = require('../roleService');
|
||||
const { publishEvent } = require('../eventBus');
|
||||
const { resolveDataDir, resolveDataPath } = require('../../helpers/dataPaths');
|
||||
|
||||
const DATA_DIR = resolveDataDir();
|
||||
const STORE_PATH = resolveDataPath('community-goal.json');
|
||||
const STORE_PATH = resolveDataPath('global-objective.json');
|
||||
const LEGACY_STORE_PATH = resolveDataPath('community-goal.json');
|
||||
const MAX_GOAL_LENGTH = 240;
|
||||
|
||||
let cache = null;
|
||||
@@ -21,9 +22,18 @@ function loadStore() {
|
||||
cache = JSON.parse(raw);
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
logger.warn('Failed to load community goal', err.message);
|
||||
logger.warn('Failed to load global objective', err.message);
|
||||
} else {
|
||||
try {
|
||||
const legacyRaw = fs.readFileSync(LEGACY_STORE_PATH, 'utf8');
|
||||
cache = JSON.parse(legacyRaw);
|
||||
} catch (legacyErr) {
|
||||
if (legacyErr.code !== 'ENOENT') {
|
||||
logger.warn('Failed to load legacy global objective', legacyErr.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
cache = null;
|
||||
if (!cache) cache = null;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
@@ -39,11 +49,11 @@ function normalizeText(input) {
|
||||
return input.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
function getCommunityGoal() {
|
||||
function getGlobalObjective() {
|
||||
return loadStore();
|
||||
}
|
||||
|
||||
function setCommunityGoal(text, meta = {}) {
|
||||
function setGlobalObjective(text, meta = {}) {
|
||||
const clean = normalizeText(text);
|
||||
if (!clean) {
|
||||
throw new Error('Goal text required');
|
||||
@@ -57,23 +67,23 @@ function setCommunityGoal(text, meta = {}) {
|
||||
updatedBy: meta.by || null,
|
||||
};
|
||||
saveStore(payload);
|
||||
publishEvent({ source: 'communityGoal', type: 'communityGoal.updated', payload });
|
||||
publishEvent({ source: 'globalObjective', type: 'globalObjective.updated', payload });
|
||||
return payload;
|
||||
}
|
||||
|
||||
function clearCommunityGoal(meta = {}) {
|
||||
function clearGlobalObjective(meta = {}) {
|
||||
const payload = {
|
||||
text: null,
|
||||
updatedAt: Date.now(),
|
||||
updatedBy: meta.by || null,
|
||||
};
|
||||
saveStore(payload);
|
||||
publishEvent({ source: 'communityGoal', type: 'communityGoal.updated', payload });
|
||||
publishEvent({ source: 'globalObjective', type: 'globalObjective.updated', payload });
|
||||
return payload;
|
||||
}
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
socket.on('communityGoal:set', ({ text } = {}, cb = () => {}) => {
|
||||
socket.on('globalObjective:set', ({ text } = {}, cb = () => {}) => {
|
||||
if (!isAdmin(socket)) {
|
||||
cb({ error: 'Not authorized' });
|
||||
return;
|
||||
@@ -81,8 +91,8 @@ io.on('connection', (socket) => {
|
||||
try {
|
||||
const result =
|
||||
text == null || String(text).trim() === ''
|
||||
? clearCommunityGoal({ by: socket?.data?.user?.username || socket?.id })
|
||||
: setCommunityGoal(text, { by: socket?.data?.user?.username || socket?.id });
|
||||
? clearGlobalObjective({ by: socket?.data?.user?.username || socket?.id })
|
||||
: setGlobalObjective(text, { by: socket?.data?.user?.username || socket?.id });
|
||||
cb({ success: true, goal: result });
|
||||
} catch (err) {
|
||||
cb({ error: err.message });
|
||||
@@ -91,8 +101,8 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
getCommunityGoal,
|
||||
setCommunityGoal,
|
||||
clearCommunityGoal,
|
||||
getGlobalObjective,
|
||||
setGlobalObjective,
|
||||
clearGlobalObjective,
|
||||
MAX_GOAL_LENGTH,
|
||||
};
|
||||
@@ -26,7 +26,7 @@ const {
|
||||
} = require('../privateRoverAccessRequestService');
|
||||
const { getReplayState, replayEvents, getReplaySources } = require('../replayEngineV2');
|
||||
const { getHealthSnapshot } = require('../healthService');
|
||||
const { getCommunityGoal } = require('../communityGoalService');
|
||||
const { getGlobalObjective } = require('../globalObjectiveService');
|
||||
const { getAdminReason } = require('../adminReasonService');
|
||||
const { subscribe } = require('../eventBus');
|
||||
const { getSocketIp, isLocalNetwork } = require('../../helpers/ipResolver');
|
||||
@@ -105,7 +105,7 @@ function buildSession(socket) {
|
||||
replay: getReplayState(),
|
||||
replaySources: getReplaySources(socket),
|
||||
health: getHealthSnapshot(),
|
||||
communityGoal: getCommunityGoal(),
|
||||
globalObjective: getGlobalObjective(),
|
||||
adminReason: getAdminReason(),
|
||||
users,
|
||||
socials,
|
||||
@@ -308,8 +308,8 @@ verificationEvents.on('change', ({ socketId } = {}) => {
|
||||
syncAll();
|
||||
});
|
||||
|
||||
subscribe('communityGoal.updated', () => {
|
||||
logger.info('Community goal updated; syncing all clients');
|
||||
subscribe('globalObjective.updated', () => {
|
||||
logger.info('Global objective updated; syncing all clients');
|
||||
syncAll();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user