From 9b5ad472c75456db1e2b9607a32f1ffdb93749c2 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Fri, 28 Jun 2019 11:03:42 +0200 Subject: [PATCH] Refactor messages handling --- draftbot/draftbot.go | 135 +++++++++++++--------------------- draftbot/draftbot.messages.go | 133 +++++++++++++++++++++++++++++++++ draftbot/draftbot_test.go | 42 +++++++++++ draftbot/go.mod | 2 +- draftbot/go.sum | 2 + draftbot/main.go | 4 +- 6 files changed, 231 insertions(+), 87 deletions(-) create mode 100644 draftbot/draftbot.messages.go create mode 100644 draftbot/draftbot_test.go diff --git a/draftbot/draftbot.go b/draftbot/draftbot.go index 2fbae07..22f778c 100644 --- a/draftbot/draftbot.go +++ b/draftbot/draftbot.go @@ -1,13 +1,11 @@ package main import ( - "fmt" - room "git.fromouter.space/mcg/cardgage/room/api" - "git.fromouter.space/mcg/draft" ) -type draftBot struct { +// DraftBot is the functional part of draftbot +type DraftBot struct { API BotInterface Name string Rooms map[string]roomInfo @@ -25,15 +23,18 @@ type BotInterface interface { Send(room.BotMessage) } -func newDraftBot(botAPI BotInterface, name string) *draftBot { - return &draftBot{ +// NewDraftBot creates a new draft bot instance with the given name +// and communication interface +func NewDraftBot(botAPI BotInterface, name string) *DraftBot { + return &DraftBot{ API: botAPI, Name: name, Sessions: make(map[string]session), } } -func (d *draftBot) onMessage(msg room.ServerMessage) { +// OnMessage is the function to be called when messages are received +func (d *DraftBot) OnMessage(msg room.ServerMessage) { switch msg.Type { case room.MsgMessage: if *logAll { @@ -53,10 +54,11 @@ func (d *draftBot) onMessage(msg room.ServerMessage) { "roomid", msg.RoomID, "content", msg.Event.Message) } + d.handleEvent(msg.RoomID, *msg.Event) } } -func (d *draftBot) sendMessage(roomid string, msg room.Message) { +func (d *DraftBot) sendMessage(roomid string, msg room.Message) { d.API.Send(room.BotMessage{ RoomID: roomid, Type: room.MsgMessage, @@ -64,96 +66,61 @@ func (d *draftBot) sendMessage(roomid string, msg room.Message) { }) } -func (d *draftBot) handleMessage(roomid string, msg room.Message) { +func (d *DraftBot) handleMessage(roomid string, msg room.Message) { // Get session in room session, ok := d.Sessions[roomid] if !ok { // Room does not have a currently running session, ignore unless it's the owner asking for specific stuff - //TODO - + d.commands(commandMap{ + // Owner wants to create a session + "create": d.cmdfnOnlyOwner(d.cmdCreateSession), + })(roomid, msg) return } // Check if player is in the draft - player, ok := session.Players[msg.From] + _, ok = session.Players[msg.From] if !ok { // Player not in draft, are they asking to join? - switch msg.Type { - // Player is asking to join - case "join": - // Players can only join if session didn't start yet - if session.started { - d.sendMessage(roomid, room.Message{ - To: msg.From, - Type: "session-already-started", - Message: "You can't join a running session", - }) - return - } - - // Check if there are still open slots - if len(session.Players)+1 > len(session.Pod.Players) { - d.sendMessage(roomid, room.Message{ - To: msg.From, - Type: "session-full", - Message: "There aren't any spots left", - }) - return - } - - // Add player to the list - session.Players[msg.From] = nil - - d.sendMessage(roomid, room.Message{ - Channel: "draft", - Type: "player-joined-session", - Data: msg.From, - Message: fmt.Sprintf("%s joined the draft session (%d players, %d missing)", msg.From, len(session.Players), len(session.Pod.Players)-len(session.Players)), - }) - - // Player wants something else - default: - // Tell them can either join or GTFO - d.sendMessage(roomid, room.Message{ - To: msg.From, - Type: "not-joined-err", - Data: "not joined", - Message: "You must join the session to do anything", - }) - return - } - + d.commands(commandMap{ + // Player wants to join the session + "join": d.cmdJoinSession, + })(roomid, msg) return } - switch msg.Type { - // Player has picked a card - case "pick": - // Get picked card - picked := msg.Data.(string) + // Players in the draft session + d.commands(commandMap{ + // Player has picked a card + "pick": d.cmdPickCard, - // Try to pick on player struct - err := player.Pick(draft.Card{ID: picked}) - if err != nil { - if err == draft.ErrNotInPack { - d.sendMessage(roomid, room.Message{ - To: msg.From, - Type: "invalid-pick", - }) - } else { - // Technically not needed, as Pick can only throw ErrNotInPack right now - } - return + // Owner wants to start the session + "start": d.cmdfnOnlyOwner(d.cmdStartSession), + })(roomid, msg) +} + +func (d *DraftBot) handleEvent(roomid string, evt room.Event) { + switch evt.Type { + + // Got added to a new room + case room.EvtNewRoom: + // Add room to the list + d.Rooms[evt.Room.Id] = roomInfo{ + Name: evt.Room.Name, + Owner: evt.Room.Creator, } - d.sendMessage(roomid, room.Message{ - Channel: "draft", - Type: "card-picked", - Data: struct { - Player string - }{ - msg.From, - }, - Message: fmt.Sprintf("%s picked a card from his pack", msg.From), - }) + + // A room got closed + case room.EvtRoomClosed: + // Check if there's a session there + session, ok := d.Sessions[roomid] + if ok { + // Close session + session.exit <- true + // Remove session from list + delete(d.Sessions, roomid) + } + // Remove room from list + delete(d.Rooms, roomid) } } diff --git a/draftbot/draftbot.messages.go b/draftbot/draftbot.messages.go new file mode 100644 index 0000000..d3d8ea8 --- /dev/null +++ b/draftbot/draftbot.messages.go @@ -0,0 +1,133 @@ +package main + +import ( + "fmt" + + room "git.fromouter.space/mcg/cardgage/room/api" + "git.fromouter.space/mcg/draft" +) + +type commandHandler func(roomid string, msg room.Message) +type commandMap map[string]commandHandler + +func (d *DraftBot) commands(commands commandMap) commandHandler { + return func(roomid string, msg room.Message) { + action, ok := commands[msg.Type] + if !ok { + cmdlist := []string{} + for cmd := range commands { + cmdlist = append(cmdlist, cmd) + } + d.sendMessage(roomid, room.Message{ + To: msg.From, + Type: "command-unavailable", + Message: fmt.Sprintf("Available commands (at this state) are: %s", cmdlist), + }) + return + } + action(roomid, msg) + } +} + +func (d *DraftBot) cmdJoinSession(roomid string, msg room.Message) { + // Get session + session, _ := d.Sessions[roomid] + + // Players can only join if session didn't start yet + if session.started { + d.sendMessage(roomid, room.Message{ + To: msg.From, + Type: "session-already-started", + Message: "You can't join a running session", + }) + return + } + + // Check if there are still open slots + if len(session.Players)+1 > len(session.Pod.Players) { + d.sendMessage(roomid, room.Message{ + To: msg.From, + Type: "session-full", + Message: "There aren't any spots left", + }) + return + } + + // Add player to the list + session.Players[msg.From] = nil + + d.sendMessage(roomid, room.Message{ + Channel: "draft", + Type: "player-joined-session", + Data: msg.From, + Message: fmt.Sprintf("%s joined the draft session (%d players, %d missing)", msg.From, len(session.Players), len(session.Pod.Players)-len(session.Players)), + }) +} + +func (d *DraftBot) cmdPickCard(roomid string, msg room.Message) { + // Get session + session, _ := d.Sessions[roomid] + + // Get player + player, _ := session.Players[msg.From] + + // Get picked card + picked := msg.Data.(string) + + // Try to pick on player struct + err := player.Pick(draft.Card{ID: picked}) + if err != nil { + if err == draft.ErrNotInPack { + d.sendMessage(roomid, room.Message{ + To: msg.From, + Type: "invalid-pick", + }) + } else { + // Technically not needed, as Pick can only throw ErrNotInPack right now + } + return + } + d.sendMessage(roomid, room.Message{ + Channel: "draft", + Type: "card-picked", + Data: struct { + Player string + }{ + msg.From, + }, + Message: fmt.Sprintf("%s picked a card from his pack", msg.From), + }) +} + +func (d *DraftBot) cmdCreateSession(roomid string, msg room.Message) { + //TODO +} + +func (d *DraftBot) cmdStartSession(roomid string, msg room.Message) { + //TODO +} + +func (d *DraftBot) cmdfnOnlyOwner(wrapped commandHandler) commandHandler { + return func(roomid string, msg room.Message) { + // Get room the message was sent from + roomData, ok := d.Rooms[roomid] + if !ok { + // Message from a room we're not in? + // Ignore it for now + return + } + + // Make sure the message is coming from the room owner + if msg.From != roomData.Owner { + d.sendMessage(roomid, room.Message{ + To: msg.From, + Type: "no-session", + Message: "Sorry, only the owner can speak to me when no session is active", + }) + return + } + + // Check done, call wrapped function + wrapped(roomid, msg) + } +} diff --git a/draftbot/draftbot_test.go b/draftbot/draftbot_test.go new file mode 100644 index 0000000..6c104d8 --- /dev/null +++ b/draftbot/draftbot_test.go @@ -0,0 +1,42 @@ +package main_test + +import ( + "testing" + + "git.fromouter.space/mcg/cardgage/client/bot" + room "git.fromouter.space/mcg/cardgage/room/api" + draft "git.fromouter.space/mcg/mlp-server-tools/draftbot" +) + +type MockServer struct { + in chan room.ServerMessage + out chan room.BotMessage +} + +func makeMockServer() *MockServer { + in := make(chan room.ServerMessage) + out := make(chan room.BotMessage) + srv := &MockServer{ + in: in, + out: out, + } + return srv +} + +func (m *MockServer) Send(msg room.BotMessage) { + m.out <- msg +} + +func (m *MockServer) Bind(fn bot.MessageHandler) { + for msg := range m.in { + fn(msg) + } +} + +func TestDraftSession(t *testing.T) { + mock := makeMockServer() + draftbot := draft.NewDraftBot(mock, "bot") + go mock.Bind(draftbot.OnMessage) + + //TODO sample session +} diff --git a/draftbot/go.mod b/draftbot/go.mod index 15051fd..bb6e287 100644 --- a/draftbot/go.mod +++ b/draftbot/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( git.fromouter.space/Artificiale/moa v0.0.1-p2 - git.fromouter.space/mcg/cardgage v0.0.2 + git.fromouter.space/mcg/cardgage v0.0.4 git.fromouter.space/mcg/draft v0.0.2 github.com/go-kit/kit v0.8.0 ) diff --git a/draftbot/go.sum b/draftbot/go.sum index bb699b7..c5bbc45 100644 --- a/draftbot/go.sum +++ b/draftbot/go.sum @@ -5,6 +5,8 @@ git.fromouter.space/mcg/cardgage v0.0.1-p2 h1:u65ofEmtDHHQ8nbai97DW9kcrYa4x0I3NH git.fromouter.space/mcg/cardgage v0.0.1-p2/go.mod h1:vCmJ9HRdRGSWg2YQW9oNG7geYACdgWYmzL+zZdrsYhQ= git.fromouter.space/mcg/cardgage v0.0.2 h1:u3Wz+UJx0wEix7vlqMlDX9Kg8nplCy8A0+nIKdNNPp0= git.fromouter.space/mcg/cardgage v0.0.2/go.mod h1:vCmJ9HRdRGSWg2YQW9oNG7geYACdgWYmzL+zZdrsYhQ= +git.fromouter.space/mcg/cardgage v0.0.4 h1:LHMUeNMh0QiMkM3TgsLe9l5sDmanQrej6UiWSVTb67c= +git.fromouter.space/mcg/cardgage v0.0.4/go.mod h1:vCmJ9HRdRGSWg2YQW9oNG7geYACdgWYmzL+zZdrsYhQ= git.fromouter.space/mcg/draft v0.0.2 h1:OT1uztgfCZnZCOg3uOtjQKH2WdwRAxNdYr4KXklVgXY= git.fromouter.space/mcg/draft v0.0.2/go.mod h1:QQmDm9FgAZL3b2/pIDd4Eo608SxMiCQQe5vIybe/CDY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/draftbot/main.go b/draftbot/main.go index 6841db0..76de1d2 100644 --- a/draftbot/main.go +++ b/draftbot/main.go @@ -81,8 +81,8 @@ func runBot(name string, games, tags []string) error { return err } - draftbot := newDraftBot(wsbot, name) - wsbot.Listen(draftbot.onMessage) + draftbot := NewDraftBot(wsbot, name) + wsbot.Listen(draftbot.OnMessage) return errors.New("eof") }