diff --git a/connectors/slackapp/slackApp.go b/connectors/slackapp/slackApp.go index f53cbd8..dca6b2c 100644 --- a/connectors/slackapp/slackApp.go +++ b/connectors/slackapp/slackApp.go @@ -422,7 +422,12 @@ func (s *SlackApp) buildMessage(m *slackevents.MessageEvent) msg.Message { if m.BotID != "" { defaultName = m.Username } + icon := "" + log.Debug().Msgf("Getting user %s", m.User) name, u := s.getUser(m.User, defaultName) + if u != nil { + icon = u.Profile.Image192 + } if m.Username != "" && name == "unknown" { name = m.Username } @@ -438,7 +443,7 @@ func (s *SlackApp) buildMessage(m *slackevents.MessageEvent) msg.Message { User: &user.User{ ID: m.User, Name: name, - Icon: u.Profile.Image192, + Icon: icon, }, Body: text, Raw: m, @@ -637,14 +642,23 @@ func (s *SlackApp) reactionReceived(event *slack.ReactionAddedEvent) error { } func (s *SlackApp) Profile(name string) (user.User, error) { - n, u := s.getUser(name, "unknown") - if n == "unknown" { - return user.User{}, fmt.Errorf("user %s is not known to us", name) + log.Debug().Msgf("Getting profile for %s", name) + + users, err := s.api.GetUsers() + if err != nil { + return user.User{}, err } - return user.User{ - ID: u.ID, - Name: n, - Admin: false, - Icon: u.Profile.Image192, - }, nil + + for _, u := range users { + if u.Name == name { + return user.User{ + ID: u.ID, + Name: stringForUser(&u), + Admin: false, + Icon: u.Profile.Image192, + }, nil + } + } + + return user.User{}, fmt.Errorf("user %s not found", err) } diff --git a/plugins/meme/meme.go b/plugins/meme/meme.go index fda453c..e0402c6 100644 --- a/plugins/meme/meme.go +++ b/plugins/meme/meme.go @@ -6,6 +6,8 @@ import ( "fmt" "html/template" "image" + "image/color" + "image/draw" "image/png" "net/http" "net/url" @@ -214,14 +216,25 @@ func (p *MemePlugin) slashMeme(c bot.Connector) http.HandlerFunc { isCmd, message := bot.IsCmd(p.c, parts[1]) format := parts[0] + bullyIcon := "" + for _, bully := range p.c.GetArray("meme.bully", []string{}) { if format == bully { + if u, err := c.Profile(bully); err == nil { + bullyIcon = u.Icon + } else { + log.Debug().Err(err).Msgf("could not get profile for %s", format) + } formats := p.c.GetMap("meme.memes", defaultFormats) format = randEntry(formats) break } } + if u, err := c.Profile(from); bullyIcon == "" && err == nil { + bullyIcon = u.Icon + } + log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text) w.WriteHeader(200) w.Write(nil) @@ -239,7 +252,7 @@ func (p *MemePlugin) slashMeme(c bot.Connector) http.HandlerFunc { message = top } - id, err := p.genMeme(format, top, bottom) + id, err := p.genMeme(format, top, bottom, bullyIcon) if err != nil { msg := fmt.Sprintf("Hey %s, I couldn't download that image you asked for.", from) p.bot.Send(c, bot.Message, channel, msg) @@ -304,7 +317,7 @@ var defaultFormats = map[string]string{ "raptor": "Philosoraptor.jpg", } -func (p *MemePlugin) genMeme(meme, top, bottom string) (string, error) { +func (p *MemePlugin) genMeme(meme, top, bottom, bully string) (string, error) { fontSizes := []float64{48, 36, 24, 16, 12} fontSize := fontSizes[0] @@ -354,6 +367,10 @@ func (p *MemePlugin) genMeme(meme, top, bottom string) (string, error) { h = r.Dy() log.Debug().Msgf("resized to %v, %v", w, h) + if bully != "" { + img = p.applyBully(img, bully) + } + m := gg.NewContext(w, h) m.DrawImage(img, 0, 0) fontLocation := p.c.Get("meme.font", "impact.ttf") @@ -407,3 +424,57 @@ func (p *MemePlugin) genMeme(meme, top, bottom string) (string, error) { return path, nil } + +func (p *MemePlugin) applyBully(img image.Image, bully string) image.Image { + log.Debug().Msgf("applying bully: %s", bully) + dst := image.NewRGBA(img.Bounds()) + u, err := url.Parse(bully) + if err != nil { + return img + } + bullyImg, err := DownloadTemplate(u) + if err != nil { + return img + } + + scaleFactor := p.c.GetFloat64("meme.bullyScale", 0.1) + + newSzX := uint(float64(img.Bounds().Max.X) * scaleFactor) + newSzY := uint(float64(img.Bounds().Max.Y) * scaleFactor) + + bullyImg = resize.Resize(newSzX, newSzY, bullyImg, resize.Lanczos3) + + draw.Draw(dst, img.Bounds(), img, image.Point{}, draw.Src) + srcSz := img.Bounds().Size() + + w, h := bullyImg.Bounds().Max.X, bullyImg.Bounds().Max.Y + + pt := image.Point{srcSz.X - w, srcSz.Y - h} + rect := image.Rect(pt.X, pt.Y, srcSz.X, srcSz.Y) + + //draw.Draw(dst, image.Rect(srcSz.X-128, srcSz.Y-128, srcSz.X, srcSz.Y), bullyImg, image.Point{}, draw.Src) + draw.DrawMask(dst, rect, bullyImg, image.Point{}, &circle{image.Point{w / 2, h / 2}, w / 2}, image.Point{}, draw.Over) + return dst +} + +// the following is ripped off of https://blog.golang.org/image-draw +type circle struct { + p image.Point + r int +} + +func (c *circle) ColorModel() color.Model { + return color.AlphaModel +} + +func (c *circle) Bounds() image.Rectangle { + return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r) +} + +func (c *circle) At(x, y int) color.Color { + xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r) + if xx*xx+yy*yy < rr*rr { + return color.Alpha{255} + } + return color.Alpha{0} +}