mirror of https://github.com/velour/catbase.git
meme: add plugin
This commit is contained in:
parent
c7a3200a23
commit
13a3af1e55
|
@ -4,6 +4,7 @@ package config
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
|
@ -94,6 +95,19 @@ func (c *Config) GetString(key, fallback string) string {
|
|||
return configValue
|
||||
}
|
||||
|
||||
func (c *Config) GetMap(key string, fallback map[string]string) map[string]string {
|
||||
content := c.Get(key, "")
|
||||
if content == "" {
|
||||
return fallback
|
||||
}
|
||||
vals := map[string]string{}
|
||||
err := json.Unmarshal([]byte(content), &vals)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// GetArray returns the string slice config value for a string key
|
||||
// It will first look in the env vars for the key with ;; separated values
|
||||
// Look, I'm too lazy to do parsing to ensure that a comma is what the user meant
|
||||
|
|
3
go.mod
3
go.mod
|
@ -19,12 +19,15 @@ require (
|
|||
github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035
|
||||
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90
|
||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gocolly/colly v1.2.0
|
||||
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
|
||||
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/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.4.1 // indirect
|
||||
github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6
|
||||
github.com/james-bowman/sparse v0.0.0-20190423065201-80c6877364c7 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -43,6 +43,7 @@ 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/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=
|
||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ=
|
||||
|
@ -53,6 +54,7 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
|||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI=
|
||||
github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=
|
||||
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -62,6 +64,10 @@ github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV
|
|||
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
|
||||
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
|
||||
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
|
||||
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/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
|
@ -127,6 +133,7 @@ golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e h1:ewBcnrlKhy0GKnQ31tXkOC/G7
|
|||
golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
|
|
2
main.go
2
main.go
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/velour/catbase/plugins/achievements"
|
||||
"github.com/velour/catbase/plugins/aoc"
|
||||
"github.com/velour/catbase/plugins/meme"
|
||||
"github.com/velour/catbase/plugins/twitter"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -139,6 +140,7 @@ func main() {
|
|||
b.AddPlugin(impossible.New(b))
|
||||
b.AddPlugin(cli.New(b))
|
||||
b.AddPlugin(aoc.New(b))
|
||||
b.AddPlugin(meme.New(b))
|
||||
b.AddPlugin(achievements.New(b))
|
||||
// catches anything left, will always return true
|
||||
b.AddPlugin(fact.New(b))
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
package meme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
"github.com/velour/catbase/config"
|
||||
)
|
||||
|
||||
type MemePlugin struct {
|
||||
bot bot.Bot
|
||||
c *config.Config
|
||||
|
||||
images map[string][]byte
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *MemePlugin {
|
||||
mp := &MemePlugin{
|
||||
bot: b,
|
||||
c: b.Config(),
|
||||
images: make(map[string][]byte),
|
||||
}
|
||||
|
||||
b.Register(mp, bot.Message, mp.message)
|
||||
b.Register(mp, bot.Help, mp.help)
|
||||
mp.registerWeb(b.DefaultConnector())
|
||||
|
||||
return mp
|
||||
}
|
||||
|
||||
func (p *MemePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *MemePlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||
formats := p.c.GetMap("meme.memes", defaultFormats)
|
||||
msg := "Use `/meme [format] [text]` to create a meme.\nI know the following formats:"
|
||||
for k := range formats {
|
||||
msg += "\n" + k
|
||||
}
|
||||
p.bot.Send(c, bot.Message, message.Channel, msg)
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *MemePlugin) registerWeb(c bot.Connector) {
|
||||
http.HandleFunc("/slash/meme", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
log.Debug().Msgf("Meme:\n%+v", r.PostForm.Get("text"))
|
||||
channel := r.PostForm.Get("channel_id")
|
||||
log.Debug().Msgf("channel: %s", channel)
|
||||
|
||||
parts := strings.SplitN(r.PostForm.Get("text"), " ", 2)
|
||||
log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", r.PostForm.Get("text"))
|
||||
w.WriteHeader(200)
|
||||
|
||||
id := p.genMeme(parts[0], parts[1])
|
||||
baseURL := p.c.Get("BaseURL", `https://catbase.velour.ninja`)
|
||||
u, _ := url.Parse(baseURL)
|
||||
u.Path = path.Join(u.Path, "meme", "img", id)
|
||||
|
||||
log.Debug().Msgf("image is at %s", u.String())
|
||||
p.bot.Send(c, bot.Message, channel, "", bot.ImageAttachment{
|
||||
URL: u.String(),
|
||||
AltTxt: parts[1],
|
||||
})
|
||||
w.Write(nil)
|
||||
})
|
||||
|
||||
http.HandleFunc("/meme/img/", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, file := path.Split(r.URL.Path)
|
||||
id := file
|
||||
img := p.images[id]
|
||||
w.Write(img)
|
||||
})
|
||||
}
|
||||
|
||||
func DownloadTemplate(file string) image.Image {
|
||||
url := fmt.Sprintf("https://imgflip.com/s/meme/%s", file)
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Error().Msgf("%s template from %s failed because of %v", file, url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
image, _, err := image.Decode(res.Body)
|
||||
if err != nil {
|
||||
log.Error().Msgf("Could not decode %s because of %v", file, err)
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
var defaultFormats = map[string]string{
|
||||
"fry": "Futurama-Fry.jpg",
|
||||
"aliens": "Ancient-Aliens.jpg",
|
||||
"doge": "Doge.jpg",
|
||||
"simply": "One-Does-Not-Simply.jpg",
|
||||
"wonka": "Creepy-Condescending-Wonka.jpg",
|
||||
"grumpy": "Grumpy-Cat.jpg",
|
||||
"raptor": "Philosoraptor.jpg",
|
||||
}
|
||||
|
||||
func (p *MemePlugin) genMeme(meme, text string) string {
|
||||
const fontSize = 36
|
||||
|
||||
formats := p.c.GetMap("meme.memes", defaultFormats)
|
||||
|
||||
path := uuid.New().String()
|
||||
|
||||
imgName, ok := formats[meme]
|
||||
if !ok {
|
||||
imgName = meme
|
||||
}
|
||||
img := DownloadTemplate(imgName)
|
||||
r := img.Bounds()
|
||||
w := r.Dx()
|
||||
h := r.Dy()
|
||||
|
||||
m := gg.NewContext(w, h)
|
||||
m.DrawImage(img, 0, 0)
|
||||
fontLocation := p.c.Get("meme.font", "impact.ttf")
|
||||
err := m.LoadFontFace(fontLocation, fontSize) // problem
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not load font")
|
||||
}
|
||||
|
||||
// Apply black stroke
|
||||
m.SetHexColor("#000")
|
||||
strokeSize := 6
|
||||
for dy := -strokeSize; dy <= strokeSize; dy++ {
|
||||
for dx := -strokeSize; dx <= strokeSize; dx++ {
|
||||
// give it rounded corners
|
||||
if dx*dx+dy*dy >= strokeSize*strokeSize {
|
||||
continue
|
||||
}
|
||||
x := float64(w/2 + dx)
|
||||
y := float64(h - fontSize + dy)
|
||||
m.DrawStringAnchored(text, x, y, 0.5, 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply white fill
|
||||
m.SetHexColor("#FFF")
|
||||
m.DrawStringAnchored(text, float64(w)/2, float64(h)-fontSize, 0.5, 0.5)
|
||||
|
||||
i := bytes.Buffer{}
|
||||
png.Encode(&i, m.Image())
|
||||
p.images[path] = i.Bytes()
|
||||
|
||||
log.Debug().Msgf("Saved to %s\n", path)
|
||||
|
||||
return path
|
||||
}
|
Loading…
Reference in New Issue