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'
312 lines
8.6 KiB
JavaScript
312 lines
8.6 KiB
JavaScript
#!/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);
|
||
});
|