|
|
|
|
|
const DubAPI = require('./index.js'); |
|
|
const AIHandler = require('./ai-handler.js'); |
|
|
const MemoryHandler = require('./memory-handler.js'); |
|
|
|
|
|
console.log('Starting DubAPI example...'); |
|
|
|
|
|
|
|
|
const username = 'kuber'; |
|
|
const password = 'cookTV12'; |
|
|
|
|
|
|
|
|
const roomName = 'nononono'; |
|
|
|
|
|
|
|
|
const config = { |
|
|
commandPrefix: '!', |
|
|
autoReconnect: true, |
|
|
reconnectInterval: 15000, |
|
|
greetUsers: true, |
|
|
autoUpvote: true, |
|
|
botName: 'Kuber', |
|
|
respondToMentions: true, |
|
|
useAI: true, |
|
|
geminiAPIKey: 'AIzaSyAQAw_EZv1kX_l24ViMGnPYGC3ExbIZFCU' |
|
|
}; |
|
|
|
|
|
console.log('Attempting to create DubAPI instance with username:', username); |
|
|
console.log('Connecting to room:', roomName); |
|
|
|
|
|
|
|
|
let memoryHandler = new MemoryHandler({ |
|
|
maxMessagesPerUser: 10, |
|
|
maxMemoryAge: 30 * 60 * 1000 |
|
|
}); |
|
|
|
|
|
|
|
|
let aiHandler = null; |
|
|
if (config.useAI && config.geminiAPIKey) { |
|
|
aiHandler = new AIHandler(config.geminiAPIKey, memoryHandler); |
|
|
console.log('AI handler initialized with Gemini API and conversation memory'); |
|
|
} |
|
|
|
|
|
new DubAPI({username: username, password: password}, function(err, bot) { |
|
|
if (err) return console.error('Error connecting:', err); |
|
|
|
|
|
console.log('Running DubAPI v' + bot.version); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function formatTime(seconds) { |
|
|
const mins = Math.floor(seconds / 60); |
|
|
const secs = Math.floor(seconds % 60); |
|
|
return `${mins}:${secs < 10 ? '0' + secs : secs}`; |
|
|
} |
|
|
|
|
|
|
|
|
function isStaff(user) { |
|
|
return user && bot.isStaff(user); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function handleCommand(data) { |
|
|
|
|
|
if (!data.message.startsWith(config.commandPrefix)) return; |
|
|
|
|
|
|
|
|
const args = data.message.slice(config.commandPrefix.length).trim().split(/\s+/); |
|
|
const command = args.shift().toLowerCase(); |
|
|
|
|
|
|
|
|
const user = bot.getUser(data.user.id); |
|
|
|
|
|
switch (command) { |
|
|
case 'ping': |
|
|
bot.sendChat('Pong! 🏓'); |
|
|
break; |
|
|
|
|
|
case 'memory': |
|
|
if (memoryHandler) { |
|
|
const stats = memoryHandler.getStats(); |
|
|
const now = Date.now(); |
|
|
const oldestTime = stats.oldestMessage ? new Date(stats.oldestMessage).toISOString().substring(11, 19) : 'none'; |
|
|
|
|
|
bot.sendChat(`Memory stats: ${stats.userCount} users, ${stats.totalMessages} messages, oldest from ${oldestTime}`); |
|
|
|
|
|
if (args[0] === 'clear' && isStaff(user)) { |
|
|
|
|
|
memoryHandler.memories.clear(); |
|
|
bot.sendChat('Memory cleared by ' + user.username); |
|
|
} |
|
|
} else { |
|
|
bot.sendChat('Memory system is not active.'); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'info': |
|
|
const currentMedia = bot.getMedia(); |
|
|
if (currentMedia) { |
|
|
bot.sendChat(`Current track: ${currentMedia.name} by ${currentMedia.artist || 'Unknown'}`); |
|
|
} else { |
|
|
bot.sendChat('No track is currently playing.'); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'time': |
|
|
const remaining = bot.getTimeRemaining(); |
|
|
const elapsed = bot.getTimeElapsed(); |
|
|
if (remaining >= 0) { |
|
|
bot.sendChat(`Time remaining: ${formatTime(remaining)} | Elapsed: ${formatTime(elapsed)}`); |
|
|
} else { |
|
|
bot.sendChat('No track is currently playing.'); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'skip': |
|
|
if (isStaff(user) && bot.hasPermission(user, 'skip')) { |
|
|
bot.moderateSkip(function(err) { |
|
|
if (err) return console.error('Error skipping track:', err); |
|
|
bot.sendChat('Track skipped by ' + user.username); |
|
|
}); |
|
|
} else { |
|
|
bot.sendChat('You need to be staff with skip permission to use this command.'); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'users': |
|
|
const users = bot.getUsers(); |
|
|
bot.sendChat(`There are ${users.length} users in the room.`); |
|
|
break; |
|
|
|
|
|
case 'staff': |
|
|
const staff = bot.getStaff(); |
|
|
bot.sendChat(`There are ${staff.length} staff members in the room.`); |
|
|
break; |
|
|
|
|
|
case 'help': |
|
|
bot.sendChat('Available commands: !ping, !info, !time, !users, !staff, !help'); |
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function connect() { |
|
|
console.log('Attempting to connect to room:', roomName); |
|
|
bot.connect(roomName); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bot.on('connected', function(name) { |
|
|
console.log('Connected to ' + name); |
|
|
console.log('Bot is ready to respond to commands. Use ' + config.commandPrefix + 'help for available commands'); |
|
|
}); |
|
|
|
|
|
bot.on('disconnected', function(name) { |
|
|
console.log('Disconnected from ' + name); |
|
|
|
|
|
if (config.autoReconnect) { |
|
|
console.log(`Attempting to reconnect in ${config.reconnectInterval / 1000} seconds...`); |
|
|
setTimeout(connect, config.reconnectInterval); |
|
|
} |
|
|
}); |
|
|
|
|
|
bot.on('error', function(err) { |
|
|
console.error('Error:', err); |
|
|
}); |
|
|
|
|
|
|
|
|
bot.on(bot.events.chatMessage, function(data) { |
|
|
console.log(data.user.username + ': ' + data.message); |
|
|
|
|
|
|
|
|
if (data.user.username !== config.botName && memoryHandler) { |
|
|
memoryHandler.addMessage(data.user.username, data.message, false); |
|
|
} |
|
|
|
|
|
|
|
|
handleCommand(data); |
|
|
|
|
|
|
|
|
if (config.respondToMentions && data.user.username !== config.botName) { |
|
|
|
|
|
const message = data.message; |
|
|
const lowerMessage = message.toLowerCase(); |
|
|
const botName = config.botName.toLowerCase(); |
|
|
|
|
|
|
|
|
const isMentioned = |
|
|
lowerMessage.includes('@' + botName) || |
|
|
lowerMessage.startsWith(botName) || |
|
|
lowerMessage.includes(' ' + botName + ' ') || |
|
|
lowerMessage.endsWith(' ' + botName); |
|
|
|
|
|
console.log('Checking for mentions in message:', message, 'Mentioned:', isMentioned); |
|
|
|
|
|
|
|
|
if (isMentioned) { |
|
|
console.log(`Bot was mentioned by ${data.user.username}`); |
|
|
|
|
|
|
|
|
let userMessage = data.message; |
|
|
const botNamePattern = new RegExp(`@?${config.botName}`, 'gi'); |
|
|
userMessage = userMessage.replace(botNamePattern, '').trim(); |
|
|
|
|
|
|
|
|
if (userMessage.toLowerCase().includes('what') && |
|
|
(userMessage.toLowerCase().includes('song') || |
|
|
userMessage.toLowerCase().includes('track') || |
|
|
userMessage.toLowerCase().includes('playing'))) { |
|
|
|
|
|
|
|
|
const currentMedia = bot.getMedia(); |
|
|
if (currentMedia) { |
|
|
const response = `@${data.user.username}, we're listening to ${currentMedia.name} by ${currentMedia.artist || 'Unknown'}.`; |
|
|
return bot.sendChat(response); |
|
|
} |
|
|
} |
|
|
|
|
|
if (userMessage.toLowerCase() === 'help' || userMessage.toLowerCase() === '?') { |
|
|
const response = `@${data.user.username}, try using ${config.commandPrefix}help to see my commands.`; |
|
|
return bot.sendChat(response); |
|
|
} |
|
|
|
|
|
|
|
|
if (config.useAI && aiHandler) { |
|
|
|
|
|
bot.sendChat(`/me is thinking...`); |
|
|
|
|
|
|
|
|
console.log(`Sending to AI: ${userMessage} (from: ${data.user.username})`); |
|
|
aiHandler.getAIResponse(userMessage, data.user.username) |
|
|
.then(aiResponse => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let firstMessage = `@${data.user.username}, `; |
|
|
|
|
|
|
|
|
const maxChatLength = 160; |
|
|
|
|
|
|
|
|
if (aiResponse.length > maxChatLength - firstMessage.length) { |
|
|
console.log(`Response too long (${aiResponse.length} chars), splitting into multiple messages`); |
|
|
|
|
|
|
|
|
let chunks = []; |
|
|
|
|
|
|
|
|
console.log('Full AI response:', aiResponse); |
|
|
|
|
|
|
|
|
|
|
|
const listRegex = /((?:\d+\.\s+|[•\*]\s+)[^\d•\*\n.]+)/g; |
|
|
const hasList = listRegex.test(aiResponse); |
|
|
|
|
|
|
|
|
if (hasList) { |
|
|
console.log('Detected list items in response, preserving list structure'); |
|
|
|
|
|
|
|
|
listRegex.lastIndex = 0; |
|
|
|
|
|
|
|
|
const listItems = []; |
|
|
let match; |
|
|
let lastIndex = 0; |
|
|
let preListText = ''; |
|
|
|
|
|
|
|
|
const firstListItem = aiResponse.search(/(\d+\.\s+|[•\*]\s+)/); |
|
|
if (firstListItem > 0) { |
|
|
preListText = aiResponse.substring(0, firstListItem); |
|
|
} |
|
|
|
|
|
|
|
|
while ((match = listRegex.exec(aiResponse)) !== null) { |
|
|
listItems.push(match[0]); |
|
|
lastIndex = match.index + match[0].length; |
|
|
} |
|
|
|
|
|
|
|
|
let postListText = ''; |
|
|
if (lastIndex < aiResponse.length) { |
|
|
postListText = aiResponse.substring(lastIndex); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let chunks = []; |
|
|
if (preListText.trim()) { |
|
|
chunks.push(firstMessage + preListText.trim()); |
|
|
} else if (firstMessage) { |
|
|
|
|
|
if (listItems.length > 0) { |
|
|
listItems[0] = firstMessage + listItems[0]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
listItems.forEach(item => { |
|
|
if (item.length > maxChatLength) { |
|
|
|
|
|
let remaining = item; |
|
|
while (remaining.length > 0) { |
|
|
const splitPoint = Math.min(remaining.length, maxChatLength); |
|
|
const chunk = remaining.substring(0, splitPoint); |
|
|
chunks.push(chunk); |
|
|
remaining = remaining.substring(splitPoint); |
|
|
} |
|
|
} else { |
|
|
chunks.push(item); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
if (postListText.trim()) { |
|
|
chunks.push(postListText.trim()); |
|
|
} |
|
|
} else { |
|
|
|
|
|
|
|
|
const sentences = aiResponse.split(/([.!?]+\s+)/).filter(part => part.trim().length > 0); |
|
|
|
|
|
let currentChunk = firstMessage; |
|
|
let isFirstChunk = true; |
|
|
|
|
|
|
|
|
for (let i = 0; i < sentences.length; i++) { |
|
|
const sentence = sentences[i]; |
|
|
|
|
|
|
|
|
if (currentChunk.length + sentence.length > maxChatLength) { |
|
|
|
|
|
if (sentence.length > maxChatLength) { |
|
|
|
|
|
if (currentChunk.length > (isFirstChunk ? firstMessage.length : 0)) { |
|
|
chunks.push(currentChunk); |
|
|
isFirstChunk = false; |
|
|
} |
|
|
|
|
|
|
|
|
let remaining = sentence; |
|
|
while (remaining.length > 0) { |
|
|
|
|
|
let splitPoint = maxChatLength; |
|
|
if (remaining.length > maxChatLength) { |
|
|
|
|
|
const lastSpace = remaining.substring(0, maxChatLength).lastIndexOf(' '); |
|
|
if (lastSpace > 0) { |
|
|
splitPoint = lastSpace + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
const chunk = remaining.substring(0, splitPoint); |
|
|
chunks.push(isFirstChunk ? (firstMessage + chunk) : chunk); |
|
|
isFirstChunk = false; |
|
|
remaining = remaining.substring(splitPoint); |
|
|
} |
|
|
|
|
|
|
|
|
currentChunk = ''; |
|
|
} else { |
|
|
|
|
|
chunks.push(currentChunk); |
|
|
currentChunk = sentence; |
|
|
isFirstChunk = false; |
|
|
} |
|
|
} else { |
|
|
|
|
|
if (currentChunk.length > (isFirstChunk ? firstMessage.length : 0)) { |
|
|
currentChunk += sentence; |
|
|
} else { |
|
|
currentChunk += (isFirstChunk ? '' : '') + sentence; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (currentChunk.length > 0) { |
|
|
chunks.push(currentChunk); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let sentChunks = 0; |
|
|
|
|
|
function sendNextChunk() { |
|
|
if (sentChunks < chunks.length) { |
|
|
bot.sendChat(chunks[sentChunks]); |
|
|
sentChunks++; |
|
|
|
|
|
if (sentChunks < chunks.length) { |
|
|
|
|
|
setTimeout(sendNextChunk, 800); |
|
|
} else { |
|
|
|
|
|
if (memoryHandler) { |
|
|
memoryHandler.addMessage(data.user.username, aiResponse, true); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
sendNextChunk(); |
|
|
} else { |
|
|
|
|
|
bot.sendChat(firstMessage + aiResponse); |
|
|
|
|
|
|
|
|
if (memoryHandler) { |
|
|
memoryHandler.addMessage(data.user.username, aiResponse, true); |
|
|
} |
|
|
} |
|
|
}) |
|
|
.catch(err => { |
|
|
console.error('AI Response Error:', err); |
|
|
bot.sendChat(`@${data.user.username}, sorry, my AI brain is having trouble right now. Try again later?`); |
|
|
}); |
|
|
} else { |
|
|
|
|
|
const responses = [ |
|
|
`Yes, @${data.user.username}? How can I help you?`, |
|
|
`I'm here, @${data.user.username}! Need something?`, |
|
|
`Hey @${data.user.username}, what's up?`, |
|
|
`@${data.user.username}, at your service! Try ${config.commandPrefix}help for commands.` |
|
|
]; |
|
|
const response = responses[Math.floor(Math.random() * responses.length)]; |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
bot.sendChat(response); |
|
|
}, 1000 + Math.random() * 1000); |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
bot.on(bot.events.userJoin, function(data) { |
|
|
console.log(`${data.user.username} joined the room`); |
|
|
|
|
|
|
|
|
if (config.greetUsers) { |
|
|
bot.sendChat(`Welcome to the room, @${data.user.username}!`); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
bot.on(bot.events.userLeave, function(data) { |
|
|
console.log(`${data.user.username} left the room`); |
|
|
}); |
|
|
|
|
|
|
|
|
bot.on(bot.events.roomPlaylistUpdate, function(data) { |
|
|
if (!data.media) return; |
|
|
|
|
|
console.log(`Now playing: ${data.media.name} by ${data.media.artist || 'Unknown'}`); |
|
|
|
|
|
|
|
|
if (config.autoUpvote) { |
|
|
setTimeout(() => { |
|
|
bot.updub(function(err) { |
|
|
if (err) console.error('Error upvoting:', err); |
|
|
else console.log('Auto-upvoted current song'); |
|
|
}); |
|
|
}, 5000); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
connect(); |
|
|
}); |
|
|
|