From 14ef3298b4968a53a3edb7d35d9c01294929cab7 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 3 Feb 2018 09:30:41 -0500 Subject: [PATCH] Revert "Revert "redesign Irwin API"" This reverts commit 1df1e559b075500571668c53f4d74cf8eec4e6d8. --- app/controllers/Irwin.scala | 9 --- app/controllers/Mod.scala | 4 +- app/controllers/User.scala | 2 +- app/views/irwin/dashboard.scala.html | 34 +-------- app/views/irwin/irwinReport.scala.html | 8 +- app/views/user/mod.scala.html | 11 +-- conf/routes | 1 - modules/irwin/src/main/BSONHandlers.scala | 19 +---- modules/irwin/src/main/Env.scala | 17 +---- modules/irwin/src/main/IrwinApi.scala | 81 +++++++++------------ modules/irwin/src/main/IrwinDashboard.scala | 5 +- modules/irwin/src/main/IrwinReport.scala | 10 +-- modules/irwin/src/main/IrwinRequest.scala | 32 +------- modules/irwin/src/main/IrwinStream.scala | 18 ++++- modules/mod/src/main/AssessApi.scala | 3 +- modules/mod/src/main/ModApi.scala | 8 +- modules/report/src/main/model.scala | 10 +++ 17 files changed, 92 insertions(+), 180 deletions(-) diff --git a/app/controllers/Irwin.scala b/app/controllers/Irwin.scala index 3d0b30aaf7..8018f30d21 100644 --- a/app/controllers/Irwin.scala +++ b/app/controllers/Irwin.scala @@ -28,15 +28,6 @@ object Irwin extends LilaController { } } - def getRequest = Open { implicit ctx => - ModExternalBot { - Env.irwin.api.requests.getAndStart map { - case None => NotFound - case Some(req) => Ok(req.id) - } - } - } - def assessment(username: String) = Open { implicit ctx => ModExternalBot { OptionFuResult(UserRepo named username) { user => diff --git a/app/controllers/Mod.scala b/app/controllers/Mod.scala index bb17a5c51d..a387df2ea5 100644 --- a/app/controllers/Mod.scala +++ b/app/controllers/Mod.scala @@ -4,7 +4,7 @@ import lila.api.Context import lila.app._ import lila.chat.Chat import lila.common.{ IpAddress, EmailAddress } -import lila.report.{ Suspect, Mod => AsMod } +import lila.report.{ Suspect, Mod => AsMod, SuspectId } import lila.user.{ UserRepo, User => UserModel } import views._ @@ -194,7 +194,7 @@ object Mod extends LilaController { def refreshUserAssess(username: String) = Secure(_.MarkEngine) { implicit ctx => me => assessApi.refreshAssessByUsername(username) >> - Env.irwin.api.requests.fromMod(lila.user.User normalize username, me) inject + Env.irwin.api.requests.fromMod(SuspectId normalize username, me) inject redirect(username) } diff --git a/app/controllers/User.scala b/app/controllers/User.scala index 532d0aa110..9860d1a5cf 100644 --- a/app/controllers/User.scala +++ b/app/controllers/User.scala @@ -248,7 +248,7 @@ object User extends LilaController { Env.plan.api.recentChargesOf(user) zip Env.report.api.byAndAbout(user, 20) zip Env.pref.api.getPref(user) zip - Env.irwin.api.status(user) flatMap { + Env.irwin.api.reports.withPovs(user) flatMap { case emails ~ spy ~ assess ~ history ~ charges ~ reports ~ pref ~ irwin => val familyUserIds = user.id :: spy.otherUserIds.toList Env.playban.api.bans(familyUserIds) zip diff --git a/app/views/irwin/dashboard.scala.html b/app/views/irwin/dashboard.scala.html index 8ee9d452cd..04f88c2ef3 100644 --- a/app/views/irwin/dashboard.scala.html +++ b/app/views/irwin/dashboard.scala.html @@ -28,37 +28,7 @@ moreCss = cssTag("mod-irwin.css")) {
- - - - - - - - - - @dashboard.queue.map { req => - - - - - - } - -
Queued requestCreatedStarted
- @userIdLink(req.id.some, params = "?mod") - - @momentFromNow(req.createdAt)
- @req.notifyUserId.map { modId => - by @userIdSpanMini(modId, withOnline = true) - }.getOrElse { - by @req.origin - } -
- @req.startedAt.map { at => - @momentFromNow(at) - } -
+ We no longer know what irwin is up to.
@@ -73,7 +43,7 @@ moreCss = cssTag("mod-irwin.css")) { @dashboard.recent.map { rep =>
- @userIdLink(rep.id.some, params = "?mod") + @userIdLink(rep.suspectId.value.some, params = "?mod") @momentFromNow(rep.date) diff --git a/app/views/irwin/irwinReport.scala.html b/app/views/irwin/irwinReport.scala.html index 046b4aa107..0e2c791545 100644 --- a/app/views/irwin/irwinReport.scala.html +++ b/app/views/irwin/irwinReport.scala.html @@ -1,4 +1,4 @@ -@(report: lila.irwin.IrwinReport.WithPovs, request: Option[lila.irwin.IrwinRequest])(implicit ctx: Context) +@(report: lila.irwin.IrwinReport.WithPovs)(implicit ctx: Context) @percentClass(percent: Int) = { @percent match { case p if p < 30 => {green} @@ -16,12 +16,6 @@ case _ => {red}

Updated @momentFromNow(report.report.date)

- @request.map { r => -

New update is in progress

-

Requested @momentFromNow(r.createdAt) by: @r.origin

- }.getOrElse { -

Hit "Evaluate" to update

- }
@report.report.activation% diff --git a/app/views/user/mod.scala.html b/app/views/user/mod.scala.html index 7f182073d9..843aa73a83 100644 --- a/app/views/user/mod.scala.html +++ b/app/views/user/mod.scala.html @@ -1,4 +1,4 @@ -@(u: User, emails: User.Emails, spy: lila.security.UserSpy, optionAggregateAssessment: Option[lila.evaluation.PlayerAggregateAssessment.WithGames], bans: Map[String, Int], history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref, irwinStatus: lila.irwin.IrwinStatus, notes: List[lila.user.Note])(implicit ctx: Context) +@(u: User, emails: User.Emails, spy: lila.security.UserSpy, optionAggregateAssessment: Option[lila.evaluation.PlayerAggregateAssessment.WithGames], bans: Map[String, Int], history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref, irwinReport: Option[lila.irwin.IrwinReport.WithPovs], notes: List[lila.user.Note])(implicit ctx: Context) @import lila.evaluation.Display @import lila.pref.Pref @@ -105,13 +105,8 @@ Notable preferences: @if(pref.keyboardMove != Pref.KeyboardMove.NO) { [keyboard moves] } else { none }
- @irwinStatus.report.map { report => - @views.html.irwin.irwinReport(report, irwinStatus.request) - }.getOrElse { - @irwinStatus.request.map { request => - Irwin request: - in progress, requested @momentFromNow(request.createdAt) by: @request.origin - } + @irwinReport.map { report => + @views.html.irwin.irwinReport(report) } @optionAggregateAssessment.map { pag =>
diff --git a/conf/routes b/conf/routes index 79b7f64ef0..13b3edfcaf 100644 --- a/conf/routes +++ b/conf/routes @@ -405,7 +405,6 @@ POST /mod/chat-panic controllers.Mod.chatPanicPost GET /irwin controllers.Irwin.dashboard GET /irwin/stream controllers.Irwin.eventStream POST /irwin/report controllers.Irwin.saveReport -GET /irwin/request controllers.Irwin.getRequest GET /irwin/:username/assessment controllers.Irwin.assessment(username: String) GET /irwin/users-mark-and-current-report controllers.Irwin.usersMarkAndCurrentReport(ids: String) diff --git a/modules/irwin/src/main/BSONHandlers.scala b/modules/irwin/src/main/BSONHandlers.scala index b165ddab02..e0477eb608 100644 --- a/modules/irwin/src/main/BSONHandlers.scala +++ b/modules/irwin/src/main/BSONHandlers.scala @@ -1,8 +1,10 @@ package lila.irwin +import reactivemongo.bson._ + import lila.db.dsl._ import lila.db.BSON -import reactivemongo.bson._ +import lila.report.ReporterId object BSONHandlers { @@ -35,19 +37,6 @@ object BSONHandlers { private implicit val GameReportBSONHandler = Macros.handler[GameReport] private implicit val PvBSONHandler = nullableHandler[Int, BSONInteger] + private implicit val ReporterIdBSONHandler = stringIsoHandler[ReporterId](ReporterId.reporterIdIso) implicit val ReportBSONHandler = Macros.handler[IrwinReport] - - private implicit val RequestOriginBSONHandler: BSONHandler[BSONString, IrwinRequest.Origin] = - new BSONHandler[BSONString, IrwinRequest.Origin] { - import IrwinRequest.Origin, Origin._ - def read(bs: BSONString) = bs.value match { - case "moderator" => Moderator - case "report" => Report - case "tournament" => Tournament - case "leaderboard" => Leaderboard - case _ => sys error s"Invalid origin ${bs.value}" - } - def write(x: Origin) = BSONString(x.key) - } - implicit val RequestBSONHandler = Macros.handler[IrwinRequest] } diff --git a/modules/irwin/src/main/Env.scala b/modules/irwin/src/main/Env.scala index 7971f89624..a377d94560 100644 --- a/modules/irwin/src/main/Env.scala +++ b/modules/irwin/src/main/Env.scala @@ -20,7 +20,6 @@ final class Env( ) { private val reportColl = db(config getString "collection.report") - private val requestColl = db(config getString "collection.request") lazy val irwinModeSetting = settingStore[String]( "irwinMode", @@ -28,31 +27,23 @@ final class Env( text = "Allow Irwin to: [mark|report|none]".some ) - val api = new IrwinApi( + val stream = new IrwinStream(system) + + lazy val api = new IrwinApi( reportColl = reportColl, - requestColl = requestColl, modApi = modApi, reportApi = reportApi, notifyApi = notifyApi, + bus = system.lilaBus, mode = irwinModeSetting.get ) - lazy val stream = new IrwinStream(system) - scheduler.future(5 minutes, "irwin tournament leaders") { tournamentApi.allCurrentLeadersInStandard flatMap api.requests.fromTournamentLeaders } scheduler.future(15 minutes, "irwin leaderboards") { userCache.getTop50Online flatMap api.requests.fromLeaderboard } - - system.lilaBus.subscribe(system.actorOf(Props(new Actor { - import lila.hub.actorApi.report._ - def receive = { - case Created(userId, "cheat" | "cheatprint", reporterId) => api.requests.insert(userId, _.Report, none) - case Processed(userId, "cheat" | "cheatprint") => api.requests.drop(userId) - } - })), 'report) } object Env { diff --git a/modules/irwin/src/main/IrwinApi.scala b/modules/irwin/src/main/IrwinApi.scala index 47247921db..fc139f2d3e 100644 --- a/modules/irwin/src/main/IrwinApi.scala +++ b/modules/irwin/src/main/IrwinApi.scala @@ -5,38 +5,31 @@ import reactivemongo.bson._ import lila.db.dsl._ import lila.game.{ Pov, GameRepo } -import lila.report.{ Report, Mod, Suspect, Reporter } +import lila.report.{ Report, Mod, Suspect, Reporter, SuspectId, ModId } import lila.tournament.{ Tournament, TournamentTop } import lila.user.{ User, UserRepo } final class IrwinApi( reportColl: Coll, - requestColl: Coll, modApi: lila.mod.ModApi, reportApi: lila.report.ReportApi, notifyApi: lila.notify.NotifyApi, + bus: lila.common.Bus, mode: () => String ) { import BSONHandlers._ - def status(user: User): Fu[IrwinStatus] = - reports.withPovs(user) zip requests.get(user.id) map { (IrwinStatus.apply _).tupled } - - def dashboard: Fu[IrwinDashboard] = for { - queue <- requestColl.find($empty).sort($sort asc "priority").list[IrwinRequest](20) - recent <- reportColl.find($empty).sort($sort desc "date").list[IrwinReport](20) - } yield IrwinDashboard(queue, recent) + def dashboard: Fu[IrwinDashboard] = + reportColl.find($empty).sort($sort desc "date").list[IrwinReport](20) map IrwinDashboard.apply object reports { def insert(report: IrwinReport) = (mode() != "none") ?? { for { - _ <- reportColl.update($id(report.id), report, upsert = true) - request <- requests get report.id - _ <- request.??(r => requests.drop(r.id)) - _ <- request.??(notifyRequester) + _ <- reportColl.update($id(report._id), report, upsert = true) _ <- markOrReport(report) + _ <- notification(report) } yield () } @@ -59,10 +52,10 @@ final class IrwinApi( private def markOrReport(report: IrwinReport): Funit = if (report.activation > 90 && mode() == "mark") - modApi.autoMark(report.userId, "irwin") >>- + modApi.autoMark(report.suspectId, ModId.irwin) >>- lila.mon.mod.irwin.mark() else if (report.activation >= 60 && mode() != "none") for { - suspect <- getSuspect(report.userId) + suspect <- getSuspect(report.suspectId.value) irwin <- UserRepo byId "irwin" flatten s"Irwin user not found" map Mod.apply _ <- reportApi.create(Report.Candidate( reporter = Reporter(irwin.user), @@ -78,32 +71,17 @@ final class IrwinApi( import IrwinRequest.Origin - def getAndStart: Fu[Option[IrwinRequest]] = - requestColl - .find($doc("startedAt" $exists false)) - .sort($sort asc "priority") - .uno[IrwinRequest] flatMap { - _ ?? { request => - requestColl.updateField($id(request.id), "startedAt", DateTime.now) inject request.some - } - } + def fromMod(suspectId: SuspectId, mod: User) = { + notification.add(suspectId, ModId(mod.id)) + insert(suspectId, _.Moderator) + } - def get(reportedId: User.ID): Fu[Option[IrwinRequest]] = - requestColl.byId[IrwinRequest](reportedId) - - def fromMod(reportedId: User.ID, mod: User) = insert(reportedId, _.Moderator, mod.id.some) - - private[irwin] def drop(reportedId: User.ID): Funit = requestColl.remove($id(reportedId)).void - - private[irwin] def insert(reportedId: User.ID, origin: Origin.type => Origin, notifyUserId: Option[User.ID]) = { - val request = IrwinRequest.make(reportedId, origin(Origin), notifyUserId) - get(reportedId) flatMap { - case Some(prev) if prev.isInProgress => funit - case Some(prev) if prev.priority isAfter request.priority => - requestColl.update($id(request.id), request).void - case Some(prev) => funit - case None => requestColl.insert(request).void - } + private[irwin] def insert(suspectId: SuspectId, origin: Origin.type => Origin): Funit = fuccess { + bus.publish(IrwinRequest( + suspect = suspectId, + origin = origin(Origin), + date = DateTime.now + ), 'irwin) } private[irwin] def fromTournamentLeaders(leaders: Map[Tournament, TournamentTop]): Funit = @@ -111,20 +89,29 @@ final class IrwinApi( case (tour, top) => val userIds = top.value.zipWithIndex.filter(_._2 <= tour.nbPlayers * 2 / 100).map(_._1.userId) lila.common.Future.applySequentially(userIds) { userId => - insert(userId, _.Tournament, none) + insert(SuspectId(userId), _.Tournament) } } private[irwin] def fromLeaderboard(leaders: List[User]): Funit = lila.common.Future.applySequentially(leaders) { user => - insert(user.id, _.Leaderboard, none) + insert(SuspectId(user.id), _.Leaderboard) } } - private def notifyRequester(request: IrwinRequest): Funit = request.notifyUserId ?? { userId => - import lila.notify.{ Notification, IrwinDone } - notifyApi.addNotification( - Notification.make(Notification.Notifies(userId), IrwinDone(request.id)) - ) + object notification { + + private var subs = Map.empty[SuspectId, ModId] + + def add(suspectId: SuspectId, modId: ModId): Unit = subs += (suspectId -> modId) + + private[IrwinApi] def apply(report: IrwinReport): Funit = + subs.get(report.suspectId) ?? { modId => + subs = subs - report.suspectId + import lila.notify.{ Notification, IrwinDone } + notifyApi.addNotification( + Notification.make(Notification.Notifies(modId.value), IrwinDone(report.suspectId.value)) + ) + } } } diff --git a/modules/irwin/src/main/IrwinDashboard.scala b/modules/irwin/src/main/IrwinDashboard.scala index fda95c2803..430aef808b 100644 --- a/modules/irwin/src/main/IrwinDashboard.scala +++ b/modules/irwin/src/main/IrwinDashboard.scala @@ -2,10 +2,7 @@ package lila.irwin import org.joda.time.DateTime -case class IrwinDashboard( - queue: List[IrwinRequest], - recent: List[IrwinReport] -) { +case class IrwinDashboard(recent: List[IrwinReport]) { def lastSeenAt = recent.headOption.map(_.date) diff --git a/modules/irwin/src/main/IrwinReport.scala b/modules/irwin/src/main/IrwinReport.scala index e51852e61b..53d801ac40 100644 --- a/modules/irwin/src/main/IrwinReport.scala +++ b/modules/irwin/src/main/IrwinReport.scala @@ -1,9 +1,10 @@ package lila.irwin -import lila.game.{ Game, Pov } - import org.joda.time.DateTime +import lila.game.{ Game, Pov } +import lila.report.{ SuspectId, ReporterId } + case class IrwinReport( _id: String, // user id activation: Int, // 0 = clean, 100 = cheater @@ -11,12 +12,9 @@ case class IrwinReport( date: DateTime ) { - def id = _id - def userId = _id + def suspectId = SuspectId(_id) } -case class IrwinStatus(report: Option[IrwinReport.WithPovs], request: Option[IrwinRequest]) - object IrwinReport { case class GameReport( diff --git a/modules/irwin/src/main/IrwinRequest.scala b/modules/irwin/src/main/IrwinRequest.scala index 86d43f04c5..c0e2bb40e3 100644 --- a/modules/irwin/src/main/IrwinRequest.scala +++ b/modules/irwin/src/main/IrwinRequest.scala @@ -2,21 +2,14 @@ package lila.irwin import org.joda.time.DateTime +import lila.report.{ SuspectId, ReporterId } import lila.user.User case class IrwinRequest( - _id: User.ID, + suspect: SuspectId, origin: IrwinRequest.Origin, - priority: DateTime, // older = more prioritary; affected by origin - createdAt: DateTime, - startedAt: Option[DateTime], - notifyUserId: Option[User.ID] -) { - - def id = _id - - def isInProgress = startedAt.isDefined -} + date: DateTime +) object IrwinRequest { @@ -26,24 +19,7 @@ object IrwinRequest { object Origin { case object Moderator extends Origin - case object Report extends Origin case object Tournament extends Origin case object Leaderboard extends Origin } - - def make(userId: User.ID, origin: Origin, notifyUserId: Option[User.ID]) = IrwinRequest( - _id = userId, - origin = origin, - priority = DateTime.now minusHours originPriorityDays(origin), - createdAt = DateTime.now, - startedAt = none, - notifyUserId = notifyUserId - ) - - private def originPriorityDays(origin: Origin) = origin match { - case Origin.Moderator => 1000 - case Origin.Report => 20 - case Origin.Tournament => -1000 - case Origin.Leaderboard => -1000 - } } diff --git a/modules/irwin/src/main/IrwinStream.scala b/modules/irwin/src/main/IrwinStream.scala index a8362b631d..e06ee96db4 100644 --- a/modules/irwin/src/main/IrwinStream.scala +++ b/modules/irwin/src/main/IrwinStream.scala @@ -17,11 +17,25 @@ final class IrwinStream(system: ActorSystem) { onStart = channel => { val actor = system.actorOf(Props(new Actor { def receive = { + case request: IrwinRequest => + channel push Json.obj( + "t" -> "request", + "origin" -> request.origin.key, + "user" -> request.suspect.value + ) case lila.hub.actorApi.report.Created(userId, "cheat" | "cheatprint", _) => channel push Json.obj( - "t" -> "report", + "t" -> "reportCreated", "user" -> userId ) + case lila.hub.actorApi.report.Processed(userId, "cheat" | "cheatprint") => + lila.user.UserRepo.isEngine(userId) foreach { marked => + channel push Json.obj( + "t" -> "reportProcessed", + "user" -> userId, + "marked" -> marked + ) + } case lila.hub.actorApi.mod.MarkCheater(userId, value) => channel push Json.obj( "t" -> "mark", @@ -30,7 +44,7 @@ final class IrwinStream(system: ActorSystem) { ) } })) - system.lilaBus.subscribe(actor, 'report, 'adjustCheater) + system.lilaBus.subscribe(actor, 'report, 'adjustCheater, 'irwin) }, onComplete = { stream.foreach { actor => diff --git a/modules/mod/src/main/AssessApi.scala b/modules/mod/src/main/AssessApi.scala index 56d02ce0b0..a143a3d24a 100644 --- a/modules/mod/src/main/AssessApi.scala +++ b/modules/mod/src/main/AssessApi.scala @@ -8,6 +8,7 @@ import lila.evaluation.Statistics import lila.evaluation.{ AccountAction, Analysed, PlayerAssessment, PlayerAggregateAssessment, PlayerFlags, PlayerAssessments, Assessible } import lila.game.{ Game, Player, GameRepo, Source, Pov } import lila.user.{ User, UserRepo } +import lila.report.{ SuspectId, ModId } import reactivemongo.api.ReadPreference import reactivemongo.bson._ @@ -112,7 +113,7 @@ final class AssessApi( case Some(playerAggregateAssessment) => playerAggregateAssessment.action match { case AccountAction.Engine | AccountAction.EngineAndBan => UserRepo.getTitle(userId).flatMap { - case None => modApi.autoMark(userId, "lichess") + case None => modApi.autoMark(SuspectId(userId), ModId.lichess) case Some(title) => fuccess { val reason = s"Would mark as engine, but has a $title title" reporter ! lila.hub.actorApi.report.Cheater(userId, playerAggregateAssessment.reportText(reason, 3)) diff --git a/modules/mod/src/main/ModApi.scala b/modules/mod/src/main/ModApi.scala index 5ae8617ee4..b3dbcd2bcb 100644 --- a/modules/mod/src/main/ModApi.scala +++ b/modules/mod/src/main/ModApi.scala @@ -1,7 +1,7 @@ package lila.mod import lila.common.{ IpAddress, EmailAddress } -import lila.report.{ Mod, Suspect, Room } +import lila.report.{ Mod, ModId, Suspect, SuspectId, Room } import lila.security.Permission import lila.security.{ Firewall, UserSpy, Store => SecurityStore } import lila.user.{ User, UserRepo, LightUserApi } @@ -33,10 +33,10 @@ final class ModApi( } } - def autoMark(username: String, modId: User.ID): Funit = for { - sus <- reportApi.getSuspect(username) flatten s"No such suspect $username" + def autoMark(suspectId: SuspectId, modId: ModId): Funit = for { + sus <- reportApi.getSuspect(suspectId.value) flatten s"No such suspect $suspectId" unengined <- logApi.wasUnengined(sus) - _ <- if (unengined) funit else reportApi.getMod(modId) flatMap { + _ <- if (unengined) funit else reportApi.getMod(modId.value) flatMap { _ ?? { mod => lila.mon.cheat.autoMark.count() setEngine(mod, sus, true) diff --git a/modules/report/src/main/model.scala b/modules/report/src/main/model.scala index 22f249d352..8ee66ebd6e 100644 --- a/modules/report/src/main/model.scala +++ b/modules/report/src/main/model.scala @@ -4,10 +4,20 @@ import lila.user.User case class Mod(user: User) extends AnyVal +case class ModId(value: User.ID) extends AnyVal +object ModId { + def lichess = ModId("lichess") + def irwin = ModId("irwin") + def normalize(username: String) = ModId(User normalize username) +} + case class Suspect(user: User) extends AnyVal { def set(f: User => User) = copy(user = f(user)) } case class SuspectId(value: User.ID) extends AnyVal +object SuspectId { + def normalize(username: String) = SuspectId(User normalize username) +} case class Victim(user: User) extends AnyVal