174 lines
5.6 KiB
Scala
174 lines
5.6 KiB
Scala
package lila.irwin
|
|
|
|
import org.joda.time.DateTime
|
|
import reactivemongo.api.bson._
|
|
import reactivemongo.api.ReadPreference
|
|
|
|
import lila.analyse.Analysis
|
|
import lila.analyse.AnalysisRepo
|
|
import lila.common.Bus
|
|
import lila.db.dsl._
|
|
import lila.game.{ Game, GameRepo, Pov, Query }
|
|
import lila.report.{ Mod, ModId, Report, Reporter, Suspect, SuspectId }
|
|
import lila.tournament.{ Tournament, TournamentTop }
|
|
import lila.user.{ Holder, User, UserRepo }
|
|
|
|
final class IrwinApi(
|
|
reportColl: Coll,
|
|
gameRepo: GameRepo,
|
|
userRepo: UserRepo,
|
|
analysisRepo: AnalysisRepo,
|
|
modApi: lila.mod.ModApi,
|
|
reportApi: lila.report.ReportApi,
|
|
notifyApi: lila.notify.NotifyApi,
|
|
thresholds: lila.memo.SettingStore[IrwinThresholds]
|
|
)(implicit ec: scala.concurrent.ExecutionContext) {
|
|
|
|
import BSONHandlers._
|
|
|
|
def dashboard: Fu[IrwinDashboard] =
|
|
reportColl
|
|
.find($empty)
|
|
.sort($sort desc "date")
|
|
.cursor[IrwinReport]()
|
|
.list(20) dmap IrwinDashboard.apply
|
|
|
|
object reports {
|
|
|
|
def insert(report: IrwinReport) =
|
|
reportColl.update.one($id(report._id), report, upsert = true) >>
|
|
markOrReport(report) >>
|
|
notification(report) >>-
|
|
lila.mon.mod.irwin.ownerReport(report.owner).increment().unit
|
|
|
|
def get(user: User): Fu[Option[IrwinReport]] =
|
|
reportColl.find($id(user.id)).one[IrwinReport]
|
|
|
|
def withPovs(user: User): Fu[Option[IrwinReport.WithPovs]] =
|
|
get(user) flatMap {
|
|
_ ?? { report =>
|
|
gameRepo.gamesFromSecondary(report.games.map(_.gameId)) dmap { games =>
|
|
val povs = games.flatMap { g =>
|
|
Pov(g, user) map { g.id -> _ }
|
|
}.toMap
|
|
IrwinReport.WithPovs(report, povs).some
|
|
}
|
|
}
|
|
}
|
|
|
|
private def getSuspect(suspectId: User.ID) =
|
|
userRepo byId suspectId orFail s"suspect $suspectId not found" dmap Suspect.apply
|
|
|
|
private def markOrReport(report: IrwinReport): Funit =
|
|
userRepo.getTitle(report.suspectId.value) flatMap { title =>
|
|
if (report.activation >= thresholds.get().mark && title.isEmpty)
|
|
modApi.autoMark(report.suspectId, ModId.irwin, report.note) >>-
|
|
lila.mon.mod.irwin.mark.increment().unit
|
|
else if (report.activation >= thresholds.get().report) for {
|
|
suspect <- getSuspect(report.suspectId.value)
|
|
irwin <- userRepo.irwin orFail s"Irwin user not found" dmap Mod.apply
|
|
_ <- reportApi.create(
|
|
Report.Candidate(
|
|
reporter = Reporter(irwin.user),
|
|
suspect = suspect,
|
|
reason = lila.report.Reason.Cheat,
|
|
text = s"${report.activation}% over ${report.games.size} games"
|
|
)
|
|
)
|
|
} yield lila.mon.mod.irwin.report.increment().unit
|
|
else funit
|
|
}
|
|
}
|
|
|
|
object requests {
|
|
|
|
import IrwinRequest.Origin
|
|
|
|
def fromMod(suspect: Suspect, mod: Holder) = {
|
|
notification.add(suspect.id, ModId(mod.id))
|
|
insert(suspect, _.Moderator)
|
|
}
|
|
|
|
private[irwin] def insert(suspect: Suspect, origin: Origin.type => Origin): Funit =
|
|
for {
|
|
analyzed <- getAnalyzedGames(suspect, 15)
|
|
more <- getMoreGames(suspect, 20 - analyzed.size)
|
|
all = analyzed.map { case (game, analysis) =>
|
|
game -> analysis.some
|
|
} ::: more.map(_ -> none)
|
|
} yield Bus.publish(
|
|
IrwinRequest(
|
|
suspect = suspect,
|
|
origin = origin(Origin),
|
|
games = all
|
|
),
|
|
"irwin"
|
|
)
|
|
|
|
private[irwin] def fromTournamentLeaders(leaders: Map[Tournament, TournamentTop]): Funit =
|
|
lila.common.Future.applySequentially(leaders.toList) { case (tour, top) =>
|
|
userRepo byIds top.value.zipWithIndex
|
|
.filter(_._2 <= tour.nbPlayers * 2 / 100)
|
|
.map(_._1.userId)
|
|
.take(20) flatMap { users =>
|
|
lila.common.Future.applySequentially(users) { user =>
|
|
insert(Suspect(user), _.Tournament)
|
|
}
|
|
}
|
|
}
|
|
|
|
private[irwin] def fromLeaderboard(leaders: List[User]): Funit =
|
|
lila.common.Future.applySequentially(leaders) { user =>
|
|
insert(Suspect(user), _.Leaderboard)
|
|
}
|
|
|
|
import lila.game.BSONHandlers._
|
|
|
|
private def baseQuery(suspect: Suspect) =
|
|
Query.finished ++
|
|
Query.variantStandard ++
|
|
Query.rated ++
|
|
Query.user(suspect.id.value) ++
|
|
Query.turnsGt(20) ++
|
|
Query.createdSince(DateTime.now minusMonths 6)
|
|
|
|
private def getAnalyzedGames(suspect: Suspect, nb: Int): Fu[List[(Game, Analysis)]] =
|
|
gameRepo.coll
|
|
.find(baseQuery(suspect) ++ Query.analysed(true))
|
|
.sort(Query.sortCreated)
|
|
.cursor[Game](ReadPreference.secondaryPreferred)
|
|
.list(nb)
|
|
.flatMap(analysisRepo.associateToGames)
|
|
|
|
private def getMoreGames(suspect: Suspect, nb: Int): Fu[List[Game]] =
|
|
(nb > 0) ??
|
|
gameRepo.coll
|
|
.find(baseQuery(suspect) ++ Query.analysed(false))
|
|
.sort(Query.sortCreated)
|
|
.cursor[Game](ReadPreference.secondaryPreferred)
|
|
.list(nb)
|
|
}
|
|
|
|
object notification {
|
|
|
|
private var subs = Map.empty[SuspectId, Set[ModId]]
|
|
|
|
def add(suspectId: SuspectId, modId: ModId): Unit =
|
|
subs = subs.updated(suspectId, ~subs.get(suspectId) + modId)
|
|
|
|
private[IrwinApi] def apply(report: IrwinReport): Funit =
|
|
subs.get(report.suspectId) ?? { modIds =>
|
|
subs = subs - report.suspectId
|
|
import lila.notify.{ IrwinDone, Notification }
|
|
modIds
|
|
.map { modId =>
|
|
notifyApi.addNotification(
|
|
Notification.make(Notification.Notifies(modId.value), IrwinDone(report.suspectId.value))
|
|
)
|
|
}
|
|
.sequenceFu
|
|
.void
|
|
}
|
|
}
|
|
}
|