Refactor messages handling
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Hamcha 2019-06-28 11:03:42 +02:00
parent be83334e3a
commit 9b5ad472c7
6 changed files with 231 additions and 87 deletions

View File

@ -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)
}
}

View File

@ -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)
}
}

42
draftbot/draftbot_test.go Normal file
View File

@ -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
}

View File

@ -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
)

View File

@ -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=

View File

@ -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")
}