feat: impl release hand & next speaker selection

This commit is contained in:
efim 2023-11-09 06:20:43 +00:00
parent aa048efbd3
commit 480d007e6c
5 changed files with 199 additions and 48 deletions

View File

@ -31,10 +31,10 @@ func main() {
fmt.Printf("Server will start on port %d\n", port) fmt.Printf("Server will start on port %d\n", port)
rooms := rooms.RedisRM { Rdb: rdb, } roomsM := rooms.RedisRM { Rdb: rdb, }
sessions := sessions.RedisSM{ Rdb: rdb, } sessions := sessions.RedisSM{ Rdb: rdb, }
routes.RegisterRoutes(sessions, rooms) routes.RegisterRoutes(sessions, roomsM)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
} }

147
rooms/room.go Normal file
View File

@ -0,0 +1,147 @@
package rooms
import (
"log"
"slices"
)
type Room struct {
Name string // will be unique ID
AdminIds []PersonId
PasswordHash string
CurrentSpeaker PersonId // i guess let's set to zero value when it's noone from the room
Paricipants []Person
// TODO hands, for each type of hand?
// i guess participants order fixed for now?
// and i'll still need 'current' for each hand level
ParticipantHands map[PersonId]HandGesture
Marks map[HandGesture]PersonId
}
func (r *Room)InitMaps() {
if r.ParticipantHands == nil {
r.ParticipantHands = make(map[PersonId]HandGesture)
}
if r.Marks == nil {
r.Marks = make(map[HandGesture]PersonId)
}
}
// and how would i organize?
// i should have map[HandGesture]ParticipantId as mark. for 'from where to go clockwise if returning a level lover'
// now i want methods that for some person raise some hand, so i guess it adds what? to map[ParticipantId]HandGesture
// i suppose methods should be on a room, in a session i'd have ParticipantId to pass in, and would load room
// and i want all parts of room in same struct, because i'd want to subscribe to changes to all of them
// i suppose raising a hand could be just in place adding PersonId->HandGesture,
// but releasing hand should have additional logic
func (r *Room) ReleaseHand(p PersonId) {
// releasing a hand of a current speaker should result in selection of a new speaker
log.Printf("about to release hand of %d in %s", p, r.Name)
// first remove hand of the requested person
currentSpeakerGesture, currentSpeakerHandFound := r.ParticipantHands[p]
if !currentSpeakerHandFound {
return
}
delete(r.ParticipantHands, p)
// if not a current speaker, no complicated logic required to release a hand
if r.CurrentSpeaker != p {
return
}
// if a current speaker - after removing the hand, we need to find next speaker
// from highest hand gesture to lowest, until one is found
gestures := [...]HandGesture{Meta, ClarifyingQ, Expand, ProbingQ, ChangeTopic}
var nextSpeakerId PersonId
var nextSpeakerFound bool
gestureIteration:
for _, gesture := range gestures {
log.Printf("searching for gesture %s", gesture.String())
startIndex := r.gestureSearchStartIndex(gesture, currentSpeakerGesture)
participantsCount := len(r.Paricipants)
for i := 1; i < participantsCount; i++ {
checkIndex := startIndex + i%participantsCount
checkPerson := r.Paricipants[checkIndex]
checkGesture, isFound := r.ParticipantHands[checkPerson.Id]
if isFound && checkGesture == gesture {
nextSpeakerId, nextSpeakerFound = checkPerson.Id, true
break gestureIteration
}
}
}
if !nextSpeakerFound {
log.Printf("there is not next speaker, that's ok")
}
nextSpeakerGesture := r.ParticipantHands[nextSpeakerId]
log.Printf("found next speaker %+v", nextSpeakerId)
if nextSpeakerGesture > currentSpeakerGesture {
// raising the level of the speaker, need to save mark
log.Printf("we do have nextSpeaker of priority %s higher than current %s", nextSpeakerGesture.String(), currentSpeakerGesture.String())
r.Marks[currentSpeakerGesture] = p
}
// also we need to remove marks from top to current speaker level
for _, gesture := range gestures {
if gesture < nextSpeakerGesture {
break
}
delete(r.Marks, gesture)
}
r.CurrentSpeaker = nextSpeakerId
}
// find place to start - separate function
// current speaker level, searched gesture level, room marks
// if search level >= current speaker start from speaker
// if search level < current speaekr look for mark, start from mark of that level,
//
// if no mark - from speaker
func (r *Room) gestureSearchStartIndex(gesture, curSpeakerGesture HandGesture) int {
if r.CurrentSpeaker == PersonId(0) {
// nobody is actully a speaker
return 0
}
var personFromWhichToStartSearch PersonId
// if searched guesture is higher or same as current speaker, start from speaker
if gesture >= curSpeakerGesture {
personFromWhichToStartSearch = r.CurrentSpeaker
}
// if searched gesture is of lower priority from current speaker, check marks to return to, or use speaker level
gestureMark, found := r.Marks[gesture]
if found {
personFromWhichToStartSearch = gestureMark
}
// if no mark found - count from the speaker
personFromWhichToStartSearch = r.CurrentSpeaker
indexFromWhichToStart := slices.IndexFunc(r.Paricipants, func(p Person) bool {
return p.Id == personFromWhichToStartSearch
})
return indexFromWhichToStart
}
// sooooooo. i need hand types. are there enums in go?
type HandGesture uint8
const (
ChangeTopic HandGesture = iota
ProbingQ
Expand
ClarifyingQ
Meta
)
// String() method to print the name of the days
func (g HandGesture) String() string {
return [...]string{"Change Topic", "Probing Quesion", "Expand", "Clarifying Quesion", "Meta"}[g]
}

View File

@ -5,25 +5,28 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"log" "log"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
type Person struct { type PersonId int
PersonId int
Name string // TODO move to rooms i guess
PasswordHash string func RandomPersonId() PersonId {
randInt := rand.Int()
if randInt == 0 {
randInt = 1
}
return PersonId(randInt)
} }
type Room struct {
Name string // will be unique ID type Person struct {
AdminIds []int Id PersonId
Name string
PasswordHash string PasswordHash string
Paricipants []Person
// TODO hands, for each type of hand?
// i guess participants order fixed for now?
// and i'll still need 'current' for each hand level
} }
// well, it seems that i'd better do marshalling into bytes then // well, it seems that i'd better do marshalling into bytes then

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"log" "log"
"math/rand"
"net/http" "net/http"
"strconv" "strconv"
@ -31,6 +30,7 @@ func registerLoginRoutes(
const authCookieName = "auth" const authCookieName = "auth"
const loginPath = "/login" const loginPath = "/login"
type contextKey string type contextKey string
func getContextSession(ctx context.Context) (sessions.SessionData, bool) { func getContextSession(ctx context.Context) (sessions.SessionData, bool) {
@ -90,7 +90,7 @@ func renderLoginPage(w http.ResponseWriter) {
} }
} }
func createRoomHandler( templateFs *embed.FS, func createRoomHandler(templateFs *embed.FS,
sessionSM sessions.SessionManagement, sessionSM sessions.SessionManagement,
roomsM rooms.RoomManager, roomsM rooms.RoomManager,
) http.HandlerFunc { ) http.HandlerFunc {
@ -108,14 +108,14 @@ func createRoomHandler( templateFs *embed.FS,
return return
} }
person := rooms.Person{ person := rooms.Person{
PersonId: rand.Int(), Id: rooms.RandomPersonId(),
Name: r.PostFormValue("personalName"), Name: r.PostFormValue("personalName"),
PasswordHash: r.PostFormValue("personalPassword"), // TODO hash the password, not to store PasswordHash: r.PostFormValue("personalPassword"), // TODO hash the password, not to store
} }
newRoom := rooms.Room{ newRoom := rooms.Room{
Name: roomName, Name: roomName,
PasswordHash: r.PostFormValue("roomPassword"), // TODO hash the password, not to store PasswordHash: r.PostFormValue("roomPassword"), // TODO hash the password, not to store
AdminIds: []int{person.PersonId}, AdminIds: []rooms.PersonId{person.Id},
Paricipants: []rooms.Person{person}, Paricipants: []rooms.Person{person},
} }
err = roomsM.Save(newRoom) err = roomsM.Save(newRoom)
@ -123,7 +123,7 @@ func createRoomHandler( templateFs *embed.FS,
log.Printf("what am i to do? error saving room %s", err) log.Printf("what am i to do? error saving room %s", err)
// todo return error notice somehow // todo return error notice somehow
} }
newSessionId, err := sessionSM.Save(newRoom.Name, person.PersonId) newSessionId, err := sessionSM.Save(newRoom.Name, person.Id)
if err != nil { if err != nil {
log.Printf("what am i to do? error saving session %s", err) log.Printf("what am i to do? error saving session %s", err)
// todo return error notice somehow // todo return error notice somehow
@ -148,7 +148,7 @@ func createRoomHandler( templateFs *embed.FS,
// checking whether the room name already exists // checking whether the room name already exists
// toggle button between Join or Create // toggle button between Join or Create
func checkRoomName( templateFs *embed.FS, func checkRoomName(templateFs *embed.FS,
roomsM rooms.RoomManager, roomsM rooms.RoomManager,
) http.HandlerFunc { ) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -167,7 +167,7 @@ func checkRoomName( templateFs *embed.FS,
} }
} }
func joinRoomHandler( templateFs *embed.FS, func joinRoomHandler(templateFs *embed.FS,
sessionSM sessions.SessionManagement, sessionSM sessions.SessionManagement,
roomsM rooms.RoomManager, roomsM rooms.RoomManager,
) http.HandlerFunc { ) http.HandlerFunc {
@ -223,7 +223,7 @@ func joinRoomHandler( templateFs *embed.FS,
person = rooms.Person{ person = rooms.Person{
Name: personName, Name: personName,
PasswordHash: personPass, PasswordHash: personPass,
PersonId: rand.Int(), Id: rooms.RandomPersonId(),
} }
err := roomsM.Update(context.TODO(), room.Name, func(fromRoom rooms.Room) (toRoom rooms.Room) { err := roomsM.Update(context.TODO(), room.Name, func(fromRoom rooms.Room) (toRoom rooms.Room) {
toRoom = fromRoom toRoom = fromRoom
@ -242,7 +242,7 @@ func joinRoomHandler( templateFs *embed.FS,
// now we have room and person, can create a session // now we have room and person, can create a session
// and we've checked password // and we've checked password
newSessionId, err := sessionSM.Save(room.Name, person.PersonId) newSessionId, err := sessionSM.Save(room.Name, person.Id)
if err != nil { if err != nil {
log.Printf("/login/submit > error saving session %s", err) log.Printf("/login/submit > error saving session %s", err)
} }
@ -261,4 +261,3 @@ func joinRoomHandler( templateFs *embed.FS,
w.Header().Add("HX-Redirect", "/") w.Header().Add("HX-Redirect", "/")
} }
} }

View File

@ -6,18 +6,20 @@ import (
"log" "log"
"math/rand" "math/rand"
"sunshine.industries/some-automoderation/rooms"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
type SessionData struct { type SessionData struct {
SessionId int `redis:"session_id"` SessionId int `redis:"session_id"`
RoomId string `redis:"room_id"` RoomId string `redis:"room_id"`
PersonId int `redis:"person_id"` PersonId rooms.PersonId `redis:"person_id"`
} }
type SessionManagement interface { type SessionManagement interface {
Get(sessionId int) SessionData Get(sessionId int) SessionData
Save(roomName string, personId int) (int, error) Save(roomName string, personId rooms.PersonId) (int, error)
} }
var ctx = context.Background() var ctx = context.Background()
@ -43,7 +45,7 @@ func (redisSM RedisSM) Get(sessionId int) SessionData {
log.Printf("> successfully found %d %+v", sessionId, foundSession) log.Printf("> successfully found %d %+v", sessionId, foundSession)
return foundSession return foundSession
} }
func (redisSM RedisSM) Save(roomName string, personId int) (int, error) { func (redisSM RedisSM) Save(roomName string, personId rooms.PersonId) (int, error) {
randId := rand.Int() randId := rand.Int()
newSession := SessionData{ newSession := SessionData{
SessionId: randId, SessionId: randId,
@ -64,7 +66,7 @@ func (d DummySM) Get(sessionId int) SessionData {
log.Printf("get dummy session by %d", sessionId) log.Printf("get dummy session by %d", sessionId)
return SessionData{} return SessionData{}
} }
func (d DummySM) Save(roomName string, personId int) (int, error) { func (d DummySM) Save(roomName string, personId rooms.PersonId) (int, error) {
log.Printf("save dummy session with %s %d", roomName, personId) log.Printf("save dummy session with %s %d", roomName, personId)
return 1, nil return 1, nil
} }