Fix: notify bot
This commit is contained in:
		| @@ -33,8 +33,7 @@ func saveRoom(room *models.Room) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	memcache.Set(models.CacheRoomPrefix+room.ID, data) | ||||
| 	log.Debug("saved room", "room", room, "key", key) | ||||
| 	memcache.Set(key, data) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -84,6 +83,21 @@ func saveFullInfo(fi *models.FullInfo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func notifyBotIfNeeded(fi *models.FullInfo) { | ||||
| 	if botName := fi.Room.WhichBotToMove(); botName != "" { | ||||
| 		// // get bot from memcache | ||||
| 		// bot, err := loadBot(botName, fi.Room.ID) | ||||
| 		// if err != nil { | ||||
| 		// 	log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) | ||||
| 		// 	// abortWithError(w, err.Error()) | ||||
| 		// 	// return | ||||
| 		// } | ||||
| 		// send signal to bot | ||||
| 		llmapi.SignalChanMap[botName] <- true | ||||
| 	} | ||||
| 	log.Debug("no bot", "room_id", fi.Room.ID) | ||||
| } | ||||
|  | ||||
| // cache | ||||
|  | ||||
| func saveState(username string, state *models.UserState) error { | ||||
| @@ -110,7 +124,16 @@ func loadState(username string) (*models.UserState, error) { | ||||
| } | ||||
|  | ||||
| func loadBot(botName, roomID string) (*llmapi.Bot, error) { | ||||
| 	return nil, nil | ||||
| 	key := "botkey_" + roomID + botName | ||||
| 	data, err := memcache.Get(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp := &llmapi.Bot{} | ||||
| 	if err := json.Unmarshal(data, &resp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func getAllNames() []string { | ||||
|   | ||||
| @@ -92,6 +92,7 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | ||||
| 	case "white", string(oppositeColor): | ||||
| 		// end turn | ||||
| 		fi.Room.TeamTurn = oppositeColor | ||||
| 		fi.Room.MimeDone = false | ||||
| 	} | ||||
| 	// check if no cards left => game over | ||||
| 	if fi.Room.BlueCounter == 0 { | ||||
| @@ -110,6 +111,8 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	// get mime bot for opp team and notify it | ||||
| 	notifyBotIfNeeded(fi) | ||||
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | ||||
| 	if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil { | ||||
| 		log.Error("failed to execute cardword template", "error", err) | ||||
| @@ -136,16 +139,19 @@ func HandleAddBot(w http.ResponseWriter, r *http.Request) { | ||||
| 	// get team; // get role; make up a name | ||||
| 	team := r.URL.Query().Get("team") | ||||
| 	role := r.URL.Query().Get("role") | ||||
| 	log.Debug("got add-bot request", "team", team, "role", role) | ||||
| 	fi, err := getFullInfoByCtx(r.Context()) | ||||
| 	if err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	// TODO: what if bot exists already? | ||||
| 	// control number and names of bots | ||||
| 	bot, err := llmapi.NewBot(role, team, "bot1", fi.Room.ID, cfg) | ||||
| 	if err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	bot.StartBot() | ||||
| 	go bot.StartBot() | ||||
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | ||||
| } | ||||
|   | ||||
| @@ -51,53 +51,6 @@ func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DEPRACATED: duplication of HandleJoinRoom | ||||
| // func HandleRoomEnter(w http.ResponseWriter, r *http.Request) { | ||||
| // 	// parse payload | ||||
| // 	roomID := r.URL.Query().Get("id") | ||||
| // 	if roomID == "" { | ||||
| // 		msg := "room id not provided" | ||||
| // 		log.Error(msg) | ||||
| // 		abortWithError(w, msg) | ||||
| // 		return | ||||
| // 	} | ||||
| // 	tmpl, err := template.ParseGlob("components/*.html") | ||||
| // 	if err != nil { | ||||
| // 		abortWithError(w, err.Error()) | ||||
| // 		return | ||||
| // 	} | ||||
| // 	// create a room | ||||
| // 	room, err := getRoomByID(roomID) | ||||
| // 	if err != nil { | ||||
| // 		msg := "failed to find the room" | ||||
| // 		log.Error(msg, "error", err, "room_id", roomID) | ||||
| // 		abortWithError(w, msg) | ||||
| // 		return | ||||
| // 	} | ||||
| // 	state, err := getStateByCtx(r.Context()) | ||||
| // 	// INFO: if non-loggined user join: prompt to login | ||||
| // 	if err != nil { | ||||
| // 		log.Error("failed to get state", "error", err) | ||||
| // 		// abortWithError(w, err.Error()) | ||||
| // 		tmpl.ExecuteTemplate(w, "login", nil) | ||||
| // 		return | ||||
| // 	} | ||||
| // 	state.RoomID = room.ID | ||||
| // 	// update state | ||||
| // 	if err := saveStateByCtx(r.Context(), state); err != nil { | ||||
| // 		log.Error("failed to update state", "error", err) | ||||
| // 		abortWithError(w, err.Error()) | ||||
| // 		return | ||||
| // 	} | ||||
| // 	// send msg of created room | ||||
| // 	// h.Broker.Notifier <- broker.NotificationEvent{ | ||||
| // 	// 	EventName: models.MsgRoomListUpdate, | ||||
| // 	// 	Payload:   fmt.Sprintf("%s created a room named %s", r.CreatorName, r.RoomName), | ||||
| // 	// } | ||||
| // 	// return html | ||||
| // 	tmpl.ExecuteTemplate(w, "base", room) | ||||
| // } | ||||
|  | ||||
| func HandleJoinTeam(w http.ResponseWriter, r *http.Request) { | ||||
| 	if err := r.ParseForm(); err != nil { | ||||
| 		log.Error("failed to parse form", "error", err) | ||||
| @@ -158,6 +111,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 	fi.Room.ChangeTurn() | ||||
| 	fi.Room.MimeDone = false | ||||
| 	if err := saveFullInfo(fi); err != nil { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| @@ -168,17 +122,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	if botName := fi.Room.WhichBotToMove(); botName != "" { | ||||
| 		// get bot from memcache | ||||
| 		bot, err := loadBot(botName, fi.Room.ID) | ||||
| 		if err != nil { | ||||
| 			log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) | ||||
| 			abortWithError(w, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 		// send signal to bot | ||||
| 		bot.SignalsCh <- true | ||||
| 	} | ||||
| 	notifyBotIfNeeded(fi) | ||||
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | ||||
| 	if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { | ||||
| 		log.Error("failed to execute base template", "error", err) | ||||
| @@ -222,17 +166,7 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { | ||||
| 		abortWithError(w, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	if botName := fi.Room.WhichBotToMove(); botName != "" { | ||||
| 		// get bot from memcache | ||||
| 		bot, err := loadBot(botName, fi.Room.ID) | ||||
| 		if err != nil { | ||||
| 			log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) | ||||
| 			abortWithError(w, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 		// send signal to bot | ||||
| 		bot.SignalsCh <- true | ||||
| 	} | ||||
| 	notifyBotIfNeeded(fi) | ||||
| 	// to update only the room that should be updated | ||||
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | ||||
| 	// notify(models.NotifyBacklogPrefix+fi.Room.ID, "game started") | ||||
|   | ||||
| @@ -5,24 +5,22 @@ import ( | ||||
| 	"crypto/hmac" | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"golias/models" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // LogRequests logs all HTTP requests with method, path and duration | ||||
| func LogRequests(next http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		start := time.Now() | ||||
| 		// start := time.Now() | ||||
| 		// Wrap response writer to capture status code | ||||
| 		next.ServeHTTP(w, r) | ||||
| 		duration := time.Since(start) | ||||
| 		log.Debug("request completed", | ||||
| 			"method", r.Method, | ||||
| 			"path", r.URL.RequestURI(), | ||||
| 			"duration", duration.String(), | ||||
| 		) | ||||
| 		// duration := time.Since(start) | ||||
| 		// log.Debug("request completed", | ||||
| 		// 	"method", r.Method, | ||||
| 		// 	"path", r.URL.RequestURI(), | ||||
| 		// 	"duration", duration.String(), | ||||
| 		// ) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @@ -65,11 +63,11 @@ func GetSession(next http.Handler) http.Handler { | ||||
| 			return | ||||
| 		} | ||||
| 		userSession, err := cacheGetSession(sessionToken) | ||||
| 		log.Debug("userSession from cache", "us", userSession) | ||||
| 		// log.Debug("userSession from cache", "us", userSession) | ||||
| 		if err != nil { | ||||
| 			msg := "auth failed; session does not exists" | ||||
| 			err = errors.New(msg) | ||||
| 			log.Debug(msg, "error", err) | ||||
| 			// msg := "auth failed; session does not exists" | ||||
| 			// err = errors.New(msg) | ||||
| 			// log.Debug(msg, "error", err) | ||||
| 			next.ServeHTTP(w, r) | ||||
| 			return | ||||
| 		} | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import ( | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| @@ -18,6 +19,14 @@ import ( | ||||
| // MIME: llm needs to know all the cards, colors and previous actions | ||||
| // GUESSER: llm needs to know all the cards and previous actions | ||||
|  | ||||
| // channels are not serializable | ||||
|  | ||||
| var ( | ||||
| 	// botname -> channel | ||||
| 	SignalChanMap = make(map[string]chan bool) | ||||
| 	DoneChanMap   = make(map[string]chan bool) | ||||
| ) | ||||
|  | ||||
| type Bot struct { | ||||
| 	Role    string // gueeser | mime | ||||
| 	Team    string | ||||
| @@ -26,15 +35,17 @@ type Bot struct { | ||||
| 	BotName string | ||||
| 	log     *slog.Logger | ||||
| 	// channels for communicaton | ||||
| 	SignalsCh chan bool | ||||
| 	DoneCh    chan bool | ||||
| 	// channels are not serializable | ||||
| 	// SignalsCh chan bool | ||||
| 	// DoneCh    chan bool | ||||
| } | ||||
|  | ||||
| // StartBot | ||||
| func (b *Bot) StartBot() { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-b.SignalsCh: | ||||
| 		case <-SignalChanMap[b.BotName]: | ||||
| 			b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) | ||||
| 			// get room cards and actions | ||||
| 			room, err := getRoomByID(b.RoomID) | ||||
| 			if err != nil { | ||||
| @@ -53,7 +64,7 @@ func (b *Bot) StartBot() { | ||||
| 		// if mime -> give clue | ||||
| 		// if guesser -> open card (does opening one card prompting new loop?) | ||||
| 		// send notification to sse broker | ||||
| 		case <-b.DoneCh: | ||||
| 		case <-DoneChanMap[b.BotName]: | ||||
| 			b.log.Debug("got done signal", "bot-name", b.BotName) | ||||
| 			return | ||||
| 		} | ||||
| @@ -69,6 +80,10 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { | ||||
| 		BotName: name, | ||||
| 		Team:    team, | ||||
| 		cfg:     cfg, | ||||
| 		log: slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ | ||||
| 			Level:     slog.LevelDebug, | ||||
| 			AddSource: true, | ||||
| 		})), | ||||
| 	} | ||||
| 	// add to room | ||||
| 	room, err := getRoomByID(bot.RoomID) | ||||
| @@ -108,10 +123,25 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { | ||||
| 	if err := saveRoom(room); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := saveBot(bot); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	SignalChanMap[bot.BotName] = make(chan bool) | ||||
| 	DoneChanMap[bot.BotName] = make(chan bool) | ||||
| 	go bot.StartBot() // run bot routine | ||||
| 	return bot, nil | ||||
| } | ||||
|  | ||||
| func saveBot(bot *Bot) error { | ||||
| 	key := "botkey_" + bot.RoomID + bot.BotName | ||||
| 	data, err := json.Marshal(bot) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.MemCache.Set(key, data) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getRoomByID(roomID string) (*models.Room, error) { | ||||
| 	roomBytes, err := cache.MemCache.Get(models.CacheRoomPrefix + roomID) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"golias/utils" | ||||
| 	"time" | ||||
|  | ||||
| @@ -84,6 +85,7 @@ type Room struct { | ||||
|  | ||||
| // WhichBotToMove returns bot name that have to move or empty string | ||||
| func (r *Room) WhichBotToMove() string { | ||||
| 	fmt.Println("looking for bot to move", "team-turn", r.TeamTurn, "mime-done", r.MimeDone, "bot-map", r.BotMap, "is_running", r.IsRunning) | ||||
| 	if !r.IsRunning { | ||||
| 		return "" | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										1
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								todos.md
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ | ||||
| - login with invite link; | ||||
| - add html icons of whos turn it is (like an image of big ? when mime is thinking); | ||||
| - there two places for bot to check if its its move: start-game; end-turn; | ||||
| - remove bot button (if game is not running); | ||||
|  | ||||
| #### sse points | ||||
| - clue sse update; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder