tome_matrix/dealer.go

293 lines
7.9 KiB
Go

package main
import (
"fmt"
"log"
"strings"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type Dealer struct {
RoomsList map[id.RoomID]*Match
PlayerRooms map[id.UserID]id.RoomID
Client *mautrix.Client
QueueRoom id.RoomID
HomeserverDomain string
}
func (d *Dealer) ConnectToMatrix(homeserver, uname, passwd string) {
log.Println("Logging into", homeserver, "as", uname)
client, err := mautrix.NewClient(homeserver, "", "")
if err != nil {
panic(err)
}
client.Store = NewLazyMemStore("dealer_state")
_, err = client.Login(&mautrix.ReqLogin{
Type: "m.login.password",
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: uname},
Password: passwd,
StoreCredentials: true,
})
if err != nil {
panic(err)
}
log.Println("Login successful")
d.Client = client
}
func (d *Dealer) RegisterHandlers() {
syncer := d.Client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
if evt.Sender == d.Client.UserID {
return
}
if evt.RoomID == d.QueueRoom {
d.QueueEvent(evt)
return
}
proom, ok := d.PlayerRooms[evt.Sender]
if ok {
log.Printf("membership event for player room %v", proom)
if evt.Content.AsMember().Membership.IsLeaveOrBan() {
delete(d.PlayerRooms, evt.Sender)
return
}
}
if evt.Content.AsMember().IsDirect {
//intention to create player room
if evt.Content.AsMember().Membership.IsInviteOrJoin() {
d.PlayerRooms[evt.Sender] = evt.RoomID
_, err := d.Client.JoinRoomByID(evt.RoomID)
if err != nil {
panic(err)
}
} else if evt.Content.AsMember().Membership.IsLeaveOrBan() {
delete(d.PlayerRooms, evt.Sender)
}
return
}
match, ok := d.RoomsList[evt.RoomID]
if !ok {
log.Printf("received membership event for non active game: %v", evt)
return
}
if evt.Sender != match.P1 && evt.Sender != match.P2 && evt.Sender != d.Client.UserID {
log.Println("membership event for non player")
return
}
members, err := d.Client.JoinedMembers(evt.RoomID)
if err != nil {
panic(err)
}
match.JoinOrLeave(members.Joined, evt.Content.AsMember())
})
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
playerRoom, pok := d.PlayerRooms[evt.Sender]
if pok {
log.Printf("received direct message in room %v", playerRoom)
var roominfo PlayerRoomInfo
_ = d.Client.StateEvent(playerRoom, event.Type{"snen.player", event.StateEventType}, "player_info", &roominfo)
if roominfo.CurrentGame != "" {
//parse as gamecommand
match, ok := d.RoomsList[roominfo.CurrentGame]
if !ok {
log.Println("Dealer thinks a game is happening, but no game found")
return
}
if match.Game == nil {
d.Client.SendText(playerRoom, "can't accept game commands yet")
return
}
resp := match.ParseAction(evt.Sender, evt.Content.AsMessage().Body)
if resp.Body != "" {
_, err := d.Client.SendText(playerRoom, resp.Body)
if err != nil {
panic(err)
}
d.Client.SendText(roominfo.CurrentGame, fmt.Sprintf("%v played %v", evt.Sender, evt.Content.AsMessage().Body))
if !resp.Private {
d.Client.SendText(roominfo.CurrentGame, fmt.Sprintf("Result: %v", resp.Body))
}
}
} else {
//parse as out of game command
log.Println("out of game command received")
//TODO make this better
msg := evt.Content.AsMessage().Body
msg_s := strings.Split(msg, " ")
if msg_s[0] == "direct" {
d.NewMatch(evt.Sender, id.NewUserID(msg_s[1], d.HomeserverDomain))
}
}
return
}
match, ok := d.RoomsList[evt.RoomID]
if !ok {
log.Println("received message event for non active game")
return
}
if !match.IsPlayer(evt.Sender) {
log.Println("ignoring message event from non player")
return
}
//parse as in game command i.e. chat
})
}
func (d *Dealer) SetupRooms(domain string) {
room := "#tomequeue:" + domain
// try creating the room first
res, createErr := d.Client.CreateRoom(&mautrix.ReqCreateRoom{
Preset: "public_chat",
Visibility: "public",
RoomAliasName: "tomequeue",
})
if createErr != nil {
//okay let's try joining the room
res, err := d.Client.JoinRoom(room, "", nil)
if err != nil {
log.Println("unable to create or join queue room")
log.Printf("Create err: %v", createErr)
panic(err)
}
log.Printf("succesfully joined %v", res.RoomID)
d.QueueRoom = res.RoomID
} else {
resJoin, err := d.Client.JoinRoomByID(res.RoomID)
if err != nil {
log.Println("error joined recently created queue room")
panic(err)
}
log.Printf("succesfully created and joined %v", resJoin.RoomID)
d.QueueRoom = resJoin.RoomID
}
//load player rooms
log.Println("Loading known player rooms")
joinedResp, err := d.Client.JoinedRooms()
if err != nil {
panic(err)
}
for _, r := range joinedResp.JoinedRooms {
tags, err := d.Client.GetTags(r)
if err != nil {
log.Printf("can't get tags for room %v:%v", r, err)
continue
}
_, ok := tags.Tags["tome_player_room"]
if !ok {
continue
}
memberListResp, err := d.Client.JoinedMembers(r)
if err != nil {
continue
}
keys := make([]id.UserID, 0, len(memberListResp.Joined))
for k := range memberListResp.Joined {
keys = append(keys, k)
}
if len(keys) != 2 {
log.Printf("tagged player room %v has more or less then 2 members:%v", r, len(keys))
if len(keys) == 1 && keys[0] == d.Client.UserID {
//clean up old player room
log.Printf("leaving player room %v that only has us in it", r)
d.Client.LeaveRoom(r)
}
continue
}
for _, k := range keys {
if k != d.Client.UserID {
d.PlayerRooms[k] = r
d.Client.SendStateEvent(r, event.Type{"snen.player", event.StateEventType}, "player_info", PlayerRoomInfo{
CurrentGame: "",
CurrentDeck: []int{},
})
}
}
}
log.Println("initial room setup complete")
}
func (d *Dealer) NewMatch(p1, p2 id.UserID) *Match {
//creates a new matrix room for the match
match := NewMatch()
roomname := fmt.Sprintf("tome_match_%v", match.ID)
createRes, err := d.Client.CreateRoom(&mautrix.ReqCreateRoom{
Preset: "public_chat",
RoomAliasName: roomname,
Invite: []id.UserID{p1, p2},
})
if err != nil {
log.Println("error creating matrix room for match")
panic(err)
}
match.MatrixRoom = createRes.RoomID
match.P1 = p1
_, found := d.PlayerRooms[p1]
if !found {
d.NewPlayerRoom(p1)
}
match.P2 = p2
_, found = d.PlayerRooms[p2]
if !found {
d.NewPlayerRoom(p2)
}
match.P1Room = d.PlayerRooms[p1]
match.P2Room = d.PlayerRooms[p2]
match.Dealer = d.Client.UserID
d.RoomsList[createRes.RoomID] = match
d.Client.SendStateEvent(match.P1Room, event.Type{"snen.player", event.StateEventType}, "player_info", PlayerRoomInfo{
CurrentGame: match.MatrixRoom,
CurrentDeck: []int{},
})
d.Client.SendStateEvent(match.P2Room, event.Type{"snen.player", event.StateEventType}, "player_info", PlayerRoomInfo{
CurrentGame: match.MatrixRoom,
CurrentDeck: []int{},
})
return match
}
func (d *Dealer) QueueEvent(evt *event.Event) {
mem := evt.Content.AsMember()
if mem.Membership == event.MembershipJoin {
log.Printf("Player %v has queued", evt.Sender)
_, ok := d.PlayerRooms[evt.Sender]
if !ok {
//open up DM with player
err := d.NewPlayerRoom(evt.Sender)
if err != nil {
panic(err)
}
}
} else if mem.Membership.IsLeaveOrBan() {
log.Printf("Player %v has dequeued", evt.Sender)
}
return
}
func (d *Dealer) NewPlayerRoom(user id.UserID) error {
createRes, err := d.Client.CreateRoom(&mautrix.ReqCreateRoom{
Preset: "private_chat",
Invite: []id.UserID{user},
IsDirect: true,
})
if err != nil {
log.Println("error creating matrix room for player")
return err
}
d.PlayerRooms[user] = createRes.RoomID
err = d.Client.AddTag(createRes.RoomID, "tome_player_room", 0.0)
if err != nil {
return err
}
return nil
}