150 lines
4.5 KiB
JavaScript
150 lines
4.5 KiB
JavaScript
const path = require("path");
|
|
|
|
const BINARIES_OF_GM = {
|
|
linux: path.join(__dirname, "bin/linux/"),
|
|
win32: path.join(__dirname, "bin/win32/gm.exe"),
|
|
}
|
|
|
|
const MAIN_FONT = path.join(__dirname, "fonts/Helvetica.ttf");
|
|
|
|
const gmPortable = (appPath = BINARIES_OF_GM[process.platform] ?? BINARIES_OF_GM["linux"]) => require("gm").subClass({
|
|
appPath
|
|
});
|
|
const gmGlobal = () => require("gm");
|
|
|
|
function shuffle(array) {
|
|
let currentIndex = array.length;
|
|
// While there remain elements to shuffle...
|
|
while (currentIndex != 0) {
|
|
// Pick a remaining element...
|
|
let randomIndex = Math.floor(Math.random() * currentIndex);
|
|
currentIndex--;
|
|
|
|
// And swap it with the current element.
|
|
[array[currentIndex], array[randomIndex]] = [
|
|
array[randomIndex], array[currentIndex]];
|
|
}
|
|
return array;
|
|
}
|
|
|
|
function randint (min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
}
|
|
|
|
class Captcha {
|
|
constructor (isPortable = false, customPathToGM = undefined) {
|
|
const gm = !isPortable ? gmGlobal() : gmPortable(customPathToGM);
|
|
this.image = gm(400, 150, "#ffffff");
|
|
this.fonts = null;
|
|
}
|
|
|
|
addSharp () {
|
|
this.image.stroke("#ffffff", 1);
|
|
const curCordsA = { x: 0, y: 0 };
|
|
const curCordsB = { x: 400, y: 0 };
|
|
for (let i = 0; i <= 800; i++) {
|
|
this.image.drawPoint(curCordsA.x, curCordsA.y);
|
|
curCordsA.x += randint(1, 10);
|
|
curCordsA.y = Math.floor(i / 2);
|
|
|
|
this.image.drawPoint(curCordsB.x, curCordsB.y);
|
|
curCordsB.x -= randint(1, 10);
|
|
curCordsB.y = Math.floor(i / 2);
|
|
}
|
|
}
|
|
|
|
wave () {
|
|
return this.image.wave(0.25, 111);
|
|
}
|
|
|
|
setupRandFonts (...fonts) {
|
|
if (this.fonts === null)
|
|
this.fonts = fonts;
|
|
else
|
|
this.fonts.push(...fonts);
|
|
}
|
|
|
|
getRandomCaptcha () {
|
|
const dictonary="qwertyuiopasdfghjklzxcvbnm1234567890";
|
|
let captchaCode = "";
|
|
for (let i = 0; i < 8; i++) {
|
|
captchaCode += dictonary[randint(0, dictonary.length - 1)];
|
|
}
|
|
return captchaCode;
|
|
}
|
|
|
|
randomiseFonts () {
|
|
this.fonts = [
|
|
MAIN_FONT,
|
|
path.join(__dirname, "fonts/Zametka_Parletter.otf"),
|
|
path.join(__dirname, "fonts/LeonovSP.otf"),
|
|
path.join(__dirname, "fonts/RuthlessSketchItalic.ttf"),
|
|
//path.join(__dirname, "fonts/Pshek_PY2.ttf"),
|
|
path.join(__dirname, "fonts/Daneehand Regular Cyr.ttf"),
|
|
];
|
|
}
|
|
|
|
setupCode (code) {
|
|
this.image.stroke("#ffffff" , 1);
|
|
if (typeof code !== "string" || code.length !== 8)
|
|
throw new SyntaxError("Code must be string and contains only 8 characters!");
|
|
if (this.fonts === null)
|
|
return this.image.font(MAIN_FONT, 86).drawText(10, randint(80, 120), ""+code);
|
|
else {
|
|
const codeSize = (""+code).length;
|
|
let i = 0;
|
|
while (true) {
|
|
for (let font of shuffle([].concat(this.fonts, this.fonts, this.fonts))) {
|
|
this.image.font(font, randint(80, 86));
|
|
this.image.drawText(10 + (i * randint(46, 48)), randint(80, 120), ""+code[i]);
|
|
i++;
|
|
if (i >= codeSize) break;
|
|
}
|
|
if (i >= codeSize) break;
|
|
}
|
|
return this.image;
|
|
}
|
|
}
|
|
|
|
async saveToImage (path) {
|
|
const promise = new Promise((rs, rj) => {
|
|
this.image.write(path, function (err) {
|
|
if (!err)
|
|
return rs();
|
|
rj(err);
|
|
});
|
|
});
|
|
return await promise;
|
|
}
|
|
|
|
async saveToBuffer () {
|
|
const promise = new Promise((rs, rj) => {
|
|
this.image.toBuffer("JPG", function (err, buffer) {
|
|
if (!err)
|
|
return rs(buffer);
|
|
rj(err);
|
|
});
|
|
});
|
|
return await promise;
|
|
}
|
|
}
|
|
|
|
class MasterCaptcha extends Captcha {
|
|
constructor (...p) { super(...p); }
|
|
|
|
async setupRandomCaptcha (savePath = null) {
|
|
const code = this.getRandomCaptcha();
|
|
this.randomiseFonts();
|
|
this.setupCode(code);
|
|
this.wave();
|
|
for (let _ = 0; _ < 5; _++)
|
|
this.addSharp();
|
|
if (savePath === null)
|
|
return { code, buffer: await this.saveToBuffer() };
|
|
await this.saveToImage(savePath);
|
|
return { code };
|
|
}
|
|
}
|
|
|
|
module.exports = { Captcha, MasterCaptcha };
|