Files
spotlightcam/backend/scripts/test-bot.js
Radosław Gierwiało 671b16cb82 feat(backend): add test bot for automated event chat testing
Add test-bot.js script that simulates a user participating in event chat for testing purposes.

Features:
- Authenticates as user via API
- Checks in to event using QR code token
- Connects to Socket.IO and joins event room
- Sends random messages at configurable intervals
- Auto-accepts recording suggestions

Usage from container:
docker compose exec backend sh -c 'API_URL=http://localhost:3000 node scripts/test-bot.js --email user@example.com --password pass --slug event-slug --interval 10'
2025-11-29 19:00:43 +01:00

312 lines
8.6 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);
});