mirror of https://gitgud.io/fatchan/jschan.git
parent
6e80af2eec
commit
c969814f54
7 changed files with 179 additions and 16 deletions
@ -0,0 +1,147 @@ |
||||
'use strict'; |
||||
|
||||
const gm = require('@fatchan/gm').subClass({ imageMagick: true }) |
||||
, { promisify } = require('util') |
||||
, { Captchas } = require(__dirname+'/../../../db/') |
||||
, config = require(__dirname+'/../../misc/config.js') |
||||
, uploadDirectory = require(__dirname+'/../../file/uploaddirectory.js') |
||||
, getDistorts = require(__dirname+'/../getdistorts.js') |
||||
, randomRange = promisify(require('crypto').randomInt) |
||||
, randomBytes = promisify(require('crypto').randomBytes) |
||||
, padding = 30 //pad edge of image to account for character size + distortion
|
||||
, nArrows = ['↑', '↟', '↥', '↾', '↿', '⇑', '⇡'] |
||||
, eArrows = ['➸', '→', '➳', '➵', '→', '↛', '↠', '↣', '↦', '↪', '↬', '↱', '↳', '⇉', '⇏', '⇒', '⇛', '⇝', '⇢'] |
||||
, wArrows = [ '←', '↚', '↞', '↜', '↢', '↩', '↤', '↫', '↰', '↲', '↵', '⇇', '⇍', '⇐', '⇚', '⇜', '⇠'] |
||||
, sArrows = ['↓', '↡', '↧', '↴', '⇂', '⇃', '⇊', '⇓', '⇣'] |
||||
, allArrows = [...nArrows, ...eArrows, ...wArrows, ...sArrows] |
||||
, randomBool = async (p) => { return ((await randomBytes(1))[0] > p); } |
||||
, randomOf = async (arr) => { return arr[(await randomRange(0, arr.length))]; }; |
||||
//TODO: last two could belong in lib/misc/(random?)
|
||||
|
||||
module.exports = async () => { |
||||
|
||||
const { captchaOptions } = config.get; |
||||
|
||||
const { size, trues, falses, imageSize, noise, edge } = captchaOptions.grid; |
||||
const width = imageSize+padding; //TODO: these will never be different, right?
|
||||
const height = imageSize+padding; |
||||
|
||||
const charMatrix = new Array(size).fill(false) |
||||
.map(() => new Array(size).fill(false)); |
||||
const answerMatrix = new Array(size).fill(false) |
||||
.map(() => new Array(size).fill(false)); |
||||
|
||||
//put the icon arrows should point at
|
||||
const correctRow = await randomRange(0, size); |
||||
const correctCol = await randomRange(0, size); |
||||
charMatrix[correctRow][correctCol] = await randomOf(trues); |
||||
|
||||
//put correct and incorrect arrows in the row/column
|
||||
const numArray = [...new Array(size).keys()]; |
||||
const perpendicularRows = numArray.filter(x => x !== correctRow); |
||||
for (let row of perpendicularRows) { |
||||
/*TODO: necessary to memoize these "inverse" sets of arrows? or maybe instead of even doing a 50/50 |
||||
random, it should just pick a random from allArrows then set the isCorrect based on if its in the correct set?*/ |
||||
let arrows; |
||||
const isCorrect = await randomBool(127); |
||||
if (row < correctRow) { |
||||
arrows = isCorrect ? sArrows : [...nArrows, ...eArrows, ...wArrows]; |
||||
} else if (row > correctRow) { |
||||
arrows = isCorrect ? nArrows : [...sArrows, ...eArrows, ...wArrows]; |
||||
} |
||||
charMatrix[row][correctCol] = await randomOf(arrows); |
||||
answerMatrix[row][correctCol] = isCorrect;
|
||||
} |
||||
const perpendicularCols = numArray.filter(x => x !== correctCol); |
||||
for (let col of perpendicularCols) { |
||||
let arrows; |
||||
const isCorrect = await randomBool(127); |
||||
if (col < correctCol) { |
||||
arrows = isCorrect ? eArrows : [...sArrows, ...nArrows, ...wArrows]; |
||||
charMatrix[correctRow][col] = await randomOf(arrows); |
||||
} else if (col > correctCol) { |
||||
arrows = isCorrect ? wArrows : [...sArrows, ...nArrows, ...eArrows]; |
||||
charMatrix[correctRow][col] = await randomOf(arrows); |
||||
} |
||||
charMatrix[correctRow][col] = await randomOf(arrows); |
||||
answerMatrix[correctRow][col] = isCorrect; |
||||
} |
||||
//TODO: diagonals? need more arrows
|
||||
|
||||
//this sucks
|
||||
if (!answerMatrix.flat().some(x => x === true)) { |
||||
if ((await randomBool(127)) === true) { |
||||
const randomRow = await randomOf(perpendicularRows); |
||||
const arrows = randomRow < correctRow ? sArrows : nArrows; |
||||
charMatrix[randomRow][correctCol] = await randomOf(arrows); |
||||
answerMatrix[randomRow][correctCol] = true; |
||||
} else { |
||||
const randomCol = await randomOf(perpendicularCols); |
||||
const arrows = randomCol < correctCol ? eArrows : wArrows; |
||||
charMatrix[correctRow][randomCol] = await randomOf(arrows); |
||||
answerMatrix[correctRow][randomCol] = true; |
||||
} |
||||
} |
||||
|
||||
//fill the rest with junk arrows/falses
|
||||
for (let row = 0; row < size; row++) { |
||||
for (let col = 0; col < size; col++) { |
||||
if (charMatrix[row][col] === false) { |
||||
charMatrix[row][col] = (await randomBool(80)) |
||||
? (await randomOf(allArrows)) |
||||
: (await randomOf(falses)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
const captcha = gm(width, height, '#ffffff') |
||||
.fill('#000000') |
||||
.font(__dirname+'/../font.ttf'); |
||||
|
||||
const spaceSize = (width-padding)/size; |
||||
for (let row = 0; row < size; row++) { |
||||
let cxOffset = Math.floor(spaceSize * 1.5); |
||||
for (let col = 0; col < size; col++) { |
||||
const cyOffset = captchaOptions.grid.iconYOffset; |
||||
captcha.fontSize((await randomRange(20, 30))); |
||||
captcha.drawText( |
||||
(spaceSize * col) + cyOffset, |
||||
(spaceSize * row) + cxOffset, |
||||
charMatrix[row][col] |
||||
);
|
||||
} |
||||
} |
||||
|
||||
//console.log(charMatrix, answerMatrix);
|
||||
|
||||
//insert the captcha to db and get id
|
||||
const captchaId = await Captchas.insertOne(answerMatrix.flat()).then(r => r.insertedId); |
||||
|
||||
//create an array of distortions and apply to the image, if distortion is enabled
|
||||
const { distortion, numDistorts } = captchaOptions; |
||||
if (distortion > 0) { |
||||
const distorts = await getDistorts(width, height, numDistorts, distortion); |
||||
captcha.distort(distorts, 'Shepards'); |
||||
} |
||||
|
||||
//add optional edge effect
|
||||
if (edge > 0) { |
||||
captcha.edge(edge); |
||||
} |
||||
|
||||
//add optional noise effect
|
||||
if (noise > 0) { |
||||
captcha.noise(noise); |
||||
} |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
captcha |
||||
.write(`${uploadDirectory}/captcha/${captchaId}.jpg`, (err) => { |
||||
if (err) { |
||||
return reject(err); |
||||
} |
||||
return resolve({ captchaId }); |
||||
}); |
||||
}); |
||||
|
||||
}; |
Loading…
Reference in new issue