<commit_before>package controllers



import play.api.libs.json.Json
import play.api.mvc.Result
import scala.annotation.nowarn
import scala.concurrent.duration._

import lila.api.Context
import lila.app._
import lila.challenge.{ Challenge => ChallengeModel }
import lila.common.{ HTTPRequest, IpAddress }
import lila.game.{ AnonCookie, Pov }
import lila.socket.Socket.SocketVersion
import lila.user.{ User => UserModel }
import views.html

final class Challenge(
    env: Env
) extends LilaController(env) {

  def api = env.challenge.api

  def all =
    Auth { implicit ctx => me =>
      XhrOrRedirectHome {
        api allFor me.id map { all =>
          Ok(env.challenge.jsonView(all)) as JSON
        }
      }
    }

  def show(id: String, @nowarn("cat=unused") _color: Option[String]) =
    Open { implicit ctx =>
      showId(id)
    }

  protected[controllers] def showId(id: String)(implicit
      ctx: Context
  ): Fu[Result] =
    OptionFuResult(api byId id)(showChallenge(_))

  protected[controllers] def showChallenge(
      c: ChallengeModel,
      error: Option[String] = None
  )(implicit
      ctx: Context
  ): Fu[Result] =
    env.challenge version c.id flatMap { version =>
      val mine = isMine(c)
      import lila.challenge.Direction
      val direction: Option[Direction] =
        if (mine) Direction.Out.some
        else if (isForMe(c)) Direction.In.some
        else none
      val json = env.challenge.jsonView.show(c, version, direction)
      negotiate(
        html =
          if (mine) fuccess {
            error match {
              case Some(e) => BadRequest(html.challenge.mine(c, json, e.some))
              case None    => Ok(html.challenge.mine(c, json, none))
            }
          }
          else
            (c.challengerUserId ?? env.user.repo.named) map { user =>
              Ok(html.challenge.theirs(c, json, user, get("color") flatMap chess.Color.apply))
            },
        api = _ => Ok(json).fuccess
      ) flatMap withChallengeAnonCookie(mine && c.challengerIsAnon, c, owner = true)
    } dmap env.lilaCookie.ensure(ctx.req)

  private def isMine(challenge: ChallengeModel)(implicit ctx: Context) =
    challenge.challenger match {
      case lila.challenge.Challenge.Challenger.Anonymous(secret)     => HTTPRequest sid ctx.req contains secret
      case lila.challenge.Challenge.Challenger.Registered(userId, _) => ctx.userId contains userId
      case lila.challenge.Challenge.Challenger.Open                  => false
    }

  private def isForMe(challenge: ChallengeModel)(implicit ctx: Context) =
    challenge.destUserId.fold(true)(ctx.userId.contains)

  def accept(id: String, color: Option[String]) =
    Open { implicit ctx =>
      OptionFuResult(api byId id) { c =>
        val cc = color flatMap chess.Color.apply
        isForMe(c) ?? api
          .accept(c, ctx.me, HTTPRequest sid ctx.req, cc)
          .flatMap {
            case Some(pov) =>
              negotiate(
                html = Redirect(routes.Round.watcher(pov.gameId, cc.fold("white")(_.name))).fuccess,
                api = apiVersion => env.api.roundApi.player(pov, none, apiVersion) map { Ok(_) }
              ) flatMap withChallengeAnonCookie(ctx.isAnon, c, owner = false)
            case None =>
              negotiate(
                html = Redirect(routes.Round.watcher(c.id, cc.fold("white")(_.name))).fuccess,
                api = _ => notFoundJson("Someone else accepted the challenge")
              )
          }
      }
    }
  def apiAccept(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.onlineByIdFor(id, me) flatMap {
        _ ?? { api.accept(_, me.some, none) }
      } flatMap { res =>
        if (res.isDefined) jsonOkResult.fuccess
        else
          env.bot.player.rematchAccept(id, me) flatMap {
            case true => jsonOkResult.fuccess
            case _    => notFoundJson()
          }
      }
    }

  private def withChallengeAnonCookie(cond: Boolean, c: ChallengeModel, owner: Boolean)(
      res: Result
  )(implicit ctx: Context): Fu[Result] =
    cond ?? {
      env.game.gameRepo.game(c.id).map {
        _ map { game =>
          env.lilaCookie.cookie(
            AnonCookie.name,
            game.player(if (owner) c.finalColor else !c.finalColor).id,
            maxAge = AnonCookie.maxAge.some,
            httpOnly = false.some
          )
        }
      }
    } map { cookieOption =>
      cookieOption.fold(res) { res.withCookies(_) }
    }

  def decline(id: String) =
    Auth { implicit ctx => _ =>
      OptionFuResult(api byId id) { c =>
        if (isForMe(c)) api decline c
        else notFound
      }
    }
  def apiDecline(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.activeByIdFor(id, me) flatMap {
        case None =>
          env.bot.player.rematchDecline(id, me) flatMap {
            case true => jsonOkResult.fuccess
            case _    => notFoundJson()
          }
        case Some(c) => api.decline(c) inject jsonOkResult
      }
    }

  def cancel(id: String) =
    Open { implicit ctx =>
      OptionFuResult(api byId id) { c =>
        if (isMine(c)) api cancel c
        else notFound
      }
    }
  def apiCancel(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.activeByIdBy(id, me) flatMap {
        case None    => notFoundJson()
        case Some(c) => api.cancel(c) inject jsonOkResult
      }
    }

  private val ChallengeIpRateLimit = new lila.memo.RateLimit[IpAddress](
    100,
    10.minute,
    key = "challenge.create.ip",
    enforce = env.net.rateLimit.value
  )

  private val ChallengeUserRateLimit = lila.memo.RateLimit.composite[lila.user.User.ID](
    key = "challenge.create.user",
    enforce = env.net.rateLimit.value
  )(
    ("fast", 5, 1.minute),
    ("slow", 30, 1.day)
  )

  def toFriend(id: String) =
    AuthBody { implicit ctx => _ =>
      import play.api.data._
      import play.api.data.Forms._
      implicit def req = ctx.body
      OptionFuResult(api byId id) { c =>
        if (isMine(c))<commit_msg>{master} challenge cancel API can also abort games<commit_after>package controllers

import play.api.libs.json.Json
import play.api.mvc.Result
import scala.annotation.nowarn
import scala.concurrent.duration._
import views.html

import lila.api.Context
import lila.app._
import lila.challenge.{ Challenge => ChallengeModel }
import lila.common.{ HTTPRequest, IpAddress }
import lila.game.{ AnonCookie, Pov }
import lila.socket.Socket.SocketVersion
import lila.user.{ User => UserModel }

final class Challenge(
    env: Env
) extends LilaController(env) {

  def api = env.challenge.api

  def all =
    Auth { implicit ctx => me =>
      XhrOrRedirectHome {
        api allFor me.id map { all =>
          Ok(env.challenge.jsonView(all)) as JSON
        }
      }
    }

  def show(id: String, @nowarn("cat=unused") _color: Option[String]) =
    Open { implicit ctx =>
      showId(id)
    }

  protected[controllers] def showId(id: String)(implicit
      ctx: Context
  ): Fu[Result] =
    OptionFuResult(api byId id)(showChallenge(_))

  protected[controllers] def showChallenge(
      c: ChallengeModel,
      error: Option[String] = None
  )(implicit
      ctx: Context
  ): Fu[Result] =
    env.challenge version c.id flatMap { version =>
      val mine = isMine(c)
      import lila.challenge.Direction
      val direction: Option[Direction] =
        if (mine) Direction.Out.some
        else if (isForMe(c)) Direction.In.some
        else none
      val json = env.challenge.jsonView.show(c, version, direction)
      negotiate(
        html =
          if (mine) fuccess {
            error match {
              case Some(e) => BadRequest(html.challenge.mine(c, json, e.some))
              case None    => Ok(html.challenge.mine(c, json, none))
            }
          }
          else
            (c.challengerUserId ?? env.user.repo.named) map { user =>
              Ok(html.challenge.theirs(c, json, user, get("color") flatMap chess.Color.apply))
            },
        api = _ => Ok(json).fuccess
      ) flatMap withChallengeAnonCookie(mine && c.challengerIsAnon, c, owner = true)
    } dmap env.lilaCookie.ensure(ctx.req)

  private def isMine(challenge: ChallengeModel)(implicit ctx: Context) =
    challenge.challenger match {
      case lila.challenge.Challenge.Challenger.Anonymous(secret)     => HTTPRequest sid ctx.req contains secret
      case lila.challenge.Challenge.Challenger.Registered(userId, _) => ctx.userId contains userId
      case lila.challenge.Challenge.Challenger.Open                  => false
    }

  private def isForMe(challenge: ChallengeModel)(implicit ctx: Context) =
    challenge.destUserId.fold(true)(ctx.userId.contains)

  def accept(id: String, color: Option[String]) =
    Open { implicit ctx =>
      OptionFuResult(api byId id) { c =>
        val cc = color flatMap chess.Color.apply
        isForMe(c) ?? api
          .accept(c, ctx.me, HTTPRequest sid ctx.req, cc)
          .flatMap {
            case Some(pov) =>
              negotiate(
                html = Redirect(routes.Round.watcher(pov.gameId, cc.fold("white")(_.name))).fuccess,
                api = apiVersion => env.api.roundApi.player(pov, none, apiVersion) map { Ok(_) }
              ) flatMap withChallengeAnonCookie(ctx.isAnon, c, owner = false)
            case None =>
              negotiate(
                html = Redirect(routes.Round.watcher(c.id, cc.fold("white")(_.name))).fuccess,
                api = _ => notFoundJson("Someone else accepted the challenge")
              )
          }
      }
    }
  def apiAccept(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.onlineByIdFor(id, me) flatMap {
        _ ?? { api.accept(_, me.some, none) }
      } flatMap { res =>
        if (res.isDefined) jsonOkResult.fuccess
        else
          env.bot.player.rematchAccept(id, me) flatMap {
            case true => jsonOkResult.fuccess
            case _    => notFoundJson()
          }
      }
    }

  private def withChallengeAnonCookie(cond: Boolean, c: ChallengeModel, owner: Boolean)(
      res: Result
  )(implicit ctx: Context): Fu[Result] =
    cond ?? {
      env.game.gameRepo.game(c.id).map {
        _ map { game =>
          env.lilaCookie.cookie(
            AnonCookie.name,
            game.player(if (owner) c.finalColor else !c.finalColor).id,
            maxAge = AnonCookie.maxAge.some,
            httpOnly = false.some
          )
        }
      }
    } map { cookieOption =>
      cookieOption.fold(res) { res.withCookies(_) }
    }

  def decline(id: String) =
    Auth { implicit ctx => _ =>
      OptionFuResult(api byId id) { c =>
        if (isForMe(c)) api decline c
        else notFound
      }
    }
  def apiDecline(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.activeByIdFor(id, me) flatMap {
        case None =>
          env.bot.player.rematchDecline(id, me) flatMap {
            case true => jsonOkResult.fuccess
            case _    => notFoundJson()
          }
        case Some(c) => api.decline(c) inject jsonOkResult
      }
    }

  def cancel(id: String) =
    Open { implicit ctx =>
      OptionFuResult(api byId id) { c =>
        if (isMine(c)) api cancel c
        else notFound
      }
    }
  def apiCancel(id: String) =
    Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
      api.activeByIdBy(id, me) flatMap {
        case Some(c) => api.cancel(c) inject jsonOkResult
        case None =>
          api.activeByIdFor(id, me) flatMap {
            case Some(c) => api.decline(c) inject jsonOkResult
            case None =>
              env.game.gameRepo game id dmap {
                _ flatMap { Pov.ofUserId(_, me.id) }
              } flatMap {
                _ ?? { p => env.round.proxyRepo.upgradeIfPresent(p) dmap some }
              } dmap (_.filter(_.game.abortable)) flatMap {
                case Some(pov) =>
                  import lila.hub.actorApi.map.Tell
                  import lila.hub.actorApi.round.Abort
                  lila.common.Bus.publish(Tell(id, Abort(pov.playerId)), "roundSocket")
                  jsonOkResult.fuccess
                case None => notFoundJson()
              }
          }
      }
    }

  private val ChallengeIpRateLimit = new lila.memo.RateLimit[IpAddress](
    100,
    10.minute,
    key = "challenge.create.ip",
    enforce = env.net.rateLimit.value
  )

  private val ChallengeUserRateLimit = lila.memo.RateLimit.composite[lila.user.User.ID](
    key = "challenge.create.user",
    enforce = env.net.rateLimit.value
  )(
    ("fast", 5, 1.minute),
    ("slow", 30, 1.day)
  )

  def toFriend(id: String) =
    AuthBody { implicit ctx => _ =>
      import play.api.data._
      import play.api.data.Forms._
      implicit def req = ctx.body
      OptionFuResult(api byId id) { c =>
        if (isMine(c))
{'fraction_non_alphanumeric': 0.11983375663818979, 'fraction_numerical': 0.0015392903871315324, 'mean_word_length': 2.1638665692719745, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 98, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1661594', 'n_tokens_mistral': 4212, 'n_tokens_neox': 4050, 'n_words': 891}