Add snapchat module
This commit is contained in:
parent
6efec1b50a
commit
f9d5fb46f1
3 changed files with 272 additions and 13 deletions
60
mods/main.go
60
mods/main.go
|
@ -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)
|
||||||
|
|
|
@ -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
217
mods/snapchat.go
Normal 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
|
||||||
|
}
|
Reference in a new issue