/** * DiscoverProfile Scraper * Scraper untuk mencari profil media sosial berdasarkan username/nickname * Sumber: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o/324 * Credit By Zx * Note! Kalau mau di sher harap jangan hapus credit atau sumber saluran 🄶 */ const axios = require('axios'); class DiscoverProfile { constructor(options = {}) { this.baseURL = options.baseURL || 'https://api.discoverprofile.com'; this.timeout = options.timeout || 30000; this.retries = options.retries || 3; this.retryDelay = options.retryDelay || 1000; this.client = axios.create({ baseURL: this.baseURL, timeout: this.timeout, headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Mobile Safari/537.36', 'Referer': 'https://discoverprofile.com/', 'Origin': 'https://discoverprofile.com', ...options.headers } }); // Cache untuk menyimpan hasil pencarian this._cache = new Map(); this._cacheTTL = options.cacheTTL || 5 * 60 * 1000; // 5 menit default } /** * Cari profil berdasarkan username/nickname * @param {string} source - Username/nickname yang dicari * @param {Object} [options={}] - Opsi pencarian * @param {string} [options.type='name'] - Tipe pencarian: 'name' atau 'email' * @param {boolean} [options.rescan=false] - Force rescan * @returns {Promise} Hasil pencarian */ async search(source, options = {}) { const { type = 'name', rescan = false } = options; if (!source || typeof source !== 'string') { throw new Error('Parameter "source" harus berupa string yang valid'); } const cacheKey = `${source}:${type}:${rescan}`; // Cek cache jika bukan rescan if (!rescan && this._cache.has(cacheKey)) { const cached = this._cache.get(cacheKey); if (Date.now() - cached.timestamp < this._cacheTTL) { return cached.data; } this._cache.delete(cacheKey); } const payload = { source: source.trim(), type, rescan }; const result = await this._requestWithRetry('/discoverprofile', 'POST', payload); // Simpan ke cache this._cache.set(cacheKey, { data: result, timestamp: Date.now() }); return result; } /** * Cari profil berdasarkan username (alias untuk search dengan type='name') * @param {string} username - Username yang dicari * @returns {Promise} */ async searchByUsername(username) { return this.search(username, { type: 'name' }); } /** * Cari profil berdasarkan email * @param {string} email - Email yang dicari * @returns {Promise} */ async searchByEmail(email) { return this.search(email, { type: 'email' }); } /** * Filter hasil berdasarkan kategori * @param {Object} searchResult - Hasil dari search() * @param {string} category - Kategori yang difilter * @returns {Array} Daftar profil sesuai kategori */ filterByCategory(searchResult, category) { if (!searchResult?.result) return []; return searchResult.result.filter( item => item.category?.toLowerCase() === category.toLowerCase() ); } /** * Filter hasil hanya yang eksis (isExist = true) * @param {Object} searchResult - Hasil dari search() * @returns {Array} Daftar profil yang ditemukan */ filterExisting(searchResult) { if (!searchResult?.result) return []; return searchResult.result.filter(item => item.isExist === true); } /** * Filter hasil yang tidak eksis * @param {Object} searchResult - Hasil dari search() * @returns {Array} Daftar profil yang tidak ditemukan */ filterNonExisting(searchResult) { if (!searchResult?.result) return []; return searchResult.result.filter(item => item.isExist === false); } /** * Dapatkan semua kategori unik dari hasil * @param {Object} searchResult - Hasil dari search() * @returns {Array} Daftar kategori unik */ getCategories(searchResult) { if (!searchResult?.result) return []; const categories = new Set( searchResult.result.map(item => item.category).filter(Boolean) ); return [...categories].sort(); } /** * Dapatkan ringkasan statistik hasil pencarian * @param {Object} searchResult - Hasil dari search() * @returns {Object} Statistik */ getStats(searchResult) { const results = searchResult?.result || []; const existing = results.filter(r => r.isExist); const categories = this.getCategories(searchResult); const categoryStats = {}; for (const cat of categories) { const catItems = results.filter(r => r.category === cat); const catExisting = catItems.filter(r => r.isExist); categoryStats[cat] = { total: catItems.length, found: catExisting.length, notFound: catItems.length - catExisting.length }; } return { totalChecked: results.length, totalFound: existing.length, totalNotFound: results.length - existing.length, totalCategories: categories.length, categories: categoryStats, createdAt: searchResult?.createdAt || null }; } /** * Dapatkan semua platform yang tersedia dari hasil * @param {Object} searchResult - Hasil dari search() * @returns {Array} Daftar nama platform */ getPlatforms(searchResult) { if (!searchResult?.result) return []; return searchResult.result.map(item => item.source).filter(Boolean); } /** * Cari di platform tertentu dari hasil * @param {Object} searchResult - Hasil dari search() * @param {string} platformName - Nama platform (case-insensitive) * @returns {Object|null} Profil di platform tersebut */ findByPlatform(searchResult, platformName) { if (!searchResult?.result) return null; return searchResult.result.find( item => item.source?.toLowerCase() === platformName.toLowerCase() ) || null; } /** * Batch search beberapa username sekaligus * @param {Array} usernames - Daftar username * @param {Object} [options={}] - Opsi * @param {number} [options.concurrency=3] - Jumlah request paralel * @param {number} [options.delay=500] - Delay antar batch dalam ms * @returns {Promise} Map username -> hasil */ async batchSearch(usernames, options = {}) { const { concurrency = 3, delay = 500 } = options; const results = {}; for (let i = 0; i < usernames.length; i += concurrency) { const batch = usernames.slice(i, i + concurrency); const batchPromises = batch.map(async (username) => { try { const result = await this.search(username); results[username] = { success: true, data: result }; } catch (error) { results[username] = { success: false, error: error.message }; } }); await Promise.all(batchPromises); if (i + concurrency < usernames.length) { await this._sleep(delay); } } return results; } clearCache() { this._cache.clear(); } get cacheSize() { return this._cache.size; } async _requestWithRetry(endpoint, method = 'GET', data = null, attempt = 1) { try { const config = { method, url: endpoint }; if (data && (method === 'POST' || method === 'PUT')) { config.data = data; } const response = await this.client.request(config); if (response.data?.error) { throw new Error(`API Error: ${response.data.error}`); } return response.data; } catch (error) { if (attempt < this.retries) { const delay = this.retryDelay * attempt; await this._sleep(delay); return this._requestWithRetry(endpoint, method, data, attempt + 1); } throw new Error( `Request gagal setelah ${this.retries} percobaan: ${error.message}` ); } } _sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } _escapeCSV(value) { if (typeof value !== 'string') return String(value || ''); if (value.includes(',') || value.includes('"') || value.includes('\n')) { return `"${value.replace(/"/g, '""')}"`; } return value; } } // ==================== EXPORT ==================== module.exports = DiscoverProfile; // ==================== CONTOH PENGGUNAAN ==================== if (require.main === module) { (async () => { const scraper = new DiscoverProfile({ timeout: 30000, retries: 3, retryDelay: 1000 }); try { // Cari username console.log('šŸ” Mencari username "jokowi"...'); const result = await scraper.searchByUsername('jokowi'); // Statistik const stats = scraper.getStats(result); console.log('\nšŸ“Š Statistik:'); console.log(` Total platform dicek: ${stats.totalChecked}`); console.log(` Ditemukan: ${stats.totalFound}`); console.log(` Tidak ditemukan: ${stats.totalNotFound}`); console.log(` Total kategori: ${stats.totalCategories}`); // Hanya yang eksis const existing = scraper.filterExisting(result); console.log(`\nāœ… Profil yang ditemukan (${existing.length}):`); existing.forEach(item => { console.log(` • ${item.source}: ${item.url} [${item.category}]`); }); // Filter per kategori const socialMedia = scraper.filterByCategory(result, 'Social network'); console.log(`\n🌐 Social Networks (${socialMedia.length}):`); socialMedia.forEach(item => { const status = item.isExist ? 'āœ…' : 'āŒ'; console.log(` ${status} ${item.source}: ${item.url}`); }); // Cari platform spesifik const instagram = scraper.findByPlatform(result, 'Instagram'); if (instagram) { console.log(`\nšŸ“· Instagram: ${instagram.url} (${instagram.isExist ? 'Found' : 'Not Found'})`); } } catch (error) { console.error('āŒ Error:', error.message); } })(); }