diff --git a/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala b/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala index 85ae74c..65e4315 100644 --- a/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala +++ b/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala @@ -75,4 +75,7 @@ class OfficialBotAccount extends PanacheEntityBase: var rating: Int = 1500 var createdAt: Instant = uninitialized + + @Column(length = 1024) + var token: String = uninitialized // scalafix:on diff --git a/modules/account/src/main/scala/de/nowchess/account/dto/Dtos.scala b/modules/account/src/main/scala/de/nowchess/account/dto/Dtos.scala index 7307bed..f6f6726 100644 --- a/modules/account/src/main/scala/de/nowchess/account/dto/Dtos.scala +++ b/modules/account/src/main/scala/de/nowchess/account/dto/Dtos.scala @@ -46,6 +46,6 @@ case class BotAccountWithTokenDto(id: String, name: String, rating: Int, token: case class RotatedTokenDto(token: String) -case class OfficialBotAccountDto(id: String, name: String, rating: Int, createdAt: String) +case class OfficialBotAccountDto(id: String, name: String, rating: Int, createdAt: String, token: Option[String] = None) case class OfficialChallengeResponse(gameId: String, botName: String, difficulty: Int) diff --git a/modules/account/src/main/scala/de/nowchess/account/resource/AccountResource.scala b/modules/account/src/main/scala/de/nowchess/account/resource/AccountResource.scala index df4d367..e75c437 100644 --- a/modules/account/src/main/scala/de/nowchess/account/resource/AccountResource.scala +++ b/modules/account/src/main/scala/de/nowchess/account/resource/AccountResource.scala @@ -187,11 +187,11 @@ class AccountResource: @POST @Path("/official-bots") - @RolesAllowed(Array("Admin")) + @RolesAllowed(Array("**")) def createOfficialBot(req: CreateBotAccountRequest): Response = accountService.createOfficialBotAccount(req.name) match case Right(bot) => - Response.status(Response.Status.CREATED).entity(toOfficialBotDto(bot)).build() + Response.status(Response.Status.CREATED).entity(toOfficialBotDtoWithToken(bot)).build() case Left(error) => Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ErrorDto(error.message)).build() @@ -211,3 +211,12 @@ class AccountResource: rating = bot.rating, createdAt = bot.createdAt.toString, ) + + private def toOfficialBotDtoWithToken(bot: OfficialBotAccount): OfficialBotAccountDto = + OfficialBotAccountDto( + id = bot.id.toString, + name = bot.name, + rating = bot.rating, + createdAt = bot.createdAt.toString, + token = Some(bot.token), + ) diff --git a/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala b/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala index 114dfe2..f9027e8 100644 --- a/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala +++ b/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala @@ -205,6 +205,8 @@ class AccountService: bot.name = botName bot.createdAt = Instant.now() officialBotAccountRepository.persist(bot) + bot.token = generateBotToken(bot.id, bot.name) + officialBotAccountRepository.persist(bot) Right(bot) def getOfficialBotAccounts(): List[OfficialBotAccount] = diff --git a/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala b/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala index 8acbda0..e34f06d 100644 --- a/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala +++ b/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala @@ -1,10 +1,12 @@ package de.nowchess.tournament.service import de.nowchess.tournament.client.{CoreCreateGameRequest, CoreGameClient, CorePlayerInfo, CoreTimeControl} +import de.nowchess.tournament.config.RedisConfig import de.nowchess.tournament.domain.{Tournament, TournamentPairing, TournamentParticipant} import de.nowchess.tournament.dto.{BotRef, Clock, CreateTournamentForm, PairingDto, ResultDto, Standing, TournamentDto, Variant} import de.nowchess.tournament.error.TournamentError import de.nowchess.tournament.repository.{PairingRepository, ParticipantRepository, TournamentRepository} +import io.quarkus.redis.datasource.RedisDataSource import jakarta.enterprise.context.ApplicationScoped import jakarta.inject.Inject import jakarta.transaction.Transactional @@ -28,6 +30,9 @@ class TournamentService: @Inject @RestClient var coreGameClient: CoreGameClient = uninitialized + + @Inject var redis: RedisDataSource = uninitialized + @Inject var redisConfig: RedisConfig = uninitialized // scalafix:on @Transactional @@ -156,6 +161,20 @@ class TournamentService: pairingRepository.persist(pairing) streamManager.publishToBot(tournamentId, white.botId, s"""{"type":"gameStart","round":$round,"gameId":"${resp.gameId}","color":"white"}""") streamManager.publishToBot(tournamentId, black.botId, s"""{"type":"gameStart","round":$round,"gameId":"${resp.gameId}","color":"black"}""") + publishBotGameStart(white.botName, resp.gameId, "white", white.botId) + publishBotGameStart(black.botName, resp.gameId, "black", black.botId) + + private def publishBotGameStart( + botName: String, + gameId: String, + playingAs: String, + botAccountId: String, + ): Unit = + val channel = s"${redisConfig.prefix}:bot:$botName:events" + val payload = s"""{"type":"gameStart","gameId":"$gameId","playingAs":"$playingAs","difficulty":1500,"botAccountId":"$botAccountId"}""" + Try(redis.pubsub(classOf[String]).publish(channel, payload)) match + case Failure(ex) => log.warnf(ex, "Failed to publish gameStart to bot channel %s", channel) + case Success(_) => () @Transactional def handleGameResult(gameId: String, result: String, pgn: String): Unit =