lila/app/controllers/Setup.scala

231 lines
7.8 KiB
Scala

package controllers
import play.api.data.Form
import play.api.libs.json.Json
import play.api.mvc.{ Result, Results }
import scala.concurrent.duration._
import chess.format.FEN
import lila.api.{ Context, BodyContext }
import lila.app._
import lila.common.{ HTTPRequest, LilaCookie, IpAddress }
import lila.game.{ GameRepo, Pov, AnonCookie }
import lila.setup.Processor.HookResult
import lila.setup.ValidFen
import lila.socket.Socket.Uid
import lila.user.UserRepo
import views._
object Setup extends LilaController with TheftPrevention {
private def env = Env.setup
private[controllers] val PostRateLimit = new lila.memo.RateLimit[IpAddress](5, 1 minute,
name = "setup post",
key = "setup_post",
enforce = Env.api.Net.RateLimit)
def aiForm = Open { implicit ctx =>
if (HTTPRequest isXhr ctx.req) {
env.forms aiFilled get("fen").map(FEN) map { form =>
html.setup.forms.ai(
form,
Env.fishnet.aiPerfApi.intRatings,
form("fen").value flatMap ValidFen(getBool("strict"))
)
}
} else fuccess {
Redirect(routes.Lobby.home + "#ai")
}
}
def ai = process(env.forms.ai) { config => implicit ctx =>
env.processor ai config
}
def friendForm(userId: Option[String]) = Open { implicit ctx =>
if (HTTPRequest isXhr ctx.req)
env.forms friendFilled get("fen").map(FEN) flatMap { form =>
val validFen = form("fen").value flatMap ValidFen(false)
userId ?? UserRepo.named flatMap {
case None => Ok(html.setup.forms.friend(form, none, none, validFen)).fuccess
case Some(user) => Env.challenge.granter(ctx.me, user, none) map {
case Some(denied) => BadRequest(lila.challenge.ChallengeDenied.translated(denied))
case None => Ok(html.setup.forms.friend(form, user.some, none, validFen))
}
}
}
else fuccess {
Redirect(routes.Lobby.home + "#friend")
}
}
def friend(userId: Option[String]) = OpenBody { implicit ctx =>
implicit val req = ctx.body
PostRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
env.forms.friend(ctx).bindFromRequest.fold(
err => negotiate(
html = Lobby.renderHome(Results.BadRequest),
api = _ => jsonFormError(err)
),
config => userId ?? UserRepo.enabledById flatMap { destUser =>
destUser ?? { Env.challenge.granter(ctx.me, _, config.perfType) } flatMap {
case Some(denied) =>
val message = lila.challenge.ChallengeDenied.translated(denied)
negotiate(
html = BadRequest(message).fuccess,
api = _ => BadRequest(jsonError(message)).fuccess
)
case None =>
import lila.challenge.Challenge._
val challenge = lila.challenge.Challenge.make(
variant = config.variant,
initialFen = config.fen,
timeControl = config.makeClock map { c =>
TimeControl.Clock(c)
} orElse config.makeDaysPerTurn.map {
TimeControl.Correspondence.apply
} getOrElse TimeControl.Unlimited,
mode = config.mode,
color = config.color.name,
challenger = (ctx.me, HTTPRequest sid req) match {
case (Some(user), _) => Right(user)
case (_, Some(sid)) => Left(sid)
case _ => Left("no_sid")
},
destUser = destUser,
rematchOf = none
)
env.processor.saveFriendConfig(config) >>
(Env.challenge.api create challenge) flatMap {
case true => negotiate(
html = fuccess(Redirect(routes.Round.watcher(challenge.id, "white"))),
api = _ => Challenge showChallenge challenge
)
case false => negotiate(
html = fuccess(Redirect(routes.Lobby.home)),
api = _ => fuccess(BadRequest(jsonError("Challenge not created")))
)
}
}
}
)
}
}
def hookForm = Open { implicit ctx =>
NoBot {
if (HTTPRequest isXhr ctx.req) NoPlaybanOrCurrent {
env.forms.hookFilled(timeModeString = get("time")) map { html.setup.forms.hook(_) }
}
else fuccess {
Redirect(routes.Lobby.home + "#hook")
}
}
}
private def hookResponse(res: HookResult) = res match {
case HookResult.Created(id) => Ok(Json.obj(
"ok" -> true,
"hook" -> Json.obj("id" -> id)
)) as JSON
case HookResult.Refused => BadRequest(jsonError("Game was not created"))
}
private val hookSaveOnlyResponse = Ok(Json.obj("ok" -> true))
def hook(uid: String) = OpenBody { implicit ctx =>
NoBot {
implicit val req = ctx.body
PostRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
NoPlaybanOrCurrent {
env.forms.hook(ctx).bindFromRequest.fold(
jsonFormError,
userConfig => {
val config = userConfig withinLimits ctx.me
if (getBool("pool")) env.processor.saveHookConfig(config) inject hookSaveOnlyResponse
else (ctx.userId ?? Env.relation.api.fetchBlocking) flatMap {
blocking =>
env.processor.hook(config, Uid(uid), HTTPRequest sid req, blocking) map hookResponse
}
}
)
}
}
}
}
def like(uid: String, gameId: String) = Open { implicit ctx =>
NoBot {
PostRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
NoPlaybanOrCurrent {
for {
config <- env.forms.hookConfig
game <- GameRepo.game(gameId)
blocking <- ctx.userId ?? Env.relation.api.fetchBlocking
hookConfig = game.fold(config)(config.updateFrom)
sameOpponents = game.??(_.userIds)
hookResult <- env.processor.hook(hookConfig, Uid(uid), HTTPRequest sid ctx.req, blocking ++ sameOpponents)
} yield hookResponse(hookResult)
}
}
}
}
def filterForm = Open { implicit ctx =>
env.forms.filterFilled map {
case (form, filter) => html.setup.filter(form, filter)
}
}
def filter = OpenBody { implicit ctx =>
implicit val req = ctx.body
env.forms.filter(ctx).bindFromRequest.fold[Fu[Result]](
f => {
controllerLogger.branch("setup").warn(f.errors.toString)
BadRequest(()).fuccess
},
config => JsonOk(env.processor filter config inject config.render)
)
}
def validateFen = Open { implicit ctx =>
get("fen") flatMap ValidFen(getBool("strict")) match {
case None => BadRequest.fuccess
case Some(v) => Ok(html.game.bits.miniBoard(v.fen, v.color)).fuccess
}
}
private def process[A](form: Context => Form[A])(op: A => BodyContext[_] => Fu[Pov]) =
OpenBody { implicit ctx =>
PostRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
implicit val req = ctx.body
form(ctx).bindFromRequest.fold(
err => negotiate(
html = Lobby.renderHome(Results.BadRequest),
api = _ => jsonFormError(err)
),
config => op(config)(ctx) flatMap { pov =>
negotiate(
html = fuccess(redirectPov(pov)),
api = apiVersion => Env.api.roundApi.player(pov, apiVersion) map { data =>
Created(data) as JSON
}
)
}
)
}
}
private[controllers] def redirectPov(pov: Pov)(implicit ctx: Context) = {
val redir = Redirect(routes.Round.watcher(pov.gameId, "white"))
if (ctx.isAuth) redir
else redir withCookies LilaCookie.cookie(
AnonCookie.name,
pov.playerId,
maxAge = AnonCookie.maxAge.some,
httpOnly = false.some
)
}
}