diff --git a/modules/api/src/main/AccountClosure.scala b/modules/api/src/main/AccountClosure.scala index ed9c380e7ac78..ee9b69260d753 100644 --- a/modules/api/src/main/AccountClosure.scala +++ b/modules/api/src/main/AccountClosure.scala @@ -30,7 +30,7 @@ final class AccountClosure( "garbageCollect" -> { case lila.core.actorApi.security.GarbageCollect(userId) => (modApi.garbageCollect(userId) >> lichessClose(userId)) }, - "rageSitClose" -> { case lila.core.actorApi.playban.RageSitClose(userId) => lichessClose(userId) } + "rageSitClose" -> { case lila.core.playban.RageSitClose(userId) => lichessClose(userId) } ) def close(u: User)(using me: Me): Funit = for diff --git a/modules/core/src/main/actorApi.scala b/modules/core/src/main/actorApi.scala index d751891dd0046..fb7db0c28feef 100644 --- a/modules/core/src/main/actorApi.scala +++ b/modules/core/src/main/actorApi.scala @@ -56,10 +56,6 @@ package puzzle: case class RacerRun(userId: UserId, score: Int) case class StreakRun(userId: UserId, score: Int) -package playban: - case class Playban(userId: UserId, mins: Int, inTournament: Boolean) - case class RageSitClose(userId: UserId) - package lpv: enum LpvEmbed: case PublicPgn(pgn: PgnStr) diff --git a/modules/core/src/main/playban.scala b/modules/core/src/main/playban.scala index 46d9a33821049..56573812c0b03 100644 --- a/modules/core/src/main/playban.scala +++ b/modules/core/src/main/playban.scala @@ -9,4 +9,6 @@ type BansOf = List[UserId] => Fu[Map[UserId, Int]] type RageSitOf = UserId => Fu[RageSit] type HasCurrentPlayban = UserId => Fu[Boolean] -case class Playban(userId: UserId, mins: Int) +case class Playban(userId: UserId, mins: Int, inTournament: Boolean) +case class SittingDetected(tourId: TourId, userId: UserId) +case class RageSitClose(userId: UserId) diff --git a/modules/playban/src/main/PlaybanApi.scala b/modules/playban/src/main/PlaybanApi.scala index 050896596af11..c69f895c8ea75 100644 --- a/modules/playban/src/main/PlaybanApi.scala +++ b/modules/playban/src/main/PlaybanApi.scala @@ -51,12 +51,12 @@ final class PlaybanApi( pov.player.userId .ifTrue(isOnGame(pov.opponent.color)) .so: userId => - save(Outcome.Abort, userId, RageSit.Update.Reset).andDo(feedback.abort(pov)) + save(Outcome.Abort, userId, RageSit.Update.Reset, pov.game.source).andDo(feedback.abort(pov)) def noStart(pov: Pov): Funit = IfBlameable(pov.game): pov.player.userId.so: userId => - save(Outcome.NoPlay, userId, RageSit.Update.Reset).andDo(feedback.noStart(pov)) + save(Outcome.NoPlay, userId, RageSit.Update.Reset, pov.game.source).andDo(feedback.noStart(pov)) def rageQuit(game: Game, quitterColor: Color): Funit = IfBlameable(game): @@ -64,7 +64,7 @@ final class PlaybanApi( .player(quitterColor) .userId .so: userId => - save(Outcome.RageQuit, userId, RageSit.imbalanceInc(game, quitterColor)) + save(Outcome.RageQuit, userId, RageSit.imbalanceInc(game, quitterColor), game.source) .andDo(feedback.rageQuit(Pov(game, quitterColor))) def flag(game: Game, flaggerColor: Color): Funit = @@ -79,7 +79,7 @@ final class PlaybanApi( userId <- game.player(flaggerColor).userId seconds = nowSeconds - game.movedAt.toSeconds if unreasonableTime.exists(seconds >= _) - yield (save(Outcome.Sitting, userId, RageSit.imbalanceInc(game, flaggerColor)) >> + yield (save(Outcome.Sitting, userId, RageSit.imbalanceInc(game, flaggerColor), game.source) >> propagateSitting(game, userId)).andDo(feedback.sitting(Pov(game, flaggerColor))) // flagged after waiting a short time; @@ -97,7 +97,7 @@ final class PlaybanApi( yield lastMovetime.toSeconds >= limit) } .map { userId => - (save(Outcome.SitMoving, userId, RageSit.imbalanceInc(game, flaggerColor)) >> + (save(Outcome.SitMoving, userId, RageSit.imbalanceInc(game, flaggerColor), game.source) >> propagateSitting(game, userId)).andDo(feedback.sitting(Pov(game, flaggerColor))) } @@ -106,9 +106,11 @@ final class PlaybanApi( } private def propagateSitting(game: Game, userId: UserId): Funit = - rageSitCache.get(userId).map { rageSit => - if rageSit.isBad then Bus.publish(SittingDetected(game, userId), "playban") - } + game.tournamentId.so: tourId => + rageSitCache.get(userId).map { rageSit => + if rageSit.isBad + then Bus.publish(lila.core.playban.SittingDetected(tourId, userId), "playban") + } def other(game: Game, status: Status.type => Status, winner: Option[Color]): Funit = IfBlameable(game) { @@ -118,7 +120,7 @@ final class PlaybanApi( loserId <- loser.userId yield if Status.NoStart.is(status) then - save(Outcome.NoPlay, loserId, RageSit.Update.Reset) + save(Outcome.NoPlay, loserId, RageSit.Update.Reset, game.source) .andDo(feedback.noStart(Pov(game, !w))) else game.clock @@ -130,7 +132,7 @@ final class PlaybanApi( (c.estimateTotalSeconds / 10).atLeast(30).atMost(3 * 60) .exists(_ < nowSeconds - game.movedAt.toSeconds) .option: - (save(Outcome.SitResign, loserId, RageSit.imbalanceInc(game, loser.color)) >> + (save(Outcome.SitResign, loserId, RageSit.imbalanceInc(game, loser.color), game.source) >> propagateSitting(game, loserId)).andDo(feedback.sitting(Pov(game, loser.color))) .getOrElse: good(game, !w) @@ -139,7 +141,7 @@ final class PlaybanApi( private def good(game: Game, loserColor: Color): Funit = game.player(loserColor).userId.so { - save(Outcome.Good, _, RageSit.redeem(game)) + save(Outcome.Good, _, RageSit.redeem(game), game.source) } // memorize users without any ban to save DB reads @@ -193,7 +195,12 @@ final class PlaybanApi( } } - private def save(outcome: Outcome, userId: UserId, rsUpdate: RageSit.Update): Funit = { + private def save( + outcome: Outcome, + userId: UserId, + rsUpdate: RageSit.Update, + source: Option[Source] + ): Funit = { lila.mon.playban.outcome(outcome.key).increment() for withOutcome <- coll @@ -216,13 +223,13 @@ final class PlaybanApi( else for createdAt <- userRepo.createdAtById(userId).orFail(s"Missing user creation date $userId") - withBan <- legiferate(withOutcome, createdAt) + withBan <- legiferate(withOutcome, createdAt, source) yield withBan _ <- registerRageSit(withBan, rsUpdate) yield () }.void.logFailure(lila.log("playban")) - private def legiferate(record: UserRecord, accCreatedAt: Instant): Fu[UserRecord] = + private def legiferate(record: UserRecord, accCreatedAt: Instant, source: Option[Source]): Fu[UserRecord] = record .bannable(accCreatedAt) .ifFalse(record.banInEffect) @@ -230,7 +237,7 @@ final class PlaybanApi( lila.mon.playban.ban.count.increment() lila.mon.playban.ban.mins.record(ban.mins) Bus.publish( - lila.core.playban.Playban(record.userId, ban.mins), + lila.core.playban.Playban(record.userId, ban.mins, inTournament = source.has(Source.Arena)), "playban" ) coll @@ -263,6 +270,6 @@ final class PlaybanApi( .flatMapz: user => noteApi .lichessWrite(user, "Closed for ragesit recidive") - .andDo(Bus.publish(lila.core.actorApi.playban.RageSitClose(user.id), "rageSitClose")) + .andDo(Bus.publish(lila.core.playban.RageSitClose(user.id), "rageSitClose")) } case _ => funit diff --git a/modules/playban/src/main/RageSit.scala b/modules/playban/src/main/RageSit.scala index 14288ecc66b74..1189c4b16e9c1 100644 --- a/modules/playban/src/main/RageSit.scala +++ b/modules/playban/src/main/RageSit.scala @@ -47,5 +47,3 @@ object RageSit: case Speed.Blitz => 1 case _ => 2 } - -case class SittingDetected(game: Game, userId: UserId) diff --git a/modules/report/src/main/Env.scala b/modules/report/src/main/Env.scala index d6fe3d0a0a7dd..a72415cda4f79 100644 --- a/modules/report/src/main/Env.scala +++ b/modules/report/src/main/Env.scala @@ -52,4 +52,4 @@ final class Env( api.inquiries.expire lila.common.Bus.subscribeFun("playban"): - case lila.core.playban.Playban(userId, mins) => api.maybeAutoPlaybanReport(userId, mins) + case lila.core.playban.Playban(userId, mins, _) => api.maybeAutoPlaybanReport(userId, mins) diff --git a/modules/tournament/src/main/TournamentApi.scala b/modules/tournament/src/main/TournamentApi.scala index c7718f6865b8a..fe2950d21a329 100644 --- a/modules/tournament/src/main/TournamentApi.scala +++ b/modules/tournament/src/main/TournamentApi.scala @@ -345,13 +345,12 @@ final class TournamentApi( def selfPause(tourId: TourId, userId: UserId): Funit = withdraw(tourId, userId, isPause = true, isStalling = false) - private def stallPause(tourId: TourId, userId: UserId): Funit = - withdraw(tourId, userId, isPause = false, isStalling = true) - - private[tournament] def sittingDetected(game: Game, player: UserId): Funit = - game.tournamentId.so { stallPause(_, player) } - - private def withdraw(tourId: TourId, userId: UserId, isPause: Boolean, isStalling: Boolean): Funit = + private[tournament] def withdraw( + tourId: TourId, + userId: UserId, + isPause: Boolean, + isStalling: Boolean + ): Funit = Parallel(tourId, "withdraw")(cached.tourCache.enterable): case tour if tour.isCreated => (playerRepo.remove(tour.id, userId) >> updateNbPlayers(tour.id)).andDo { diff --git a/modules/tournament/src/main/TournamentBusHandler.scala b/modules/tournament/src/main/TournamentBusHandler.scala index e42bb06d7c619..5e235656615ff 100644 --- a/modules/tournament/src/main/TournamentBusHandler.scala +++ b/modules/tournament/src/main/TournamentBusHandler.scala @@ -34,11 +34,12 @@ final private class TournamentBusHandler( winnersApi.clearAfterMarking(userId) () - case lila.core.mod.MarkBooster(userId) => ejectFromEnterable(userId) - case lila.core.round.Berserk(gameId, userId) => api.berserk(gameId, userId) - case lila.core.actorApi.playban.Playban(userId, _, true) => api.pausePlaybanned(userId) - case lila.core.team.KickFromTeam(teamId, _, userId) => api.kickFromTeam(teamId, userId) - case lila.playban.SittingDetected(game, player) => api.sittingDetected(game, player) + case lila.core.mod.MarkBooster(userId) => ejectFromEnterable(userId) + case lila.core.round.Berserk(gameId, userId) => api.berserk(gameId, userId) + case lila.core.playban.Playban(userId, _, true) => api.pausePlaybanned(userId) + case lila.core.team.KickFromTeam(teamId, _, userId) => api.kickFromTeam(teamId, userId) + case lila.core.playban.SittingDetected(tourId, userId) => + api.withdraw(tourId, userId, isPause = false, isStalling = true) private def ejectFromEnterable(userId: UserId) = tournamentRepo.withdrawableIds(userId, reason = "ejectFromEnterable").flatMap {