KickerBot/captchagen/captchagen.go

126 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package captchagen
import (
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"io/ioutil"
"log"
"math/rand"
"strings"
"time"
"github.com/fogleman/gg"
)
var ImageWidth int = 600
var ImageHeight int = 400
type Logo struct {
Image image.Image
IsCorrect bool
}
type Captcha struct {
Image image.Image
CorrectAnswer int
}
var Logos []Logo = []Logo{}
var XPositions []int = []int{}
// Создаём пустое изображение с серым градиентом, грузим шрифт из /assets и возвращаем контекст вызвавшей функции
func initImage() *gg.Context {
dc := gg.NewContext(ImageWidth, ImageHeight)
if err := dc.LoadFontFace("./assets/font.ttf", 24); err != nil {
panic(err)
}
fIWidth, fIHeight := float64(ImageWidth), float64(ImageHeight)
grad := gg.NewLinearGradient(0, 0, fIWidth, fIHeight)
grad.AddColorStop(0, color.RGBA{71, 100, 106, 255})
grad.AddColorStop(1, color.RGBA{44, 43, 51, 255})
dc.SetFillStyle(grad)
dc.DrawRectangle(0, 0, fIWidth, fIHeight)
dc.Fill()
return dc
}
// Генерация капчи.
//
// На пустое изображение наносятся логотипы из списка, предварительно перемешанного.
// К изображениям также добавляются порядковые номера (начиная с 1 вместо 0),
// а правильный вариант возвращается вместе с итоговой картинкой
func GenCaptcha() Captcha {
dc := initImage()
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(Logos), func(i, j int) { Logos[i], Logos[j] = Logos[j], Logos[i] }) // Перемешиваем логотипы
rand.Shuffle(len(XPositions), func(i, j int) { XPositions[i], XPositions[j] = XPositions[j], XPositions[i] }) // И позиции
correct_answer := 0
for i, logo := range Logos {
x := XPositions[i]
y := rand.Intn(ImageHeight - logo.Image.Bounds().Dy() - 30)
dc.DrawImage(logo.Image, x, y)
if logo.IsCorrect {
correct_answer = i + 1
}
var tx float64 = float64(x) + 50.0
var ty float64 = float64(y) + 120.0
text := fmt.Sprintf("%d", i+1)
dc.SetRGB(1, 1, 1)
dc.DrawStringAnchored(text, tx, ty, 0.5, 0.5)
}
img := dc.Image()
captcha := Captcha{
Image: img,
CorrectAnswer: correct_answer,
}
return captcha
}
func (captcha *Captcha) ToReader() *bytes.Reader {
buff := new(bytes.Buffer)
err := png.Encode(buff, captcha.Image)
if err != nil {
fmt.Println("failed to create buffer", err)
}
reader := bytes.NewReader(buff.Bytes())
return reader
}
// Инициализация списка логотипов.
//
// Логотипы читаются из папки /assets рядом с исполняемым файлом.
// Принимается формат .png, логотип, представляющий правильный ответ называется godot.png
func Init() error {
files, err := ioutil.ReadDir("./assets")
if err != nil {
return err
}
for _, file := range files {
name := file.Name()
if !strings.HasSuffix(name, ".png") { // Грузим только .png
continue
}
log.Printf("%s", name) // Для отладки выводим имена файлов с логотипами
path := fmt.Sprintf("./assets/%s", name) // Составляем путь до файла
im, err := gg.LoadPNG(path) // Грузим png, возвращаем ошибку если что-то идёт не так
if err != nil {
log.Print(err)
return err
}
is_correct := strings.HasPrefix(name, "godot") // Если грузимый файл -- godot*.png - помечаем его как правильный
logo := Logo{Image: im, IsCorrect: is_correct} // Создаём в памяти структуру для капчи
Logos = append(Logos, logo) // Заносим логотип в общий список
}
for i := range Logos {
XPositions = append(XPositions, 50+i*(ImageWidth-50)/len(Logos)) // Горизонтальное расположение не рандомно: чтобы логотипы не перемешались.
}
return nil
}