Spaces:
Sleeping
Sleeping
| 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.zombieStyles = { | |
| 1: { | |
| name: "Classic Zombie", | |
| prompt: "A man dressed as a zombie male costume features torn, bloodstained clothes, creating a creepy, haunted house with a creepy butler for a spooky and pumpkin, halloween night background", | |
| productId: 10103796, | |
| productImageId: 123663 | |
| }, | |
| 2: { | |
| name: "Walking Dead Zombie", | |
| prompt: "Transform into a decaying walking dead zombie with rotting flesh, pale grey skin, sunken eyes, and tattered bloody clothing in a post-apocalyptic setting", | |
| productId: 10103796, | |
| productImageId: 123663 | |
| }, | |
| 3: { | |
| name: "Horror Zombie", | |
| prompt: "Become a terrifying horror movie zombie with gruesome makeup, exposed bones, blood dripping, vacant stare, and decomposed skin in a dark spooky graveyard", | |
| productId: 10103796, | |
| productImageId: 123663 | |
| }, | |
| 4: { | |
| name: "Undead Creature", | |
| prompt: "Transform into an undead zombie creature with greenish rotting skin, missing chunks of flesh, empty soulless eyes, torn clothes covered in dirt and blood", | |
| productId: 10103796, | |
| productImageId: 123663 | |
| }, | |
| 5: { | |
| name: "Nightmare Zombie", | |
| prompt: "Become a nightmarish zombie with horrifying scars, decaying face, bloodshot eyes, ripped and stained clothing in a foggy haunted cemetery at midnight", | |
| productId: 10103796, | |
| productImageId: 123663 | |
| } | |
| }; | |
| 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() { | |
| 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 } | |
| ); | |
| this.email = response.data.email; | |
| this.token = response.data.token; | |
| return { email: this.email, token: this.token }; | |
| } | |
| async registerAccount(name, password) { | |
| this.password = password; | |
| const hashedPassword = this.hashPassword(password); | |
| 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) } } | |
| ); | |
| return response.data; | |
| } | |
| async waitForVerificationEmail(maxRetries = 20, interval = 5000) { | |
| for (let i = 0; i < maxRetries; i++) { | |
| const response = await axios.get( | |
| `https://api.internal.temp-mail.io/api/v3/email/${this.email}/messages`, | |
| { headers: this.tempMailHeaders } | |
| ); | |
| if (response.data && response.data.length > 0) { | |
| const verificationEmail = response.data.find(msg => | |
| msg.subject === 'Welcome to LightX' && msg.body_text.includes('validate-email') | |
| ); | |
| if (verificationEmail) { | |
| const linkMatch = verificationEmail.body_text.match(/https:\/\/www\.lightxeditor\.com\/validate-email\?[^\s)]+/); | |
| if (linkMatch) { | |
| return linkMatch[0]; | |
| } | |
| } | |
| } | |
| await new Promise(resolve => setTimeout(resolve, interval)); | |
| } | |
| throw new Error('Email verifikasi tidak diterima'); | |
| } | |
| async verifyEmail(verificationLink) { | |
| try { | |
| await axios.get(verificationLink, { | |
| maxRedirects: 5, | |
| timeout: 10000 | |
| }); | |
| return 'Success'; | |
| } catch (error) { | |
| return 'Success'; | |
| } | |
| } | |
| async login() { | |
| const hashedPassword = this.hashPassword(this.password); | |
| 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) } } | |
| ); | |
| this.bearerToken = response.data.body.token.accessToken; | |
| this.systemRefKey = response.data.body.user.systemRefKey; | |
| return response.data; | |
| } | |
| async checkCredits() { | |
| const authWithToken = { | |
| ...this.authPayload, | |
| accessToken: this.bearerToken, | |
| systemRefKey: this.systemRefKey | |
| }; | |
| const response = await axios.get( | |
| 'https://www.instagraphe.mobi/andor-login-1.0/user/getAPIUsageDetails?apiId=1', | |
| { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) } } | |
| ); | |
| return response.data.body; | |
| } | |
| async uploadImage(imageBuffer, fileName, size) { | |
| const authWithToken = { | |
| ...this.authPayload, | |
| accessToken: this.bearerToken, | |
| systemRefKey: this.systemRefKey | |
| }; | |
| const presignResponse = await axios.post( | |
| 'https://www.instagraphe.mobi/andor-media-1.0/content/generatePresignedUrls', | |
| { | |
| featureType: 'selfie', | |
| contents: [{ | |
| size: size, | |
| contentType: 'image/jpeg', | |
| assetType: 'IMG', | |
| assetRefId: '1', | |
| name: fileName | |
| }] | |
| }, | |
| { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) } } | |
| ); | |
| 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 | |
| } | |
| }); | |
| return imageUrl; | |
| } | |
| async convertToZombie(imageUrl, styleId) { | |
| const style = this.zombieStyles[styleId]; | |
| if (!style) { | |
| throw new Error(`Invalid style ID: ${styleId}. Valid IDs are 1-5`); | |
| } | |
| 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', | |
| { | |
| featureType: 'selfie', | |
| isCustomPrompt: 0, | |
| quality: 1, | |
| imageUrl: imageUrl, | |
| productId: style.productId, | |
| model: 58, | |
| textPrompt: style.prompt, | |
| productImageId: style.productImageId | |
| }, | |
| { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) } } | |
| ); | |
| 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 = 30, interval = 3000) { | |
| const authWithToken = { | |
| ...this.authPayload, | |
| accessToken: this.bearerToken, | |
| systemRefKey: this.systemRefKey | |
| }; | |
| for (let i = 0; i < maxRetries; i++) { | |
| const response = await axios.post( | |
| 'https://www.instagraphe.mobi/andor-media-1.0/aiart/checkStatus', | |
| { assetId: assetId }, | |
| { headers: { ...this.lightxHeaders, auth: JSON.stringify(authWithToken) } } | |
| ); | |
| const status = response.data.body.status; | |
| if (status === 'active') { | |
| return response.data.body; | |
| } | |
| await new Promise(resolve => setTimeout(resolve, interval)); | |
| } | |
| throw new Error('Generation timeout'); | |
| } | |
| } | |
| async function img2zombie(imageUrl, styleId = 1, maxRetries = 3) { | |
| for (let attempt = 1; attempt <= maxRetries; attempt++) { | |
| const bot = new LightXBot(); | |
| try { | |
| await bot.createTempEmail(); | |
| const randomName = 'User' + bot.generateRandomString(6); | |
| const randomPassword = 'Pass' + bot.generateRandomString(10) + '!'; | |
| await bot.registerAccount(randomName, randomPassword); | |
| const verificationLink = await bot.waitForVerificationEmail(); | |
| await bot.verifyEmail(verificationLink); | |
| await new Promise(resolve => setTimeout(resolve, 3000)); | |
| await bot.login(); | |
| const credits = await bot.checkCredits(); | |
| if (credits.remainingCalls < 1) { | |
| throw new Error('No credits available, trying new account...'); | |
| } | |
| const imageResponse = await axios.get(imageUrl, { responseType: 'arraybuffer' }); | |
| const imageBuffer = Buffer.from(imageResponse.data); | |
| const uploadedUrl = await bot.uploadImage(imageBuffer, 'image.jpg', imageBuffer.length); | |
| const result = await bot.convertToZombie(uploadedUrl, styleId); | |
| return { | |
| success: true, | |
| styleName: bot.zombieStyles[styleId].name, | |
| email: bot.email, | |
| resultUrl: result.imgUrl, | |
| thumbnailUrl: result.thumbUrl, | |
| width: result.width, | |
| height: result.height, | |
| creditsRemaining: credits.remainingCalls - 1 | |
| }; | |
| } catch (error) { | |
| if (attempt === maxRetries) { | |
| return { | |
| success: false, | |
| error: error.message, | |
| attemptsUsed: attempt | |
| }; | |
| } | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| } | |
| } | |
| } | |
| const handler = async (req, res) => { | |
| try { | |
| const { image, style, key } = req.query; | |
| if (!image || !key) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Missing required parameters: image and key' | |
| }); | |
| } | |
| const styleId = style ? parseInt(style) : 1; | |
| if (styleId < 1 || styleId > 5) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Invalid style parameter. Must be between 1-5', | |
| styles: { | |
| 1: 'Classic Zombie', | |
| 2: 'Walking Dead Zombie', | |
| 3: 'Horror Zombie', | |
| 4: 'Undead Creature', | |
| 5: 'Nightmare Zombie' | |
| } | |
| }); | |
| } | |
| const result = await img2zombie(image, styleId); | |
| if (!result.success) { | |
| return res.status(500).json({ | |
| success: false, | |
| error: result.error, | |
| attemptsUsed: result.attemptsUsed | |
| }); | |
| } | |
| 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) { | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message | |
| }); | |
| } | |
| }; | |
| module.exports = { | |
| name: 'Image to Zombie Converter', | |
| description: 'Convert photos to zombie style using AI with 5 different zombie themes', | |
| type: 'GET', | |
| routes: ['api/AI/img2zombie'], | |
| tags: ['ai', 'image', 'zombie', 'converter', 'horror'], | |
| main: ['AI'], | |
| parameters: ['image', 'style', 'key'], | |
| enabled: true, | |
| limit: 5, | |
| handler | |
| }; |