diff --git a/index.js b/index.js index 1e1867d..aa6a037 100644 --- a/index.js +++ b/index.js @@ -256,173 +256,179 @@ async function validateImageUrl(url) { } } -// Custom search function as alternative to google-it +// Real search function using DuckDuckGo API for actual search results async function searchImages(query, limit = 10) { try { - console.log(`Custom search for: ${query}`); + console.log(`Searching for images: ${query}`); + // Use DuckDuckGo Instant Answer API for real search results + const searchUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(query + ' image')}&format=json&no_html=1&skip_disambig=1&safe_search=strict`; + + const response = await fetch(searchUrl, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + }); + + if (!response.ok) { + throw new Error(`Search API failed: ${response.status}`); + } + + const data = await response.json(); const searchResults = []; - // Use Dog CEO API - it's reliable and provides direct image URLs - try { - // Get multiple dog images since they always work - const numberOfImages = Math.min(limit, 5); - const promises = []; - - for (let i = 0; i < numberOfImages; i++) { - promises.push( - fetch('https://dog.ceo/api/breeds/image/random') - .then(response => response.json()) - .then(data => { - if (data.status === 'success') { - return { - title: `${query} - Image ${i + 1}`, - link: data.message - }; - } - return null; - }) - .catch(() => null) - ); + // Extract image results from DuckDuckGo response + if (data.Results && data.Results.length > 0) { + for (let i = 0; i < Math.min(data.Results.length, limit); i++) { + const result = data.Results[i]; + if (result.Icon && result.Icon.URL) { + searchResults.push({ + title: result.Text || `${query} - Result ${i + 1}`, + link: result.Icon.URL + }); + } } - - const results = await Promise.all(promises); - - // Add successful results - results.forEach(result => { - if (result) { - searchResults.push(result); - } - }); - - } catch (error) { - console.log('Dog API failed:', error); } - // If we don't have enough results, add some static fallback images that are guaranteed to work - if (searchResults.length < limit) { - const fallbackImages = [ - { - title: `${query} - Sample Image 1`, - link: `https://images.dog.ceo/breeds/hound-afghan/n02088094_1003.jpg` - }, - { - title: `${query} - Sample Image 2`, - link: `https://images.dog.ceo/breeds/terrier-fox/n02095314_2650.jpg` - }, - { - title: `${query} - Sample Image 3`, - link: `https://images.dog.ceo/breeds/spaniel-blenheim/n02086646_1061.jpg` + // If no Results, try using Related Topics + if (searchResults.length === 0 && data.RelatedTopics && data.RelatedTopics.length > 0) { + for (let i = 0; i < Math.min(data.RelatedTopics.length, limit); i++) { + const topic = data.RelatedTopics[i]; + if (topic.Icon && topic.Icon.URL) { + searchResults.push({ + title: topic.Text || `${query} - Topic ${i + 1}`, + link: topic.Icon.URL + }); } - ]; - - const needed = limit - searchResults.length; - searchResults.push(...fallbackImages.slice(0, needed)); + } } - console.log(`Generated ${searchResults.length} reliable image results`); + // If still no results, try scraping Unsplash for real photos + if (searchResults.length === 0) { + try { + const unsplashUrl = `https://unsplash.com/s/photos/${encodeURIComponent(query)}`; + const pageResponse = await fetch(unsplashUrl, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + }); + + if (pageResponse.ok) { + const html = await pageResponse.text(); + const $ = cheerio.load(html); + + // Extract image URLs from Unsplash + $('img[src*="unsplash"]').each((i, element) => { + if (searchResults.length >= limit) return false; + + const src = $(element).attr('src'); + const alt = $(element).attr('alt') || `${query} - Image ${i + 1}`; + + if (src && src.includes('unsplash')) { + searchResults.push({ + title: alt, + link: src + }); + } + }); + } + } catch (unsplashError) { + console.log('Unsplash scraping failed:', unsplashError.message); + } + } + + console.log(`Found ${searchResults.length} real search results for: ${query}`); return searchResults.slice(0, limit); } catch (error) { - console.error('Custom search failed:', error); - - // Ultimate fallback - use known working dog image URLs - return [ - { - title: `${query} - Fallback Image 1`, - link: `https://images.dog.ceo/breeds/hound-afghan/n02088094_1003.jpg` - }, - { - title: `${query} - Fallback Image 2`, - link: `https://images.dog.ceo/breeds/terrier-fox/n02095314_2650.jpg` - }, - { - title: `${query} - Fallback Image 3`, - link: `https://images.dog.ceo/breeds/spaniel-blenheim/n02086646_1061.jpg` - } - ]; + console.error('Search failed:', error); + throw new Error(`Failed to search for "${query}": ${error.message}`); } } -// Custom video search function +// Real video search function using YouTube search async function searchVideos(query, limit = 5) { try { - console.log(`Custom video search for: ${query}`); + console.log(`Searching for videos: ${query}`); + // Use DuckDuckGo to search for YouTube videos + const searchUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(query + ' site:youtube.com')}&format=json&no_html=1&skip_disambig=1`; + + const response = await fetch(searchUrl, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + }); + + if (!response.ok) { + throw new Error(`Video search API failed: ${response.status}`); + } + + const data = await response.json(); const videoResults = []; - // Create themed video results based on the query - const videoThemes = { - 'music': [ - { - title: `${query} - Music Video Example`, - link: `https://www.youtube.com/watch?v=dQw4w9WgXcQ`, - snippet: `Music video related to: ${query}` + // Extract video results from DuckDuckGo response + if (data.Results && data.Results.length > 0) { + for (let i = 0; i < Math.min(data.Results.length, limit); i++) { + const result = data.Results[i]; + if (result.FirstURL && result.FirstURL.includes('youtube.com')) { + videoResults.push({ + title: result.Text || `${query} - Video ${i + 1}`, + link: result.FirstURL, + snippet: result.Text || `Video related to: ${query}` + }); } - ], - 'tutorial': [ - { - title: `${query} - Tutorial Video`, - link: `https://www.youtube.com/watch?v=9bZkp7q19f0`, - snippet: `Educational content about: ${query}` - } - ], - 'tech': [ - { - title: `${query} - Technology Overview`, - link: `https://www.youtube.com/watch?v=dQw4w9WgXcQ`, - snippet: `Technology video about: ${query}` - } - ], - 'coding': [ - { - title: `${query} - Programming Tutorial`, - link: `https://www.youtube.com/watch?v=9bZkp7q19f0`, - snippet: `Coding tutorial for: ${query}` - } - ] - }; - - const queryLower = query.toLowerCase(); - let foundTheme = false; - - // Check if query matches any theme - for (const [theme, videos] of Object.entries(videoThemes)) { - if (queryLower.includes(theme)) { - videoResults.push(...videos); - foundTheme = true; - break; } } - // If no theme matched, add generic results - if (!foundTheme) { - videoResults.push( - { - title: `${query} - Video Result 1`, - link: `https://www.youtube.com/watch?v=dQw4w9WgXcQ`, - snippet: `Video content related to: ${query}` - }, - { - title: `${query} - Video Result 2`, - link: `https://www.youtube.com/watch?v=9bZkp7q19f0`, - snippet: `Additional video about: ${query}` + // Try scraping YouTube search results if DuckDuckGo doesn't return enough + if (videoResults.length === 0) { + try { + const youtubeSearchUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`; + const pageResponse = await fetch(youtubeSearchUrl, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + }); + + if (pageResponse.ok) { + const html = await pageResponse.text(); + + // Extract video IDs from YouTube search results + const videoIdRegex = /"videoId":"([^"]+)"/g; + const titleRegex = /"title":{"runs":\[{"text":"([^"]+)"/g; + + let match; + const videoIds = []; + const titles = []; + + while ((match = videoIdRegex.exec(html)) !== null && videoIds.length < limit) { + videoIds.push(match[1]); + } + + while ((match = titleRegex.exec(html)) !== null && titles.length < limit) { + titles.push(match[1]); + } + + for (let i = 0; i < Math.min(videoIds.length, limit); i++) { + videoResults.push({ + title: titles[i] || `${query} - Video ${i + 1}`, + link: `https://www.youtube.com/watch?v=${videoIds[i]}`, + snippet: `Video about: ${query}` + }); + } } - ); + } catch (youtubeError) { + console.log('YouTube scraping failed:', youtubeError.message); + } } - console.log(`Generated ${videoResults.length} video results for: ${query}`); + console.log(`Found ${videoResults.length} real video results for: ${query}`); return videoResults.slice(0, limit); } catch (error) { console.error('Video search failed:', error); - return [ - { - title: `${query} - Default Video`, - link: `https://www.youtube.com/watch?v=dQw4w9WgXcQ`, - snippet: `Video placeholder for: ${query}` - } - ]; + throw new Error(`Failed to search videos for "${query}": ${error.message}`); } } @@ -638,69 +644,47 @@ client.on(Events.InteractionCreate, async interaction => { try { if (mediaType === 'image') { - console.log('Processing image search with custom function...'); + console.log('Processing real image search...'); - // Use our custom search function - const results = await searchImages(query, count * 2); - console.log(`Custom search returned ${results.length} results`); + // Use real search function + const results = await searchImages(query, count); + console.log(`Real search returned ${results.length} results`); if (results.length === 0) { - await interaction.editReply(`❌ No image results found for "${query}". Try a different search term.`); + await interaction.editReply(`❌ No image results found for "${query}". The search engines may not have returned any results for this query.`); return; } - const imageResults = []; - console.log(`Processing ${results.length} search results...`); - - for (const result of results) { - const url = result.link; - // Use more lenient validation for image search results - const validation = await validateImageUrl(url); - if (validation.valid) { - imageResults.push({ - title: result.title, - url: url, - contentType: validation.contentType - }); - if (imageResults.length >= count) break; - } - } - - if (imageResults.length === 0) { - await interaction.editReply(`No valid image results found for "${query}". The search returned results but they couldn't be validated as images.`); - return; - } - - // Create embeds for images - const embeds = imageResults.map((result, i) => { + // Create embeds for images (skip validation since we're getting real search results) + const embeds = results.map((result, i) => { return new EmbedBuilder() - .setTitle(`Image ${i + 1}: ${result.title}`) - .setImage(result.url) + .setTitle(`${result.title}`) + .setImage(result.link) .setColor(0x00AE86) - .setFooter({ text: `Content-Type: ${result.contentType}` }); + .setFooter({ text: `Search result ${i + 1} of ${results.length}` }); }); await interaction.editReply({ - content: `Found ${imageResults.length} images for "${query}":`, + content: `🔍 Found ${results.length} images for "${query}":`, embeds: embeds.slice(0, 10) // Discord allows max 10 embeds }); } else if (mediaType === 'video') { - console.log('Processing video search with custom function...'); + console.log('Processing real video search...'); - // Use our custom video search function + // Use real video search function const results = await searchVideos(query, count); - console.log(`Custom video search returned ${results.length} results`); + console.log(`Real video search returned ${results.length} results`); if (results.length === 0) { - await interaction.editReply(`❌ No video results found for "${query}". Try a different search term.`); + await interaction.editReply(`❌ No video results found for "${query}". The search engines may not have returned any results for this query.`); return; } // Create embeds for videos const embeds = results.map((result, i) => { const embed = new EmbedBuilder() - .setTitle(`Video ${i + 1}: ${result.title}`) + .setTitle(`${result.title}`) .setURL(result.link) .setColor(0xFF0000) .setDescription(result.snippet || 'No description available'); @@ -717,13 +701,13 @@ client.on(Events.InteractionCreate, async interaction => { }); await interaction.editReply({ - content: `Found ${results.length} videos for "${query}":`, + content: `🔍 Found ${results.length} videos for "${query}":`, embeds: embeds }); } } catch (error) { console.error('Search error:', error); - await interaction.editReply(`Error searching for "${query}": ${error.message}`); + await interaction.editReply(`❌ Error searching for "${query}": ${error.message}\n\nThis could be due to rate limiting or the search service being unavailable.`); } break;