-
Notifications
You must be signed in to change notification settings - Fork 32
Closed
Description
require('dotenv').config();
const cloudscraper = require('cloudscraper');
const { KickClient } = require('@botk4cp3r/kick.js');
const initDatabase = require('./database');
const fs = require('fs');
const path = require('path');
// Bloqueo para evitar ejecución múltiple
const lockFile = path.join(__dirname, 'bot.lock');
try {
if (fs.existsSync(lockFile)) {
const lockContent = fs.readFileSync(lockFile, 'utf8');
console.log(`Script ya está ejecutándose (PID: ${lockContent}), saliendo...`);
process.exit(0);
}
fs.writeFileSync(lockFile, process.pid.toString());
} catch (error) {
console.error('Error al manejar lock file:', error.message);
process.exit(1);
}
// Limpiar lock file al salir
process.on('exit', () => {
try {
if (fs.existsSync(lockFile)) {
fs.unlinkSync(lockFile);
console.log('Lock file eliminado');
}
} catch (error) {
console.error('Error al eliminar lock file:', error.message);
}
});
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));
let giveawayActive = false;
let participants = [];
let endTime = null;
(async () => {
// Conectar a la base de datos
let db;
try {
db = await initDatabase();
console.log('Conectado a la base de datos');
} catch (error) {
console.error('Error al conectar a la base de datos:', error.message);
return;
}
// Verificar variables de entorno
const requiredEnvVars = ['KICK_CHANNEL', 'CLIENT_ID', 'CLIENT_SECRET', 'BROADCASTER_USER_ID'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Error: ${envVar} no está definido en .env`);
return;
}
}
// Obtener access_token con cloudscraper
let accessToken;
try {
const response = await cloudscraper({
method: 'POST',
url: 'https://id.kick.com/oauth/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET
}).toString(),
cloudflareMaxTimeout: 30000,
challengesToSolve: 3
});
const data = JSON.parse(response);
if (!data.access_token) {
throw new Error('No se recibió access_token');
}
accessToken = data.access_token;
console.log('Access token obtenido correctamente');
} catch (error) {
console.error('Error al obtener access_token:', error.message);
return;
}
// Obtener broadcaster_user_id
let broadcasterUserId = process.env.BROADCASTER_USER_ID;
const broadcasterUserIdNum = parseInt(broadcasterUserId, 10);
if (isNaN(broadcasterUserIdNum) || broadcasterUserIdNum <= 0) {
console.error('Error: BROADCASTER_USER_ID debe ser un número válido');
return;
}
console.log('Usando BROADCASTER_USER_ID de .env:', broadcasterUserIdNum);
// Crear instancia del cliente
const clientOptions = {
token: accessToken,
channel: process.env.KICK_CHANNEL,
broadcaster_user_id: broadcasterUserIdNum,
broadcaster_id: broadcasterUserId // Prueba ambos formatos
};
console.log('Inicializando KickClient con:', {
channel: clientOptions.channel,
broadcaster_user_id: clientOptions.broadcaster_user_id,
broadcaster_id: clientOptions.broadcaster_id
});
let client;
try {
client = new KickClient(clientOptions);
console.log('KickClient creado correctamente');
} catch (error) {
console.error('Error al crear KickClient:', error.message);
console.error('Parámetros enviados:', clientOptions);
return;
}
// Intentar inicializar el cliente para eventos de chat
try {
await client.subscribeToEvents(['chat.message.sent']);
console.log('Conectado al chat de Kick');
} catch (error) {
console.error('Error al inicializar el cliente:', error.message);
console.error('Parámetros enviados a KickClient:', clientOptions);
return;
}
// Escuchar mensajes del chat
client.on('chatMessage', async (message) => {
try {
const sender = message.sender.username;
const content = message.content;
const args = content.split(' ');
if (content.startsWith('!giveaway')) {
const subcommand = args[1]?.toLowerCase();
if (subcommand === 'start' && args[2]) {
if (!giveawayActive) {
const minutes = parseInt(args[2]);
if (isNaN(minutes) || minutes <= 0) {
await client.sendChatMessage({ content: 'Por favor, indica un número válido de minutos.' });
return;
}
giveawayActive = true;
participants = [];
endTime = new Date(Date.now() + minutes * 60 * 1000);
await db.query('INSERT INTO giveaways (active, end_time, participants) VALUES (?, ?, ?)', [
true,
endTime,
JSON.stringify(participants)
]);
await client.sendChatMessage({ content: `¡Sorteo iniciado! Dura ${minutes} minutos. Usa !giveaway join para participar.` });
setTimeout(async () => {
if (giveawayActive) {
await endGiveaway(db, client);
}
}, minutes * 60 * 1000);
} else {
await client.sendChatMessage({ content: 'Ya hay un sorteo activo.' });
}
}
if (subcommand === 'join') {
if (giveawayActive) {
if (!participants.includes(sender)) {
participants.push(sender);
await db.query('UPDATE giveaways SET participants = ? WHERE active = TRUE', [
JSON.stringify(participants)
]);
await client.sendChatMessage({ content: `${sender} ha entrado al sorteo.` });
} else {
await client.sendChatMessage({ content: `${sender}, ya estás en el sorteo.` });
}
} else {
await client.sendChatMessage({ content: 'No hay un sorteo activo.' });
}
}
if (subcommand === 'participants') {
if (giveawayActive) {
await client.sendChatMessage({ content: `Participantes: ${participants.join(', ') || 'Ninguno'}` });
} else {
await client.sendChatMessage({ content: 'No hay un sorteo activo.' });
}
}
if (subcommand === 'end') {
if (giveawayActive) {
await endGiveaway(db, client);
} else {
await client.sendChatMessage({ content: 'No hay un sorteo activo.' });
}
}
if (subcommand === 'enable') {
giveawayActive = true;
await db.query('UPDATE giveaways SET active = TRUE WHERE id = (SELECT MAX(id) FROM giveaways)');
await client.sendChatMessage({ content: 'Sorteos habilitados.' });
}
if (subcommand === 'disable') {
giveawayActive = false;
await db.query('UPDATE giveaways SET active = FALSE WHERE id = (SELECT MAX(id) FROM giveaways)');
await client.sendChatMessage({ content: 'Sorteos deshabilitados.' });
}
}
} catch (error) {
console.error('Error al procesar mensaje:', error.message);
}
});
// Manejar errores del cliente
client.on('error', (error) => {
console.error('Error en el cliente:', error.message);
});
// Manejar desconexión
client.on('close', async () => {
console.log('Desconectado del chat, intentando reconectar...');
try {
const tokenResponse = await cloudscraper({
method: 'POST',
url: 'https://id.kick.com/oauth/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET
}).toString(),
cloudflareMaxTimeout: 30000,
challengesToSolve: 3
});
const tokenData = JSON.parse(tokenResponse);
if (!tokenData.access_token) {
throw new Error('No se recibió access_token');
}
accessToken = tokenData.access_token;
client.token = accessToken;
await client.subscribeToEvents(['chat.message.sent']);
console.log('Reconectado al chat de Kick');
} catch (error) {
console.error('Error al reconectar:', error.message);
}
});
async function endGiveaway(db, client) {
if (participants.length > 0) {
const winner = participants[Math.floor(Math.random() * participants.length)];
await client.sendChatMessage({ content: `¡El sorteo ha terminado! El ganador es: ${winner}` });
} else {
await client.sendChatMessage({ content: 'El sorteo ha terminado, pero no hubo participantes.' });
}
giveawayActive = false;
participants = [];
endTime = null;
await db.query('UPDATE giveaways SET active = FALSE, participants = ? WHERE active = TRUE', [
JSON.stringify([])
]);
}
})();
===
KICK_CHANNEL=benzitczo
CLIENT_ID=
CLIENT_SECRET=
ACCESS_TOKEN=
BROADCASTER_USER_ID=
Oh sorry, that's bot hosting, this Pterodacty, for if I can, right? and how explain where is acces tokens?
Metadata
Metadata
Assignees
Labels
No labels