commit 0caafd9f813b5a2cb4295ffa723ff904a9b0efc9 Author: Christopher Brown Date: Thu Dec 21 05:50:48 2017 -0600 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2132d39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/plugin.exe +/mattermost-memes-plugin.tar.gz +/memelibrary/assets.go +/dist +/vendor diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6506513 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +.PHONY: test + +all: test dist + +TAR_PLUGIN_EXE_TRANSFORM = --transform 'flags=r;s|dist/intermediate/plugin_.*|plugin.exe|' +ifneq (,$(findstring bsdtar,$(shell tar --version))) + TAR_PLUGIN_EXE_TRANSFORM = -s '|dist/intermediate/plugin_.*|plugin.exe|' +endif + +dist: vendor memelibrary/assets.go $(shell go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}} {{end}}' ./...) plugin.yaml + rm -rf ./dist + go get github.com/mitchellh/gox + $(shell go env GOPATH)/bin/gox -osarch='darwin/amd64 linux/amd64 windows/amd64' -output 'dist/intermediate/plugin_{{.OS}}_{{.Arch}}' + tar -czvf dist/mattermost-meme-plugin-darwin-amd64.tar.gz $(TAR_PLUGIN_EXE_TRANSFORM) dist/intermediate/plugin_darwin_amd64 plugin.yaml + tar -czvf dist/mattermost-meme-plugin-linux-amd64.tar.gz $(TAR_PLUGIN_EXE_TRANSFORM) dist/intermediate/plugin_linux_amd64 plugin.yaml + tar -czvf dist/mattermost-meme-plugin-windows-amd64.tar.gz $(TAR_PLUGIN_EXE_TRANSFORM) dist/intermediate/plugin_windows_amd64.exe plugin.yaml + rm -rf dist/intermediate + +memelibrary/assets.go: $(shell find memelibrary/assets) + go get github.com/jteeuwen/go-bindata/... + $(shell go env GOPATH)/bin/go-bindata -o $@ -pkg memelibrary -prefix memelibrary/assets/ -ignore '(^|/)\..*' memelibrary/assets/... + +mattermost-memes-plugin.tar.gz: vendor memelibrary/assets.go $(shell go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}} {{end}}' ./...) plugin.yaml + go build -o plugin.exe + tar -czvf $@ plugin.exe plugin.yaml + rm plugin.exe + +test: vendor + go test -v ./... + +vendor: glide.lock + go get github.com/Masterminds/glide + $(shell go env GOPATH)/bin/glide install diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e6b992 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# mattermost-plugin-memes diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..3bd2ade --- /dev/null +++ b/glide.lock @@ -0,0 +1,64 @@ +hash: 5c78b0337c128afa41779ef112bf41aea7a92c9094e13fab7223d831de386916 +updated: 2017-12-10T01:37:04.279381-06:00 +imports: +- name: github.com/alecthomas/log4go + version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c + repo: https://github.com/mattermost/log4go.git +- name: github.com/golang/freetype + version: e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4 + subpackages: + - raster + - truetype +- name: github.com/gorilla/context + version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 +- name: github.com/gorilla/mux + version: 7f08801859139f86dfafd1c296e2cba9a80d292e +- name: github.com/gorilla/websocket + version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b +- name: github.com/kballard/go-shellquote + version: cd60e84ee657ff3dc51de0b4f55dd299a3e136f2 +- name: github.com/mattermost/mattermost-server + version: 4c17bdff1bb871fb31520b7b547f584c53ed854f + subpackages: + - model + - plugin + - plugin/rpcplugin +- name: github.com/nicksnyder/go-i18n + version: 0dc1626d56435e9d605a29875701721c54bc9bbd + subpackages: + - i18n + - i18n/bundle + - i18n/language + - i18n/translation +- name: github.com/pborman/uuid + version: e790cca94e6cc75c7064b1332e63811d4aae1a53 +- name: github.com/pelletier/go-toml + version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd +- name: github.com/pkg/errors + version: f15c970de5b76fac0b59abb32d62c17cc7bed265 +- name: golang.org/x/crypto + version: 94eea52f7b742c7cbe0b03b22f0c4c8631ece122 + subpackages: + - bcrypt + - blowfish +- name: golang.org/x/image + version: e5db4c466346ada62f27934dab20e4b6f6bda285 + subpackages: + - font + - math/fixed +- name: gopkg.in/yaml.v2 + version: 287cf08546ab5e7e37d55a84f7ed3fd1db036de5 +testImports: +- name: github.com/davecgh/go-spew + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..b4d79df --- /dev/null +++ b/glide.yaml @@ -0,0 +1,24 @@ +package: github.com/mattermost/mattermost-plugin-memes +import: +- package: github.com/golang/freetype + subpackages: + - truetype +- package: github.com/gorilla/mux + version: ^1.6.0 +- package: github.com/kballard/go-shellquote +- package: github.com/mattermost/mattermost-server + version: ^4.5.0-rc1 + subpackages: + - plugin + - plugin/rpcplugin +- package: golang.org/x/image + subpackages: + - font + - math/fixed +- package: gopkg.in/yaml.v2 +testImport: +- package: github.com/stretchr/testify + version: ^1.1.4 + subpackages: + - assert + - require diff --git a/meme/template.go b/meme/template.go new file mode 100644 index 0000000..e115e3e --- /dev/null +++ b/meme/template.go @@ -0,0 +1,27 @@ +package meme + +import ( + "image" + "image/draw" +) + +type Template struct { + Name string + Image image.Image + TextSlots []*TextSlot +} + +func (t *Template) Render(text []string) (image.Image, error) { + b := t.Image.Bounds() + img := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy())) + draw.Draw(img, img.Bounds(), t.Image, b.Min, draw.Src) + + for i, slot := range t.TextSlots { + if i >= len(text) { + break + } + slot.Render(img, text[i]) + } + + return img, nil +} diff --git a/meme/text_slot.go b/meme/text_slot.go new file mode 100644 index 0000000..6601aea --- /dev/null +++ b/meme/text_slot.go @@ -0,0 +1,199 @@ +package meme + +import ( + "image" + "image/color" + "image/draw" + "strings" + "unicode" + + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +type HorizontalAlignment int + +const ( + Left HorizontalAlignment = -1 + Center = 0 + Right = 1 +) + +type VerticalAlignment int + +const ( + Top VerticalAlignment = -1 + Middle = 0 + Bottom = 1 +) + +type TextSlot struct { + Bounds image.Rectangle + Font *truetype.Font + MaxFontSize float64 + HorizontalAlignment HorizontalAlignment + VerticalAlignment VerticalAlignment + TextColor color.Color + OutlineColor color.Color + AllUppercase bool +} + +func (s *TextSlot) Render(img draw.Image, text string) { + if s.AllUppercase { + text = strings.ToUpper(text) + } + + layout := s.TextLayout(text) + if layout == nil { + return + } + + textColor := s.TextColor + if textColor == nil { + textColor = color.Black + } + + for i, line := range layout.Lines { + if s.OutlineColor != nil { + // it's okay, memes aren't supposed to look good + offset := layout.Face.Metrics().Height / 16 + for _, delta := range []fixed.Point26_6{ + {X: offset, Y: offset}, + {X: -offset, Y: offset}, + {X: -offset, Y: -offset}, + {X: offset, Y: -offset}, + } { + drawer := font.Drawer{ + Dst: img, + Src: image.NewUniform(s.OutlineColor), + Face: layout.Face, + Dot: layout.LinePositions[i].Add(delta), + } + drawer.DrawString(line) + } + } + + drawer := font.Drawer{ + Dst: img, + Src: image.NewUniform(textColor), + Face: layout.Face, + Dot: layout.LinePositions[i], + } + drawer.DrawString(line) + + } +} + +type TextLayout struct { + Face font.Face + Lines []string + LinePositions []fixed.Point26_6 +} + +func (s *TextSlot) TextLayout(text string) *TextLayout { + fontSize := s.MaxFontSize + if fontSize == 0.0 { + fontSize = 80.0 + } + + hlimit := fixed.Int26_6(s.Bounds.Dx() * 64) + vlimit := fixed.Int26_6(s.Bounds.Dy() * 64) + + for fontSize >= 6.0 { + face := truetype.NewFace(s.Font, &truetype.Options{ + Size: fontSize, + }) + lineLimit := s.Bounds.Dy() / int(face.Metrics().Height/64) + lines, widths := lines(face, text, hlimit) + if len(lines) > lineLimit { + fontSize -= (fontSize + 9) / 10 + continue + } + + layout := &TextLayout{ + Face: face, + Lines: lines, + LinePositions: make([]fixed.Point26_6, len(lines)), + } + + y := fixed.Int26_6(s.Bounds.Min.Y * 64) + totalHeight := face.Metrics().Height.Mul(fixed.Int26_6(len(lines) * 64)) + switch s.VerticalAlignment { + case Middle: + y += (vlimit - totalHeight) / 2 + case Bottom: + y += (vlimit - totalHeight) + } + + for i, width := range widths { + x := fixed.Int26_6(s.Bounds.Min.X * 64) + switch s.HorizontalAlignment { + case Center: + x += (hlimit - width) / 2 + case Right: + x += (hlimit - width) + } + y += face.Metrics().Height + layout.LinePositions[i] = fixed.Point26_6{ + X: x, + Y: y, + } + } + return layout + } + + return nil +} + +func lines(face font.Face, text string, limit fixed.Int26_6) (lines []string, widths []fixed.Int26_6) { + for text != "" { + line, width, remaining := firstLine(face, text, limit) + if line == "" { + return nil, nil + } + lines = append(lines, line) + widths = append(widths, width) + text = remaining + } + return +} + +func firstLine(face font.Face, text string, limit fixed.Int26_6) (string, fixed.Int26_6, string) { + text = strings.TrimSpace(text) + + pos := 0 + lastBreak := 0 + + var width fixed.Int26_6 + var lastBreakWidth fixed.Int26_6 + + var prev rune = -1 + for _, r := range text { + advance, ok := face.GlyphAdvance(r) + if !ok { + continue + } + if prev >= 0 { + advance += face.Kern(prev, r) + } + + if unicode.IsSpace(r) && !unicode.IsSpace(prev) { + lastBreak = pos + lastBreakWidth = width + } + + if width+advance > limit { + if lastBreak == 0 { + return text[:pos], width, text[pos:] + } + return text[:lastBreak], lastBreakWidth, text[lastBreak:] + } + + pos += 1 + width += advance + prev = r + } + + return text, width, "" +} diff --git a/memelibrary/assets/fonts/Anton-Regular.ttf b/memelibrary/assets/fonts/Anton-Regular.ttf new file mode 100755 index 0000000..5a582b1 Binary files /dev/null and b/memelibrary/assets/fonts/Anton-Regular.ttf differ diff --git a/memelibrary/assets/fonts/ComicNeue-Bold.ttf b/memelibrary/assets/fonts/ComicNeue-Bold.ttf new file mode 100755 index 0000000..ca78778 Binary files /dev/null and b/memelibrary/assets/fonts/ComicNeue-Bold.ttf differ diff --git a/memelibrary/assets/fonts/ComicNeue-Regular.ttf b/memelibrary/assets/fonts/ComicNeue-Regular.ttf new file mode 100755 index 0000000..ab3a417 Binary files /dev/null and b/memelibrary/assets/fonts/ComicNeue-Regular.ttf differ diff --git a/memelibrary/assets/images/all-the-things.jpg b/memelibrary/assets/images/all-the-things.jpg new file mode 100644 index 0000000..1a730de Binary files /dev/null and b/memelibrary/assets/images/all-the-things.jpg differ diff --git a/memelibrary/assets/images/am-i-the-only-one.jpg b/memelibrary/assets/images/am-i-the-only-one.jpg new file mode 100644 index 0000000..4fccf34 Binary files /dev/null and b/memelibrary/assets/images/am-i-the-only-one.jpg differ diff --git a/memelibrary/assets/images/ancient-aliens.jpg b/memelibrary/assets/images/ancient-aliens.jpg new file mode 100644 index 0000000..502f1b8 Binary files /dev/null and b/memelibrary/assets/images/ancient-aliens.jpg differ diff --git a/memelibrary/assets/images/bad-luck-brian.jpg b/memelibrary/assets/images/bad-luck-brian.jpg new file mode 100644 index 0000000..23ddd69 Binary files /dev/null and b/memelibrary/assets/images/bad-luck-brian.jpg differ diff --git a/memelibrary/assets/images/batman-slapping-robin.jpg b/memelibrary/assets/images/batman-slapping-robin.jpg new file mode 100644 index 0000000..de08150 Binary files /dev/null and b/memelibrary/assets/images/batman-slapping-robin.jpg differ diff --git a/memelibrary/assets/images/boardroom-suggestion.jpg b/memelibrary/assets/images/boardroom-suggestion.jpg new file mode 100644 index 0000000..56b126f Binary files /dev/null and b/memelibrary/assets/images/boardroom-suggestion.jpg differ diff --git a/memelibrary/assets/images/brace-yourselves.jpg b/memelibrary/assets/images/brace-yourselves.jpg new file mode 100644 index 0000000..ae32e10 Binary files /dev/null and b/memelibrary/assets/images/brace-yourselves.jpg differ diff --git a/memelibrary/assets/images/but-thats-none-of-my-business.jpg b/memelibrary/assets/images/but-thats-none-of-my-business.jpg new file mode 100644 index 0000000..60994f5 Binary files /dev/null and b/memelibrary/assets/images/but-thats-none-of-my-business.jpg differ diff --git a/memelibrary/assets/images/confession-bear.jpg b/memelibrary/assets/images/confession-bear.jpg new file mode 100644 index 0000000..2b83940 Binary files /dev/null and b/memelibrary/assets/images/confession-bear.jpg differ diff --git a/memelibrary/assets/images/disaster-girl.jpg b/memelibrary/assets/images/disaster-girl.jpg new file mode 100644 index 0000000..6c101be Binary files /dev/null and b/memelibrary/assets/images/disaster-girl.jpg differ diff --git a/memelibrary/assets/images/doge.jpg b/memelibrary/assets/images/doge.jpg new file mode 100644 index 0000000..36a8092 Binary files /dev/null and b/memelibrary/assets/images/doge.jpg differ diff --git a/memelibrary/assets/images/everywhere.jpg b/memelibrary/assets/images/everywhere.jpg new file mode 100644 index 0000000..0a2a963 Binary files /dev/null and b/memelibrary/assets/images/everywhere.jpg differ diff --git a/memelibrary/assets/images/first-world-problems.jpg b/memelibrary/assets/images/first-world-problems.jpg new file mode 100644 index 0000000..9c4e139 Binary files /dev/null and b/memelibrary/assets/images/first-world-problems.jpg differ diff --git a/memelibrary/assets/images/grandma.jpg b/memelibrary/assets/images/grandma.jpg new file mode 100644 index 0000000..a290304 Binary files /dev/null and b/memelibrary/assets/images/grandma.jpg differ diff --git a/memelibrary/assets/images/great-gatsby.jpg b/memelibrary/assets/images/great-gatsby.jpg new file mode 100644 index 0000000..e448ae3 Binary files /dev/null and b/memelibrary/assets/images/great-gatsby.jpg differ diff --git a/memelibrary/assets/images/grumpy-cat.jpg b/memelibrary/assets/images/grumpy-cat.jpg new file mode 100644 index 0000000..ba4d52f Binary files /dev/null and b/memelibrary/assets/images/grumpy-cat.jpg differ diff --git a/memelibrary/assets/images/jackie-chan-wtf.jpg b/memelibrary/assets/images/jackie-chan-wtf.jpg new file mode 100644 index 0000000..6a542d2 Binary files /dev/null and b/memelibrary/assets/images/jackie-chan-wtf.jpg differ diff --git a/memelibrary/assets/images/not-sure-if.jpg b/memelibrary/assets/images/not-sure-if.jpg new file mode 100644 index 0000000..fd7b79f Binary files /dev/null and b/memelibrary/assets/images/not-sure-if.jpg differ diff --git a/memelibrary/assets/images/one-does-not-simply.jpg b/memelibrary/assets/images/one-does-not-simply.jpg new file mode 100644 index 0000000..a96c081 Binary files /dev/null and b/memelibrary/assets/images/one-does-not-simply.jpg differ diff --git a/memelibrary/assets/images/oprah.jpg b/memelibrary/assets/images/oprah.jpg new file mode 100644 index 0000000..a3cb498 Binary files /dev/null and b/memelibrary/assets/images/oprah.jpg differ diff --git a/memelibrary/assets/images/philosoraptor.jpg b/memelibrary/assets/images/philosoraptor.jpg new file mode 100644 index 0000000..3628f06 Binary files /dev/null and b/memelibrary/assets/images/philosoraptor.jpg differ diff --git a/memelibrary/assets/images/picard-facepalm.jpg b/memelibrary/assets/images/picard-facepalm.jpg new file mode 100644 index 0000000..9454edc Binary files /dev/null and b/memelibrary/assets/images/picard-facepalm.jpg differ diff --git a/memelibrary/assets/images/picard-wtf.jpg b/memelibrary/assets/images/picard-wtf.jpg new file mode 100644 index 0000000..a1e11a7 Binary files /dev/null and b/memelibrary/assets/images/picard-wtf.jpg differ diff --git a/memelibrary/assets/images/success-kid.jpg b/memelibrary/assets/images/success-kid.jpg new file mode 100644 index 0000000..c7dbaa4 Binary files /dev/null and b/memelibrary/assets/images/success-kid.jpg differ diff --git a/memelibrary/assets/images/that-would-be-great.jpg b/memelibrary/assets/images/that-would-be-great.jpg new file mode 100644 index 0000000..ae66565 Binary files /dev/null and b/memelibrary/assets/images/that-would-be-great.jpg differ diff --git a/memelibrary/assets/images/the-most-interesting-man-in-the-world.jpg b/memelibrary/assets/images/the-most-interesting-man-in-the-world.jpg new file mode 100644 index 0000000..6499929 Binary files /dev/null and b/memelibrary/assets/images/the-most-interesting-man-in-the-world.jpg differ diff --git a/memelibrary/assets/images/the-rock-driving.jpg b/memelibrary/assets/images/the-rock-driving.jpg new file mode 100644 index 0000000..5c02c3d Binary files /dev/null and b/memelibrary/assets/images/the-rock-driving.jpg differ diff --git a/memelibrary/assets/images/too-damn-high.jpg b/memelibrary/assets/images/too-damn-high.jpg new file mode 100644 index 0000000..c62626a Binary files /dev/null and b/memelibrary/assets/images/too-damn-high.jpg differ diff --git a/memelibrary/assets/images/we-need-to-go-deeper.jpg b/memelibrary/assets/images/we-need-to-go-deeper.jpg new file mode 100644 index 0000000..cb4d79b Binary files /dev/null and b/memelibrary/assets/images/we-need-to-go-deeper.jpg differ diff --git a/memelibrary/assets/images/what-if-i-told-you.jpg b/memelibrary/assets/images/what-if-i-told-you.jpg new file mode 100644 index 0000000..e978928 Binary files /dev/null and b/memelibrary/assets/images/what-if-i-told-you.jpg differ diff --git a/memelibrary/assets/images/willy-wonka.jpg b/memelibrary/assets/images/willy-wonka.jpg new file mode 100644 index 0000000..49a33a4 Binary files /dev/null and b/memelibrary/assets/images/willy-wonka.jpg differ diff --git a/memelibrary/assets/images/y-u-no.jpg b/memelibrary/assets/images/y-u-no.jpg new file mode 100644 index 0000000..a167c61 Binary files /dev/null and b/memelibrary/assets/images/y-u-no.jpg differ diff --git a/memelibrary/assets/images/yall-got-any-more.jpg b/memelibrary/assets/images/yall-got-any-more.jpg new file mode 100644 index 0000000..74b61d1 Binary files /dev/null and b/memelibrary/assets/images/yall-got-any-more.jpg differ diff --git a/memelibrary/assets/images/yo-dawg.jpg b/memelibrary/assets/images/yo-dawg.jpg new file mode 100644 index 0000000..e74e17e Binary files /dev/null and b/memelibrary/assets/images/yo-dawg.jpg differ diff --git a/memelibrary/assets/metadata/all-the-things.yaml b/memelibrary/assets/metadata/all-the-things.yaml new file mode 100644 index 0000000..7b5f619 --- /dev/null +++ b/memelibrary/assets/metadata/all-the-things.yaml @@ -0,0 +1,6 @@ +patterns: + - pattern: ((?:\b\S+\b\W*){1,2}) all the ((?:\b\S+\b\W*){1,2}) + text: + - $1 + - all the $2 + example: delete all the things! diff --git a/memelibrary/assets/metadata/am-i-the-only-one.yaml b/memelibrary/assets/metadata/am-i-the-only-one.yaml new file mode 100644 index 0000000..726af36 --- /dev/null +++ b/memelibrary/assets/metadata/am-i-the-only-one.yaml @@ -0,0 +1,6 @@ +patterns: + - pattern: am i the only one (here|around here)? (.*) + text: + - am i the only one $1 + - $2 + example: am i the only one here that knows how to meme diff --git a/memelibrary/assets/metadata/ancient-aliens.yaml b/memelibrary/assets/metadata/ancient-aliens.yaml new file mode 100644 index 0000000..08bc740 --- /dev/null +++ b/memelibrary/assets/metadata/ancient-aliens.yaml @@ -0,0 +1,2 @@ +aliases: + - aliens diff --git a/memelibrary/assets/metadata/bad-luck-brian.yaml b/memelibrary/assets/metadata/bad-luck-brian.yaml new file mode 100644 index 0000000..6d22915 --- /dev/null +++ b/memelibrary/assets/metadata/bad-luck-brian.yaml @@ -0,0 +1,3 @@ +aliases: + - bad-luck + - brian diff --git a/memelibrary/assets/metadata/batman-slapping-robin.yaml b/memelibrary/assets/metadata/batman-slapping-robin.yaml new file mode 100644 index 0000000..931773a --- /dev/null +++ b/memelibrary/assets/metadata/batman-slapping-robin.yaml @@ -0,0 +1,16 @@ +aliases: + - batman-robin + - batman-and-robin + - batman-slap +example: + - silence is golden + - and duct tape is silver +slots: + - x: 2 + y: 2 + width: 200 + height: 90 + - x: 212 + y: 2 + width: 180 + height: 80 diff --git a/memelibrary/assets/metadata/boardroom-suggestion.yaml b/memelibrary/assets/metadata/boardroom-suggestion.yaml new file mode 100644 index 0000000..2f86ca7 --- /dev/null +++ b/memelibrary/assets/metadata/boardroom-suggestion.yaml @@ -0,0 +1,29 @@ +aliases: + - boardroom + - boardroom-meeting +example: + - Meow. + - Woof. + - Ribbet. + - I didn't know I worked in a mental hospital. +slots: + - x: 155 + y: 6 + width: 300 + height: 40 + font: ComicNeue-Regular + - x: 28 + y: 249 + width: 96 + height: 21 + font: ComicNeue-Regular + - x: 168 + y: 258 + width: 78 + height: 21 + font: ComicNeue-Regular + - x: 310 + y: 260 + width: 127 + height: 41 + font: ComicNeue-Regular diff --git a/memelibrary/assets/metadata/brace-yourselves.yaml b/memelibrary/assets/metadata/brace-yourselves.yaml new file mode 100644 index 0000000..916fca7 --- /dev/null +++ b/memelibrary/assets/metadata/brace-yourselves.yaml @@ -0,0 +1,9 @@ +aliases: + - brace-yourself + - winter-is-coming +patterns: + - pattern: brace (yourselves|yourself)([^a-z]+)?\b([^,\.;]+.*) + text: + - brace $1$2 + - $3 + example: brace yourselves, winter is coming diff --git a/memelibrary/assets/metadata/but-thats-none-of-my-business.yaml b/memelibrary/assets/metadata/but-thats-none-of-my-business.yaml new file mode 100644 index 0000000..952f275 --- /dev/null +++ b/memelibrary/assets/metadata/but-thats-none-of-my-business.yaml @@ -0,0 +1,8 @@ +aliases: + - kermit +patterns: + - pattern: (.*) (but that'?s none of [^ ]+ business.*) + text: + - $1 + - $2 + example: this amount of tea may be unhealthy. but that's none of your business diff --git a/memelibrary/assets/metadata/confession-bear.yaml b/memelibrary/assets/metadata/confession-bear.yaml new file mode 100644 index 0000000..ec14eaf --- /dev/null +++ b/memelibrary/assets/metadata/confession-bear.yaml @@ -0,0 +1,3 @@ +example: + - my reason to get out of bed in the morning + - is to earn enough money to have a bed to go back to diff --git a/memelibrary/assets/metadata/disaster-girl.yaml b/memelibrary/assets/metadata/disaster-girl.yaml new file mode 100644 index 0000000..8923519 --- /dev/null +++ b/memelibrary/assets/metadata/disaster-girl.yaml @@ -0,0 +1,3 @@ +example: + - my house + - is lit diff --git a/memelibrary/assets/metadata/doge.yaml b/memelibrary/assets/metadata/doge.yaml new file mode 100644 index 0000000..745b76d --- /dev/null +++ b/memelibrary/assets/metadata/doge.yaml @@ -0,0 +1,40 @@ +patterns: + - pattern: (wo+w|such|much|very|so+|many)\b(.*?)\b(wo+w|such|much|very|so+|many)\b(.*?)(?:\b(wo+w|such|much|very|so+|many)\b(.*?)(?:\b(wo+w|such|much|very|so+|many)\b(.*?)(?:\b(wo+w|such|much|very|so+|many)\b(.*?))?)?)? + text: + - $1$2 + - $3$4 + - $5$6 + - $7$8 + - $9$10 + example: wooow such meme very dank so cool +slots: + - x: 30 + y: 50 + width: 280 + height: 80 + font: ComicNeue-Bold + text_color: [255, 0, 0] + - x: 367 + y: 154 + width: 235 + height: 70 + font: ComicNeue-Bold + text_color: [0, 0, 255] + - x: 231 + y: 319 + width: 300 + height: 98 + font: ComicNeue-Bold + text_color: [0, 255, 0] + - x: 76 + y: 496 + width: 240 + height: 70 + font: ComicNeue-Bold + text_color: [0, 255, 255] + - x: 394 + y: 504 + width: 190 + height: 90 + font: ComicNeue-Bold + text_color: [255, 255, 0] diff --git a/memelibrary/assets/metadata/everywhere.yaml b/memelibrary/assets/metadata/everywhere.yaml new file mode 100644 index 0000000..0bb4a83 --- /dev/null +++ b/memelibrary/assets/metadata/everywhere.yaml @@ -0,0 +1,6 @@ +patterns: + - pattern: (.+[\.,]) (.+) (everywhere[^a-z]*) + text: + - $1 + - $2 $3 + example: geese. geese everywhere diff --git a/memelibrary/assets/metadata/first-world-problems.yaml b/memelibrary/assets/metadata/first-world-problems.yaml new file mode 100644 index 0000000..2139580 --- /dev/null +++ b/memelibrary/assets/metadata/first-world-problems.yaml @@ -0,0 +1,5 @@ +aliases: + - white-whine +example: + - someone on the internet + - disagrees with me diff --git a/memelibrary/assets/metadata/grandma.yaml b/memelibrary/assets/metadata/grandma.yaml new file mode 100644 index 0000000..fd8c513 --- /dev/null +++ b/memelibrary/assets/metadata/grandma.yaml @@ -0,0 +1,5 @@ +aliases: + - grandma-finds-the-internet +example: + - do they deliver emails + - on sunday? diff --git a/memelibrary/assets/metadata/great-gatsby.yaml b/memelibrary/assets/metadata/great-gatsby.yaml new file mode 100644 index 0000000..1eded9f --- /dev/null +++ b/memelibrary/assets/metadata/great-gatsby.yaml @@ -0,0 +1,9 @@ +aliases: + - gatsby + - old-sport +patterns: + - pattern: (.*)\b(old sport[^a-z]*) + text: + - $1 + - $2 + example: merry christmas, old sport diff --git a/memelibrary/assets/metadata/grumpy-cat.yaml b/memelibrary/assets/metadata/grumpy-cat.yaml new file mode 100644 index 0000000..17345c9 --- /dev/null +++ b/memelibrary/assets/metadata/grumpy-cat.yaml @@ -0,0 +1,5 @@ +aliases: + - grumpy +example: + - i tried to sits. + - i didn't fits. diff --git a/memelibrary/assets/metadata/jackie-chan-wtf.yaml b/memelibrary/assets/metadata/jackie-chan-wtf.yaml new file mode 100644 index 0000000..a5197d4 --- /dev/null +++ b/memelibrary/assets/metadata/jackie-chan-wtf.yaml @@ -0,0 +1,3 @@ +example: + - this does not + - compute diff --git a/memelibrary/assets/metadata/not-sure-if.yaml b/memelibrary/assets/metadata/not-sure-if.yaml new file mode 100644 index 0000000..e9bc8d4 --- /dev/null +++ b/memelibrary/assets/metadata/not-sure-if.yaml @@ -0,0 +1,9 @@ +aliases: + - fry + - not-sure +patterns: + - pattern: (not sure if .+) (or .+) + text: + - $1 + - $2 + example: not sure if song is skipping or just a remix diff --git a/memelibrary/assets/metadata/one-does-not-simply.yaml b/memelibrary/assets/metadata/one-does-not-simply.yaml new file mode 100644 index 0000000..052b9e9 --- /dev/null +++ b/memelibrary/assets/metadata/one-does-not-simply.yaml @@ -0,0 +1,9 @@ +aliases: + - not-simply + - one-does-not +patterns: + - pattern: (one does not simply)(.+) + text: + - $1 + - $2 + example: one does not simply walk into mordor diff --git a/memelibrary/assets/metadata/oprah.yaml b/memelibrary/assets/metadata/oprah.yaml new file mode 100644 index 0000000..7a4d4d7 --- /dev/null +++ b/memelibrary/assets/metadata/oprah.yaml @@ -0,0 +1,29 @@ +aliases: + - you-get + - everyone-gets +patterns: + - pattern: (you get|everyone gets) (.+) + text: + - you get $2 + - and you get $2 + - and you get $2 + - everyone gets $2 + example: you get a car! +slots: + - x: 20 + y: 20 + width: 380 + height: 80 + - x: 406 + y: 124 + width: 200 + height: 80 + - x: 36 + y: 256 + width: 206 + height: 80 + - x: 31 + y: 348 + width: 558 + height: 86 + diff --git a/memelibrary/assets/metadata/philosoraptor.yaml b/memelibrary/assets/metadata/philosoraptor.yaml new file mode 100644 index 0000000..0814b2e --- /dev/null +++ b/memelibrary/assets/metadata/philosoraptor.yaml @@ -0,0 +1,3 @@ +example: + - if actions are stronger than words, + - why is the pen mightier than the sword? diff --git a/memelibrary/assets/metadata/picard-facepalm.yaml b/memelibrary/assets/metadata/picard-facepalm.yaml new file mode 100644 index 0000000..2b77d74 --- /dev/null +++ b/memelibrary/assets/metadata/picard-facepalm.yaml @@ -0,0 +1,4 @@ +aliases: + - captain-picard-facepalm +example: + - when you forget to study for your exam diff --git a/memelibrary/assets/metadata/picard-wtf.yaml b/memelibrary/assets/metadata/picard-wtf.yaml new file mode 100644 index 0000000..9a722c2 --- /dev/null +++ b/memelibrary/assets/metadata/picard-wtf.yaml @@ -0,0 +1,9 @@ +aliases: + - annoyed-picard + - captain-picard-wtf +patterns: + - pattern: (why|what) the fuck\b(.+) + text: + - $1 the fuck + - $2 + example: why the fuck does it always need to install directx diff --git a/memelibrary/assets/metadata/success-kid.yaml b/memelibrary/assets/metadata/success-kid.yaml new file mode 100644 index 0000000..9ebdbcc --- /dev/null +++ b/memelibrary/assets/metadata/success-kid.yaml @@ -0,0 +1,5 @@ +aliases: + - success +example: + - thought i only had one beer left + - two left diff --git a/memelibrary/assets/metadata/that-would-be-great.yaml b/memelibrary/assets/metadata/that-would-be-great.yaml new file mode 100644 index 0000000..4264f71 --- /dev/null +++ b/memelibrary/assets/metadata/that-would-be-great.yaml @@ -0,0 +1,8 @@ +aliases: + - thatd-be-great +patterns: + - pattern: (.+)\b(that would|that'd|thatd)( be great[^a-z]*) + text: + - $1 + - $2$3 + example: yeah if you could post more memes, that would be great diff --git a/memelibrary/assets/metadata/the-most-interesting-man-in-the-world.yaml b/memelibrary/assets/metadata/the-most-interesting-man-in-the-world.yaml new file mode 100644 index 0000000..f19f33c --- /dev/null +++ b/memelibrary/assets/metadata/the-most-interesting-man-in-the-world.yaml @@ -0,0 +1,10 @@ +aliases: + - most-interesting-man + - most-interesting-man-in-the-world + - i-dont-always +patterns: + - pattern: (i don'?t always) (.+) (but .+) + text: + - $1 $2 + - $3 + example: i don't always take out the recycling, but when i do, i look like a raging alcoholic diff --git a/memelibrary/assets/metadata/the-rock-driving.yaml b/memelibrary/assets/metadata/the-rock-driving.yaml new file mode 100644 index 0000000..cb4eb49 --- /dev/null +++ b/memelibrary/assets/metadata/the-rock-driving.yaml @@ -0,0 +1,14 @@ +slots: + - x: 322 + y: 36 + width: 234 + height: 110 + font: ComicNeue-Regular + - x: 334 + y: 264 + width: 225 + height: 104 + font: ComicNeue-Regular +example: + - I finally caught all 151 Pokemon. + - There are 646 Pokemon now. diff --git a/memelibrary/assets/metadata/too-damn-high.yaml b/memelibrary/assets/metadata/too-damn-high.yaml new file mode 100644 index 0000000..d462071 --- /dev/null +++ b/memelibrary/assets/metadata/too-damn-high.yaml @@ -0,0 +1,8 @@ +aliases: + - jimmy-mcmillan +patterns: + - pattern: (.+)(is|are) (too damn high[^a-z]*) + text: + - $1 + - $2 $3 + example: the mouse sensitivity is too damn high diff --git a/memelibrary/assets/metadata/we-need-to-go-deeper.yaml b/memelibrary/assets/metadata/we-need-to-go-deeper.yaml new file mode 100644 index 0000000..7560476 --- /dev/null +++ b/memelibrary/assets/metadata/we-need-to-go-deeper.yaml @@ -0,0 +1,8 @@ +aliases: + - deeper +patterns: + - pattern: (.*)\b(we need to go deeper[^a-z]*) + text: + - $1 + - $2 + example: we need to go deeper diff --git a/memelibrary/assets/metadata/what-if-i-told-you.yaml b/memelibrary/assets/metadata/what-if-i-told-you.yaml new file mode 100644 index 0000000..4c9b379 --- /dev/null +++ b/memelibrary/assets/metadata/what-if-i-told-you.yaml @@ -0,0 +1,8 @@ +aliases: + - morpheus +patterns: + - pattern: (what if i told you)\b(.+) + text: + - $1 + - $2 + example: what if i told you pressing a and b doesn't help you catch pokemon diff --git a/memelibrary/assets/metadata/willy-wonka.yaml b/memelibrary/assets/metadata/willy-wonka.yaml new file mode 100644 index 0000000..55ca109 --- /dev/null +++ b/memelibrary/assets/metadata/willy-wonka.yaml @@ -0,0 +1,5 @@ +aliases: + - condescending-wonka +example: + - i'm condescending + - that means i talk down to you diff --git a/memelibrary/assets/metadata/y-u-no.yaml b/memelibrary/assets/metadata/y-u-no.yaml new file mode 100644 index 0000000..b2a0b41 --- /dev/null +++ b/memelibrary/assets/metadata/y-u-no.yaml @@ -0,0 +1,6 @@ +patterns: + - pattern: (.+) (y u .+) + text: + - $1 + - $2 + example: onions y u make eyes water diff --git a/memelibrary/assets/metadata/yall-got-any-more.yaml b/memelibrary/assets/metadata/yall-got-any-more.yaml new file mode 100644 index 0000000..faae68a --- /dev/null +++ b/memelibrary/assets/metadata/yall-got-any-more.yaml @@ -0,0 +1,6 @@ +patterns: + - pattern: (ya'?ll got any more of them) (.+) + text: + - $1 + - $2 + example: ya'll got any more of them pixels? diff --git a/memelibrary/assets/metadata/yo-dawg.yaml b/memelibrary/assets/metadata/yo-dawg.yaml new file mode 100644 index 0000000..fec4a27 --- /dev/null +++ b/memelibrary/assets/metadata/yo-dawg.yaml @@ -0,0 +1,8 @@ +aliases: + - xzibit +patterns: + - pattern: (yo dawg\b.+?)(so .+) + text: + - $1 + - $2 + example: yo dawg, i heard you like cars. so i put a car in your car so you can drive while you drive diff --git a/memelibrary/memelibrary.go b/memelibrary/memelibrary.go new file mode 100644 index 0000000..04a65fe --- /dev/null +++ b/memelibrary/memelibrary.go @@ -0,0 +1,101 @@ +package memelibrary + +import ( + "bytes" + "image" + "path/filepath" + "strings" + + _ "image/jpeg" + _ "image/png" + + "github.com/golang/freetype/truetype" + + "github.com/mattermost/mattermost-plugin-memes/meme" +) + +var fonts map[string]*truetype.Font = make(map[string]*truetype.Font) +var images map[string]image.Image = make(map[string]image.Image) +var metadata map[string]*Metadata = make(map[string]*Metadata) +var templates map[string]*meme.Template = make(map[string]*meme.Template) + +func isImageAsset(assetName string) bool { + ext := strings.ToLower(filepath.Ext(assetName)) + return ext == ".jpg" || ext == ".jpeg" || ext == ".png" +} + +func mustLoadImage(assetName string) image.Image { + img, _, err := image.Decode(bytes.NewReader(MustAsset(assetName))) + if err != nil { + panic(err) + } + return img +} + +func init() { + fontAssets, _ := AssetDir("fonts") + for _, assetName := range fontAssets { + fontName := strings.TrimSuffix(assetName, filepath.Ext(assetName)) + font, err := truetype.Parse(MustAsset(filepath.Join("fonts", assetName))) + if err != nil { + panic(err) + } + fonts[fontName] = font + } + + imageAssets, _ := AssetDir("images") + for _, assetName := range imageAssets { + if !isImageAsset(assetName) { + continue + } + templateName := strings.TrimSuffix(assetName, filepath.Ext(assetName)) + images[templateName] = mustLoadImage(filepath.Join("images", assetName)) + } + + metadataAssets, _ := AssetDir("metadata") + for _, assetName := range metadataAssets { + ext := filepath.Ext(assetName) + if ext != ".yaml" { + continue + } + + templateName := strings.TrimSuffix(assetName, ext) + + m, err := ParseMetadata(MustAsset(filepath.Join("metadata", assetName))) + if err != nil { + panic(err) + } + metadata[templateName] = m + } + + for templateName, metadata := range metadata { + img := images[templateName] + + template := &meme.Template{ + Name: templateName, + Image: img, + TextSlots: metadata.TextSlots(img.Bounds()), + } + templates[templateName] = template + for _, alias := range metadata.Aliases { + templates[alias] = template + } + } +} + +func Memes() map[string]*Metadata { + return metadata +} + +func Template(name string) *meme.Template { + return templates[name] +} + +func PatternMatch(input string) (*meme.Template, []string) { + for templateName, metadata := range metadata { + if text := metadata.PatternMatch(input); text != nil { + return templates[templateName], text + } + } + return nil, nil +} diff --git a/memelibrary/memelibrary_test.go b/memelibrary/memelibrary_test.go new file mode 100644 index 0000000..ea27388 --- /dev/null +++ b/memelibrary/memelibrary_test.go @@ -0,0 +1,46 @@ +package memelibrary + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIsImageAsset(t *testing.T) { + assert.True(t, isImageAsset("all-the-things.jpg")) + assert.False(t, isImageAsset("all-the-things.json")) +} + +func TestMustLoadImage(t *testing.T) { + assert.NotPanics(t, func() { + img := mustLoadImage("images/all-the-things.jpg") + assert.NotNil(t, img) + }) + + assert.Panics(t, func() { + mustLoadImage("this-asset-does-not-exist.jpg") + }) + + assert.Panics(t, func() { + mustLoadImage("metadata/all-the-things.yaml") + }) +} + +func TestTemplate(t *testing.T) { + assert.Nil(t, Template("not-a-template")) + + template := Template("all-the-things") + require.NotNil(t, template) + assert.NotNil(t, template.Image) +} + +func TestPatternMatch(t *testing.T) { + for name, metadata := range Memes() { + for _, pattern := range metadata.Patterns { + template, text := PatternMatch(pattern.Example) + assert.Equal(t, Template(name), template) + assert.NotNil(t, text) + } + } +} diff --git a/memelibrary/metadata.go b/memelibrary/metadata.go new file mode 100644 index 0000000..e783dce --- /dev/null +++ b/memelibrary/metadata.go @@ -0,0 +1,124 @@ +package memelibrary + +import ( + "image" + "image/color" + "regexp" + "strings" + + yaml "gopkg.in/yaml.v2" + + "github.com/mattermost/mattermost-plugin-memes/meme" +) + +type Pattern struct { + Pattern string + Text []string + Example string + + pattern *regexp.Regexp +} + +type Slot struct { + X int + Y int + Width int + Height int + Font string + TextColor []int `yaml:"text_color"` +} + +type Metadata struct { + Aliases []string + Patterns []*Pattern + Slots []*Slot + Example []string +} + +func ParseMetadata(in []byte) (*Metadata, error) { + var m Metadata + if err := yaml.Unmarshal(in, &m); err != nil { + return nil, err + } + for _, pattern := range m.Patterns { + r, err := regexp.Compile("(?i)^" + pattern.Pattern + "$") + if err != nil { + return nil, err + } + pattern.pattern = r + } + return &m, nil +} + +func (m *Metadata) PatternMatch(input string) []string { + for _, pattern := range m.Patterns { + if text := pattern.Match(input); text != nil { + return text + } + } + return nil +} + +func (m *Metadata) TextSlots(bounds image.Rectangle) (slots []*meme.TextSlot) { + if m.Slots != nil { + for _, slot := range m.Slots { + textSlot := &meme.TextSlot{ + Bounds: image.Rect(slot.X, slot.Y, slot.X+slot.Width, slot.Y+slot.Height), + } + if slot.Font != "" { + textSlot.Font = fonts[slot.Font] + } else { + textSlot.Font = fonts["Anton-Regular"] + textSlot.TextColor = color.White + textSlot.OutlineColor = color.Black + textSlot.AllUppercase = true + } + if c := sliceToColor(slot.TextColor); c != nil { + textSlot.TextColor = c + } + slots = append(slots, textSlot) + } + return + } + + padding := bounds.Dy() / 20 + return []*meme.TextSlot{ + { + Bounds: image.Rect(padding, padding, bounds.Dx()-padding, bounds.Dy()/4), + Font: fonts["Anton-Regular"], + TextColor: color.White, + OutlineColor: color.Black, + AllUppercase: true, + }, + { + Bounds: image.Rect(padding, bounds.Dy()*3/4, bounds.Dx()-padding, bounds.Dy()-padding), + Font: fonts["Anton-Regular"], + TextColor: color.White, + OutlineColor: color.Black, + AllUppercase: true, + }, + } +} + +func sliceToColor(s []int) color.Color { + switch len(s) { + case 1: + return color.Gray16{uint16(s[0]) << 1} + case 2: + return color.RGBA{uint8(s[0]), uint8(s[0]), uint8(s[0]), uint8(s[1])} + case 3: + return color.RGBA{uint8(s[0]), uint8(s[1]), uint8(s[2]), 255} + case 4: + return color.RGBA{uint8(s[0]), uint8(s[1]), uint8(s[2]), uint8(s[3])} + } + return nil +} + +func (p *Pattern) Match(input string) (text []string) { + if matches := p.pattern.FindStringSubmatchIndex(input); matches != nil { + for _, slotText := range p.Text { + text = append(text, strings.TrimSpace(string(p.pattern.ExpandString(nil, slotText, input, matches)))) + } + } + return +} diff --git a/plugin.go b/plugin.go new file mode 100644 index 0000000..15e411a --- /dev/null +++ b/plugin.go @@ -0,0 +1,187 @@ +package main + +import ( + "flag" + "fmt" + "image/jpeg" + "net/http" + "net/url" + "os" + "strings" + + "github.com/gorilla/mux" + "github.com/kballard/go-shellquote" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/plugin" + "github.com/mattermost/mattermost-server/plugin/rpcplugin" + + "github.com/mattermost/mattermost-plugin-memes/meme" + "github.com/mattermost/mattermost-plugin-memes/memelibrary" +) + +func resolveMeme(input string) (*meme.Template, []string, error) { + if template, text := memelibrary.PatternMatch(input); template != nil { + return template, text, nil + } + parts, err := shellquote.Split(input) + if err != nil { + return nil, nil, err + } + if template := memelibrary.Template(parts[0]); template != nil { + return template, parts[1:], nil + } + return nil, nil, fmt.Errorf("i don't know this meme") +} + +func demo(args []string) error { + fs := flag.NewFlagSet("demo", flag.ContinueOnError) + out := fs.String("out", "", "output path to write the meme to") + if err := fs.Parse(args); err != nil { + return err + } + + input := fs.Args() + if len(input) != 1 { + return fmt.Errorf("specify an input string") + } + + template, text, err := resolveMeme(input[0]) + if err != nil { + return err + } + + if *out != "" { + f, err := os.Create(*out) + if err != nil { + return err + } + defer f.Close() + + img, err := template.Render(text) + if err != nil { + return err + } + if err := jpeg.Encode(f, img, &jpeg.Options{ + Quality: 100, + }); err != nil { + return err + } + } + + return nil +} + +func serveTemplateJPEG(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + templateName := vars["name"] + + template := memelibrary.Template(templateName) + if template == nil { + http.NotFound(w, r) + return + } + + query := r.URL.Query() + text := query["text"] + + img, err := template.Render(text) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "image/jpeg") + w.Header().Set("Cache-Control", "public, max-age=604800") + if err := jpeg.Encode(w, img, &jpeg.Options{ + Quality: 90, + }); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +type Plugin struct { + router *mux.Router +} + +func (p *Plugin) OnActivate(api plugin.API) error { + p.router = mux.NewRouter() + p.router.HandleFunc("/templates/{name}.jpg", serveTemplateJPEG).Methods("GET") + return api.RegisterCommand(&model.Command{ + Trigger: "meme", + AutoComplete: true, + AutoCompleteDesc: "Renders custom memes so you can express yourself with culture.", + }) +} + +func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Mattermost-User-Id") == "" { + http.Error(w, "please log in", http.StatusForbidden) + return + } + + p.router.ServeHTTP(w, r) +} + +func (p *Plugin) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { + input := strings.TrimSpace(strings.TrimPrefix(args.Command, "/meme")) + + if input == "" { + var availableMemes []string + for name, metadata := range memelibrary.Memes() { + availableMemes = append(availableMemes, name) + for _, alias := range metadata.Aliases { + availableMemes = append(availableMemes, alias) + } + } + return &model.CommandResponse{ + ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, + Text: `You can get started meming in one of two ways: + +If your meme has well-defined phrasing, you can just type it: + +` + "`" + `/meme brace yourself. memes are coming.` + "`" + ` + +If your meme doesn't have well-defined phrasing or you want more control, you can name a meme, then follow it with text to fill the slots: + +` + "`" + `/meme brace-yourself "brace yourself." "memes are coming."` + "`" + ` + +In either case... + +![brace-yourself](/plugins/memes/templates/brace-yourselves.jpg?text=brace+yourself.&text=memes+are+coming.) + +Available memes: ` + strings.Join(availableMemes, ", "), + }, nil + } + + template, text, err := resolveMeme(input) + if err != nil { + return nil, model.NewAppError("ExecuteCommand", "error resolving meme", nil, err.Error(), http.StatusInternalServerError) + } + + queryString := "" + for _, t := range text { + if queryString == "" { + queryString += "?" + } else { + queryString += "&" + } + queryString += "text=" + url.QueryEscape(t) + } + + return &model.CommandResponse{ + ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, + Text: "![" + template.Name + "](/plugins/memes/templates/" + template.Name + ".jpg" + queryString + ")", + }, nil +} + +func main() { + if len(os.Args) > 1 { + if err := demo(os.Args[1:]); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + } else { + rpcplugin.Main(&Plugin{}) + } +} diff --git a/plugin.yaml b/plugin.yaml new file mode 100644 index 0000000..212ac5e --- /dev/null +++ b/plugin.yaml @@ -0,0 +1,6 @@ +id: memes +backend: + executable: plugin.exe +name: Memes +description: Gives you the ability to quickly create and post memes via a /meme slash command. +version: '0.0'