Skip to content

Commit

Permalink
Merge pull request #5245 from ornicar/report-playbans-multi-acc
Browse files Browse the repository at this point in the history
Report users with >= 80 playbans over multiple accounts
  • Loading branch information
ornicar committed Jul 3, 2019
2 parents d80492b + 0990a15 commit d4cac06
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 11 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ lazy val bookmark = module("bookmark", Seq(common, memo, db, hub, user, game)).s
)
)

lazy val report = module("report", Seq(common, db, user, game, security)).settings(
lazy val report = module("report", Seq(common, db, user, game, security, playban)).settings(
libraryDependencies ++= provided(play.api, reactivemongo.driver, reactivemongo.iteratees)
)

Expand Down
10 changes: 10 additions & 0 deletions modules/report/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ final class Env(
isOnline: lila.user.User.ID => Boolean,
noteApi: lila.user.NoteApi,
securityApi: lila.security.SecurityApi,
userSpyApi: lila.security.UserSpyApi,
playbanApi: lila.playban.PlaybanApi,
system: ActorSystem,
hub: lila.hub.Env,
settingStore: lila.memo.SettingStore.Builder,
Expand Down Expand Up @@ -39,6 +41,8 @@ final class Env(
autoAnalysis,
noteApi,
securityApi,
userSpyApi,
playbanApi,
isOnline,
asyncCache,
scoreThreshold = scoreThresholdSetting.get
Expand All @@ -58,6 +62,10 @@ final class Env(
}
}), name = ActorName)

system.lilaBus.subscribeFun('playban) {
case lila.hub.actorApi.playban.Playban(userId, _) => api.maybeAutoPlaybanReport(userId)
}

system.scheduler.schedule(1 minute, 1 minute) { api.inquiries.expire }

lazy val reportColl = db(CollectionReport)
Expand All @@ -71,6 +79,8 @@ object Env {
isOnline = lila.user.Env.current.isOnline,
noteApi = lila.user.Env.current.noteApi,
securityApi = lila.security.Env.current.api,
userSpyApi = lila.security.Env.current.userSpyApi,
playbanApi = lila.playban.Env.current.api,
system = lila.common.PlayApp.system,
hub = lila.hub.Env.current,
settingStore = lila.memo.Env.current.settingStore,
Expand Down
2 changes: 2 additions & 0 deletions modules/report/src/main/Reason.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object Reason {
case object Troll extends Reason
case object Boost extends Reason
case object Other extends Reason
case object Playbans extends Reason

val communication: Set[Reason] = Set(Insult, Troll, Other)

Expand All @@ -37,5 +38,6 @@ object Reason {
def isInsult = reason == Insult
def isPrint = reason == CheatPrint
def isTrollOrInsult = reason == Troll || reason == Insult
def isPlaybans = reason == Playbans
}
}
21 changes: 21 additions & 0 deletions modules/report/src/main/ReportApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ final class ReportApi(
autoAnalysis: AutoAnalysis,
noteApi: NoteApi,
securityApi: lila.security.SecurityApi,
userSpyApi: lila.security.UserSpyApi,
playbanApi: lila.playban.PlaybanApi,
isOnline: User.ID => Boolean,
asyncCache: lila.memo.AsyncCache.Builder,
scoreThreshold: () => Int
Expand Down Expand Up @@ -103,6 +105,25 @@ final class ReportApi(
case _ => funit
}

def maybeAutoPlaybanReport(userId: String): Funit =
userSpyApi.getUserIdsWithSameIpAndPrint(userId) map { ids =>
playbanApi.bans(ids.toList ::: List(userId)) map { bans =>
(bans.values.sum >= 80) ?? {
UserRepo.byId(userId) zip
getLichessReporter zip
findRecent(1, selectRecent(SuspectId(userId), Reason.Playbans)) flatMap {
case Some(abuser) ~ reporter ~ past if past.size < 1 => create(Report.Candidate(
reporter = reporter,
suspect = Suspect(abuser),
reason = Reason.Playbans,
text = s"${bans.values.sum} playbans over ${bans.keys.size} accounts with IP+Print match."
))
case _ => funit
}
}
}
}

def processAndGetBySuspect(suspect: Suspect): Fu[List[Report]] = for {
all <- recent(suspect, 10)
open = all.filter(_.open)
Expand Down
2 changes: 1 addition & 1 deletion modules/report/src/main/ReportScore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private final class ReportScore(
// https://github.com/ornicar/lila/issues/4587
def fixedAutoCommPrintScore(c: Report.Candidate)(score: Double): Double =
if (c.isAutoComm) baseScore
else if (c.isPrint || c.isCoachReview) baseScoreAboveThreshold
else if (c.isPrint || c.isCoachReview || c.isPlaybans) baseScoreAboveThreshold
else score
}

Expand Down
2 changes: 1 addition & 1 deletion modules/report/src/main/Room.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object Room {
case Reason.Cheat => Cheat
case Reason.CheatPrint => Print
case Reason.Troll | Reason.Insult => Coms
case Reason.Boost | Reason.Other => Other
case Reason.Boost | Reason.Playbans | Reason.Other => Other
}

def toReasons(room: Room): Set[Reason] = room match {
Expand Down
33 changes: 25 additions & 8 deletions modules/security/src/main/UserSpy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ case class UserSpy(
def otherUserIds = otherUsers.map(_.user.id)
}

private[security] final class UserSpyApi(firewall: Firewall, geoIP: GeoIP, coll: Coll) {
final class UserSpyApi(firewall: Firewall, geoIP: GeoIP, coll: Coll) {

import UserSpy._

def apply(user: User): Fu[UserSpy] = for {
infos Store.chronoInfoByUser(user.id)
ips = distinctRecent(infos.map(_.datedIp))
prints = distinctRecent(infos.flatMap(_.datedFp))
sharingIp exploreSimilar("ip")(user)(coll)
sharingFingerprint exploreSimilar("fp")(user)(coll)
sharingIp exploreSimilar("ip")(user)
sharingFingerprint exploreSimilar("fp")(user)
} yield UserSpy(
ips = ips map { ip =>
IPData(ip, firewall blocksIp ip.value, geoIP orUnknown ip.value)
Expand All @@ -62,20 +62,20 @@ private[security] final class UserSpyApi(firewall: Firewall, geoIP: GeoIP, coll:
readPreference = ReadPreference.secondaryPreferred
)

private def exploreSimilar(field: String)(user: User)(implicit coll: Coll): Fu[Set[User]] =
nextValues(field)(user) flatMap { nValues =>
private def exploreSimilar(field: String)(user: User): Fu[Set[User]] =
nextValues(field)(user.id) flatMap { nValues =>
nextUsers(field)(nValues, user)
}

private def nextValues(field: String)(user: User)(implicit coll: Coll): Fu[Set[Value]] =
private def nextValues(field: String)(userId: User.ID): Fu[Set[Value]] =
coll.find(
$doc("user" -> user.id),
$doc("user" -> userId),
$doc(field -> true)
).cursor[Bdoc]().gather[List]() map {
_.flatMap(_.getAs[Value](field))(breakOut)
}

private def nextUsers(field: String)(values: Set[Value], user: User)(implicit coll: Coll): Fu[Set[User]] =
private def nextUsers(field: String)(values: Set[Value], user: User): Fu[Set[User]] =
values.nonEmpty ?? {
coll.distinctWithReadPreference[String, Set](
"user",
Expand All @@ -88,6 +88,23 @@ private[security] final class UserSpyApi(firewall: Firewall, geoIP: GeoIP, coll:
userIds.nonEmpty ?? (UserRepo byIds userIds) map (_.toSet)
}
}

def getUserIdsWithSameIpAndPrint(userId: User.ID): Fu[Set[User.ID]] = {
for {
ips <- nextValues("ip")(userId)
fps <- nextValues("fp")(userId)
} yield (ips.nonEmpty && fps.nonEmpty) ?? {
coll.distinctWithReadPreference[String, Set](
"user",
$doc(
"ip" $in ips,
"fp" $in fps,
"user" $ne userId
).some,
ReadPreference.secondaryPreferred
)
}
} flatMap identity
}

object UserSpy {
Expand Down

0 comments on commit d4cac06

Please sign in to comment.