Skip to content

Commit

Permalink
Merge pull request #5254 from ornicar/distinguish-abusers-and-bad-conn
Browse files Browse the repository at this point in the history
Keep track of what positions people tend to sit or disconnect in
  • Loading branch information
ornicar committed Jul 3, 2019
2 parents 07c36c4 + 748d794 commit cf76cdc
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 14 deletions.
7 changes: 4 additions & 3 deletions app/controllers/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,11 @@ object User extends LilaController {
Env.mod.logApi.userHistory(user.id).logTimeIfGt(s"$username logApi.userHistory", 2 seconds) zip
Env.plan.api.recentChargesOf(user).logTimeIfGt(s"$username plan.recentChargesOf", 2 seconds) zip
Env.report.api.byAndAbout(user, 20).logTimeIfGt(s"$username report.byAndAbout", 2 seconds) zip
Env.pref.api.getPref(user).logTimeIfGt(s"$username pref.getPref", 2 seconds) flatMap {
case history ~ charges ~ reports ~ pref =>
Env.pref.api.getPref(user).logTimeIfGt(s"$username pref.getPref", 2 seconds) zip
Env.playban.api.getSitAndDcCounter(user) flatMap {
case history ~ charges ~ reports ~ pref ~ sitAndDcCounter =>
Env.user.lightUserApi.preloadMany(reports.userIds).logTimeIfGt(s"$username lightUserApi.preloadMany", 2 seconds) inject
html.user.mod.parts(user, history, charges, reports, pref).some
html.user.mod.parts(user, history, charges, reports, pref, sitAndDcCounter).some
}
val actions = UserRepo.isErased(user) map { erased =>
html.user.mod.actions(user, emails, erased).some
Expand Down
10 changes: 9 additions & 1 deletion app/views/user/mod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ object mod {
)
)

def parts(u: User, history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref)(implicit ctx: Context) = frag(
def parts(u: User, history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref, sitAndDcCounter: Int)(implicit ctx: Context) = frag(
roles(u),
prefs(u, pref),
plan(u, charges),
sitDcCounter(sitAndDcCounter),
modLog(u, history),
reportLog(u, reports)
)
Expand All @@ -151,6 +152,13 @@ object mod {
)
)

def sitDcCounter(sitAndDcCounter: Int)(implicit ctx: Context) = div(id := "mz_sitdccounter")(
strong(cls := "text inline")("Sit/disconnect counter: "),
span(cls := "text inline")(sitAndDcCounter.toString),
br,
span(cls := "text inline")("+1 for every sit/disconnect in 'winning' position, -1 for 'losing' position")
)

def plan(u: User, charges: List[lila.plan.Charge])(implicit ctx: Context) = charges.headOption.map { firstCharge =>
div(id := "mz_plan")(
strong(cls := "text", dataIcon := patronIconChar)(
Expand Down
29 changes: 20 additions & 9 deletions modules/playban/src/main/PlaybanApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,30 @@ final class PlaybanApi(
blameable(game) flatMap { _ ?? f }
}

private def roughWinEstimate(game: Game, color: Color) = {
game.chess.board.materialImbalance match {
case a if a >= 5 => 1
case a if a <= -5 => -1
case _ => 0
}
} * (if (color == Color.White) 1 else -1)

def abort(pov: Pov, isOnGame: Set[Color]): Funit = IfBlameable(pov.game) {
pov.player.userId.ifTrue(isOnGame(pov.opponent.color)) ?? { userId =>
save(Outcome.Abort, userId) >>- feedback.abort(pov)
save(Outcome.Abort, userId, 0) >>- feedback.abort(pov)
}
}

def noStart(pov: Pov): Funit = IfBlameable(pov.game) {
pov.player.userId ?? { userId =>
save(Outcome.NoPlay, userId) >>- feedback.noStart(pov)
save(Outcome.NoPlay, userId, 0) >>- feedback.noStart(pov)
}
}

def rageQuit(game: Game, quitterColor: Color): Funit =
sandbag(game, quitterColor) >> IfBlameable(game) {
game.player(quitterColor).userId ?? { userId =>
save(Outcome.RageQuit, userId) >>- feedback.rageQuit(Pov(game, quitterColor))
save(Outcome.RageQuit, userId, roughWinEstimate(game, quitterColor)) >>- feedback.rageQuit(Pov(game, quitterColor))
}
}

Expand All @@ -73,7 +81,7 @@ final class PlaybanApi(
seconds = nowSeconds - game.movedAt.getSeconds
limit <- unreasonableTime
if seconds >= limit
} yield save(Outcome.Sitting, userId) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.Sitting, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))

// flagged after waiting a short time;
// but the previous move used a long time.
Expand All @@ -84,7 +92,7 @@ final class PlaybanApi(
lastMovetime <- movetimes.lastOption
limit <- unreasonableTime
if lastMovetime.toSeconds >= limit
} yield save(Outcome.SitMoving, userId) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.SitMoving, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))

sandbag(game, flaggerColor) flatMap { isSandbag =>
IfBlameable(game) {
Expand All @@ -102,7 +110,7 @@ final class PlaybanApi(
w <- winner
loserId <- game.player(!w).userId
} yield {
if (Status.NoStart is status) save(Outcome.NoPlay, loserId) >>- feedback.noStart(Pov(game, !w))
if (Status.NoStart is status) save(Outcome.NoPlay, loserId, 0) >>- feedback.noStart(Pov(game, !w))
else goodOrSandbag(game, !w, isSandbag)
})
}
Expand All @@ -111,7 +119,7 @@ final class PlaybanApi(
private def goodOrSandbag(game: Game, color: Color, isSandbag: Boolean): Funit =
game.player(color).userId ?? { userId =>
if (isSandbag) feedback.sandbag(Pov(game, color))
save(if (isSandbag) Outcome.Sandbag else Outcome.Good, userId)
save(if (isSandbag) Outcome.Sandbag else Outcome.Good, userId, 0)
}

def currentBan(userId: User.ID): Fu[Option[TempBan]] = coll.find(
Expand Down Expand Up @@ -148,7 +156,10 @@ final class PlaybanApi(
}(scala.collection.breakOut)
}

private def save(outcome: Outcome, userId: User.ID): Funit = {
def getSitAndDcCounter(user: User): Fu[Int] =
coll.primitiveOne[Int]($doc("_id" -> user.id, "c" $exists true), "c").map(~_)

private def save(outcome: Outcome, userId: User.ID, sitAndDcCounterChange: Int): Funit = {
lila.mon.playban.outcome(outcome.key)()
coll.findAndUpdate(
selector = $id(userId),
Expand All @@ -157,7 +168,7 @@ final class PlaybanApi(
"$each" -> List(outcome),
"$slice" -> -30
)
)),
), "$inc" -> $doc("c" -> sitAndDcCounterChange)),
fetchNewObject = true,
upsert = true
).map(_.value) map2 UserRecordBSONHandler.read flatMap {
Expand Down
4 changes: 3 additions & 1 deletion modules/playban/src/main/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import play.api.libs.json._
case class UserRecord(
_id: String,
o: Option[List[Outcome]],
b: Option[List[TempBan]]
b: Option[List[TempBan]],
c: Option[Int]
) {

def userId = _id
def outcomes: List[Outcome] = ~o
def bans: List[TempBan] = ~b
def sitAndDcCounter: Int = ~c

def banInEffect = bans.lastOption.exists(_.inEffect)

Expand Down

0 comments on commit cf76cdc

Please sign in to comment.