mirror of https://github.com/velour/catbase.git
Compare commits
6 Commits
22658949a8
...
3297878421
Author | SHA1 | Date |
---|---|---|
Chris Sexton | 3297878421 | |
Chris Sexton | 93cfc0ddfb | |
Chris Sexton | a64f4800b1 | |
Chris Sexton | 2ce2b01295 | |
Chris Sexton | 112ccea89a | |
Chris Sexton | ba99b2113c |
12
bot/bot.go
12
bot/bot.go
|
@ -6,6 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -135,8 +137,14 @@ func New(config *config.Config, connector Connector) Bot {
|
||||||
|
|
||||||
func (b *bot) ListenAndServe() {
|
func (b *bot) ListenAndServe() {
|
||||||
addr := b.config.Get("HttpAddr", "127.0.0.1:1337")
|
addr := b.config.Get("HttpAddr", "127.0.0.1:1337")
|
||||||
log.Debug().Msgf("starting web service at %s", addr)
|
stop := make(chan os.Signal, 1)
|
||||||
log.Fatal().Err(http.ListenAndServe(addr, b.router)).Msg("bot killed")
|
signal.Notify(stop, os.Interrupt)
|
||||||
|
go func() {
|
||||||
|
log.Debug().Msgf("starting web service at %s", addr)
|
||||||
|
log.Fatal().Err(http.ListenAndServe(addr, b.router)).Msg("bot killed")
|
||||||
|
}()
|
||||||
|
<-stop
|
||||||
|
b.DefaultConnector().Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bot) RegisterWeb(r http.Handler, root string) {
|
func (b *bot) RegisterWeb(r http.Handler, root string) {
|
||||||
|
|
|
@ -191,6 +191,9 @@ type Connector interface {
|
||||||
// Serve starts a connector's connection routine
|
// Serve starts a connector's connection routine
|
||||||
Serve() error
|
Serve() error
|
||||||
|
|
||||||
|
// Shutdown cleans up after the connection
|
||||||
|
Shutdown()
|
||||||
|
|
||||||
// Who returns a user list for a channel
|
// Who returns a user list for a channel
|
||||||
Who(string) []string
|
Who(string) []string
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ type Discord struct {
|
||||||
|
|
||||||
// store IDs -> nick and vice versa for quick conversion
|
// store IDs -> nick and vice versa for quick conversion
|
||||||
uidCache map[string]string
|
uidCache map[string]string
|
||||||
|
|
||||||
|
registeredCmds []*discordgo.ApplicationCommand
|
||||||
|
cmdHandlers map[string]CmdHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *config.Config) *Discord {
|
func New(config *config.Config) *Discord {
|
||||||
|
@ -35,10 +38,17 @@ func New(config *config.Config) *Discord {
|
||||||
log.Fatal().Err(err).Msg("Could not connect to Discord")
|
log.Fatal().Err(err).Msg("Could not connect to Discord")
|
||||||
}
|
}
|
||||||
d := &Discord{
|
d := &Discord{
|
||||||
config: config,
|
config: config,
|
||||||
client: client,
|
client: client,
|
||||||
uidCache: map[string]string{},
|
uidCache: map[string]string{},
|
||||||
|
registeredCmds: []*discordgo.ApplicationCommand{},
|
||||||
|
cmdHandlers: map[string]CmdHandler{},
|
||||||
}
|
}
|
||||||
|
d.client.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
if h, ok := d.cmdHandlers[i.ApplicationCommandData().Name]; ok {
|
||||||
|
h(s, i)
|
||||||
|
}
|
||||||
|
})
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
func (d *Discord) GetRouter() (http.Handler, string) {
|
func (d *Discord) GetRouter() (http.Handler, string) {
|
||||||
|
@ -390,3 +400,26 @@ func (d *Discord) SetRole(userID, roleID string) error {
|
||||||
}
|
}
|
||||||
return d.client.GuildMemberRoleAdd(guildID, userID, roleID)
|
return d.client.GuildMemberRoleAdd(guildID, userID, roleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CmdHandler func(s *discordgo.Session, i *discordgo.InteractionCreate)
|
||||||
|
|
||||||
|
func (d *Discord) RegisterSlashCmd(c discordgo.ApplicationCommand, handler CmdHandler) error {
|
||||||
|
guildID := d.config.Get("discord.guildid", "")
|
||||||
|
cmd, err := d.client.ApplicationCommandCreate(d.client.State.User.ID, guildID, &c)
|
||||||
|
d.cmdHandlers[c.Name] = handler
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.registeredCmds = append(d.registeredCmds, cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discord) Shutdown() {
|
||||||
|
log.Debug().Msgf("Shutting down and deleting %d slash commands", len(d.registeredCmds))
|
||||||
|
guildID := d.config.Get("discord.guildid", "")
|
||||||
|
for _, c := range d.registeredCmds {
|
||||||
|
if err := d.client.ApplicationCommandDelete(d.client.State.User.ID, guildID, c.ID); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not delete command %s", c.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -359,3 +359,5 @@ func (i Irc) GetRoles() ([]bot.Role, error) {
|
||||||
func (i Irc) SetRole(userID, roleID string) error {
|
func (i Irc) SetRole(userID, roleID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i Irc) Shutdown() {}
|
||||||
|
|
|
@ -749,3 +749,5 @@ func (s *SlackApp) GetRoles() ([]bot.Role, error) {
|
||||||
func (s *SlackApp) SetRole(userID, roleID string) error {
|
func (s *SlackApp) SetRole(userID, roleID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SlackApp) Shutdown() {}
|
||||||
|
|
1
main.go
1
main.go
|
@ -180,6 +180,7 @@ func main() {
|
||||||
log.Fatal().Err(err)
|
log.Fatal().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug().Msgf("Sending bot.Startup message")
|
||||||
b.Receive(client, bot.Startup, msg.Message{})
|
b.Receive(client, bot.Startup, msg.Message{})
|
||||||
|
|
||||||
b.ListenAndServe()
|
b.ListenAndServe()
|
||||||
|
|
|
@ -43,6 +43,8 @@ func (p *CliPlugin) registerWeb() {
|
||||||
p.bot.RegisterWebName(r, "/cli", "CLI")
|
p.bot.RegisterWebName(r, "/cli", "CLI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *CliPlugin) Shutdown() {}
|
||||||
|
|
||||||
func (p *CliPlugin) GetRouter() (http.Handler, string) {
|
func (p *CliPlugin) GetRouter() (http.Handler, string) {
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,14 +256,14 @@ func (i *Item) Delete() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CounterPlugin) migrate(r bot.Request) bool {
|
func (p *CounterPlugin) migrate(r bot.Request) (retVal bool) {
|
||||||
db := p.db
|
db := p.db
|
||||||
|
|
||||||
nicks := []string{}
|
nicks := []string{}
|
||||||
err := db.Select(&nicks, `select distinct nick from counter where userid is null`)
|
err := db.Select(&nicks, `select distinct nick from counter where userid is null`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("could not get nick list")
|
log.Error().Err(err).Msg("could not get nick list")
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("Migrating %d nicks to IDs", len(nicks))
|
log.Debug().Msgf("Migrating %d nicks to IDs", len(nicks))
|
||||||
|
@ -284,7 +284,7 @@ func (p *CounterPlugin) migrate(r bot.Request) bool {
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
log.Error().Err(err).Msg("Could not migrate users")
|
log.Error().Err(err).Msg("Could not migrate users")
|
||||||
}
|
}
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDB(b bot.Bot) error {
|
func setupDB(b bot.Bot) error {
|
||||||
|
|
|
@ -2,8 +2,12 @@ package cowboy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/velour/catbase/plugins/emojy"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/velour/catbase/connectors/discord"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/velour/catbase/bot"
|
"github.com/velour/catbase/bot"
|
||||||
"github.com/velour/catbase/config"
|
"github.com/velour/catbase/config"
|
||||||
|
@ -17,6 +21,8 @@ type Cowboy struct {
|
||||||
baseEmojyURL string
|
baseEmojyURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultOverlays = map[string]string{"hat": "hat"}
|
||||||
|
|
||||||
func New(b bot.Bot) *Cowboy {
|
func New(b bot.Bot) *Cowboy {
|
||||||
emojyPath := b.Config().Get("emojy.path", "emojy")
|
emojyPath := b.Config().Get("emojy.path", "emojy")
|
||||||
baseURL := b.Config().Get("emojy.baseURL", "/emojy/file")
|
baseURL := b.Config().Get("emojy.baseURL", "/emojy/file")
|
||||||
|
@ -33,6 +39,28 @@ func New(b bot.Bot) *Cowboy {
|
||||||
|
|
||||||
func (p *Cowboy) register() {
|
func (p *Cowboy) register() {
|
||||||
tbl := bot.HandlerTable{
|
tbl := bot.HandlerTable{
|
||||||
|
{
|
||||||
|
Kind: bot.Startup, IsCmd: false,
|
||||||
|
Regex: regexp.MustCompile(`.*`),
|
||||||
|
Handler: func(r bot.Request) bool {
|
||||||
|
log.Debug().Msgf("Got bot.Startup")
|
||||||
|
switch conn := r.Conn.(type) {
|
||||||
|
case *discord.Discord:
|
||||||
|
log.Debug().Msg("Found a discord connection")
|
||||||
|
p.registerCmds(conn)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: true,
|
||||||
|
Regex: regexp.MustCompile(`(?i)^:cowboy_clear_cache:$`),
|
||||||
|
Handler: func(r bot.Request) bool {
|
||||||
|
cowboyClearCache()
|
||||||
|
p.b.Send(r.Conn, bot.Ephemeral, r.Msg.Channel, r.Msg.User.ID, ":cowboy_cache_cleared:")
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Kind: bot.Message, IsCmd: false,
|
Kind: bot.Message, IsCmd: false,
|
||||||
Regex: regexp.MustCompile(`(?i)^:cowboy_(?P<what>.+):$`),
|
Regex: regexp.MustCompile(`(?i)^:cowboy_(?P<what>.+):$`),
|
||||||
|
@ -46,6 +74,16 @@ func (p *Cowboy) register() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Cowboy) makeCowboy(r bot.Request) {
|
func (p *Cowboy) makeCowboy(r bot.Request) {
|
||||||
|
what := r.Values["what"]
|
||||||
|
// This'll add the image to the cowboy_cache before discord tries to access it over http
|
||||||
|
overlays := p.c.GetMap("cowboy.overlays", defaultOverlays)
|
||||||
|
hat := overlays["hat"]
|
||||||
|
i, err := cowboy(p.emojyPath, p.baseEmojyURL, hat, what)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg(":cowboy_fail:")
|
||||||
|
p.b.Send(r.Conn, bot.Ephemeral, r.Msg.Channel, r.Msg.User.ID, "Hey cowboy, that image wasn't there.")
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Debug().Msgf("makeCowboy: %s", r.Values["what"])
|
log.Debug().Msgf("makeCowboy: %s", r.Values["what"])
|
||||||
base := p.c.Get("baseURL", "http://127.0.0.1:1337")
|
base := p.c.Get("baseURL", "http://127.0.0.1:1337")
|
||||||
u := base + "/cowboy/img/" + r.Values["what"]
|
u := base + "/cowboy/img/" + r.Values["what"]
|
||||||
|
@ -53,7 +91,107 @@ func (p *Cowboy) makeCowboy(r bot.Request) {
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "", bot.ImageAttachment{
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "", bot.ImageAttachment{
|
||||||
URL: u,
|
URL: u,
|
||||||
AltTxt: fmt.Sprintf("%s: %s", r.Msg.User.Name, r.Msg.Body),
|
AltTxt: fmt.Sprintf("%s: %s", r.Msg.User.Name, r.Msg.Body),
|
||||||
Width: 64,
|
Width: i.Bounds().Max.X,
|
||||||
Height: 64,
|
Height: i.Bounds().Max.Y,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Cowboy) registerCmds(d *discord.Discord) {
|
||||||
|
log.Debug().Msg("About to register some startup commands")
|
||||||
|
cmd := discordgo.ApplicationCommand{
|
||||||
|
Name: "cowboy",
|
||||||
|
Description: "cowboy-ify an emojy",
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "emojy",
|
||||||
|
Description: "which emojy you want cowboied",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
overlays := p.c.GetMap("cowboy.overlays", defaultOverlays)
|
||||||
|
hat := overlays["hat"]
|
||||||
|
log.Debug().Msgf("Overlay: %s", hat)
|
||||||
|
if err := d.RegisterSlashCmd(cmd, p.mkOverlayCB(hat)); err != nil {
|
||||||
|
log.Error().Err(err).Msg("could not register cowboy command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.c.GetInt("cowboy.overlaysEnabled", 0) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = discordgo.ApplicationCommand{
|
||||||
|
Name: "overlay",
|
||||||
|
Description: "overlay-ify an emojy",
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "overlay",
|
||||||
|
Description: "which overlay you want",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "emojy",
|
||||||
|
Description: "which emojy you want overlaid",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := d.RegisterSlashCmd(cmd, p.mkOverlayCB("")); err != nil {
|
||||||
|
log.Error().Err(err).Msg("could not register cowboy command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Cowboy) mkOverlayCB(overlay string) func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
lastEmojy := p.c.Get("cowboy.lastEmojy", "rust")
|
||||||
|
emojyPlugin := emojy.NewAPI(p.b)
|
||||||
|
|
||||||
|
name := i.ApplicationCommandData().Options[0].StringValue()
|
||||||
|
if overlay == "" {
|
||||||
|
overlay = name
|
||||||
|
name = i.ApplicationCommandData().Options[1].StringValue()
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf("You asked for %s overlaid by %s", name, overlay)
|
||||||
|
log.Debug().Msgf("got a cowboy command for %s overlaid by %s replacing %s",
|
||||||
|
name, overlay, lastEmojy)
|
||||||
|
prefix := overlay
|
||||||
|
|
||||||
|
newEmojy, err := cowboy(p.emojyPath, p.baseEmojyURL, overlay, name)
|
||||||
|
if err != nil {
|
||||||
|
msg = err.Error()
|
||||||
|
goto resp
|
||||||
|
}
|
||||||
|
|
||||||
|
err = emojyPlugin.RmEmojy(p.b.DefaultConnector(), lastEmojy)
|
||||||
|
if err != nil {
|
||||||
|
msg = err.Error()
|
||||||
|
goto resp
|
||||||
|
}
|
||||||
|
|
||||||
|
if overlay == "hat" {
|
||||||
|
prefix = "cowboy"
|
||||||
|
}
|
||||||
|
name = emojy.SanitizeName(prefix + "_" + name)
|
||||||
|
err = emojyPlugin.UploadEmojyImage(p.b.DefaultConnector(), name, newEmojy)
|
||||||
|
if err != nil {
|
||||||
|
msg = err.Error()
|
||||||
|
goto resp
|
||||||
|
}
|
||||||
|
|
||||||
|
p.c.Set("cowboy.lastEmojy", name)
|
||||||
|
|
||||||
|
msg = fmt.Sprintf("You replaced %s with a new emojy %s, pardner!", lastEmojy, name)
|
||||||
|
|
||||||
|
resp:
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: msg,
|
||||||
|
Flags: uint64(discordgo.MessageFlagsEphemeral),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,12 +9,18 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
"github.com/velour/catbase/config"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/velour/catbase/plugins/emojy"
|
"github.com/velour/catbase/plugins/emojy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cowboyCache = map[string]image.Image{}
|
||||||
|
cowboyMutex = sync.Mutex{}
|
||||||
|
)
|
||||||
|
|
||||||
func getEmojy(emojyPath, baseEmojyURL, name string) (image.Image, error) {
|
func getEmojy(emojyPath, baseEmojyURL, name string) (image.Image, error) {
|
||||||
files, _, err := emojy.AllFiles(emojyPath, baseEmojyURL)
|
files, _, err := emojy.AllFiles(emojyPath, baseEmojyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,10 +41,17 @@ func getEmojy(emojyPath, baseEmojyURL, name string) (image.Image, error) {
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCowboyHat(c *config.Config, emojyPath string) (image.Image, error) {
|
func getCowboyHat(emojyPath, overlay string) (image.Image, error) {
|
||||||
p := path.Join(emojyPath, c.Get("cowboy.hatname", "hat.png"))
|
emojies, _, err := emojy.AllFiles(emojyPath, "")
|
||||||
p = path.Clean(p)
|
if err != nil {
|
||||||
f, err := os.Open(p)
|
return nil, err
|
||||||
|
}
|
||||||
|
overlay, ok := emojies[overlay]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("could not find overlay %s", overlay)
|
||||||
|
}
|
||||||
|
overlay = path.Clean(overlay)
|
||||||
|
f, err := os.Open(overlay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -49,8 +62,8 @@ func getCowboyHat(c *config.Config, emojyPath string) (image.Image, error) {
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cowboyifyImage(c *config.Config, emojyPath string, input image.Image) (image.Image, error) {
|
func cowboyifyImage(emojyPath, overlay string, input image.Image) (image.Image, error) {
|
||||||
hat, err := getCowboyHat(c, emojyPath)
|
hat, err := getCowboyHat(emojyPath, overlay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -65,12 +78,27 @@ func cowboyifyImage(c *config.Config, emojyPath string, input image.Image) (imag
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cowboy(c *config.Config, emojyPath, baseEmojyURL, name string) ([]byte, error) {
|
func cowboy(emojyPath, baseEmojyURL, overlay, name string) (image.Image, error) {
|
||||||
emojy, err := getEmojy(emojyPath, baseEmojyURL, name)
|
cowboyMutex.Lock()
|
||||||
|
defer cowboyMutex.Unlock()
|
||||||
|
if img, ok := cowboyCache[name]; ok {
|
||||||
|
log.Debug().Msgf(":cowboy_using_cached_image: %s", name)
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
log.Debug().Msgf(":cowboy_generating_image: %s with overlay %s", name, overlay)
|
||||||
|
emjy, err := getEmojy(emojyPath, baseEmojyURL, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
img, err := cowboyifyImage(c, emojyPath, emojy)
|
img, err := cowboyifyImage(emojyPath, overlay, emjy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cowboyCache[name] = img
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(img image.Image, err error) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -81,3 +109,9 @@ func cowboy(c *config.Config, emojyPath, baseEmojyURL, name string) ([]byte, err
|
||||||
}
|
}
|
||||||
return w.Bytes(), nil
|
return w.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cowboyClearCache() {
|
||||||
|
cowboyMutex.Lock()
|
||||||
|
defer cowboyMutex.Unlock()
|
||||||
|
cowboyCache = map[string]image.Image{}
|
||||||
|
}
|
||||||
|
|
|
@ -9,15 +9,22 @@ import (
|
||||||
|
|
||||||
func (p *Cowboy) registerWeb() {
|
func (p *Cowboy) registerWeb() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.HandleFunc("/img/{what}", p.handleImage)
|
r.HandleFunc("/img/{overlay}/{what}", p.handleImage)
|
||||||
p.b.RegisterWeb(r, "/cowboy")
|
p.b.RegisterWeb(r, "/cowboy")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Cowboy) handleImage(w http.ResponseWriter, r *http.Request) {
|
func (p *Cowboy) handleImage(w http.ResponseWriter, r *http.Request) {
|
||||||
what := chi.URLParam(r, "what")
|
what := chi.URLParam(r, "what")
|
||||||
img, err := cowboy(p.c, p.emojyPath, p.baseEmojyURL, what)
|
overlay := chi.URLParam(r, "overlay")
|
||||||
|
overlays := p.c.GetMap("cowboy.overlays", defaultOverlays)
|
||||||
|
overlayPath, ok := overlays[overlay]
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
img, err := encode(cowboy(p.emojyPath, p.baseEmojyURL, overlayPath, what))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
fmt.Fprintf(w, "Error: %s", err)
|
fmt.Fprintf(w, "Error: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -34,7 +36,14 @@ type EmojyPlugin struct {
|
||||||
const maxLen = 32
|
const maxLen = 32
|
||||||
|
|
||||||
func New(b bot.Bot) *EmojyPlugin {
|
func New(b bot.Bot) *EmojyPlugin {
|
||||||
log.Debug().Msgf("emojy.New")
|
p := NewAPI(b)
|
||||||
|
p.register()
|
||||||
|
p.registerWeb()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPI creates a version used only for API purposes (no callbacks registered)
|
||||||
|
func NewAPI(b bot.Bot) *EmojyPlugin {
|
||||||
emojyPath := b.Config().Get("emojy.path", "emojy")
|
emojyPath := b.Config().Get("emojy.path", "emojy")
|
||||||
baseURL := b.Config().Get("emojy.baseURL", "/emojy/file")
|
baseURL := b.Config().Get("emojy.baseURL", "/emojy/file")
|
||||||
p := &EmojyPlugin{
|
p := &EmojyPlugin{
|
||||||
|
@ -45,8 +54,6 @@ func New(b bot.Bot) *EmojyPlugin {
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
}
|
}
|
||||||
p.setupDB()
|
p.setupDB()
|
||||||
p.register()
|
|
||||||
p.registerWeb()
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,10 +93,10 @@ func (p *EmojyPlugin) register() {
|
||||||
Kind: bot.Message, IsCmd: true,
|
Kind: bot.Message, IsCmd: true,
|
||||||
Regex: regexp.MustCompile(`(?i)^swapemojy (?P<old>.+) (?P<new>.+)$`),
|
Regex: regexp.MustCompile(`(?i)^swapemojy (?P<old>.+) (?P<new>.+)$`),
|
||||||
Handler: func(r bot.Request) bool {
|
Handler: func(r bot.Request) bool {
|
||||||
old := sanitizeName(r.Values["old"])
|
old := SanitizeName(r.Values["old"])
|
||||||
new := sanitizeName(r.Values["new"])
|
new := SanitizeName(r.Values["new"])
|
||||||
p.rmEmojy(r, old)
|
p.rmEmojyHandler(r, old)
|
||||||
p.addEmojy(r, new)
|
p.addEmojyHandler(r, new)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -97,46 +104,57 @@ func (p *EmojyPlugin) register() {
|
||||||
Kind: bot.Message, IsCmd: true,
|
Kind: bot.Message, IsCmd: true,
|
||||||
Regex: regexp.MustCompile(`(?i)^addemojy (?P<name>.+)$`),
|
Regex: regexp.MustCompile(`(?i)^addemojy (?P<name>.+)$`),
|
||||||
Handler: func(r bot.Request) bool {
|
Handler: func(r bot.Request) bool {
|
||||||
name := sanitizeName(r.Values["name"])
|
name := SanitizeName(r.Values["name"])
|
||||||
return p.addEmojy(r, name)
|
return p.addEmojyHandler(r, name)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Kind: bot.Message, IsCmd: true,
|
Kind: bot.Message, IsCmd: true,
|
||||||
Regex: regexp.MustCompile(`(?i)^rmemojy (?P<name>.+)$`),
|
Regex: regexp.MustCompile(`(?i)^rmemojy (?P<name>.+)$`),
|
||||||
Handler: func(r bot.Request) bool {
|
Handler: func(r bot.Request) bool {
|
||||||
name := sanitizeName(r.Values["name"])
|
name := SanitizeName(r.Values["name"])
|
||||||
return p.rmEmojy(r, name)
|
return p.rmEmojyHandler(r, name)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p.b.RegisterTable(p, ht)
|
p.b.RegisterTable(p, ht)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) rmEmojy(r bot.Request, name string) bool {
|
func (p *EmojyPlugin) RmEmojy(c bot.Connector, name string) error {
|
||||||
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
||||||
|
// Call a non-existent emojy a successful remove
|
||||||
if _, ok := onServerList[name]; !ok {
|
if _, ok := onServerList[name]; !ok {
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "Emoji does not exist")
|
return fmt.Errorf("could not find emojy %s", name)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := r.Conn.DeleteEmojy(name); err != nil {
|
if err := c.DeleteEmojy(name); err != nil {
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "error "+err.Error())
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) rmEmojyHandler(r bot.Request, name string) bool {
|
||||||
|
err := p.RmEmojy(r.Conn, name)
|
||||||
|
if err != nil {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "Emoji does not exist")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "removed emojy "+name)
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "removed emojy "+name)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) addEmojy(r bot.Request, name string) bool {
|
func (p *EmojyPlugin) AddEmojy(c bot.Connector, name string) error {
|
||||||
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
||||||
if _, ok := onServerList[name]; ok {
|
if _, ok := onServerList[name]; ok {
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "Emoji already exists")
|
return fmt.Errorf("emojy already exists")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := p.uploadEmojy(r.Conn, name); err != nil {
|
if err := p.UploadEmojyInCache(c, name); err != nil {
|
||||||
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("error adding emojy: %v", err))
|
return err
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) addEmojyHandler(r bot.Request, name string) bool {
|
||||||
|
p.AddEmojy(r.Conn, name)
|
||||||
list := r.Conn.GetEmojiList(true)
|
list := r.Conn.GetEmojiList(true)
|
||||||
for k, v := range list {
|
for k, v := range list {
|
||||||
if v == name {
|
if v == name {
|
||||||
|
@ -233,23 +251,44 @@ func isEmoji(in string) bool {
|
||||||
return gomoji.ContainsEmoji(in)
|
return gomoji.ContainsEmoji(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) uploadEmojy(c bot.Connector, name string) error {
|
func (p *EmojyPlugin) UploadEmojyInCache(c bot.Connector, name string) error {
|
||||||
maxEmojySz := p.c.GetFloat64("emoji.maxsize", 128.0)
|
|
||||||
ok, fname, _, err := p.isKnownEmojy(name)
|
ok, fname, _, err := p.isKnownEmojy(name)
|
||||||
if !ok || err != nil {
|
if !ok || err != nil {
|
||||||
u := p.c.Get("baseurl", "")
|
u := p.c.Get("baseurl", "")
|
||||||
u = u + "/emojy"
|
u = u + "/emojy"
|
||||||
return fmt.Errorf("error getting emojy, the known emojy list can be found at: %s", u)
|
return fmt.Errorf("error getting emojy, the known emojy list can be found at: %s", u)
|
||||||
}
|
}
|
||||||
i, err := gg.LoadImage(fname)
|
f, err := os.Open(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx := gg.NewContextForImage(i)
|
img, _, err := image.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.UploadEmojyImage(c, name, img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageToRGBA(src image.Image) *image.RGBA {
|
||||||
|
// No conversion needed if image is an *image.RGBA.
|
||||||
|
if dst, ok := src.(*image.RGBA); ok {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the image/draw package to convert to *image.RGBA.
|
||||||
|
b := src.Bounds()
|
||||||
|
dst := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
|
||||||
|
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) UploadEmojyImage(c bot.Connector, name string, data image.Image) error {
|
||||||
|
maxEmojySz := p.c.GetFloat64("emoji.maxsize", 128.0)
|
||||||
|
ctx := gg.NewContextForRGBA(imageToRGBA(data))
|
||||||
max := math.Max(float64(ctx.Width()), float64(ctx.Height()))
|
max := math.Max(float64(ctx.Width()), float64(ctx.Height()))
|
||||||
ctx.Scale(maxEmojySz/max, maxEmojySz/max)
|
ctx.Scale(maxEmojySz/max, maxEmojySz/max)
|
||||||
w := bytes.NewBuffer([]byte{})
|
w := bytes.NewBuffer([]byte{})
|
||||||
err = ctx.EncodePNG(w)
|
err := ctx.EncodePNG(w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -261,7 +300,7 @@ func (p *EmojyPlugin) uploadEmojy(c bot.Connector, name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitizeName(name string) string {
|
func SanitizeName(name string) string {
|
||||||
name = strings.ReplaceAll(name, "-", "_")
|
name = strings.ReplaceAll(name, "-", "_")
|
||||||
nameLen := len(name)
|
nameLen := len(name)
|
||||||
if nameLen > maxLen {
|
if nameLen > maxLen {
|
||||||
|
|
Loading…
Reference in New Issue