const fs = require("fs"); const path = require("path"); const gm = require("./gm"); const mime = require("./mimetype"); function shuffle(array) { for (let i = array.length - 1; i > 0; i--) { let j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } function readFixedBytes(pathToFile, size = 16) { const fd = fs.openSync(pathToFile, "r"); try { const buffer = Buffer.alloc(size); fs.readSync(fd, buffer, 0, size, 0); return buffer; } catch (err) { console.error(err); } finally { fs.closeSync(fd); } } async function isImage (pathToFile, requiredSize=null) { const contentType = mime.byPath(pathToFile).contentType; const isImageMime = /^image\//m.test(contentType); if ( !isImageMime || !requiredSize || !Array.isArray(requiredSize) ) return isImageMime; const [width, height] = requiredSize; const { width:ow, height:oh } = await gm.getSize( fs.readFileSync(pathToFile) ); return width === ow && height === oh; } async function getImageFromFolderGeneral (imagesFolder) { // Get list of images from folder const entries = fs.readdirSync(imagesFolder, { withFileTypes: true }); const images = []; for (let entry of entries) { if (!entry.isDirectory() && !!(await isImage(path.join(imagesFolder, entry.name), [150, 150]))) { images.push(path.join(imagesFolder, entry.name)); } } if (images.length === 0) throw new Error(imagesFolder, "has not required images (150x150 px)"); shuffle(images); //console.debug(images); return images[0]; } function genHashGeneral(length = 3) { return Buffer.from( [...new Array(length)].map((x) => Math.round(Math.random() * 255)), ).toString("hex"); } async function generateTask (imagesFolder, options = {}) { if (!path.isAbsolute(imagesFolder)) imagesFolder = path.join(process.cwd(), imagesFolder); // Define tools if (!options.genHash) options.genHash = genHashGeneral; if (!options.getImageFromFolder) options.getImageFromFolder = getImageFromFolderGeneral; const choosenImage = fs.readFileSync(await getImageFromFolderGeneral(imagesFolder)); // Split the image by 9 shards const fragments = new Object(); let answer = ""; const addOps = []; for (let row = 0; row < 3; row++) for (let col = 0; col < 3; col++) { let key; do { key = options.genHash(); } while (!!fragments[key]); answer += key; const shardBytes = await gm.repixelise( await gm.cropImage(choosenImage, 50, 50, col * 50, row * 50), ); addOps.push( () => (fragments[key] = `data:image/png;base64,${shardBytes.toString("base64")}`), ); } shuffle(addOps); addOps.forEach((f) => f()); const successCode = options.genHash(8); return { openPuzzleCaptchaAPI: { getTask: { isError: false, mode: "3x3", fragments }, taskSolved: { isError: false, isSuccess: true, successCode }, }, answer, }; } module.exports = { generateTask };