2016-01-26 21:37:23 -07:00
|
|
|
package controllers
|
|
|
|
|
2017-01-15 05:26:08 -07:00
|
|
|
import play.api.mvc.Result
|
2016-01-26 21:37:23 -07:00
|
|
|
|
2017-01-15 05:26:08 -07:00
|
|
|
import lila.api.Context
|
2016-01-26 21:37:23 -07:00
|
|
|
import lila.app._
|
2016-01-30 03:27:41 -07:00
|
|
|
import lila.challenge.{ Challenge => ChallengeModel }
|
2019-12-04 16:39:16 -07:00
|
|
|
import lila.common.HTTPRequest
|
|
|
|
import lila.game.{ Pov, AnonCookie }
|
2018-08-04 19:18:46 -06:00
|
|
|
import lila.socket.Socket.SocketVersion
|
2016-01-30 03:27:41 -07:00
|
|
|
import views.html
|
2016-01-26 21:37:23 -07:00
|
|
|
|
2019-12-04 16:39:16 -07:00
|
|
|
final class Challenge(
|
|
|
|
env: Env,
|
2019-12-05 14:51:18 -07:00
|
|
|
setupC: => Setup
|
2019-12-04 16:39:16 -07:00
|
|
|
) extends LilaController(env) {
|
2016-01-26 21:37:23 -07:00
|
|
|
|
2019-12-04 16:39:16 -07:00
|
|
|
def api = env.challenge.api
|
2016-01-26 21:37:23 -07:00
|
|
|
|
2017-01-16 06:05:02 -07:00
|
|
|
def all = Auth { implicit ctx => me =>
|
2017-03-24 04:58:02 -06:00
|
|
|
XhrOrRedirectHome {
|
2019-12-04 16:39:16 -07:00
|
|
|
api allFor me.id map { all =>
|
|
|
|
Ok(env.challenge.jsonView(all, ctx.lang)) as JSON
|
2017-03-24 04:58:02 -06:00
|
|
|
}
|
2017-01-16 06:05:02 -07:00
|
|
|
}
|
2016-01-28 05:04:55 -07:00
|
|
|
}
|
|
|
|
|
2016-02-03 01:26:22 -07:00
|
|
|
def show(id: String) = Open { implicit ctx =>
|
|
|
|
showId(id)
|
|
|
|
}
|
2016-01-30 19:04:27 -07:00
|
|
|
|
2016-02-03 01:26:22 -07:00
|
|
|
protected[controllers] def showId(id: String)(implicit ctx: Context): Fu[Result] =
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(api byId id)(showChallenge(_))
|
2016-02-03 01:26:22 -07:00
|
|
|
|
2017-06-05 12:50:51 -06:00
|
|
|
protected[controllers] def showChallenge(c: ChallengeModel, error: Option[String] = None)(implicit ctx: Context): Fu[Result] =
|
2019-12-04 16:39:16 -07:00
|
|
|
env.challenge version c.id flatMap { version =>
|
2016-02-07 02:07:54 -07:00
|
|
|
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
|
2019-12-04 16:39:16 -07:00
|
|
|
val json = env.challenge.jsonView.show(c, version, direction)
|
2016-02-07 03:10:49 -07:00
|
|
|
negotiate(
|
2019-04-07 23:29:41 -06:00
|
|
|
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))
|
|
|
|
}
|
2017-06-05 12:50:51 -06:00
|
|
|
}
|
2019-12-04 18:47:46 -07:00
|
|
|
else (c.challengerUserId ?? env.user.repo.named) map { user =>
|
2019-04-07 23:29:41 -06:00
|
|
|
Ok(html.challenge.theirs(c, json, user))
|
|
|
|
},
|
2016-02-07 03:10:49 -07:00
|
|
|
api = _ => Ok(json).fuccess
|
|
|
|
) flatMap withChallengeAnonCookie(mine && c.challengerIsAnon, c, true)
|
2016-02-04 09:10:23 -07:00
|
|
|
}
|
|
|
|
|
2016-01-30 03:27:41 -07:00
|
|
|
private def isMine(challenge: ChallengeModel)(implicit ctx: Context) = challenge.challenger match {
|
2017-02-14 08:34:07 -07:00
|
|
|
case Left(anon) => HTTPRequest sid ctx.req contains anon.secret
|
2016-01-28 05:04:55 -07:00
|
|
|
case Right(user) => ctx.userId contains user.id
|
2016-01-27 08:01:23 -07:00
|
|
|
}
|
2016-01-30 03:27:41 -07:00
|
|
|
|
2016-01-31 06:44:12 -07:00
|
|
|
private def isForMe(challenge: ChallengeModel)(implicit ctx: Context) =
|
|
|
|
challenge.destUserId.fold(true)(ctx.userId.contains)
|
|
|
|
|
2018-04-26 17:58:29 -06:00
|
|
|
def accept(id: String) = Open { implicit ctx =>
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(api byId id) { c =>
|
|
|
|
isForMe(c) ?? api.accept(c, ctx.me).flatMap {
|
2016-02-03 17:29:30 -07:00
|
|
|
case Some(pov) => negotiate(
|
2018-04-07 11:07:26 -06:00
|
|
|
html = Redirect(routes.Round.watcher(pov.gameId, "white")).fuccess,
|
2019-12-04 16:39:16 -07:00
|
|
|
api = apiVersion => env.api.roundApi.player(pov, apiVersion) map { Ok(_) }
|
2016-02-07 03:10:49 -07:00
|
|
|
) flatMap withChallengeAnonCookie(ctx.isAnon, c, false)
|
2016-02-03 17:29:30 -07:00
|
|
|
case None => negotiate(
|
|
|
|
html = Redirect(routes.Round.watcher(c.id, "white")).fuccess,
|
2017-02-14 08:34:07 -07:00
|
|
|
api = _ => notFoundJson("Someone else accepted the challenge")
|
|
|
|
)
|
2016-02-03 01:26:22 -07:00
|
|
|
}
|
2018-04-26 17:58:29 -06:00
|
|
|
}
|
|
|
|
}
|
2018-12-18 07:22:26 -07:00
|
|
|
def apiAccept(id: String) = Scoped(_.Challenge.Write, _.Bot.Play) { _ => me =>
|
2019-12-04 16:39:16 -07:00
|
|
|
api.onlineByIdFor(id, me) flatMap {
|
|
|
|
_ ?? { api.accept(_, me.some) }
|
2018-04-17 14:46:15 -06:00
|
|
|
} flatMap { res =>
|
|
|
|
if (res.isDefined) jsonOkResult.fuccess
|
2019-12-04 16:39:16 -07:00
|
|
|
else env.bot.player.rematchAccept(id, me) flatMap {
|
2018-07-20 03:41:46 -06:00
|
|
|
case true => jsonOkResult.fuccess
|
|
|
|
case _ => notFoundJson()
|
2018-04-16 18:36:37 -06:00
|
|
|
}
|
2016-01-31 06:44:12 -07:00
|
|
|
}
|
2018-04-26 17:58:29 -06:00
|
|
|
}
|
2016-01-31 06:44:12 -07:00
|
|
|
|
2016-02-07 03:10:49 -07:00
|
|
|
private def withChallengeAnonCookie(cond: Boolean, c: ChallengeModel, owner: Boolean)(res: Result)(implicit ctx: Context): Fu[Result] =
|
|
|
|
cond ?? {
|
2019-12-04 16:39:16 -07:00
|
|
|
env.game.gameRepo.game(c.id).map {
|
2016-02-07 03:10:49 -07:00
|
|
|
_ map { game =>
|
2019-12-04 16:39:16 -07:00
|
|
|
env.lilaCookie.cookie(
|
2016-02-07 03:10:49 -07:00
|
|
|
AnonCookie.name,
|
2018-07-20 03:41:46 -06:00
|
|
|
game.player(if (owner) c.finalColor else !c.finalColor).id,
|
2016-02-07 03:10:49 -07:00
|
|
|
maxAge = AnonCookie.maxAge.some,
|
2017-02-14 08:34:07 -07:00
|
|
|
httpOnly = false.some
|
|
|
|
)
|
2016-02-07 03:10:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} map { cookieOption =>
|
|
|
|
cookieOption.fold(res) { res.withCookies(_) }
|
|
|
|
}
|
|
|
|
|
2018-04-26 17:58:29 -06:00
|
|
|
def decline(id: String) = Auth { implicit ctx => me =>
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(api byId id) { c =>
|
|
|
|
if (isForMe(c)) api decline c
|
2017-01-16 06:05:02 -07:00
|
|
|
else notFound
|
2018-04-26 17:58:29 -06:00
|
|
|
}
|
|
|
|
}
|
2018-12-18 07:22:26 -07:00
|
|
|
def apiDecline(id: String) = Scoped(_.Challenge.Write, _.Bot.Play) { _ => me =>
|
2019-12-04 16:39:16 -07:00
|
|
|
api.activeByIdFor(id, me) flatMap {
|
|
|
|
case None => env.bot.player.rematchDecline(id, me) flatMap {
|
2018-07-20 03:41:46 -06:00
|
|
|
case true => jsonOkResult.fuccess
|
|
|
|
case _ => notFoundJson()
|
2018-04-17 15:18:22 -06:00
|
|
|
}
|
2019-12-04 16:39:16 -07:00
|
|
|
case Some(c) => api.decline(c) inject jsonOkResult
|
2017-01-16 06:05:02 -07:00
|
|
|
}
|
2018-04-26 17:58:29 -06:00
|
|
|
}
|
2016-01-31 06:44:12 -07:00
|
|
|
|
2016-01-30 03:27:41 -07:00
|
|
|
def cancel(id: String) = Open { implicit ctx =>
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(api byId id) { c =>
|
|
|
|
if (isMine(c)) api cancel c
|
2016-01-30 03:27:41 -07:00
|
|
|
else notFound
|
|
|
|
}
|
|
|
|
}
|
2016-02-01 19:28:05 -07:00
|
|
|
|
2017-01-16 06:05:02 -07:00
|
|
|
def toFriend(id: String) = AuthBody { implicit ctx => me =>
|
|
|
|
import play.api.data._
|
|
|
|
import play.api.data.Forms._
|
|
|
|
implicit def req = ctx.body
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(api byId id) { c =>
|
2017-01-16 06:05:02 -07:00
|
|
|
if (isMine(c)) Form(single(
|
2018-12-04 03:38:50 -07:00
|
|
|
"username" -> lila.user.DataForm.historicalUsernameField
|
2017-01-16 06:05:02 -07:00
|
|
|
)).bindFromRequest.fold(
|
|
|
|
err => funit,
|
2019-12-04 18:47:46 -07:00
|
|
|
username => env.user.repo named username flatMap {
|
2017-06-05 12:50:51 -06:00
|
|
|
case None => Redirect(routes.Challenge.show(c.id)).fuccess
|
2019-12-04 16:39:16 -07:00
|
|
|
case Some(dest) => env.challenge.granter(ctx.me, dest, c.perfType.some) flatMap {
|
2017-12-29 10:50:32 -07:00
|
|
|
case Some(denied) => showChallenge(c, lila.challenge.ChallengeDenied.translated(denied).some)
|
2019-12-04 16:39:16 -07:00
|
|
|
case None => api.setDestUser(c, dest) inject Redirect(routes.Challenge.show(c.id))
|
2017-06-05 12:50:51 -06:00
|
|
|
}
|
2017-01-16 06:05:02 -07:00
|
|
|
}
|
2017-06-05 12:50:51 -06:00
|
|
|
)
|
2017-01-16 06:05:02 -07:00
|
|
|
else notFound
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 07:22:26 -07:00
|
|
|
def apiCreate(userId: String) = ScopedBody(_.Challenge.Write, _.Bot.Play) { implicit req => me =>
|
2018-12-11 03:06:45 -07:00
|
|
|
implicit val lang = lila.i18n.I18nLangPicker(req, me.some)
|
2019-12-04 16:39:16 -07:00
|
|
|
setupC.PostRateLimit(HTTPRequest lastRemoteAddress req) {
|
|
|
|
env.setup.forms.api.bindFromRequest.fold(
|
2018-12-11 03:06:45 -07:00
|
|
|
jsonFormErrorDefaultLang,
|
2019-12-04 18:47:46 -07:00
|
|
|
config => env.user.repo enabledById userId.toLowerCase flatMap { destUser =>
|
2019-12-04 16:39:16 -07:00
|
|
|
destUser ?? { env.challenge.granter(me.some, _, config.perfType) } flatMap {
|
2018-12-11 03:06:45 -07:00
|
|
|
case Some(denied) =>
|
|
|
|
BadRequest(jsonError(lila.challenge.ChallengeDenied.translated(denied))).fuccess
|
|
|
|
case _ =>
|
|
|
|
import lila.challenge.Challenge._
|
|
|
|
val challenge = lila.challenge.Challenge.make(
|
|
|
|
variant = config.variant,
|
|
|
|
initialFen = config.position,
|
|
|
|
timeControl = config.clock map { c =>
|
|
|
|
TimeControl.Clock(c)
|
|
|
|
} orElse config.days.map {
|
|
|
|
TimeControl.Correspondence.apply
|
|
|
|
} getOrElse TimeControl.Unlimited,
|
|
|
|
mode = config.mode,
|
|
|
|
color = config.color.name,
|
|
|
|
challenger = Right(me),
|
|
|
|
destUser = destUser,
|
|
|
|
rematchOf = none
|
|
|
|
)
|
2019-12-04 16:39:16 -07:00
|
|
|
(env.challenge.api create challenge) map {
|
2018-12-11 03:06:45 -07:00
|
|
|
case true =>
|
2019-12-04 16:39:16 -07:00
|
|
|
JsonOk(env.challenge.jsonView.show(challenge, SocketVersion(0), lila.challenge.Direction.Out.some))
|
2018-12-11 03:06:45 -07:00
|
|
|
case false =>
|
|
|
|
BadRequest(jsonError("Challenge not created"))
|
|
|
|
}
|
2018-12-18 17:58:43 -07:00
|
|
|
} map (_ as JSON)
|
2018-12-11 03:06:45 -07:00
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 06:05:02 -07:00
|
|
|
def rematchOf(gameId: String) = Auth { implicit ctx => me =>
|
2019-12-04 16:39:16 -07:00
|
|
|
OptionFuResult(env.game.gameRepo game gameId) { g =>
|
2019-12-04 18:47:46 -07:00
|
|
|
Pov.opponentOfUserId(g, me.id).flatMap(_.userId) ?? env.user.repo.byId flatMap {
|
2017-01-16 06:05:02 -07:00
|
|
|
_ ?? { opponent =>
|
2019-12-04 16:39:16 -07:00
|
|
|
env.challenge.granter(me.some, opponent, g.perfType) flatMap {
|
2017-06-05 03:44:37 -06:00
|
|
|
case Some(d) => BadRequest(jsonError {
|
2017-12-29 10:50:32 -07:00
|
|
|
lila.challenge.ChallengeDenied translated d
|
2017-06-05 03:44:37 -06:00
|
|
|
}).fuccess
|
2019-12-04 16:39:16 -07:00
|
|
|
case _ => api.sendRematchOf(g, me) map {
|
2018-07-20 03:41:46 -06:00
|
|
|
case true => Ok
|
|
|
|
case _ => BadRequest(jsonError("Sorry, couldn't create the rematch."))
|
2016-02-28 18:54:23 -07:00
|
|
|
}
|
|
|
|
}
|
2016-02-07 02:07:54 -07:00
|
|
|
}
|
2016-02-05 07:52:13 -07:00
|
|
|
}
|
2017-01-16 06:05:02 -07:00
|
|
|
}
|
2016-02-05 07:52:13 -07:00
|
|
|
}
|
2016-01-26 21:37:23 -07:00
|
|
|
}
|