mirror of https://github.com/velour/catbase.git
parent
6d004d8d1f
commit
7199f8ed13
|
@ -51,6 +51,8 @@ type ImageAttachment struct {
|
|||
Height int
|
||||
}
|
||||
|
||||
type VideoAttachment ImageAttachment
|
||||
|
||||
type Request struct {
|
||||
Conn Connector
|
||||
Kind Kind
|
||||
|
|
|
@ -96,8 +96,15 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...i
|
|||
|
||||
for _, arg := range args {
|
||||
switch a := arg.(type) {
|
||||
case bot.VideoAttachment:
|
||||
embeds = &discordgo.MessageEmbed{}
|
||||
embeds.Description = a.AltTxt
|
||||
embeds.Video = &discordgo.MessageEmbedVideo{
|
||||
URL: a.URL,
|
||||
Width: a.Width,
|
||||
Height: a.Height,
|
||||
}
|
||||
case bot.ImageAttachment:
|
||||
//embeds.URL = a.URL
|
||||
embeds = &discordgo.MessageEmbed{}
|
||||
embeds.Description = a.AltTxt
|
||||
embeds.Image = &discordgo.MessageEmbedImage{
|
||||
|
|
2
go.mod
2
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.5.0
|
||||
github.com/andybalholm/cascadia v1.1.0 // indirect
|
||||
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812 // indirect
|
||||
github.com/antchfx/htmlquery v1.2.0 // indirect
|
||||
github.com/antchfx/xmlquery v1.2.0 // indirect
|
||||
github.com/antchfx/xpath v1.1.1 // indirect
|
||||
|
@ -30,6 +31,7 @@ require (
|
|||
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
|
||||
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect
|
||||
github.com/itchyny/gojq v0.12.3
|
||||
github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6
|
||||
|
|
6
go.sum
6
go.sum
|
@ -15,6 +15,8 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
|
|||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812 h1:WBBv0ka2SO7Ut4bpskb87E9cHNnJabqA6VoBTex0Jng=
|
||||
github.com/andybons/gogif v0.0.0-20140526152223-16d573594812/go.mod h1:lkVwYUDYv/mJZK69J7BP7HRUhHEAone7OQHFBRnhQdQ=
|
||||
github.com/antchfx/htmlquery v1.2.0 h1:oKShnsGlnOHX6t4uj5OHgLKkABcJoqnXpqnscoi9Lpw=
|
||||
github.com/antchfx/htmlquery v1.2.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
|
||||
github.com/antchfx/xmlquery v1.2.0 h1:1nrzsSN5mFrlqFWSK9byiq/qXKE7O2vivYzhv1Ksnfw=
|
||||
|
@ -41,6 +43,8 @@ github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc h1:tP7tkU+vI
|
|||
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc/go.mod h1:ORH5Qp2bskd9NzSfKqAF7tKfONsEkCarTE5ESr/RVBw=
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
|
||||
|
@ -71,6 +75,8 @@ github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhS
|
|||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
|
|
2
main.go
2
main.go
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
"github.com/velour/catbase/connectors/discord"
|
||||
"github.com/velour/catbase/plugins/gifmeme"
|
||||
"github.com/velour/catbase/plugins/giphy"
|
||||
"github.com/velour/catbase/plugins/gpt3"
|
||||
"github.com/velour/catbase/plugins/last"
|
||||
|
@ -162,6 +163,7 @@ func main() {
|
|||
b.AddPlugin(impossible.New(b))
|
||||
b.AddPlugin(cli.New(b))
|
||||
b.AddPlugin(aoc.New(b))
|
||||
b.AddPlugin(gifmeme.New(b))
|
||||
b.AddPlugin(meme.New(b))
|
||||
b.AddPlugin(achievements.New(b))
|
||||
b.AddPlugin(sms.New(b))
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package gifmeme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/andybons/gogif"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/velour/catbase/config"
|
||||
"github.com/velour/catbase/plugins/meme"
|
||||
"image"
|
||||
"image/gif"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const tpc = 5
|
||||
const nColors = 64
|
||||
|
||||
func getGif(c *config.Config, s meme.Specification) ([]byte, int, int, error) {
|
||||
t0 := time.Now()
|
||||
resp, err := http.Get(s.ImageURL)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
g, err := gif.DecodeAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
log.Debug().Msgf("Len before adding text: %d", len(g.Image))
|
||||
err = addText(c, g, s.Configs)
|
||||
log.Debug().Msgf("Len after adding text: %d", len(g.Image))
|
||||
log.Debug().Msgf("%v elapsed adding text", time.Now().Sub(t0))
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
t1 := time.Now()
|
||||
err = gif.EncodeAll(out, g)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
log.Debug().Msgf("%v elapsed encoding gif", time.Now().Sub(t1))
|
||||
w, h := g.Image[0].Bounds().Max.X, g.Image[0].Bounds().Max.Y
|
||||
return out.Bytes(), w, h, nil
|
||||
}
|
||||
|
||||
func addText(c *config.Config, g *gif.GIF, text []meme.Text) error {
|
||||
totalTime := calcTime(g)
|
||||
framesNeeded, avgFrames := calcTextTime(text)
|
||||
loops := 1
|
||||
|
||||
if totalTime < framesNeeded {
|
||||
loops = framesNeeded / totalTime
|
||||
totalTime = loops * totalTime
|
||||
} else {
|
||||
framesNeeded = totalTime
|
||||
}
|
||||
|
||||
log.Debug().Msgf("We need %d loops for %d total time with %d frames",
|
||||
loops, totalTime, framesNeeded)
|
||||
|
||||
frames := []*image.Paletted{}
|
||||
delays := []int{}
|
||||
|
||||
idx := 0
|
||||
|
||||
toicfg := meme.MkTextOnImageConfig(c)
|
||||
|
||||
bounds := g.Image[0].Bounds()
|
||||
maxSz := max(bounds.Max.X, bounds.Max.Y)
|
||||
if toicfg.MaxSz > float64(maxSz) {
|
||||
toicfg.MaxSz = float64(maxSz)
|
||||
}
|
||||
|
||||
currText := 0
|
||||
currTextFrames := 0
|
||||
|
||||
log.Debug().Msgf("Writing text configs %+v on image", text)
|
||||
|
||||
log.Debug().Msgf("Starting text with %+v", text[currText])
|
||||
|
||||
for i := 0; i < framesNeeded; i++ {
|
||||
img := g.Image[idx]
|
||||
i, err := meme.TextOnImage(toicfg, img, []meme.Text{text[currText]})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error encoding gif")
|
||||
return err
|
||||
}
|
||||
frames = append(frames, imgToPaletted(i))
|
||||
delays = append(delays, g.Delay[idx])
|
||||
idx = (idx + 1) % len(g.Image)
|
||||
currTextFrames++
|
||||
if currTextFrames > avgFrames {
|
||||
currText = (currText + 1) % len(text)
|
||||
currTextFrames = 0
|
||||
log.Debug().Msgf("Switching text to %+v", text[currText])
|
||||
}
|
||||
}
|
||||
|
||||
g.Image = frames
|
||||
g.Delay = delays
|
||||
g.Disposal = nil
|
||||
g.Config = image.Config{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcTextTime(text []meme.Text) (int, int) {
|
||||
total := 1
|
||||
for _, frame := range text {
|
||||
total += tpc * len(frame.Text)
|
||||
}
|
||||
return total, total / len(text)
|
||||
}
|
||||
|
||||
func calcTime(g *gif.GIF) int {
|
||||
total := 0
|
||||
for _, frameDelay := range g.Delay {
|
||||
total += frameDelay
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func imgToPaletted(i image.Image) *image.Paletted {
|
||||
bounds := i.Bounds()
|
||||
p := image.NewPaletted(bounds, nil)
|
||||
quantizer := gogif.MedianCutQuantizer{NumColor: nColors}
|
||||
quantizer.Quantize(p, bounds, i, image.Point{})
|
||||
return p
|
||||
}
|
||||
|
||||
func max(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package gifmeme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/config"
|
||||
"github.com/velour/catbase/plugins/meme"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type gifMap map[string][]byte
|
||||
|
||||
// Plugin creates gifs with text
|
||||
type Plugin struct {
|
||||
b bot.Bot
|
||||
c *config.Config
|
||||
db *sqlx.DB
|
||||
h bot.HandlerTable
|
||||
|
||||
gifs gifMap
|
||||
}
|
||||
|
||||
// New creates a new Plugin
|
||||
func New(b bot.Bot) *Plugin {
|
||||
p := &Plugin{
|
||||
b: b,
|
||||
c: b.Config(),
|
||||
db: b.DB(),
|
||||
gifs: make(gifMap),
|
||||
}
|
||||
p.register()
|
||||
p.registerWeb()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Plugin) register() {
|
||||
p.h = bot.HandlerTable{
|
||||
{
|
||||
Kind: bot.Message, IsCmd: true,
|
||||
Regex: regexp.MustCompile(`(?i)^gifmeme (?P<gif>\S+) (?P<text>.+)$`),
|
||||
Handler: p.gifmemeCmd,
|
||||
},
|
||||
}
|
||||
p.b.RegisterTable(p, p.h)
|
||||
}
|
||||
|
||||
func (p *Plugin) gifmemeCmd(r bot.Request) bool {
|
||||
gifs := p.c.GetMap("gifmeme.memes", map[string]string{
|
||||
"key": "https://media.giphy.com/media/3o6Zt4HU9uwXmXSAuI/giphy.gif",
|
||||
})
|
||||
u, err := url.Parse(gifs[r.Values["gif"]])
|
||||
if checkErr(p.b, r, err) {
|
||||
return true
|
||||
}
|
||||
texts := strings.Split(r.Values["text"], "||")
|
||||
configs := []meme.Text{}
|
||||
// do this till we have configs
|
||||
for _, t := range texts {
|
||||
configs = append(configs, meme.Text{
|
||||
XPerc: 0.5,
|
||||
YPerc: 0.9,
|
||||
Text: t,
|
||||
Caps: true,
|
||||
})
|
||||
}
|
||||
s := meme.Specification{
|
||||
ImageURL: u.String(),
|
||||
Configs: configs,
|
||||
}
|
||||
gif, w, h, err := getGif(p.c, s)
|
||||
if checkErr(p.b, r, err) {
|
||||
return true
|
||||
}
|
||||
log.Debug().Msgf("Saving gif %s, len: %d", s.ID(), len(gif))
|
||||
p.gifs[s.ID()] = gif
|
||||
u = p.urlForGif(s)
|
||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, u.String())
|
||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "", bot.ImageAttachment{
|
||||
URL: u.String(),
|
||||
AltTxt: fmt.Sprintf("%v", texts),
|
||||
Width: w,
|
||||
Height: h,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Plugin) urlForGif(s meme.Specification) *url.URL {
|
||||
baseURL := p.c.Get("BaseURL", "")
|
||||
u, _ := url.Parse(baseURL)
|
||||
u.Path = path.Join(u.Path, "gifmeme", "api", "gif", s.ID()+".gif")
|
||||
return u
|
||||
}
|
||||
|
||||
func checkErr(b bot.Bot, r bot.Request, err error) bool {
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Error: %s", err)
|
||||
b.Send(r.Conn, bot.Message, r.Msg.Channel, msg)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package gifmeme
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/rs/zerolog/log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (p *Plugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.Handle("/api/gif/{id}.gif", handlers.CompressHandlerLevel(http.HandlerFunc(p.handleGif), gzip.BestCompression))
|
||||
p.b.RegisterWeb(r, "/gifmeme")
|
||||
}
|
||||
|
||||
func (p *Plugin) handleGif(w http.ResponseWriter, r *http.Request) {
|
||||
//id := r.URL.Query().Get("id")
|
||||
id := chi.URLParam(r, "id")
|
||||
gif, ok := p.gifs[id]
|
||||
keys := []string{}
|
||||
for k, _ := range p.gifs {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
log.Debug().Msgf("Looking for %s in gifs: %v", id, keys)
|
||||
if !ok {
|
||||
w.WriteHeader(404)
|
||||
e := struct {
|
||||
error error
|
||||
}{fmt.Errorf("%s not found", id)}
|
||||
jsonErr, _ := json.Marshal(e)
|
||||
w.Write(jsonErr)
|
||||
}
|
||||
w.Write(gif)
|
||||
}
|
|
@ -2,6 +2,7 @@ package meme
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
|
@ -25,6 +26,8 @@ import (
|
|||
"github.com/velour/catbase/config"
|
||||
)
|
||||
|
||||
var fontSizes = []float64{48, 36, 24, 16, 12}
|
||||
|
||||
type MemePlugin struct {
|
||||
bot bot.Bot
|
||||
c *config.Config
|
||||
|
@ -37,7 +40,7 @@ type cachedImage struct {
|
|||
repr []byte
|
||||
}
|
||||
|
||||
type memeText struct {
|
||||
type Text struct {
|
||||
XPerc float64 `json:"x"`
|
||||
YPerc float64 `json:"y"`
|
||||
Text string `json:"t",omitempty`
|
||||
|
@ -45,19 +48,25 @@ type memeText struct {
|
|||
Font string `json:"f",omitempty`
|
||||
}
|
||||
|
||||
type specification struct {
|
||||
type Specification struct {
|
||||
ImageURL string
|
||||
StampURL string
|
||||
Configs []memeText
|
||||
Configs []Text
|
||||
}
|
||||
|
||||
func (s specification) toJSON() string {
|
||||
func (s Specification) ID() string {
|
||||
out, _ := json.Marshal(s)
|
||||
b64 := base64.StdEncoding.EncodeToString(out)
|
||||
return b64
|
||||
}
|
||||
|
||||
func (s Specification) toJSON() string {
|
||||
out, _ := json.Marshal(s)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func SpecFromJSON(input []byte) (specification, error) {
|
||||
out := specification{}
|
||||
func SpecFromJSON(input []byte) (Specification, error) {
|
||||
out := Specification{}
|
||||
err := json.Unmarshal(input, &out)
|
||||
return out, err
|
||||
}
|
||||
|
@ -149,7 +158,7 @@ func (p *MemePlugin) sendMeme(c bot.Connector, channel, channelName, msgID strin
|
|||
|
||||
log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text)
|
||||
|
||||
var config []memeText
|
||||
var config []Text
|
||||
|
||||
message = strings.TrimPrefix(message, "`")
|
||||
message = strings.TrimSuffix(message, "`")
|
||||
|
@ -199,7 +208,7 @@ func (p *MemePlugin) sendMeme(c bot.Connector, channel, channelName, msgID strin
|
|||
|
||||
stampURL := p.stamp(c, format, from.ID)
|
||||
|
||||
spec := specification{
|
||||
spec := Specification{
|
||||
ImageURL: imgURL,
|
||||
StampURL: stampURL,
|
||||
Configs: config,
|
||||
|
@ -304,7 +313,7 @@ var defaultFormats = map[string]string{
|
|||
"raptor": "https://imgflip.com/s/meme/Philosoraptor.jpg",
|
||||
}
|
||||
|
||||
func (p *MemePlugin) findFontSize(config []memeText, fontLocation string, w, h int, sizes []float64) float64 {
|
||||
func findFontSize(config []Text, fontLocation string, w, h int, sizes []float64) float64 {
|
||||
fontSize := 12.0
|
||||
|
||||
m := gg.NewContext(w, h)
|
||||
|
@ -341,8 +350,8 @@ func (p *MemePlugin) findFontSize(config []memeText, fontLocation string, w, h i
|
|||
return fontSize
|
||||
}
|
||||
|
||||
func defaultFormatConfig() []memeText {
|
||||
return []memeText{
|
||||
func defaultFormatConfig() []Text {
|
||||
return []Text{
|
||||
{XPerc: 0.5, YPerc: 0.05, Caps: true},
|
||||
{XPerc: 0.5, YPerc: 0.95, Caps: true},
|
||||
}
|
||||
|
@ -368,9 +377,7 @@ func (p *MemePlugin) checkMeme(imgURL string) (int, int, error) {
|
|||
return img.Bounds().Dx(), img.Bounds().Dy(), err
|
||||
}
|
||||
|
||||
func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
||||
fontSizes := []float64{48, 36, 24, 16, 12}
|
||||
|
||||
func (p *MemePlugin) genMeme(spec Specification) ([]byte, error) {
|
||||
jsonSpec := spec.toJSON()
|
||||
if cached, ok := p.images[jsonSpec]; ok {
|
||||
log.Debug().Msgf("Returning cached image for %s", jsonSpec)
|
||||
|
@ -391,28 +398,7 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
r := img.Bounds()
|
||||
w := r.Dx()
|
||||
h := r.Dy()
|
||||
|
||||
maxSz := p.c.GetFloat64("maxImgSz", 750.0)
|
||||
|
||||
if w > h {
|
||||
scale := maxSz / float64(w)
|
||||
w = int(float64(w) * scale)
|
||||
h = int(float64(h) * scale)
|
||||
} else {
|
||||
scale := maxSz / float64(h)
|
||||
w = int(float64(w) * scale)
|
||||
h = int(float64(h) * scale)
|
||||
}
|
||||
|
||||
log.Debug().Msgf("trynig to resize to %v, %v", w, h)
|
||||
img = resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
|
||||
r = img.Bounds()
|
||||
w = r.Dx()
|
||||
h = r.Dy()
|
||||
log.Debug().Msgf("resized to %v, %v", w, h)
|
||||
img, err = TextOnImage(MkTextOnImageConfig(p.c), img, spec.Configs)
|
||||
|
||||
if spec.StampURL != "" {
|
||||
img, err = p.applyStamp(img, spec.StampURL)
|
||||
|
@ -424,13 +410,60 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
i := bytes.Buffer{}
|
||||
png.Encode(&i, img)
|
||||
p.images[jsonSpec] = &cachedImage{time.Now(), i.Bytes()}
|
||||
|
||||
log.Debug().Msgf("Saved to %s\n", jsonSpec)
|
||||
|
||||
return p.images[jsonSpec].repr, nil
|
||||
}
|
||||
|
||||
type TextOnImageConfig struct {
|
||||
MaxSz float64
|
||||
Font string
|
||||
}
|
||||
|
||||
// MkTextOnImageConfig saves us some db lookups for gif creation
|
||||
func MkTextOnImageConfig(c *config.Config) TextOnImageConfig {
|
||||
return TextOnImageConfig{
|
||||
MaxSz: c.GetFloat64("maxImgSz", 750.0),
|
||||
Font: c.Get("meme.font", "impact.ttf"),
|
||||
}
|
||||
}
|
||||
|
||||
func TextOnImage(c TextOnImageConfig, img image.Image, configs []Text) (image.Image, error) {
|
||||
r := img.Bounds()
|
||||
w := r.Dx()
|
||||
h := r.Dy()
|
||||
|
||||
maxSz := c.MaxSz
|
||||
|
||||
scale := 1.0
|
||||
if w > h {
|
||||
scale = maxSz / float64(w)
|
||||
w = int(float64(w) * scale)
|
||||
h = int(float64(h) * scale)
|
||||
} else {
|
||||
scale = maxSz / float64(h)
|
||||
w = int(float64(w) * scale)
|
||||
h = int(float64(h) * scale)
|
||||
}
|
||||
|
||||
if scale > 1.1 || scale < 0.9 {
|
||||
img = resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
|
||||
}
|
||||
|
||||
r = img.Bounds()
|
||||
w = r.Dx()
|
||||
h = r.Dy()
|
||||
m := gg.NewContext(w, h)
|
||||
m.DrawImage(img, 0, 0)
|
||||
defaultFont := p.c.Get("meme.font", "impact.ttf")
|
||||
defaultFont := c.Font
|
||||
|
||||
for i, c := range spec.Configs {
|
||||
for i, c := range configs {
|
||||
if c.Caps {
|
||||
spec.Configs[i].Text = strings.ToUpper(c.Text)
|
||||
configs[i].Text = strings.ToUpper(c.Text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,12 +476,12 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
if dx*dx+dy*dy >= strokeSize*strokeSize {
|
||||
continue
|
||||
}
|
||||
for _, c := range spec.Configs {
|
||||
for _, c := range configs {
|
||||
fontLocation := c.Font
|
||||
if fontLocation == "" {
|
||||
fontLocation = defaultFont
|
||||
}
|
||||
fontSize := p.findFontSize(spec.Configs, fontLocation, w, h, fontSizes)
|
||||
fontSize := findFontSize(configs, fontLocation, w, h, fontSizes)
|
||||
m.LoadFontFace(fontLocation, fontSize)
|
||||
x := float64(w)*c.XPerc + float64(dx)
|
||||
y := float64(h)*c.YPerc + float64(dy)
|
||||
|
@ -459,25 +492,18 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
|
||||
// Apply white fill
|
||||
m.SetHexColor("#FFF")
|
||||
for _, c := range spec.Configs {
|
||||
for _, c := range configs {
|
||||
fontLocation := c.Font
|
||||
if fontLocation == "" {
|
||||
fontLocation = defaultFont
|
||||
}
|
||||
fontSize := p.findFontSize(spec.Configs, fontLocation, w, h, fontSizes)
|
||||
fontSize := findFontSize(configs, fontLocation, w, h, fontSizes)
|
||||
m.LoadFontFace(fontLocation, fontSize)
|
||||
x := float64(w) * c.XPerc
|
||||
y := float64(h) * c.YPerc
|
||||
m.DrawStringAnchored(c.Text, x, y, 0.5, 0.5)
|
||||
}
|
||||
|
||||
i := bytes.Buffer{}
|
||||
png.Encode(&i, m.Image())
|
||||
p.images[jsonSpec] = &cachedImage{time.Now(), i.Bytes()}
|
||||
|
||||
log.Debug().Msgf("Saved to %s\n", jsonSpec)
|
||||
|
||||
return p.images[jsonSpec].repr, nil
|
||||
return m.Image(), nil
|
||||
}
|
||||
|
||||
func (p *MemePlugin) applyStamp(img image.Image, bullyURL string) (image.Image, error) {
|
||||
|
|
Loading…
Reference in New Issue