mirror of https://github.com/velour/catbase.git
Compare commits
2 Commits
2f8bf0edcb
...
56120b168c
Author | SHA1 | Date |
---|---|---|
Chris Sexton | 56120b168c | |
Chris Sexton | feb42b8293 |
|
@ -91,8 +91,8 @@ func (b *bot) Send(conn Connector, kind Kind, args ...any) (string, error) {
|
||||||
return conn.Send(kind, args...)
|
return conn.Send(kind, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bot) GetEmojiList() map[string]string {
|
func (b *bot) GetEmojiList(force bool) map[string]string {
|
||||||
return b.conn.GetEmojiList()
|
return b.conn.GetEmojiList(force)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks to see if the user is asking for help, returns true if so and handles the situation.
|
// Checks to see if the user is asking for help, returns true if so and handles the situation.
|
||||||
|
|
|
@ -129,7 +129,7 @@ type Bot interface {
|
||||||
CheckAdmin(string) bool
|
CheckAdmin(string) bool
|
||||||
|
|
||||||
// GetEmojiList returns known emoji
|
// GetEmojiList returns known emoji
|
||||||
GetEmojiList() map[string]string
|
GetEmojiList(bool) map[string]string
|
||||||
|
|
||||||
// RegisterFilter creates a filter function for message processing
|
// RegisterFilter creates a filter function for message processing
|
||||||
RegisterFilter(string, func(string) string)
|
RegisterFilter(string, func(string) string)
|
||||||
|
@ -186,7 +186,7 @@ type Connector interface {
|
||||||
Send(Kind, ...any) (string, error)
|
Send(Kind, ...any) (string, error)
|
||||||
|
|
||||||
// GetEmojiList returns a connector's known custom emoji
|
// GetEmojiList returns a connector's known custom emoji
|
||||||
GetEmojiList() map[string]string
|
GetEmojiList(bool) map[string]string
|
||||||
|
|
||||||
// Serve starts a connector's connection routine
|
// Serve starts a connector's connection routine
|
||||||
Serve() error
|
Serve() error
|
||||||
|
@ -203,6 +203,12 @@ type Connector interface {
|
||||||
// Emojy translates emojy to/from services
|
// Emojy translates emojy to/from services
|
||||||
Emojy(string) string
|
Emojy(string) string
|
||||||
|
|
||||||
|
// UploadEmojy creates a new emojy on the server related to the file at path
|
||||||
|
UploadEmojy(emojy, path string) error
|
||||||
|
|
||||||
|
// DeleteEmojy removes the specified emojy from the server
|
||||||
|
DeleteEmojy(emojy string) error
|
||||||
|
|
||||||
// GetChannelName returns the human-friendly name for an ID (if possible)
|
// GetChannelName returns the human-friendly name for an ID (if possible)
|
||||||
GetChannelName(id string) string
|
GetChannelName(id string) string
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,9 @@ func (mb *MockBot) edit(c Connector, channel, newMessage, identifier string) (st
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) }
|
func (mb *MockBot) GetEmojiList(bool) map[string]string { return make(map[string]string) }
|
||||||
|
func (mb *MockBot) DeleteEmojy(name string) error { return nil }
|
||||||
|
func (mb *MockBot) UploadEmojy(emojy, path string) error { return nil }
|
||||||
func (mb *MockBot) RegisterFilter(s string, f func(string) string) {}
|
func (mb *MockBot) RegisterFilter(s string, f func(string) string) {}
|
||||||
|
|
||||||
func NewMockBot() *MockBot {
|
func NewMockBot() *MockBot {
|
||||||
|
|
|
@ -134,8 +134,8 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...a
|
||||||
return st.ID, err
|
return st.ID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Discord) GetEmojiList() map[string]string {
|
func (d *Discord) GetEmojiList(force bool) map[string]string {
|
||||||
if d.emojiCache != nil {
|
if d.emojiCache != nil && !force {
|
||||||
return d.emojiCache
|
return d.emojiCache
|
||||||
}
|
}
|
||||||
guildID := d.config.Get("discord.guildid", "")
|
guildID := d.config.Get("discord.guildid", "")
|
||||||
|
@ -155,6 +155,16 @@ func (d *Discord) GetEmojiList() map[string]string {
|
||||||
return emojis
|
return emojis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Discord) GetEmojySnowflake(name string) string {
|
||||||
|
list := d.GetEmojiList(true)
|
||||||
|
for k, v := range list {
|
||||||
|
if name == v {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// Who gets the users in the guild
|
// Who gets the users in the guild
|
||||||
// Note that the channel ID does not matter in this particular case
|
// Note that the channel ID does not matter in this particular case
|
||||||
func (d *Discord) Who(id string) []string {
|
func (d *Discord) Who(id string) []string {
|
||||||
|
@ -298,6 +308,22 @@ func (d *Discord) Emojy(name string) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Discord) UploadEmojy(emojy, path string) error {
|
||||||
|
guildID := d.config.Get("discord.guildid", "")
|
||||||
|
defaultRoles := d.config.GetArray("discord.emojyRoles", []string{})
|
||||||
|
_, err := d.client.GuildEmojiCreate(guildID, emojy, path, defaultRoles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discord) DeleteEmojy(emojy string) error {
|
||||||
|
guildID := d.config.Get("discord.guildid", "")
|
||||||
|
emojyID := d.GetEmojySnowflake(emojy)
|
||||||
|
return d.client.GuildEmojiDelete(guildID, emojyID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Discord) URLFormat(title, url string) string {
|
func (d *Discord) URLFormat(title, url string) string {
|
||||||
return fmt.Sprintf("%s (%s)", title, url)
|
return fmt.Sprintf("%s (%s)", title, url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ func (i *Irc) sendAction(channel, message string, args ...any) (string, error) {
|
||||||
return i.sendMessage(channel, message, args...)
|
return i.sendMessage(channel, message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) GetEmojiList() map[string]string {
|
func (i *Irc) GetEmojiList(force bool) map[string]string {
|
||||||
//we're not going to do anything because it's IRC
|
//we're not going to do anything because it's IRC
|
||||||
return make(map[string]string)
|
return make(map[string]string)
|
||||||
}
|
}
|
||||||
|
@ -334,6 +334,14 @@ func (i Irc) Emojy(name string) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i Irc) UploadEmojy(emojy, path string) error {
|
||||||
|
return fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Irc) DeleteEmojy(emojy string) error {
|
||||||
|
return fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
// GetChannelName returns the channel ID for a human-friendly name (if possible)
|
// GetChannelName returns the channel ID for a human-friendly name (if possible)
|
||||||
func (i Irc) GetChannelID(name string) string {
|
func (i Irc) GetChannelID(name string) string {
|
||||||
return name
|
return name
|
||||||
|
|
|
@ -396,7 +396,10 @@ func (s *SlackApp) edit(channel, newMessage, identifier string) (string, error)
|
||||||
return ts, err
|
return ts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SlackApp) GetEmojiList() map[string]string {
|
func (s *SlackApp) GetEmojiList(force bool) map[string]string {
|
||||||
|
if force {
|
||||||
|
s.populateEmojiList()
|
||||||
|
}
|
||||||
return s.emoji
|
return s.emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -727,6 +730,14 @@ func (s *SlackApp) Emojy(name string) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SlackApp) UploadEmojy(emojy, path string) error {
|
||||||
|
return fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SlackApp) DeleteEmojy(emojy string) error {
|
||||||
|
return fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SlackApp) URLFormat(title, url string) string {
|
func (s *SlackApp) URLFormat(title, url string) string {
|
||||||
return fmt.Sprintf("<%s|%s>", url, title)
|
return fmt.Sprintf("<%s|%s>", url, title)
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -12,6 +12,7 @@ require (
|
||||||
github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035
|
github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90
|
||||||
github.com/forPelevin/gomoji v1.1.4
|
github.com/forPelevin/gomoji v1.1.4
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.0
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
github.com/gocolly/colly v1.2.0
|
github.com/gocolly/colly v1.2.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -48,6 +48,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCro
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/forPelevin/gomoji v1.1.4 h1:mlxsZQgTO7v1qnpUUoS8kk0Lf/rEvxZYgYxuVUX7edg=
|
github.com/forPelevin/gomoji v1.1.4 h1:mlxsZQgTO7v1qnpUUoS8kk0Lf/rEvxZYgYxuVUX7edg=
|
||||||
github.com/forPelevin/gomoji v1.1.4/go.mod h1:ypB7Kz3Fsp+LVR7KoT7mEFOioYBuTuAtaAT4RGl+ASY=
|
github.com/forPelevin/gomoji v1.1.4/go.mod h1:ypB7Kz3Fsp+LVR7KoT7mEFOioYBuTuAtaAT4RGl+ASY=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE=
|
||||||
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ=
|
github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ=
|
||||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||||
|
@ -197,6 +199,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
|
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
|
||||||
|
|
|
@ -122,13 +122,15 @@ func (p *CliPlugin) Send(kind bot.Kind, args ...any) (string, error) {
|
||||||
p.counter++
|
p.counter++
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
func (p *CliPlugin) GetEmojiList() map[string]string { return nil }
|
func (p *CliPlugin) GetEmojiList(bool) map[string]string { return nil }
|
||||||
func (p *CliPlugin) Serve() error { return nil }
|
func (p *CliPlugin) Serve() error { return nil }
|
||||||
func (p *CliPlugin) Who(s string) []string { return nil }
|
func (p *CliPlugin) Who(s string) []string { return nil }
|
||||||
func (p *CliPlugin) Profile(name string) (user.User, error) {
|
func (p *CliPlugin) Profile(name string) (user.User, error) {
|
||||||
return user.User{}, fmt.Errorf("unimplemented")
|
return user.User{}, fmt.Errorf("unimplemented")
|
||||||
}
|
}
|
||||||
func (p *CliPlugin) Emojy(name string) string { return name }
|
func (p *CliPlugin) Emojy(name string) string { return name }
|
||||||
|
func (p *CliPlugin) DeleteEmojy(name string) error { return nil }
|
||||||
|
func (p *CliPlugin) UploadEmojy(emojy, path string) error { return nil }
|
||||||
func (p *CliPlugin) URLFormat(title, url string) string {
|
func (p *CliPlugin) URLFormat(title, url string) string {
|
||||||
return fmt.Sprintf("%s (%s)", title, url)
|
return fmt.Sprintf("%s (%s)", title, url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (p *EmojifyMePlugin) message(r bot.Request) bool {
|
||||||
message := r.Msg
|
message := r.Msg
|
||||||
if !p.GotBotEmoji {
|
if !p.GotBotEmoji {
|
||||||
p.GotBotEmoji = true
|
p.GotBotEmoji = true
|
||||||
emojiMap := p.Bot.GetEmojiList()
|
emojiMap := p.Bot.GetEmojiList(false)
|
||||||
for e := range emojiMap {
|
for e := range emojiMap {
|
||||||
p.Emoji[e] = e
|
p.Emoji[e] = e
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
package emojy
|
package emojy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
"github.com/gabriel-vasile/mimetype"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/forPelevin/gomoji"
|
"github.com/forPelevin/gomoji"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"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"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmojyPlugin struct {
|
type EmojyPlugin struct {
|
||||||
|
@ -19,6 +27,8 @@ type EmojyPlugin struct {
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxLen = 32
|
||||||
|
|
||||||
func New(b bot.Bot) *EmojyPlugin {
|
func New(b bot.Bot) *EmojyPlugin {
|
||||||
log.Debug().Msgf("emojy.New")
|
log.Debug().Msgf("emojy.New")
|
||||||
p := &EmojyPlugin{
|
p := &EmojyPlugin{
|
||||||
|
@ -64,10 +74,71 @@ func (p *EmojyPlugin) register() {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: true,
|
||||||
|
Regex: regexp.MustCompile(`(?i)^swapemojy (?P<old>.+) (?P<new>.+)$`),
|
||||||
|
Handler: func(r bot.Request) bool {
|
||||||
|
old := sanitizeName(r.Values["old"])
|
||||||
|
new := sanitizeName(r.Values["new"])
|
||||||
|
p.rmEmojy(r, old)
|
||||||
|
p.addEmojy(r, new)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: true,
|
||||||
|
Regex: regexp.MustCompile(`(?i)^addemojy (?P<name>.+)$`),
|
||||||
|
Handler: func(r bot.Request) bool {
|
||||||
|
name := sanitizeName(r.Values["name"])
|
||||||
|
return p.addEmojy(r, name)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: true,
|
||||||
|
Regex: regexp.MustCompile(`(?i)^rmemojy (?P<name>.+)$`),
|
||||||
|
Handler: func(r bot.Request) bool {
|
||||||
|
name := sanitizeName(r.Values["name"])
|
||||||
|
return p.rmEmojy(r, name)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
p.b.RegisterTable(p, ht)
|
p.b.RegisterTable(p, ht)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) rmEmojy(r bot.Request, name string) bool {
|
||||||
|
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
||||||
|
if _, ok := onServerList[name]; !ok {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "Emoji does not exist")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err := r.Conn.DeleteEmojy(name); err != nil {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "error "+err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "removed emojy "+name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) addEmojy(r bot.Request, name string) bool {
|
||||||
|
onServerList := invertEmojyList(p.b.GetEmojiList(false))
|
||||||
|
if _, ok := onServerList[name]; ok {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "Emoji already exists")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err := p.uploadEmojy(r.Conn, name); err != nil {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("error adding emojy: %v", err))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
list := r.Conn.GetEmojiList(true)
|
||||||
|
for k, v := range list {
|
||||||
|
if v == name {
|
||||||
|
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("added emojy: %s <:%s:%s>", name, name, k))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) recordReaction(emojy string) error {
|
func (p *EmojyPlugin) recordReaction(emojy string) error {
|
||||||
q := `insert into emojyLog (emojy, observed) values (?, ?)`
|
q := `insert into emojyLog (emojy, observed) values (?, ?)`
|
||||||
_, err := p.db.Exec(q, emojy, time.Now())
|
_, err := p.db.Exec(q, emojy, time.Now())
|
||||||
|
@ -100,7 +171,7 @@ func invertEmojyList(emojy map[string]string) map[string]string {
|
||||||
|
|
||||||
func (p *EmojyPlugin) allCounts() (map[string][]EmojyCount, error) {
|
func (p *EmojyPlugin) allCounts() (map[string][]EmojyCount, error) {
|
||||||
out := map[string][]EmojyCount{}
|
out := map[string][]EmojyCount{}
|
||||||
onServerList := invertEmojyList(p.b.DefaultConnector().GetEmojiList())
|
onServerList := invertEmojyList(p.b.GetEmojiList(true))
|
||||||
q := `select emojy, count(observed) as count from emojyLog group by emojy order by count desc`
|
q := `select emojy, count(observed) as count from emojyLog group by emojy order by count desc`
|
||||||
result := []EmojyCount{}
|
result := []EmojyCount{}
|
||||||
err := p.db.Select(&result, q)
|
err := p.db.Select(&result, q)
|
||||||
|
@ -111,8 +182,8 @@ func (p *EmojyPlugin) allCounts() (map[string][]EmojyCount, error) {
|
||||||
_, e.OnServer = onServerList[e.Emojy]
|
_, e.OnServer = onServerList[e.Emojy]
|
||||||
if isEmoji(e.Emojy) {
|
if isEmoji(e.Emojy) {
|
||||||
out["emoji"] = append(out["emoji"], e)
|
out["emoji"] = append(out["emoji"], e)
|
||||||
} else if ok, fname, _ := p.isKnownEmojy(e.Emojy); ok {
|
} else if ok, _, eURL, _ := p.isKnownEmojy(e.Emojy); ok {
|
||||||
e.URL = fname
|
e.URL = eURL
|
||||||
out["emojy"] = append(out["emojy"], e)
|
out["emojy"] = append(out["emojy"], e)
|
||||||
} else {
|
} else {
|
||||||
out["unknown"] = append(out["unknown"], e)
|
out["unknown"] = append(out["unknown"], e)
|
||||||
|
@ -121,25 +192,74 @@ func (p *EmojyPlugin) allCounts() (map[string][]EmojyCount, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) isKnownEmojy(name string) (bool, string, error) {
|
func (p *EmojyPlugin) allFiles() (map[string]string, map[string]string, error) {
|
||||||
|
files := map[string]string{}
|
||||||
|
urls := map[string]string{}
|
||||||
emojyPath := p.c.Get("emojy.path", "emojy")
|
emojyPath := p.c.Get("emojy.path", "emojy")
|
||||||
baseURL := p.c.Get("emojy.baseURL", "/emojy/file")
|
baseURL := p.c.Get("emojy.baseURL", "/emojy/file")
|
||||||
entries, err := os.ReadDir(emojyPath)
|
entries, err := os.ReadDir(emojyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
if !e.IsDir() &&
|
if !e.IsDir() {
|
||||||
(strings.HasPrefix(e.Name(), name) ||
|
baseName := strings.TrimSuffix(e.Name(), filepath.Ext(e.Name()))
|
||||||
strings.HasPrefix(e.Name(), strings.Replace(name, "-", "_", -1)) ||
|
|
||||||
strings.HasPrefix(e.Name(), strings.Trim(name, "-_"))) {
|
|
||||||
url := path.Join(baseURL, e.Name())
|
url := path.Join(baseURL, e.Name())
|
||||||
return true, url, nil
|
files[baseName] = path.Join(emojyPath, e.Name())
|
||||||
|
urls[baseName] = url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, "", nil
|
return files, urls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) isKnownEmojy(name string) (bool, string, string, error) {
|
||||||
|
allFiles, allURLs, err := p.allFiles()
|
||||||
|
if err != nil {
|
||||||
|
return false, "", "", err
|
||||||
|
}
|
||||||
|
if _, ok := allURLs[name]; !ok {
|
||||||
|
return false, "", "", nil
|
||||||
|
}
|
||||||
|
return true, allFiles[name], allURLs[name], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmoji(in string) bool {
|
func isEmoji(in string) bool {
|
||||||
return gomoji.ContainsEmoji(in)
|
return gomoji.ContainsEmoji(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) uploadEmojy(c bot.Connector, name string) error {
|
||||||
|
maxEmojySz := p.c.GetFloat64("emoji.maxsize", 128.0)
|
||||||
|
ok, fname, _, err := p.isKnownEmojy(name)
|
||||||
|
if !ok || err != nil {
|
||||||
|
u := p.c.Get("baseurl", "")
|
||||||
|
u = u + "/emojy"
|
||||||
|
return fmt.Errorf("error getting emojy, the known emojy list can be found at: %s", u)
|
||||||
|
}
|
||||||
|
i, err := gg.LoadImage(fname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx := gg.NewContextForImage(i)
|
||||||
|
max := math.Max(float64(ctx.Width()), float64(ctx.Height()))
|
||||||
|
ctx.Scale(maxEmojySz/max, maxEmojySz/max)
|
||||||
|
w := bytes.NewBuffer([]byte{})
|
||||||
|
err = ctx.EncodePNG(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mtype := mimetype.Detect(w.Bytes())
|
||||||
|
base64Img := "data:" + mtype.String() + ";base64," + base64.StdEncoding.EncodeToString(w.Bytes())
|
||||||
|
if err := c.UploadEmojy(name, base64Img); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeName(name string) string {
|
||||||
|
name = strings.ReplaceAll(name, "-", "_")
|
||||||
|
nameLen := len(name)
|
||||||
|
if nameLen > maxLen {
|
||||||
|
nameLen = maxLen
|
||||||
|
}
|
||||||
|
return name[:nameLen]
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<!-- Load required Bootstrap and BootstrapVue CSS -->
|
<!-- Load required Bootstrap and BootstrapVue CSS -->
|
||||||
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
|
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
|
||||||
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@^2/dist/bootstrap-vue.min.css" />
|
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@^2/dist/bootstrap-vue.min.css"/>
|
||||||
|
|
||||||
<!-- Load polyfills to support older browsers -->
|
<!-- Load polyfills to support older browsers -->
|
||||||
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CMutationObserver"></script>
|
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CMutationObserver"></script>
|
||||||
|
@ -22,18 +22,39 @@
|
||||||
<b-navbar>
|
<b-navbar>
|
||||||
<b-navbar-brand>Emojys</b-navbar-brand>
|
<b-navbar-brand>Emojys</b-navbar-brand>
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Meme'" :key="item.key">{{ item.name }}</b-nav-item>
|
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Meme'" :key="item.key">{{ item.name
|
||||||
|
}}
|
||||||
|
</b-nav-item>
|
||||||
</b-navbar-nav>
|
</b-navbar-nav>
|
||||||
</b-navbar>
|
</b-navbar>
|
||||||
<b-alert
|
|
||||||
dismissable
|
|
||||||
variant="error"
|
|
||||||
:show="err != ''"
|
|
||||||
@dismissed="err = ''">
|
|
||||||
{{ err }}
|
|
||||||
</b-alert>
|
|
||||||
|
|
||||||
<ul>
|
<b-navbar>
|
||||||
|
<b-navbar-nav>
|
||||||
|
<b-nav-item :active="view == 'stats'" @click="view = 'stats'">Stats</b-nav-item>
|
||||||
|
<b-nav-item :active="view == 'list'" @click="view = 'list'">List</b-nav-item>
|
||||||
|
<b-nav-item :active="view == 'upload'" @click="view = 'upload'">Upload</b-nav-item>
|
||||||
|
</b-navbar-nav>
|
||||||
|
</b-navbar>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style="background-color:red;"
|
||||||
|
variant="error"
|
||||||
|
v-if="err != ''"
|
||||||
|
@click="err = ''">
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container" v-if="view == 'upload'">
|
||||||
|
<label>Passphrase</label>
|
||||||
|
<b-input v-model="password"></b-input>
|
||||||
|
<label>File
|
||||||
|
<input type="file" @change="handleFileUpload( $event )"/>
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<button @click="submitFile()">Submit</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul v-if="view == 'stats'">
|
||||||
<li v-for="(category, name) in results" key="name">
|
<li v-for="(category, name) in results" key="name">
|
||||||
{{name}}:
|
{{name}}:
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -44,12 +65,20 @@
|
||||||
<span v-else>❎</span>
|
<span v-else>❎</span>
|
||||||
-
|
-
|
||||||
</span>
|
</span>
|
||||||
<img v-if="emojy.url" :src="emojy.url" :alt="emojy.name" class="img-thumbnail" style="max-width: 64px; max-height: 64px" />
|
<img v-if="emojy.url" :src="emojy.url" :alt="emojy.name" class="img-thumbnail"
|
||||||
|
style="max-width: 64px; max-height: 64px"/>
|
||||||
<span v-else>{{emojy.emojy}}</span>
|
<span v-else>{{emojy.emojy}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<ul v-if="view == 'list'">
|
||||||
|
<li v-for="(path, name) in fileList" key="name">
|
||||||
|
{{name}}:
|
||||||
|
<img :src="path" :alt="name" class="img-thumbnail" style="max-width: 64px; max-height: 64px"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -60,10 +89,21 @@
|
||||||
var app = new Vue({
|
var app = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
router,
|
router,
|
||||||
data: {
|
data: function () {
|
||||||
|
return {
|
||||||
err: '',
|
err: '',
|
||||||
|
view: '',
|
||||||
nav: [],
|
nav: [],
|
||||||
results: [],
|
results: [],
|
||||||
|
fileList: {},
|
||||||
|
image: null,
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
view(newView, oldView) {
|
||||||
|
this.err = '';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
axios.get('/nav')
|
axios.get('/nav')
|
||||||
|
@ -81,6 +121,35 @@
|
||||||
this.err = ''
|
this.err = ''
|
||||||
})
|
})
|
||||||
.catch(err => (this.err = err))
|
.catch(err => (this.err = err))
|
||||||
|
axios.get('/emojy/allFiles')
|
||||||
|
.then(resp => {
|
||||||
|
this.fileList = resp.data
|
||||||
|
this.err = ''
|
||||||
|
})
|
||||||
|
.catch(err => (this.err = err))
|
||||||
|
},
|
||||||
|
handleFileUpload(evt) {
|
||||||
|
this.file = evt.target.files[0];
|
||||||
|
console.log("added file data: " + this.file);
|
||||||
|
},
|
||||||
|
submitFile() {
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('file', this.file);
|
||||||
|
formData.append('password', this.password)
|
||||||
|
console.log('trying to make/save form data: ' + formData)
|
||||||
|
axios.post('/emojy/upload',
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
headers: {'Content-Type': 'multipart/form-data'}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('SUCCESS!!');
|
||||||
|
this.view = 'list';
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('FAILURE!!' + e);
|
||||||
|
this.err = 'Could not upload file.';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,11 +3,16 @@ package emojy
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
|
@ -16,6 +21,8 @@ var embeddedFS embed.FS
|
||||||
func (p *EmojyPlugin) registerWeb() {
|
func (p *EmojyPlugin) registerWeb() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.HandleFunc("/all", p.handleAll)
|
r.HandleFunc("/all", p.handleAll)
|
||||||
|
r.HandleFunc("/allFiles", p.handleAllFiles)
|
||||||
|
r.HandleFunc("/upload", p.handleUpload)
|
||||||
r.HandleFunc("/file/{name}", p.handleEmojy)
|
r.HandleFunc("/file/{name}", p.handleEmojy)
|
||||||
r.HandleFunc("/", p.handleIndex)
|
r.HandleFunc("/", p.handleIndex)
|
||||||
p.b.RegisterWebName(r, "/emojy", "Emojys")
|
p.b.RegisterWebName(r, "/emojy", "Emojys")
|
||||||
|
@ -38,6 +45,90 @@ func (p *EmojyPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(out)
|
w.Write(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) handleAllFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, urlMap, err := p.allFiles()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
out, _ := json.Marshal(struct{ err error }{err})
|
||||||
|
w.Write(out)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
err = enc.Encode(urlMap)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
out, _ := json.Marshal(struct{ err error }{err})
|
||||||
|
w.Write(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) handleUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
newFilePath, err := p.FileSave(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not upload file")
|
||||||
|
w.WriteHeader(500)
|
||||||
|
enc.Encode(struct{ err error }{fmt.Errorf("file not saved: %s", err)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug().Msgf("uploaded file to %s", newFilePath)
|
||||||
|
w.WriteHeader(200)
|
||||||
|
enc.Encode(struct{ file string }{newFilePath})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EmojyPlugin) FileSave(r *http.Request) (string, error) {
|
||||||
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
||||||
|
if err != http.ErrNotMultipart {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.MultipartForm == nil || len(r.MultipartForm.File) == 0 {
|
||||||
|
return "", fmt.Errorf("no files")
|
||||||
|
}
|
||||||
|
|
||||||
|
password := r.Form.Get("password")
|
||||||
|
if password != p.b.GetPassword() {
|
||||||
|
return "", fmt.Errorf("incorrect password")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fileHeaders := range r.MultipartForm.File {
|
||||||
|
for _, fileHeader := range fileHeaders {
|
||||||
|
body, err := fileHeader.Open()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error opening part %q: %s", fileHeader.Filename, err)
|
||||||
|
}
|
||||||
|
emojyFileName := fileHeader.Filename
|
||||||
|
emojyName := strings.TrimSuffix(emojyFileName, filepath.Ext(emojyFileName))
|
||||||
|
if ok, _, _, _ := p.isKnownEmojy(emojyName); ok {
|
||||||
|
return "", fmt.Errorf("emojy already exists")
|
||||||
|
}
|
||||||
|
emojyPath := p.c.Get("emojy.path", "emojy")
|
||||||
|
contentType := fileHeader.Header.Get("Content-Type")
|
||||||
|
if !strings.HasPrefix(contentType, "image") {
|
||||||
|
return "", fmt.Errorf("incorrect mime type - given: %s", contentType)
|
||||||
|
}
|
||||||
|
fullPath := filepath.Clean(filepath.Join(emojyPath, emojyFileName))
|
||||||
|
_ = os.MkdirAll(emojyPath, os.ModePerm)
|
||||||
|
log.Debug().Msgf("trying to create/open file: %s", fullPath)
|
||||||
|
file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
_, err = io.Copy(file, body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return emojyFileName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("did not find file")
|
||||||
|
}
|
||||||
|
|
||||||
func (p *EmojyPlugin) handleEmojy(w http.ResponseWriter, r *http.Request) {
|
func (p *EmojyPlugin) handleEmojy(w http.ResponseWriter, r *http.Request) {
|
||||||
fname := chi.URLParam(r, "name")
|
fname := chi.URLParam(r, "name")
|
||||||
emojyPath := p.c.Get("emojy.path", "emojy")
|
emojyPath := p.c.Get("emojy.path", "emojy")
|
||||||
|
|
Loading…
Reference in New Issue