counter: add api, bot: change routes

* many routes changed by adding the chi router
* counter has an authenticated API to increment and decrement
This commit is contained in:
Chris Sexton 2021-07-28 11:32:59 -04:00 committed by Chris Sexton
parent c47a4f7c6f
commit 7ba9d94ac2
21 changed files with 124 additions and 31 deletions

View File

@ -129,8 +129,11 @@ func (b *bot) ListenAndServe() {
log.Fatal().Err(http.ListenAndServe(addr, b.router)).Msg("bot killed")
}
func (b *bot) RegisterWeb(r http.Handler, root, name string) {
log.Debug().Msgf("registering %s at %s", name, root)
func (b *bot) RegisterWeb(r http.Handler, root string) {
b.router.Mount(root, r)
}
func (b *bot) RegisterWebName(r http.Handler, root, name string) {
b.httpEndPoints = append(b.httpEndPoints, EndPoint{name, root})
b.router.Mount(root, r)
}
@ -391,6 +394,9 @@ func PluginName(p Plugin) string {
}
func (b *bot) CheckPassword(secret, password string) bool {
if password == "" {
return false
}
if b.password == password {
return true
}

View File

@ -135,7 +135,10 @@ type Bot interface {
RegisterFilter(string, func(string) string)
// RegisterWeb records a web endpoint for the UI
RegisterWeb(http.Handler, string, string)
RegisterWebName(http.Handler, string, string)
// RegisterWeb records a web endpoint for the API
RegisterWeb(http.Handler, string)
// Start the HTTP service
ListenAndServe()
@ -202,6 +205,9 @@ type Connector interface {
// GetChannelName returns the channel ID for a human-friendly name (if possible)
GetChannelID(id string) string
// Get any web handlers the connector exposes
GetRouter() (http.Handler, string)
}
// Plugin interface used for compatibility with the Plugin interface

View File

@ -57,7 +57,8 @@ func (mb *MockBot) Register(p Plugin, kind Kind, cb Callback)
func (mb *MockBot) RegisterTable(p Plugin, hs HandlerTable) {}
func (mb *MockBot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {}
func (mb *MockBot) RegisterRegexCmd(p Plugin, kind Kind, r *regexp.Regexp, h ResponseHandler) {}
func (mb *MockBot) RegisterWeb(_, _ string) {}
func (mb *MockBot) RegisterWebName(_ http.Handler, _, _ string) {}
func (mb *MockBot) RegisterWeb(_ http.Handler, _ string) {}
func (mb *MockBot) GetWebNavigation() []EndPoint { return nil }
func (mb *MockBot) Receive(c Connector, kind Kind, msg msg.Message, args ...interface{}) bool {
return false
@ -124,3 +125,4 @@ func (mb *MockBot) GetWhitelist() []string { return []string
func (mb *MockBot) OnBlacklist(ch, p string) bool { return false }
func (mb *MockBot) URLFormat(title, url string) string { return title + url }
func (mb *MockBot) CheckPassword(secret, password string) bool { return true }
func (mb *MockBot) ListenAndServe() {}

View File

@ -3,6 +3,7 @@ package discord
import (
"errors"
"fmt"
"net/http"
"strings"
"github.com/velour/catbase/bot/msg"
@ -34,6 +35,9 @@ func New(config *config.Config) *Discord {
}
return d
}
func (d *Discord) GetRouter() (http.Handler, string) {
return nil, ""
}
func (d *Discord) RegisterEvent(callback bot.Callback) {
d.event = callback

View File

@ -5,6 +5,7 @@ package irc
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
@ -52,6 +53,10 @@ func New(c *config.Config) *Irc {
return &i
}
func (i *Irc) GetRouter() (http.Handler, string) {
return nil, ""
}
func (i *Irc) RegisterEvent(f bot.Callback) {
i.event = f
}

View File

@ -174,6 +174,10 @@ func New(c *config.Config) *Slack {
}
}
func (s *Slack) GetRouter() (http.Handler, string) {
return nil, ""
}
func (s *Slack) Send(kind bot.Kind, args ...interface{}) (string, error) {
switch kind {
case bot.Message:

View File

@ -17,6 +17,7 @@ import (
"text/template"
"time"
"github.com/go-chi/chi/v5"
zerowidth "github.com/trubitsyn/go-zero-width"
"github.com/rs/zerolog/log"
@ -44,6 +45,7 @@ const defaultLogFormat = "[{{fixDate .Time \"2006-01-02 15:04:05\"}}] {{if .Topi
type SlackApp struct {
config *config.Config
api *slack.Client
router *chi.Mux
botToken string
userToken string
@ -84,6 +86,7 @@ func New(c *config.Config) *SlackApp {
return &SlackApp{
api: api,
router: chi.NewRouter(),
config: c,
botToken: token,
userToken: c.Get("slack.usertoken", "NONE"),
@ -103,10 +106,14 @@ func (s *SlackApp) RegisterEvent(f bot.Callback) {
s.event = f
}
func (s *SlackApp) GetRouter() (http.Handler, string) {
return s.router, "/evt"
}
func (s *SlackApp) Serve() error {
s.populateEmojiList()
http.HandleFunc("/evt", func(w http.ResponseWriter, r *http.Request) {
s.router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
body := buf.String()

View File

@ -120,6 +120,10 @@ func main() {
b := bot.New(c, client)
if r, path := client.GetRouter(); r != nil {
b.RegisterWeb(r, path)
}
b.AddPlugin(admin.New(b))
b.AddPlugin(secrets.New(b))
b.AddPlugin(giphy.New(b))

View File

@ -18,12 +18,12 @@ func (p *AdminPlugin) registerWeb() {
r := chi.NewRouter()
r.HandleFunc("/api", p.handleVarsAPI)
r.HandleFunc("/", p.handleVars)
p.bot.RegisterWeb(r, "/vars", "Variables")
p.bot.RegisterWebName(r, "/vars", "Variables")
r = chi.NewRouter()
r.HandleFunc("/verify", p.handleAppPassCheck)
r.HandleFunc("/api", p.handleAppPassAPI)
r.HandleFunc("/", p.handleAppPass)
p.bot.RegisterWeb(r, "/apppass", "App Pass")
p.bot.RegisterWebName(r, "/apppass", "App Pass")
}
func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) {

View File

@ -19,6 +19,7 @@ import (
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"github.com/nfnt/resize"
@ -589,7 +590,9 @@ func (p *BeersPlugin) untappdLoop(c bot.Connector, channel string) {
}
func (p *BeersPlugin) registerWeb() {
http.HandleFunc("/beers/img/", p.img)
r := chi.NewRouter()
r.HandleFunc("/img", p.img)
p.b.RegisterWeb(r, "/beers")
}
func (p *BeersPlugin) img(w http.ResponseWriter, r *http.Request) {

View File

@ -36,7 +36,11 @@ func (p *CliPlugin) registerWeb() {
r := chi.NewRouter()
r.HandleFunc("/api", p.handleWebAPI)
r.HandleFunc("/", p.handleWeb)
p.bot.RegisterWeb(r, "/cli", "CLI")
p.bot.RegisterWebName(r, "/cli", "CLI")
}
func (p *CliPlugin) GetRouter() (http.Handler, string) {
return nil, ""
}
func (p *CliPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {

View File

@ -10,7 +10,6 @@ import (
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/bot/user"
)
func (p *CounterPlugin) registerWeb() {
@ -19,19 +18,53 @@ func (p *CounterPlugin) registerWeb() {
r.HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementAPI(-1))
r.HandleFunc("/api", p.handleCounterAPI)
r.HandleFunc("/", p.handleCounter)
p.b.RegisterWeb(r, "/counter", "Counter")
p.b.RegisterWebName(r, "/counter", "Counter")
}
func (p *CounterPlugin) mkIncrementAPI(delta int) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
vars := map[string]string{}
userName := vars["user"]
itemName := vars["item"]
item, err := GetUserItem(p.db, userName, "", itemName)
if err != nil {
userName := chi.URLParam(r, "user")
itemName := chi.URLParam(r, "item")
secret, pass, ok := r.BasicAuth()
if !ok || !p.b.CheckPassword(secret, pass) {
err := fmt.Errorf("unauthorized access")
log.Error().
Err(err).
Msg("error authenticating user")
w.WriteHeader(401)
j, _ := json.Marshal(struct {
Status bool
Error string
}{false, err.Error()})
fmt.Fprint(w, string(j))
return
}
// Try to find an ID if possible
u, err := p.b.DefaultConnector().Profile(userName)
if err != nil {
log.Error().Err(err).Msg("error finding user")
w.WriteHeader(400)
j, _ := json.Marshal(struct {
Status bool
Error error
}{false, err})
fmt.Fprint(w, string(j))
return
}
item, err := GetUserItem(p.db, userName, u.ID, itemName)
if err != nil {
log.Error().Err(err).Msg("error finding item")
w.WriteHeader(400)
j, _ := json.Marshal(struct {
Status bool
Error error
}{false, err})
fmt.Fprint(w, string(j))
return
}
u := user.New(userName)
req := &bot.Request{
Conn: p.b.DefaultConnector(),
Kind: bot.Message,
@ -45,8 +78,8 @@ func (p *CounterPlugin) mkIncrementAPI(delta int) func(w http.ResponseWriter, r
Args: nil,
}
item.UpdateDelta(req, delta)
msg := fmt.Sprintf("%s changed their %s counter by %d via the amazing %s API",
userName, itemName, delta, p.cfg.Get("nick", "catbase"))
msg := fmt.Sprintf("%s changed their %s counter by %d for a total of %d via the amazing %s API",
userName, itemName, delta, item.Count, p.cfg.Get("nick", "catbase"))
for _, ch := range p.cfg.GetArray("channels", []string{}) {
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
}

View File

@ -323,6 +323,7 @@ func New(b bot.Bot) *CounterPlugin {
cp := &CounterPlugin{
b: b,
db: b.DB(),
cfg: b.Config(),
}
b.RegisterRegex(cp, bot.Startup, regexp.MustCompile(`.*`), cp.migrate)

View File

@ -806,7 +806,7 @@ func (p *FactoidPlugin) registerWeb() {
r.HandleFunc("/api", p.serveAPI)
r.HandleFunc("/req", p.serveQuery)
r.HandleFunc("/", p.serveQuery)
p.Bot.RegisterWeb(r, "/factoid", "Factoid")
p.Bot.RegisterWebName(r, "/factoid", "Factoid")
}
func linkify(text string) template.HTML {

View File

@ -2,9 +2,9 @@ package git
import (
"fmt"
"net/http"
"regexp"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log"
"gopkg.in/go-playground/webhooks.v5/github"
"gopkg.in/go-playground/webhooks.v5/gitlab"
@ -77,7 +77,9 @@ func (p *GitPlugin) register() {
}
func (p *GitPlugin) registerWeb() {
http.HandleFunc("/git/gitea/event", p.giteaEvent)
http.HandleFunc("/git/github/event", p.githubEvent)
http.HandleFunc("/git/gitlab/event", p.gitlabEvent)
r := chi.NewRouter()
r.HandleFunc("/gitea/event", p.giteaEvent)
r.HandleFunc("/github/event", p.githubEvent)
r.HandleFunc("/gitlab/event", p.gitlabEvent)
p.b.RegisterWeb(r, "/git")
}

View File

@ -361,6 +361,9 @@ func (p *MemePlugin) checkMeme(imgURL string) (int, int, error) {
}
img, err := DownloadTemplate(u)
if err != nil {
return 0, 0, err
}
return img.Bounds().Dx(), img.Bounds().Dy(), err
}

View File

@ -21,7 +21,7 @@ func (p *MemePlugin) registerWeb(c bot.Connector) {
r.HandleFunc("/add", p.addMeme)
r.HandleFunc("/rm", p.rmMeme)
r.HandleFunc("/", p.webRoot)
p.bot.RegisterWeb(r, "/meme", "Memes")
p.bot.RegisterWebName(r, "/meme", "Memes")
}
type webResp struct {

View File

@ -39,7 +39,7 @@ func (p *SecretsPlugin) registerWeb() {
w.Write(j)
})
r.HandleFunc("/", p.handleIndex)
p.b.RegisterWeb(r, "/secrets", "Secrets")
p.b.RegisterWebName(r, "/secrets", "Secrets")
}
func (p *SecretsPlugin) registerSecret(key, value string) error {

View File

@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"github.com/go-chi/chi/v5"
twilio "github.com/kevinburke/twilio-go"
"github.com/rs/zerolog/log"
@ -118,7 +119,9 @@ func (p *SMSPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, ar
}
func (p *SMSPlugin) registerWeb() {
http.HandleFunc("/sms/new", p.receive)
r := chi.NewRouter()
r.HandleFunc("/new", p.receive)
p.b.RegisterWeb(r, "/sms")
}
func (p *SMSPlugin) receive(w http.ResponseWriter, r *http.Request) {

View File

@ -10,6 +10,7 @@ import (
"os/exec"
"strings"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
@ -171,7 +172,8 @@ func (p *TalkerPlugin) allCows() []string {
}
func (p *TalkerPlugin) registerWeb(c bot.Connector) {
http.HandleFunc("/slash/cowsay", func(w http.ResponseWriter, r *http.Request) {
r := chi.NewRouter()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
log.Debug().Msgf("Cowsay:\n%+v", r.PostForm.Get("text"))
channel := r.PostForm.Get("channel_id")
@ -184,4 +186,5 @@ func (p *TalkerPlugin) registerWeb(c bot.Connector) {
p.bot.Send(c, bot.Message, channel, msg)
w.WriteHeader(200)
})
p.bot.RegisterWeb(r, "/cowsay")
}

View File

@ -11,6 +11,7 @@ import (
"text/template"
"time"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
@ -85,7 +86,9 @@ func New(b bot.Bot) *TwitchPlugin {
}
func (p *TwitchPlugin) registerWeb() {
http.HandleFunc("/isstreaming/", p.serveStreaming)
r := chi.NewRouter()
r.HandleFunc("/", p.serveStreaming)
p.bot.RegisterWeb(r, "/isstreaming")
}
func (p *TwitchPlugin) serveStreaming(w http.ResponseWriter, r *http.Request) {