const { WebcastPushConnection } = require('tiktok-live-connector'); const mysql = require('mysql2/promise'); const [,, username, dbHost, dbUser, dbPass, dbName, userId] = process.argv; if (!username) { console.error('Usage: node tiktok_bridge.js '); process.exit(1); } async function start() { console.log(`[${new Date().toISOString()}] TikTok Bridge starting for @${username} (User ID: ${userId})`); let connection; try { connection = await mysql.createConnection({ host: dbHost || '127.0.0.1', user: dbUser || 'root', password: dbPass || '', database: dbName || 'app_db' }); console.log(`[${new Date().toISOString()}] Connected to database: ${dbName} at ${dbHost}`); } catch (err) { console.error(`[${new Date().toISOString()}] DB Connection failed:`, err.message); process.exit(1); } // Create a new connection instance let tiktokConnection = new WebcastPushConnection(username, { processInitialData: false, enableExtendedGiftInfo: true, requestPollingIntervalMs: 2000 }); tiktokConnection.connect().then(state => { console.log(`[${new Date().toISOString()}] Connected to TikTok Live @${username} (Room ID: ${state.roomId})`); }).catch(err => { console.error(`[${new Date().toISOString()}] TikTok Connection failed:`, err.message); console.error('Check if the username is correct and the user is currently LIVE.'); process.exit(1); }); tiktokConnection.on('chat', async (data) => { console.log(`[Chat] ${data.uniqueId}: ${data.comment}`); try { await connection.execute( 'INSERT INTO tiktok_history (username, comment_author, comment_text, user_id, created_at) VALUES (?, ?, ?, ?, NOW())', [username, data.uniqueId, data.comment, userId || null] ); } catch (err) { console.error('Error inserting chat:', err.message); } }); tiktokConnection.on('gift', async (data) => { if (data.giftType === 1 && !data.repeatEnd) { // Wait for repeatEnd for streaks return; } const giftMsg = `sent ${data.repeatCount}x ${data.giftName}!`; console.log(`[Gift] ${data.uniqueId}: ${giftMsg}`); try { await connection.execute( 'INSERT INTO tiktok_history (username, comment_author, comment_text, user_id, created_at) VALUES (?, ?, ?, ?, NOW())', [username, data.uniqueId, giftMsg, userId || null] ); } catch (err) { console.error('Error inserting gift:', err.message); } }); tiktokConnection.on('member', (data) => { console.log(`[Join] ${data.uniqueId} joined the stream`); }); tiktokConnection.on('disconnected', () => { console.log(`[${new Date().toISOString()}] TikTok connection disconnected`); process.exit(0); }); tiktokConnection.on('streamEnd', () => { console.log(`[${new Date().toISOString()}] Stream ended by host`); process.exit(0); }); tiktokConnection.on('error', (err) => { console.error(`[${new Date().toISOString()}] TikTok error:`, err.message); }); // Keep DB connection alive setInterval(async () => { try { await connection.query('SELECT 1'); } catch (err) { console.log(`[${new Date().toISOString()}] DB connection lost, reconnecting...`); try { connection = await mysql.createConnection({ host: dbHost, user: dbUser, password: dbPass, database: dbName }); } catch (reconnectErr) { console.error('Reconnection failed:', reconnectErr.message); } } }, 30000); } // Handle termination signals process.on('SIGINT', () => process.exit(0)); process.on('SIGTERM', () => process.exit(0)); start();