Files
spotlightcam/backend/scripts/test-bot.js

312 lines
8.6 KiB
JavaScript
Raw Normal View History

#!/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 <email> --password <password> --slug <event-slug>');
console.log('\nOptions:');
console.log(' --interval <seconds> 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);
});