package flags import ( "context" "database/sql" "net/http" "strconv" "time" "git.tijl.dev/tijl/tijl.dev-core/internal/queries" "git.tijl.dev/tijl/tijl.dev-core/internal/user" "git.tijl.dev/tijl/tijl.dev-core/internal/utils" "git.tijl.dev/tijl/tijl.dev-core/modules/db" "git.tijl.dev/tijl/tijl.dev-core/modules/i18n" "git.tijl.dev/tijl/tijl.dev-core/modules/web" "github.com/enescakir/emoji" "github.com/gofiber/fiber/v2" "github.com/google/uuid" ) const flagSessionCookie string = "app_flags_game_session" type NullableUUID struct { uuid.UUID valid bool } type NullableString struct { String string valid bool } func answerHandler(c *fiber.Ctx) error { gameId, err := uuid.Parse(c.Cookies(flagSessionCookie)) if err != nil { return err } answer := c.FormValue("answer") gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId) if err != nil { return err } err, countries := filterCountriesByTags(gameSession.Tags) if err != nil { return err } shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.String()) correctAnswer := shuffledCountries[gameSession.QuestionCurrent-1] if answer == correctAnswer { db.Queries.AppFlagsUpdateGame(context.TODO(), queries.AppFlagsUpdateGameParams{ GameID: gameId, QuestionCurrent: gameSession.QuestionCurrent + 1, }) db.Queries.AppFlagsUpsertGameAnswer(context.TODO(), queries.AppFlagsUpsertGameAnswerParams{ GameID: gameId, Question: gameSession.QuestionCurrent, Errors: 0, }) } else { db.Queries.AppFlagsUpsertGameAnswer(context.TODO(), queries.AppFlagsUpsertGameAnswerParams{ GameID: gameId, Question: gameSession.QuestionCurrent, Errors: 1, }) } err = db.Queries.AppFlagsUpdateQuestionCorrect(context.TODO(), gameId) if err != nil { return err } if answer == correctAnswer { return questionHandler(c, NullableUUID{}, NullableString{}) } else { return questionHandler(c, NullableUUID{}, NullableString{valid: true, String: answer}) } } func questionHandler(c *fiber.Ctx, newGame NullableUUID, prevError NullableString) error { var gameId uuid.UUID var err error if newGame.valid { gameId = newGame.UUID } else { gameId, err = uuid.Parse(c.Cookies(flagSessionCookie)) if err != nil { return err } } gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId) if err != nil { return err } uid, err := user.GetSession(c) if uid != gameSession.Uid.String { utils.ClearCookie(c, flagSessionCookie) return gameStartHandler(c) } if (gameSession.QuestionAmount != 0) && (gameSession.QuestionAmount+1 == gameSession.QuestionCurrent) { return gameEndHandler(c) } err, countries := filterCountriesByTags(gameSession.Tags) if err != nil { return err } if int(gameSession.QuestionCurrent) == len(countries) { return gameEndHandler(c) } shuffledCountries := shuffleSlice(countries, gameSession.GameSeed.String()) correctAnswer := shuffledCountries[gameSession.QuestionCurrent-1] filteredCountries := filterSlice(countries, correctAnswer) shuffledAnswers := shuffleSlice(filteredCountries, gameSession.GameSeed.String()+string(gameSession.QuestionCurrent)) var correctAnswerData CountryCode for _, country := range countryCodes { if country.Code == correctAnswer { correctAnswerData = country break } } var filteredAnswers []string for _, shuffledAnswer := range shuffledAnswers { for _, country := range countryCodes { if shuffledAnswer == country.Code { if hasCommonTag(correctAnswerData.Tags, country.Tags) { filteredAnswers = append(filteredAnswers, country.Code) } } } } shuffledAnswers = filteredAnswers shuffledAnswers = shuffledAnswers[0:4] shuffledAnswers = append(shuffledAnswers, correctAnswer) shuffledAnswers = shuffleSlice(shuffledAnswers, gameSession.GameSeed.String()+string(gameSession.QuestionCurrent)) if gameSession.QuestionAmount != 0 && int(gameSession.QuestionAmount) < len(countries) { shuffledCountries = shuffledCountries[0:gameSession.QuestionAmount] } var timeleft = []string{} if gameSession.Seconds != 0 { timeleft = append(timeleft, strconv.Itoa(int(gameSession.Seconds)-int(time.Since(gameSession.CreatedAt).Seconds()))) } flag, err := emoji.CountryFlag(shuffledCountries[gameSession.QuestionCurrent-1]) if err != nil { return err } data := *web.Common(c) data["Title"] = "tmp" data["TimeLeft"] = timeleft data["QuestionsLeft"] = len(shuffledCountries) - int(gameSession.QuestionCurrent) + 1 data["Answers"] = shuffledAnswers data["Flag"] = flag data["Errors"] = gameSession.QuestionsErrors data["PreviousError"] = prevError.String data["ShortcutKeys"] = []string{"d", "f", "h", "j", "k"} return web.Render(c, "apps/flags/question", data, "layouts/base") } func sharedGameHandler(c *fiber.Ctx) error { shareKey := c.FormValue("sharekey") data, err := db.Queries.AppFlagsGetSharedData(context.TODO(), shareKey) if err != nil { return err } return setupGame(c, data.Tags, int(data.Questions), int(data.Seconds), NullableUUID{valid: true, UUID: data.GameSeed}) } func startNewGameHandler(c *fiber.Ctx) error { values := c.Request().PostArgs().PeekMulti("tags") var selectedTags []string for _, v := range values { selectedTags = append(selectedTags, string(v)) } if len(selectedTags) < 1 { return c.Status(http.StatusBadRequest).SendString(i18n.GetTranslations(i18n.GetLanguage(c))["select_more_countries"]) } err, countries := filterCountriesByTags(selectedTags) if err != nil { return err } if len(countries) < 6 { return c.Status(http.StatusBadRequest).SendString(i18n.GetTranslations(i18n.GetLanguage(c))["select_more_countries"]) } maxQuestions, err := strconv.Atoi(c.FormValue("max_questions")) if err != nil { return err } seconds, err := strconv.Atoi(c.FormValue("seconds")) if err != nil { return err } if c.FormValue("share") != "" { return createSharedGameData(c, selectedTags, maxQuestions, seconds) } return setupGame(c, selectedTags, maxQuestions, seconds, NullableUUID{}) } func createSharedGameData(c *fiber.Ctx, tags []string, maxQuestions int, seconds int) error { uid, err := user.GetSession(c) if err != nil { return c.SendStatus(http.StatusUnauthorized) } shareKey := utils.RandString(4) _, err = db.Queries.AppFlagsNewSharedData(context.TODO(), queries.AppFlagsNewSharedDataParams{ Uid: uid, ShareKey: shareKey, Tags: tags, Questions: int32(maxQuestions), Seconds: int32(seconds), GameSeed: uuid.New(), }) if err != nil { return err } data := *web.Common(c) data["Title"] = "tmp" data["ShareKey"] = shareKey return web.Render(c, "apps/flags/shared", data, "layouts/base") } func setupGame(c *fiber.Ctx, tags []string, maxQuestions int, seconds int, gameSeed NullableUUID) error { var Quid = sql.NullString{} uid, err := user.GetSession(c) if err == nil { Quid.Valid = true Quid.String = uid } createGameParams := queries.AppFlagsCreateGameWithSeedParams{ Uid: Quid, Tags: tags, QuestionAmount: int32(maxQuestions), Seconds: int32(seconds), GameSeed: uuid.New(), } if gameSeed.valid { createGameParams.GameSeed = gameSeed.UUID } gameID, err := db.Queries.AppFlagsCreateGameWithSeed(context.TODO(), createGameParams) if err != nil { return err } c.Cookie(&fiber.Cookie{ Name: flagSessionCookie, Value: gameID.String(), //Secure: true, }) return questionHandler(c, NullableUUID{valid: true, UUID: gameID}, NullableString{}) } func gameEndHandler(c *fiber.Ctx) error { gameId, err := uuid.Parse(c.Cookies(flagSessionCookie)) if err != nil { return err } gameSession, err := db.Queries.AppFlagsGetGame(context.TODO(), gameId) if err != nil { return err } data := *web.Common(c) data["Title"] = "tmp" data["Errors"] = gameSession.QuestionsErrors return web.Render(c, "apps/flags/end", data, "layouts/base") } func stopGameHandler(c *fiber.Ctx) error { // exit game utils.ClearCookie(c, flagSessionCookie) return gameStartHandler(c) } func gameStartHandler(c *fiber.Ctx) error { data := *web.Common(c) data["Title"] = "Flags Game | tijl.dev" data["SupportedTags"] = supportedTags return web.Render(c, "apps/flags/start", data, "layouts/base") }