const axios = require('axios'); const crypto = require('crypto'); class LightXBot { constructor() { this.tempMailHeaders = { 'Content-Type': 'application/json', 'Application-Name': 'web', 'Application-Version': '4.0.0', 'X-CORS-Header': 'iaWg3pchvFx48fY' }; this.lightxHeaders = { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }; this.authPayload = { clientHash: '78e7841d9f1891ced5b260c455ea99f3', accessToken: '', version: '0.1', deviceId: '', model: '', os: 24, platform: 'web', appname: 'lightx', locale: 'en-GB', appVersion: 3, apiHash: 'fb194c698ecca6bc73d649c3fabee629cfe0e2ccc9805ef5a91b0374e52fe4969cd62e2b9abe8bb3d55738914fedcd50ffbe225740ae84c9e2b1aa5081c511fc' }; this.gtaStyles = { 1: { name: "GTA Classic (1997)", prompt: "Transform this image into the visual style of the original Grand Theft Auto (1997) — gritty, top-down pixel art with a retro 90s vibe. The final result should look like an old-school PC game screenshot, viewed from above, featuring blocky buildings, tiny cars, and crime-filled city streets. Emphasize pixelated textures, limited color palettes, and simple shading while keeping the overall scene dynamic and full of energy. The lighting should be flat but vibrant, reflecting the chaotic atmosphere of late-90s video games." }, 2: { name: "GTA II (1999)", prompt: "Transform this image into the GTA II (1999) art style — a futuristic cyber-crime city seen from a top-down perspective, with neon colors, metallic tones, and gritty urban lighting. The scene should feel like a dystopian metropolis filled with glowing billboards, neon lights, and advanced cars, yet maintain the retro-pixel atmosphere of late 90s games. Focus on electronic ambiance and dark tones mixed with vibrant highlights, as if the image came from a sci-fi city under constant surveillance." }, 3: { name: "GTA III (2001)", prompt: "Transform this image into the iconic GTA III (2001) style — early 3D realism with sharp edges, low-poly textures, and muted colors. The environment should capture Liberty City's gritty, industrial, and crime-ridden feel. Focus on a cinematic but minimal 3D look: concrete streets, gloomy skies, dark alleys, and cars with basic reflections. The lighting should be moody, resembling early 2000s console graphics with slightly rough textures." }, 4: { name: "GTA Vice City (2002)", prompt: "Transform this image into the glamorous and colorful GTA: Vice City style — 1980s Miami vibes full of neon lights, palm trees, and pastel tones. The subject should look like a stylish GTA poster: vibrant lighting, bold pink and blue highlights, tropical background, and vintage cars with chrome reflections. Add cinematic sunset skies and the iconic 80s fashion — sunglasses, suits, and tropical shirts. The whole image should radiate that retro luxury plus chaos Vice City feeling." }, 5: { name: "GTA San Andreas (2004)", prompt: "Transform this image into the legendary GTA: San Andreas visual style — bold outlines, comic-style shading, and vivid West Coast energy. Focus on street life, graffiti, lowriders, and warm tones like orange and gold. The art should look like an official loading screen or cover art — exaggerated but cool, confident, and full of attitude. The background should include Los Santos-style streets, palm trees, and sunset skies, reflecting early 2000s California gang culture." }, 6: { name: "GTA IV (2008)", prompt: "Transform this image into the GTA IV aesthetic — dark, realistic, and heavily influenced by New York City. Emphasize gritty realism, muted colors, foggy atmosphere, and overcast lighting. The textures should be slightly desaturated with cinematic shadows and reflective rain-soaked streets. Capture that somber tone of Liberty City — urban struggle, steel bridges, traffic lights, and cold realism." }, 7: { name: "GTA V (2013)", prompt: "Transform this image into the GTA V style — cinematic, ultra-detailed, and full of Los Santos luxury. Keep realistic lighting, natural shadows, and sharp reflections. Add modern California cityscapes, palm trees, beaches, and sports cars. The mood should be vibrant and sunny, like a high-end Rockstar promotional render — polished, dynamic, and stylishly composed." }, 8: { name: "GTA VI (2025)", prompt: "Transform this image into the GTA VI (2025) next-generation style — hyper-realistic visuals mixed with neon-tropical Miami energy. The image should feel futuristic yet grounded: ultra-sharp reflections, cinematic color grading, vibrant sunsets, and advanced lighting physics. Focus on pastel neon tones, rich texture detail, and dynamic atmosphere — capturing the rumored Vice City return with next-gen realism. The composition should look like an official Rockstar cinematic trailer still: breathtaking realism, expressive tone, and immersive Miami-inspired scenery." } }; this.email = ''; this.token = ''; this.password = ''; this.bearerToken = ''; this.systemRefKey = ''; } hashPassword(password) { return crypto.createHash('sha512').update(password).digest('hex'); } generateRandomString(length) { const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } async createTempEmail(retries = 5) { for (let i = 0; i < retries; i++) { try { console.log(`[${i + 1}] Creating temp email...`); const response = await axios.post( 'https://api.internal.temp-mail.io/api/v3/email/new', { min_name_length: 10, max_name_length: 10 }, { headers: this.tempMailHeaders, timeout: 30000 } ); console.log('Response status:', response.status); console.log('Response data:', JSON.stringify(response.data)); // Langsung assign tanpa banyak validasi const responseData = response.data; this.email = responseData.email; this.token = responseData.token; // Validasi setelah assign if (!this.email || !this.token) { throw new Error(`Invalid data: email=${this.email}, token=${this.token}`); } console.log(`Email created: ${this.email}`); return { email: this.email, token: this.token }; } catch (error) { console.error(`Attempt ${i + 1} failed:`, error.message); if (error.response) { console.error('Response status:', error.response.status); console.error('Response headers:', error.response.headers); console.error('Response data:', error.response.data); } if (i === retries - 1) { throw new Error(`Failed after ${retries} attempts: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 3000)); } } } async registerAccount(name, password) { this.password = password; const hashedPassword = this.hashPassword(password); console.log(`Registering account for ${this.email}...`); try { const response = await axios.post( 'https://www.instagraphe.mobi/andor-login-1.0/mobile/emailSignup', { name: name, email: this.email, password: hashedPassword }, { headers: { ...this.lightxHeaders, auth: JSON.stringify(this.authPayload) }, timeout: 30000 } ); console.log('Registration response:', JSON.stringify(response.data)); return response.data; } catch (error) { console.error('Registration error:', error.message); if (error.response) { console.error('Registration response:', error.response.data); } throw error; } } async waitForVerificationEmail(maxRetries = 40, interval = 6000) { console.log('Waiting for verification email...'); for (let i = 0; i < maxRetries; i++) { try { const response = await axios.get( `https://api.internal.temp-mail.io/api/v3/email/${this.email}/messages`, { headers: this.tempMailHeaders, timeout: 20000 } ); if (response.data && response.data.length > 0) { console.log(`Found ${response.data.length} messages`); for (const msg of response.data) { if (msg.body_text && msg.body_text.includes('validate-email')) { const linkMatch = msg.body_text.match(/https:\/\/www\.lightxeditor\.com\/validate-email\?[^\s)]+/); if (linkMatch) { console.log('Verification link found'); return linkMatch[0]; } } } } console.log(`Check ${i + 1}/${maxRetries}...`); } catch (error) { console.error(`Email check error: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, interval)); } throw new Error('Email verification timeout'); } async verifyEmail(verificationLink) { console.log('Verifying email...'); try { await axios.get(verificationLink, { maxRedirects: 5, timeout: 20000, validateStatus: () => true }); await new Promise(resolve => setTimeout(resolve, 3000)); console.log('Email verified'); return 'Success'; } catch (error) { console.log('Verification request sent (error ignored)'); return 'Success'; } } async login() { console.log('Logging in...'); const hashedPassword = this.hashPassword(this.password); try { const response = await axios.post( 'https://www.instagraphe.mobi/andor-login-1.0/mobile/login', { password: hashedPassword, type: 'EMAIL', value: this.email }, { headers: { ...this.lightxHeaders, auth: JSON.stringify(this.authPayload) }, timeout: 30000 } ); console.log('Login response:', JSON.stringify(response.data)); // Validasi response structure if (!response.data || !response.data.body) { throw new Error(`Invalid login response: ${JSON.stringify(response.data)}`); } if (!response.data.body.token || !response.data.body.user) { throw new Error(`Missing token or user: ${JSON.stringify(response.data.body)}`); } this.bearerToken = response.data.body.token.accessToken; this.systemRefKey = response.data.body.user.systemRefKey; if (!this.bearerToken || !this.systemRefKey) { throw new Error(`Token/RefKey is null: token=${this.bearerToken}, refKey=${this.systemRefKey}`); } console.log('Login successful'); return response.data; } catch (error) { console.error('Login error:', error.message); if (error.response) { console.error('Login response:', error.response.data); } throw error; } } async checkCredits() { const authWithToken = { ...this.authPayload, accessToken: this.bearerToken, systemRefKey: this.systemRefKey }; try { const response = await axios.get( 'https://www.instagraphe.mobi/andor-login-1.0/user/getAPIUsageDetails?apiId=1', { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) }, timeout: 25000 } ); console.log(`Credits remaining: ${response.data.body.remainingCalls}`); return response.data.body; } catch (error) { console.error('Check credits error:', error.message); throw error; } } async uploadImage(imageBuffer, fileName, size) { console.log('Uploading image...'); const authWithToken = { ...this.authPayload, accessToken: this.bearerToken, systemRefKey: this.systemRefKey }; try { const presignResponse = await axios.post( 'https://www.instagraphe.mobi/andor-media-1.0/content/generatePresignedUrls', { featureType: 'photoshoot', contents: [{ size: size, contentType: 'image/jpeg', assetType: 'IMG', assetRefId: '1', name: fileName }] }, { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) }, timeout: 30000 } ); const uploadData = presignResponse.data.body.contents[0]; const presignedUrl = uploadData.presignedUrl; const imageUrl = uploadData.contentUrl; await axios.put(presignedUrl, imageBuffer, { headers: { 'Content-Type': 'image/jpeg', 'Content-Length': size }, timeout: 60000 }); console.log('Image uploaded'); return imageUrl; } catch (error) { console.error('Upload image error:', error.message); throw error; } } async convertToGTAStyle(imageUrl, styleId) { const style = this.gtaStyles[styleId]; if (!style) { throw new Error(`Invalid style ID: ${styleId}. Valid IDs are 1-8`); } console.log(`Converting to ${style.name}...`); const authWithToken = { ...this.authPayload, accessToken: this.bearerToken, systemRefKey: this.systemRefKey }; try { const response = await axios.post( 'https://www.instagraphe.mobi/andor-media-1.0/aiartweb/generateImage', { imageUrl: imageUrl, featureType: 'image2image', subFeatureType: 'gpt', textPrompt: style.prompt }, { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) }, timeout: 35000 } ); if (!response.data || !response.data.body || !response.data.body.assetId) { throw new Error(`Invalid response: ${JSON.stringify(response.data)}`); } const assetId = response.data.body.assetId; return await this.checkGenerationStatus(assetId); } catch (error) { if (error.response) { throw new Error(`API Error: ${JSON.stringify(error.response.data)}`); } throw error; } } async checkGenerationStatus(assetId, maxRetries = 50, interval = 5000) { console.log('Checking generation status...'); const authWithToken = { ...this.authPayload, accessToken: this.bearerToken, systemRefKey: this.systemRefKey }; for (let i = 0; i < maxRetries; i++) { try { const response = await axios.post( 'https://www.instagraphe.mobi/andor-media-1.0/aiart/checkStatus', { assetId: assetId }, { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) }, timeout: 25000 } ); const status = response.data.body.status; console.log(`Status check ${i + 1}: ${status}`); if (status === 'active') { console.log('Generation complete!'); return response.data.body; } await new Promise(resolve => setTimeout(resolve, interval)); } catch (error) { console.error(`Status check error: ${error.message}`); if (i === maxRetries - 1) throw error; } } throw new Error('Generation timeout'); } } async function img2gta(imageUrl, styleId = 5, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { console.log(`\n========== ATTEMPT ${attempt}/${maxRetries} ==========`); const bot = new LightXBot(); try { // Step 1: Create temp email await bot.createTempEmail(); // Step 2: Generate random credentials const randomName = 'User' + bot.generateRandomString(6); const randomPassword = 'Pass' + bot.generateRandomString(10) + '!'; // Step 3: Register account await bot.registerAccount(randomName, randomPassword); // Step 4: Wait for verification email const verificationLink = await bot.waitForVerificationEmail(); // Step 5: Verify email await bot.verifyEmail(verificationLink); // Step 6: Wait before login await new Promise(resolve => setTimeout(resolve, 5000)); // Step 7: Login await bot.login(); // Step 8: Check credits const credits = await bot.checkCredits(); if (credits.remainingCalls < 1) { throw new Error('No credits available'); } // Step 9: Download image const imageResponse = await axios.get(imageUrl, { responseType: 'arraybuffer', timeout: 45000 }); const imageBuffer = Buffer.from(imageResponse.data); // Step 10: Upload image const uploadedUrl = await bot.uploadImage(imageBuffer, 'image.jpg', imageBuffer.length); // Step 11: Convert to GTA style const result = await bot.convertToGTAStyle(uploadedUrl, styleId); // Success! return { success: true, styleName: bot.gtaStyles[styleId].name, email: bot.email, resultUrl: result.imgUrl, thumbnailUrl: result.thumbUrl, width: result.width, height: result.height, creditsRemaining: credits.remainingCalls - 1 }; } catch (error) { console.error(`Attempt ${attempt} failed:`, error.message); console.error('Error stack:', error.stack); if (attempt === maxRetries) { return { success: false, error: error.message, attemptsUsed: attempt }; } // Wait before retry await new Promise(resolve => setTimeout(resolve, 5000)); } } } // API Handler for Hugging Face const handler = async (req, res) => { try { const { image, style, key } = req.query; // Validate required parameters if (!image || !key) { return res.status(400).json({ success: false, error: 'Missing required parameters: image and key' }); } // Parse style ID const styleId = style ? parseInt(style) : 5; // Validate style ID if (styleId < 1 || styleId > 8) { return res.status(400).json({ success: false, error: 'Invalid style parameter. Must be between 1-8', styles: { 1: 'GTA Classic (1997)', 2: 'GTA II (1999)', 3: 'GTA III (2001)', 4: 'GTA Vice City (2002)', 5: 'GTA San Andreas (2004)', 6: 'GTA IV (2008)', 7: 'GTA V (2013)', 8: 'GTA VI (2025)' } }); } // Process image const result = await img2gta(image, styleId); // Check if conversion failed if (!result.success) { return res.status(500).json({ success: false, error: result.error, attemptsUsed: result.attemptsUsed }); } // Return success response res.json({ author: "Herza", success: true, data: { styleName: result.styleName, url: result.resultUrl, thumbnail: result.thumbnailUrl, width: result.width, height: result.height, creditsUsed: 1, email: result.email } }); } catch (error) { console.error('Handler error:', error); res.status(500).json({ success: false, error: error.message, stack: error.stack }); } }; // Export for Hugging Face module.exports = { name: 'Image to GTA Converter', description: 'Convert photos to GTA style using AI with 8 different GTA game themes from 1997 to 2025', type: 'GET', routes: ['api/AI/img2gta'], tags: ['ai', 'image', 'gta', 'converter', 'game'], main: ['AI'], parameters: ['image', 'style', 'key'], enabled: true, limit: 5, handler };