2021-07-21 18:52:45 +00:00
|
|
|
package counter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2024-03-13 13:36:40 +00:00
|
|
|
"github.com/ggicci/httpin"
|
2021-07-21 18:52:45 +00:00
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi/v5"
|
2022-07-07 15:22:07 +00:00
|
|
|
"github.com/go-chi/httprate"
|
2021-07-21 18:52:45 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/velour/catbase/bot"
|
|
|
|
"github.com/velour/catbase/bot/msg"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (p *CounterPlugin) registerWeb() {
|
|
|
|
r := chi.NewRouter()
|
2022-07-07 15:22:07 +00:00
|
|
|
requests := p.cfg.GetInt("counter.requestsPer", 1)
|
|
|
|
seconds := p.cfg.GetInt("counter.seconds", 1)
|
2022-07-17 17:47:49 +00:00
|
|
|
dur := time.Duration(seconds) * time.Second
|
|
|
|
subrouter := chi.NewRouter()
|
|
|
|
subrouter.Use(httprate.LimitByIP(requests, dur))
|
2024-03-13 13:36:40 +00:00
|
|
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/api/users/{user}/items/{item}/increment/{delta}", p.mkIncrementByNAPI(1))
|
|
|
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/api/users/{user}/items/{item}/decrement/{delta}", p.mkIncrementByNAPI(-1))
|
|
|
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/api/users/{user}/items/{item}/increment", p.mkIncrementByNAPI(1))
|
|
|
|
subrouter.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementByNAPI(-1))
|
2022-07-17 17:47:49 +00:00
|
|
|
r.Mount("/", subrouter)
|
2024-03-13 13:36:40 +00:00
|
|
|
r.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/users/{user}/items/{item}/increment", p.incHandler(1))
|
|
|
|
r.With(httpin.NewInput(CounterChangeReq{})).
|
|
|
|
HandleFunc("/users/{user}/items/{item}/decrement", p.incHandler(-1))
|
2021-07-21 18:52:45 +00:00
|
|
|
r.HandleFunc("/", p.handleCounter)
|
2024-02-27 19:29:54 +00:00
|
|
|
p.b.GetWeb().RegisterWebName(r, "/counter", "Counter")
|
2021-07-21 18:52:45 +00:00
|
|
|
}
|
|
|
|
|
2024-03-13 13:36:40 +00:00
|
|
|
type CounterChangeReq struct {
|
|
|
|
UserName string `in:"path=user"`
|
|
|
|
Item string `in:"path=item"`
|
|
|
|
Password string `in:"form=password"`
|
|
|
|
Delta int `in:"path=delta"`
|
|
|
|
Body struct {
|
|
|
|
Message string `json:"message"`
|
|
|
|
} `in:"body=json"`
|
|
|
|
}
|
|
|
|
|
2024-02-27 20:36:50 +00:00
|
|
|
func (p *CounterPlugin) incHandler(delta int) http.HandlerFunc {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2024-03-13 13:36:40 +00:00
|
|
|
input := r.Context().Value(httpin.Input).(*CounterChangeReq)
|
|
|
|
if !p.b.CheckPassword("", input.Password) {
|
2024-02-27 20:36:50 +00:00
|
|
|
w.WriteHeader(401)
|
|
|
|
fmt.Fprintf(w, "error")
|
|
|
|
return
|
|
|
|
}
|
2024-03-13 13:36:40 +00:00
|
|
|
item, err := p.delta(input.UserName, input.Item, "", delta)
|
2024-02-27 20:36:50 +00:00
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
fmt.Fprintf(w, "error")
|
|
|
|
return
|
|
|
|
}
|
2024-03-13 13:36:40 +00:00
|
|
|
p.renderItem(input.UserName, item).Render(r.Context(), w)
|
2024-02-27 20:36:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *CounterPlugin) delta(userName, itemName, personalMessage string, delta int) (Item, error) {
|
|
|
|
// Try to find an ID if possible
|
|
|
|
id := ""
|
|
|
|
u, err := p.b.DefaultConnector().Profile(userName)
|
|
|
|
if err == nil {
|
|
|
|
id = u.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
item, err := GetUserItem(p.db, userName, id, itemName)
|
|
|
|
if err != nil {
|
|
|
|
return item, err
|
|
|
|
}
|
|
|
|
|
|
|
|
chs := p.cfg.GetMap("counter.channelItems", map[string]string{})
|
|
|
|
ch, ok := chs[itemName]
|
|
|
|
req := &bot.Request{
|
|
|
|
Conn: p.b.DefaultConnector(),
|
|
|
|
Kind: bot.Message,
|
|
|
|
Msg: msg.Message{
|
|
|
|
User: &u,
|
|
|
|
// Noting here that we're only going to do goals in a "default"
|
|
|
|
// channel even if it should send updates to others.
|
|
|
|
Channel: ch,
|
|
|
|
Body: fmt.Sprintf("%s += %d", itemName, delta),
|
|
|
|
Time: time.Now(),
|
|
|
|
},
|
|
|
|
Values: nil,
|
|
|
|
Args: nil,
|
|
|
|
}
|
|
|
|
msg := fmt.Sprintf("%s changed their %s counter by %d for a total of %d via the amazing %s API. %s",
|
|
|
|
userName, itemName, delta, item.Count+delta, p.cfg.Get("nick", "catbase"), personalMessage)
|
|
|
|
if !ok {
|
|
|
|
chs := p.cfg.GetArray("counter.channels", []string{})
|
|
|
|
for _, ch := range chs {
|
|
|
|
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
|
|
|
|
req.Msg.Channel = ch
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
|
|
|
|
req.Msg.Channel = ch
|
|
|
|
}
|
|
|
|
if err := item.UpdateDelta(req, delta); err != nil {
|
|
|
|
return item, err
|
|
|
|
}
|
|
|
|
return item, nil
|
|
|
|
}
|
|
|
|
|
2022-05-05 22:10:59 +00:00
|
|
|
func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2024-03-13 13:36:40 +00:00
|
|
|
input := r.Context().Value(httpin.Input).(*CounterChangeReq)
|
|
|
|
if input.Delta == 0 {
|
|
|
|
input.Delta = direction
|
2023-10-04 14:22:17 +00:00
|
|
|
} else {
|
2024-03-13 13:36:40 +00:00
|
|
|
input.Delta = input.Delta * direction
|
2023-10-03 14:49:25 +00:00
|
|
|
}
|
2022-05-05 22:10:59 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-02-27 20:36:50 +00:00
|
|
|
personalMsg := ""
|
2024-03-13 13:36:40 +00:00
|
|
|
if input.Body.Message != "" {
|
|
|
|
personalMsg = fmt.Sprintf("\nMessage: %s", input.Body.Message)
|
2022-05-05 22:10:59 +00:00
|
|
|
}
|
|
|
|
|
2024-03-13 13:36:40 +00:00
|
|
|
if _, err := p.delta(input.UserName, input.Item, personalMsg, input.Delta*direction); err != nil {
|
2022-05-05 22:10:59 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
j, _ := json.Marshal(struct{ Status bool }{true})
|
|
|
|
fmt.Fprint(w, string(j))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-21 18:52:45 +00:00
|
|
|
func (p *CounterPlugin) handleCounter(w http.ResponseWriter, r *http.Request) {
|
2024-02-27 20:36:50 +00:00
|
|
|
p.b.GetWeb().Index("Counter", p.index()).Render(r.Context(), w)
|
2021-07-21 18:52:45 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 22:18:17 +00:00
|
|
|
// Update represents a change that gets sent off to other plugins such as goals
|
2021-07-21 18:52:45 +00:00
|
|
|
type Update struct {
|
2021-11-18 22:18:17 +00:00
|
|
|
// Username to be displayed/recorded
|
|
|
|
Who string `json:"who"`
|
|
|
|
// Counter Item
|
|
|
|
What string `json:"what"`
|
|
|
|
// Total counter amount
|
|
|
|
Amount int `json:"amount"`
|
2021-07-21 18:52:45 +00:00
|
|
|
}
|