diff --git a/mods/main.go b/mods/main.go
index 87b17ae..f6075f6 100644
--- a/mods/main.go
+++ b/mods/main.go
@@ -2,24 +2,58 @@ package main
import (
"flag"
+ "log"
"strings"
"github.com/hamcha/clessy/tg"
)
+type Mod struct {
+ OnInit func()
+ OnMessage func(*tg.Broker, tg.APIMessage)
+}
+
+var mods = map[string]Mod{
+ "metafora": {
+ OnMessage: metafora,
+ },
+ "viaggi": {
+ OnInit: initviaggi,
+ OnMessage: viaggi,
+ },
+ "meme": {
+ OnInit: initmeme,
+ OnMessage: memegen,
+ },
+ "unsplash": {
+ OnInit: initunsplash,
+ OnMessage: unsplash,
+ },
+ "macro": {
+ OnInit: initmacro,
+ OnMessage: macro,
+ },
+ "snapchat": {
+ OnInit: initsnapchat,
+ OnMessage: snapchat,
+ },
+}
+
func initmods() {
- initviaggi()
- initmeme()
- initunsplash()
- initmacro()
+ for name, mod := range mods {
+ log.Printf("Initializing %s..", name)
+ if mod.OnInit != nil {
+ mod.OnInit()
+ }
+ }
}
func dispatch(broker *tg.Broker, update tg.APIMessage) {
- metafora(broker, update)
- viaggi(broker, update)
- memegen(broker, update)
- unsplash(broker, update)
- macro(broker, update)
+ for _, mod := range mods {
+ if mod.OnMessage != nil {
+ mod.OnMessage(broker, update)
+ }
+ }
}
func isCommand(update tg.APIMessage, cmdname string) bool {
@@ -34,15 +68,23 @@ func isCommand(update tg.APIMessage, cmdname string) bool {
var botname *string
var impact *string
var gillmt *string
+var sourcesans *string
func main() {
brokerAddr := flag.String("broker", "localhost:7314", "Broker address:port")
botname = flag.String("botname", "maudbot", "Bot name for /targetet@commands")
impact = flag.String("impact", "impact.ttf", "Path to impact.ttf (Impact font)")
gillmt = flag.String("gillmt", "gill.ttf", "Path to gill.ttf (Gill Sans MT font)")
+ sourcesans = flag.String("sourcesans", "source.ttf", "Path to source.ttf (Source Sans Pro font)")
macropath = flag.String("macropath", "macros.json", "Path to macros db (JSON)")
+ disable := flag.String("disable", "", "Disable mods (separated by comma)")
flag.Parse()
+ for _, modname := range strings.Split(*disable, ",") {
+ modname = strings.TrimSpace(modname)
+ delete(mods, modname)
+ }
+
initmods()
err := tg.CreateBrokerClient(*brokerAddr, dispatch)
diff --git a/mods/memegen.go b/mods/memegen.go
index d8ca8bb..69b7bab 100644
--- a/mods/memegen.go
+++ b/mods/memegen.go
@@ -65,21 +65,21 @@ func memegen(broker *tg.Broker, update tg.APIMessage) {
broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) {
if data.Type == tg.BError {
- log.Println("[memegen] Received error from broker: %s\n", *data.Error)
+ log.Printf("[memegen] Received error from broker: %s\n", *data.Error)
broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
return
}
pbytes, err := base64.StdEncoding.DecodeString(*data.Bytes)
if err != nil {
- log.Println("[memegen] Base64 decode error: %s\n", err.Error())
+ log.Printf("[memegen] Base64 decode error: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
return
}
img, _, err := image.Decode(bytes.NewReader(pbytes))
if err != nil {
- log.Println("[memegen] Image decode error: %s\n", err.Error())
+ log.Printf("[memegen] Image decode error: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "ERROR: Non riesco a leggere l'immagine", &update.MessageID)
return
}
@@ -170,7 +170,7 @@ func memegen(broker *tg.Broker, update tg.APIMessage) {
buf := new(bytes.Buffer)
err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
if err != nil {
- log.Println("[memegen] Image encode error: %s\n", err.Error())
+ log.Printf("[memegen] Image encode error: %s\n", err.Error())
broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
return
}
diff --git a/mods/snapchat.go b/mods/snapchat.go
new file mode 100644
index 0000000..f720d70
--- /dev/null
+++ b/mods/snapchat.go
@@ -0,0 +1,217 @@
+package main
+
+import (
+ "bytes"
+ "encoding/base64"
+ "image"
+ "image/color"
+ _ "image/gif"
+ "image/jpeg"
+ _ "image/png"
+ "io/ioutil"
+ "log"
+ "math/rand"
+ "os"
+ "strings"
+ "unicode"
+
+ "time"
+
+ "github.com/golang/freetype"
+ "github.com/hamcha/clessy/tg"
+ "github.com/llgcode/draw2d"
+ "github.com/llgcode/draw2d/draw2dimg"
+)
+
+var snapFontData draw2d.FontData
+
+func initsnapchat() {
+ rand.Seed(time.Now().Unix())
+
+ fontfile, err := os.Open(*sourcesans)
+ assert(err)
+ defer fontfile.Close()
+
+ bytes, err := ioutil.ReadAll(fontfile)
+ assert(err)
+
+ font, err := freetype.ParseFont(bytes)
+ assert(err)
+
+ snapFontData = draw2d.FontData{"sourcesans", draw2d.FontFamilySans, draw2d.FontStyleBold}
+ draw2d.RegisterFont(snapFontData, font)
+}
+
+func snapchat(broker *tg.Broker, update tg.APIMessage) {
+ // Make replies work
+ if update.ReplyTo != nil && update.Text != nil && update.ReplyTo.Photo != nil {
+ update.Photo = update.ReplyTo.Photo
+ update.Caption = update.Text
+ }
+
+ if update.Photo != nil && update.Caption != nil {
+ caption := *(update.Caption)
+ if strings.HasPrefix(caption, "/snap ") && len(caption) > 6 {
+ txt := strings.TrimSpace(caption[6:])
+
+ maxsz := 0
+ photo := tg.APIPhotoSize{}
+ for _, curphoto := range update.Photo {
+ if curphoto.Width > maxsz {
+ maxsz = curphoto.Width
+ photo = curphoto
+ }
+ }
+
+ broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) {
+ if data.Type == tg.BError {
+ log.Printf("[snapchat] Received error from broker: %s\n", *data.Error)
+ broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
+ return
+ }
+
+ pbytes, err := base64.StdEncoding.DecodeString(*data.Bytes)
+ if err != nil {
+ log.Printf("[snapchat] Base64 decode error: %s\n", err.Error())
+ broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
+ return
+ }
+
+ img, _, err := image.Decode(bytes.NewReader(pbytes))
+ if err != nil {
+ log.Printf("[snapchat] Image decode error: %s\n", err.Error())
+ broker.SendTextMessage(update.Chat, "ERROR: Non riesco a leggere l'immagine", &update.MessageID)
+ return
+ }
+
+ // Create target image
+ bounds := img.Bounds()
+ iwidth := float64(bounds.Size().Y) / 1.6
+ iheight := float64(bounds.Size().Y)
+
+ repos := iwidth < float64(bounds.Size().X)
+ if !repos {
+ iwidth = float64(bounds.Size().X)
+ }
+
+ timg := image.NewRGBA(image.Rect(0, 0, int(iwidth), int(iheight)))
+ gc := draw2dimg.NewGraphicContext(timg)
+ gc.SetFontData(snapFontData)
+
+ gc.Save()
+ if repos {
+ gc.Translate(-(float64(bounds.Size().X)-iwidth)/2, 0)
+ }
+ gc.DrawImage(img)
+ gc.Restore()
+
+ scale := iwidth / 25
+ gc.SetFontSize(scale)
+
+ lineMargin := scale / 3
+ boxMargin := lineMargin
+ topMargin := lineMargin / 6
+ write := func(text string, startHeight float64) {
+ texts := wordWrap(gc, strings.TrimSpace(text), iwidth*0.9)
+ totalHeight := startHeight
+ firstLine := 0.
+ for _, txt := range texts {
+ _, top, _, bottom := gc.GetStringBounds(txt)
+ height := (bottom - top)
+ if firstLine == 0 {
+ firstLine = height
+ }
+ totalHeight += lineMargin + height
+ }
+
+ // Draw background
+ starty := startHeight - boxMargin - topMargin - firstLine
+ endy := totalHeight + boxMargin - firstLine
+ gc.Save()
+ gc.SetFillColor(color.RGBA{0, 0, 0, 160})
+ gc.BeginPath()
+ gc.MoveTo(0, starty)
+ gc.LineTo(iwidth, starty)
+ gc.LineTo(iwidth, endy)
+ gc.LineTo(0, endy)
+ gc.Close()
+ gc.Fill()
+ gc.Restore()
+
+ // Write lines
+ gc.SetFillColor(image.White)
+ height := startHeight
+ for _, txt := range texts {
+ left, top, right, bottom := gc.GetStringBounds(txt)
+ width := right - left
+ gc.FillStringAt(txt, (iwidth-width)/2, height)
+ height += lineMargin + (bottom - top)
+ }
+ }
+ write(txt, (rand.Float64()*0.4+0.3)*iheight)
+
+ buf := new(bytes.Buffer)
+ err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
+ if err != nil {
+ log.Printf("[snapchat] Image encode error: %s\n", err.Error())
+ broker.SendTextMessage(update.Chat, "ERRORE! @hamcha controlla la console!", &update.MessageID)
+ return
+ }
+ broker.SendPhoto(update.Chat, buf.Bytes(), "meme.jpg", "", &update.MessageID)
+ })
+ }
+ }
+}
+
+// Word wrapping code from https://github.com/fogleman/gg
+// Copyright (C) 2016 Michael Fogleman
+// Licensed under MIT (https://github.com/fogleman/gg/blob/master/LICENSE.md)
+
+func splitOnSpace(x string) []string {
+ var result []string
+ pi := 0
+ ps := false
+ for i, c := range x {
+ s := unicode.IsSpace(c)
+ if s != ps && i > 0 {
+ result = append(result, x[pi:i])
+ pi = i
+ }
+ ps = s
+ }
+ result = append(result, x[pi:])
+ return result
+}
+
+func wordWrap(gc draw2d.GraphicContext, s string, width float64) []string {
+ var result []string
+ for _, line := range strings.Split(s, "\n") {
+ fields := splitOnSpace(line)
+ if len(fields)%2 == 1 {
+ fields = append(fields, "")
+ }
+ x := ""
+ for i := 0; i < len(fields); i += 2 {
+ left, _, right, _ := gc.GetStringBounds(x + fields[i])
+ w := right - left
+ if w > width {
+ if x == "" {
+ result = append(result, fields[i])
+ x = ""
+ continue
+ } else {
+ result = append(result, x)
+ x = ""
+ }
+ }
+ x += fields[i] + fields[i+1]
+ }
+ if x != "" {
+ result = append(result, x)
+ }
+ }
+ for i, line := range result {
+ result[i] = strings.TrimSpace(line)
+ }
+ return result
+}