#!/usr/bin/env node /** * Test Bot for spotlight.cam * * Usage: * # From inside backend container: * docker compose exec backend sh -c 'API_URL=http://localhost:3000 node scripts/test-bot.js --email bot@example.com --password Bot123! --slug test-event-2024' * * # From host (if backend dependencies are installed): * cd backend && API_URL=http://localhost:3001 node scripts/test-bot.js --email bot@example.com --password Bot123! --slug test-event-2024 * * Options: * --email Bot user email * --password Bot user password * --slug Event slug to join * --interval Message interval in seconds (default: 15) * * Environment: * API_URL Backend API URL (default: http://localhost:3001) * Use http://localhost:3000 when running inside container */ const io = require('socket.io-client'); // Parse CLI arguments const args = process.argv.slice(2); const getArg = (flag) => { const index = args.indexOf(flag); return index !== -1 ? args[index + 1] : null; }; const email = getArg('--email'); const password = getArg('--password'); const slug = getArg('--slug'); const interval = parseInt(getArg('--interval')) || 15; if (!email || !password || !slug) { console.error('āŒ Missing required arguments!'); console.log('\nUsage:'); console.log(' node backend/scripts/test-bot.js --email --password --slug '); console.log('\nOptions:'); console.log(' --interval Message interval (default: 15)'); process.exit(1); } const API_URL = process.env.API_URL || 'http://localhost:3001'; // Random messages pool const RANDOM_MESSAGES = [ 'Hello everyone! šŸ‘‹', 'Great to be here!', 'Anyone else excited for this event?', 'This is going to be awesome!', 'Can\'t wait to dance! šŸ’ƒ', 'Hey there!', 'Looking forward to this!', 'Who else is competing?', 'Good luck everyone!', 'Let\'s do this! šŸ”„', 'Anyone want to practice later?', 'What division are you in?', 'First time at this event?', 'The energy here is amazing!', 'See you on the dance floor!', ]; let token = null; let socket = null; let userId = null; let eventId = null; let messageInterval = null; // Login to get JWT token async function login() { try { console.log(`šŸ” Logging in as ${email}...`); const response = await fetch(`${API_URL}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `HTTP error! status: ${response.status}`); } const responseData = await response.json(); token = responseData.data.token; userId = responseData.data.user.id; console.log(`āœ… Logged in! User ID: ${userId}`); console.log(` Token: ${token.substring(0, 20)}...`); return token; } catch (error) { console.error('āŒ Login failed:', error.message); process.exit(1); } } // Join event (check-in using token) async function joinEvent() { try { console.log(`šŸ“ Getting event details for: ${slug}...`); // Get event details which includes the check-in token const detailsResponse = await fetch(`${API_URL}/api/events/${slug}/details`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', } }); if (!detailsResponse.ok) { const errorData = await detailsResponse.json(); throw new Error(errorData.error || `HTTP error! status: ${detailsResponse.status}`); } const detailsData = await detailsResponse.json(); eventId = detailsData.data.event.id; const checkinToken = detailsData.data.checkin?.token; if (!checkinToken) { throw new Error('No check-in token available for this event'); } console.log(` Event ID: ${eventId}`); console.log(`šŸ“ Checking in to event...`); // Check in to the event using the token const checkinResponse = await fetch(`${API_URL}/api/events/checkin/${checkinToken}`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({}), }); if (!checkinResponse.ok) { const errorData = await checkinResponse.json(); // If already checked in, that's fine if (errorData.alreadyCheckedIn) { console.log(`ā„¹ļø Already checked in to this event`); return eventId; } throw new Error(errorData.error || `HTTP error! status: ${checkinResponse.status}`); } const checkinData = await checkinResponse.json(); if (checkinData.alreadyCheckedIn) { console.log(`ā„¹ļø Already checked in to this event`); } else { console.log(`āœ… Checked in to event!`); } return eventId; } catch (error) { console.error('āŒ Join event failed:', error.message); process.exit(1); } } // Connect to Socket.IO function connectSocket() { return new Promise((resolve, reject) => { console.log(`šŸ”Œ Connecting to Socket.IO...`); socket = io(API_URL, { auth: { token }, transports: ['websocket', 'polling'], }); socket.on('connect', () => { console.log(`āœ… Socket connected! Socket ID: ${socket.id}`); // Join event room console.log(`🚪 Joining event room for: ${slug}...`); socket.emit('join_event_room', { slug }); resolve(); }); socket.on('connect_error', (error) => { console.error('āŒ Socket connection error:', error.message); reject(error); }); socket.on('disconnect', (reason) => { console.log(`āš ļø Socket disconnected: ${reason}`); if (reason === 'io server disconnect') { // Server disconnected, try to reconnect socket.connect(); } }); // Listen for messages socket.on('new_message', (data) => { if (data.userId !== userId) { console.log(`šŸ’¬ Message from ${data.username}: ${data.content}`); } }); // Listen for recording suggestions socket.on('suggestion_created', async (data) => { console.log(`šŸ“¹ Recording suggestion received:`, data); // Auto-accept if it's for us (we're the recorder) if (data.suggestion && data.suggestion.recorderId === userId) { console.log(` Auto-accepting suggestion ${data.suggestion.id}...`); await acceptSuggestion(data.suggestion.id); } }); socket.on('error', (error) => { console.error('āŒ Socket error:', error); }); }); } // Send random message function sendRandomMessage() { const message = RANDOM_MESSAGES[Math.floor(Math.random() * RANDOM_MESSAGES.length)]; console.log(`šŸ“¤ Sending: "${message}"`); socket.emit('send_event_message', { content: message, }); } // Accept recording suggestion async function acceptSuggestion(suggestionId) { try { const response = await fetch( `${API_URL}/api/events/${slug}/suggestions/${suggestionId}/status`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ status: 'accepted' }), } ); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `HTTP error! status: ${response.status}`); } console.log(`āœ… Accepted suggestion ${suggestionId}`); } catch (error) { console.error(`āŒ Failed to accept suggestion:`, error.message); } } // Main function async function main() { console.log('šŸ¤– Test Bot Starting...\n'); console.log(` Email: ${email}`); console.log(` Event: ${slug}`); console.log(` Message Interval: ${interval}s\n`); // Login await login(); // Join event await joinEvent(); // Connect socket await connectSocket(); console.log(`\nāœ… Bot is ready!`); console.log(` Sending random messages every ${interval} seconds`); console.log(` Auto-accepting recording suggestions`); console.log(` Press Ctrl+C to stop\n`); // Start sending random messages messageInterval = setInterval(() => { sendRandomMessage(); }, interval * 1000); // Send first message immediately setTimeout(() => sendRandomMessage(), 2000); } // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n\nšŸ›‘ Shutting down bot...'); if (messageInterval) { clearInterval(messageInterval); } if (socket) { socket.disconnect(); } console.log('šŸ‘‹ Goodbye!'); process.exit(0); }); // Run main().catch((error) => { console.error('āŒ Fatal error:', error); process.exit(1); });