forked from lichess-org/lila
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathChallenge.scala
214 lines (196 loc) · 7.5 KB
/
Challenge.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package controllers
import play.api.libs.json._
import play.api.mvc.Result
import lila.api.Context
import lila.app._
import lila.challenge.{ Challenge => ChallengeModel }
import lila.common.{ HTTPRequest, LilaCookie }
import lila.game.{ Pov, GameRepo, AnonCookie }
import lila.socket.Socket.SocketVersion
import lila.user.UserRepo
import views.html
object Challenge extends LilaController {
private def env = Env.challenge
def all = Auth { implicit ctx => me =>
XhrOrRedirectHome {
env.api allFor me.id map { all =>
Ok(env.jsonView(all, ctx.lang)) as JSON
}
}
}
def show(id: String) = Open { implicit ctx =>
showId(id)
}
protected[controllers] def showId(id: String)(implicit ctx: Context): Fu[Result] =
OptionFuResult(env.api byId id)(showChallenge(_))
protected[controllers] def showChallenge(c: ChallengeModel, error: Option[String] = None)(implicit ctx: Context): Fu[Result] =
env 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.jsonView.show(c, version, direction)
negotiate(
html = fuccess {
if (mine) error match {
case Some(e) => BadRequest(html.challenge.mine.apply(c, json, e.some))
case None => Ok(html.challenge.mine.apply(c, json, none))
}
else Ok(html.challenge.theirs.apply(c, json))
},
api = _ => Ok(json).fuccess
) flatMap withChallengeAnonCookie(mine && c.challengerIsAnon, c, true)
}
private def isMine(challenge: ChallengeModel)(implicit ctx: Context) = challenge.challenger match {
case Left(anon) => HTTPRequest sid ctx.req contains anon.secret
case Right(user) => ctx.userId contains user.id
}
private def isForMe(challenge: ChallengeModel)(implicit ctx: Context) =
challenge.destUserId.fold(true)(ctx.userId.contains)
def accept(id: String) = Open { implicit ctx =>
OptionFuResult(env.api byId id) { c =>
isForMe(c) ?? env.api.accept(c, ctx.me).flatMap {
case Some(pov) => negotiate(
html = Redirect(routes.Round.watcher(pov.gameId, "white")).fuccess,
api = apiVersion => Env.api.roundApi.player(pov, apiVersion) map { Ok(_) }
) flatMap withChallengeAnonCookie(ctx.isAnon, c, false)
case None => negotiate(
html = Redirect(routes.Round.watcher(c.id, "white")).fuccess,
api = _ => notFoundJson("Someone else accepted the challenge")
)
}
}
}
def apiAccept(id: String) = Scoped(_.Challenge.Write, _.Bot.Play) { _ => me =>
env.api.onlineByIdFor(id, me) flatMap {
_ ?? { env.api.accept(_, me.some) }
} 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 ?? {
GameRepo.game(c.id).map {
_ map { game =>
implicit val req = ctx.req
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 => me =>
OptionFuResult(env.api byId id) { c =>
if (isForMe(c)) env.api decline c
else notFound
}
}
def apiDecline(id: String) = Scoped(_.Challenge.Write, _.Bot.Play) { _ => me =>
env.api.activeByIdFor(id, me) flatMap {
case None => Env.bot.player.rematchDecline(id, me) flatMap {
case true => jsonOkResult.fuccess
case _ => notFoundJson()
}
case Some(c) => env.api.decline(c) inject jsonOkResult
}
}
def cancel(id: String) = Open { implicit ctx =>
OptionFuResult(env.api byId id) { c =>
if (isMine(c)) env.api cancel c
else notFound
}
}
def toFriend(id: String) = AuthBody { implicit ctx => me =>
import play.api.data._
import play.api.data.Forms._
implicit def req = ctx.body
OptionFuResult(env.api byId id) { c =>
if (isMine(c)) Form(single(
"username" -> lila.user.DataForm.historicalUsernameField
)).bindFromRequest.fold(
err => funit,
username => UserRepo named username flatMap {
case None => Redirect(routes.Challenge.show(c.id)).fuccess
case Some(dest) => Env.challenge.granter(ctx.me, dest, c.perfType.some) flatMap {
case Some(denied) => showChallenge(c, lila.challenge.ChallengeDenied.translated(denied).some)
case None => env.api.setDestUser(c, dest) inject Redirect(routes.Challenge.show(c.id))
}
}
)
else notFound
}
}
def apiCreate(userId: String) = ScopedBody(_.Challenge.Write, _.Bot.Play) { 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 =>
JsonOk(env.jsonView.show(challenge, SocketVersion(0), lila.challenge.Direction.Out.some))
case false =>
BadRequest(jsonError("Challenge not created"))
}
} map (_ as JSON)
}
)
}
}
def rematchOf(gameId: String) = Auth { implicit ctx => me =>
OptionFuResult(GameRepo game gameId) { g =>
Pov.opponentOfUserId(g, me.id).flatMap(_.userId) ?? UserRepo.byId flatMap {
_ ?? { opponent =>
env.granter(me.some, opponent, g.perfType) flatMap {
case Some(d) => BadRequest(jsonError {
lila.challenge.ChallengeDenied translated d
}).fuccess
case _ => env.api.sendRematchOf(g, me) map {
case true => Ok
case _ => BadRequest(jsonError("Sorry, couldn't create the rematch."))
}
}
}
}
}
}
def websocket(id: String, apiVersion: Int) = SocketOption[JsValue] { implicit ctx =>
env.api byId id flatMap {
_ ?? { c =>
getSocketUid("sri") ?? { uid =>
env.socketHandler.join(id, uid, ctx.userId, isMine(c), getSocketVersion, apiVersion) map some
}
}
}
}
}