emojy: create plugin

- added reaction type and event for discord connectors
- added web page to view emojy usage
- added new table for counting emojy
This commit is contained in:
Chris Sexton 2022-05-30 16:34:42 -04:00
parent 104f48b1c3
commit 1c22632a41
7 changed files with 226 additions and 5 deletions

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/velour/catbase/bot/msg" "github.com/velour/catbase/bot/msg"
@ -216,8 +217,11 @@ func (d *Discord) convertUser(u *discordgo.User) *user.User {
func (d *Discord) Serve() error { func (d *Discord) Serve() error {
log.Debug().Msg("starting discord serve function") log.Debug().Msg("starting discord serve function")
d.client.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuilds | d.client.Identify.Intents = discordgo.MakeIntent(
discordgo.IntentsGuildMessages) discordgo.IntentsGuilds |
discordgo.IntentsGuildMessages |
discordgo.IntentsGuildEmojis |
discordgo.IntentsGuildMessageReactions)
err := d.client.Open() err := d.client.Open()
if err != nil { if err != nil {
@ -228,10 +232,32 @@ func (d *Discord) Serve() error {
log.Debug().Msg("discord connection open") log.Debug().Msg("discord connection open")
d.client.AddHandler(d.messageCreate) d.client.AddHandler(d.messageCreate)
d.client.AddHandler(d.messageReactAdd)
return nil return nil
} }
func (d *Discord) messageReactAdd(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
log.Debug().Msg("messageReactAdd")
author, _ := d.Profile(m.UserID)
ch, err := s.Channel(m.ChannelID)
if err != nil {
log.Error().Err(err).Msg("error getting channel info")
}
msg := msg.Message{
ID: m.MessageID,
User: &author,
Channel: m.ChannelID,
ChannelName: ch.Name,
Body: m.Emoji.Name,
Time: time.Now(),
AdditionalData: map[string]string{
"messageFormat": m.Emoji.MessageFormat(),
},
}
d.event(d, bot.Reaction, msg)
}
func (d *Discord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { func (d *Discord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// if we haven't seen this user, we need to resolve their nick before continuing // if we haven't seen this user, we need to resolve their nick before continuing
author, _ := d.Profile(m.Author.ID) author, _ := d.Profile(m.Author.ID)

6
go.mod
View File

@ -27,7 +27,7 @@ require (
github.com/stretchr/testify v1.7.1 github.com/stretchr/testify v1.7.1
github.com/trubitsyn/go-zero-width v1.0.1 github.com/trubitsyn/go-zero-width v1.0.1
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 github.com/velour/velour v0.0.0-20160303155839-8e090e68d158
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
gopkg.in/go-playground/webhooks.v5 v5.17.0 gopkg.in/go-playground/webhooks.v5 v5.17.0
) )
@ -51,7 +51,7 @@ require (
github.com/golang/protobuf v1.3.1 // indirect github.com/golang/protobuf v1.3.1 // indirect
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect
github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/itchyny/timefmt-go v0.1.3 // indirect
github.com/james-bowman/sparse v0.0.0-20190423065201-80c6877364c7 // indirect github.com/james-bowman/sparse v0.0.0-20190423065201-80c6877364c7 // indirect
@ -78,7 +78,7 @@ require (
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 // indirect golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
gonum.org/v1/gonum v0.6.0 // indirect gonum.org/v1/gonum v0.6.0 // indirect
google.golang.org/appengine v1.6.5 // indirect google.golang.org/appengine v1.6.5 // indirect

6
go.sum
View File

@ -80,6 +80,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/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=
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ= github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ=
@ -171,6 +173,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -209,6 +213,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

View File

@ -4,6 +4,7 @@ package main
import ( import (
"flag" "flag"
"github.com/velour/catbase/plugins/emojy"
"io" "io"
"math/rand" "math/rand"
"os" "os"
@ -168,6 +169,7 @@ func main() {
b.AddPlugin(countdown.New(b)) b.AddPlugin(countdown.New(b))
b.AddPlugin(rest.New(b)) b.AddPlugin(rest.New(b))
b.AddPlugin(quotegame.New(b)) b.AddPlugin(quotegame.New(b))
b.AddPlugin(emojy.New(b))
// catches anything left, will always return true // catches anything left, will always return true
b.AddPlugin(fact.New(b)) b.AddPlugin(fact.New(b))

76
plugins/emojy/emojy.go Normal file
View File

@ -0,0 +1,76 @@
package emojy
import (
"github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/config"
"regexp"
)
type EmojyPlugin struct {
b bot.Bot
c *config.Config
db *sqlx.DB
}
func New(b bot.Bot) *EmojyPlugin {
log.Debug().Msgf("emojy.New")
p := &EmojyPlugin{
b: b,
c: b.Config(),
db: b.DB(),
}
p.setupDB()
p.register()
p.registerWeb()
return p
}
func (p *EmojyPlugin) setupDB() {
p.db.MustExec(`create table if not exists emojyCounter (
emojy text primary key,
count integer
)`)
}
func (p *EmojyPlugin) register() {
ht := bot.HandlerTable{
{
Kind: bot.Reaction, IsCmd: false,
Regex: regexp.MustCompile(`.*`),
Handler: func(request bot.Request) bool {
log.Debug().Msgf("Emojy detected: %s", request.Msg.Body)
p.recordReaction(request.Msg.Body)
return false
},
},
}
p.b.RegisterTable(p, ht)
}
func (p *EmojyPlugin) recordReaction(emojy string) error {
q := `insert into emojyCounter (emojy, count) values (?, 1)
on conflict(emojy) do update set count=count+1 where emojy=?;`
_, err := p.db.Exec(q, emojy, emojy)
if err != nil {
log.Error().Err(err).Msgf("recordReaction")
return err
}
return nil
}
type EmojyEntry struct {
Emojy string `db:"emojy"`
Count int `db:"count"`
}
func (p *EmojyPlugin) all() ([]EmojyEntry, error) {
q := `select emojy, count from emojyCounter order by count desc`
result := []EmojyEntry{}
err := p.db.Select(&result, q)
if err != nil {
return nil, err
}
return result, nil
}

75
plugins/emojy/index.html Normal file
View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 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-vue@^2/dist/bootstrap-vue.min.css" />
<!-- Load polyfills to support older browsers -->
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CMutationObserver"></script>
<!-- Load Vue followed by BootstrapVue -->
<script src="//unpkg.com/vue@^2/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@^2/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/vue-router@^2"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<meta charset="UTF-8">
<title>Memes</title>
</head>
<body>
<div id="app">
<b-navbar>
<b-navbar-brand>Emojys</b-navbar-brand>
<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-navbar-nav>
</b-navbar>
<b-alert
dismissable
variant="error"
:show="err != ''"
@dismissed="err = ''">
{{ err }}
</b-alert>
<ul>
<li v-for="emojy in results" key="Emojy">{{emojy.Count}} - {{emojy.Emojy}}</li>
</ul>
</div>
<script>
var router = new VueRouter({
mode: 'history',
routes: []
});
var app = new Vue({
el: '#app',
router,
data: {
err: '',
nav: [],
results: [],
},
mounted() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
this.refresh();
},
methods: {
refresh: function () {
axios.get('/emojy/all')
.then(resp => {
this.results = resp.data
this.err = ''
})
.catch(err => (this.err = err))
}
}
})
</script>
</body>
</html>

36
plugins/emojy/web.go Normal file
View File

@ -0,0 +1,36 @@
package emojy
import (
"embed"
"encoding/json"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log"
"net/http"
)
//go:embed *.html
var embeddedFS embed.FS
func (p *EmojyPlugin) registerWeb() {
r := chi.NewRouter()
r.HandleFunc("/all", p.handleAll)
r.HandleFunc("/", p.handleIndex)
p.b.RegisterWebName(r, "/emojy", "Emojys")
}
func (p *EmojyPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {
index, _ := embeddedFS.ReadFile("index.html")
w.Write(index)
}
func (p *EmojyPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
emojy, err := p.all()
if err != nil {
w.WriteHeader(500)
log.Error().Err(err).Msgf("handleAll")
out, _ := json.Marshal(struct{ err error }{err})
w.Write(out)
}
out, _ := json.Marshal(emojy)
w.Write(out)
}