Feat: remove bot
This commit is contained in:
		| @@ -1,18 +1,31 @@ | |||||||
| {{ define "addbot" }} | {{ define "addbot" }} | ||||||
|  | {{$botName := ""}} | ||||||
| <div> | <div> | ||||||
|  |   {{$botName = .Room.FindBotByTeamRole "blue" "mime"}} | ||||||
|   {{ if eq .Room.BlueTeam.Mime "" }} |   {{ if eq .Room.BlueTeam.Mime "" }} | ||||||
|   <button hx-get="/add-bot?team=blue&role=mime" hx-target="#addbot" class="bg-blue-400 text-black px-4 py-2 rounded">Add Bot Mime</button> |   <button hx-get="/add-bot?team=blue&role=mime" hx-target="#addbot" class="bg-blue-400 text-black px-4 py-2 rounded">Add Bot Mime</button> | ||||||
|  |   {{ else if ne $botName "" }} | ||||||
|  |   <button hx-get="/remove-bot?bot={{$botName}}" hx-target="#addbot" class="bg-blue-400 text-black px-4 py-2 rounded">Remove {{$botName}}</button> | ||||||
|   {{ end }} |   {{ end }} | ||||||
|  |   {{$botName = .Room.FindBotByTeamRole "red" "mime"}} | ||||||
|   {{ if eq .Room.RedTeam.Mime "" }} |   {{ if eq .Room.RedTeam.Mime "" }} | ||||||
|   <button hx-get="/add-bot?team=red&role=mime" hx-target="#addbot" class="bg-red-400 text-black px-4 py-2 rounded">Add Bot Mime</button> |   <button hx-get="/add-bot?team=red&role=mime" hx-target="#addbot" class="bg-red-400 text-black px-4 py-2 rounded">Add Bot Mime</button> | ||||||
|  |   {{ else if ne $botName "" }} | ||||||
|  |   <button hx-get="/remove-bot?bot={{$botName}}" hx-target="#addbot" class="bg-red-400 text-black px-4 py-2 rounded">Remove {{$botName}}</button> | ||||||
|   {{ end }} |   {{ end }} | ||||||
| </div> | </div> | ||||||
|  |   {{$botName = .Room.FindBotByTeamRole "blue" "guesser"}} | ||||||
| <div> | <div> | ||||||
|   {{ if not .Room.BlueTeam.Guessers }} |   {{ if not .Room.BlueTeam.Guessers }} | ||||||
|   <button hx-get="/add-bot?team=blue&role=guesser" hx-target="#addbot" class="bg-blue-300 text-black px-4 py-2 rounded">Add Bot Guesser</button> |   <button hx-get="/add-bot?team=blue&role=guesser" hx-target="#addbot" class="bg-blue-300 text-black px-4 py-2 rounded">Add Bot Guesser</button> | ||||||
|  |   {{ else if ne $botName "" }} | ||||||
|  |   <button hx-get="/remove-bot?bot={{$botName}}" hx-target="#addbot" class="bg-blue-300 text-black px-4 py-2 rounded">Remove {{$botName}}</button> | ||||||
|   {{ end }} |   {{ end }} | ||||||
|  |   {{$botName = .Room.FindBotByTeamRole "red" "guesser"}} | ||||||
|   {{ if not .Room.RedTeam.Guessers }} |   {{ if not .Room.RedTeam.Guessers }} | ||||||
|   <button hx-get="/add-bot?team=red&role=guesser" hx-target="#addbot" class="bg-red-300 text-black px-4 py-2 rounded">Add Bot Guesser</button> |   <button hx-get="/add-bot?team=red&role=guesser" hx-target="#addbot" class="bg-red-300 text-black px-4 py-2 rounded">Add Bot Guesser</button> | ||||||
|  |   {{ else if ne $botName "" }} | ||||||
|  |   <button hx-get="/remove-bot?bot={{$botName}}" hx-target="#addbot" class="bg-red-300 text-black px-4 py-2 rounded">Remove {{$botName}}</button> | ||||||
|   {{ end }} |   {{ end }} | ||||||
| </div> | </div> | ||||||
| {{end}} | {{end}} | ||||||
|   | |||||||
| @@ -1,16 +1,16 @@ | |||||||
| {{define "cardword"}} | {{define "cardword"}} | ||||||
| {{if .Revealed}} | {{if .Revealed}} | ||||||
|   {{if eq .Color "amber"}} |   {{if eq .Color "amber"}} | ||||||
|     <div id="card-{{.Word}}" class="bg-{{.Color}}-100 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  |     <div id="card-{{.Word}}" class="bg-{{.Color}}-100 border border-gray-500 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  | ||||||
|          style="text-shadow: 0 2px 4px rgba(0,0,0,0.9);"> {{.Word}} |          style="text-shadow: 0 2px 4px rgba(0,0,0,0.9);"> {{.Word}} | ||||||
|     </div> |     </div> | ||||||
|   {{else}} |   {{else}} | ||||||
|     <div id="card-{{.Word}}" class="bg-{{.Color}}-600 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  |     <div id="card-{{.Word}}" class="bg-{{.Color}}-600 border border-gray-500 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  | ||||||
|          style="text-shadow: 0 2px 4px rgba(0,0,0,0.9);"> {{.Word}} |          style="text-shadow: 0 2px 4px rgba(0,0,0,0.9);"> {{.Word}} | ||||||
|     </div> |     </div> | ||||||
|   {{end}} |   {{end}} | ||||||
| {{else}} | {{else}} | ||||||
| <div id="card-{{.Word}}" class="bg-stone-600 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  | <div id="card-{{.Word}}" class="bg-stone-600 border border-gray-500 p-4 rounded-lg min-w-[100px] text-center text-white cursor-pointer"  | ||||||
|      style="text-shadow: 0 2px 4px rgba(0,0,0,0.8);" |      style="text-shadow: 0 2px 4px rgba(0,0,0,0.8);" | ||||||
|      hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.05s"> |      hx-get="/word/show-color?word={{.Word}}" hx-trigger="click" hx-swap="outerHTML transition:true swap:.05s"> | ||||||
|     {{.Word}} |     {{.Word}} | ||||||
|   | |||||||
| @@ -194,3 +194,18 @@ func HandleAddBot(w http.ResponseWriter, r *http.Request) { | |||||||
| 	// go bot.StartBot() | 	// go bot.StartBot() | ||||||
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") | 	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, "") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { | ||||||
| @@ -248,12 +249,20 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) { | |||||||
| 		abortWithError(w, "your team already has a clue") | 		abortWithError(w, "your team already has a clue") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	// check if the clue is the same as one of the existing words | ||||||
|  | 	for _, card := range fi.Room.Cards { | ||||||
|  | 		if strings.EqualFold(card.Word, clue) { | ||||||
|  | 			msg := fmt.Sprintf("cannot use existing word (%s) as a clue", clue) | ||||||
|  | 			abortWithError(w, msg) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	// === | 	// === | ||||||
| 	action := models.Action{ | 	action := models.Action{ | ||||||
| 		Actor:      fi.State.Username, | 		Actor:      fi.State.Username, | ||||||
| 		ActorColor: string(fi.State.Team), | 		ActorColor: string(fi.State.Team), | ||||||
| 		WordColor:  string(fi.State.Team), | 		WordColor:  string(fi.State.Team), | ||||||
| 		Action:     "gave clue", | 		Action:     models.ActionTypeClue, | ||||||
| 		Word:       clue, | 		Word:       clue, | ||||||
| 		Number:     num, | 		Number:     num, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -77,7 +77,7 @@ func (b *Bot) checkGuess(word string, room *models.Room) error { | |||||||
| 		Actor:      b.BotName, | 		Actor:      b.BotName, | ||||||
| 		ActorColor: b.Team, | 		ActorColor: b.Team, | ||||||
| 		WordColor:  string(color), | 		WordColor:  string(color), | ||||||
| 		Action:     "guessed", | 		Action:     models.ActionTypeGuess, | ||||||
| 		Word:       word, | 		Word:       word, | ||||||
| 	} | 	} | ||||||
| 	room.ActionHistory = append(room.ActionHistory, action) | 	room.ActionHistory = append(room.ActionHistory, action) | ||||||
| @@ -115,12 +115,7 @@ func (b *Bot) checkGuess(word string, room *models.Room) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // StartBot | func (b *Bot) BotMove() { | ||||||
| func (b *Bot) StartBot() { |  | ||||||
| 	for { |  | ||||||
| 		select { |  | ||||||
| 		case <-SignalChanMap[b.BotName]: |  | ||||||
| 			func() { |  | ||||||
| 	// botJournalName := models.NotifyJournalPrefix + b.RoomID | 	// botJournalName := models.NotifyJournalPrefix + b.RoomID | ||||||
| 	b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) | 	b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) | ||||||
| 	// get room cards and actions | 	// get room cards and actions | ||||||
| @@ -168,7 +163,7 @@ func (b *Bot) StartBot() { | |||||||
| 			Actor:      b.BotName, | 			Actor:      b.BotName, | ||||||
| 			ActorColor: b.Team, | 			ActorColor: b.Team, | ||||||
| 			WordColor:  b.Team, | 			WordColor:  b.Team, | ||||||
| 						Action:     "gave clue", | 			Action:     models.ActionTypeClue, | ||||||
| 			Word:       mimeResp.Clue, | 			Word:       mimeResp.Clue, | ||||||
| 			Number:     mimeResp.Number, | 			Number:     mimeResp.Number, | ||||||
| 		} | 		} | ||||||
| @@ -213,7 +208,14 @@ func (b *Bot) StartBot() { | |||||||
| 		b.log.Debug("notifying bot", "name", botName) | 		b.log.Debug("notifying bot", "name", botName) | ||||||
| 		SignalChanMap[botName] <- true | 		SignalChanMap[botName] <- true | ||||||
| 	} | 	} | ||||||
| 			}() | } | ||||||
|  |  | ||||||
|  | // StartBot | ||||||
|  | func (b *Bot) StartBot() { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-SignalChanMap[b.BotName]: | ||||||
|  | 			b.BotMove() | ||||||
| 		case <-DoneChanMap[b.BotName]: | 		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 | ||||||
| @@ -221,6 +223,18 @@ func (b *Bot) StartBot() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func RemoveBot(botName string, room *models.Room) error { | ||||||
|  | 	// channels | ||||||
|  | 	DoneChanMap[botName] <- true | ||||||
|  | 	close(DoneChanMap[botName]) | ||||||
|  | 	close(SignalChanMap[botName]) | ||||||
|  | 	// maps | ||||||
|  | 	delete(room.BotMap, botName) | ||||||
|  | 	delete(DoneChanMap, botName) | ||||||
|  | 	delete(SignalChanMap, botName) | ||||||
|  | 	return saveRoom(room) | ||||||
|  | } | ||||||
|  |  | ||||||
| // EndBot | // EndBot | ||||||
|  |  | ||||||
| func NewBot(role, team, name, roomID string, cfg *config.Config, recovery bool) (*Bot, error) { | func NewBot(role, team, name, roomID string, cfg *config.Config, recovery bool) (*Bot, error) { | ||||||
|   | |||||||
| @@ -160,8 +160,10 @@ func (p *openRouterParser) ParseBytes(body []byte) (map[string]any, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (p *openRouterParser) MakePayload(prompt string) io.Reader { | func (p *openRouterParser) MakePayload(prompt string) io.Reader { | ||||||
|  | 	// "model": "deepseek/deepseek-chat-v3-0324:free", | ||||||
|  | 	// TODO: set list of models an option to pick on the frontend | ||||||
| 	strPayload := fmt.Sprintf(`{ | 	strPayload := fmt.Sprintf(`{ | ||||||
| 	"model": "deepseek/deepseek-chat-v3-0324:free", | 	"model": "google/gemini-2.0-flash-exp:free", | ||||||
| 	"messages": [ | 	"messages": [ | ||||||
| 		{ | 		{ | ||||||
| 		"role": "user", | 		"role": "user", | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							| @@ -47,6 +47,7 @@ func ListenToRequests(port string) *http.Server { | |||||||
| 	mux.HandleFunc("GET /word/show-color", handlers.HandleShowColor) | 	mux.HandleFunc("GET /word/show-color", handlers.HandleShowColor) | ||||||
| 	mux.HandleFunc("POST /check/name", handlers.HandleNameCheck) | 	mux.HandleFunc("POST /check/name", handlers.HandleNameCheck) | ||||||
| 	mux.HandleFunc("GET /add-bot", handlers.HandleAddBot) | 	mux.HandleFunc("GET /add-bot", handlers.HandleAddBot) | ||||||
|  | 	mux.HandleFunc("GET /remove-bot", handlers.HandleRemoveBot) | ||||||
| 	// special | 	// special | ||||||
| 	mux.HandleFunc("GET /renotify-bot", handlers.HandleRenotifyBot) | 	mux.HandleFunc("GET /renotify-bot", handlers.HandleRenotifyBot) | ||||||
| 	// sse | 	// sse | ||||||
|   | |||||||
| @@ -97,6 +97,16 @@ type Room struct { | |||||||
| 	LogJournal []string | 	LogJournal []string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // FindBotByTeamRole returns bot name if found; otherwise empty string | ||||||
|  | func (r *Room) FindBotByTeamRole(team, role string) string { | ||||||
|  | 	for bn, b := range r.BotMap { | ||||||
|  | 		if b.Role == StrToUserRole(role) && b.Team == StrToUserTeam(team) { | ||||||
|  | 			return bn | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
| func (r *Room) FetchLastClue() (*Action, error) { | func (r *Room) FetchLastClue() (*Action, error) { | ||||||
| 	for i := len(r.ActionHistory) - 1; i >= 0; i-- { | 	for i := len(r.ActionHistory) - 1; i >= 0; i-- { | ||||||
| 		if r.ActionHistory[i].Action == string(ActionTypeClue) { | 		if r.ActionHistory[i].Action == string(ActionTypeClue) { | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								todos.md
									
									
									
									
									
								
							| @@ -18,6 +18,7 @@ | |||||||
| - clear indication that model (llm) is thinking / answered; | - clear indication that model (llm) is thinking / answered; | ||||||
| - instead of guessing all words at ones, ask only for 1 word to be open. | - instead of guessing all words at ones, ask only for 1 word to be open. | ||||||
| - ways to remove bots from teams; | - ways to remove bots from teams; | ||||||
|  | - check if clue word is the same as one of the cards and return err if it is; + | ||||||
|  |  | ||||||
| #### sse points | #### sse points | ||||||
| - clue sse update; | - clue sse update; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder