UNPKG

nodejs-insta-private-api

Version:

A pure JavaScript Instagram Private API client inspired by instagram-private-api

281 lines (236 loc) 8.87 kB
const crypto = require('crypto'); const { random } = require('lodash'); class Utils { // ======================== // ORIGINAL FUNCTIONS // ======================== static generateUUID() { if (crypto.randomUUID) return crypto.randomUUID(); return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } static generateRandomString(length) { const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * characters.length)); } return result; } static generateDeviceId() { return 'android-' + this.generateRandomString(16); } static generatePhoneId() { return this.generateUUID(); } static generateAdId() { return this.generateUUID(); } static sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } static randomDelay(min = 1000, max = 3000) { return this.sleep(random(min, max)); } static md5(data) { return crypto.createHash('md5').update(data).digest('hex'); } static sha256(data) { return crypto.createHash('sha256').update(data).digest('hex'); } static hmacSha256(data, key) { return crypto.createHmac('sha256', key).update(data).digest('hex'); } static base64Encode(data) { return Buffer.from(data).toString('base64'); } static base64Decode(data) { return Buffer.from(data, 'base64').toString(); } static getCurrentTimestamp() { return Math.floor(Date.now() / 1000); } static getTimestampMs() { return Date.now(); } static formatUserAgent(appVersion, deviceString, language, appVersionCode) { return `Instagram ${appVersion} Android (${deviceString}; ${language}; ${appVersionCode})`; } static formatWebUserAgent(devicePayload, build, appUserAgent) { return `Mozilla/5.0 (Linux; Android ${devicePayload.android_release}; ${devicePayload.model} Build/${build}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 ${appUserAgent}`; } static parseUserId(userIdOrUsername) { if (typeof userIdOrUsername === 'number' || /^\d+$/.test(userIdOrUsername)) { return userIdOrUsername.toString(); } return null; } static isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } static isValidUsername(username) { const usernameRegex = /^[a-zA-Z0-9._]{1,30}$/; return usernameRegex.test(username); } static sanitizeCaption(caption) { if (!caption) return ''; return caption.replace(/[\u0000-\u001F\u007F-\u009F]/g, ''); } static chunkArray(array, chunkSize) { const chunks = []; for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; } static retryOperation(operation, maxRetries = 3, delay = 1000) { return new Promise((resolve, reject) => { let retries = 0; const attempt = async () => { try { const result = await operation(); resolve(result); } catch (error) { retries++; if (retries >= maxRetries) { reject(error); } else { setTimeout(attempt, delay * retries); } } }; attempt(); }); } static validateFileSize(filePath, maxSizeBytes) { const fs = require('fs'); try { const stats = fs.statSync(filePath); return stats.size <= maxSizeBytes; } catch (error) { return false; } } static getFileExtension(filePath) { return filePath.split('.').pop().toLowerCase(); } static isImageFile(filePath) { const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']; const extension = this.getFileExtension(filePath); return imageExtensions.includes(extension); } static isVideoFile(filePath) { const videoExtensions = ['mp4', 'mov', 'avi', 'mkv', 'webm', '3gp']; const extension = this.getFileExtension(filePath); return videoExtensions.includes(extension); } static humanizeError(error) { const errorMessages = { 'IgLoginBadPasswordError': 'The password you entered is incorrect. Please check your password and try again.', 'IgLoginInvalidUserError': 'The username you entered doesn\'t appear to belong to an account. Please check your username and try again.', 'IgLoginTwoFactorRequiredError': 'Two-factor authentication is required. Please enter the verification code.', 'IgCheckpointError': 'Instagram requires additional verification. Please complete the security challenge.', 'IgActionSpamError': 'This action has been blocked by Instagram\'s spam detection. Please try again later.', 'IgNotFoundError': 'The requested content could not be found.', 'IgPrivateUserError': 'This account is private. You must follow this user to see their content.', 'IgUserHasLoggedOutError': 'Your session has expired. Please log in again.', 'IgInactiveUserError': 'This account is inactive or has been suspended.', 'IgSentryBlockError': 'This request has been blocked by Instagram\'s security system.', 'IgNetworkError': 'A network error occurred. Please check your internet connection and try again.', 'IgUploadError': 'Failed to upload the file. Please check the file format and size.', 'IgConfigureMediaError': 'Failed to configure the media. Please try again.', }; if (error.response && error.response.status) { return `Request failed with status ${error.response.status}: ${error.response.statusText || ''}`; } return errorMessages[error.name] || error.message || 'An unknown error occurred.'; } static rateLimitDelay(retryAfter = null) { if (retryAfter) { return parseInt(retryAfter) * 1000; } return random(5000, 15000); } static createUserAgentFromDevice(device) { return `Instagram 401.0.0.48.79 Android (${device.android_version}/${device.android_release}; ${device.dpi}dpi; ${device.resolution}; ${device.manufacturer}; ${device.model}; ${device.device}; ${device.cpu})`; } // ======================== // NEW ENHANCEMENTS // ======================== static generateSecureRandomString(length) { const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const randomBytes = crypto.randomBytes(length); let result = ''; for (let i = 0; i < length; i++) { result += characters[randomBytes[i] % characters.length]; } return result; } static fileHash(filePath, algorithm = 'sha256') { const fs = require('fs'); const data = fs.readFileSync(filePath); return crypto.createHash(algorithm).update(data).digest('hex'); } static prettyBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let i = 0; while (bytes >= 1024 && i < units.length - 1) { bytes /= 1024; i++; } return `${bytes.toFixed(2)} ${units[i]}`; } static async retryWithBackoff(operation, maxRetries = 5, baseDelay = 1000) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { if (attempt === maxRetries) throw error; const delay = baseDelay * Math.pow(2, attempt - 1) + Math.random() * 500; await this.sleep(delay); } } } static logError(error, context = '') { console.error(`[${new Date().toISOString()}] ❌ ${context}:`, { name: error.name, message: error.message, stack: error.stack?.split('\n').slice(0, 3).join('\n') }); } static debugLog(message) { if (process.env.DEBUG === 'true') { console.log(`[DEBUG] ${new Date().toISOString()} - ${message}`); } } static generateAndroidDevice() { const versions = ['11', '12', '13', '14']; const models = ['Pixel 6', 'SM-G991B', 'Redmi Note 10', 'OnePlus 9']; const manufacturer = ['Google', 'Samsung', 'Xiaomi', 'OnePlus']; const randomIndex = Math.floor(Math.random() * models.length); return { android_release: versions[randomIndex], model: models[randomIndex], manufacturer: manufacturer[randomIndex], dpi: 480, resolution: '1080x2400', device: models[randomIndex].replace(/\s+/g, '_').toLowerCase(), cpu: 'arm64-v8a', android_version: versions[randomIndex] }; } static generateHeaders(userAgent) { return { 'User-Agent': userAgent, 'Accept': '*/*', 'Accept-Language': 'en-US', 'X-Requested-With': 'com.instagram.android', 'Connection': 'keep-alive' }; } } module.exports = Utils;