From 90bebe9ada90fd1699fc3ca53622533d46e8da70 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sat, 1 Oct 2022 17:40:41 -0600 Subject: [PATCH 01/22] Stubbed out farkle game --- internal/handler/games/farkle.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 internal/handler/games/farkle.go diff --git a/internal/handler/games/farkle.go b/internal/handler/games/farkle.go new file mode 100644 index 0000000..99b9e23 --- /dev/null +++ b/internal/handler/games/farkle.go @@ -0,0 +1,12 @@ +package games + +import "fmt" + +func (bg BotGame) Farkle(event BotGame) (response Response, err error) { + response.Type = "command" + response.Message = fmt.Sprintf("/echo %s would like to play a game of Farkle.", event.sender) + + // TODO - for kr0w? + + return response, nil +} From e1da3472c553de2a260213d0745397b83ae90204 Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sat, 1 Oct 2022 11:19:27 -0600 Subject: [PATCH 02/22] rename quit to reload --- internal/handler/commands/quit.go | 16 ---------------- internal/handler/commands/reload.go | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 internal/handler/commands/quit.go create mode 100644 internal/handler/commands/reload.go diff --git a/internal/handler/commands/quit.go b/internal/handler/commands/quit.go deleted file mode 100644 index 6c3c822..0000000 --- a/internal/handler/commands/quit.go +++ /dev/null @@ -1,16 +0,0 @@ -package commands - -func (h BotCommandHelp) Quit(request BotCommand) (response HelpResponse) { - return HelpResponse{ - Help: "Shuts down the bot", - Description: "Shuts down the bot", - } -} - -func (bc BotCommand) Quit(event BotCommand) (response Response, err error) { - response.Type = "shutdown" - - response.Message = "So, if my body gets killed, big whoop! I just download into another body. I'm immortal, baby!" - - return response, nil -} diff --git a/internal/handler/commands/reload.go b/internal/handler/commands/reload.go new file mode 100644 index 0000000..c272622 --- /dev/null +++ b/internal/handler/commands/reload.go @@ -0,0 +1,16 @@ +package commands + +func (h BotCommandHelp) Reload(request BotCommand) (response HelpResponse) { + return HelpResponse{ + Help: "Causes the bot to shutdown, pull any changes from git, and restart", + Description: "Reloads the bot", + } +} + +func (bc BotCommand) Reload(event BotCommand) (response Response, err error) { + response.Type = "shutdown" + + response.Message = "So, if my body gets killed, big whoop! I just download into another body. I'm immortal, baby!" + + return response, nil +} From 4bc4c98b21674c6f6843692c6791e8776e4a0225 Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sun, 2 Oct 2022 06:40:39 -0600 Subject: [PATCH 03/22] functionality to track the reload in cache --- internal/handler/command-handler.go | 2 ++ internal/handler/handler.go | 15 +++++++++++++++ internal/mmclient/mmclient.go | 7 ++++--- internal/settings/config.go | 10 ++++------ main.go | 2 +- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/internal/handler/command-handler.go b/internal/handler/command-handler.go index c9d7c61..be82c24 100644 --- a/internal/handler/command-handler.go +++ b/internal/handler/command-handler.go @@ -86,6 +86,8 @@ func (h *Handler) HandleCommand(quit chan bool, event *model.WebSocketEvent) err log.Print(err) } + h.Cache.Put("restart-usr", post.UserId) + quit <- true } } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 4be5400..5294831 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -24,6 +24,21 @@ func NewHandler(mm *mmclient.MMClient, botCache cache.Cache) (*Handler, error) { settings, err := settings.NewSettings(mm.SettingsUrl) users.SetupUsers(mm, botCache) + // restartUsr, ok, err := botCache.Get("restart-usr") + // if ok { + // c, _, _ := mm.Client.CreateDirectChannel(restartUsr.(string), mm.BotUser.Id) + // replyPost := &model.Post{} + // replyPost.ChannelId = c.Id + // replyPost.Message = "I' back, baby!" + // + // _, _, err := mm.Client.CreatePost(replyPost) + // if err != nil { + // log.Print(err) + // } + // + // botCache.Clean("restart-usr") + // } + return &Handler{ Settings: settings, Mm: mm, diff --git a/internal/mmclient/mmclient.go b/internal/mmclient/mmclient.go index 4105105..63107f5 100644 --- a/internal/mmclient/mmclient.go +++ b/internal/mmclient/mmclient.go @@ -24,9 +24,10 @@ type MMClient struct { } type Server struct { - HOST string `yaml:"host"` - PROTOCOL string `yaml:"protocol"` - PORT string `yaml:"port"` + HOST string `yaml:"host"` + PROTOCOL string `yaml:"protocol"` + PORT string `yaml:"port"` + CACHE_URI string `yaml:"cache_uri"` } // Documentation for the Go driver can be found diff --git a/internal/settings/config.go b/internal/settings/config.go index 6e7ec56..6b829d3 100644 --- a/internal/settings/config.go +++ b/internal/settings/config.go @@ -8,9 +8,10 @@ import ( type Config struct { Server struct { - HOST string `yaml:"host"` - PROTOCOL string `yaml:"protocol"` - PORT string `yaml:"port"` + HOST string `yaml:"host"` + PROTOCOL string `yaml:"protocol"` + PORT string `yaml:"port"` + CACHE_URI string `yaml:"cache_uri"` } `yaml:"server"` Bot struct { SAMPLE_NAME string `yaml:"sample_name"` @@ -23,9 +24,6 @@ type Config struct { LOG_NAME string `yaml:"log_name"` SETTINGS_URL string `yaml:"settings_url"` } `yaml:"bot"` - Cache struct { - CONN_STR string `yaml:"connection_string"` - } `yaml:"cache"` } // GetConfig reads the bot configuration file and loads into a Config struct diff --git a/main.go b/main.go index 0df658d..6f7433c 100644 --- a/main.go +++ b/main.go @@ -41,7 +41,7 @@ func main() { } }() - botCache := cache.GetCachingMechanism(cfg.Cache.CONN_STR) + botCache := cache.GetCachingMechanism(cfg.Server.CACHE_URI) handler, err := handler.NewHandler(mmClient, botCache) if err != nil { From 722328701524cdaa8b56c7b01d2d23a8f80671b5 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sat, 1 Oct 2022 17:08:31 -0600 Subject: [PATCH 04/22] Code consistent with building the RPS game. --- internal/cache/cache.go | 1 + internal/cache/local.go | 12 +++ internal/cache/redis.go | 8 +- internal/handler/game-handler.go | 27 +++++- internal/handler/games/rps.go | 156 +++++++++++++++++++++++++++++++ internal/users/users.go | 59 ++++++++++-- 6 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 internal/handler/games/rps.go diff --git a/internal/cache/cache.go b/internal/cache/cache.go index efb5c13..4d215d8 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -10,6 +10,7 @@ type Cache interface { Get(key string) (interface{}, bool, error) GetAll(keys []string) map[string]interface{} Clean(key string) + GetKeys(prefix string) []string CleanAll() } diff --git a/internal/cache/local.go b/internal/cache/local.go index 8148827..2cbac73 100644 --- a/internal/cache/local.go +++ b/internal/cache/local.go @@ -1,6 +1,7 @@ package cache import ( + "strings" "sync" ) @@ -56,3 +57,14 @@ func (c *LocalCache) CleanAll() { c.data = make(map[string]interface{}) c.mu.Unlock() } + +func (c *LocalCache) GetKeys(prefix string) []string { + keys := make([]string, 0, len(c.data)) + for k, _ := range c.data { + if strings.Contains(k, prefix) { + keys = append(keys, k) + } + } + + return keys +} diff --git a/internal/cache/redis.go b/internal/cache/redis.go index 2bbb3b0..979e139 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -2,6 +2,7 @@ package cache import ( "context" + "encoding/json" "fmt" "net/url" @@ -31,7 +32,8 @@ func GetRedisCache(connStr string) *RedisCache { } func (rc *RedisCache) Put(key string, value interface{}) { - if err := rc.conn.Set(rc.ctx, key, value, 0); err != nil { + jsonItem, _ := json.Marshal(value) + if err := rc.conn.Set(rc.ctx, key, jsonItem, 0); err != nil { fmt.Println(err) } } @@ -71,3 +73,7 @@ func (rc *RedisCache) Clean(key string) { func (rc *RedisCache) CleanAll() { rc.conn.FlushDB(rc.ctx) } + +func (rc *RedisCache) GetKeys(prefix string) []string { + return rc.conn.Keys(rc.ctx, prefix+"_*").Val() +} diff --git a/internal/handler/game-handler.go b/internal/handler/game-handler.go index 1894100..4789dc1 100644 --- a/internal/handler/game-handler.go +++ b/internal/handler/game-handler.go @@ -1,6 +1,7 @@ package handler import ( + "fmt" "log" "strings" @@ -13,11 +14,16 @@ func (h *Handler) HandleGame(quit chan bool, event *model.WebSocketEvent) error channelId := event.GetBroadcast().ChannelId post := h.Mm.PostFromJson(strings.NewReader(event.GetData()["post"].(string))) sender := event.GetData()["sender_name"].(string) + var e error bg, err := gms.NewBotGame(post.Message, sender) if err != nil { return h.SendErrorResponse(post, err.Error()) } + bg.ReplyChannel, _, e = h.Mm.Client.GetChannel(channelId, "") + if e != nil { + return h.SendErrorResponse(post, e.Error()) + } r, err := gms.CallGame(bg) if err != nil { @@ -27,7 +33,7 @@ func (h *Handler) HandleGame(quit chan bool, event *model.WebSocketEvent) error if r.Channel == "" && bg.ReplyChannel.Id == "" { r.Channel = event.GetBroadcast().ChannelId - } else { + } else if r.Type != "multi" && bg.ReplyChannel.Id == "" { r.Channel = bg.ReplyChannel.Id r.Type = "command" checkMsg := strings.Split(r.Message, " ") @@ -46,6 +52,25 @@ func (h *Handler) HandleGame(quit chan bool, event *model.WebSocketEvent) error err = h.Mm.SendMsgToChannel(r.Message, r.Channel, post) case "command": err = h.Mm.SendCmdToChannel(r.Message, r.Channel, post) + case "multi": + messages := strings.Split(r.Message, "##") + var firstMessage, secondMessage string + if len(messages) > 1 { + firstMessage = messages[0] + secondMessage = messages[1] + } else { + return fmt.Errorf("multi message wasn't formatted properly") + } + if err != nil { + return err + } + c, _, _ := h.Mm.Client.CreateDirectChannel(post.UserId, h.Mm.BotUser.Id) + replyPost := &model.Post{} + replyPost.ChannelId = c.Id + replyPost.Message = firstMessage + + _, _, err = h.Mm.Client.CreatePost(replyPost) + err = h.Mm.SendMsgToChannel(secondMessage, r.Channel, post) case "dm": c, _, _ := h.Mm.Client.CreateDirectChannel(post.UserId, h.Mm.BotUser.Id) replyPost := &model.Post{} diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go new file mode 100644 index 0000000..82b1137 --- /dev/null +++ b/internal/handler/games/rps.go @@ -0,0 +1,156 @@ +package games + +import ( + "fmt" + "github.com/google/uuid" + "github.com/pyrousnet/pyrous-gobot/internal/users" + "strings" +) + +const ROCK = "rock" +const PAPER = "paper" +const SCISSORS = "scissors" + +func (bg BotGame) Rps(event BotGame) (response Response, err error) { + response.Type = "multi" + player, _, err := users.GetUser(strings.TrimLeft(event.sender, "@"), event.cache) + opponent, oErr := findApponent(event, player) + if !playing(player) { + if oErr == nil && playing(opponent) { + channelId, ok, _ := event.cache.Get(opponent.RpsPlaying) + if ok && event.ReplyChannel != nil && channelId == event.ReplyChannel.Id { + player.RpsPlaying = opponent.RpsPlaying + } + } else { + id, e := uuid.NewRandom() + event.cache.Put(id.String(), event.ReplyChannel.Id) + response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps rock)##%s is looking for an opponent in RPS.", event.sender) + if e != nil { + return response, e + } + player.RpsPlaying = id.String() + } + } + + if event.body != "" { + switch strings.ToLower(event.body) { + case "rock", "paper", "scissors": + player.Rps = strings.ToLower(event.body) + response.Type = "dm" + response.Message = fmt.Sprintf("I have you down for: %s", strings.Title(strings.ToLower(event.body))) + default: + response.Type = "dm" + response.Message = fmt.Sprintf(`Uh, %s isn't an option. Try rock, paper or scissors'`, event.body) + } + } + + if oErr == nil { + winners, hasWinner := getWinner(player, opponent) + if hasWinner { + channelId, ok, _ := event.cache.Get(player.RpsPlaying) + response.Type = "command" + if ok { + response.Channel = channelId.(string) + if len(winners) > 1 { + response.Message = fmt.Sprintf("/echo The RPS game between %s and %s ended in a draw.", player.Name, opponent.Name) + } else { + response.Message = fmt.Sprintf("/echo The RPS game between %s and %s ended with %s winning.", player.Name, opponent.Name, winners[0].Name) + } + } + + player.Rps = "" + player.RpsPlaying = "" + opponent.Rps = "" + opponent.RpsPlaying = "" + } + + users.UpdateUser(opponent, event.cache) + } + + users.UpdateUser(player, event.cache) + + return response, err +} + +func playing(player users.User) bool { + return player.RpsPlaying != "" +} + +func sameGame(player users.User, opponent users.User) bool { + return player.RpsPlaying == "" || player.RpsPlaying == opponent.RpsPlaying +} + +func differentUser(player users.User, opponent users.User) bool { + return player.Name != opponent.Name +} + +func findApponent(event BotGame, forPlayer users.User) (users.User, error) { + us, ok, err := users.GetUsers(event.cache) + var opponent users.User + var found = false + + if us == nil { + return users.User{}, fmt.Errorf("no opponent") + } + + if ok { + for _, u := range us { + if playing(u) && sameGame(forPlayer, u) && differentUser(forPlayer, u) { + opponent = u + found = true + } + } + } else { + return users.User{}, err + } + + if !found { + err = fmt.Errorf("no opponent") + } + return opponent, err +} + +func getWinner(player users.User, opponent users.User) ([]users.User, bool) { + var hasWinner bool = false + var winners []users.User + + if player.Rps == "" { + return []users.User{}, false + } + + if opponent.Rps == "" { + return []users.User{}, false + } + + if player.Rps == opponent.Rps { + winners = append(winners, player) + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(player.Rps) == ROCK { + if strings.ToLower(opponent.Rps) == PAPER { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == SCISSORS { + winners = append(winners, player) + hasWinner = true + } + } else if strings.ToLower(player.Rps) == PAPER { + if strings.ToLower(opponent.Rps) == SCISSORS { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == ROCK { + winners = append(winners, player) + hasWinner = true + } + } else if strings.ToLower(player.Rps) == SCISSORS { + if strings.ToLower(opponent.Rps) == ROCK { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == PAPER { + winners = append(winners, player) + hasWinner = true + } + } + + return winners, hasWinner +} diff --git a/internal/users/users.go b/internal/users/users.go index d1a3fbf..92a45f7 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -1,9 +1,11 @@ package users import ( + "encoding/json" "github.com/mattermost/mattermost-server/v6/model" "github.com/pyrousnet/pyrous-gobot/internal/cache" "github.com/pyrousnet/pyrous-gobot/internal/mmclient" + "reflect" ) const KeyPrefix = "user-" @@ -14,9 +16,9 @@ type ( Name string `json:"name"` Message string `json:"message"` Rps string `json:"rps"` - RpsPlaying bool `json:"rps-playing"` + RpsPlaying string `json:"rps-playing"` FarkleValue int `json:"farkle-value"` - FarklePlaying bool `json:"farkle-playing"` + FarklePlaying string `json:"farkle-playing"` SyndFeed string `json:"synd-feed"` FeedCount int `json:"feed-count"` } @@ -33,13 +35,14 @@ func SetupUsers(mm *mmclient.MMClient, c cache.Cache) error { Name: user.Username, Message: "", Rps: "", - RpsPlaying: false, - FarklePlaying: false, + RpsPlaying: "", + FarklePlaying: "", FarkleValue: 0, SyndFeed: "", FeedCount: 0, } - c.Put(key, newUser) + u, _ := json.Marshal(newUser) + c.Put(key, u) } } return err @@ -60,14 +63,56 @@ func HandlePost(post *model.Post, mm *mmclient.MMClient, c cache.Cache) error { func GetUser(username string, c cache.Cache) (User, bool, error) { key := KeyPrefix + username - user, ok, err := c.Get(key) + u, ok, err := c.Get(key) + var user User + + user, err = getUserFromUnknownType(u, user, err) + if err != nil { return User{}, false, err } if ok { - return user.(User), ok, nil + return user, ok, nil } return User{}, ok, nil } + +func GetUsers(c cache.Cache) ([]User, bool, error) { + userKeys := c.GetKeys(KeyPrefix) + var users []User + var err error + + for _, key := range userKeys { + u, ok, e := c.Get(key) + err = e + + if ok { + var user User + user, err = getUserFromUnknownType(u, user, err) + users = append(users, user) + } else { + break + } + } + + return users, true, err +} + +func getUserFromUnknownType(u interface{}, user User, err error) (User, error) { + if reflect.TypeOf(u).String() != "[]uint8" { + user = u.(User) + } else { + err = json.Unmarshal(u.([]byte), &user) + } + return user, err +} + +func UpdateUser(user User, c cache.Cache) (User, bool) { + key := KeyPrefix + user.Name + + c.Put(key, user) + + return user, true +} From 1acde4d98c92aca034cb5ae63d244a3acc9efa35 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sat, 1 Oct 2022 17:11:34 -0600 Subject: [PATCH 05/22] Added TODO about multi --- internal/handler/game-handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handler/game-handler.go b/internal/handler/game-handler.go index 4789dc1..152d903 100644 --- a/internal/handler/game-handler.go +++ b/internal/handler/game-handler.go @@ -52,7 +52,7 @@ func (h *Handler) HandleGame(quit chan bool, event *model.WebSocketEvent) error err = h.Mm.SendMsgToChannel(r.Message, r.Channel, post) case "command": err = h.Mm.SendCmdToChannel(r.Message, r.Channel, post) - case "multi": + case "multi": // TODO - We need to rethink this. It only allows 2 commands. messages := strings.Split(r.Message, "##") var firstMessage, secondMessage string if len(messages) > 1 { From 19d4d33dc09d575cf4cc92b46406868cb57e3d2d Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sat, 1 Oct 2022 20:43:33 -0600 Subject: [PATCH 06/22] fix dm bug log output of git pull --- daemon/daemon.go | 3 ++- internal/handler/games/rps.go | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index d090219..c36ac60 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -18,10 +18,11 @@ func main() { for { cmd := exec.Command("git", "pull") cmd.Dir = cmdDir - _, err := cmd.Output() + output, err := cmd.CombinedOutput() if err != nil { log.Fatal(err) } + log.Println(string(output)) cmd = exec.Command("go", "build", "-o", "gobot", ".") cmd.Dir = cmdDir diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 82b1137..e4d722b 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -2,9 +2,10 @@ package games import ( "fmt" + "strings" + "github.com/google/uuid" "github.com/pyrousnet/pyrous-gobot/internal/users" - "strings" ) const ROCK = "rock" @@ -20,6 +21,8 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { channelId, ok, _ := event.cache.Get(opponent.RpsPlaying) if ok && event.ReplyChannel != nil && channelId == event.ReplyChannel.Id { player.RpsPlaying = opponent.RpsPlaying + response.Type = "dm" + response.Message = "Would you like to throw Rock, Paper or Scissors (Usage: $rps rock)" } } else { id, e := uuid.NewRandom() From 8c1f1135706c9bd7ea47f0188f195ebe247cf64c Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sun, 2 Oct 2022 08:35:32 -0600 Subject: [PATCH 07/22] rework how user is unmarshalled from redis --- internal/cache/redis.go | 15 ++++++++------ internal/handler/command-handler.go | 2 +- internal/handler/handler.go | 32 ++++++++++++++++------------- internal/users/users.go | 23 ++++++++++++++------- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/internal/cache/redis.go b/internal/cache/redis.go index 979e139..f3366f1 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -45,13 +45,16 @@ func (rc *RedisCache) PutAll(entries map[string]interface{}) { } func (rc *RedisCache) Get(key string) (interface{}, bool, error) { - value, err := rc.conn.Get(rc.ctx, key).Result() - ok, _ := rc.conn.Exists(rc.ctx, key).Result() - if err != nil { - fmt.Println(err) - return "", false, err + numkeys, _ := rc.conn.Exists(rc.ctx, key).Result() + if ok := numkeys > 0; ok { + value, err := rc.conn.Get(rc.ctx, key).Result() + if err != nil { + return "", false, err + } + return value, ok, nil } - return value, ok > 0, err + + return "", false, nil } func (rc *RedisCache) GetAll(keys []string) map[string]interface{} { diff --git a/internal/handler/command-handler.go b/internal/handler/command-handler.go index be82c24..2175ded 100644 --- a/internal/handler/command-handler.go +++ b/internal/handler/command-handler.go @@ -86,7 +86,7 @@ func (h *Handler) HandleCommand(quit chan bool, event *model.WebSocketEvent) err log.Print(err) } - h.Cache.Put("restart-usr", post.UserId) + h.Cache.Put("sys_restarted_by_user", post.UserId) quit <- true } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 5294831..3f5936b 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -24,20 +24,24 @@ func NewHandler(mm *mmclient.MMClient, botCache cache.Cache) (*Handler, error) { settings, err := settings.NewSettings(mm.SettingsUrl) users.SetupUsers(mm, botCache) - // restartUsr, ok, err := botCache.Get("restart-usr") - // if ok { - // c, _, _ := mm.Client.CreateDirectChannel(restartUsr.(string), mm.BotUser.Id) - // replyPost := &model.Post{} - // replyPost.ChannelId = c.Id - // replyPost.Message = "I' back, baby!" - // - // _, _, err := mm.Client.CreatePost(replyPost) - // if err != nil { - // log.Print(err) - // } - // - // botCache.Clean("restart-usr") - // } + juid, ok, err := botCache.Get("sys_restarted_by_user") + if ok { + usr := strings.Trim(juid.(string), `"`) + c, _, err := mm.Client.CreateDirectChannel(usr, mm.BotUser.Id) + if err != nil { + log.Print(err) + } + replyPost := &model.Post{} + replyPost.ChannelId = c.Id + replyPost.Message = "I'm back, baby! 😉" + + _, _, err = mm.Client.CreatePost(replyPost) + if err != nil { + log.Print(err) + } + + botCache.Clean("sys_restarted_by_user") + } return &Handler{ Settings: settings, diff --git a/internal/users/users.go b/internal/users/users.go index 92a45f7..ddb858b 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -2,10 +2,12 @@ package users import ( "encoding/json" + "fmt" + "reflect" + "github.com/mattermost/mattermost-server/v6/model" "github.com/pyrousnet/pyrous-gobot/internal/cache" "github.com/pyrousnet/pyrous-gobot/internal/mmclient" - "reflect" ) const KeyPrefix = "user-" @@ -63,16 +65,19 @@ func HandlePost(post *model.Post, mm *mmclient.MMClient, c cache.Cache) error { func GetUser(username string, c cache.Cache) (User, bool, error) { key := KeyPrefix + username - u, ok, err := c.Get(key) - var user User - - user, err = getUserFromUnknownType(u, user, err) + u, ok, err := c.Get(key) if err != nil { - return User{}, false, err + return User{}, false, fmt.Errorf("error communicating with redis: %v", err) } if ok { + var user User + user, err = getUserFromUnknownType(u, user, err) + if err != nil { + return User{}, false, fmt.Errorf("error unmarshalling user: %v", err) + } + return user, ok, nil } @@ -102,7 +107,11 @@ func GetUsers(c cache.Cache) ([]User, bool, error) { func getUserFromUnknownType(u interface{}, user User, err error) (User, error) { if reflect.TypeOf(u).String() != "[]uint8" { - user = u.(User) + var user User + var jm map[string]interface{} + err = json.Unmarshal([]byte(u.(string)), &jm) + jb, _ := json.Marshal(jm) + err = json.Unmarshal(jb, &user) } else { err = json.Unmarshal(u.([]byte), &user) } From 0fa7d6d26f6472ac034e39e9df5ef91088ada6f8 Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sun, 2 Oct 2022 09:49:11 -0600 Subject: [PATCH 08/22] fix getting users from redis --- internal/cache/redis.go | 4 +--- internal/handler/commands/s.go | 3 ++- internal/users/users.go | 27 ++++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/cache/redis.go b/internal/cache/redis.go index f3366f1..0c542cc 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -2,7 +2,6 @@ package cache import ( "context" - "encoding/json" "fmt" "net/url" @@ -32,8 +31,7 @@ func GetRedisCache(connStr string) *RedisCache { } func (rc *RedisCache) Put(key string, value interface{}) { - jsonItem, _ := json.Marshal(value) - if err := rc.conn.Set(rc.ctx, key, jsonItem, 0); err != nil { + if err := rc.conn.Set(rc.ctx, key, value, 0); err != nil { fmt.Println(err) } } diff --git a/internal/handler/commands/s.go b/internal/handler/commands/s.go index 72f3b16..845fb6c 100644 --- a/internal/handler/commands/s.go +++ b/internal/handler/commands/s.go @@ -2,8 +2,9 @@ package commands import ( "fmt" - "github.com/pyrousnet/pyrous-gobot/internal/users" "strings" + + "github.com/pyrousnet/pyrous-gobot/internal/users" ) func (h BotCommandHelp) S(request BotCommand) (response HelpResponse) { diff --git a/internal/users/users.go b/internal/users/users.go index ddb858b..861bad3 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -57,7 +57,9 @@ func HandlePost(post *model.Post, mm *mmclient.MMClient, c cache.Cache) error { if ok { persisted.Message = post.Message - c.Put(key, persisted) + + ub, _ := json.Marshal(persisted) + c.Put(key, ub) } return err @@ -72,13 +74,12 @@ func GetUser(username string, c cache.Cache) (User, bool, error) { } if ok { - var user User - user, err = getUserFromUnknownType(u, user, err) + usr, err := getUserFromUnknownType(u, err) if err != nil { return User{}, false, fmt.Errorf("error unmarshalling user: %v", err) } - return user, ok, nil + return usr, ok, nil } return User{}, ok, nil @@ -95,7 +96,7 @@ func GetUsers(c cache.Cache) ([]User, bool, error) { if ok { var user User - user, err = getUserFromUnknownType(u, user, err) + user, err = getUserFromUnknownType(u, err) users = append(users, user) } else { break @@ -105,16 +106,16 @@ func GetUsers(c cache.Cache) ([]User, bool, error) { return users, true, err } -func getUserFromUnknownType(u interface{}, user User, err error) (User, error) { +func getUserFromUnknownType(u interface{}, err error) (User, error) { + var user User + if reflect.TypeOf(u).String() != "[]uint8" { - var user User - var jm map[string]interface{} - err = json.Unmarshal([]byte(u.(string)), &jm) - jb, _ := json.Marshal(jm) - err = json.Unmarshal(jb, &user) - } else { - err = json.Unmarshal(u.([]byte), &user) + err := json.Unmarshal([]byte(u.(string)), &user) + return user, err } + + err = json.Unmarshal(u.([]byte), &user) + return user, err } From d2383f3485a5502f2465778ab248637a2e6e5358 Mon Sep 17 00:00:00 2001 From: Kevin Payne Date: Sun, 2 Oct 2022 10:14:23 -0600 Subject: [PATCH 09/22] add another message on reload --- internal/handler/command-handler.go | 9 ++++++++- internal/handler/handler.go | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/internal/handler/command-handler.go b/internal/handler/command-handler.go index 2175ded..8c7f921 100644 --- a/internal/handler/command-handler.go +++ b/internal/handler/command-handler.go @@ -1,6 +1,7 @@ package handler import ( + "encoding/json" "log" "strings" "time" @@ -86,7 +87,13 @@ func (h *Handler) HandleCommand(quit chan bool, event *model.WebSocketEvent) err log.Print(err) } - h.Cache.Put("sys_restarted_by_user", post.UserId) + cache := map[string]interface{}{ + "user": post.UserId, + "channel": r.Channel, + } + cj, _ := json.Marshal(cache) + + h.Cache.Put("sys_restarted_by_user", cj) quit <- true } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 3f5936b..c7c0f2e 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -1,6 +1,7 @@ package handler import ( + "encoding/json" "fmt" "log" "regexp" @@ -24,16 +25,22 @@ func NewHandler(mm *mmclient.MMClient, botCache cache.Cache) (*Handler, error) { settings, err := settings.NewSettings(mm.SettingsUrl) users.SetupUsers(mm, botCache) - juid, ok, err := botCache.Get("sys_restarted_by_user") + rb, ok, err := botCache.Get("sys_restarted_by_user") if ok { - usr := strings.Trim(juid.(string), `"`) - c, _, err := mm.Client.CreateDirectChannel(usr, mm.BotUser.Id) + var rm map[string]string + json.Unmarshal([]byte(rb.(string)), &rm) + + replyPost := &model.Post{} + + mm.SendMsgToChannel("I'm back, baby!", rm["channel"], replyPost) + + c, _, err := mm.Client.CreateDirectChannel(rm["user"], mm.BotUser.Id) if err != nil { log.Print(err) } - replyPost := &model.Post{} + replyPost.ChannelId = c.Id - replyPost.Message = "I'm back, baby! 😉" + replyPost.Message = "See? 😉" _, _, err = mm.Client.CreatePost(replyPost) if err != nil { From 26eabfe4c978d851bb6d8d4fdcc630d41c4db9bb Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 16:26:05 -0600 Subject: [PATCH 10/22] Started refactor, this is the basic skeleton --- internal/handler/games/rps.go | 57 ++++++++++++++++++++++++++--------- internal/users/users.go | 28 ++++++----------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index e4d722b..dfe8506 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -2,6 +2,7 @@ package games import ( "fmt" + "github.com/pyrousnet/pyrous-gobot/internal/cache" "strings" "github.com/google/uuid" @@ -12,9 +13,19 @@ const ROCK = "rock" const PAPER = "paper" const SCISSORS = "scissors" +type RPS struct { + RpsPlaying string `json:"rps-playing"` + Rps string `json:"rps"` + Name string `json:"name"` +} + func (bg BotGame) Rps(event BotGame) (response Response, err error) { response.Type = "multi" - player, _, err := users.GetUser(strings.TrimLeft(event.sender, "@"), event.cache) + playerUser, _, err := users.GetUser(strings.TrimLeft(event.sender, "@"), event.cache) + if err != nil { + return Response{}, err + } + player, err := getPlayer(playerUser) opponent, oErr := findApponent(event, player) if !playing(player) { if oErr == nil && playing(opponent) { @@ -67,44 +78,45 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { opponent.RpsPlaying = "" } - users.UpdateUser(opponent, event.cache) + updateRps(opponent, event.cache) } - users.UpdateUser(player, event.cache) + updateRps(player, event.cache) return response, err } -func playing(player users.User) bool { +func playing(player RPS) bool { return player.RpsPlaying != "" } -func sameGame(player users.User, opponent users.User) bool { +func sameGame(player RPS, opponent RPS) bool { return player.RpsPlaying == "" || player.RpsPlaying == opponent.RpsPlaying } -func differentUser(player users.User, opponent users.User) bool { +func differentUser(player RPS, opponent RPS) bool { return player.Name != opponent.Name } -func findApponent(event BotGame, forPlayer users.User) (users.User, error) { +func findApponent(event BotGame, forPlayer RPS) (RPS, error) { us, ok, err := users.GetUsers(event.cache) - var opponent users.User + rpsUs, ok, err := getPlayers(us, event.cache) + var opponent RPS var found = false if us == nil { - return users.User{}, fmt.Errorf("no opponent") + return RPS{}, fmt.Errorf("no opponent") } if ok { - for _, u := range us { + for _, u := range rpsUs { if playing(u) && sameGame(forPlayer, u) && differentUser(forPlayer, u) { opponent = u found = true } } } else { - return users.User{}, err + return RPS{}, err } if !found { @@ -113,16 +125,16 @@ func findApponent(event BotGame, forPlayer users.User) (users.User, error) { return opponent, err } -func getWinner(player users.User, opponent users.User) ([]users.User, bool) { +func getWinner(player RPS, opponent RPS) ([]RPS, bool) { var hasWinner bool = false - var winners []users.User + var winners []RPS if player.Rps == "" { - return []users.User{}, false + return []RPS{}, false } if opponent.Rps == "" { - return []users.User{}, false + return []RPS{}, false } if player.Rps == opponent.Rps { @@ -157,3 +169,18 @@ func getWinner(player users.User, opponent users.User) ([]users.User, bool) { return winners, hasWinner } + +func getPlayer(player users.User) (RPS, error) { + // TODO + return RPS{}, nil +} + +func getPlayers(pUsers []users.User, c cache.Cache) ([]RPS, bool, error) { + // TODO + return []RPS{}, true, nil +} + +func updateRps(playerRps RPS, c cache.Cache) (RPS, error) { + // TODO + return RPS{}, nil +} diff --git a/internal/users/users.go b/internal/users/users.go index 861bad3..8f827fd 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -14,15 +14,11 @@ const KeyPrefix = "user-" type ( User struct { - Id string `json:"id"` - Name string `json:"name"` - Message string `json:"message"` - Rps string `json:"rps"` - RpsPlaying string `json:"rps-playing"` - FarkleValue int `json:"farkle-value"` - FarklePlaying string `json:"farkle-playing"` - SyndFeed string `json:"synd-feed"` - FeedCount int `json:"feed-count"` + Id string `json:"id"` + Name string `json:"name"` + Message string `json:"message"` + SyndFeed string `json:"synd-feed"` + FeedCount int `json:"feed-count"` } ) @@ -33,15 +29,11 @@ func SetupUsers(mm *mmclient.MMClient, c cache.Cache) error { user, _, _ := mm.Client.GetUser(u, "") key := KeyPrefix + user.Username newUser := User{ - Id: u, - Name: user.Username, - Message: "", - Rps: "", - RpsPlaying: "", - FarklePlaying: "", - FarkleValue: 0, - SyndFeed: "", - FeedCount: 0, + Id: u, + Name: user.Username, + Message: "", + SyndFeed: "", + FeedCount: 0, } u, _ := json.Marshal(newUser) c.Put(key, u) From a7e64f2937a3f679de983459d0b9a03ba3812071 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 16:41:48 -0600 Subject: [PATCH 11/22] Built Get Users. --- internal/handler/games/rps.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index dfe8506..909dcbf 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -12,6 +12,7 @@ import ( const ROCK = "rock" const PAPER = "paper" const SCISSORS = "scissors" +const PREFIX = "rps-" type RPS struct { RpsPlaying string `json:"rps-playing"` @@ -176,8 +177,18 @@ func getPlayer(player users.User) (RPS, error) { } func getPlayers(pUsers []users.User, c cache.Cache) ([]RPS, bool, error) { - // TODO - return []RPS{}, true, nil + var rpsUsers []RPS + for _, u := range pUsers { + rps, ok, _ := c.Get(PREFIX + u.Name) + + if ok { + rpsUsers = append(rpsUsers, rps.(RPS)) + } + } + if len(rpsUsers) == 0 { + return []RPS{}, false, nil + } + return rpsUsers, true, nil } func updateRps(playerRps RPS, c cache.Cache) (RPS, error) { From b27fffb6250ddd7047f380c5cc4c4f31b7aec4b6 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 19:37:28 -0600 Subject: [PATCH 12/22] Continued work on refactor. RPS still does not function. --- internal/handler/games/rps.go | 73 ++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 909dcbf..8b05881 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -1,8 +1,10 @@ package games import ( + "encoding/json" "fmt" "github.com/pyrousnet/pyrous-gobot/internal/cache" + "reflect" "strings" "github.com/google/uuid" @@ -26,20 +28,20 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { if err != nil { return Response{}, err } - player, err := getPlayer(playerUser) - opponent, oErr := findApponent(event, player) - if !playing(player) { + player, perr := getPlayer(playerUser, event.ReplyChannel.Id, event.cache) + opponent, oErr := findApponent(event, player, event.ReplyChannel.Id) + if perr != nil || !playing(player) { if oErr == nil && playing(opponent) { channelId, ok, _ := event.cache.Get(opponent.RpsPlaying) if ok && event.ReplyChannel != nil && channelId == event.ReplyChannel.Id { player.RpsPlaying = opponent.RpsPlaying response.Type = "dm" - response.Message = "Would you like to throw Rock, Paper or Scissors (Usage: $rps rock)" + response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps %s rock)", event.ReplyChannel.Name) } } else { id, e := uuid.NewRandom() event.cache.Put(id.String(), event.ReplyChannel.Id) - response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps rock)##%s is looking for an opponent in RPS.", event.sender) + response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps %s rock)##%s is looking for an opponent in RPS.", event.ReplyChannel.Name, event.sender) if e != nil { return response, e } @@ -48,14 +50,20 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { } if event.body != "" { - switch strings.ToLower(event.body) { + var choice, channel string + fmt.Sscanf(event.body, "%s %s", &channel, &choice) + foundChannel, cErr := event.mm.GetChannelByName(channel) + if cErr == nil { + response.Channel = foundChannel.Id + } + switch strings.ToLower(choice) { case "rock", "paper", "scissors": - player.Rps = strings.ToLower(event.body) + player.Rps = strings.ToLower(choice) response.Type = "dm" - response.Message = fmt.Sprintf("I have you down for: %s", strings.Title(strings.ToLower(event.body))) + response.Message = fmt.Sprintf("I have you down for: %s", strings.Title(strings.ToLower(choice))) default: response.Type = "dm" - response.Message = fmt.Sprintf(`Uh, %s isn't an option. Try rock, paper or scissors'`, event.body) + response.Message = fmt.Sprintf(`Uh, %s isn't an option. Try rock, paper or scissors'`, choice) } } @@ -79,10 +87,10 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { opponent.RpsPlaying = "" } - updateRps(opponent, event.cache) + updateRps(opponent, event.ReplyChannel.Id, event.cache) } - updateRps(player, event.cache) + updateRps(player, event.ReplyChannel.Id, event.cache) return response, err } @@ -99,9 +107,9 @@ func differentUser(player RPS, opponent RPS) bool { return player.Name != opponent.Name } -func findApponent(event BotGame, forPlayer RPS) (RPS, error) { +func findApponent(event BotGame, forPlayer RPS, chanId string) (RPS, error) { us, ok, err := users.GetUsers(event.cache) - rpsUs, ok, err := getPlayers(us, event.cache) + rpsUs, ok, err := getPlayers(us, chanId, event.cache) var opponent RPS var found = false @@ -171,18 +179,35 @@ func getWinner(player RPS, opponent RPS) ([]RPS, bool) { return winners, hasWinner } -func getPlayer(player users.User) (RPS, error) { - // TODO - return RPS{}, nil +func getPlayer(player users.User, chanId string, c cache.Cache) (RPS, error) { + var key string = PREFIX + player.Name + "-" + chanId + var rps RPS + r, ok, _ := c.Get(key) + if ok { + if reflect.TypeOf(r).String() != "[]uint8" { + json.Unmarshal([]byte(r.(string)), &rps) + } else { + json.Unmarshal(r.([]byte), &rps) + } + return rps, nil + } + return RPS{Name: player.Name}, fmt.Errorf("not found") } -func getPlayers(pUsers []users.User, c cache.Cache) ([]RPS, bool, error) { +func getPlayers(pUsers []users.User, chanId string, c cache.Cache) ([]RPS, bool, error) { var rpsUsers []RPS for _, u := range pUsers { - rps, ok, _ := c.Get(PREFIX + u.Name) - + var key string = PREFIX + u.Name + "-" + chanId + r, ok, _ := c.Get(key) + var rps RPS if ok { - rpsUsers = append(rpsUsers, rps.(RPS)) + if reflect.TypeOf(r).String() != "[]uint8" { + json.Unmarshal([]byte(r.(string)), &rps) + } else { + json.Unmarshal(r.([]byte), &rps) + } + + rpsUsers = append(rpsUsers, rps) } } if len(rpsUsers) == 0 { @@ -191,7 +216,9 @@ func getPlayers(pUsers []users.User, c cache.Cache) ([]RPS, bool, error) { return rpsUsers, true, nil } -func updateRps(playerRps RPS, c cache.Cache) (RPS, error) { - // TODO - return RPS{}, nil +func updateRps(playerRps RPS, chanId string, c cache.Cache) (RPS, error) { + var key string = PREFIX + playerRps.Name + "-" + chanId + p, _ := json.Marshal(playerRps) + c.Put(key, p) + return playerRps, nil } From 1c9832e64d1d6b3ab4b85c9c41afd88e7f29619b Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 19:59:29 -0600 Subject: [PATCH 13/22] Fixed issue with channel. Still doesn't finish. --- internal/handler/games/rps.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 8b05881..e2be26f 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -24,16 +24,24 @@ type RPS struct { func (bg BotGame) Rps(event BotGame) (response Response, err error) { response.Type = "multi" + var choice, channel string + fmt.Sscanf(event.body, "%s %s", &channel, &choice) + foundChannel, cErr := event.mm.GetChannelByName(channel) + if cErr == nil && foundChannel != nil { + response.Channel = foundChannel.Id + } else { + foundChannel = event.ReplyChannel + } playerUser, _, err := users.GetUser(strings.TrimLeft(event.sender, "@"), event.cache) if err != nil { return Response{}, err } - player, perr := getPlayer(playerUser, event.ReplyChannel.Id, event.cache) - opponent, oErr := findApponent(event, player, event.ReplyChannel.Id) + player, perr := getPlayer(playerUser, foundChannel.Id, event.cache) + opponent, oErr := findApponent(event, player, foundChannel.Id) if perr != nil || !playing(player) { if oErr == nil && playing(opponent) { channelId, ok, _ := event.cache.Get(opponent.RpsPlaying) - if ok && event.ReplyChannel != nil && channelId == event.ReplyChannel.Id { + if ok && event.ReplyChannel != nil && channelId == foundChannel.Id { player.RpsPlaying = opponent.RpsPlaying response.Type = "dm" response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps %s rock)", event.ReplyChannel.Name) @@ -50,12 +58,6 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { } if event.body != "" { - var choice, channel string - fmt.Sscanf(event.body, "%s %s", &channel, &choice) - foundChannel, cErr := event.mm.GetChannelByName(channel) - if cErr == nil { - response.Channel = foundChannel.Id - } switch strings.ToLower(choice) { case "rock", "paper", "scissors": player.Rps = strings.ToLower(choice) @@ -67,7 +69,7 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { } } - if oErr == nil { + if oErr == nil && opponent.Name != "" { winners, hasWinner := getWinner(player, opponent) if hasWinner { channelId, ok, _ := event.cache.Get(player.RpsPlaying) @@ -109,7 +111,7 @@ func differentUser(player RPS, opponent RPS) bool { func findApponent(event BotGame, forPlayer RPS, chanId string) (RPS, error) { us, ok, err := users.GetUsers(event.cache) - rpsUs, ok, err := getPlayers(us, chanId, event.cache) + rpsUs, ok, gPerr := getPlayers(us, chanId, event.cache) var opponent RPS var found = false @@ -125,7 +127,7 @@ func findApponent(event BotGame, forPlayer RPS, chanId string) (RPS, error) { } } } else { - return RPS{}, err + return RPS{}, gPerr } if !found { From 8dd2e794b748fa02aeb06681897c5c502a7de3f9 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 20:19:36 -0600 Subject: [PATCH 14/22] Use channel when updating user. --- internal/handler/games/rps.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index e2be26f..810c1e1 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -89,10 +89,10 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { opponent.RpsPlaying = "" } - updateRps(opponent, event.ReplyChannel.Id, event.cache) + updateRps(opponent, foundChannel.Id, event.cache) } - updateRps(player, event.ReplyChannel.Id, event.cache) + updateRps(player, foundChannel.Id, event.cache) return response, err } From aa888027bbdd91c5c5721a35275d257b5a86abb9 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 2 Oct 2022 21:00:54 -0600 Subject: [PATCH 15/22] Clean RPS when finished. --- internal/handler/games/rps.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 810c1e1..2415f55 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -83,10 +83,8 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { } } - player.Rps = "" - player.RpsPlaying = "" - opponent.Rps = "" - opponent.RpsPlaying = "" + deleteRps(player, foundChannel.Id, event.cache) + deleteRps(opponent, foundChannel.Id, event.cache) } updateRps(opponent, foundChannel.Id, event.cache) @@ -224,3 +222,8 @@ func updateRps(playerRps RPS, chanId string, c cache.Cache) (RPS, error) { c.Put(key, p) return playerRps, nil } + +func deleteRps(playerRps RPS, chanId string, c cache.Cache) { + var key string = PREFIX + playerRps.Name + "-" + chanId + c.Clean(key) +} From 4682b6c8343e32d19a1ea61ce2cabd614f34d7df Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Mon, 3 Oct 2022 10:13:27 -0600 Subject: [PATCH 16/22] Deletes game from redis. --- internal/handler/games/rps.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 2415f55..0803d38 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -83,6 +83,7 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { } } + deleteGame(player.RpsPlaying, event.cache) deleteRps(player, foundChannel.Id, event.cache) deleteRps(opponent, foundChannel.Id, event.cache) } @@ -227,3 +228,6 @@ func deleteRps(playerRps RPS, chanId string, c cache.Cache) { var key string = PREFIX + playerRps.Name + "-" + chanId c.Clean(key) } +func deleteGame(uuid string, c cache.Cache) { + c.Clean(uuid) +} From bfb806be250301fba1581a1b6305359038b41d48 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Mon, 3 Oct 2022 11:06:20 -0600 Subject: [PATCH 17/22] Fixed key handling in RPS. --- internal/cache/cache.go | 2 +- internal/cache/local.go | 4 ++-- internal/cache/redis.go | 4 ++-- internal/handler/games/rps.go | 11 ++++++----- internal/users/users.go | 5 ++++- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 4d215d8..3b41be2 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -10,7 +10,7 @@ type Cache interface { Get(key string) (interface{}, bool, error) GetAll(keys []string) map[string]interface{} Clean(key string) - GetKeys(prefix string) []string + GetKeys(prefix string) ([]string, error) CleanAll() } diff --git a/internal/cache/local.go b/internal/cache/local.go index 2cbac73..7ef323c 100644 --- a/internal/cache/local.go +++ b/internal/cache/local.go @@ -58,7 +58,7 @@ func (c *LocalCache) CleanAll() { c.mu.Unlock() } -func (c *LocalCache) GetKeys(prefix string) []string { +func (c *LocalCache) GetKeys(prefix string) ([]string, error) { keys := make([]string, 0, len(c.data)) for k, _ := range c.data { if strings.Contains(k, prefix) { @@ -66,5 +66,5 @@ func (c *LocalCache) GetKeys(prefix string) []string { } } - return keys + return keys, nil } diff --git a/internal/cache/redis.go b/internal/cache/redis.go index 0c542cc..3b6427d 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -75,6 +75,6 @@ func (rc *RedisCache) CleanAll() { rc.conn.FlushDB(rc.ctx) } -func (rc *RedisCache) GetKeys(prefix string) []string { - return rc.conn.Keys(rc.ctx, prefix+"_*").Val() +func (rc *RedisCache) GetKeys(prefix string) ([]string, error) { + return rc.conn.Keys(rc.ctx, prefix+"*").Result() } diff --git a/internal/handler/games/rps.go b/internal/handler/games/rps.go index 0803d38..c7b3087 100644 --- a/internal/handler/games/rps.go +++ b/internal/handler/games/rps.go @@ -14,7 +14,7 @@ import ( const ROCK = "rock" const PAPER = "paper" const SCISSORS = "scissors" -const PREFIX = "rps-" +const PREFIX = "rps" type RPS struct { RpsPlaying string `json:"rps-playing"` @@ -86,6 +86,7 @@ func (bg BotGame) Rps(event BotGame) (response Response, err error) { deleteGame(player.RpsPlaying, event.cache) deleteRps(player, foundChannel.Id, event.cache) deleteRps(opponent, foundChannel.Id, event.cache) + return response, err } updateRps(opponent, foundChannel.Id, event.cache) @@ -181,7 +182,7 @@ func getWinner(player RPS, opponent RPS) ([]RPS, bool) { } func getPlayer(player users.User, chanId string, c cache.Cache) (RPS, error) { - var key string = PREFIX + player.Name + "-" + chanId + key := fmt.Sprintf("%s-%s-%s", PREFIX, player.Name, chanId) var rps RPS r, ok, _ := c.Get(key) if ok { @@ -198,7 +199,7 @@ func getPlayer(player users.User, chanId string, c cache.Cache) (RPS, error) { func getPlayers(pUsers []users.User, chanId string, c cache.Cache) ([]RPS, bool, error) { var rpsUsers []RPS for _, u := range pUsers { - var key string = PREFIX + u.Name + "-" + chanId + key := fmt.Sprintf("%s-%s-%s", PREFIX, u.Name, chanId) r, ok, _ := c.Get(key) var rps RPS if ok { @@ -218,14 +219,14 @@ func getPlayers(pUsers []users.User, chanId string, c cache.Cache) ([]RPS, bool, } func updateRps(playerRps RPS, chanId string, c cache.Cache) (RPS, error) { - var key string = PREFIX + playerRps.Name + "-" + chanId + key := fmt.Sprintf("%s-%s-%s", PREFIX, playerRps.Name, chanId) p, _ := json.Marshal(playerRps) c.Put(key, p) return playerRps, nil } func deleteRps(playerRps RPS, chanId string, c cache.Cache) { - var key string = PREFIX + playerRps.Name + "-" + chanId + key := fmt.Sprintf("%s-%s-%s", PREFIX, playerRps.Name, chanId) c.Clean(key) } func deleteGame(uuid string, c cache.Cache) { diff --git a/internal/users/users.go b/internal/users/users.go index 8f827fd..aaa3ec0 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -78,7 +78,10 @@ func GetUser(username string, c cache.Cache) (User, bool, error) { } func GetUsers(c cache.Cache) ([]User, bool, error) { - userKeys := c.GetKeys(KeyPrefix) + userKeys, kerr := c.GetKeys(KeyPrefix) + if kerr != nil { + return []User{}, false, kerr + } var users []User var err error From 819ac1db289d85c80d1a37b2f70b2134d4df56d3 Mon Sep 17 00:00:00 2001 From: Josh Payne Date: Mon, 10 Oct 2022 22:20:13 -0700 Subject: [PATCH 18/22] Initial work for Farkle. Lots more to do --- internal/handler/games/farkle.go | 258 +++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 internal/handler/games/farkle.go diff --git a/internal/handler/games/farkle.go b/internal/handler/games/farkle.go new file mode 100644 index 0000000..d6533da --- /dev/null +++ b/internal/handler/games/farkle.go @@ -0,0 +1,258 @@ +package games + +import ( + "encoding/json" + "fmt" + "github.com/pyrousnet/pyrous-gobot/internal/cache" + "reflect" + "math/rand" + "time" + "strings" + + "github.com/google/uuid" + "github.com/pyrousnet/pyrous-gobot/internal/users" +) + +type Farkle struct { + GameID string `json:"game-id"` //ID of this farkle game + FarklePlaying bool `json:"farkle-playing"` //If the game has started + FarkleMembers []string `json:"farkle-members"` //Members of the farkle game + FarkleTotal int `json:"farkle-total"` //TODO: How to track scores + FarkleTemp int `json:"farkle-temp"` //Track current rolling total + DiceHolder string `json:"dice-holder"` //User currently holding the dice + DiceThrown []int `json:"dice-thrown"` //Current dice list + DiceToThrow int `json:"dice-to-throw"` //Number of dice which can be thrown + Name string `json:"name"` //Name of the farkle user. +} + +var DICE = 6; + +func (h BotGameHelp) Farkle(request BotGame) (response HelpResponse) { + response.Help = "Play Farkle in the current channel." + response.Description = `Play a game of Farkle!\n + Usage: $farklestart - Create a new game of Farkle. + Usage: $farklejoin - Join a game of Farkle already created. + Usage: $farkle - Throw your dice in the current game of Farkle (if they are yours to throw). + Usage: $farkle n # - Attempt to rethrow # of dice. + Usage: $farkle y - Keeps all the dice and ends your turn.` //TODO: $ should come from settings + + return response +} + +func (bg BotGame) Farkle(event BotGame) (response Response, err error) { + response.Type = "multi" + var choice, channel string + + fmt.Sscanf(event.body, "%s %s", &channel, &choice) //TODO: Do we want to allow joining from other channels? + foundChannel, cErr := event.mm.GetChannelByName(channel) + if cErr == nil && foundChannel != nil { + response.Channel = foundChannel.Id + } else { + foundChannel = event.ReplyChannel + } + playerUser, _, err := users.GetUser(strings.TrimLeft(event.sender, "@"), event.cache) //Gets the current user's details + if err != nil { + return Response{}, err + } + player, perr := getPlayer(playerUser, foundChannel.Id, event.cache) + opponent, oErr := findApponent(event, player, foundChannel.Id) + if perr != nil || !playing(player) { + if oErr == nil && playing(opponent) { + channelId, ok, _ := event.cache.Get(opponent.RpsPlaying) + if ok && event.ReplyChannel != nil && channelId == foundChannel.Id { + player.RpsPlaying = opponent.RpsPlaying + response.Type = "dm" + response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps %s rock)", event.ReplyChannel.Name) + } + } else { //TODO: This is the logic to start a game. + id, e := uuid.NewRandom() + event.cache.Put(id.String(), event.ReplyChannel.Id) + response.Message = fmt.Sprintf("Would you like to throw Rock, Paper or Scissors (Usage: $rps %s rock)##%s is looking for an opponent in RPS.", event.ReplyChannel.Name, event.sender) + if e != nil { + return response, e + } + player.RpsPlaying = id.String() //TODO: This is where the player is setup. + } + } + + if event.body != "" { + switch strings.ToLower(choice) { + case "rock", "paper", "scissors": + player.Rps = strings.ToLower(choice) + response.Type = "dm" + response.Message = fmt.Sprintf("I have you down for: %s", strings.Title(strings.ToLower(choice))) + default: + response.Type = "dm" + response.Message = fmt.Sprintf(`Uh, %s isn't an option. Try rock, paper or scissors'`, choice) + } + } + + if oErr == nil && opponent.Name != "" { + winners, hasWinner := getWinner(player, opponent) + if hasWinner { + channelId, ok, _ := event.cache.Get(player.RpsPlaying) + response.Type = "command" + if ok { + response.Channel = channelId.(string) + if len(winners) > 1 { + response.Message = fmt.Sprintf("/echo The RPS game between %s and %s ended in a draw.", player.Name, opponent.Name) + } else { + response.Message = fmt.Sprintf("/echo The RPS game between %s and %s ended with %s winning.", player.Name, opponent.Name, winners[0].Name) + } + } + + deleteGame(player.RpsPlaying, event.cache) + deleteRps(player, foundChannel.Id, event.cache) + deleteRps(opponent, foundChannel.Id, event.cache) + return response, err + } + + updateRps(opponent, foundChannel.Id, event.cache) + } + + updateRps(player, foundChannel.Id, event.cache) + + return response, err +} + +func checkGame(players Farkle) (Farkle, error) { +} + +func playing(player Farkle) bool { + return player.GameID != "" +} + +func getPlayer(player users.User, chanId string, c cache.Cache) (Farkle, error) { + key := fmt.Sprintf("%s-%s-%s", "farkle", player.Name, chanId) + var farkle Farkle + r, ok, _ := c.Get(key) + if ok { + if reflect.TypeOf(r).String() != "[]uint8" { + json.Unmarshal([]byte(r.(string)), &farkle) + } else { + json.Unmarshal(r.([]byte), &farkle) + } + return farkle, nil + } + return Farkle{Name: player.Name}, fmt.Errorf("not found") +} + + +//TODO: modify or remove the following functions. + +func sameGame(player RPS, opponent RPS) bool { + return player.RpsPlaying == "" || player.RpsPlaying == opponent.RpsPlaying +} + +func differentUser(player RPS, opponent RPS) bool { + return player.Name != opponent.Name +} + +func findApponent(event BotGame, forPlayer RPS, chanId string) (RPS, error) { + us, ok, err := users.GetUsers(event.cache) + rpsUs, ok, gPerr := getPlayers(us, chanId, event.cache) + var opponent RPS + var found = false + + if us == nil { + return RPS{}, fmt.Errorf("no opponent") + } + + if ok { + for _, u := range rpsUs { + if playing(u) && sameGame(forPlayer, u) && differentUser(forPlayer, u) { + opponent = u + found = true + } + } + } else { + return RPS{}, gPerr + } + + if !found { + err = fmt.Errorf("no opponent") + } + return opponent, err +} + +func getWinner(player RPS, opponent RPS) ([]RPS, bool) { + var hasWinner bool = false + var winners []RPS + + if player.Rps == "" { + return []RPS{}, false + } + + if opponent.Rps == "" { + return []RPS{}, false + } + + if player.Rps == opponent.Rps { + winners = append(winners, player) + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(player.Rps) == "rock" { + if strings.ToLower(opponent.Rps) == "paper" { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == "scissors" { + winners = append(winners, player) + hasWinner = true + } + } else if strings.ToLower(player.Rps) == "paper" { + if strings.ToLower(opponent.Rps) == "scissors" { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == "rock" { + winners = append(winners, player) + hasWinner = true + } + } else if strings.ToLower(player.Rps) == "scissors" { + if strings.ToLower(opponent.Rps) == "rock" { + winners = append(winners, opponent) + hasWinner = true + } else if strings.ToLower(opponent.Rps) == "paper" { + winners = append(winners, player) + hasWinner = true + } + } + + return winners, hasWinner +} + +func getPlayers(pUsers []users.User, chanId string, c cache.Cache) ([]RPS, bool, error) { + var rpsUsers []RPS + for _, u := range pUsers { + key := fmt.Sprintf("%s-%s-%s", "rps", u.Name, chanId) + r, ok, _ := c.Get(key) + var rps RPS + if ok { + if reflect.TypeOf(r).String() != "[]uint8" { + json.Unmarshal([]byte(r.(string)), &rps) + } else { + json.Unmarshal(r.([]byte), &rps) + } + + rpsUsers = append(rpsUsers, rps) + } + } + if len(rpsUsers) == 0 { + return []RPS{}, false, nil + } + return rpsUsers, true, nil +} + +func updateRps(playerRps RPS, chanId string, c cache.Cache) (RPS, error) { + key := fmt.Sprintf("%s-%s-%s", "rps", playerRps.Name, chanId) + p, _ := json.Marshal(playerRps) + c.Put(key, p) + return playerRps, nil +} + +func deleteRps(playerRps RPS, chanId string, c cache.Cache) { + key := fmt.Sprintf("%s-%s-%s", "rps", playerRps.Name, chanId) + c.Clean(key) +} +func deleteGame(uuid string, c cache.Cache) { + c.Clean(uuid) +} From 2c1b51dc378a4708097fadb72ac4c294f94e9ade Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Wed, 11 Jan 2023 20:58:16 -0700 Subject: [PATCH 19/22] Move web socket to config --- internal/mmclient/mmclient.go | 11 ++++++----- internal/settings/config.go | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/mmclient/mmclient.go b/internal/mmclient/mmclient.go index 63107f5..3872128 100644 --- a/internal/mmclient/mmclient.go +++ b/internal/mmclient/mmclient.go @@ -24,10 +24,11 @@ type MMClient struct { } type Server struct { - HOST string `yaml:"host"` - PROTOCOL string `yaml:"protocol"` - PORT string `yaml:"port"` - CACHE_URI string `yaml:"cache_uri"` + HOST string `yaml:"host"` + PROTOCOL string `yaml:"protocol"` + PORT string `yaml:"port"` + CACHE_URI string `yaml:"cache_uri"` + WS_PROTOCOL string `yaml:"ws_protocol"` } // Documentation for the Go driver can be found @@ -240,7 +241,7 @@ func (c *MMClient) SendMsgToChannel(msg string, channelId string, prePost *model func (c *MMClient) NewWebSocketClient() (*model.WebSocketClient, error) { var err error - uri := fmt.Sprintf("wss://%s:%s", c.Server.HOST, c.Server.PORT) + uri := fmt.Sprintf("%s://%s:%s", c.Server.WS_PROTOCOL, c.Server.HOST, c.Server.PORT) ws, appErr := model.NewWebSocketClient4(uri, c.Client.AuthToken) if appErr != nil { diff --git a/internal/settings/config.go b/internal/settings/config.go index 6b829d3..ad45a6c 100644 --- a/internal/settings/config.go +++ b/internal/settings/config.go @@ -8,10 +8,11 @@ import ( type Config struct { Server struct { - HOST string `yaml:"host"` - PROTOCOL string `yaml:"protocol"` - PORT string `yaml:"port"` - CACHE_URI string `yaml:"cache_uri"` + HOST string `yaml:"host"` + PROTOCOL string `yaml:"protocol"` + PORT string `yaml:"port"` + CACHE_URI string `yaml:"cache_uri"` + WS_PROTOCOL string `yaml:"ws_protocol"` } `yaml:"server"` Bot struct { SAMPLE_NAME string `yaml:"sample_name"` From c205304fb914ad8309b9ac6500937ee29b799025 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Wed, 18 Jan 2023 18:23:04 -0700 Subject: [PATCH 20/22] Fixes the say command for DMs. --- internal/comms/comms.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/comms/comms.go b/internal/comms/comms.go index d19eaf7..7e2fd39 100644 --- a/internal/comms/comms.go +++ b/internal/comms/comms.go @@ -47,13 +47,23 @@ func (h *MessageHandler) SendMessage(r *Response) { } } + dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.Type != "shutdown" { - dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.ReplyChannelId == dmchannel.Id { r.Type = "dm" } } + if r.Type == "dm" { + if strings.HasPrefix(checkMsg[0], "/") { + commandParts := strings.Split(r.Message, "\"") + if commandParts[1] != "" { + post.Message = commandParts[1] + } + } + + } + if r.Message != "" { switch r.Type { case "post": @@ -61,12 +71,11 @@ func (h *MessageHandler) SendMessage(r *Response) { case "command": err = h.Mm.SendCmdToChannel(r.Message, r.ReplyChannelId, post) case "dm": - c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if err != nil { panic(err) } - post.ChannelId = c.Id + post.ChannelId = dmchannel.Id _, _, err = h.Mm.Client.CreatePost(post) case "shutdown": c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) From f8e1a4685988dde5d2afcbae87ec94cae4cfcb07 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Thu, 19 Jan 2023 10:12:48 -0700 Subject: [PATCH 21/22] Revert "Fixes the say command for DMs." --- internal/comms/comms.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/internal/comms/comms.go b/internal/comms/comms.go index 7e2fd39..d19eaf7 100644 --- a/internal/comms/comms.go +++ b/internal/comms/comms.go @@ -47,23 +47,13 @@ func (h *MessageHandler) SendMessage(r *Response) { } } - dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.Type != "shutdown" { + dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.ReplyChannelId == dmchannel.Id { r.Type = "dm" } } - if r.Type == "dm" { - if strings.HasPrefix(checkMsg[0], "/") { - commandParts := strings.Split(r.Message, "\"") - if commandParts[1] != "" { - post.Message = commandParts[1] - } - } - - } - if r.Message != "" { switch r.Type { case "post": @@ -71,11 +61,12 @@ func (h *MessageHandler) SendMessage(r *Response) { case "command": err = h.Mm.SendCmdToChannel(r.Message, r.ReplyChannelId, post) case "dm": + c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if err != nil { panic(err) } - post.ChannelId = dmchannel.Id + post.ChannelId = c.Id _, _, err = h.Mm.Client.CreatePost(post) case "shutdown": c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) From 5bc840c3138b1eb5636b22ee3f1239929bfea532 Mon Sep 17 00:00:00 2001 From: Benjamin Payne Date: Sun, 22 Jan 2023 15:52:58 -0700 Subject: [PATCH 22/22] Revert "Revert "Fixes the say command for DMs."" --- internal/comms/comms.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/comms/comms.go b/internal/comms/comms.go index d19eaf7..7e2fd39 100644 --- a/internal/comms/comms.go +++ b/internal/comms/comms.go @@ -47,13 +47,23 @@ func (h *MessageHandler) SendMessage(r *Response) { } } + dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.Type != "shutdown" { - dmchannel, _, _ := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if r.ReplyChannelId == dmchannel.Id { r.Type = "dm" } } + if r.Type == "dm" { + if strings.HasPrefix(checkMsg[0], "/") { + commandParts := strings.Split(r.Message, "\"") + if commandParts[1] != "" { + post.Message = commandParts[1] + } + } + + } + if r.Message != "" { switch r.Type { case "post": @@ -61,12 +71,11 @@ func (h *MessageHandler) SendMessage(r *Response) { case "command": err = h.Mm.SendCmdToChannel(r.Message, r.ReplyChannelId, post) case "dm": - c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id) if err != nil { panic(err) } - post.ChannelId = c.Id + post.ChannelId = dmchannel.Id _, _, err = h.Mm.Client.CreatePost(post) case "shutdown": c, _, err := h.Mm.Client.CreateDirectChannel(r.UserId, h.Mm.BotUser.Id)