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