Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions app/controllers/PlayApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package controllers
import play.api.i18n.Lang
import play.api.mvc.*

import lila.app.*
import lila.app.{ *, given }
import lila.core.id.GameAnyId
import lila.core.perf.UserWithPerfs

Expand Down Expand Up @@ -131,9 +131,15 @@ final class PlayApi(env: Env)(using akka.stream.Materializer) extends LilaContro
BadRequest:
jsonError:
"This endpoint can only be used with a Bot account. See https://lichess.org/api#operation/botAccountUpgrade"
else if !lila.game.Game.isBotCompatible(pov.game) then
BadRequest(jsonError("This game cannot be played with the Bot API."))
else f(pov)
else
isReallyBotCompatible(pov.game).flatMap:
if _ then f(pov)
else BadRequest(jsonError("This game cannot be played with the Bot API."))

private def isReallyBotCompatible(game: lila.core.game.Game): Fu[Boolean] =
lila.game.Game.isBotCompatible(game) match
case Some(known) => fuccess(known)
case None => game.tournamentId.so(env.tournament.api.isForBots)

private def WithPovAsBoard(id: GameId)(f: Pov => Fu[Result])(using ctx: Context)(using Me) =
WithPov(id): pov =>
Expand Down
2 changes: 1 addition & 1 deletion modules/api/src/main/EventStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ final class EventStream(
gameJsonView
.ownerPreview(pov)(using lightUserApi.sync)
.add("source" -> game.source.map(_.name)) ++ compatJson(
bot = me.isBot && lila.game.Game.isBotCompatible(game),
bot = me.isBot && lila.game.Game.isBotCompatible(game).|(true),
board = lila.game.Game.isBoardCompatible(game)
) ++ Json.obj(
"id" -> game.id // API BC
Expand Down
11 changes: 7 additions & 4 deletions modules/game/src/main/Game.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,16 @@ object Game:
chess.Speed(c.config) >= Speed.Blitz
}

def isBotCompatible(game: Game): Boolean = {
game.hasAi || game.sourceIs(_.Friend) || game.sourceIs(_.Api)
} && isBotCompatible(game.speed)
// if source is Arena, we will also need to check if the arena accepts bots!
def isBotCompatible(game: Game): Option[Boolean] =
if !isBotCompatible(game.speed) then false.some
else if game.hasAi || game.sourceIs(_.Friend) || game.sourceIs(_.Api) then true.some
else if game.sourceIs(_.Arena) then none
else false.some

def isBotCompatible(speed: Speed): Boolean = speed >= Speed.Bullet

def isBoardOrBotCompatible(game: Game) = isBoardCompatible(game) || isBotCompatible(game)
def mightBeBoardOrBotCompatible(game: Game) = isBoardCompatible(game) || isBotCompatible(game).|(true)

object BSONFields:
export lila.core.game.BSONFields.*
Expand Down
2 changes: 1 addition & 1 deletion modules/round/src/main/Drawer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ final private[round] class Drawer(
lila.core.round.CorresDrawOfferEvent(game.id),
"offerEventCorres"
)
if lila.game.Game.isBoardOrBotCompatible(game) then
if lila.game.Game.mightBeBoardOrBotCompatible(game) then
Bus.publish(
lila.game.actorApi.BoardDrawOffer(game),
lila.game.actorApi.BoardDrawOffer.makeChan(game.id)
Expand Down
2 changes: 1 addition & 1 deletion modules/round/src/main/RoundAsyncActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ final private class RoundAsyncActor(
publishBoardBotGone(pov, millis.some)

private def publishBoardBotGone(pov: Pov, millis: Option[Long]) =
if lila.game.Game.isBoardOrBotCompatible(pov.game) then
if lila.game.Game.mightBeBoardOrBotCompatible(pov.game) then
lila.common.Bus.publish(
lila.game.actorApi.BoardGone(pov, millis.map(m => (m.atLeast(0) / 1000).toInt)),
lila.game.actorApi.BoardGone.makeChan(gameId)
Expand Down
4 changes: 2 additions & 2 deletions modules/round/src/main/Takebacker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,14 @@ final private class Takebacker(
proxy.save(p2).inject(p2.events)

private def publishTakebackOffer(game: Game): Unit =
if lila.game.Game.isBoardOrBotCompatible(game) then
if lila.game.Game.mightBeBoardOrBotCompatible(game) then
Bus.publish(
lila.game.actorApi.BoardTakebackOffer(game),
lila.game.actorApi.BoardTakebackOffer.makeChan(game.id)
)

private def publishTakeback(prevPov: Pov)(using proxy: GameProxy): Unit =
if lila.game.Game.isBoardOrBotCompatible(prevPov.game) then
if lila.game.Game.mightBeBoardOrBotCompatible(prevPov.game) then
proxy.withPov(prevPov.color): p =>
fuccess:
Bus.publish(
Expand Down
4 changes: 4 additions & 0 deletions modules/tournament/src/main/TournamentApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ final class TournamentApi(
_.sequentiallyVoid(playerRepo.withdraw(_, userId))
}

def isForBots(tourId: TourId): Fu[Boolean] =
for tour <- cached.tourCache.byId(tourId)
yield tour.exists(_.conditions.bots.so(_.allowed))

private[tournament] def kickFromTeam(teamId: TeamId, userId: UserId): Funit =
tournamentRepo.withdrawableIds(userId, teamId = teamId.some, reason = "kickFromTeam").flatMap {
_.sequentiallyVoid: tourId =>
Expand Down
Loading