2013-05-06 08:51:19 -06:00
|
|
|
package controllers
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
import lila.api.Context
|
2013-05-06 08:51:19 -06:00
|
|
|
import lila.app._
|
2016-07-18 14:00:57 -06:00
|
|
|
import lila.user.{ UserRepo, User => UserModel }
|
2014-02-26 17:18:09 -07:00
|
|
|
import views._
|
2013-05-06 08:51:19 -06:00
|
|
|
|
2015-01-23 01:13:56 -07:00
|
|
|
import org.joda.time.DateTime
|
2013-05-06 08:51:19 -06:00
|
|
|
import play.api.mvc._
|
|
|
|
import play.api.mvc.Results._
|
2016-12-19 12:49:47 -07:00
|
|
|
import play.api.libs.json._
|
2013-05-06 08:51:19 -06:00
|
|
|
|
|
|
|
object Mod extends LilaController {
|
|
|
|
|
2013-05-06 10:41:02 -06:00
|
|
|
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
|
2013-05-06 08:51:19 -06:00
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def engine(username: String) = Secure(_.MarkEngine) { _ => me =>
|
|
|
|
modApi.toggleEngine(me.id, username) inject redirect(username)
|
2013-05-10 03:56:34 -06:00
|
|
|
}
|
|
|
|
|
2016-10-17 04:01:00 -06:00
|
|
|
def publicChat = Secure(_.ChatTimeout) { implicit ctx => _ =>
|
|
|
|
val tourChats = Env.mod.publicChat.tournamentChats
|
|
|
|
val simulChats = Env.mod.publicChat.simulChats
|
2016-09-04 09:40:51 -06:00
|
|
|
|
2016-10-17 04:01:00 -06:00
|
|
|
tourChats zip simulChats map {
|
|
|
|
case (tournamentsAndChats, simulsAndChats) =>
|
|
|
|
Ok(html.mod.publicChat(tournamentsAndChats, simulsAndChats))
|
|
|
|
}
|
2016-09-04 07:34:35 -06:00
|
|
|
}
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def booster(username: String) = Secure(_.MarkBooster) { _ => me =>
|
|
|
|
modApi.toggleBooster(me.id, username) inject redirect(username)
|
2015-02-13 07:14:39 -07:00
|
|
|
}
|
|
|
|
|
2016-10-17 04:01:00 -06:00
|
|
|
def troll(username: String) = Secure(_.MarkTroll) { implicit ctx => me =>
|
|
|
|
modApi.troll(me.id, username, getBool("set")) inject {
|
|
|
|
get("then") match {
|
|
|
|
case Some("reports") => Redirect(routes.Report.list)
|
|
|
|
case _ => redirect(username)
|
2015-07-01 03:15:54 -06:00
|
|
|
}
|
2016-10-17 04:01:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def ban(username: String) = Secure(_.IpBan) { implicit ctx => me =>
|
|
|
|
modApi.ban(me.id, username) inject redirect(username)
|
2013-05-10 03:56:34 -06:00
|
|
|
}
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def ipban(ip: String) = Secure(_.IpBan) { implicit ctx => me =>
|
|
|
|
modApi.ipban(me.id, ip)
|
2013-05-10 03:56:34 -06:00
|
|
|
}
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def closeAccount(username: String) = Secure(_.CloseAccount) { implicit ctx => me =>
|
|
|
|
modApi.closeAccount(me.id, username) flatMap {
|
|
|
|
_ ?? Account.doClose
|
|
|
|
} inject redirect(username)
|
2013-05-06 10:41:02 -06:00
|
|
|
}
|
2013-05-06 08:51:19 -06:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-12-19 12:49:47 -07:00
|
|
|
private case class Irwin(result: Boolean, reason: String)
|
|
|
|
private implicit val IrwinReads = Json.reads[Irwin]
|
|
|
|
|
|
|
|
def irwin(username: String) = OpenBody(parse.json) { implicit ctx =>
|
|
|
|
Mod.ModExternalBot {
|
|
|
|
OptionFuResult(UserRepo named username) { user =>
|
|
|
|
UserRepo.irwin.flatten("Missing irwin user") flatMap { irwin =>
|
|
|
|
ctx.body.body.validate[Irwin].fold(
|
|
|
|
err => fuccess(BadRequest(err.toString)),
|
|
|
|
data => {
|
|
|
|
val text =
|
|
|
|
if (data.result) s"Irwin would mark as engine: ${data.reason}"
|
|
|
|
else s"Irwin is indecise: ${data.reason}"
|
2016-12-20 05:12:24 -07:00
|
|
|
(if (data.result) modApi.setEngine(irwin.id, username, true)
|
2016-12-19 12:49:47 -07:00
|
|
|
else funit) >>
|
|
|
|
Env.user.noteApi.write(user, text, irwin, true) inject Ok
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.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 => {
|
|
|
|
val email = Env.security.emailAddress.validate(rawEmail) err s"Invalid email ${rawEmail}"
|
|
|
|
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
|
|
|
private[controllers] def ModExternalBot(f: => Fu[Result])(implicit ctx: Context) =
|
2016-10-17 04:01:00 -06:00
|
|
|
if (!get("api_key").contains(Env.mod.ApiKey)) fuccess(NotFound)
|
2016-10-19 08:55:32 -06:00
|
|
|
else f
|
|
|
|
|
|
|
|
def assessment(username: String) = Open { implicit ctx =>
|
|
|
|
ModExternalBot {
|
|
|
|
OptionFuResult(UserRepo named username) { user =>
|
2016-10-30 05:29:26 -06:00
|
|
|
Env.mod.jsonView(user) flatMap {
|
2016-11-08 09:38:46 -07:00
|
|
|
case None => NotFound.fuccess
|
2016-10-30 05:29:26 -06:00
|
|
|
case Some(data) => Env.mod.userHistory(user) map { history =>
|
|
|
|
Ok(data + ("history" -> history))
|
|
|
|
}
|
2016-10-19 08:55:32 -06:00
|
|
|
} map (_ as JSON)
|
|
|
|
}
|
2016-10-17 04:01:00 -06:00
|
|
|
}
|
2016-01-04 21:18:39 -07: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
|
|
|
|
2016-10-17 04:01:00 -06:00
|
|
|
def communication(username: String) = Secure(_.MarkTroll) { implicit ctx => me =>
|
|
|
|
OptionFuOk(UserRepo named username) { user =>
|
|
|
|
for {
|
|
|
|
povs <- lila.game.GameRepo.recentPovsByUser(user, 100)
|
|
|
|
chats <- povs.map(p => Env.chat.api.playerChat findNonEmpty p.gameId).sequence
|
|
|
|
povWithChats = (povs zip chats) collect {
|
|
|
|
case (p, Some(c)) => p -> c
|
|
|
|
} take 9
|
|
|
|
threads <- {
|
|
|
|
lila.message.ThreadRepo.visibleByUser(user.id, 50) map {
|
|
|
|
_ filter (_ hasPostsWrittenBy user.id) take 9
|
2014-05-22 13:01:54 -06:00
|
|
|
}
|
2016-10-17 04:01:00 -06:00
|
|
|
}
|
|
|
|
publicLines <- Env.shutup.api getPublicLines user.id
|
|
|
|
spy <- Env.security userSpy user.id
|
|
|
|
} yield html.mod.communication(user, povWithChats, threads, publicLines, spy)
|
|
|
|
}
|
2014-05-22 13:01:54 -06:00
|
|
|
}
|
|
|
|
|
2016-10-02 18:46:33 -06:00
|
|
|
private[controllers] val ipIntelCache =
|
2016-11-27 22:42:37 -07:00
|
|
|
lila.memo.AsyncCache[String, Int](
|
|
|
|
name = "ipIntel",
|
|
|
|
f = ip => {
|
2015-08-20 16:39:32 -06:00
|
|
|
import play.api.libs.ws.WS
|
|
|
|
import play.api.Play.current
|
2016-09-06 07:53:36 -06:00
|
|
|
val email = Env.api.Net.Email
|
2015-10-13 01:28:54 -06:00
|
|
|
val url = s"http://check.getipintel.net/check.php?ip=$ip&contact=$email"
|
2016-03-11 21:29:21 -07:00
|
|
|
WS.url(url).get().map(_.body).mon(_.security.proxy.request.time).flatMap { str =>
|
2016-06-20 03:58:59 -06:00
|
|
|
parseFloatOption(str).fold[Fu[Int]](fufail(s"Invalid ratio ${str.take(140)}")) { ratio =>
|
2016-11-08 09:38:46 -07:00
|
|
|
if (ratio < 0) fufail(s"Error code $ratio")
|
|
|
|
else fuccess((ratio * 100).toInt)
|
2016-03-11 21:29:21 -07:00
|
|
|
}
|
|
|
|
}.addEffects(
|
|
|
|
fail = _ => lila.mon.security.proxy.request.failure(),
|
|
|
|
succ = percent => {
|
2016-10-17 04:01:00 -06:00
|
|
|
lila.mon.security.proxy.percent(percent max 0)
|
|
|
|
lila.mon.security.proxy.request.success()
|
|
|
|
})
|
2016-03-11 21:29:21 -07:00
|
|
|
}, maxCapacity = 1024)
|
2015-08-20 16:45:48 -06:00
|
|
|
|
2016-10-17 04:01:00 -06:00
|
|
|
def ipIntel(ip: String) = Secure(_.IpBan) { ctx => me =>
|
|
|
|
ipIntelCache(ip).map { Ok(_) }.recover {
|
|
|
|
case e: Exception => InternalServerError(e.getMessage)
|
|
|
|
}
|
2015-08-20 16:39:32 -06:00
|
|
|
}
|
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def redirect(username: String, mod: Boolean = true) =
|
|
|
|
Redirect(routes.User.show(username).url + mod.??("?mod"))
|
2015-01-13 03:44:12 -07:00
|
|
|
|
2016-10-19 08:55:32 -06:00
|
|
|
def refreshUserAssess(username: String) = Secure(_.MarkEngine) { implicit ctx => me =>
|
|
|
|
assessApi.refreshAssessByUsername(username) inject redirect(username)
|
2015-02-22 23:12:25 -07:00
|
|
|
}
|
2016-01-10 21:09:37 -07: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-01-11 20:16:20 -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-01-11 20:16:20 -07:00
|
|
|
}
|
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.lightUser _
|
|
|
|
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
|
|
|
|
OptionFuResult(UserRepo named username) { user =>
|
|
|
|
import play.api.data._
|
|
|
|
import play.api.data.Forms._
|
|
|
|
Form(single(
|
|
|
|
"permissions" -> list(nonEmptyText.verifying { str =>
|
|
|
|
lila.security.Permission.allButSuperAdmin.exists(_.name == str)
|
|
|
|
})
|
|
|
|
)).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 =>
|
|
|
|
UserRepo.setRoles(user.id, permissions.map(_.toUpperCase)) inject
|
|
|
|
Redirect(routes.User.show(user.username) + "?mod")
|
|
|
|
)
|
|
|
|
}
|
2016-06-20 10:44:30 -06:00
|
|
|
}
|
2013-05-06 08:51:19 -06:00
|
|
|
}
|