Merge pull request #295 from velour/discord

Discord
This commit is contained in:
Chris Sexton 2020-10-09 10:56:37 -04:00 committed by GitHub
commit 17b5f11fb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 386 additions and 95 deletions

2
.gitignore vendored
View File

@ -70,8 +70,6 @@ util/*/files
.idea .idea
logs logs
util/files util/files
impact.ttf
gus.sh gus.sh
rathaus.sh rathaus.sh
run.sh run.sh

View File

@ -29,11 +29,15 @@ const (
Help Help
// SelfMessage triggers when the bot is sending a message // SelfMessage triggers when the bot is sending a message
SelfMessage SelfMessage
// Delete removes a message by ID
Delete
) )
type ImageAttachment struct { type ImageAttachment struct {
URL string URL string
AltTxt string AltTxt string
Width int
Height int
} }
type Kind int type Kind int

View File

@ -12,6 +12,7 @@ type Log Messages
type Messages []Message type Messages []Message
type Message struct { type Message struct {
ID string
User *user.User User *user.User
// With Slack, channel is the ID of a channel // With Slack, channel is the ID of a channel
Channel string Channel string

View File

@ -2,14 +2,17 @@
package user package user
import "image"
// User type stores user history. This is a vehicle that will follow the user for the active // User type stores user history. This is a vehicle that will follow the user for the active
// session // session
type User struct { type User struct {
// Current nickname known // Current nickname known
ID string ID string
Name string Name string
Admin bool Admin bool
Icon string Icon string
IconImg image.Image
} }
func New(name string) User { func New(name string) User {

View File

@ -0,0 +1,235 @@
package discord
import (
"errors"
"fmt"
"strings"
"github.com/velour/catbase/bot/msg"
"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/user"
"github.com/velour/catbase/config"
)
type Discord struct {
config *config.Config
client *discordgo.Session
event bot.Callback
emojiCache map[string]string
}
func New(config *config.Config) *Discord {
client, err := discordgo.New("Bot " + config.Get("DISCORDBOTTOKEN", ""))
if err != nil {
log.Fatal().Err(err).Msg("Could not connect to Discord")
}
d := &Discord{
config: config,
client: client,
}
return d
}
func (d *Discord) RegisterEvent(callback bot.Callback) {
d.event = callback
}
func (d Discord) Send(kind bot.Kind, args ...interface{}) (string, error) {
switch kind {
case bot.Message:
return d.sendMessage(args[0].(string), args[1].(string), false, args...)
case bot.Action:
return d.sendMessage(args[0].(string), args[1].(string), true, args...)
case bot.Edit:
st, err := d.client.ChannelMessageEdit(args[0].(string), args[1].(string), args[2].(string))
return st.ID, err
case bot.Reply:
original, err := d.client.ChannelMessage(args[0].(string), args[1].(string))
msg := args[2].(string)
if err != nil {
log.Error().Err(err).Msg("could not get original")
} else {
msg = fmt.Sprintf("> %v\n%s", original, msg)
}
return d.sendMessage(args[0].(string), msg, false, args...)
case bot.Reaction:
msg := args[2].(msg.Message)
err := d.client.MessageReactionAdd(args[0].(string), msg.ID, args[1].(string))
return args[1].(string), err
case bot.Delete:
ch := args[0].(string)
id := args[1].(string)
err := d.client.ChannelMessageDelete(ch, id)
if err != nil {
log.Error().Err(err).Msg("cannot delete message")
}
return id, err
default:
log.Error().Msgf("discord.Send: unknown kind, %+v", kind)
return "", errors.New("unknown message type")
}
}
func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...interface{}) (string, error) {
if meMessage && !strings.HasPrefix(message, "_") && !strings.HasSuffix(message, "_") {
message = "_" + message + "_"
}
var embeds *discordgo.MessageEmbed
for _, arg := range args {
switch a := arg.(type) {
case bot.ImageAttachment:
//embeds.URL = a.URL
embeds = &discordgo.MessageEmbed{}
embeds.Description = a.AltTxt
embeds.Image = &discordgo.MessageEmbedImage{
URL: a.URL,
Width: a.Width,
Height: a.Height,
}
}
}
data := &discordgo.MessageSend{
Content: message,
Embed: embeds,
}
log.Debug().
Interface("data", data).
Interface("args", args).
Msg("sending message")
st, err := d.client.ChannelMessageSendComplex(channel, data)
//st, err := d.client.ChannelMessageSend(channel, message)
if err != nil {
log.Error().Err(err).Msg("Error sending message")
return "", err
}
return st.ID, err
}
func (d *Discord) GetEmojiList() map[string]string {
if d.emojiCache != nil {
return d.emojiCache
}
guidID := d.config.Get("discord.guildid", "")
if guidID == "" {
}
e, err := d.client.GuildEmojis(guidID)
if err != nil {
log.Error().Err(err).Msg("could not retrieve emojis")
return map[string]string{}
}
emojis := map[string]string{}
for _, e := range e {
emojis[e.ID] = e.Name
}
return emojis
}
func (d *Discord) Who(id string) []string {
ch, err := d.client.Channel(id)
if err != nil {
log.Error().Err(err).Msgf("Error getting users")
return []string{}
}
users := []string{}
for _, u := range ch.Recipients {
users = append(users, u.Username)
}
return users
}
func (d *Discord) Profile(id string) (user.User, error) {
u, err := d.client.User(id)
if err != nil {
log.Error().Err(err).Msg("Error getting user")
return user.User{}, err
}
return *d.convertUser(u), nil
}
func (d *Discord) convertUser(u *discordgo.User) *user.User {
img, err := d.client.UserAvatar(u.ID)
if err != nil {
log.Error().Err(err).Msg("error getting avatar")
}
return &user.User{
ID: u.ID,
Name: u.Username,
Admin: false,
IconImg: img,
}
}
func (d *Discord) Serve() error {
log.Debug().Msg("starting discord serve function")
d.client.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuilds |
discordgo.IntentsGuildMessages)
err := d.client.Open()
if err != nil {
log.Debug().Err(err).Msg("error opening client")
return err
}
log.Debug().Msg("discord connection open")
d.client.AddHandler(d.messageCreate)
return nil
}
func (d *Discord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
log.Debug().Msgf("discord message: %+v", m)
if m.Author.ID == s.State.User.ID {
return
}
ch, err := s.Channel(m.ChannelID)
if err != nil {
log.Error().Err(err).Msg("error getting channel info")
}
isCmd, text := bot.IsCmd(d.config, m.Content)
tStamp, _ := m.Timestamp.Parse()
msg := msg.Message{
ID: m.ID,
User: d.convertUser(m.Author),
Channel: m.ChannelID,
ChannelName: ch.Name,
Body: text,
Command: isCmd,
Time: tStamp,
}
log.Debug().Interface("m", m).Interface("msg", msg).Msg("message received")
authorizedChannels := d.config.GetArray("channels", []string{})
if in(ch.Name, authorizedChannels) {
d.event(d, bot.Message, msg)
}
}
func in(s string, lst []string) bool {
for _, i := range lst {
if s == i {
return true
}
}
return false
}

View File

@ -40,7 +40,6 @@ const defaultLogFormat = "[{{fixDate .Time \"2006-01-02 15:04:05\"}}] {{if .Topi
// "User":{"Admin":false,"ID":"U0RLUDELD","Name":"flyngpngn"}} // "User":{"Admin":false,"ID":"U0RLUDELD","Name":"flyngpngn"}}
type SlackApp struct { type SlackApp struct {
bot bot.Bot
config *config.Config config *config.Config
api *slack.Client api *slack.Client
@ -448,6 +447,7 @@ func (s *SlackApp) buildMessage(m *slackevents.MessageEvent) msg.Message {
tstamp := slackTStoTime(m.TimeStamp) tstamp := slackTStoTime(m.TimeStamp)
return msg.Message{ return msg.Message{
ID: m.TimeStamp,
User: &user.User{ User: &user.User{
ID: m.User, ID: m.User,
Name: name, Name: name,
@ -652,8 +652,8 @@ func (s *SlackApp) reactionReceived(event *slackevents.ReactionAddedEvent) error
return s.log(msg, channel) return s.log(msg, channel)
} }
func (s *SlackApp) Profile(name string) (user.User, error) { func (s *SlackApp) Profile(identifier string) (user.User, error) {
log.Debug().Msgf("Getting profile for %s", name) log.Debug().Msgf("Getting profile for %s", identifier)
users, err := s.api.GetUsers() users, err := s.api.GetUsers()
if err != nil { if err != nil {
@ -661,7 +661,7 @@ func (s *SlackApp) Profile(name string) (user.User, error) {
} }
for _, u := range users { for _, u := range users {
if u.Name == name { if u.Name == identifier || u.ID == identifier {
return user.User{ return user.User{
ID: u.ID, ID: u.ID,
Name: stringForUser(&u), Name: stringForUser(&u),

1
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/antchfx/xpath v1.1.1 // indirect github.com/antchfx/xpath v1.1.1 // indirect
github.com/armon/go-radix v1.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect
github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 // indirect github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 // indirect
github.com/bwmarrin/discordgo v0.22.0
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598
github.com/chrissexton/gofuck v1.0.0 github.com/chrissexton/gofuck v1.0.0
github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff

5
go.sum
View File

@ -27,6 +27,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 h1:ekDALXAVvY/Ub1UtNta3inKQwZ/jMB/zpOtD8rAYh78= github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 h1:ekDALXAVvY/Ub1UtNta3inKQwZ/jMB/zpOtD8rAYh78=
github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330/go.mod h1:nH+k0SvAt3HeiYyOlJpLLv1HG1p7KWP7qU9QPp2/pCo= github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330/go.mod h1:nH+k0SvAt3HeiYyOlJpLLv1HG1p7KWP7qU9QPp2/pCo=
github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM=
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 h1:j2XRGH5Y5uWtBYXGwmrjKeM/kfu/jh7ZcnrGvyN5Ttk= github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 h1:j2XRGH5Y5uWtBYXGwmrjKeM/kfu/jh7ZcnrGvyN5Ttk=
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o= github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o=
github.com/chrissexton/gofuck v1.0.0 h1:vxg/tIfI2HunJOErSotmMqMRNfLRVO+BTjSKpFoAizA= github.com/chrissexton/gofuck v1.0.0 h1:vxg/tIfI2HunJOErSotmMqMRNfLRVO+BTjSKpFoAizA=
@ -70,6 +72,7 @@ github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0s
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM=
@ -145,7 +148,9 @@ github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89/go.mod h1:ejwOYCjnDMyO
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 h1:p3rTUXxzuKsBOsHlkly7+rj9wagFBKeIsCDKkDII9sw= github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 h1:p3rTUXxzuKsBOsHlkly7+rj9wagFBKeIsCDKkDII9sw=
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158/go.mod h1:Ojy3BTOiOTwpHpw7/HNi+TVTFppao191PQs+Qc3sqpE= github.com/velour/velour v0.0.0-20160303155839-8e090e68d158/go.mod h1:Ojy3BTOiOTwpHpw7/HNi+TVTFppao191PQs+Qc3sqpE=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=

View File

@ -9,6 +9,8 @@ import (
"os" "os"
"time" "time"
"github.com/velour/catbase/connectors/discord"
"github.com/velour/catbase/plugins/gpt2" "github.com/velour/catbase/plugins/gpt2"
"github.com/velour/catbase/plugins/achievements" "github.com/velour/catbase/plugins/achievements"
@ -107,6 +109,8 @@ func main() {
client = slack.New(c) client = slack.New(c)
case "slackapp": case "slackapp":
client = slackapp.New(c) client = slackapp.New(c)
case "discord":
client = discord.New(c)
default: default:
log.Fatal().Msgf("Unknown connection type: %s", c.Get("type", "UNSET")) log.Fatal().Msgf("Unknown connection type: %s", c.Get("type", "UNSET"))
} }
@ -159,5 +163,6 @@ func main() {
} }
addr := c.Get("HttpAddr", "127.0.0.1:1337") addr := c.Get("HttpAddr", "127.0.0.1:1337")
log.Fatal().Err(http.ListenAndServe(addr, nil)) log.Debug().Msgf("starting web service at %s", addr)
log.Fatal().Err(http.ListenAndServe(addr, nil)).Msg("bot killed")
} }

View File

@ -12,6 +12,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"regexp"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -67,7 +68,19 @@ func New(b bot.Bot) *MemePlugin {
return mp return mp
} }
var cmdMatch = regexp.MustCompile(`(?i)meme (.+)`)
func (p *MemePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *MemePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if message.Command && cmdMatch.MatchString(message.Body) {
subs := cmdMatch.FindStringSubmatch(message.Body)
if len(subs) != 2 {
p.bot.Send(c, bot.Message, message.Channel, "Invalid meme request.")
return true
}
minusMeme := subs[1]
p.sendMeme(c, message.Channel, message.ChannelName, message.ID, message.User, minusMeme)
return true
}
return false return false
} }
@ -202,6 +215,107 @@ func (p *MemePlugin) img(w http.ResponseWriter, r *http.Request) {
p.images.cleanup() p.images.cleanup()
} }
func (p *MemePlugin) bully(c bot.Connector, format, id string) image.Image {
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(id); bullyIcon == "" && err == nil {
if u.IconImg != nil {
return u.IconImg
}
bullyIcon = u.Icon
}
u, err := url.Parse(bullyIcon)
if err != nil {
log.Error().Err(err).Msg("error with bully URL")
}
bullyImg, err := DownloadTemplate(u)
if err != nil {
log.Error().Err(err).Msg("error downloading bully icon")
}
return bullyImg
}
func (p *MemePlugin) sendMeme(c bot.Connector, channel, channelName, msgID string, from *user.User, text string) {
parts := strings.SplitN(text, " ", 2)
if len(parts) != 2 {
log.Debug().Msgf("Bad meme request: %v, %v", from, text)
p.bot.Send(c, bot.Message, channel, fmt.Sprintf("%v tried to send me a bad meme request.", from.Name))
return
}
isCmd, message := bot.IsCmd(p.c, parts[1])
format := parts[0]
log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text)
go func() {
top, bottom := "", message
parts = strings.Split(message, "\n")
if len(parts) > 1 {
top, bottom = parts[0], parts[1]
}
if top == "_" {
message = bottom
} else if bottom == "_" {
message = top
}
bullyImg := p.bully(c, format, from.ID)
id, w, h, err := p.genMeme(format, top, bottom, bullyImg)
if err != nil {
msg := fmt.Sprintf("Hey %v, I couldn't download that image you asked for.", from.Name)
p.bot.Send(c, bot.Message, channel, msg)
return
}
baseURL := p.c.Get("BaseURL", ``)
u, _ := url.Parse(baseURL)
u.Path = path.Join(u.Path, "meme", "img", id)
log.Debug().Msgf("image is at %s", u.String())
_, err = p.bot.Send(c, bot.Message, channel, "", bot.ImageAttachment{
URL: u.String(),
AltTxt: fmt.Sprintf("%s: %s", from.Name, message),
Width: w,
Height: h,
})
if err == nil && msgID != "" {
p.bot.Send(c, bot.Delete, channel, msgID)
}
m := msg.Message{
User: &user.User{
ID: from.ID,
Name: from.Name,
Admin: false,
},
Channel: channel,
ChannelName: channelName,
Body: message,
Command: isCmd,
Time: time.Now(),
}
p.bot.Receive(c, bot.Message, m)
}()
}
func (p *MemePlugin) slashMeme(c bot.Connector) http.HandlerFunc { func (p *MemePlugin) slashMeme(c bot.Connector) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
@ -212,81 +326,15 @@ func (p *MemePlugin) slashMeme(c bot.Connector) http.HandlerFunc {
text := r.PostForm.Get("text") text := r.PostForm.Get("text")
log.Debug().Msgf("channel: %s", channel) log.Debug().Msgf("channel: %s", channel)
parts := strings.SplitN(text, " ", 2) user := &user.User{
if len(parts) != 2 { ID: from, // HACK but should work fine
log.Debug().Msgf("Bad meme request: %s, %s", from, text) Name: from,
p.bot.Send(c, bot.Message, channel, fmt.Sprintf("%s tried to send me a bad meme request.", from))
return
}
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 { p.sendMeme(c, channel, channelName, "", user, text)
bullyIcon = u.Icon
}
log.Debug().Strs("parts", parts).Msgf("Meme:\n%+v", text)
w.WriteHeader(200) w.WriteHeader(200)
w.Write(nil) w.Write(nil)
go func() {
top, bottom := "", message
parts = strings.Split(message, "\n")
if len(parts) > 1 {
top, bottom = parts[0], parts[1]
}
if top == "_" {
message = bottom
} else if bottom == "_" {
message = top
}
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)
return
}
baseURL := p.c.Get("BaseURL", `https://catbase.velour.ninja`)
u, _ := url.Parse(baseURL)
u.Path = path.Join(u.Path, "meme", "img", id)
log.Debug().Msgf("image is at %s", u.String())
p.bot.Send(c, bot.Message, channel, "", bot.ImageAttachment{
URL: u.String(),
AltTxt: fmt.Sprintf("%s: %s", from, message),
})
m := msg.Message{
User: &user.User{
ID: from,
Name: from,
Admin: false,
},
Channel: channel,
ChannelName: channelName,
Body: message,
Command: isCmd,
Time: time.Now(),
}
p.bot.Receive(c, bot.Message, m)
}()
} }
} }
@ -322,7 +370,7 @@ var defaultFormats = map[string]string{
"raptor": "Philosoraptor.jpg", "raptor": "Philosoraptor.jpg",
} }
func (p *MemePlugin) genMeme(meme, top, bottom, bully string) (string, error) { func (p *MemePlugin) genMeme(meme, top, bottom string, bully image.Image) (string, int, int, error) {
fontSizes := []float64{48, 36, 24, 16, 12} fontSizes := []float64{48, 36, 24, 16, 12}
fontSize := fontSizes[0] fontSize := fontSizes[0]
@ -346,7 +394,7 @@ func (p *MemePlugin) genMeme(meme, top, bottom, bully string) (string, error) {
img, err := DownloadTemplate(u) img, err := DownloadTemplate(u)
if err != nil { if err != nil {
log.Debug().Msgf("failed to download image: %s", err) log.Debug().Msgf("failed to download image: %s", err)
return "", err return "", 0, 0, err
} }
r := img.Bounds() r := img.Bounds()
@ -372,7 +420,7 @@ func (p *MemePlugin) genMeme(meme, top, bottom, bully string) (string, error) {
h = r.Dy() h = r.Dy()
log.Debug().Msgf("resized to %v, %v", w, h) log.Debug().Msgf("resized to %v, %v", w, h)
if bully != "" { if bully != nil {
img = p.applyBully(img, bully) img = p.applyBully(img, bully)
} }
@ -427,20 +475,11 @@ func (p *MemePlugin) genMeme(meme, top, bottom, bully string) (string, error) {
log.Debug().Msgf("Saved to %s\n", path) log.Debug().Msgf("Saved to %s\n", path)
return path, nil return path, w, h, nil
} }
func (p *MemePlugin) applyBully(img image.Image, bully string) image.Image { func (p *MemePlugin) applyBully(img, bullyImg image.Image) image.Image {
log.Debug().Msgf("applying bully: %s", bully)
dst := image.NewRGBA(img.Bounds()) 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) scaleFactor := p.c.GetFloat64("meme.bullyScale", 0.1)