333 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"gralias/llmapi"
 | |
| 	"gralias/models"
 | |
| 	"html/template"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| func HandleShowCreateForm(w http.ResponseWriter, r *http.Request) {
 | |
| 	show := true
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tmpl.ExecuteTemplate(w, "createform", show); err != nil {
 | |
| 		log.Error("failed to execute createform template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleHideCreateForm(w http.ResponseWriter, r *http.Request) {
 | |
| 	show := false
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tmpl.ExecuteTemplate(w, "createform", show); err != nil {
 | |
| 		log.Error("failed to execute createform template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleShowColor(w http.ResponseWriter, r *http.Request) {
 | |
| 	word := r.URL.Query().Get("word")
 | |
| 	ctx := r.Context()
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi, err := getFullInfoByCtx(ctx)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := validateMove(fi, models.UserRoleGuesser); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// color, exists := fi.Room.WCMap[word]
 | |
| 	color, exists := fi.Room.FindColor(word)
 | |
| 	if !exists {
 | |
| 		abortWithError(w, "word is not found")
 | |
| 		return
 | |
| 	}
 | |
| 	cardword := models.WordCard{
 | |
| 		Word:     word,
 | |
| 		Color:    color,
 | |
| 		Revealed: true,
 | |
| 	}
 | |
| 	revCardID := fi.Room.RevealSpecificWord(word)
 | |
| 	if revCardID == 0 {
 | |
| 		// error
 | |
| 		abortWithError(w, "word has 0 id")
 | |
| 		return
 | |
| 	}
 | |
| 	if err := repo.WordCardReveal(r.Context(), word, fi.Room.ID); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	updateStatsOnCardReveal(r.Context(), fi.State, color)
 | |
| 	fi.Room.UpdateCounter()
 | |
| 	action := models.Action{
 | |
| 		Actor:      fi.State.Username,
 | |
| 		ActorColor: string(fi.State.Team),
 | |
| 		WordColor:  string(color),
 | |
| 		Action:     models.ActionTypeGuess,
 | |
| 		Word:       word,
 | |
| 		RoomID:     fi.Room.ID,
 | |
| 	}
 | |
| 	if err := repo.ActionCreate(r.Context(), &action); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 	// if opened card is of color of opp team, change turn
 | |
| 	oppositeColor := fi.Room.GetOppositeTeamColor()
 | |
| 	var clearMarks bool
 | |
| 	fi.Room.OpenedThisTurn++
 | |
| 	log.Debug("got show-color request", "word", word, "color", color,
 | |
| 		"limit", fi.Room.ThisTurnLimit, "opened", fi.Room.OpenedThisTurn,
 | |
| 		"team-turn", fi.Room.TeamTurn, "opposite-color", oppositeColor)
 | |
| 	if fi.Room.OpenedThisTurn >= fi.Room.ThisTurnLimit {
 | |
| 		log.Debug("reached limit", "room", fi.Room)
 | |
| 		// end turn
 | |
| 		fi.Room.TeamTurn = oppositeColor
 | |
| 		fi.Room.MimeDone = false
 | |
| 		fi.Room.OpenedThisTurn = 0
 | |
| 		fi.Room.ThisTurnLimit = 0
 | |
| 		clearMarks = true
 | |
| 		StopTurnTimer(fi.Room.ID)
 | |
| 	}
 | |
| 	switch string(color) {
 | |
| 	case string(models.WordColorBlack):
 | |
| 		log.Debug("opened black word", "room", fi.Room)
 | |
| 		// game over
 | |
| 		fi.Room.IsRunning = false
 | |
| 		fi.Room.IsOver = true
 | |
| 		fi.Room.TeamWon = oppositeColor
 | |
| 		action := models.Action{
 | |
| 			RoomID:     fi.Room.ID,
 | |
| 			Actor:      fi.State.Username,
 | |
| 			ActorColor: string(fi.State.Team),
 | |
| 			WordColor:  models.WordColorBlack,
 | |
| 			Action:     models.ActionTypeGameOver,
 | |
| 		}
 | |
| 		fi.Room.OpenedThisTurn = 0
 | |
| 		fi.Room.ThisTurnLimit = 0
 | |
| 		fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 		clearMarks = true
 | |
| 		StopTurnTimer(fi.Room.ID)
 | |
| 		updateStatsOnGameOver(r.Context(), fi.Room)
 | |
| 	case string(models.WordColorWhite), string(oppositeColor):
 | |
| 		log.Debug("opened white or opposite color word", "word", word, "opposite-color", oppositeColor)
 | |
| 		// end turn
 | |
| 		fi.Room.TeamTurn = oppositeColor
 | |
| 		fi.Room.MimeDone = false
 | |
| 		fi.Room.OpenedThisTurn = 0
 | |
| 		fi.Room.ThisTurnLimit = 0
 | |
| 		clearMarks = true
 | |
| 		StopTurnTimer(fi.Room.ID)
 | |
| 		// check if no cards left => game over
 | |
| 		if fi.Room.BlueCounter == 0 {
 | |
| 			// blue won
 | |
| 			fi.Room.IsRunning = false
 | |
| 			fi.Room.IsOver = true
 | |
| 			fi.Room.TeamWon = "blue"
 | |
| 			action := models.Action{
 | |
| 				RoomID:     fi.Room.ID,
 | |
| 				Actor:      fi.State.Username,
 | |
| 				ActorColor: string(fi.State.Team),
 | |
| 				WordColor:  models.WordColorBlue,
 | |
| 				Action:     models.ActionTypeGameOver,
 | |
| 			}
 | |
| 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 			updateStatsOnGameOver(r.Context(), fi.Room)
 | |
| 		}
 | |
| 		if fi.Room.RedCounter == 0 {
 | |
| 			// red won
 | |
| 			fi.Room.IsRunning = false
 | |
| 			fi.Room.IsOver = true
 | |
| 			fi.Room.TeamWon = "red"
 | |
| 			action := models.Action{
 | |
| 				RoomID:     fi.Room.ID,
 | |
| 				Actor:      fi.State.Username,
 | |
| 				ActorColor: string(fi.State.Team),
 | |
| 				WordColor:  models.WordColorRed,
 | |
| 				Action:     models.ActionTypeGameOver,
 | |
| 			}
 | |
| 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 			updateStatsOnGameOver(r.Context(), fi.Room)
 | |
| 		}
 | |
| 	default: // same color as the team
 | |
| 		// check if game over
 | |
| 		if fi.Room.RedCounter == 0 || fi.Room.BlueCounter == 0 {
 | |
| 			fi.Room.IsRunning = false
 | |
| 			fi.Room.IsOver = true
 | |
| 			fi.Room.TeamWon = fi.State.Team
 | |
| 			action := models.Action{
 | |
| 				RoomID:     fi.Room.ID,
 | |
| 				Actor:      fi.State.Username,
 | |
| 				ActorColor: string(fi.State.Team),
 | |
| 				WordColor:  models.WordColorRed,
 | |
| 				Action:     models.ActionTypeGameOver,
 | |
| 			}
 | |
| 			fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 			updateStatsOnGameOver(r.Context(), fi.Room)
 | |
| 		}
 | |
| 	}
 | |
| 	if clearMarks {
 | |
| 		fi.Room.ClearMarks()
 | |
| 		if err := repo.CardMarksRemoveByRoomID(r.Context(), fi.Room.ID); err != nil {
 | |
| 			log.Error("failed to remove marks", "error", err, "room_id", fi.Room.ID)
 | |
| 		}
 | |
| 	}
 | |
| 	if err := saveFullInfo(r.Context(), fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// get mime bot for opp team and notify it
 | |
| 	notifyBotIfNeeded(fi.Room)
 | |
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
 | |
| 	if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil {
 | |
| 		log.Error("failed to execute cardword template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleMarkCard(w http.ResponseWriter, r *http.Request) {
 | |
| 	word := r.URL.Query().Get("word")
 | |
| 	ctx := r.Context()
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi, err := getFullInfoByCtx(ctx)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := validateMove(fi, models.UserRoleGuesser); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	color, exists := fi.Room.FindColor(word)
 | |
| 	log.Debug("got mark-card request", "word", word, "color", color)
 | |
| 	if !exists {
 | |
| 		abortWithError(w, "word is not found")
 | |
| 		return
 | |
| 	}
 | |
| 	cardword := models.WordCard{}
 | |
| 	// check if card already was revealed
 | |
| 	for i, card := range fi.Room.Cards {
 | |
| 		if !strings.EqualFold(card.Word, word) {
 | |
| 			continue
 | |
| 		}
 | |
| 		if card.Revealed {
 | |
| 			abortWithError(w, "cannot mark already revealed")
 | |
| 			return
 | |
| 		}
 | |
| 		// Check if the current user already has an active mark on this card
 | |
| 		found := false
 | |
| 		var newMarks []models.CardMark
 | |
| 		for _, mark := range card.Marks {
 | |
| 			if mark.Username == fi.State.Username {
 | |
| 				found = true
 | |
| 			} else {
 | |
| 				newMarks = append(newMarks, mark)
 | |
| 			}
 | |
| 		}
 | |
| 		if !found {
 | |
| 			cm := models.CardMark{
 | |
| 				Username: fi.State.Username,
 | |
| 				CardID:   card.ID,
 | |
| 			}
 | |
| 			newMarks = append(newMarks, cm)
 | |
| 			if err := repo.CardMarksAdd(r.Context(), &cm); err != nil {
 | |
| 				log.Error("failed to add mark", "error", err, "card", card)
 | |
| 				abortWithError(w, "failed to add mark")
 | |
| 				return
 | |
| 			}
 | |
| 		} else {
 | |
| 			// if mark was found, it needs to be removed
 | |
| 			if err := repo.CardMarksRemove(r.Context(), card.ID, fi.State.Username); err != nil {
 | |
| 				log.Error("failed to remove mark", "error", err, "card", card)
 | |
| 				abortWithError(w, "failed to remove mark")
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		fi.Room.Cards[i].Marks = newMarks
 | |
| 		cardword = fi.Room.Cards[i]
 | |
| 	}
 | |
| 	if err := saveFullInfo(r.Context(), fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
 | |
| 	if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil {
 | |
| 		log.Error("failed to execute cardword template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleActionHistory(w http.ResponseWriter, r *http.Request) {
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tmpl.ExecuteTemplate(w, "actionhistory", fi.Room.ActionHistory); err != nil {
 | |
| 		log.Error("failed to execute actionhistory template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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")
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	var botname string
 | |
| 	maxID, err := repo.PlayerGetMaxID(r.Context())
 | |
| 	if err != nil {
 | |
| 		log.Warn("failed to get players max id")
 | |
| 		botname = fmt.Sprintf("bot_%d", len(llmapi.SignalChanMap)+1) // what if many rooms?
 | |
| 	} else {
 | |
| 		botname = fmt.Sprintf("bot_%d", maxID+1) // what if many rooms?
 | |
| 	}
 | |
| 	log.Debug("got add-bot request", "team", team, "role", role, "max_id", maxID, "botname", botname, "error", err)
 | |
| 	_, err = llmapi.NewBot(role, team, botname, fi.Room.ID, cfg, false)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// go bot.StartBot()
 | |
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
 | |
| }
 | |
| 
 | |
| func HandleRemoveBot(w http.ResponseWriter, r *http.Request) {
 | |
| 	botName := r.URL.Query().Get("bot")
 | |
| 	log.Debug("got remove-bot request", "bot_name", botName)
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := llmapi.RemoveBot(botName, fi.Room); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
 | |
| }
 | 
