implement /api/challenge/{username} endpoint - closes #4771
parent
e5167f7116
commit
419c977d42
|
@ -147,6 +147,43 @@ object Challenge extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def apiCreate(userId: String) = ScopedBody() { implicit req => me =>
|
||||
implicit val lang = lila.i18n.I18nLangPicker(req, me.some)
|
||||
Setup.PostRateLimit(HTTPRequest lastRemoteAddress req) {
|
||||
Env.setup.forms.api.bindFromRequest.fold(
|
||||
jsonFormErrorDefaultLang,
|
||||
config => UserRepo enabledById userId flatMap { destUser =>
|
||||
destUser ?? { Env.challenge.granter(me.some, _, config.perfType) } flatMap {
|
||||
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
|
||||
)
|
||||
(Env.challenge.api create challenge) map {
|
||||
case true =>
|
||||
Ok(env.jsonView.show(challenge, SocketVersion(0), lila.challenge.Direction.Out.some))
|
||||
case false =>
|
||||
BadRequest(jsonError("Challenge not created"))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def rematchOf(gameId: String) = Auth { implicit ctx => me =>
|
||||
OptionFuResult(GameRepo game gameId) { g =>
|
||||
Pov.opponentOfUserId(g, me.id).flatMap(_.userId) ?? UserRepo.byId flatMap {
|
||||
|
|
|
@ -20,7 +20,7 @@ object Setup extends LilaController with TheftPrevention {
|
|||
|
||||
private def env = Env.setup
|
||||
|
||||
private val PostRateLimit = new lila.memo.RateLimit[IpAddress](5, 1 minute,
|
||||
private[controllers] val PostRateLimit = new lila.memo.RateLimit[IpAddress](5, 1 minute,
|
||||
name = "setup post",
|
||||
key = "setup_post")
|
||||
|
||||
|
@ -66,47 +66,46 @@ object Setup extends LilaController with TheftPrevention {
|
|||
err => negotiate(
|
||||
html = Lobby.renderHome(Results.BadRequest),
|
||||
api = _ => jsonFormError(err)
|
||||
), {
|
||||
case config => userId ?? UserRepo.byId 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")))
|
||||
)
|
||||
}
|
||||
}
|
||||
),
|
||||
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")))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -512,6 +512,7 @@ GET /api/account/email controllers.Account.apiEmail
|
|||
GET /api/account/kid controllers.Account.apiKid
|
||||
POST /api/account/kid controllers.Account.apiKidPost
|
||||
GET /api/account/preferences controllers.Pref.apiGet
|
||||
POST /api/challenge/:user controllers.Challenge.apiCreate(user: String)
|
||||
POST /api/challenge/$id<\w{8}>/accept controllers.Challenge.apiAccept(id: String)
|
||||
POST /api/challenge/$id<\w{8}>/decline controllers.Challenge.apiDecline(id: String)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import lila.i18n.I18nKeys
|
|||
import lila.pref.Pref
|
||||
import lila.rating.PerfType
|
||||
import lila.relation.{ Relation, Block, Follow }
|
||||
import lila.user.{ User, UserContext }
|
||||
import lila.user.User
|
||||
|
||||
case class ChallengeDenied(dest: User, reason: ChallengeDenied.Reason)
|
||||
|
||||
|
@ -21,7 +21,7 @@ object ChallengeDenied {
|
|||
case object FriendsOnly extends Reason
|
||||
}
|
||||
|
||||
def translated(d: ChallengeDenied)(implicit ctx: UserContext) = d.reason match {
|
||||
def translated(d: ChallengeDenied)(implicit lang: lila.common.Lang) = d.reason match {
|
||||
case Reason.YouAreAnon => I18nKeys.registerToSendChallenges.txt()
|
||||
case Reason.YouAreBlocked => I18nKeys.youCannotChallengeX.txt(d.dest.titleUsername)
|
||||
case Reason.TheyDontAcceptChallenges => I18nKeys.xDoesNotAcceptChallenges.txt(d.dest.titleUsername)
|
||||
|
|
|
@ -2,7 +2,6 @@ package lila.i18n
|
|||
|
||||
import play.twirl.api.Html
|
||||
|
||||
import lila.user.UserContext
|
||||
import lila.common.Lang
|
||||
|
||||
sealed trait I18nKey {
|
||||
|
@ -20,19 +19,19 @@ sealed trait I18nKey {
|
|||
/* Implicit context convenience functions */
|
||||
|
||||
// literal
|
||||
def apply(args: Any*)(implicit ctx: UserContext): Html = literalHtmlTo(ctx.lang, args)
|
||||
def apply(args: Any*)(implicit lang: Lang): Html = literalHtmlTo(lang, args)
|
||||
|
||||
def plural(count: Count, args: Any*)(implicit ctx: UserContext): Html = pluralHtmlTo(ctx.lang, count, args)
|
||||
def plural(count: Count, args: Any*)(implicit lang: Lang): Html = pluralHtmlTo(lang, count, args)
|
||||
|
||||
// literalTxt
|
||||
def txt(args: Any*)(implicit ctx: UserContext): String = literalTxtTo(ctx.lang, args)
|
||||
def txt(args: Any*)(implicit lang: Lang): String = literalTxtTo(lang, args)
|
||||
|
||||
def pluralTxt(count: Count, args: Any*)(implicit ctx: UserContext): String = pluralTxtTo(ctx.lang, count, args)
|
||||
def pluralTxt(count: Count, args: Any*)(implicit lang: Lang): String = pluralTxtTo(lang, count, args)
|
||||
|
||||
// reuses the count as the single argument
|
||||
// allows `plural(nb)` instead of `plural(nb, nb)`
|
||||
def pluralSame(count: Int)(implicit ctx: UserContext): Html = plural(count, count)
|
||||
def pluralSameTxt(count: Int)(implicit ctx: UserContext): String = pluralTxt(count, count)
|
||||
def pluralSame(count: Int)(implicit lang: Lang): Html = plural(count, count)
|
||||
def pluralSameTxt(count: Int)(implicit lang: Lang): String = pluralTxt(count, count)
|
||||
}
|
||||
|
||||
final class Translated(val key: String, val db: I18nDb.Ref) extends I18nKey {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package lila.setup
|
||||
|
||||
import scala.collection.breakOut
|
||||
|
||||
import chess.Clock
|
||||
import chess.format.{ FEN, Forsyth }
|
||||
import chess.variant.FromPosition
|
||||
import lila.lobby.Color
|
||||
import lila.rating.PerfType
|
||||
import lila.game.PerfPicker
|
||||
|
||||
case class ApiConfig(
|
||||
variant: chess.variant.Variant,
|
||||
clock: Option[Clock.Config],
|
||||
days: Option[Int],
|
||||
rated: Boolean,
|
||||
color: Color,
|
||||
position: Option[FEN] = None
|
||||
) extends {
|
||||
|
||||
val strictFen = false
|
||||
|
||||
def >> = (variant.key.some, clock, days, rated, color.name.some, position.map(_.value)).some
|
||||
|
||||
def perfType: Option[PerfType] = PerfPicker.perfType(chess.Speed(clock), variant, days)
|
||||
|
||||
def validFen = variant != FromPosition || {
|
||||
position ?? { f => ~(Forsyth <<< f.value).map(_.situation playable strictFen) }
|
||||
}
|
||||
|
||||
def mode = chess.Mode(rated)
|
||||
}
|
||||
|
||||
object ApiConfig extends BaseHumanConfig {
|
||||
|
||||
lazy val clockLimitSeconds: Set[Int] = Set(0, 15, 30, 45, 60, 90) ++ (2 to 180).map(60*)(breakOut)
|
||||
|
||||
def <<(v: Option[String], cl: Option[Clock.Config], d: Option[Int], r: Boolean, c: Option[String], pos: Option[String]) =
|
||||
new ApiConfig(
|
||||
variant = chess.variant.Variant.orDefault(~v),
|
||||
clock = cl,
|
||||
days = d,
|
||||
rated = r,
|
||||
color = Color.orDefault(~c),
|
||||
position = pos map FEN
|
||||
)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package lila.setup
|
||||
|
||||
import chess.format.FEN
|
||||
import chess.variant.Variant
|
||||
import lila.lobby.Color
|
||||
import lila.user.UserContext
|
||||
import play.api.data._
|
||||
|
@ -83,7 +84,7 @@ private[setup] final class FormFactory {
|
|||
"days" -> days,
|
||||
"mode" -> mode(ctx.isAuth),
|
||||
"ratingRange" -> optional(ratingRange),
|
||||
"color" -> text.verifying(Color.names contains _)
|
||||
"color" -> color
|
||||
)(HookConfig.<<)(_.>>)
|
||||
.verifying("Invalid clock", _.validClock)
|
||||
.verifying("Can't create rated unlimited in lobby", _.noRatedUnlimited)
|
||||
|
@ -91,6 +92,20 @@ private[setup] final class FormFactory {
|
|||
|
||||
def hookConfig(implicit ctx: UserContext): Fu[HookConfig] = savedConfig map (_.hook)
|
||||
|
||||
lazy val api = Form(
|
||||
mapping(
|
||||
"variant" -> optional(text.verifying(Variant.byKey.contains _)),
|
||||
"clock" -> optional(mapping(
|
||||
"limit" -> number.verifying(ApiConfig.clockLimitSeconds.contains _),
|
||||
"increment" -> increment
|
||||
)(chess.Clock.Config.apply)(chess.Clock.Config.unapply)),
|
||||
"days" -> optional(days),
|
||||
"rated" -> boolean,
|
||||
"color" -> optional(color),
|
||||
"fen" -> fen
|
||||
)(ApiConfig.<<)(_.>>).verifying("invalidFen", _.validFen)
|
||||
)
|
||||
|
||||
def savedConfig(implicit ctx: UserContext): Fu[UserConfig] =
|
||||
ctx.me.fold(AnonConfigRepo config ctx.req)(UserConfigRepo.config)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue