betty2 / example_copy_4.js
sdgsdggds's picture
Upload folder using huggingface_hub
e7c953d verified
// Enhanced example of using the DubAPI library with AI integration
const DubAPI = require('./index.js');
const AIHandler = require('./ai-handler.js');
const MemoryHandler = require('./memory-handler.js');
console.log('Starting DubAPI example...');
// Replace with your queup.net credentials
const username = 'kuber';
const password = 'cookTV12';
// Replace with the room you want to connect to
const roomName = 'nononono';
// Bot configuration
const config = {
commandPrefix: '!', // Prefix for commands
autoReconnect: true, // Automatically reconnect on disconnection
reconnectInterval: 15000, // Time in ms to wait before reconnecting
greetUsers: true, // Whether to greet users when they join
autoUpvote: true, // Whether to automatically upvote songs
botName: 'Kuber', // Bot's name for mention detection
respondToMentions: true, // Whether to respond when mentioned
useAI: true, // Whether to use AI responses
geminiAPIKey: 'AIzaSyAQAw_EZv1kX_l24ViMGnPYGC3ExbIZFCU' // Gemini API key
};
console.log('Attempting to create DubAPI instance with username:', username);
console.log('Connecting to room:', roomName);
// Initialize memory handler
let memoryHandler = new MemoryHandler({
maxMessagesPerUser: 10, // Remember last 10 messages per user
maxMemoryAge: 30 * 60 * 1000 // Remember messages for 30 minutes
});
// Initialize AI handler if enabled
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);
// ========== Utility Functions ==========
// Format time in seconds to mm:ss
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' + secs : secs}`;
}
// Check if user has staff permissions
function isStaff(user) {
return user && bot.isStaff(user);
}
// ========== Command Handler ==========
// Handle chat commands
function handleCommand(data) {
// Ignore if not a command
if (!data.message.startsWith(config.commandPrefix)) return;
// Parse command and arguments
const args = data.message.slice(config.commandPrefix.length).trim().split(/\s+/);
const command = args.shift().toLowerCase();
// Get user who sent the command
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)) {
// Only staff can clear memory
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:
// Unknown command
break;
}
}
// ========== Connection Functions ==========
function connect() {
console.log('Attempting to connect to room:', roomName);
bot.connect(roomName);
}
// ========== Event Listeners ==========
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);
});
// Chat message handler
bot.on(bot.events.chatMessage, function(data) {
console.log(data.user.username + ': ' + data.message);
// Store all messages in memory for context (except bot's own messages)
if (data.user.username !== config.botName && memoryHandler) {
memoryHandler.addMessage(data.user.username, data.message, false);
}
// Handle commands
handleCommand(data);
// Handle mentions (when bot is called by name)
if (config.respondToMentions && data.user.username !== config.botName) {
// Use simple string methods instead of regex for more reliable detection
const message = data.message;
const lowerMessage = message.toLowerCase();
const botName = config.botName.toLowerCase();
// Check for mentions in several ways
const isMentioned =
lowerMessage.includes('@' + botName) || // @kuber
lowerMessage.startsWith(botName) || // starts with kuber
lowerMessage.includes(' ' + botName + ' ') || // kuber with spaces around
lowerMessage.endsWith(' ' + botName); // ends with kuber
console.log('Checking for mentions in message:', message, 'Mentioned:', isMentioned);
// Check if bot is mentioned
if (isMentioned) {
console.log(`Bot was mentioned by ${data.user.username}`);
// Get the user message without the bot's name for cleaner AI input
let userMessage = data.message;
const botNamePattern = new RegExp(`@?${config.botName}`, 'gi');
userMessage = userMessage.replace(botNamePattern, '').trim();
// Special case handling for specific commands
if (userMessage.toLowerCase().includes('what') &&
(userMessage.toLowerCase().includes('song') ||
userMessage.toLowerCase().includes('track') ||
userMessage.toLowerCase().includes('playing'))) {
// Get current song info directly
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);
}
// Handle response based on whether AI is enabled
if (config.useAI && aiHandler) {
// Show typing indicator
bot.sendChat(`/me is thinking...`);
// Call AI for response with username for context
console.log(`Sending to AI: ${userMessage} (from: ${data.user.username})`);
aiHandler.getAIResponse(userMessage, data.user.username)
.then(aiResponse => {
// Clear typing indicator by deleting last message (if possible)
// This would require custom implementation as DubAPI doesn't support this directly
// Format first message with user mention
let firstMessage = `@${data.user.username}, `;
// Smart message chunking for chat platforms
const maxChatLength = 160; // DubAPI character limit (very conservative to be safe)
// Check if we need to split the message
if (aiResponse.length > maxChatLength - firstMessage.length) {
console.log(`Response too long (${aiResponse.length} chars), splitting into multiple messages`);
// Prepare chunks array
let chunks = [];
// Log the entire response for debugging
console.log('Full AI response:', aiResponse);
// Special handling for lists (numbered or bullet points)
// Match numbered lists (1., 2., etc.) or bullet points (•, *)
const listRegex = /((?:\d+\.\s+|[•\*]\s+)[^\d•\*\n.]+)/g;
const hasList = listRegex.test(aiResponse);
// If we have lists (bullet points or numbered lists), handle them specially
if (hasList) {
console.log('Detected list items in response, preserving list structure');
// Reset regex position
listRegex.lastIndex = 0;
// Extract list items using regex
const listItems = [];
let match;
let lastIndex = 0;
let preListText = '';
// First, capture any text before the first list item
const firstListItem = aiResponse.search(/(\d+\.\s+|[•\*]\s+)/);
if (firstListItem > 0) {
preListText = aiResponse.substring(0, firstListItem);
}
// Now extract each list item
while ((match = listRegex.exec(aiResponse)) !== null) {
listItems.push(match[0]);
lastIndex = match.index + match[0].length;
}
// Get any remaining text after the last list item
let postListText = '';
if (lastIndex < aiResponse.length) {
postListText = aiResponse.substring(lastIndex);
}
// Now create chunks from these elements
// Start with pre-list text
let chunks = [];
if (preListText.trim()) {
chunks.push(firstMessage + preListText.trim());
} else if (firstMessage) {
// If no pre-list text, first list item needs the mention
if (listItems.length > 0) {
listItems[0] = firstMessage + listItems[0];
}
}
// Add each list item as a separate chunk
// This ensures each bullet or numbered item appears as a separate chat message
listItems.forEach(item => {
if (item.length > maxChatLength) {
// If a list item is very long, split it further
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);
}
});
// Add post-list text if any
if (postListText.trim()) {
chunks.push(postListText.trim());
}
} else {
// No lists found, just split by sentence or length
// First try to split by sentences to keep coherence
const sentences = aiResponse.split(/([.!?]+\s+)/).filter(part => part.trim().length > 0);
let currentChunk = firstMessage;
let isFirstChunk = true;
// Try to keep sentences together when possible
for (let i = 0; i < sentences.length; i++) {
const sentence = sentences[i];
// Check if adding this sentence would make chunk too long
if (currentChunk.length + sentence.length > maxChatLength) {
// If sentence itself is too long, force-split it
if (sentence.length > maxChatLength) {
// Save current chunk first if it has content
if (currentChunk.length > (isFirstChunk ? firstMessage.length : 0)) {
chunks.push(currentChunk);
isFirstChunk = false;
}
// Split long sentence into chunks of maxChatLength
let remaining = sentence;
while (remaining.length > 0) {
// Try to split on a space when possible
let splitPoint = maxChatLength;
if (remaining.length > maxChatLength) {
// Find last space within the limit
const lastSpace = remaining.substring(0, maxChatLength).lastIndexOf(' ');
if (lastSpace > 0) {
splitPoint = lastSpace + 1; // +1 to include the space
}
}
const chunk = remaining.substring(0, splitPoint);
chunks.push(isFirstChunk ? (firstMessage + chunk) : chunk);
isFirstChunk = false;
remaining = remaining.substring(splitPoint);
}
// Reset current chunk
currentChunk = '';
} else {
// Add current chunk and start new one with this sentence
chunks.push(currentChunk);
currentChunk = sentence;
isFirstChunk = false;
}
} else {
// Add sentence to current chunk
if (currentChunk.length > (isFirstChunk ? firstMessage.length : 0)) {
currentChunk += sentence;
} else {
currentChunk += (isFirstChunk ? '' : '') + sentence;
}
}
}
// Add the last chunk if it has content
if (currentChunk.length > 0) {
chunks.push(currentChunk);
}
}
// Send chunks with a delay between them
let sentChunks = 0;
function sendNextChunk() {
if (sentChunks < chunks.length) {
bot.sendChat(chunks[sentChunks]);
sentChunks++;
if (sentChunks < chunks.length) {
// Schedule next chunk
setTimeout(sendNextChunk, 800);
} else {
// All chunks sent, store in memory
if (memoryHandler) {
memoryHandler.addMessage(data.user.username, aiResponse, true);
}
}
}
}
// Start sending chunks
sendNextChunk();
} else {
// Send as a single message
bot.sendChat(firstMessage + aiResponse);
// Store bot's response in memory
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 {
// Fallback to pre-defined responses when AI is disabled
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)];
// Send the response with a small delay to seem more natural
setTimeout(() => {
bot.sendChat(response);
}, 1000 + Math.random() * 1000); // Random delay between 1-2 seconds
}
}
}
});
// User join handler
bot.on(bot.events.userJoin, function(data) {
console.log(`${data.user.username} joined the room`);
// Greet users when they join
if (config.greetUsers) {
bot.sendChat(`Welcome to the room, @${data.user.username}!`);
}
});
// User leave handler
bot.on(bot.events.userLeave, function(data) {
console.log(`${data.user.username} left the room`);
});
// Song change handler
bot.on(bot.events.roomPlaylistUpdate, function(data) {
if (!data.media) return;
console.log(`Now playing: ${data.media.name} by ${data.media.artist || 'Unknown'}`);
// Auto upvote songs if enabled
if (config.autoUpvote) {
setTimeout(() => {
bot.updub(function(err) {
if (err) console.error('Error upvoting:', err);
else console.log('Auto-upvoted current song');
});
}, 5000); // Wait 5 seconds before upvoting
}
});
// Connect to the room
connect();
});