118 lines
2.9 KiB
JavaScript
118 lines
2.9 KiB
JavaScript
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 };
|