lila/app/controllers/Mod.scala

300 lines
11 KiB
Scala
Raw Normal View History

package controllers
2016-10-19 08:55:32 -06:00
import lila.api.Context
import lila.app._
2017-09-04 11:41:58 -06:00
import lila.chat.Chat
2017-04-14 04:23:23 -06:00
import lila.common.{ IpAddress, EmailAddress }
2017-10-21 14:01:50 -06:00
import lila.report.{ Suspect, Mod => AsMod }
import lila.user.{ UserRepo, User => UserModel }
2014-02-26 17:18:09 -07:00
import views._
import play.api.data._
import play.api.data.Forms._
import play.api.mvc._
import scala.concurrent.duration._
object Mod extends LilaController {
private def modApi = Env.mod.api
private def modLogApi = Env.mod.logApi
2015-01-15 04:15:28 -07:00
private def assessApi = Env.mod.assessApi
2017-09-12 17:26:36 -06:00
def engine(username: String, v: Boolean) = SecureBody(_.MarkEngine) { implicit ctx => me =>
withSuspect(username) { sus =>
for {
inquiry <- Env.report.api.inquiries ofModId me.id
_ <- modApi.setEngine(AsMod(me), sus, v)
res <- Report.onInquiryClose(inquiry, me, sus.some)
} yield res
}
2013-05-10 03:56:34 -06:00
}
2016-10-17 04:01:00 -06:00
def publicChat = Secure(_.ChatTimeout) { implicit ctx => _ =>
Env.mod.publicChat.all map {
2016-10-17 04:01:00 -06:00
case (tournamentsAndChats, simulsAndChats) =>
Ok(html.mod.publicChat(tournamentsAndChats, simulsAndChats))
}
}
def booster(username: String, v: Boolean) = Secure(_.MarkBooster) { implicit ctx => me =>
withSuspect(username) { sus =>
modApi.setBooster(AsMod(me), sus, v) inject redirect(username)
}
2015-02-13 07:14:39 -07:00
}
2017-09-12 17:26:36 -06:00
def troll(username: String, v: Boolean) = SecureBody(_.MarkTroll) { implicit ctx => me =>
withSuspect(username) { prev =>
for {
inquiry <- Env.report.api.inquiries ofModId me.id
suspect <- modApi.setTroll(AsMod(me), prev, v)
res <- Report.onInquiryClose(inquiry, me, suspect.some)
} yield res
2016-10-17 04:01:00 -06:00
}
}
2017-09-12 17:26:36 -06:00
def warn(username: String, subject: String) = SecureBody(_.ModMessage) { implicit ctx => me =>
2017-03-30 10:21:52 -06:00
lila.message.ModPreset.bySubject(subject).fold(notFound) { preset =>
withSuspect(username) { prev =>
for {
inquiry <- Env.report.api.inquiries ofModId me.id
suspect <- modApi.setTroll(AsMod(me), prev, prev.user.troll)
thread <- Env.message.api.sendPreset(me, suspect.user, preset)
_ <- Env.mod.logApi.modMessage(thread.creatorId, thread.invitedId, thread.name)
res <- Report.onInquiryClose(inquiry, me, suspect.some)
} yield res
2017-03-30 10:21:52 -06:00
}
}
}
def ban(username: String, v: Boolean) = Secure(_.IpBan) { implicit ctx => me =>
withSuspect(username) { sus =>
modApi.setBan(AsMod(me), sus, v)
} inject redirect(username)
2013-05-10 03:56:34 -06:00
}
2017-10-31 10:13:37 -06:00
def deletePmsAndChats(username: String) = Secure(_.MarkTroll) { implicit ctx => me =>
withSuspect(username) { sus =>
Env.mod.publicChat.delete(sus) >>
Env.message.api.deleteThreadsBy(sus.user)
} inject redirect(username)
}
2016-10-19 08:55:32 -06:00
def closeAccount(username: String) = Secure(_.CloseAccount) { implicit ctx => me =>
modApi.closeAccount(me.id, username) flatMap {
2017-11-11 20:20:49 -07:00
_ ?? { user =>
Env.current.closeAccount(user.id)
}
2016-10-19 08:55:32 -06:00
} inject redirect(username)
}
2016-10-19 08:55:32 -06:00
def reopenAccount(username: String) = Secure(_.ReopenAccount) { implicit ctx => me =>
modApi.reopenAccount(me.id, username) inject redirect(username)
2014-02-01 06:13:22 -07:00
}
def kickFromRankings(username: String) = Secure(_.RemoveRanking) { implicit ctx => me =>
modApi.kickFromRankings(me.id, username) inject redirect(username)
}
def reportban(username: String, v: Boolean) = Secure(_.ReportBan) { implicit ctx => me =>
withSuspect(username) { sus =>
modApi.setReportban(AsMod(me), sus, v) inject redirect(username)
}
}
2017-08-03 04:43:29 -06:00
def impersonate(username: String) = Auth { implicit ctx => me =>
if (username == "-" && lila.mod.Impersonate.isImpersonated(me)) fuccess {
lila.mod.Impersonate.stop(me)
Redirect(routes.User.show(me.username))
}
else if (isGranted(_.Impersonate)) OptionFuRedirect(UserRepo named username) { user =>
lila.mod.Impersonate.start(me, user)
fuccess(routes.User.show(user.username))
}
else notFound
}
2016-10-17 04:01:00 -06:00
def setTitle(username: String) = SecureBody(_.SetTitle) { implicit ctx => me =>
implicit def req = ctx.body
lila.user.DataForm.title.bindFromRequest.fold(
err => fuccess(redirect(username, mod = true)),
title => modApi.setTitle(me.id, username, title) >>
Env.security.automaticEmail.onTitleSet(username) >>-
2016-10-17 04:01:00 -06:00
Env.user.uncacheLightUser(UserModel normalize username) inject
redirect(username, mod = false)
)
2013-09-11 04:38:16 -06:00
}
2016-10-17 04:01:00 -06:00
def setEmail(username: String) = SecureBody(_.SetEmail) { implicit ctx => me =>
implicit def req = ctx.body
OptionFuResult(UserRepo named username) { user =>
Env.security.forms.modEmail(user).bindFromRequest.fold(
err => BadRequest(err.toString).fuccess,
rawEmail => {
2017-04-14 04:23:23 -06:00
val email = Env.security.emailAddressValidator.validate(EmailAddress(rawEmail)) err s"Invalid email ${rawEmail}"
2016-10-17 04:01:00 -06:00
modApi.setEmail(me.id, user.id, email) inject redirect(user.username, mod = true)
}
2016-06-20 10:44:30 -06:00
)
2016-10-17 04:01:00 -06:00
}
2014-02-26 17:18:09 -07:00
}
2016-10-17 04:01:00 -06:00
def notifySlack(username: String) = Auth { implicit ctx => me =>
OptionFuResult(UserRepo named username) { user =>
Env.slack.api.userMod(user = user, mod = me) inject redirect(user.username)
}
2015-08-12 05:17:16 -06:00
}
2016-10-19 08:55:32 -06:00
def log = Secure(_.SeeReport) { implicit ctx => me =>
modLogApi.recent map { html.mod.log(_) }
2013-05-10 03:56:34 -06:00
}
2014-01-16 01:46:01 -07:00
2017-09-04 11:41:58 -06:00
private def communications(username: String, priv: Boolean) = Secure {
perms => if (priv) perms.ViewPrivateComms else perms.MarkTroll
} { implicit ctx => me =>
2016-10-17 04:01:00 -06:00
OptionFuOk(UserRepo named username) { user =>
lila.game.GameRepo.recentPovsByUserFromSecondary(user, 80) flatMap { povs =>
2017-09-04 11:41:58 -06:00
priv.?? {
Env.chat.api.playerChat optionsByOrderedIds povs.map(_.gameId).map(Chat.Id.apply)
} zip
priv.?? {
lila.message.ThreadRepo.visibleByUser(user.id, 60).map {
_ filter (_ hasPostsWrittenBy user.id) take 30
}
} zip
(Env.shutup.api getPublicLines user.id) zip
2017-11-10 09:28:17 -07:00
(Env.security userSpy user) zip
Env.user.noteApi.forMod(user.id) zip
Env.mod.logApi.userHistory(user.id) zip
Env.report.api.inquiries.ofModId(me.id) map {
case chats ~ threads ~ publicLines ~ spy ~ notes ~ history ~ inquiry =>
if (priv && !inquiry.??(_.isRecentCommOf(Suspect(user))))
2017-11-30 22:22:12 -07:00
Env.slack.api.commlog(mod = me, user = user, inquiry.map(_.oldestAtom.by.value))
val povWithChats = (povs zip chats) collect {
case (p, Some(c)) if c.nonEmpty => p -> c
} take 15
val filteredNotes = notes.filter(_.from != "irwin")
2017-09-04 11:41:58 -06:00
html.mod.communication(user, povWithChats, threads, publicLines, spy, filteredNotes, history, priv)
}
}
2016-10-17 04:01:00 -06:00
}
}
2017-09-04 11:41:58 -06:00
def communicationPublic(username: String) = communications(username, false)
def communicationPrivate(username: String) = communications(username, true)
2016-10-17 04:01:00 -06:00
def ipIntel(ip: String) = Secure(_.IpBan) { ctx => me =>
2017-11-10 09:28:17 -07:00
Env.security.ipIntel.failable(IpAddress(ip)).map { Ok(_) }.recover {
2016-10-17 04:01:00 -06:00
case e: Exception => InternalServerError(e.getMessage)
}
}
2017-05-02 08:40:37 -06:00
protected[controllers] def redirect(username: String, mod: Boolean = true) =
2016-10-19 08:55:32 -06:00
Redirect(routes.User.show(username).url + mod.??("?mod"))
2016-10-19 08:55:32 -06:00
def refreshUserAssess(username: String) = Secure(_.MarkEngine) { implicit ctx => me =>
2017-05-02 08:40:37 -06:00
assessApi.refreshAssessByUsername(username) >>
Env.irwin.api.requests.fromMod(lila.user.User normalize username, me) inject
2017-05-02 08:40:37 -06:00
redirect(username)
}
2016-01-10 21:09:37 -07:00
2017-05-10 17:07:04 -06:00
def spontaneousInquiry(username: String) = Secure(_.SeeReport) { implicit ctx => me =>
OptionFuResult(UserRepo named username) { user =>
Env.report.api.inquiries.spontaneous(AsMod(me), Suspect(user)) inject redirect(user.username, true)
2017-05-10 17:07:04 -06:00
}
}
2016-10-17 04:01:00 -06:00
def gamify = Secure(_.SeeReport) { implicit ctx => me =>
Env.mod.gamify.leaderboards zip
Env.mod.gamify.history(orCompute = true) map {
case (leaderboards, history) => Ok(html.mod.gamify.index(leaderboards, history))
}
2016-01-10 22:18:26 -07:00
}
2016-10-17 04:01:00 -06:00
def gamifyPeriod(periodStr: String) = Secure(_.SeeReport) { implicit ctx => me =>
lila.mod.Gamify.Period(periodStr).fold(notFound) { period =>
Env.mod.gamify.leaderboards map { leaderboards =>
Ok(html.mod.gamify.period(leaderboards, period))
2016-01-10 21:09:37 -07:00
}
2016-10-17 04:01:00 -06:00
}
2016-01-10 21:09:37 -07:00
}
2016-10-17 04:01:00 -06:00
def search = Secure(_.UserSearch) { implicit ctx => me =>
val query = (~get("q")).trim
Env.mod.search(query) map { users =>
html.mod.search(query, users)
}
}
2016-06-10 18:13:57 -06:00
2016-10-17 04:01:00 -06:00
def chatUser(username: String) = Secure(_.ChatTimeout) { implicit ctx => me =>
implicit val lightUser = Env.user.lightUserSync _
2016-10-17 04:01:00 -06:00
JsonOptionOk {
Env.chat.api.userChat userModInfo username map2 lila.chat.JsonView.userModInfo
}
2016-06-10 18:13:57 -06:00
}
2016-06-20 10:44:30 -06:00
2017-01-14 08:42:26 -07:00
def permissions(username: String) = Secure(_.ChangePermission) { implicit ctx => me =>
2016-10-17 04:01:00 -06:00
OptionOk(UserRepo named username) { user =>
2017-01-14 08:42:26 -07:00
html.mod.permissions(user)
2016-10-17 04:01:00 -06:00
}
2016-06-20 10:44:30 -06:00
}
2017-01-14 08:42:26 -07:00
def savePermissions(username: String) = SecureBody(_.ChangePermission) { implicit ctx => me =>
2016-10-17 04:01:00 -06:00
implicit def req = ctx.body
2017-01-14 08:58:24 -07:00
import lila.security.Permission
2016-10-17 04:01:00 -06:00
OptionFuResult(UserRepo named username) { user =>
Form(single(
"permissions" -> list(nonEmptyText.verifying { str =>
2017-01-14 08:58:24 -07:00
Permission.allButSuperAdmin.exists(_.name == str)
2016-10-17 04:01:00 -06:00
})
)).bindFromRequest.fold(
2017-01-14 08:42:26 -07:00
err => BadRequest(html.mod.permissions(user)).fuccess,
2016-10-17 04:01:00 -06:00
permissions =>
modApi.setPermissions(me.id, user.username, Permission(permissions)) >> {
2017-12-27 09:46:50 -07:00
(Permission(permissions) diff Permission(user.roles) contains Permission.Coach) ??
Env.security.automaticEmail.onBecomeCoach(user)
} inject redirect(user.username, true)
2016-10-17 04:01:00 -06:00
)
}
2016-06-20 10:44:30 -06:00
}
def emailConfirm = SecureBody(_.SetEmail) { implicit ctx => me =>
get("q") match {
case None => Ok(html.mod.emailConfirm("", none, none)).fuccess
case Some(rawQuery) =>
val query = rawQuery.trim.split(' ').toList
val email = query.headOption.map(EmailAddress.apply) flatMap Env.security.emailAddressValidator.validate
val username = query lift 1
def tryWith(setEmail: EmailAddress, q: String): Fu[Option[Result]] = Env.mod.search(q) flatMap {
2017-10-31 15:13:59 -06:00
case List(user) => (!user.everLoggedIn).?? {
lila.mon.user.register.modConfirmEmail()
modApi.setEmail(me.id, user.id, setEmail)
} >>
UserRepo.email(user.id) map { email =>
Ok(html.mod.emailConfirm("", user.some, email)).some
}
case _ => fuccess(none)
}
email.?? { em =>
tryWith(em, em.value) orElse {
username ?? { tryWith(em, _) }
}
} getOrElse BadRequest(html.mod.emailConfirm(rawQuery, none, none)).fuccess
}
}
2017-10-28 15:40:52 -06:00
def chatPanic = Secure(_.MarkTroll) { implicit ctx => me =>
Ok(html.mod.chatPanic(Env.chat.panic.get)).fuccess
}
def chatPanicPost = SecureBody(_.MarkTroll) { implicit ctx => me =>
implicit val req = ctx.body
2017-10-28 15:58:24 -06:00
val v = getBool("v")
Env.chat.panic.set(v)
Env.slack.api.chatPanic(me, v)
2017-10-28 15:40:52 -06:00
Redirect(routes.Mod.chatPanic).fuccess
}
private def withSuspect(username: String)(f: Suspect => Fu[Result])(implicit ctx: Context) =
Env.report.api getSuspect username flatMap {
_.fold(notFound)(f)
}
}