mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
risk of fling 2
This commit is contained in:
@@ -2,16 +2,40 @@
|
|||||||
// Purpose: Handles replay capture requests from Discord.
|
// Purpose: Handles replay capture requests from Discord.
|
||||||
// Scope: Resolves source selectors, enforces cooldowns, and replies with replay video attachment.
|
// Scope: Resolves source selectors, enforces cooldowns, and replies with replay video attachment.
|
||||||
const { AttachmentBuilder } = require('discord.js');
|
const { AttachmentBuilder } = require('discord.js');
|
||||||
|
const io = require('../../../globals/io');
|
||||||
|
|
||||||
function createReplayCommand({ getMode, MODES, tryTriggerReplay, getReplaySources, getDefaultDiscordSources, validateSources, buildReplayVideo, sanitizeMentions }) {
|
function createReplayCommand({ getMode, MODES, tryTriggerReplay, getReplaySources, getDefaultDiscordSources, validateSources, buildReplayVideo, sanitizeMentions, getActiveDrivers, getNickname, rovers }) {
|
||||||
function normalizeReplayQuery(input) { return String(input || '').trim().toLowerCase(); }
|
function normalizeReplayQuery(input) { return String(input || '').trim().toLowerCase(); }
|
||||||
function sanitizeReplayTitleForFilename(title) {
|
function sanitizeReplayTitleForFilename(title) {
|
||||||
const cleaned = String(title || '').replace(/[\\/:*?"<>|]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 96);
|
const cleaned = String(title || '').replace(/[\\/:*?"<>|]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 96);
|
||||||
return cleaned || 'replay';
|
return cleaned || 'replay';
|
||||||
}
|
}
|
||||||
function buildDefaultReplayTitle(requester, sources = []) {
|
function buildDefaultReplayTitle(requester, sources = []) {
|
||||||
const roverSource = sources.find((entry) => entry?.type === 'rover');
|
const requesterLabel = String(requester || 'Someone').trim() || 'Someone';
|
||||||
return `${String(requester || 'Someone').trim() || 'Someone'} driving ${roverSource?.label || roverSource?.id || 'a rover'}`;
|
const labels = (Array.isArray(sources) ? sources : [])
|
||||||
|
.map((entry) => String(entry?.label || entry?.id || '').trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (!labels.length) return `${requesterLabel} replay`;
|
||||||
|
if (labels.length === 1) return `${requesterLabel} replay: ${labels[0]}`;
|
||||||
|
return `${requesterLabel} replay: ${labels.slice(0, 3).join(' + ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDriverSummary(sources = []) {
|
||||||
|
const activeDrivers = getActiveDrivers();
|
||||||
|
const roverSources = (Array.isArray(sources) ? sources : []).filter((entry) => entry?.type === 'rover');
|
||||||
|
const lines = [];
|
||||||
|
roverSources.forEach((source) => {
|
||||||
|
const roverId = String(source.id);
|
||||||
|
const socketId = activeDrivers?.[roverId];
|
||||||
|
if (!socketId) return;
|
||||||
|
const socket = io.sockets.sockets.get(socketId);
|
||||||
|
const nickname = getNickname(socket) || socket?.data?.user?.username || socketId;
|
||||||
|
const record = rovers.get(roverId);
|
||||||
|
const roverName = record?.meta?.name || source?.label || roverId;
|
||||||
|
lines.push(`${nickname} → ${roverName}`);
|
||||||
|
});
|
||||||
|
if (!lines.length) return '';
|
||||||
|
return `Drivers: ${lines.join(' | ')}`;
|
||||||
}
|
}
|
||||||
function resolveReplaySources(query) {
|
function resolveReplaySources(query) {
|
||||||
const cleaned = normalizeReplayQuery(query);
|
const cleaned = normalizeReplayQuery(query);
|
||||||
@@ -48,7 +72,9 @@ function createReplayCommand({ getMode, MODES, tryTriggerReplay, getReplaySource
|
|||||||
try {
|
try {
|
||||||
const { buffer } = await buildReplayVideo({ sources: resolved.sources || [], title, requester });
|
const { buffer } = await buildReplayVideo({ sources: resolved.sources || [], title, requester });
|
||||||
const attachment = new AttachmentBuilder(buffer, { name: `${sanitizeReplayTitleForFilename(title)}.mp4` });
|
const attachment = new AttachmentBuilder(buffer, { name: `${sanitizeReplayTitleForFilename(title)}.mp4` });
|
||||||
await message.reply({ content: sanitizeMentions(`**${title}**`), files: [attachment], allowedMentions: { parse: [], repliedUser: false } });
|
const summary = buildDriverSummary(resolved.sources || []);
|
||||||
|
const body = [ `**${title}**`, summary ].filter(Boolean).join('\n');
|
||||||
|
await message.reply({ content: sanitizeMentions(body), files: [attachment], allowedMentions: { parse: [], repliedUser: false } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await message.reply({ content: sanitizeMentions(`Replay failed: ${err.message}`), allowedMentions: { parse: [], repliedUser: false } });
|
await message.reply({ content: sanitizeMentions(`Replay failed: ${err.message}`), allowedMentions: { parse: [], repliedUser: false } });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
// Purpose: Handles event-bus announcements to Discord channels.
|
// Purpose: Handles event-bus announcements to Discord channels.
|
||||||
// Scope: Processes supported event types and posts formatted messages/embeds.
|
// Scope: Processes supported event types and posts formatted messages/embeds.
|
||||||
const { EmbedBuilder, AttachmentBuilder } = require('discord.js');
|
const { EmbedBuilder, AttachmentBuilder } = require('discord.js');
|
||||||
|
const io = require('../../../globals/io');
|
||||||
const { buildBatteryStatusEmbed, buildBatteryCaption } = require('../batteryEmbeds');
|
const { buildBatteryStatusEmbed, buildBatteryCaption } = require('../batteryEmbeds');
|
||||||
|
|
||||||
function createBusEventHandler(deps) {
|
function createBusEventHandler(deps) {
|
||||||
const { logger, discordConfig, MODES, roverManager, rovers, schedulePresenceRotation, formatDuration, sendToChannel, buildReplayVideo } = deps;
|
const { logger, discordConfig, MODES, roverManager, rovers, schedulePresenceRotation, formatDuration, sendToChannel, buildReplayVideo, getActiveDrivers, getNickname } = deps;
|
||||||
const ADMIN_ALERT_EVENT_TYPES = new Set(['rover.online', 'rover.offline', 'rover.dockGuard', 'battery.warn', 'battery.urgent', 'battery.docked', 'battery.undocked', 'battery.charging.start', 'battery.charging.stop', 'battery.locked', 'battery.unlocked']);
|
const ADMIN_ALERT_EVENT_TYPES = new Set(['rover.online', 'rover.offline', 'rover.dockGuard', 'battery.warn', 'battery.urgent', 'battery.docked', 'battery.undocked', 'battery.charging.start', 'battery.charging.stop', 'battery.locked', 'battery.unlocked']);
|
||||||
let skippedFirstModeAnnouncement = false;
|
let skippedFirstModeAnnouncement = false;
|
||||||
|
|
||||||
@@ -31,12 +32,42 @@ function createBusEventHandler(deps) {
|
|||||||
return cleaned || 'replay';
|
return cleaned || 'replay';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildDefaultReplayTitle(requester, sources = []) {
|
||||||
|
const requesterLabel = String(requester || 'Someone').trim() || 'Someone';
|
||||||
|
const labels = (Array.isArray(sources) ? sources : [])
|
||||||
|
.map((entry) => String(entry?.label || entry?.id || '').trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (!labels.length) return `${requesterLabel} replay`;
|
||||||
|
if (labels.length === 1) return `${requesterLabel} replay: ${labels[0]}`;
|
||||||
|
return `${requesterLabel} replay: ${labels.slice(0, 3).join(' + ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDriverSummary(sources = []) {
|
||||||
|
const activeDrivers = getActiveDrivers();
|
||||||
|
const roverSources = (Array.isArray(sources) ? sources : []).filter((entry) => entry?.type === 'rover');
|
||||||
|
const lines = [];
|
||||||
|
roverSources.forEach((source) => {
|
||||||
|
const roverId = String(source.id);
|
||||||
|
const socketId = activeDrivers?.[roverId];
|
||||||
|
if (!socketId) return;
|
||||||
|
const socket = io.sockets.sockets.get(socketId);
|
||||||
|
const nickname = getNickname(socket) || socket?.data?.user?.username || socketId;
|
||||||
|
const record = rovers.get(roverId);
|
||||||
|
const roverName = record?.meta?.name || source?.label || roverId;
|
||||||
|
lines.push(`${nickname} → ${roverName}`);
|
||||||
|
});
|
||||||
|
if (!lines.length) return '';
|
||||||
|
return `Drivers: ${lines.join(' | ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function sendReplayToChannel(channelId, requester, sources = [], explicitTitle = '', includeSidebar = true) {
|
async function sendReplayToChannel(channelId, requester, sources = [], explicitTitle = '', includeSidebar = true) {
|
||||||
if (!channelId) throw new Error('Replay channel not configured');
|
if (!channelId) throw new Error('Replay channel not configured');
|
||||||
const resolvedTitle = String(explicitTitle || '').trim() || `${String(requester || 'Someone').trim() || 'Someone'} replay`;
|
const resolvedTitle = String(explicitTitle || '').trim() || buildDefaultReplayTitle(requester, sources);
|
||||||
const { buffer } = await buildReplayVideo({ sources, title: resolvedTitle, requester, includeSidebar });
|
const { buffer } = await buildReplayVideo({ sources, title: resolvedTitle, requester, includeSidebar });
|
||||||
const attachment = new AttachmentBuilder(buffer, { name: `${sanitizeReplayTitleForFilename(resolvedTitle)}.mp4` });
|
const attachment = new AttachmentBuilder(buffer, { name: `${sanitizeReplayTitleForFilename(resolvedTitle)}.mp4` });
|
||||||
await sendToChannel(channelId, `**${resolvedTitle}**`, { files: [attachment] }, { parse: [] });
|
const summary = buildDriverSummary(sources);
|
||||||
|
const body = [ `**${resolvedTitle}**`, summary ].filter(Boolean).join('\n');
|
||||||
|
await sendToChannel(channelId, body, { files: [attachment] }, { parse: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReplayRequested(event) {
|
function handleReplayRequested(event) {
|
||||||
|
|||||||
Reference in New Issue
Block a user