Add snapchat module

This commit is contained in:
Hamcha 2017-02-15 16:59:49 +01:00
parent 6efec1b50a
commit f9d5fb46f1
3 changed files with 272 additions and 13 deletions

View file

@ -2,24 +2,58 @@ package main
import ( import (
"flag" "flag"
"log"
"strings" "strings"
"github.com/hamcha/clessy/tg" "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() { func initmods() {
initviaggi() for name, mod := range mods {
initmeme() log.Printf("Initializing %s..", name)
initunsplash() if mod.OnInit != nil {
initmacro() mod.OnInit()
}
}
} }
func dispatch(broker *tg.Broker, update tg.APIMessage) { func dispatch(broker *tg.Broker, update tg.APIMessage) {
metafora(broker, update) for _, mod := range mods {
viaggi(broker, update) if mod.OnMessage != nil {
memegen(broker, update) mod.OnMessage(broker, update)
unsplash(broker, update) }
macro(broker, update) }
} }
func isCommand(update tg.APIMessage, cmdname string) bool { func isCommand(update tg.APIMessage, cmdname string) bool {
@ -34,15 +68,23 @@ func isCommand(update tg.APIMessage, cmdname string) bool {
var botname *string var botname *string
var impact *string var impact *string
var gillmt *string var gillmt *string
var sourcesans *string
func main() { func main() {
brokerAddr := flag.String("broker", "localhost:7314", "Broker address:port") brokerAddr := flag.String("broker", "localhost:7314", "Broker address:port")
botname = flag.String("botname", "maudbot", "Bot name for /targetet@commands") botname = flag.String("botname", "maudbot", "Bot name for /targetet@commands")
impact = flag.String("impact", "impact.ttf", "Path to impact.ttf (Impact font)") 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)") 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)") macropath = flag.String("macropath", "macros.json", "Path to macros db (JSON)")
disable := flag.String("disable", "", "Disable mods (separated by comma)")
flag.Parse() flag.Parse()
for _, modname := range strings.Split(*disable, ",") {
modname = strings.TrimSpace(modname)
delete(mods, modname)
}
initmods() initmods()
err := tg.CreateBrokerClient(*brokerAddr, dispatch) err := tg.CreateBrokerClient(*brokerAddr, dispatch)

View file

@ -65,21 +65,21 @@ func memegen(broker *tg.Broker, update tg.APIMessage) {
broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) { broker.GetFile(photo.FileID, func(broker *tg.Broker, data tg.BrokerUpdate) {
if data.Type == tg.BError { 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, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID) broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return return
} }
pbytes, err := base64.StdEncoding.DecodeString(*data.Bytes) pbytes, err := base64.StdEncoding.DecodeString(*data.Bytes)
if err != nil { 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, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID) broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return return
} }
img, _, err := image.Decode(bytes.NewReader(pbytes)) img, _, err := image.Decode(bytes.NewReader(pbytes))
if err != nil { 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, "<b>ERROR</b>: Non riesco a leggere l'immagine", &update.MessageID) broker.SendTextMessage(update.Chat, "<b>ERROR</b>: Non riesco a leggere l'immagine", &update.MessageID)
return return
} }
@ -170,7 +170,7 @@ func memegen(broker *tg.Broker, update tg.APIMessage) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80})) err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
if err != nil { 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, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID) broker.SendTextMessage(update.Chat, "<b>ERRORE!</b> @hamcha controlla la console!", &update.MessageID)
return return
} }

217
mods/snapchat.go Normal file
View file

@ -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, "<b>ERRORE!</b> @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, "<b>ERRORE!</b> @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, "<b>ERROR</b>: 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, "<b>ERRORE!</b> @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
}