forked from paked/meme-maker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bot.go
217 lines (167 loc) · 4.21 KB
/
bot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package main
import (
"fmt"
"image"
_ "image/jpeg"
"net/http"
"strings"
"github.com/paked/configure"
"github.com/paked/gg"
"github.com/paked/messenger"
)
type MessageState int
var (
conf = configure.New()
verifyToken = conf.String("verify-token", "mad-skrilla", "The token used to verify facebook")
verify = conf.Bool("should-verify", false, "Whether or not the app should verify itself")
pageToken = conf.String("page-token", "not skrilla", "The token that is used to verify the page on facebook")
font = conf.String("font", "fonts/Economica-Bold.ttf", "The font you want the meme maker to use")
states map[int64]MessageState
memes map[int64]*Meme
client *messenger.Messenger
)
const (
NoAction MessageState = iota
MakingMeme
)
func init() {
conf.Use(configure.NewFlag())
conf.Use(configure.NewEnvironment())
conf.Use(configure.NewJSONFromFile("config.json"))
}
func main() {
conf.Parse()
memes = make(map[int64]*Meme)
states = make(map[int64]MessageState)
client = messenger.New(messenger.Options{
Verify: *verify,
VerifyToken: *verifyToken,
Token: *pageToken,
})
client.HandleMessage(messages)
fmt.Println("Serving messenger bot on localhost:8080")
http.ListenAndServe(":8080", client.Handler())
}
func messages(m messenger.Message, r *messenger.Response) {
from, err := client.ProfileByID(m.Sender.ID)
if err != nil {
fmt.Println("error getting profile:", err)
return
}
fmt.Println(m.Sender.ID)
state := messageState(m.Sender)
switch state {
case NoAction:
r.Text(fmt.Sprintf("Greetings, %v. You're here to make a meme?", from.FirstName))
r.Text("If so, you are in just the right place.")
r.Text("All you need to do is send me a picture and a line of text to put on that picture!")
setState(m.Sender, MakingMeme)
case MakingMeme:
meme := messageMeme(m.Sender)
if len(m.Attachments) > 0 {
a := m.Attachments[0]
if a.Type != "image" {
r.Text("Sorry to be a sad pepe. Unfortunately you're going to need to send an image")
}
meme.ImageURL = a.Payload.URL
}
if m.Text != "" {
meme.Text = strings.ToUpper(m.Text)
}
if meme.Ready() {
err = r.Image(meme.Make())
if err != nil {
fmt.Println("error encoding image:", err)
return
}
fmt.Println("Done!")
*meme = Meme{}
}
}
}
func messageState(s messenger.Sender) MessageState {
return states[s.ID]
}
func setState(s messenger.Sender, state MessageState) {
states[s.ID] = state
}
func messageMeme(s messenger.Sender) *Meme {
meme := memes[s.ID]
if meme == nil {
meme = &Meme{}
memes[s.ID] = meme
}
return meme
}
type Meme struct {
ImageURL string
Text string
}
func (m Meme) Ready() bool {
return m.ImageURL != "" && m.Text != ""
}
func (m Meme) Make() image.Image {
res, err := http.Get(m.ImageURL)
if err != nil {
fmt.Println("error downloading image:", err)
return nil
}
defer res.Body.Close()
background, _, err := image.Decode(res.Body)
if err != nil {
fmt.Println("error decoding image:", err)
}
r := background.Bounds()
w := r.Dx()
h := r.Dy()
final := gg.NewContext(w, h)
final.DrawImage(background, 0, 0)
fontSize := findIdealFontSize(final, m.Text)
final.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+dy) - fontSize
final.DrawStringAnchored(m.Text, x, y, 0.5, 0.5)
}
}
final.SetHexColor("#FFF")
final.DrawStringAnchored(m.Text, float64(w)/2, float64(h)-fontSize, 0.5, 0.5)
return final.Image()
}
func findIdealFontSize(img *gg.Context, text string) float64 {
w := float64(img.Width())
w -= w / 5
h := float64(img.Height())
maxSize := h / 6
step := maxSize / 10
size := step
for {
img.LoadFontFace(*font, size)
line := longestLine(img.WordWrap(text, w))
dw, dh := img.MeasureString(line)
if dh > maxSize || dw > w {
size -= step
break
}
size += step
}
return size
}
func longestLine(lines []string) string {
var max int
var lineIndex int
for i, line := range lines {
l := len(line)
if l > max {
max = l
lineIndex = i
}
}
return lines[lineIndex]
}