mirror of https://github.com/velour/catbase.git
Merge branch 'master' into dependabot/go_modules/github.com/antchfx/xmlquery-1.3.1
This commit is contained in:
commit
3fe4eda22b
|
@ -147,6 +147,8 @@ func (b *bot) setupHTTP() {
|
|||
|
||||
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() {
|
||||
|
|
|
@ -26,6 +26,8 @@ const (
|
|||
Reply
|
||||
// Action any /me action
|
||||
Action
|
||||
// Spoiler is for commented out messages
|
||||
Spoiler
|
||||
// Reaction Icon reaction if service supports it
|
||||
Reaction
|
||||
// Edit message ref'd new message to replace
|
||||
|
|
|
@ -37,6 +37,9 @@ func (mb *MockBot) GetPassword() string { return "12345" }
|
|||
func (mb *MockBot) SetQuiet(bool) {}
|
||||
func (mb *MockBot) Send(c Connector, kind Kind, args ...any) (string, error) {
|
||||
switch kind {
|
||||
case Spoiler:
|
||||
mb.Messages = append(mb.Messages, "||"+args[1].(string)+"||")
|
||||
return fmt.Sprintf("m-%d", len(mb.Actions)-1), nil
|
||||
case Message:
|
||||
mb.Messages = append(mb.Messages, args[1].(string))
|
||||
return fmt.Sprintf("m-%d", len(mb.Actions)-1), nil
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<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>
|
17
bot/web.go
17
bot/web.go
|
@ -3,8 +3,12 @@ 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
|
||||
|
@ -15,6 +19,19 @@ func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) {
|
|||
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())
|
||||
|
|
|
@ -74,6 +74,9 @@ func (d Discord) Send(kind bot.Kind, args ...any) (string, error) {
|
|||
return d.sendMessage(args[0].(string), args[2].(string), false, args...)
|
||||
case bot.Message:
|
||||
return d.sendMessage(args[0].(string), args[1].(string), false, args...)
|
||||
case bot.Spoiler:
|
||||
outgoing := "||" + args[1].(string) + "||"
|
||||
return d.sendMessage(args[0].(string), outgoing, false, args...)
|
||||
case bot.Action:
|
||||
return d.sendMessage(args[0].(string), args[1].(string), true, args...)
|
||||
case bot.Edit:
|
||||
|
@ -151,7 +154,27 @@ func (d *Discord) sendMessage(channel, message string, meMessage bool, args ...a
|
|||
Interface("data", data).
|
||||
Msg("sending message")
|
||||
|
||||
st, err := d.client.ChannelMessageSendComplex(channel, data)
|
||||
maxLen := 2000
|
||||
chunkSize := maxLen - 100
|
||||
var st *discordgo.Message
|
||||
var err error
|
||||
if len(data.Content) > maxLen {
|
||||
tmp := data.Content
|
||||
data.Content = tmp[:chunkSize]
|
||||
st, err = d.client.ChannelMessageSendComplex(channel, data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := chunkSize; i < len(data.Content); i += chunkSize {
|
||||
data := &discordgo.MessageSend{Content: tmp[i : i+chunkSize]}
|
||||
st, err = d.client.ChannelMessageSendComplex(channel, data)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
st, err = d.client.ChannelMessageSendComplex(channel, data)
|
||||
}
|
||||
|
||||
//st, err := d.client.ChannelMessageSend(channel, message)
|
||||
if err != nil {
|
||||
|
|
4
main.go
4
main.go
|
@ -135,7 +135,6 @@ func main() {
|
|||
b.AddPlugin(roles.New(b))
|
||||
b.AddPlugin(twitch.New(b))
|
||||
b.AddPlugin(pagecomment.New(b))
|
||||
b.AddPlugin(gpt.New(b))
|
||||
b.AddPlugin(secrets.New(b))
|
||||
b.AddPlugin(mayi.New(b))
|
||||
b.AddPlugin(giphy.New(b))
|
||||
|
@ -179,8 +178,9 @@ func main() {
|
|||
b.AddPlugin(cowboy.New(b))
|
||||
b.AddPlugin(topic.New(b))
|
||||
b.AddPlugin(talker.New(b))
|
||||
// catches anything left, will always return true
|
||||
b.AddPlugin(fact.New(b))
|
||||
// catches anything left, will always return true
|
||||
b.AddPlugin(gpt.New(b))
|
||||
|
||||
if err := client.Serve(); err != nil {
|
||||
log.Fatal().Err(err)
|
||||
|
|
|
@ -1,77 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<!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>
|
||||
<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 id="app">
|
||||
<b-navbar>
|
||||
<b-navbar-brand>Variables</b-navbar-brand>
|
||||
<b-navbar-nav>
|
||||
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Variables'">{{ item.name }}
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-navbar>
|
||||
<b-alert
|
||||
dismissable
|
||||
variant="error"
|
||||
v-if="err"
|
||||
@dismissed="err = ''">
|
||||
{{ err }}
|
||||
</b-alert>
|
||||
<b-container>
|
||||
<b-table
|
||||
fixed
|
||||
:items="vars"
|
||||
:sort-by.sync="sortBy"
|
||||
:fields="fields"></b-table>
|
||||
</b-container>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
err: '',
|
||||
nav: [],
|
||||
vars: [],
|
||||
sortBy: 'key',
|
||||
fields: [
|
||||
{key: {sortable: true}},
|
||||
'value'
|
||||
]
|
||||
},
|
||||
mounted() {
|
||||
this.getData();
|
||||
axios.get('/nav')
|
||||
.then(resp => {
|
||||
this.nav = resp.data;
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
},
|
||||
methods: {
|
||||
getData: function () {
|
||||
axios.get('/vars/api')
|
||||
.then(resp => {
|
||||
this.vars = resp.data;
|
||||
})
|
||||
.catch(err => this.err = err);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<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>
|
|
@ -6,6 +6,7 @@ import (
|
|||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -166,9 +167,32 @@ func writeErr(w http.ResponseWriter, err error) {
|
|||
fmt.Fprint(w, string(j))
|
||||
}
|
||||
|
||||
type configEntry struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (p *AdminPlugin) handleVars(w http.ResponseWriter, r *http.Request) {
|
||||
index, _ := embeddedFS.ReadFile("vars.html")
|
||||
w.Write(index)
|
||||
tpl := template.Must(template.ParseFS(embeddedFS, "vars.html"))
|
||||
var configEntries []configEntry
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -5,8 +5,9 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/velour/catbase/bot/user"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -29,8 +30,8 @@ func (p *CounterPlugin) registerWeb() {
|
|||
subrouter.Use(httprate.LimitByIP(requests, dur))
|
||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/increment/{delta}", p.mkIncrementByNAPI(1))
|
||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/decrement/{delta}", p.mkIncrementByNAPI(-1))
|
||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/increment", p.mkIncrementAPI(1))
|
||||
subrouter.HandleFunc("/api/users/{user}/items/{item}/decrement", p.mkIncrementAPI(-1))
|
||||
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("/", p.handleCounter)
|
||||
|
@ -39,9 +40,14 @@ func (p *CounterPlugin) registerWeb() {
|
|||
|
||||
func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userName := chi.URLParam(r, "user")
|
||||
itemName := chi.URLParam(r, "item")
|
||||
delta, _ := strconv.Atoi(chi.URLParam(r, "delta"))
|
||||
userName, _ := url.QueryUnescape(chi.URLParam(r, "user"))
|
||||
itemName, _ := url.QueryUnescape(chi.URLParam(r, "item"))
|
||||
delta, err := strconv.Atoi(chi.URLParam(r, "delta"))
|
||||
if err != nil || delta == 0 {
|
||||
delta = direction
|
||||
} else {
|
||||
delta = delta * direction
|
||||
}
|
||||
|
||||
secret, pass, ok := r.BasicAuth()
|
||||
if !ok || !p.b.CheckPassword(secret, pass) {
|
||||
|
@ -77,7 +83,7 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
return
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
postData := map[string]string{}
|
||||
err = json.Unmarshal(body, &postData)
|
||||
personalMsg := ""
|
||||
|
@ -85,7 +91,11 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
personalMsg = fmt.Sprintf("\nMessage: %s", inputMsg)
|
||||
}
|
||||
|
||||
chs := p.cfg.GetArray("channels", []string{p.cfg.Get("channels", "none")})
|
||||
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,
|
||||
|
@ -93,7 +103,7 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
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: chs[0],
|
||||
Channel: ch,
|
||||
Body: fmt.Sprintf("%s += %d", itemName, delta),
|
||||
Time: time.Now(),
|
||||
},
|
||||
|
@ -102,7 +112,13 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
}
|
||||
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)
|
||||
for _, ch := range chs {
|
||||
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
|
||||
}
|
||||
|
@ -112,80 +128,6 @@ func (p *CounterPlugin) mkIncrementByNAPI(direction int) func(w http.ResponseWri
|
|||
}
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) mkIncrementAPI(delta int) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userName := chi.URLParam(r, "user")
|
||||
itemName := chi.URLParam(r, "item")
|
||||
|
||||
secret, pass, ok := r.BasicAuth()
|
||||
if !ok || !p.b.CheckPassword(secret, pass) {
|
||||
err := fmt.Errorf("unauthorized access")
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("error authenticating user")
|
||||
w.WriteHeader(401)
|
||||
j, _ := json.Marshal(struct {
|
||||
Status bool
|
||||
Error string
|
||||
}{false, err.Error()})
|
||||
fmt.Fprint(w, string(j))
|
||||
return
|
||||
}
|
||||
|
||||
// Try to find an ID if possible
|
||||
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 {
|
||||
log.Error().Err(err).Msg("error finding item")
|
||||
w.WriteHeader(400)
|
||||
j, _ := json.Marshal(struct {
|
||||
Status bool
|
||||
Error error
|
||||
}{false, err})
|
||||
fmt.Fprint(w, string(j))
|
||||
return
|
||||
}
|
||||
|
||||
body, _ := ioutil.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.GetArray("channels", []string{p.cfg.Get("channels", "none")})
|
||||
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: chs[0],
|
||||
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"), personalMsg)
|
||||
for _, ch := range chs {
|
||||
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
|
||||
req.Msg.Channel = ch
|
||||
}
|
||||
item.UpdateDelta(req, delta)
|
||||
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)
|
||||
|
|
|
@ -90,21 +90,8 @@ func New(botInst bot.Bot) *FactoidPlugin {
|
|||
|
||||
// findAction simply regexes a string for the action verb
|
||||
func findAction(message string) string {
|
||||
r, err := regexp.Compile("<.+?>")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
action := r.FindString(message)
|
||||
|
||||
if action == "" {
|
||||
if strings.Contains(message, " is ") {
|
||||
return "is"
|
||||
} else if strings.Contains(message, " are ") {
|
||||
return "are"
|
||||
}
|
||||
}
|
||||
|
||||
return action
|
||||
r := regexp.MustCompile("<.+?>")
|
||||
return r.FindString(message)
|
||||
}
|
||||
|
||||
// learnFact assumes we have a learning situation and inserts a new fact
|
||||
|
@ -157,7 +144,6 @@ func (p *FactoidPlugin) findTrigger(fact string) (bool, *Factoid) {
|
|||
|
||||
f, err := GetSingleFact(p.db, fact)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("GetSingleFact")
|
||||
return findAlias(p.db, fact)
|
||||
}
|
||||
return true, f
|
||||
|
@ -485,18 +471,7 @@ func (p *FactoidPlugin) register() {
|
|||
return true
|
||||
}
|
||||
|
||||
notFound := p.c.GetArray("fact.notfound", []string{
|
||||
"I don't know.",
|
||||
"NONONONO",
|
||||
"((",
|
||||
"*pukes*",
|
||||
"NOPE! NOPE! NOPE!",
|
||||
"One time, I learned how to jump rope.",
|
||||
})
|
||||
|
||||
// We didn't find anything, panic!
|
||||
p.b.Send(c, bot.Message, message.Channel, notFound[rand.Intn(len(notFound))])
|
||||
return true
|
||||
return false
|
||||
}},
|
||||
}
|
||||
p.b.RegisterTable(p, p.handlers)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
import "github.com/andrewstuart/openai"
|
||||
|
||||
var session *openai.ChatSession
|
||||
var session openai.ChatSession
|
||||
var client *openai.Client
|
||||
|
||||
func (p *GPTPlugin) getClient() (*openai.Client, error) {
|
||||
|
@ -14,31 +14,37 @@ func (p *GPTPlugin) getClient() (*openai.Client, error) {
|
|||
if token == "" {
|
||||
return nil, fmt.Errorf("no GPT token given")
|
||||
}
|
||||
if client == nil {
|
||||
return openai.NewClient(token)
|
||||
}
|
||||
return client, nil
|
||||
return openai.NewClient(token)
|
||||
}
|
||||
|
||||
func (p *GPTPlugin) chatGPT(request string) (string, error) {
|
||||
if session == nil {
|
||||
if err := p.setDefaultPrompt(); err != nil {
|
||||
if client == nil {
|
||||
if err := p.setPrompt(p.getDefaultPrompt()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if p.chatCount > p.c.GetInt("gpt.maxchats", 10) {
|
||||
p.setPrompt(p.c.Get("gpt3.lastprompt", p.getDefaultPrompt()))
|
||||
p.chatCount = 0
|
||||
}
|
||||
p.chatCount++
|
||||
return session.Complete(context.Background(), request)
|
||||
}
|
||||
|
||||
func (p *GPTPlugin) setDefaultPrompt() error {
|
||||
return p.setPrompt(p.c.Get("gpt.prompt", ""))
|
||||
func (p *GPTPlugin) getDefaultPrompt() string {
|
||||
return p.c.Get("gpt.prompt", "")
|
||||
}
|
||||
|
||||
func (p *GPTPlugin) setPrompt(prompt string) error {
|
||||
client, err := p.getClient()
|
||||
var err error
|
||||
client, err = p.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session = client.NewChatSession(prompt)
|
||||
err = p.c.Set("gpt3.lastprompt", prompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sess := client.NewChatSession(prompt)
|
||||
session = &sess
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ type GPTPlugin struct {
|
|||
b bot.Bot
|
||||
c *config.Config
|
||||
h bot.HandlerTable
|
||||
|
||||
chatCount int
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *GPTPlugin {
|
||||
|
@ -54,6 +56,11 @@ func (p *GPTPlugin) register() {
|
|||
HelpText: "set the ChatGPT prompt",
|
||||
Handler: p.setPromptMessage,
|
||||
},
|
||||
{
|
||||
Kind: bot.Message, IsCmd: true,
|
||||
Regex: regexp.MustCompile(`(?P<text>.*)`),
|
||||
Handler: p.chatMessage,
|
||||
},
|
||||
}
|
||||
log.Debug().Msg("Registering GPT3 handlers")
|
||||
p.b.RegisterTable(p, p.h)
|
||||
|
@ -96,7 +103,7 @@ func (p *GPTPlugin) gpt3(stem string) string {
|
|||
TopP: p.c.GetFloat64("gpt3.top_p", 1),
|
||||
N: p.c.GetInt("gpt3.n", 1),
|
||||
Stop: p.c.GetArray("gpt3.stop", []string{"\n"}),
|
||||
Echo: true,
|
||||
Echo: p.c.GetBool("gpt3.echo", false),
|
||||
}
|
||||
val, err := p.mkRequest(gpt3URL, postStruct)
|
||||
if err != nil {
|
||||
|
|
|
@ -329,7 +329,7 @@ func FindFontSize(c *config.Config, config []string, fontLocation string, w, h i
|
|||
longestStr, longestW := "", 0.0
|
||||
|
||||
for _, s := range config {
|
||||
err := m.LoadFontFace(getFont(c, fontLocation), 12)
|
||||
err := m.LoadFontFace(GetFont(c, fontLocation), 12)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not load font")
|
||||
return fontSize
|
||||
|
@ -343,7 +343,7 @@ func FindFontSize(c *config.Config, config []string, fontLocation string, w, h i
|
|||
}
|
||||
|
||||
for _, sz := range sizes {
|
||||
err := m.LoadFontFace(getFont(c, fontLocation), sz) // problem
|
||||
err := m.LoadFontFace(GetFont(c, fontLocation), sz) // problem
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not load font")
|
||||
return fontSize
|
||||
|
@ -476,7 +476,7 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
if fontLocation == "" {
|
||||
fontLocation = defaultFont
|
||||
}
|
||||
m.LoadFontFace(getFont(p.c, fontLocation), fontSize)
|
||||
m.LoadFontFace(GetFont(p.c, fontLocation), fontSize)
|
||||
x := float64(w)*c.XPerc + float64(dx)
|
||||
y := float64(h)*c.YPerc + float64(dy)
|
||||
m.DrawStringAnchored(c.Text, x, y, 0.5, 0.5)
|
||||
|
@ -491,7 +491,7 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
if fontLocation == "" {
|
||||
fontLocation = defaultFont
|
||||
}
|
||||
m.LoadFontFace(getFont(p.c, fontLocation), fontSize)
|
||||
m.LoadFontFace(GetFont(p.c, fontLocation), fontSize)
|
||||
x := float64(w) * c.XPerc
|
||||
y := float64(h) * c.YPerc
|
||||
m.DrawStringAnchored(c.Text, x, y, 0.5, 0.5)
|
||||
|
@ -506,7 +506,7 @@ func (p *MemePlugin) genMeme(spec specification) ([]byte, error) {
|
|||
return p.images[jsonSpec].repr, nil
|
||||
}
|
||||
|
||||
func getFont(c *config.Config, name string) string {
|
||||
func GetFont(c *config.Config, name string) string {
|
||||
location := c.Get("meme.fontLocation", "")
|
||||
fontShortcuts := c.GetMap("meme.fontShortcuts", map[string]string{"impact": "impact.ttf"})
|
||||
if file, ok := fontShortcuts[name]; ok {
|
||||
|
|
|
@ -120,7 +120,6 @@ func (p *ReminderPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mes
|
|||
channel := message.Channel
|
||||
from := message.User.Name
|
||||
|
||||
message.Body = replaceDuration(p.when, message.Body)
|
||||
parts := strings.Fields(message.Body)
|
||||
|
||||
if len(parts) >= 5 {
|
||||
|
|
|
@ -104,7 +104,7 @@ func (p *TalkerPlugin) message(c bot.Connector, kind bot.Kind, message msg.Messa
|
|||
line = strings.Replace(line, "{nick}", nick, 1)
|
||||
output += line + "\n"
|
||||
}
|
||||
p.bot.Send(c, bot.Message, channel, output)
|
||||
p.bot.Send(c, bot.Spoiler, channel, output)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func defaultSpec() textSpec {
|
|||
}
|
||||
|
||||
func (p *Tappd) overlay(img image.Image, texts []textSpec) ([]byte, error) {
|
||||
font := p.c.Get("meme.font", "impact.ttf")
|
||||
font := meme.GetFont(p.c, p.c.Get("meme.font", "impact.ttf"))
|
||||
fontSizes := []float64{48, 36, 24, 16, 12}
|
||||
r := img.Bounds()
|
||||
w := r.Dx()
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/nicklaw5/helix"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, err := helix.NewClient(&helix.Options{
|
||||
ClientID: "ptwtiuzl9tcrekpf3d26ey3hb7qsge",
|
||||
ClientSecret: "rpa0w6qemjqp7sgrmidwi4k0kcah82",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Login error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
access, err := client.RequestAppAccessToken([]string{"user:read:email"})
|
||||
if err != nil {
|
||||
log.Printf("Login error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", access)
|
||||
|
||||
// Set the access token on the client
|
||||
client.SetAppAccessToken(access.Data.AccessToken)
|
||||
|
||||
users, err := client.GetUsers(&helix.UsersParams{
|
||||
Logins: []string{"drseabass"},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Error getting users: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if users.Error != "" {
|
||||
log.Printf("Users error: %s", users.Error)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("drseabass: %+v", users.Data.Users[0])
|
||||
return
|
||||
|
||||
resp, err := client.CreateEventSubSubscription(&helix.EventSubSubscription{
|
||||
Type: helix.EventSubTypeStreamOnline,
|
||||
Version: "1",
|
||||
Condition: helix.EventSubCondition{
|
||||
BroadcasterUserID: users.Data.Users[0].ID,
|
||||
},
|
||||
Transport: helix.EventSubTransport{
|
||||
Method: "webhook",
|
||||
Callback: "https://rathaus.chrissexton.org/live",
|
||||
Secret: "s3cre7w0rd",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Eventsub error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", resp)
|
||||
|
||||
resp, err = client.CreateEventSubSubscription(&helix.EventSubSubscription{
|
||||
Type: helix.EventSubTypeStreamOffline,
|
||||
Version: "1",
|
||||
Condition: helix.EventSubCondition{
|
||||
BroadcasterUserID: users.Data.Users[0].ID,
|
||||
},
|
||||
Transport: helix.EventSubTransport{
|
||||
Method: "webhook",
|
||||
Callback: "https://rathaus.chrissexton.org/offline",
|
||||
Secret: "s3cre7w0rd",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Eventsub error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", resp)
|
||||
|
||||
http.HandleFunc("/offline", func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
// verify that the notification came from twitch using the secret.
|
||||
if !helix.VerifyEventSubNotification("s3cre7w0rd", r.Header, string(body)) {
|
||||
log.Println("no valid signature on subscription")
|
||||
return
|
||||
} else {
|
||||
log.Println("verified signature for subscription")
|
||||
}
|
||||
var vals map[string]any
|
||||
if err = json.Unmarshal(body, &vals); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if challenge, ok := vals["challenge"]; ok {
|
||||
w.Write([]byte(challenge.(string)))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("got offline webhook: %v\n", vals)
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("ok"))
|
||||
})
|
||||
http.HandleFunc("/live", func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
// verify that the notification came from twitch using the secret.
|
||||
if !helix.VerifyEventSubNotification("s3cre7w0rd", r.Header, string(body)) {
|
||||
log.Println("no valid signature on subscription")
|
||||
return
|
||||
} else {
|
||||
log.Println("verified signature for subscription")
|
||||
}
|
||||
var vals map[string]any
|
||||
if err = json.Unmarshal(body, &vals); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if challenge, ok := vals["challenge"]; ok {
|
||||
w.Write([]byte(challenge.(string)))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("got live webhook: %v\n", vals)
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("ok"))
|
||||
})
|
||||
http.ListenAndServe("0.0.0.0:1337", nil)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package stats
|
Loading…
Reference in New Issue