mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-01-12 14:33:20 -05:00
Merge pull request #2 from SanBlig/init
This adds two additional types of captchas - LabelCaptcha and FilterCaptcha
This commit is contained in:
commit
758eb4d9b9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*.class
|
||||
*.log
|
||||
/bin/
|
||||
|
14
README.md
14
README.md
@ -1 +1,13 @@
|
||||
# lc-core
|
||||
# lc-core
|
||||
a Captcha framework
|
||||
|
||||
Currently, the following example Captchas are provided by LibreCaptcha:
|
||||
|
||||
`BlurCaptcha`
|
||||
An image of a word is blurred before being shown to the user.
|
||||
|
||||
`LabelCaptcha`
|
||||
An image that has a pair of words is created. The answer to one of the words is known and to that of the other is unknown. The user is tested on the known word, and their answer to the unknown word is recorded. If a sufficient number of users agree on their answer to the unknown word, it is transferred to the list of known words.
|
||||
|
||||
`FilterCaptcha`
|
||||
An image of a random string of alphabets is created. Then a series of image filters that add effecs such as Smear, Diffuse, and Ripple are applied to the image to make it less readable.
|
||||
|
24
src/main/scala/BlurCaptcha.scala
Normal file
24
src/main/scala/BlurCaptcha.scala
Normal file
@ -0,0 +1,24 @@
|
||||
import com.sksamuel.scrimage._
|
||||
import com.sksamuel.scrimage.filter.GaussianBlurFilter
|
||||
import java.io._
|
||||
|
||||
class BlurCaptcha extends CaptchaProvider {
|
||||
val tokenAnswer = scala.collection.mutable.Map[String, String]()
|
||||
val imageFiles = new File("known").listFiles.toList
|
||||
def getChallenge(): (Challenge) = {
|
||||
val r = scala.util.Random.nextInt(imageFiles.length)
|
||||
val chosenImage = imageFiles(r)
|
||||
var image = Image.fromStream(new FileInputStream(chosenImage))
|
||||
val blur = new GaussianBlurFilter(5)
|
||||
blur.apply(image)
|
||||
val s = scala.util.Random
|
||||
val token = s.nextInt(10000).toString
|
||||
val challenge = new Challenge(token, image)
|
||||
val answer = "about"
|
||||
tokenAnswer += token -> answer
|
||||
challenge
|
||||
}
|
||||
def checkAnswer(token: String, input: String): Boolean = {
|
||||
tokenAnswer(token) == input
|
||||
}
|
||||
}
|
63
src/main/scala/FilterCaptcha.scala
Normal file
63
src/main/scala/FilterCaptcha.scala
Normal file
@ -0,0 +1,63 @@
|
||||
import com.sksamuel.scrimage._
|
||||
import com.sksamuel.scrimage.filter._
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.{Graphics2D,Color,Font}
|
||||
|
||||
class FilterCaptcha extends CaptchaProvider {
|
||||
val tokenAnswer = scala.collection.mutable.Map[String, String]()
|
||||
def getChallenge(): Challenge = {
|
||||
val filterTypes = List(new FilterType1, new FilterType2)
|
||||
val r = new scala.util.Random
|
||||
val alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
val n = 8
|
||||
val answer = Stream.continually(r.nextInt(alphabet.size)).map(alphabet).take(n).mkString
|
||||
val token = scala.util.Random.nextInt(10000).toString
|
||||
synchronized {
|
||||
tokenAnswer += token -> answer
|
||||
}
|
||||
val canvas = new BufferedImage(225, 50, BufferedImage.TYPE_INT_RGB)
|
||||
val g = canvas.createGraphics()
|
||||
g.setColor(Color.WHITE)
|
||||
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
|
||||
g.setColor(Color.BLACK)
|
||||
g.setFont(new Font("Serif", Font.PLAIN, 30))
|
||||
g.drawString(answer, 5, 30)
|
||||
g.dispose()
|
||||
var image = new Image(canvas, ImageMetadata.empty)
|
||||
val s = scala.util.Random.nextInt(2)
|
||||
image = filterTypes(s).applyFilter(image)
|
||||
val challenge = new Challenge(token, image)
|
||||
challenge
|
||||
}
|
||||
def checkAnswer(token: String, input: String): Boolean = {
|
||||
tokenAnswer(token) == input
|
||||
}
|
||||
}
|
||||
|
||||
trait FilterType {
|
||||
def applyFilter(image: Image): Image
|
||||
}
|
||||
|
||||
class FilterType1 extends FilterType {
|
||||
override def applyFilter(image: Image): Image = {
|
||||
val blur = new GaussianBlurFilter(2)
|
||||
val smear = new SmearFilter(com.sksamuel.scrimage.filter.SmearType.Circles, 10, 10, 10, 0, 1)
|
||||
val diffuse = new DiffuseFilter(2)
|
||||
blur.apply(image)
|
||||
diffuse.apply(image)
|
||||
smear.apply(image)
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
class FilterType2 extends FilterType {
|
||||
override def applyFilter(image: Image): Image = {
|
||||
val smear = new SmearFilter(com.sksamuel.scrimage.filter.SmearType.Circles, 10, 10, 10, 0, 1)
|
||||
val diffuse = new DiffuseFilter(1)
|
||||
val ripple = new RippleFilter(com.sksamuel.scrimage.filter.RippleType.Noise, 1, 1, 0.005.toFloat, 0.005.toFloat)
|
||||
diffuse.apply(image)
|
||||
ripple.apply(image)
|
||||
smear.apply(image)
|
||||
image
|
||||
}
|
||||
}
|
68
src/main/scala/LabelCaptcha.scala
Normal file
68
src/main/scala/LabelCaptcha.scala
Normal file
@ -0,0 +1,68 @@
|
||||
import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
import scala.collection.mutable.Map
|
||||
import java.nio.file.{Files,Path,StandardCopyOption}
|
||||
import java.awt.image.BufferedImage
|
||||
import java.awt.{Graphics2D,Color}
|
||||
|
||||
class LabelCaptcha extends CaptchaProvider {
|
||||
var knownFiles = new File("known").list.toList
|
||||
var unknownFiles = new File("unknown").list.toList
|
||||
val tokenImagePair = Map[String, ImagePair]()
|
||||
val unknownAnswers = Map[String, Map[String, Int]]()
|
||||
val total = Map[String, Int]()
|
||||
for(file <- unknownFiles) {
|
||||
unknownAnswers += file -> Map[String, Int]()
|
||||
total += file -> 0
|
||||
}
|
||||
def getChallenge(): Challenge = synchronized {
|
||||
val r = scala.util.Random.nextInt(knownFiles.length)
|
||||
val s = scala.util.Random.nextInt(unknownFiles.length)
|
||||
val knownImageFile = knownFiles(r)
|
||||
val unknownImageFile = unknownFiles(s)
|
||||
val ip = new ImagePair(knownImageFile, unknownImageFile)
|
||||
val token = scala.util.Random.nextInt(10000).toString
|
||||
tokenImagePair += token -> ip
|
||||
var knownImage = ImageIO.read(new File("known/"+knownImageFile))
|
||||
var unknownImage = ImageIO.read(new File("unknown/"+unknownImageFile))
|
||||
val width = knownImage.getWidth()+unknownImage.getWidth()
|
||||
val height = List(knownImage.getHeight(), unknownImage.getHeight()).max
|
||||
val imageType = knownImage.getType()
|
||||
val finalImage = new BufferedImage(width, height, imageType)
|
||||
val g = finalImage.createGraphics()
|
||||
g.setColor(Color.WHITE)
|
||||
g.fillRect(0, 0, finalImage.getWidth(), finalImage.getHeight())
|
||||
g.drawImage(knownImage, null, 0, 0)
|
||||
g.drawImage(unknownImage, null, knownImage.getWidth(), 0)
|
||||
g.dispose()
|
||||
val challenge = new Challenge(token, finalImage)
|
||||
challenge
|
||||
}
|
||||
def checkAnswer(token: String, input: String): Boolean = synchronized {
|
||||
val expectedAnswer = tokenImagePair(token).known.split('.')(0)
|
||||
val userAnswer = input.split(' ')
|
||||
if(userAnswer(0)==expectedAnswer) {
|
||||
val unknownFile = tokenImagePair(token).unknown
|
||||
if((unknownAnswers(unknownFile)).contains(userAnswer(1))) {
|
||||
unknownAnswers(unknownFile)(userAnswer(1)) += 1
|
||||
total(unknownFile) += 1
|
||||
} else {
|
||||
unknownAnswers(unknownFile)+=(userAnswer(1)) -> 1
|
||||
total(unknownFile) += 1
|
||||
}
|
||||
if(total(unknownFile)>=3) {
|
||||
if((unknownAnswers(unknownFile)(userAnswer(1))/total(unknownFile))>=0.9) {
|
||||
unknownAnswers -= unknownFile
|
||||
Files.move(new File("unknown/"+unknownFile).toPath, new File("known/"+userAnswer(1)+".png").toPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
knownFiles = new File("known").list.toList
|
||||
unknownFiles = new File("unknown").list.toList
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImagePair(val known: String, val unknown: String)
|
@ -2,45 +2,42 @@ import com.sksamuel.scrimage._
|
||||
import java.io._
|
||||
|
||||
class CaptchaLibrary {
|
||||
var tokenAnswer = scala.collection.mutable.Map[String, String]()
|
||||
val captchas = List(new BlurCaptcha, new LabelCaptcha, new FilterCaptcha)
|
||||
var tokenCaptcha = scala.collection.mutable.Map[String, CaptchaProvider]()
|
||||
def init = {}
|
||||
def shutdown = {}
|
||||
def getChallenge(): Challenge = {
|
||||
//choose a captcha provider randomly
|
||||
val blurCaptcha = new BlurCaptcha
|
||||
val (challenge, answer) = blurCaptcha.getChallenge()
|
||||
tokenAnswer += challenge.token->answer
|
||||
val r = scala.util.Random.nextInt(2)
|
||||
val captchaInstance = captchas(r)
|
||||
val challenge = captchaInstance.getChallenge()
|
||||
tokenCaptcha += challenge.token -> captchaInstance
|
||||
challenge
|
||||
}
|
||||
def checkAnswer(token: String, input: String): Boolean = {
|
||||
if (tokenAnswer(token) == input) {
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
val result = tokenCaptcha(token).checkAnswer(token, input)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
trait CaptchaProvider {
|
||||
def getChallenge(): (Challenge, String)
|
||||
def getChallenge(): (Challenge)
|
||||
def checkAnswer(token: String, input: String): Boolean
|
||||
}
|
||||
|
||||
class Challenge(val token: String, val image: Image)
|
||||
|
||||
class Answer(val token: String, val input: String)
|
||||
|
||||
class BlurCaptcha extends CaptchaProvider {
|
||||
def getChallenge(): (Challenge, String) = {
|
||||
val inFileName = "image2.png"
|
||||
var image = Image.fromStream(new FileInputStream(inFileName))
|
||||
image = image.filter(com.sksamuel.scrimage.filter.BlurFilter)
|
||||
image.output(new File("blur.png"))
|
||||
|
||||
val r = scala.util.Random
|
||||
val token = r.nextInt(1000).toString
|
||||
val challenge = new Challenge(token, image)
|
||||
val answer = "about"
|
||||
(challenge, answer)
|
||||
object LibreCaptcha {
|
||||
def main(args: Array[String]) {
|
||||
val captcha = new CaptchaLibrary
|
||||
val challenge = captcha.getChallenge()
|
||||
println(s"Token: ${challenge.token}")
|
||||
challenge.image.output(new File("Captcha.png"))
|
||||
println("Enter your answer: ")
|
||||
val input = scala.io.StdIn.readLine()
|
||||
val result = captcha.checkAnswer(challenge.token, input)
|
||||
println(s"Result: $result")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user