mirror of https://github.com/velour/catbase.git
Compare commits
11 Commits
c089a80ffc
...
b20da607bc
Author | SHA1 | Date |
---|---|---|
Chris Sexton | b20da607bc | |
Chris Sexton | a5e919733c | |
Chris Sexton | 2d06fd6be8 | |
Chris Sexton | b4f9f902ce | |
Chris Sexton | f83cc32788 | |
Chris Sexton | 3e3cc3cf95 | |
Chris Sexton | f6b1712eda | |
Chris Sexton | b8e6e0595d | |
Chris Sexton | e668fbe688 | |
Chris Sexton | 12f4e51ba5 | |
Chris Sexton | 00352a1512 |
|
@ -20,7 +20,6 @@ _cgo_export.*
|
|||
_testmain.go
|
||||
|
||||
*.exe
|
||||
catbase
|
||||
config.json
|
||||
*.db
|
||||
vendor
|
||||
|
@ -77,3 +76,4 @@ impact.ttf
|
|||
.env
|
||||
rathaus_discord.sh
|
||||
emojy
|
||||
catbase
|
||||
|
|
60
bot/bot.go
60
bot/bot.go
|
@ -4,10 +4,8 @@ package bot
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/httprate"
|
||||
"github.com/velour/catbase/bot/web"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
|
@ -15,7 +13,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/velour/catbase/bot/history"
|
||||
|
@ -53,8 +50,7 @@ type bot struct {
|
|||
|
||||
version string
|
||||
|
||||
// The entries to the bot's HTTP interface
|
||||
httpEndPoints []EndPoint
|
||||
web *web.Web
|
||||
|
||||
// filters registered by plugins
|
||||
filters map[string]func(string) string
|
||||
|
@ -66,16 +62,9 @@ type bot struct {
|
|||
|
||||
quiet bool
|
||||
|
||||
router *chi.Mux
|
||||
|
||||
history *history.History
|
||||
}
|
||||
|
||||
type EndPoint struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// Variable represents a $var replacement
|
||||
type Variable struct {
|
||||
Variable, Value string
|
||||
|
@ -107,10 +96,8 @@ func New(config *config.Config, connector Connector) Bot {
|
|||
me: users[0],
|
||||
logIn: logIn,
|
||||
logOut: logOut,
|
||||
httpEndPoints: make([]EndPoint, 0),
|
||||
filters: make(map[string]func(string) string),
|
||||
callbacks: make(CallbackMap),
|
||||
router: chi.NewRouter(),
|
||||
history: history.New(historySz),
|
||||
}
|
||||
|
||||
|
@ -119,60 +106,25 @@ func New(config *config.Config, connector Connector) Bot {
|
|||
bot.RefreshPluginBlacklist()
|
||||
bot.RefreshPluginWhitelist()
|
||||
|
||||
log.Debug().Msgf("created web router")
|
||||
|
||||
bot.setupHTTP()
|
||||
bot.web = web.New(bot.config)
|
||||
|
||||
connector.RegisterEvent(bot.Receive)
|
||||
|
||||
return bot
|
||||
}
|
||||
|
||||
func (b *bot) setupHTTP() {
|
||||
// Make the http logger optional
|
||||
// It has never served a purpose in production and with the emojy page, can make a rather noisy log
|
||||
if b.Config().GetInt("bot.useLogger", 0) == 1 {
|
||||
b.router.Use(middleware.Logger)
|
||||
}
|
||||
|
||||
reqCount := b.Config().GetInt("bot.httprate.requests", 500)
|
||||
reqTime := time.Duration(b.Config().GetInt("bot.httprate.seconds", 5))
|
||||
if reqCount > 0 && reqTime > 0 {
|
||||
b.router.Use(httprate.LimitByIP(reqCount, reqTime*time.Second))
|
||||
}
|
||||
|
||||
b.router.Use(middleware.RequestID)
|
||||
b.router.Use(middleware.Recoverer)
|
||||
b.router.Use(middleware.StripSlashes)
|
||||
|
||||
b.router.HandleFunc("/", b.serveRoot)
|
||||
b.router.HandleFunc("/nav", b.serveNav)
|
||||
b.router.HandleFunc("/navHTML", b.serveNavHTML)
|
||||
b.router.HandleFunc("/navHTML/{currentPage}", b.serveNavHTML)
|
||||
}
|
||||
|
||||
func (b *bot) ListenAndServe() {
|
||||
addr := b.config.Get("HttpAddr", "127.0.0.1:1337")
|
||||
stop := make(chan os.Signal, 1)
|
||||
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")
|
||||
b.web.ListenAndServe(addr)
|
||||
}()
|
||||
<-stop
|
||||
b.DefaultConnector().Shutdown()
|
||||
b.Receive(b.DefaultConnector(), Shutdown, msg.Message{})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// DefaultConnector is the main connector used for the bot
|
||||
// If more than one connector is on, some users may not see all messages if this is used.
|
||||
// Usage should be limited to out-of-band communications such as timed messages.
|
||||
|
@ -458,3 +410,7 @@ func (b *bot) CheckPassword(secret, password string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *bot) GetWeb() *web.Web {
|
||||
return b.web
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<!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"></script>
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<meta charset="UTF-8">
|
||||
<title>catbase</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<b-navbar>
|
||||
<b-navbar-brand>catbase</b-navbar-brand>
|
||||
<b-navbar-nav>
|
||||
<b-nav-item v-for="item in nav" :href="item.url">{{ item.name }}</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-navbar>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
err: '',
|
||||
nav: [],
|
||||
},
|
||||
mounted: function() {
|
||||
axios.get('/nav')
|
||||
.then(resp => {
|
||||
this.nav = resp.data;
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -4,6 +4,7 @@ package bot
|
|||
|
||||
import (
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/velour/catbase/bot/web"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
@ -168,20 +169,14 @@ type Bot interface {
|
|||
// RegisterFilter creates a filter function for message processing
|
||||
RegisterFilter(string, func(string) string)
|
||||
|
||||
// RegisterWeb records a web endpoint for the UI
|
||||
RegisterWebName(http.Handler, string, string)
|
||||
|
||||
// RegisterWeb records a web endpoint for the API
|
||||
RegisterWeb(http.Handler, string)
|
||||
|
||||
// Start the HTTP service
|
||||
ListenAndServe()
|
||||
|
||||
// DefaultConnector returns the base connector, which may not be the only connector
|
||||
DefaultConnector() Connector
|
||||
|
||||
// GetWebNavigation returns the current known web endpoints
|
||||
GetWebNavigation() []EndPoint
|
||||
// GetWeb returns the bot's webserver structure
|
||||
GetWeb() *web.Web
|
||||
|
||||
// GetPassword generates a unique password for modification commands on the public website
|
||||
GetPassword() string
|
||||
|
|
|
@ -4,6 +4,7 @@ package bot
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/velour/catbase/bot/web"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -26,6 +27,8 @@ type MockBot struct {
|
|||
Messages []string
|
||||
Actions []string
|
||||
Reactions []string
|
||||
|
||||
web *web.Web
|
||||
}
|
||||
|
||||
func (mb *MockBot) Config() *config.Config { return mb.Cfg }
|
||||
|
@ -60,9 +63,7 @@ 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) RegisterWebName(_ http.Handler, _, _ string) {}
|
||||
func (mb *MockBot) RegisterWeb(_ http.Handler, _ string) {}
|
||||
func (mb *MockBot) GetWebNavigation() []EndPoint { return nil }
|
||||
func (mb *MockBot) GetWeb() *web.Web { return mb.web }
|
||||
func (mb *MockBot) Receive(c Connector, kind Kind, msg msg.Message, args ...any) bool {
|
||||
return false
|
||||
}
|
||||
|
@ -118,6 +119,7 @@ func NewMockBot() *MockBot {
|
|||
Messages: make([]string, 0),
|
||||
Actions: make([]string, 0),
|
||||
}
|
||||
b.web = web.New(cfg)
|
||||
// If any plugin registered a route, we need to reset those before any new test
|
||||
http.DefaultServeMux = new(http.ServeMux)
|
||||
return &b
|
||||
|
|
22
bot/nav.html
22
bot/nav.html
|
@ -1,22 +0,0 @@
|
|||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">catbase</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
{{- $currentPage := .CurrentPage -}}
|
||||
{{range .Items}}
|
||||
<li class="nav-item">
|
||||
{{ if (eq $currentPage .Name) }}
|
||||
<a class="nav-link active" aria-current="page" href="{{.URL}}">{{.Name}}</a>
|
||||
{{ else }}
|
||||
<a class="nav-link" href="{{.URL}}">{{.Name}}</a>
|
||||
{{ end }}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
59
bot/web.go
59
bot/web.go
|
@ -1,59 +0,0 @@
|
|||
package bot
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var embeddedFS embed.FS
|
||||
|
||||
func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("index.html")
|
||||
w.Write(index)
|
||||
}
|
||||
|
||||
func (b *bot) serveNavHTML(w http.ResponseWriter, r *http.Request) {
|
||||
currentPage := chi.URLParam(r, "currentPage")
|
||||
tpl := template.Must(template.ParseFS(embeddedFS, "nav.html"))
|
||||
if err := tpl.Execute(w, struct {
|
||||
CurrentPage string
|
||||
Items []EndPoint
|
||||
}{currentPage, b.GetWebNavigation()}); err != nil {
|
||||
log.Error().Err(err).Msg("template error")
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, "Error parsing nav template")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bot) serveNav(w http.ResponseWriter, r *http.Request) {
|
||||
enc := json.NewEncoder(w)
|
||||
err := enc.Encode(b.GetWebNavigation())
|
||||
if err != nil {
|
||||
jsonErr, _ := json.Marshal(err)
|
||||
w.WriteHeader(500)
|
||||
w.Write(jsonErr)
|
||||
}
|
||||
}
|
||||
|
||||
// GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links
|
||||
// The parent <nav> is not included so each page may display it as
|
||||
// best fits
|
||||
func (b *bot) GetWebNavigation() []EndPoint {
|
||||
endpoints := b.httpEndPoints
|
||||
moreEndpoints := b.config.GetArray("bot.links", []string{})
|
||||
for _, e := range moreEndpoints {
|
||||
link := strings.SplitN(e, ":", 2)
|
||||
if len(link) != 2 {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, EndPoint{link[0], link[1]})
|
||||
}
|
||||
return endpoints
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package web
|
||||
|
||||
templ (w *Web) Header(title string) {
|
||||
<head>
|
||||
<!-- Load required Bootstrap and BootstrapVue CSS -->
|
||||
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8" />
|
||||
if title != "" {
|
||||
<title>catbase - { title }</title>
|
||||
} else {
|
||||
<title>catbase</title>
|
||||
}
|
||||
</head>
|
||||
}
|
||||
|
||||
templ (w *Web) Footer() {
|
||||
<script src="//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="//unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
|
||||
}
|
||||
|
||||
templ (w *Web) Index(title string, contents templ.Component) {
|
||||
<!DOCTYPE html />
|
||||
<html lang="en">
|
||||
@w.Header(title)
|
||||
<body>
|
||||
|
||||
@w.Nav(title)
|
||||
|
||||
if contents != nil {
|
||||
@contents
|
||||
}
|
||||
|
||||
@w.Footer()
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ (w *Web) Nav(currentPage string) {
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">catbase</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
for _, item := range w.GetWebNavigation() {
|
||||
<li class="nav-item">
|
||||
if currentPage == item.Name {
|
||||
<a class="nav-link active" aria-current="page" href={ templ.URL(item.URL) }>{ item.Name }</a>
|
||||
} else {
|
||||
<a class="nav-link" href={ templ.URL(item.URL) }>{ item.Name }</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package web
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func (w *Web) Header(title string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<head><!-- Load required Bootstrap and BootstrapVue CSS --><link type=\"text/css\" rel=\"stylesheet\" href=\"//unpkg.com/bootstrap/dist/css/bootstrap.min.css\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"UTF-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if title != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<title>catbase - ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 9, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<title>catbase</title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</head>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (w *Web) Footer() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var3 == nil {
|
||||
templ_7745c5c3_Var3 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script src=\"//unpkg.com/bootstrap/dist/js/bootstrap.bundle.min.js\"></script><script src=\"//unpkg.com/htmx.org@1.9.10\" integrity=\"sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC\" crossorigin=\"anonymous\"></script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (w *Web) Index(title string, contents templ.Component) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html /><html lang=\"en\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = w.Header(title).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<body>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = w.Nav(title).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if contents != nil {
|
||||
templ_7745c5c3_Err = contents.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = w.Footer().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (w *Web) Nav(currentPage string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var5 == nil {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"navbar navbar-expand-lg bg-body-tertiary\"><div class=\"container-fluid\"><a class=\"navbar-brand\" href=\"/\">catbase</a> <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#navbarNav\" aria-controls=\"navbarNav\" aria-expanded=\"false\" aria-label=\"Toggle navigation\"><span class=\"navbar-toggler-icon\"></span></button><div class=\"collapse navbar-collapse\" id=\"navbarNav\"><ul class=\"navbar-nav\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, item := range w.GetWebNavigation() {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"nav-item\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage == item.Name {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link active\" aria-current=\"page\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = templ.URL(item.URL)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 50, Col: 119}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"nav-link\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 templ.SafeURL = templ.URL(item.URL)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bot/web/index.templ`, Line: 52, Col: 92}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div></div></nav>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/httprate"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/velour/catbase/config"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
config *config.Config
|
||||
router *chi.Mux
|
||||
httpEndPoints []EndPoint
|
||||
}
|
||||
|
||||
type EndPoint struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links
|
||||
// The parent <nav> is not included so each page may display it as
|
||||
// best fits
|
||||
func (ws *Web) GetWebNavigation() []EndPoint {
|
||||
endpoints := ws.httpEndPoints
|
||||
moreEndpoints := ws.config.GetArray("bot.links", []string{})
|
||||
for _, e := range moreEndpoints {
|
||||
link := strings.SplitN(e, ":", 2)
|
||||
if len(link) != 2 {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, EndPoint{link[0], link[1]})
|
||||
}
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func (ws *Web) serveRoot(w http.ResponseWriter, r *http.Request) {
|
||||
ws.Index("Home", nil).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (ws *Web) serveNavHTML(w http.ResponseWriter, r *http.Request) {
|
||||
currentPage := chi.URLParam(r, "currentPage")
|
||||
ws.Nav(currentPage).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (ws *Web) serveNav(w http.ResponseWriter, r *http.Request) {
|
||||
enc := json.NewEncoder(w)
|
||||
err := enc.Encode(ws.GetWebNavigation())
|
||||
if err != nil {
|
||||
jsonErr, _ := json.Marshal(err)
|
||||
w.WriteHeader(500)
|
||||
w.Write(jsonErr)
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *Web) setupHTTP() {
|
||||
// Make the http logger optional
|
||||
// It has never served a purpose in production and with the emojy page, can make a rather noisy log
|
||||
if ws.config.GetInt("bot.useLogger", 0) == 1 {
|
||||
ws.router.Use(middleware.Logger)
|
||||
}
|
||||
|
||||
reqCount := ws.config.GetInt("bot.httprate.requests", 500)
|
||||
reqTime := time.Duration(ws.config.GetInt("bot.httprate.seconds", 5))
|
||||
if reqCount > 0 && reqTime > 0 {
|
||||
ws.router.Use(httprate.LimitByIP(reqCount, reqTime*time.Second))
|
||||
}
|
||||
|
||||
ws.router.Use(middleware.RequestID)
|
||||
ws.router.Use(middleware.Recoverer)
|
||||
ws.router.Use(middleware.StripSlashes)
|
||||
|
||||
ws.router.HandleFunc("/", ws.serveRoot)
|
||||
ws.router.HandleFunc("/nav", ws.serveNav)
|
||||
ws.router.HandleFunc("/navHTML", ws.serveNavHTML)
|
||||
ws.router.HandleFunc("/navHTML/{currentPage}", ws.serveNavHTML)
|
||||
}
|
||||
|
||||
func (ws *Web) RegisterWeb(r http.Handler, root string) {
|
||||
ws.router.Mount(root, r)
|
||||
}
|
||||
|
||||
func (ws *Web) RegisterWebName(r http.Handler, root, name string) {
|
||||
ws.httpEndPoints = append(ws.httpEndPoints, EndPoint{name, root})
|
||||
ws.router.Mount(root, r)
|
||||
}
|
||||
|
||||
func (ws *Web) ListenAndServe(addr string) {
|
||||
log.Debug().Msgf("starting web service at %s", addr)
|
||||
log.Fatal().Err(http.ListenAndServe(addr, ws.router)).Msg("bot killed")
|
||||
}
|
||||
|
||||
func New(config *config.Config) *Web {
|
||||
w := &Web{
|
||||
config: config,
|
||||
router: chi.NewRouter(),
|
||||
}
|
||||
w.setupHTTP()
|
||||
return w
|
||||
}
|
19
go.mod
19
go.mod
|
@ -5,11 +5,12 @@ go 1.18
|
|||
require (
|
||||
code.chrissexton.org/cws/getaoc v0.0.0-20231202052842-1b2a337b799d
|
||||
github.com/ChimeraCoder/anaconda v2.0.0+incompatible
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/a-h/templ v0.2.543
|
||||
github.com/andrewstuart/openai v0.8.0
|
||||
github.com/bwmarrin/discordgo v0.26.1
|
||||
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598
|
||||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff
|
||||
github.com/chrissexton/sentiment v0.0.0-20190927141846-d69c422ba035
|
||||
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec
|
||||
|
@ -34,7 +35,7 @@ require (
|
|||
github.com/stretchr/testify v1.8.2
|
||||
github.com/trubitsyn/go-zero-width v1.0.1
|
||||
github.com/velour/velour v0.0.0-20160303155839-8e090e68d158
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/crypto v0.14.0
|
||||
gopkg.in/go-playground/webhooks.v5 v5.17.0
|
||||
)
|
||||
|
||||
|
@ -77,8 +78,8 @@ require (
|
|||
github.com/kevinburke/go-types v0.0.0-20200309064045-f2d4aea18a7a // indirect
|
||||
github.com/kevinburke/go.uuid v1.2.0 // indirect
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
|
@ -101,10 +102,10 @@ require (
|
|||
github.com/ttacon/libphonenumber v1.1.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
gonum.org/v1/gonum v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
|
||||
|
|
43
go.sum
43
go.sum
|
@ -45,8 +45,10 @@ github.com/ChimeraCoder/anaconda v2.0.0+incompatible/go.mod h1:TCt3MijIq3Qqo9SBt
|
|||
github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 h1:r+EmXjfPosKO4wfiMLe1XQictsIlhErTufbWUsjOTZs=
|
||||
github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7/go.mod h1:b2EuEMLSG9q3bZ95ql1+8oVqzzrTNSiOQqSXWFBzxeI=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk=
|
||||
github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
|
@ -78,8 +80,8 @@ github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 h1:j2XRGH5Y5uWtBYXGw
|
|||
github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
|
@ -197,7 +199,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
@ -274,11 +276,13 @@ github.com/leso-kn/go-sqlite3 v0.0.0-20230710125852-03158dc838ed h1:lM1oz49yOQhE
|
|||
github.com/leso-kn/go-sqlite3 v0.0.0-20230710125852-03158dc838ed/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mmcdole/gofeed v1.1.3 h1:pdrvMb18jMSLidGp8j0pLvc9IGziX4vbmvVqmLH6z8o=
|
||||
|
@ -385,6 +389,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -400,8 +405,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -438,6 +444,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -475,8 +482,10 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -494,8 +503,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -539,11 +548,15 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -551,8 +564,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -600,6 +614,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
6
main.go
6
main.go
|
@ -2,6 +2,8 @@
|
|||
|
||||
package main
|
||||
|
||||
//go:generate templ generate
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/velour/catbase/plugins/gpt"
|
||||
|
@ -45,7 +47,6 @@ import (
|
|||
"github.com/velour/catbase/plugins/admin"
|
||||
"github.com/velour/catbase/plugins/babbler"
|
||||
"github.com/velour/catbase/plugins/beers"
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"github.com/velour/catbase/plugins/couldashouldawoulda"
|
||||
"github.com/velour/catbase/plugins/counter"
|
||||
"github.com/velour/catbase/plugins/dice"
|
||||
|
@ -128,7 +129,7 @@ func main() {
|
|||
b := bot.New(c, client)
|
||||
|
||||
if r, path := client.GetRouter(); r != nil {
|
||||
b.RegisterWeb(r, path)
|
||||
b.GetWeb().RegisterWeb(r, path)
|
||||
}
|
||||
|
||||
b.AddPlugin(admin.New(b))
|
||||
|
@ -166,7 +167,6 @@ func main() {
|
|||
b.AddPlugin(twitter.New(b))
|
||||
b.AddPlugin(git.New(b))
|
||||
b.AddPlugin(impossible.New(b))
|
||||
b.AddPlugin(cli.New(b))
|
||||
b.AddPlugin(aoc.New(b))
|
||||
b.AddPlugin(meme.New(b))
|
||||
b.AddPlugin(achievements.New(b))
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package admin
|
||||
|
||||
import "fmt"
|
||||
|
||||
templ (a *AdminPlugin) page() {
|
||||
<div class="container">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label for="password">Password:
|
||||
<input type="text" name="password"></input>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="secret">Secret:
|
||||
<input type="text" name="secret"></input>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button hx-post="/apppass/api" hx-target="#data" class="btn btn-primary">List</button>
|
||||
<button hx-put="/apppass/api" hx-target="#data" class="btn btn-secondary">New</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="row">
|
||||
<div id="data"></div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ (a *AdminPlugin) showPassword(entry PassEntry) {
|
||||
<div><span>ID</span><span>{ fmt.Sprintf("%d", entry.ID) }</span></div>
|
||||
<div><span>Password</span><span>{ entry.Secret }:{ entry.Pass }</span></div>
|
||||
}
|
||||
|
||||
templ (a *AdminPlugin) entries(items []PassEntry) {
|
||||
<div>
|
||||
if len(items) == 0 {
|
||||
<span>No items</span>
|
||||
}
|
||||
<ul>
|
||||
for _, entry := range items {
|
||||
<li>
|
||||
<button href="#"
|
||||
class="btn btn-danger"
|
||||
hx-delete="/apppass/api"
|
||||
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %d?", entry.ID) }
|
||||
hx-target="#data"
|
||||
hx-include="this,[name='password'],[name='secret']"
|
||||
name="id" value={ fmt.Sprintf("%d", entry.ID) }>X</button>
|
||||
{ fmt.Sprintf("%d", entry.ID) }
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ renderError(err error) {
|
||||
<div>{ err.Error() }</div>
|
||||
}
|
||||
|
||||
templ vars(items []configEntry) {
|
||||
<div class="container">
|
||||
<table class="table-responsive table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, item := range items {
|
||||
<tr>
|
||||
<td>{ item.Key }</td><td>{ item.Value }</td>
|
||||
</tr>
|
||||
}
|
||||
if len(items) == 0 {
|
||||
<tr>
|
||||
<td colspan="2">No data</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package admin
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (a *AdminPlugin) page() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><form><div class=\"row\"><div class=\"col\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"col\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"col\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-primary\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"btn btn-secondary\">New</button></div></div></form><div class=\"row\"><div id=\"data\"></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><span>ID</span><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 32, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div><div><span>Password</span><span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Secret)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 33, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(":")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Pass)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 33, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AdminPlugin) entries(items []PassEntry) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(items) == 0 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span>No items</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, entry := range items {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><button href=\"#\" class=\"btn btn-danger\" hx-delete=\"/apppass/api\" hx-confirm=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("Are you sure you want to delete %d?", entry.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" hx-include=\"this,[name='password'],[name='secret']\" name=\"id\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("%d", entry.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">X</button> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 51, Col: 57}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func renderError(err error) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var8 == nil {
|
||||
templ_7745c5c3_Var8 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(err.Error())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 59, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func vars(items []configEntry) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var10 == nil {
|
||||
templ_7745c5c3_Var10 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><table class=\"table-responsive table-striped\"><thead><tr><th>Key</th><th>Value</th></tr></thead> <tbody>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, item := range items {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 74, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 74, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if len(items) == 0 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td colspan=\"2\">No data</td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -5,8 +5,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -34,10 +32,8 @@ func makeMessage(payload string, r *regexp.Regexp) bot.Request {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
c := cli.CliPlugin{}
|
||||
values := bot.ParseValues(r, payload)
|
||||
return bot.Request{
|
||||
Conn: &c,
|
||||
Kind: bot.Message,
|
||||
Values: values,
|
||||
Msg: msg.Message{
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
<!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="//unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vars</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<b-navbar>
|
||||
<b-navbar-brand>App Pass</b-navbar-brand>
|
||||
<b-navbar-nav>
|
||||
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'App Pass'">{{ item.name }}
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-navbar>
|
||||
<div class="alert alert-warning alert-dismissible fade show" role="alert" v-if="err != ''">
|
||||
<b-button type="link" class="close" data-dismiss="alert" aria-label="Close" @click="err = ''">
|
||||
<span aria-hidden="true">×</span>
|
||||
</b-button>
|
||||
{{ err }}
|
||||
</div>
|
||||
<b-form>
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col cols="5">Password:</b-col>
|
||||
<b-col>
|
||||
<b-input v-model="password"/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="5">Secret:</b-col>
|
||||
<b-col>
|
||||
<b-input v-model="entry.secret"/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-button @click="list">List</b-button>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-button @click="newPass">New</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-form>
|
||||
<b-container v-show="showPassword" style="padding: 2em">
|
||||
<b-row align-h="center">
|
||||
<b-col align-self="center" cols="1">ID:</b-col>
|
||||
<b-col align-self="center" cols="3">{{ entry.id }}</b-col>
|
||||
</b-row>
|
||||
<b-row align-h="center">
|
||||
<b-col align-self="center" cols="1">Password:</b-col>
|
||||
<b-col align-self="center" cols="3">{{ entry.secret }}:{{ showPassword }}</b-col>
|
||||
</b-row>
|
||||
<b-row align-h="center">
|
||||
<b-col align-self="center" class="text-center" cols="6">Note: this password will only be displayed once. For
|
||||
single-field entry passwords, use the secret:password format.
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
<b-container>
|
||||
<b-row style="padding-top: 2em;">
|
||||
<b-col>
|
||||
<ul>
|
||||
<li v-for="entry in entries" key="id">
|
||||
<a @click="rm(entry)" href="#">X</a> {{entry.id}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
err: '',
|
||||
entry: {
|
||||
secret: '',
|
||||
},
|
||||
password: '',
|
||||
showPassword: '',
|
||||
nav: [],
|
||||
entries: [],
|
||||
},
|
||||
mounted() {
|
||||
axios.get('/nav')
|
||||
.then(resp => {
|
||||
this.nav = resp.data;
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
},
|
||||
methods: {
|
||||
rm: function (data) {
|
||||
this.showPassword = '';
|
||||
this.entry.id = data.id
|
||||
axios.delete('/apppass/api', {
|
||||
data: {
|
||||
password: this.password,
|
||||
passEntry: this.entry
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.getData()
|
||||
})
|
||||
.catch(({response}) => {
|
||||
console.log('error: ' + response.data.err)
|
||||
this.err = response.data.err
|
||||
})
|
||||
},
|
||||
list: function () {
|
||||
this.showPassword = '';
|
||||
this.getData();
|
||||
},
|
||||
newPass: function () {
|
||||
axios.put('/apppass/api', {
|
||||
password: this.password,
|
||||
passEntry: this.entry
|
||||
})
|
||||
.then(resp => {
|
||||
this.getData()
|
||||
this.showPassword = resp.data.pass
|
||||
this.entry.id = resp.data.id
|
||||
})
|
||||
.catch(({response}) => {
|
||||
console.log('error: ' + response.data.err)
|
||||
this.err = response.data.err
|
||||
})
|
||||
},
|
||||
getData: function () {
|
||||
axios.post('/apppass/api', {
|
||||
password: this.password,
|
||||
passEntry: this.entry
|
||||
})
|
||||
.then(resp => {
|
||||
this.entries = resp.data;
|
||||
})
|
||||
.catch(({response}) => {
|
||||
console.log('error: ' + response.data.err)
|
||||
this.err = response.data.err
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,36 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>vars</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.4"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div hx-get="/navHTML/Variables" hx-trigger="load" hx-swap="outerHTML"></div>
|
||||
|
||||
<div class="container">
|
||||
<table class="table-responsive table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Items}}
|
||||
<tr>
|
||||
<td>{{.Key}}</td><td>{{.Value}}</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr>
|
||||
<td colspan="2">No data</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,39 +1,34 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var embeddedFS embed.FS
|
||||
|
||||
func (p *AdminPlugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/api", p.handleVarsAPI)
|
||||
r.HandleFunc("/", p.handleVars)
|
||||
p.bot.RegisterWebName(r, "/vars", "Variables")
|
||||
p.bot.GetWeb().RegisterWebName(r, "/vars", "Variables")
|
||||
r = chi.NewRouter()
|
||||
r.HandleFunc("/verify", p.handleAppPassCheck)
|
||||
r.HandleFunc("/api", p.handleAppPassAPI)
|
||||
r.HandleFunc("/", p.handleAppPass)
|
||||
p.bot.RegisterWebName(r, "/apppass", "App Pass")
|
||||
p.bot.GetWeb().RegisterWebName(r, "/apppass", "App Pass")
|
||||
}
|
||||
|
||||
func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("apppass.html")
|
||||
w.Write(index)
|
||||
p.bot.GetWeb().Index("App Pass", p.page()).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
type PassEntry struct {
|
||||
|
@ -77,26 +72,41 @@ func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
b, _ := io.ReadAll(r.Body)
|
||||
query, _ := url.ParseQuery(string(b))
|
||||
secret := r.FormValue("secret")
|
||||
password := r.FormValue("password")
|
||||
id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64)
|
||||
if !r.Form.Has("secret") && query.Has("secret") {
|
||||
secret = query.Get("secret")
|
||||
}
|
||||
if !r.Form.Has("password") && query.Has("password") {
|
||||
password = query.Get("password")
|
||||
}
|
||||
if !r.Form.Has("id") && query.Has("id") {
|
||||
id, _ = strconv.ParseInt(query.Get("id"), 10, 64)
|
||||
}
|
||||
req := struct {
|
||||
Password string `json:"password"`
|
||||
PassEntry PassEntry `json:"passEntry"`
|
||||
}{}
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
_ = json.Unmarshal(body, &req)
|
||||
}{
|
||||
password,
|
||||
PassEntry{
|
||||
ID: id,
|
||||
Secret: secret,
|
||||
},
|
||||
}
|
||||
if req.PassEntry.Secret == "" {
|
||||
writeErr(w, fmt.Errorf("missing secret"))
|
||||
writeErr(r.Context(), w, fmt.Errorf("missing secret"))
|
||||
return
|
||||
}
|
||||
if req.Password == "" || !p.bot.CheckPassword(req.PassEntry.Secret, req.Password) {
|
||||
writeErr(w, fmt.Errorf("missing or incorrect password"))
|
||||
writeErr(r.Context(), w, fmt.Errorf("missing or incorrect password"))
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodPut:
|
||||
if req.PassEntry.Secret == "" {
|
||||
writeErr(w, fmt.Errorf("missing secret"))
|
||||
return
|
||||
}
|
||||
if string(req.PassEntry.Pass) == "" {
|
||||
c := 10
|
||||
b := make([]byte, c)
|
||||
|
@ -121,27 +131,27 @@ func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
res, err := p.db.Exec(q, req.PassEntry.Secret, req.PassEntry.encodedPass, req.PassEntry.Cost)
|
||||
if err != nil {
|
||||
writeErr(w, err)
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
writeErr(w, err)
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
req.PassEntry.ID = id
|
||||
j, _ := json.Marshal(req.PassEntry)
|
||||
fmt.Fprint(w, string(j))
|
||||
p.showPassword(req.PassEntry).Render(r.Context(), w)
|
||||
return
|
||||
case http.MethodDelete:
|
||||
|
||||
if req.PassEntry.ID <= 0 {
|
||||
writeErr(w, fmt.Errorf("missing ID"))
|
||||
writeErr(r.Context(), w, fmt.Errorf("missing ID"))
|
||||
return
|
||||
}
|
||||
q := `delete from apppass where id = ?`
|
||||
_, err := p.db.Exec(q, req.PassEntry.ID)
|
||||
if err != nil {
|
||||
writeErr(w, err)
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -149,22 +159,15 @@ func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
|
|||
passEntries := []PassEntry{}
|
||||
err := p.db.Select(&passEntries, q, req.PassEntry.Secret)
|
||||
if err != nil {
|
||||
writeErr(w, err)
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
j, _ := json.Marshal(passEntries)
|
||||
_, _ = fmt.Fprint(w, string(j))
|
||||
p.entries(passEntries).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func writeErr(w http.ResponseWriter, err error) {
|
||||
func writeErr(ctx context.Context, w http.ResponseWriter, err error) {
|
||||
log.Error().Err(err).Msg("apppass error")
|
||||
j, _ := json.Marshal(struct {
|
||||
Err string `json:"err"`
|
||||
}{
|
||||
err.Error(),
|
||||
})
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprint(w, string(j))
|
||||
renderError(err).Render(ctx, w)
|
||||
}
|
||||
|
||||
type configEntry struct {
|
||||
|
@ -173,7 +176,6 @@ type configEntry struct {
|
|||
}
|
||||
|
||||
func (p *AdminPlugin) handleVars(w http.ResponseWriter, r *http.Request) {
|
||||
tpl := template.Must(template.ParseFS(embeddedFS, "vars.html"))
|
||||
var configEntries []configEntry
|
||||
q := `select key, value from config`
|
||||
err := p.db.Select(&configEntries, q)
|
||||
|
@ -186,37 +188,5 @@ func (p *AdminPlugin) handleVars(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := tpl.Execute(w, struct {
|
||||
Items []configEntry
|
||||
}{configEntries}); err != nil {
|
||||
log.Error().Err(err).Msg("template error")
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, "Error parsing template")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AdminPlugin) handleVarsAPI(w http.ResponseWriter, r *http.Request) {
|
||||
var configEntries []struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
q := `select key, value from config`
|
||||
err := p.db.Select(&configEntries, q)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Error getting config entries.")
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
return
|
||||
}
|
||||
for i, e := range configEntries {
|
||||
if strings.Contains(e.Value, ";;") {
|
||||
e.Value = strings.ReplaceAll(e.Value, ";;", ", ")
|
||||
e.Value = fmt.Sprintf("[%s]", e.Value)
|
||||
configEntries[i] = e
|
||||
}
|
||||
}
|
||||
j, _ := json.Marshal(configEntries)
|
||||
fmt.Fprintf(w, "%s", j)
|
||||
p.bot.GetWeb().Index("Variables", vars(configEntries)).Render(r.Context(), w)
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -16,13 +14,11 @@ import (
|
|||
)
|
||||
|
||||
func makeMessage(payload string, r *regexp.Regexp) bot.Request {
|
||||
c := &cli.CliPlugin{}
|
||||
isCmd := strings.HasPrefix(payload, "!")
|
||||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return bot.Request{
|
||||
Conn: c,
|
||||
Kind: bot.Message,
|
||||
Values: bot.ParseValues(r, payload),
|
||||
Msg: msg.Message{
|
||||
|
@ -272,7 +268,6 @@ func TestHelp(t *testing.T) {
|
|||
mb := bot.NewMockBot()
|
||||
bp := newBabblerPlugin(mb)
|
||||
assert.NotNil(t, bp)
|
||||
c := &cli.CliPlugin{}
|
||||
bp.help(c, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
bp.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Len(t, mb.Messages, 1)
|
||||
}
|
||||
|
|
|
@ -598,7 +598,7 @@ func (p *BeersPlugin) untappdLoop(c bot.Connector, channel string) {
|
|||
func (p *BeersPlugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/img/{id}", p.img)
|
||||
p.b.RegisterWeb(r, "/beers")
|
||||
p.b.GetWeb().RegisterWeb(r, "/beers")
|
||||
}
|
||||
|
||||
func (p *BeersPlugin) img(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -21,10 +19,8 @@ func makeMessage(payload string, r *regexp.Regexp) bot.Request {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
c := &cli.CliPlugin{}
|
||||
values := bot.ParseValues(r, payload)
|
||||
return bot.Request{
|
||||
Conn: c,
|
||||
Kind: bot.Message,
|
||||
Values: values,
|
||||
Msg: msg.Message{
|
||||
|
@ -136,6 +132,6 @@ func TestBeersReport(t *testing.T) {
|
|||
|
||||
func TestHelp(t *testing.T) {
|
||||
b, mb := makeBeersPlugin(t)
|
||||
b.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
b.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Len(t, mb.Messages, 1)
|
||||
}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
"github.com/velour/catbase/bot/user"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var embeddedFS embed.FS
|
||||
|
||||
type CliPlugin struct {
|
||||
bot bot.Bot
|
||||
db *sqlx.DB
|
||||
cache string
|
||||
counter int
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *CliPlugin {
|
||||
cp := &CliPlugin{
|
||||
bot: b,
|
||||
}
|
||||
cp.registerWeb()
|
||||
return cp
|
||||
}
|
||||
|
||||
func (p *CliPlugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/api", p.handleWebAPI)
|
||||
r.HandleFunc("/", p.handleWeb)
|
||||
p.bot.RegisterWebName(r, "/cli", "CLI")
|
||||
}
|
||||
|
||||
func (p *CliPlugin) Shutdown() {}
|
||||
|
||||
func (p *CliPlugin) GetRouter() (http.Handler, string) {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (p *CliPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
fmt.Fprintf(w, "Incorrect HTTP method")
|
||||
return
|
||||
}
|
||||
info := struct {
|
||||
User string `json:"user"`
|
||||
Payload string `json:"payload"`
|
||||
Password string `json:"password"`
|
||||
}{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&info)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
return
|
||||
}
|
||||
log.Debug().
|
||||
Interface("postbody", info).
|
||||
Msg("Got a POST")
|
||||
if !p.bot.CheckPassword("", info.Password) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
j, _ := json.Marshal(struct{ Err string }{Err: "Invalid Password"})
|
||||
w.Write(j)
|
||||
return
|
||||
}
|
||||
|
||||
p.bot.Receive(p, bot.Message, msg.Message{
|
||||
User: &user.User{
|
||||
ID: info.User,
|
||||
Name: info.User,
|
||||
Admin: false,
|
||||
},
|
||||
Channel: "web",
|
||||
Body: info.Payload,
|
||||
Raw: info.Payload,
|
||||
Command: true,
|
||||
Time: time.Now(),
|
||||
})
|
||||
|
||||
info.User = p.bot.WhoAmI()
|
||||
info.Payload = p.cache
|
||||
p.cache = ""
|
||||
|
||||
data, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
return
|
||||
}
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (p *CliPlugin) handleWeb(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("index.html")
|
||||
w.Write(index)
|
||||
}
|
||||
|
||||
// Completing the Connector interface, but will not actually be a connector
|
||||
func (p *CliPlugin) RegisterEvent(cb bot.Callback) {}
|
||||
func (p *CliPlugin) Send(kind bot.Kind, args ...any) (string, error) {
|
||||
switch kind {
|
||||
case bot.Message:
|
||||
fallthrough
|
||||
case bot.Action:
|
||||
fallthrough
|
||||
case bot.Reply:
|
||||
fallthrough
|
||||
case bot.Reaction:
|
||||
p.cache += args[1].(string) + "\n"
|
||||
}
|
||||
id := fmt.Sprintf("%d", p.counter)
|
||||
p.counter++
|
||||
return id, nil
|
||||
}
|
||||
func (p *CliPlugin) GetEmojiList(bool) map[string]string { return nil }
|
||||
func (p *CliPlugin) Serve() error { return nil }
|
||||
func (p *CliPlugin) Who(s string) []string { return nil }
|
||||
func (p *CliPlugin) Profile(name string) (user.User, error) {
|
||||
return user.User{}, fmt.Errorf("unimplemented")
|
||||
}
|
||||
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 {
|
||||
return fmt.Sprintf("%s (%s)", title, url)
|
||||
}
|
||||
func (p *CliPlugin) GetChannelName(id string) string { return id }
|
||||
func (p *CliPlugin) GetChannelID(name string) string { return name }
|
||||
func (p *CliPlugin) GetRoles() ([]bot.Role, error) { return []bot.Role{}, nil }
|
||||
func (p *CliPlugin) SetRole(userID, roleID string) error { return nil }
|
||||
func (p *CliPlugin) Nick(string) error { return nil }
|
|
@ -1,132 +0,0 @@
|
|||
<!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/axios/dist/axios.min.js"></script>
|
||||
<meta charset="UTF-8">
|
||||
<title>CLI</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<b-navbar>
|
||||
<b-navbar-brand>CLI</b-navbar-brand>
|
||||
<b-navbar-nav>
|
||||
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'CLI'">{{ item.name }}</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-navbar>
|
||||
<b-alert
|
||||
dismissable
|
||||
variant="error"
|
||||
:show="err">
|
||||
{{ err }}
|
||||
</b-alert>
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col cols="5">Password:</b-col>
|
||||
<b-col><b-input v-model="answer"></b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-form-textarea
|
||||
v-sticky-scroll
|
||||
disabled
|
||||
id="textarea"
|
||||
v-model="text"
|
||||
placeholder="The bot will respond here..."
|
||||
rows="10"
|
||||
max-rows="10"
|
||||
no-resize
|
||||
></b-form-textarea>
|
||||
</b-row>
|
||||
<b-form
|
||||
@submit="send">
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-form-input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
v-model="user"></b-form-input>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-input
|
||||
type="text"
|
||||
placeholder="Enter something to send to the bot"
|
||||
v-model="input"
|
||||
autocomplete="off"
|
||||
></b-form-input>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-button type="submit" :disabled="!authenticated">Send</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</b-container>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
err: '',
|
||||
nav: [],
|
||||
answer: '',
|
||||
correct: 0,
|
||||
textarea: [],
|
||||
user: '',
|
||||
input: '',
|
||||
},
|
||||
mounted: function() {
|
||||
axios.get('/nav')
|
||||
.then(resp => {
|
||||
this.nav = resp.data;
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
},
|
||||
computed: {
|
||||
authenticated: function() {
|
||||
if (this.user !== '')
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
text: function() {
|
||||
return this.textarea.join('\n');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addText(user, text) {
|
||||
this.textarea.push(user + ": " + text);
|
||||
const len = this.textarea.length;
|
||||
if (this.textarea.length > 10)
|
||||
this.textarea = this.textarea.slice(len-10, len);
|
||||
},
|
||||
send(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation()
|
||||
if (!this.authenticated) {
|
||||
return;
|
||||
}
|
||||
const payload = {user: this.user, payload: this.input, password: this.answer};
|
||||
this.addText(this.user, this.input);
|
||||
this.input = "";
|
||||
axios.post('/cli/api', payload)
|
||||
.then(resp => {
|
||||
const data = resp.data;
|
||||
this.addText(data.user, data.payload.trim());
|
||||
this.err = '';
|
||||
})
|
||||
.catch(err => (this.err = err));
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -6,8 +6,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
|
@ -21,7 +19,6 @@ func makeMessage(payload string) bot.Request {
|
|||
payload = payload[1:]
|
||||
}
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package counter
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/velour/catbase/bot/user"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -18,9 +16,6 @@ import (
|
|||
"github.com/velour/catbase/bot/msg"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var embeddedFS embed.FS
|
||||
|
||||
func (p *CounterPlugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
requests := p.cfg.GetInt("counter.requestsPer", 1)
|
||||
|
@ -33,9 +28,77 @@ func (p *CounterPlugin) registerWeb() {
|
|||
subrouter.HandleFunc("/api/users/{user}/items/{item}/increment", p.mkIncrementByNAPI(1))
|
||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementByNAPI(-1))
|
||||
r.Mount("/", subrouter)
|
||||
r.HandleFunc("/api", p.handleCounterAPI)
|
||||
r.HandleFunc("/users/{user}/items/{item}/increment", p.incHandler(1))
|
||||
r.HandleFunc("/users/{user}/items/{item}/decrement", p.incHandler(-1))
|
||||
r.HandleFunc("/", p.handleCounter)
|
||||
p.b.RegisterWebName(r, "/counter", "Counter")
|
||||
p.b.GetWeb().RegisterWebName(r, "/counter", "Counter")
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) incHandler(delta int) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userName, _ := url.QueryUnescape(chi.URLParam(r, "user"))
|
||||
itemName, _ := url.QueryUnescape(chi.URLParam(r, "item"))
|
||||
pass := r.FormValue("password")
|
||||
if !p.b.CheckPassword("", pass) {
|
||||
w.WriteHeader(401)
|
||||
fmt.Fprintf(w, "error")
|
||||
return
|
||||
}
|
||||
item, err := p.delta(userName, itemName, "", delta)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprintf(w, "error")
|
||||
return
|
||||
}
|
||||
p.renderItem(userName, item).Render(r.Context(), w)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -64,15 +127,15 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
return
|
||||
}
|
||||
|
||||
// Try to find an ID if possible
|
||||
id := ""
|
||||
u, err := p.b.DefaultConnector().Profile(userName)
|
||||
if err == nil {
|
||||
id = u.ID
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
postData := map[string]string{}
|
||||
err = json.Unmarshal(body, &postData)
|
||||
personalMsg := ""
|
||||
if inputMsg, ok := postData["message"]; ok {
|
||||
personalMsg = fmt.Sprintf("\nMessage: %s", inputMsg)
|
||||
}
|
||||
|
||||
item, err := GetUserItem(p.db, userName, id, itemName)
|
||||
if err != nil {
|
||||
if _, err := p.delta(userName, itemName, personalMsg, delta*direction); err != nil {
|
||||
log.Error().Err(err).Msg("error finding item")
|
||||
w.WriteHeader(400)
|
||||
j, _ := json.Marshal(struct {
|
||||
|
@ -83,130 +146,13 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
return
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
postData := map[string]string{}
|
||||
err = json.Unmarshal(body, &postData)
|
||||
personalMsg := ""
|
||||
if inputMsg, ok := postData["message"]; ok {
|
||||
personalMsg = fmt.Sprintf("\nMessage: %s", inputMsg)
|
||||
}
|
||||
|
||||
chs := p.cfg.GetMap("counter.channelItems", map[string]string{})
|
||||
ch, ok := chs[itemName]
|
||||
if len(chs) == 0 || !ok {
|
||||
return
|
||||
}
|
||||
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*direction, p.cfg.Get("nick", "catbase"), personalMsg)
|
||||
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
|
||||
}
|
||||
item.UpdateDelta(req, delta*direction)
|
||||
j, _ := json.Marshal(struct{ Status bool }{true})
|
||||
fmt.Fprint(w, string(j))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) handleCounter(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("index.html")
|
||||
w.Write(index)
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) handleCounterAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost {
|
||||
info := struct {
|
||||
User string
|
||||
Thing string
|
||||
Action string
|
||||
Password string
|
||||
}{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&info)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
return
|
||||
}
|
||||
log.Debug().
|
||||
Interface("postbody", info).
|
||||
Msg("Got a POST")
|
||||
if !p.b.CheckPassword("", info.Password) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
j, _ := json.Marshal(struct{ Err string }{Err: "Invalid Password"})
|
||||
w.Write(j)
|
||||
return
|
||||
}
|
||||
req := bot.Request{
|
||||
Conn: p.b.DefaultConnector(),
|
||||
Kind: bot.Message,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{
|
||||
ID: "",
|
||||
Name: info.User,
|
||||
Admin: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
// resolveUser requires a "full" request object so we are faking it
|
||||
nick, id := p.resolveUser(req, info.User)
|
||||
item, err := GetUserItem(p.db, nick, id, info.Thing)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("subject", info.User).
|
||||
Str("itemName", info.Thing).
|
||||
Msg("error finding item")
|
||||
w.WriteHeader(404)
|
||||
fmt.Fprint(w, err)
|
||||
return
|
||||
}
|
||||
if info.Action == "++" {
|
||||
item.UpdateDelta(nil, 1)
|
||||
} else if info.Action == "--" {
|
||||
item.UpdateDelta(nil, -1)
|
||||
} else {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprint(w, "Invalid increment")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
all, err := GetAllItems(p.db)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
log.Error().Err(err).Msg("Error getting items")
|
||||
return
|
||||
}
|
||||
data, err := json.Marshal(all)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, err)
|
||||
log.Error().Err(err).Msg("Error marshaling items")
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(data))
|
||||
p.b.GetWeb().Index("Counter", p.index()).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
// Update represents a change that gets sent off to other plugins such as goals
|
||||
|
|
|
@ -45,7 +45,7 @@ type alias struct {
|
|||
}
|
||||
|
||||
// GetItems returns all counters
|
||||
func GetAllItems(db *sqlx.DB) ([]Item, error) {
|
||||
func GetAllItemsByUser(db *sqlx.DB) (map[string][]Item, error) {
|
||||
var items []Item
|
||||
err := db.Select(&items, `select * from counter`)
|
||||
if err != nil {
|
||||
|
@ -55,7 +55,11 @@ func GetAllItems(db *sqlx.DB) ([]Item, error) {
|
|||
for i := range items {
|
||||
items[i].DB = db
|
||||
}
|
||||
return items, nil
|
||||
out := map[string][]Item{}
|
||||
for _, it := range items {
|
||||
out[it.Nick] = append(out[it.Nick], it)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetItems returns all counters for a subject
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package counter
|
||||
|
||||
import "fmt"
|
||||
|
||||
func urlFor(who, what, dir string) string {
|
||||
return fmt.Sprintf("/counter/users/%s/items/%s/%s", who, what, dir)
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) allItems() map[string][]Item {
|
||||
items, err := GetAllItemsByUser(p.db)
|
||||
if err != nil {
|
||||
return map[string][]Item{"error": []Item{}}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
templ (p *CounterPlugin) index() {
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<label>Password: <input type="text" name="password" /></label>
|
||||
</div>
|
||||
for user, items := range p.allItems() {
|
||||
<div class="row">
|
||||
{ user }:
|
||||
<div class="container">
|
||||
for _, thing := range items {
|
||||
@p.renderItem(user, thing)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ (p *CounterPlugin) renderItem(user string, item Item) {
|
||||
<div class="row" id={ fmt.Sprintf("item%d", item.ID) }>
|
||||
<div class="col offset-1">
|
||||
{ item.Item }
|
||||
</div>
|
||||
<div class="col">
|
||||
{ fmt.Sprintf("%d", item.Count) }
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<button
|
||||
hx-target={ "#"+fmt.Sprintf("item%d", item.ID) }
|
||||
hx-include="[name='password']"
|
||||
hx-swap="outerHTML"
|
||||
hx-post={ urlFor(user, item.Item, "decrement") }
|
||||
>-</button>
|
||||
<button
|
||||
hx-target={ "#"+fmt.Sprintf("item%d", item.ID) }
|
||||
hx-include="[name='password']"
|
||||
hx-swap="outerHTML"
|
||||
hx-post={ urlFor(user, item.Item, "increment") }
|
||||
>+</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package counter
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "fmt"
|
||||
|
||||
func urlFor(who, what, dir string) string {
|
||||
return fmt.Sprintf("/counter/users/%s/items/%s/%s", who, what, dir)
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) allItems() map[string][]Item {
|
||||
items, err := GetAllItemsByUser(p.db)
|
||||
if err != nil {
|
||||
return map[string][]Item{"error": []Item{}}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) index() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><div class=\"row\"><label>Password: <input type=\"text\" name=\"password\"></label></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for user, items := range p.allItems() {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"row\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/counter/counter.templ`, Line: 23, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(":<div class=\"container\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, thing := range items {
|
||||
templ_7745c5c3_Err = p.renderItem(user, thing).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) renderItem(user string, item Item) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var3 == nil {
|
||||
templ_7745c5c3_Var3 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"row\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("item%d", item.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"col offset-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(item.Item)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/counter/counter.templ`, Line: 37, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"col\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", item.Count))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/counter/counter.templ`, Line: 40, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"col-2\"><button hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("#" + fmt.Sprintf("item%d", item.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-include=\"[name='password']\" hx-swap=\"outerHTML\" hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(urlFor(user, item.Item, "decrement")))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">-</button> <button hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("#" + fmt.Sprintf("item%d", item.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-include=\"[name='password']\" hx-swap=\"outerHTML\" hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(urlFor(user, item.Item, "increment")))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">+</button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -4,12 +4,12 @@ package counter
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/velour/catbase/config"
|
||||
"github.com/velour/catbase/connectors/irc"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
|
@ -33,7 +33,7 @@ func makeMessage(payload string, r *regexp.Regexp) bot.Request {
|
|||
}
|
||||
values := bot.ParseValues(r, payload)
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Conn: irc.New(&config.Config{}),
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: "tester", ID: "id"},
|
||||
Body: payload,
|
||||
|
@ -280,6 +280,6 @@ func TestInspectMe(t *testing.T) {
|
|||
func TestHelp(t *testing.T) {
|
||||
mb, c := setup(t)
|
||||
assert.NotNil(t, c)
|
||||
c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
c.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Greater(t, len(mb.Messages), 1)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
func (p *Cowboy) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/img/{overlay}/{what}", p.handleImage)
|
||||
p.b.RegisterWeb(r, "/cowboy")
|
||||
p.b.GetWeb().RegisterWeb(r, "/cowboy")
|
||||
}
|
||||
|
||||
func (p *Cowboy) handleImage(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -21,7 +19,6 @@ func makeMessage(payload string) bot.Request {
|
|||
}
|
||||
values := bot.ParseValues(rollRegex, payload)
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Values: values,
|
||||
Msg: msg.Message{
|
||||
|
@ -67,6 +64,6 @@ func TestHelp(t *testing.T) {
|
|||
mb := bot.NewMockBot()
|
||||
c := New(mb)
|
||||
assert.NotNil(t, c)
|
||||
c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
c.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Len(t, mb.Messages, 1)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func (p *EmojyPlugin) registerWeb() {
|
|||
r.HandleFunc("/list", p.handlePage("list.html"))
|
||||
r.HandleFunc("/new", p.handlePage("upload.html"))
|
||||
r.HandleFunc("/", p.handleIndex)
|
||||
p.b.RegisterWebName(r, "/emojy", "Emojys")
|
||||
p.b.GetWeb().RegisterWebName(r, "/emojy", "Emojys")
|
||||
}
|
||||
|
||||
func (p *EmojyPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -19,7 +19,7 @@ func (p *FactoidPlugin) registerWeb() {
|
|||
r.HandleFunc("/api", p.serveAPI)
|
||||
r.HandleFunc("/req", p.serveQuery)
|
||||
r.HandleFunc("/", p.serveQuery)
|
||||
p.b.RegisterWebName(r, "/factoid", "Factoid")
|
||||
p.b.GetWeb().RegisterWebName(r, "/factoid", "Factoid")
|
||||
}
|
||||
|
||||
func linkify(text string) template.HTML {
|
||||
|
|
|
@ -81,5 +81,5 @@ func (p *GitPlugin) registerWeb() {
|
|||
r.HandleFunc("/gitea/event", p.giteaEvent)
|
||||
r.HandleFunc("/github/event", p.githubEvent)
|
||||
r.HandleFunc("/gitlab/event", p.gitlabEvent)
|
||||
p.b.RegisterWeb(r, "/git")
|
||||
p.b.GetWeb().RegisterWeb(r, "/git")
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ package leftpad
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -18,7 +16,6 @@ func makeMessage(payload string) bot.Request {
|
|||
values := bot.ParseValues(leftpadRegex, payload)
|
||||
return bot.Request{
|
||||
Kind: bot.Message,
|
||||
Conn: &cli.CliPlugin{},
|
||||
Values: values,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package meme
|
||||
|
||||
templ (p *MemePlugin) index(all webResps) {
|
||||
<div class="container">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<input type="text" name="name" placeholder="Name..." />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input type="text" name="url" placeholder="URL..." />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<textarea name="config">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button class="btn btn-primary"
|
||||
hx-post="/meme/add"
|
||||
hx-target="#newMemes"
|
||||
>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="newMemes">
|
||||
</div>
|
||||
for _, meme := range all {
|
||||
@p.Show(meme)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ (p *MemePlugin) Show(meme webResp) {
|
||||
<div class="row" id={ meme.Name }>
|
||||
<div class="col-3">
|
||||
{ meme.Name }
|
||||
<img
|
||||
class="img-thumbnail rounded"
|
||||
alt={ meme.Name }
|
||||
src={ meme.URL } />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<pre>
|
||||
{ meme.Config }
|
||||
</pre>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button class="btn btn-primary"
|
||||
hx-get={ "/meme/edit/"+meme.Name }
|
||||
hx-target={ "#"+meme.Name }
|
||||
>Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ (p *MemePlugin) Edit(meme webResp) {
|
||||
<form>
|
||||
<div class="row" id={ meme.Name }>
|
||||
<div class="col-3">
|
||||
<img
|
||||
class="img-thumbnail rounded"
|
||||
alt={ meme.Name }
|
||||
src={ meme.URL } />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<textarea name="config">
|
||||
{ meme.Config }
|
||||
</textarea>
|
||||
<input type="text" name="url" value={ meme.URL } />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button class="btn btn-primary"
|
||||
hx-put={ "/meme/save/"+meme.Name }
|
||||
hx-target={ "#"+meme.Name }
|
||||
>Save</button>
|
||||
<button class="btn btn-danger"
|
||||
hx-delete={ "/meme/rm/"+meme.Name }
|
||||
hx-target={ "#"+meme.Name }
|
||||
>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package meme
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func (p *MemePlugin) index(all webResps) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><form><div class=\"row\"><div class=\"col-3\"><input type=\"text\" name=\"name\" placeholder=\"Name...\"></div><div class=\"col-3\"><input type=\"text\" name=\"url\" placeholder=\"URL...\"></div><div class=\"col-3\"><textarea name=\"config\"></textarea></div><div class=\"col-3\"><button class=\"btn btn-primary\" hx-post=\"/meme/add\" hx-target=\"#newMemes\">Save</button></div></div></form><div id=\"newMemes\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, meme := range all {
|
||||
templ_7745c5c3_Err = p.Show(meme).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *MemePlugin) Show(meme webResp) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"row\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"col-3\"><img class=\"img-thumbnail rounded\" alt=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.URL))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(meme.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/meme/meme.templ`, Line: 39, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"col-3\"><pre>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(meme.Config)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/meme/meme.templ`, Line: 43, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</pre></div><div class=\"col-3\"><button class=\"btn btn-primary\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("/meme/edit/" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("#" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Edit</button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *MemePlugin) Edit(meme webResp) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var5 == nil {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form><div class=\"row\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"col-3\"><img class=\"img-thumbnail rounded\" alt=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.URL))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"col-3\"><textarea name=\"config\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(meme.Config)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/meme/meme.templ`, Line: 66, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</textarea> <input type=\"text\" name=\"url\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(meme.URL))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></div><div class=\"col-3\"><button class=\"btn btn-primary\" hx-put=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("/meme/save/" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("#" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Save</button> <button class=\"btn btn-danger\" hx-delete=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("/meme/rm/" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("#" + meme.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Delete</button></div></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -20,12 +20,13 @@ var embeddedFS embed.FS
|
|||
func (p *MemePlugin) registerWeb(c bot.Connector) {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/slash", p.slashMeme(c))
|
||||
r.HandleFunc("/img", p.img)
|
||||
r.HandleFunc("/all", p.all)
|
||||
r.HandleFunc("/add", p.addMeme)
|
||||
r.HandleFunc("/rm", p.rmMeme)
|
||||
r.HandleFunc("/", p.webRoot)
|
||||
p.bot.RegisterWebName(r, "/meme", "Memes")
|
||||
r.Get("/img", p.img)
|
||||
r.Put("/save/{name}", p.saveMeme)
|
||||
r.Post("/add", p.saveMeme)
|
||||
r.Delete("/rm/{name}", p.rmMeme)
|
||||
r.Get("/edit/{name}", p.editMeme)
|
||||
r.Get("/", p.webRoot)
|
||||
p.bot.GetWeb().RegisterWebName(r, "/meme", "Memes")
|
||||
}
|
||||
|
||||
type webResp struct {
|
||||
|
@ -43,7 +44,7 @@ type ByName struct{ webResps }
|
|||
|
||||
func (s ByName) Less(i, j int) bool { return s.webResps[i].Name < s.webResps[j].Name }
|
||||
|
||||
func (p *MemePlugin) all(w http.ResponseWriter, r *http.Request) {
|
||||
func (p *MemePlugin) all() webResps {
|
||||
memes := p.c.GetMap("meme.memes", defaultFormats)
|
||||
configs := p.c.GetMap("meme.memeconfigs", map[string]string{})
|
||||
|
||||
|
@ -51,7 +52,7 @@ func (p *MemePlugin) all(w http.ResponseWriter, r *http.Request) {
|
|||
for n, u := range memes {
|
||||
config, ok := configs[n]
|
||||
if !ok {
|
||||
b, _ := json.Marshal(p.defaultFormatConfig())
|
||||
b, _ := json.MarshalIndent(p.defaultFormatConfig(), " ", " ")
|
||||
config = string(b)
|
||||
}
|
||||
realURL, err := url.Parse(u)
|
||||
|
@ -64,13 +65,7 @@ func (p *MemePlugin) all(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
sort.Sort(ByName{values})
|
||||
|
||||
out, err := json.Marshal(values)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
log.Error().Err(err).Msgf("could not serve all memes route")
|
||||
return
|
||||
}
|
||||
w.Write(out)
|
||||
return values
|
||||
}
|
||||
|
||||
func mkCheckError(w http.ResponseWriter) func(error) bool {
|
||||
|
@ -87,56 +82,61 @@ func mkCheckError(w http.ResponseWriter) func(error) bool {
|
|||
}
|
||||
|
||||
func (p *MemePlugin) rmMeme(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(405)
|
||||
fmt.Fprintf(w, "Incorrect HTTP method")
|
||||
return
|
||||
}
|
||||
checkError := mkCheckError(w)
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
values := webResp{}
|
||||
err := decoder.Decode(&values)
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
name := chi.URLParam(r, "name")
|
||||
formats := p.c.GetMap("meme.memes", defaultFormats)
|
||||
delete(formats, values.Name)
|
||||
err = p.c.SetMap("meme.memes", formats)
|
||||
checkError(err)
|
||||
delete(formats, name)
|
||||
err := p.c.SetMap("meme.memes", formats)
|
||||
mkCheckError(w)(err)
|
||||
}
|
||||
|
||||
func (p *MemePlugin) addMeme(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(405)
|
||||
fmt.Fprintf(w, "Incorrect HTTP method")
|
||||
return
|
||||
func (p *MemePlugin) saveMeme(w http.ResponseWriter, r *http.Request) {
|
||||
name := chi.URLParam(r, "name")
|
||||
if name == "" {
|
||||
name = r.FormValue("name")
|
||||
}
|
||||
checkError := mkCheckError(w)
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
values := webResp{}
|
||||
err := decoder.Decode(&values)
|
||||
if checkError(err) {
|
||||
log.Error().Err(err).Msgf("could not decode body")
|
||||
return
|
||||
}
|
||||
log.Debug().Msgf("POSTed values: %+v", values)
|
||||
|
||||
formats := p.c.GetMap("meme.memes", defaultFormats)
|
||||
formats[values.Name] = values.URL
|
||||
err = p.c.SetMap("meme.memes", formats)
|
||||
formats[name] = r.FormValue("url")
|
||||
err := p.c.SetMap("meme.memes", formats)
|
||||
checkError(err)
|
||||
|
||||
if values.Config == "" {
|
||||
values.Config = p.defaultFormatConfigJSON()
|
||||
config := r.FormValue("config")
|
||||
if config == "" {
|
||||
config = p.defaultFormatConfigJSON()
|
||||
}
|
||||
configs := p.c.GetMap("meme.memeconfigs", map[string]string{})
|
||||
configs[values.Name] = values.Config
|
||||
configs[name] = config
|
||||
err = p.c.SetMap("meme.memeconfigs", configs)
|
||||
checkError(err)
|
||||
|
||||
meme := webResp{
|
||||
Name: name,
|
||||
URL: formats[name],
|
||||
Config: configs[name],
|
||||
}
|
||||
|
||||
p.Show(meme).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (p *MemePlugin) webRoot(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("index.html")
|
||||
w.Write(index)
|
||||
p.bot.GetWeb().Index("Meme", p.index(p.all())).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (p *MemePlugin) editMeme(w http.ResponseWriter, r *http.Request) {
|
||||
name := chi.URLParam(r, "name")
|
||||
memes := p.c.GetMap("meme.memes", defaultFormats)
|
||||
configs := p.c.GetMap("meme.memeconfigs", map[string]string{})
|
||||
meme, ok := memes[name]
|
||||
if !ok {
|
||||
fmt.Fprintf(w, "Didn't find that meme.")
|
||||
}
|
||||
resp := webResp{
|
||||
Name: name,
|
||||
URL: meme,
|
||||
Config: configs[name],
|
||||
}
|
||||
p.Edit(resp).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (p *MemePlugin) img(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -12,8 +12,6 @@ import (
|
|||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
|
@ -45,7 +43,6 @@ func makeMessage(payload string) bot.Request {
|
|||
payload = payload[1:]
|
||||
}
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
|
|
|
@ -18,7 +18,7 @@ type PageComment struct {
|
|||
c *config.Config
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *PageComment {
|
||||
func New(b bot.Bot) bot.Plugin {
|
||||
p := &PageComment{
|
||||
b: b,
|
||||
c: b.Config(),
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
"github.com/velour/catbase/bot/user"
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
)
|
||||
|
||||
func makeMessage(payload string) bot.Request {
|
||||
|
@ -22,7 +21,6 @@ func makeMessage(payload string) bot.Request {
|
|||
}
|
||||
values := bot.ParseValues(pickRegex, payload)
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Values: values,
|
||||
Msg: msg.Message{
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -20,7 +18,6 @@ func makeMessage(nick, payload string, r *regexp.Regexp) bot.Request {
|
|||
payload = payload[1:]
|
||||
}
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Values: bot.ParseValues(r, payload),
|
||||
Msg: msg.Message{
|
||||
|
|
|
@ -4,7 +4,6 @@ package reminder
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -24,7 +23,7 @@ func makeMessageBy(payload, by string) (bot.Connector, bot.Kind, msg.Message) {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return &cli.CliPlugin{}, bot.Message, msg.Message{
|
||||
return nil, bot.Message, msg.Message{
|
||||
User: &user.User{Name: by},
|
||||
Channel: "test",
|
||||
Body: payload,
|
||||
|
@ -224,6 +223,6 @@ func TestLimitList(t *testing.T) {
|
|||
func TestHelp(t *testing.T) {
|
||||
c, mb := setup(t)
|
||||
assert.NotNil(t, c)
|
||||
c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
c.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Len(t, mb.Messages, 1)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type RolesPlugin struct {
|
|||
h bot.HandlerTable
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *RolesPlugin {
|
||||
func New(b bot.Bot) bot.Plugin {
|
||||
p := &RolesPlugin{
|
||||
b: b,
|
||||
c: b.Config(),
|
||||
|
|
|
@ -2,7 +2,6 @@ package rss
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -17,7 +16,7 @@ func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return &cli.CliPlugin{}, bot.Message, msg.Message{
|
||||
return nil, bot.Message, msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
Channel: "test",
|
||||
Body: payload,
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
<!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>Memes</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>
|
||||
<b-form @submit="add">
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col cols="3">
|
||||
<b-input placeholder="Key..." v-model="secret.key"></b-input>
|
||||
</b-col>
|
||||
<b-col cols="3">
|
||||
<b-input placeholder="Value..." v-model="secret.value"></b-input>
|
||||
</b-col>
|
||||
<b-col cols="3">
|
||||
<b-button type="submit">Add Secret</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row style="padding-top: 2em;">
|
||||
<b-col>
|
||||
<ul>
|
||||
<li v-for="key in results" key="key"><a @click="rm(key)" href="#">X</a> {{key}}</li>
|
||||
</ul>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes: []
|
||||
});
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
data: {
|
||||
err: '',
|
||||
nav: [],
|
||||
secret: {key: '', value: ''},
|
||||
results: [],
|
||||
fields: [
|
||||
{key: 'key', sortable: true},
|
||||
]
|
||||
},
|
||||
mounted() {
|
||||
axios.get('/nav')
|
||||
.then(resp => {
|
||||
this.nav = resp.data;
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
this.refresh();
|
||||
},
|
||||
methods: {
|
||||
refresh: function () {
|
||||
axios.get('/secrets/all')
|
||||
.then(resp => {
|
||||
this.results = resp.data
|
||||
this.err = ''
|
||||
})
|
||||
.catch(err => (this.err = err))
|
||||
},
|
||||
add: function (evt) {
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
}
|
||||
axios.post('/secrets/add', this.secret)
|
||||
.then(resp => {
|
||||
this.results = resp.data;
|
||||
this.secret.key = '';
|
||||
this.secret.value = '';
|
||||
this.refresh();
|
||||
})
|
||||
.catch(err => this.err = err)
|
||||
},
|
||||
rm: function (key) {
|
||||
if (confirm("Are you sure you want to delete this meme?")) {
|
||||
axios.delete('/secrets/remove', {data: {key: key}})
|
||||
.then(resp => {
|
||||
this.refresh();
|
||||
})
|
||||
.catch(err => this.err = err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,28 +1,25 @@
|
|||
package secrets
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/config"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var embeddedFS embed.FS
|
||||
|
||||
type SecretsPlugin struct {
|
||||
b bot.Bot
|
||||
c *config.Config
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *SecretsPlugin {
|
||||
func New(b bot.Bot) bot.Plugin {
|
||||
p := &SecretsPlugin{
|
||||
b: b,
|
||||
c: b.Config(),
|
||||
|
@ -36,14 +33,8 @@ func (p *SecretsPlugin) registerWeb() {
|
|||
r := chi.NewRouter()
|
||||
r.HandleFunc("/add", p.handleRegister)
|
||||
r.HandleFunc("/remove", p.handleRemove)
|
||||
r.HandleFunc("/all", p.handleAll)
|
||||
r.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
|
||||
value := r.URL.Query().Get("test")
|
||||
j, _ := json.Marshal(map[string]string{"value": value})
|
||||
w.Write(j)
|
||||
})
|
||||
r.HandleFunc("/", p.handleIndex)
|
||||
p.b.RegisterWebName(r, "/secrets", "Secrets")
|
||||
p.b.GetWeb().RegisterWebName(r, "/secrets", "Secrets")
|
||||
}
|
||||
|
||||
func mkCheckError(w http.ResponseWriter) func(error) bool {
|
||||
|
@ -68,20 +59,12 @@ func checkMethod(method string, w http.ResponseWriter, r *http.Request) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (p *SecretsPlugin) keys() []string {
|
||||
return p.c.SecretKeys()
|
||||
}
|
||||
|
||||
func (p *SecretsPlugin) sendKeys(w http.ResponseWriter, r *http.Request) {
|
||||
checkError := mkCheckError(w)
|
||||
log.Debug().Msgf("Keys before refresh: %v", p.c.SecretKeys())
|
||||
err := p.c.RefreshSecrets()
|
||||
log.Debug().Msgf("Keys after refresh: %v", p.c.SecretKeys())
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
keys, err := json.Marshal(p.c.SecretKeys())
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
w.Write(keys)
|
||||
p.keysList().Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -89,21 +72,13 @@ func (p *SecretsPlugin) handleAll(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (p *SecretsPlugin) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
log.Debug().Msgf("handleRegister")
|
||||
if checkMethod(http.MethodPost, w, r) {
|
||||
log.Debug().Msgf("failed post %s", r.Method)
|
||||
return
|
||||
}
|
||||
checkError := mkCheckError(w)
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
secret := config.Secret{}
|
||||
err := decoder.Decode(&secret)
|
||||
log.Debug().Msgf("decoding: %s", err)
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
log.Debug().Msgf("Secret: %s", secret)
|
||||
err = p.c.RegisterSecret(secret.Key, secret.Value)
|
||||
key, value := r.FormValue("key"), r.FormValue("value")
|
||||
err := p.c.RegisterSecret(key, value)
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
|
@ -115,13 +90,16 @@ func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
checkError := mkCheckError(w)
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
secret := config.Secret{}
|
||||
err := decoder.Decode(&secret)
|
||||
b, err := io.ReadAll(r.Body)
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
err = p.c.RemoveSecret(secret.Key)
|
||||
q, err := url.ParseQuery(string(b))
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
secret := q.Get("key")
|
||||
err = p.c.RemoveSecret(secret)
|
||||
if checkError(err) {
|
||||
return
|
||||
}
|
||||
|
@ -129,6 +107,5 @@ func (p *SecretsPlugin) handleRemove(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (p *SecretsPlugin) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("index.html")
|
||||
w.Write(index)
|
||||
p.b.GetWeb().Index("Secrets", p.index()).Render(r.Context(), w)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package secrets
|
||||
|
||||
import "fmt"
|
||||
|
||||
templ (s *SecretsPlugin) index() {
|
||||
<div class="container">
|
||||
<form hx-post="/secrets/add" hx-target="#data">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<input placeholder="Key..." name="key" />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input placeholder="Value..." name="value" />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button class="btn btn-primary" type="submit">Add Secret</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row" style="padding-top: 2em;">
|
||||
<div id="data">
|
||||
@s.keysList()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ (s *SecretsPlugin) keysList() {
|
||||
<ul>
|
||||
for _, key := range s.keys() {
|
||||
<li>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
hx-delete="/secrets/remove"
|
||||
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %s?", key) }
|
||||
hx-target="#data"
|
||||
hx-include="this"
|
||||
name="key" value={ key }>X</button>
|
||||
{ key }</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package secrets
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (s *SecretsPlugin) index() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><form hx-post=\"/secrets/add\" hx-target=\"#data\"><div class=\"row\"><div class=\"col-3\"><input placeholder=\"Key...\" name=\"key\"></div><div class=\"col-3\"><input placeholder=\"Value...\" name=\"value\"></div><div class=\"col-3\"><button class=\"btn btn-primary\" type=\"submit\">Add Secret</button></div></div></form><div class=\"row\" style=\"padding-top: 2em;\"><div id=\"data\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = s.keysList().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SecretsPlugin) keysList() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, key := range s.keys() {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><button class=\"btn btn-danger\" hx-delete=\"/secrets/remove\" hx-confirm=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("Are you sure you want to delete %s?", key)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" hx-include=\"this\" name=\"key\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(key))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">X</button> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(key)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/secrets/secrets.templ`, Line: 38, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -121,7 +121,7 @@ func (p *SMSPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, ar
|
|||
func (p *SMSPlugin) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/new", p.receive)
|
||||
p.b.RegisterWeb(r, "/sms")
|
||||
p.b.GetWeb().RegisterWeb(r, "/sms")
|
||||
}
|
||||
|
||||
func (p *SMSPlugin) receive(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package stock
|
||||
|
||||
import (
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -16,7 +15,7 @@ func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return &cli.CliPlugin{}, bot.Message, msg.Message{
|
||||
return nil, bot.Message, msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
Channel: "test",
|
||||
Body: payload,
|
||||
|
|
|
@ -186,5 +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")
|
||||
p.bot.GetWeb().RegisterWeb(r, "/cowsay")
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package talker
|
||||
|
||||
import (
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -18,7 +17,7 @@ func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return &cli.CliPlugin{}, bot.Message, msg.Message{
|
||||
return nil, bot.Message, msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
Channel: "test",
|
||||
Body: payload,
|
||||
|
@ -79,6 +78,6 @@ func TestHelp(t *testing.T) {
|
|||
mb := bot.NewMockBot()
|
||||
c := New(mb)
|
||||
assert.NotNil(t, c)
|
||||
c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
c.help(nil, bot.Help, msg.Message{Channel: "channel"}, []string{})
|
||||
assert.Len(t, mb.Messages, 1)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
func (p *Tappd) registerWeb() {
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/", p.serveImage)
|
||||
p.b.RegisterWeb(r, "/tappd/{id}")
|
||||
p.b.GetWeb().RegisterWeb(r, "/tappd/{id}")
|
||||
}
|
||||
|
||||
func (p *Tappd) getImg(id string) ([]byte, error) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package tldr
|
||||
|
||||
import (
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -29,7 +28,6 @@ func makeMessageBy(payload, by string) bot.Request {
|
|||
}
|
||||
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: by},
|
||||
|
|
|
@ -102,7 +102,7 @@ func (p *Twitch) registerWeb() {
|
|||
r := chi.NewRouter()
|
||||
r.HandleFunc("/online", p.onlineCB)
|
||||
r.HandleFunc("/offline", p.offlineCB)
|
||||
p.b.RegisterWeb(r, "/twitch")
|
||||
p.b.GetWeb().RegisterWeb(r, "/twitch")
|
||||
}
|
||||
|
||||
func (p *Twitch) register() {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -29,7 +28,7 @@ func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
|
|||
if isCmd {
|
||||
payload = payload[1:]
|
||||
}
|
||||
return &cli.CliPlugin{}, bot.Message, msg.Message{
|
||||
return nil, bot.Message, msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
Channel: "test",
|
||||
Body: payload,
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/velour/catbase/plugins/cli"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
|
@ -20,7 +18,6 @@ func makeMessage(payload string) bot.Request {
|
|||
payload = payload[1:]
|
||||
}
|
||||
return bot.Request{
|
||||
Conn: &cli.CliPlugin{},
|
||||
Kind: bot.Message,
|
||||
Msg: msg.Message{
|
||||
User: &user.User{Name: "tester"},
|
||||
|
|
Loading…
Reference in New Issue