diff --git a/modules/official-bots/src/main/resources/application.yml b/modules/official-bots/src/main/resources/application.yml index cf77b52..1d642d2 100644 --- a/modules/official-bots/src/main/resources/application.yml +++ b/modules/official-bots/src/main/resources/application.yml @@ -12,6 +12,12 @@ quarkus: enabled: true log: level: INFO + mp: + jwt: + verify: + publickey: + location: ${JWT_PUBLIC_KEY_PATH:keys/public.pem} + issuer: nowchess nowchess: redis: diff --git a/modules/official-bots/src/main/resources/keys/public.pem b/modules/official-bots/src/main/resources/keys/public.pem new file mode 100644 index 0000000..6b6b842 --- /dev/null +++ b/modules/official-bots/src/main/resources/keys/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxDsnsCAl0vQx7Vu9CLDZ +g0SG05NgUzu9T+3DTEaHGq60T2uriO8BenwyvsF3BnDqTbKf4voohZ1DNfzdbT1J +Fj8B62FrDmxcO+sp1/b5HUCJP6y2uSRCmzOHe5k7Pk1IEi72FgBpKXSRkFibRlVf +634g7mgsPZAQ9PJEsv4Qvm05T9L6+Gmq6N3bMVLKRXs4RhDhaFbYH9GtUg1eI0yH +YjGyRfqzW/nqVMstOLHt8CuPouq4p7eMzeDH3YHkxPm4GG5foCXMOd2DZrW0SCcr +7dhFeNVWzQ2m53eOhBzNQX+v3pgjVStsePhBRt2LyGfwkNzmqDgqWsMzSHRMY+cn +WQIDAQAB +-----END PUBLIC KEY----- diff --git a/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotConfig.scala b/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotConfig.scala index 002c436..213129f 100644 --- a/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotConfig.scala +++ b/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotConfig.scala @@ -20,7 +20,7 @@ object TournamentBotConfig: tournamentId <- env.get("TOURNAMENT_ID").filter(_.nonEmpty) token <- env.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty) botId <- jwtSubject(token) - serverUrl = env.getOrElse("TOURNAMENT_SERVER_URL", "http://localhost:8089") + serverUrl = env.getOrElse("TOURNAMENT_SERVER_URL", "http://141.37.123.132:8086") difficulty = env.getOrElse("TOURNAMENT_BOT_DIFFICULTY", "medium") yield TournamentBotConfig(serverUrl, tournamentId, token, botId, difficulty) diff --git a/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotGamePlayer.scala b/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotGamePlayer.scala index 117d74a..93d5e9d 100644 --- a/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotGamePlayer.scala +++ b/modules/official-bots/src/main/scala/de/nowchess/bot/service/TournamentBotGamePlayer.scala @@ -39,10 +39,11 @@ class TournamentBotGamePlayer: // scalafix:on DisableSyntax.var val defaultServerUrl: String = - System.getenv().asScala.getOrElse("TOURNAMENT_SERVER_URL", "http://localhost:8089") + System.getenv().asScala.getOrElse("TOURNAMENT_SERVER_URL", "http://141.37.123.132:8086") @PostConstruct def initialize(): Unit = + parkOnStartup() config match case None => log.info("Tournament bot disabled — set TOURNAMENT_ID and TOURNAMENT_BOT_TOKEN to enable") @@ -50,6 +51,35 @@ class TournamentBotGamePlayer: log.infof("Tournament bot enabled — server=%s tournament=%s bot=%s", cfg.serverUrl, cfg.tournamentId, cfg.botId) startAsync(cfg) + private def parkOnStartup(): Unit = + park(defaultServerUrl, "expert") match + case Some(id) => log.infof("Parked expert bot on %s as id %s", defaultServerUrl, id) + case None => log.warnf("Failed to park expert bot on %s", defaultServerUrl) + + private def parkToken(serverUrl: String, difficulty: String): Option[String] = + System.getenv().asScala.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty) + .orElse(registerBot(serverUrl, difficulty).map(_._2)) + + private def park(serverUrl: String, difficulty: String): Option[String] = + parkToken(serverUrl, difficulty).flatMap { token => + Try { + val body = s"""{"name":"${botName(difficulty)}"}""" + val response = client + .target(serverUrl) + .path("api") + .path("bots") + .request(MediaType.APPLICATION_JSON) + .header("Authorization", s"Bearer $token") + .post(Entity.entity(body, MediaType.APPLICATION_JSON)) + if response.getStatus == 201 || response.getStatus == 200 then + val id = objectMapper.readTree(response.readEntity(classOf[String])).path("id").asText() + response.close() + log.infof("Parked official bot %s as id %s", botName(difficulty), id) + Option(id).filter(_.nonEmpty) + else { log.warnf("Parking bot %s returned status %d", botName(difficulty), response.getStatus); response.close(); None } + }.getOrElse(None) + } + def joinTournament(tournamentId: String, difficulty: String, serverUrl: String): Either[String, String] = registerBot(serverUrl, difficulty) match case None => Left("Failed to register bot with tournament server") @@ -65,10 +95,11 @@ class TournamentBotGamePlayer: thread.setDaemon(true) thread.start() + private def botName(difficulty: String): String = s"NowChess ${difficulty.capitalize}" + private def registerBot(serverUrl: String, difficulty: String): Option[(String, String)] = Try { - val name = s"NowChess ${difficulty.capitalize}" - val body = s"""{"name":"$name","isBot":true}""" + val body = s"""{"name":"${botName(difficulty)}","isBot":true}""" val response = client .target(serverUrl) .path("api")