Browse Source

initial kvstore functions

master
David Haukeness 5 years ago
parent
commit
13c971c635
No known key found for this signature in database
GPG Key ID: 54F2372DDB7F9462
  1. 54
      commands.go
  2. 6
      go.mod
  3. 15
      go.sum
  4. 12
      handlers.go
  5. 63
      kvstore.go
  6. 16
      main.go
  7. 8
      types.go
  8. 33
      utils.go

54
commands.go

@ -3,6 +3,7 @@ package main @@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"net/url"
"strings"
"samhofi.us/x/keybase/types/chat1"
@ -40,3 +41,56 @@ func (b *bot) sendFeedback(convid chat1.ConvIDStr, mesgID chat1.MessageID, sende @@ -40,3 +41,56 @@ func (b *bot) sendFeedback(convid chat1.ConvIDStr, mesgID chat1.MessageID, sende
func (b *bot) sendWelcome(convid chat1.ConvIDStr) {
b.k.SendMessageByConvID(convid, "Hello there!! I'm the Jitsi meeting bot, made by @haukened\nI can start Jitsi meetings right here in this chat!\nI can be activated in 2 ways:\n 1. `@jitsibot`\n 2.`!jitsi`\nYou can provide feedback to my humans using:\n 1. `@jitsibot feedback <type anything>`\n 2. `!jitsibot feedback <type anything>`\nYou can also join @jitsi_meet to talk about features, enhancements, or talk to live humans! Everyone is welcome!\nI also accept donations to offset hosting costs, just send some XLM to my wallet if you feel like it by typing `+5XLM@jitsibot`\nIf you ever need to see this message again, ask me for help or say hello to me!")
}
func (b *bot) setKValue(convid chat1.ConvIDStr, msgID chat1.MessageID, args []string) {
if args[0] != "set" {
return
}
switch len(args) {
case 3:
if args[1] == "url" {
// first validate the URL
u, err := url.ParseRequestURI(args[2])
if err != nil {
b.k.ReplyByConvID(convid, msgID, "ERROR - `%s`", err)
return
}
// then make sure its HTTPS
if u.Scheme != "https" {
b.k.ReplyByConvID(convid, msgID, "ERROR - HTTPS Required")
return
}
// then get the current options
var opts ConvOptions
err = b.KVStoreGetStruct(convid, &opts)
if err != nil {
eid := b.logError(err)
b.k.ReactByConvID(convid, msgID, "Error %s", eid)
return
}
// then update the struct using only the scheme and hostname:port
if u.Port() != "" {
opts.CustomURL = fmt.Sprintf("%s://%s:%s/", u.Scheme, u.Hostname(), u.Port())
} else {
opts.CustomURL = fmt.Sprintf("%s://%s/", u.Scheme, u.Hostname())
}
// then write that back to kvstore, with revision
err = b.KVStorePutStruct(convid, opts)
if err != nil {
eid := b.logError(err)
b.k.ReactByConvID(convid, msgID, "ERROR %s", eid)
return
}
b.k.ReactByConvID(convid, msgID, "OK!")
return
}
default:
return
}
}
func (b *bot) listKValue(convid chat1.ConvIDStr, msgID chat1.MessageID, args []string) {
if args[0] != "list" {
return
}
}

6
go.mod

@ -4,9 +4,13 @@ go 1.13 @@ -4,9 +4,13 @@ go 1.13
require (
github.com/caarlos0/env v3.5.0+incompatible
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/sethvargo/go-diceware v0.2.0
github.com/stretchr/testify v1.5.1 // indirect
github.com/ugorji/go v1.1.7 // indirect
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
github.com/ugorji/go/codec v1.1.7
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
samhofi.us/x/keybase v0.0.0-20200312153536-07f5168a6a29
)

15
go.sum

@ -2,10 +2,13 @@ github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yi @@ -2,10 +2,13 @@ github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yi
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/lithammer/shortuuid/v3 v3.0.4 h1:uj4xhotfY92Y1Oa6n6HUiFn87CdoEHYUlTy0+IgbLrs=
github.com/lithammer/shortuuid/v3 v3.0.4/go.mod h1:RviRjexKqIzx/7r1peoAITm6m7gnif/h+0zmolKJjzw=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sethvargo/go-diceware v0.2.0 h1:3QzXGqUe0UR9y1XYSz1dxGS+fKtXOxRqqKjy+cG1yTI=
@ -13,12 +16,16 @@ github.com/sethvargo/go-diceware v0.2.0/go.mod h1:II+37A5sTGAtg3zd/JqyVQ8qqAjSm/ @@ -13,12 +16,16 @@ github.com/sethvargo/go-diceware v0.2.0/go.mod h1:II+37A5sTGAtg3zd/JqyVQ8qqAjSm/
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

12
handlers.go

@ -49,7 +49,8 @@ func (b *bot) chatHandler(m chat1.MsgSummary) { @@ -49,7 +49,8 @@ func (b *bot) chatHandler(m chat1.MsgSummary) {
// Determine first if this is a command
if strings.HasPrefix(m.Content.Text.Body, "!") || strings.HasPrefix(m.Content.Text.Body, "@") {
// determine the root command
words := strings.Fields(m.Content.Text.Body)
body := strings.ToLower(m.Content.Text.Body)
words := strings.Fields(body)
command := strings.Replace(words[0], "@", "", 1)
command = strings.Replace(command, "!", "", 1)
command = strings.ToLower(command)
@ -58,7 +59,14 @@ func (b *bot) chatHandler(m chat1.MsgSummary) { @@ -58,7 +59,14 @@ func (b *bot) chatHandler(m chat1.MsgSummary) {
nargs := len(args)
switch command {
case b.k.Username:
fallthrough
if nargs > 0 {
switch args[0] {
case "set":
b.setKValue(m.ConvID, m.Id, args)
case "list":
b.listKValue(m.ConvID, m.Id, args)
}
}
case "jitsi":
if nargs == 0 {
b.setupMeeting(m.ConvID, m.Sender.Username, args, m.Channel.MembersType)

63
kvstore.go

@ -1,16 +1,65 @@ @@ -1,16 +1,65 @@
package main
// writeKV is an internal func that ensures KVStore values get written consistently
func (b *bot) writeKV(key string, value string) error {
_, err := b.k.KVPut(&b.config.KVStoreTeam, b.k.Username, key, value)
import (
"reflect"
"samhofi.us/x/keybase/types/chat1"
)
// mashals an interface to JSON and sends to kvstore
func (b *bot) KVStorePutStruct(convIDstr chat1.ConvIDStr, v interface{}) error {
// marshal the struct to JSON
kvstoreDataString, err := encodeStructToJSONString(v)
if err != nil {
return err
}
// put the string in kvstore
err = b.KVStorePut(string(convIDstr), getTypeName(v), kvstoreDataString)
if err != nil {
return err
}
return nil
}
func (b *bot) KVStoreGetStruct(convIDstr chat1.ConvIDStr, v interface{}) error {
// get the string from kvstore
result, err := b.KVStoreGet(string(convIDstr), getTypeName(v))
if err != nil {
return err
}
// if there was no result just return and the struct is unmodified
if result == "" {
return nil
}
// unmarshal the string into JSON
err = decodeJSONStringToStruct(v, result)
if err != nil {
return err
}
return nil
}
// getGV is an internal function that ensures KVStore values are retreived consistently
func (b *bot) getKV(key string) (value string, revision int, err error) {
res, err := b.k.KVGet(&b.config.KVStoreTeam, b.k.Username, key)
return res.EntryValue, res.Revision, err
func (b *bot) KVStorePut(namespace string, key string, value string) error {
_, err := b.k.KVPut(&b.config.KVStoreTeam, namespace, key, value)
if err != nil {
return err
}
return nil
}
func (b *bot) KVStoreGet(namespace string, key string) (string, error) {
kvResult, err := b.k.KVGet(&b.config.KVStoreTeam, namespace, key)
if err != nil {
return "", err
}
return kvResult.EntryValue, nil
}
// getTypeName returns the name of a type, regardless of if its a pointer or not
func getTypeName(v interface{}) string {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
return t.Elem().Name()
}
return t.Name()
}

16
main.go

@ -111,6 +111,22 @@ func (b *bot) run(args []string) error { @@ -111,6 +111,22 @@ func (b *bot) run(args []string) error {
b.k.ClearCommands()
b.registerCommands()
// this is just for testing, and doesn't work yet
if err := b.KVStorePutStruct("test", &ConvOptions{ConvID: "test", CustomURL: "https://te.st:888"}); err != nil {
log.Printf("KV: %+v", err)
}
var vRes1 ConvOptions
if err := b.KVStoreGetStruct("test", &vRes1); err != nil {
log.Printf("KV: %+v", err)
} else {
fmt.Printf("VR: %+v\n", vRes1)
}
var vRes2 ConvOptions
if err := b.KVStoreGetStruct("test1", &vRes2); err != nil {
log.Printf("KV: %+v", err)
} else {
fmt.Printf("VR: %+v\n", vRes2)
}
log.Println("Starting...")
b.k.Run(b.handlers, &b.opts)
return nil

8
types.go

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
package main
// ConvOptions stores team specific options like custom servers
type ConvOptions struct {
ConvID string `json:"converation_id,omitempty"`
NotificationsEnabled bool `json:"notifications_enabled,omitempty"`
CustomURL string `json:"custom_url,omitempty"`
}

33
utils.go

@ -4,6 +4,8 @@ import ( @@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/teris-io/shortid"
"github.com/ugorji/go/codec"
"samhofi.us/x/keybase/types/chat1"
)
@ -27,3 +29,34 @@ func getFeedbackExtendedDescription(bc botConfig) *chat1.UserBotExtendedDescript @@ -27,3 +29,34 @@ func getFeedbackExtendedDescription(bc botConfig) *chat1.UserBotExtendedDescript
MobileBody: "Please note: Your feedback will be public!",
}
}
func encodeStructToJSONString(v interface{}) (string, error) {
jh := codecHandle()
var bytes []byte
err := codec.NewEncoderBytes(&bytes, jh).Encode(v)
if err != nil {
return "", err
}
result := string(bytes)
return result, nil
}
func decodeJSONStringToStruct(v interface{}, src string) error {
bytes := []byte(src)
jh := codecHandle()
return codec.NewDecoderBytes(bytes, jh).Decode(v)
}
func codecHandle() *codec.JsonHandle {
var jh codec.JsonHandle
return &jh
}
func (b *bot) logError(err error) string {
// generate the error id
eid := shortid.MustGenerate()
// send the error to the log
b.debug("`%s` - %s", eid, err)
// then return the error id for use
return eid
}

Loading…
Cancel
Save