Compare commits

...

2 Commits

Author SHA1 Message Date
Chris Sexton d8683f9fd2 meme: add user name 2020-04-28 11:51:05 -04:00
Chris Sexton 13a3af1e55 meme: add plugin 2020-04-28 11:39:33 -04:00
5 changed files with 190 additions and 0 deletions

View File

@ -4,6 +4,7 @@ package config
import ( import (
"database/sql" "database/sql"
"encoding/json"
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
@ -94,6 +95,19 @@ func (c *Config) GetString(key, fallback string) string {
return configValue 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 // 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 // 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 // Look, I'm too lazy to do parsing to ensure that a comma is what the user meant

3
go.mod
View File

@ -19,12 +19,15 @@ require (
github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035 github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035
github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // 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/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect
github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/glob v0.2.3 // indirect
github.com/gocolly/colly v1.2.0 github.com/gocolly/colly v1.2.0
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // 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/gorilla/websocket v1.4.1 // indirect
github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6 github.com/james-bowman/nlp v0.0.0-20191016091239-d9dbfaff30c6
github.com/james-bowman/sparse v0.0.0-20190423065201-80c6877364c7 // indirect github.com/james-bowman/sparse v0.0.0-20190423065201-80c6877364c7 // indirect

7
go.sum
View File

@ -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/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 h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= 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/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 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ= 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/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 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI=
github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= 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/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 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/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 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= 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 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 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/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-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-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/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/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=

View File

@ -11,6 +11,7 @@ import (
"github.com/velour/catbase/plugins/achievements" "github.com/velour/catbase/plugins/achievements"
"github.com/velour/catbase/plugins/aoc" "github.com/velour/catbase/plugins/aoc"
"github.com/velour/catbase/plugins/meme"
"github.com/velour/catbase/plugins/twitter" "github.com/velour/catbase/plugins/twitter"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@ -139,6 +140,7 @@ func main() {
b.AddPlugin(impossible.New(b)) b.AddPlugin(impossible.New(b))
b.AddPlugin(cli.New(b)) b.AddPlugin(cli.New(b))
b.AddPlugin(aoc.New(b)) b.AddPlugin(aoc.New(b))
b.AddPlugin(meme.New(b))
b.AddPlugin(achievements.New(b)) b.AddPlugin(achievements.New(b))
// catches anything left, will always return true // catches anything left, will always return true
b.AddPlugin(fact.New(b)) b.AddPlugin(fact.New(b))

164
plugins/meme/meme.go Normal file
View File

@ -0,0 +1,164 @@
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")
user := r.PostForm.Get("user_name")
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: user,
})
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
}