<commit_before>package lila.playban

import com.github.blemale.scaffeine.Cache
import scala.concurrent.duration._

import chess.Color
import lila.game.Game
import lila.msg.{ MsgApi, MsgPreset }
import lila.user.{ User, UserRepo }

final private class SandbagWatch(
    userRepo: UserRepo,
    messenger: MsgApi
)(implicit ec: scala.concurrent.ExecutionContext) {

  import SandbagWatch._

  def apply(game: Game, loser: Color): Fu[Boolean] =
    game.rated ?? {
      game.userIds
        .map { userId =>
          (records getIfPresent userId, isSandbag(game, loser, userId)) match {
            case (None, false)         => funit
            case (Some(record), false) => updateRecord(userId, record + Good)
            case (record, true)        => updateRecord(userId, (record | newRecord) + Sandbag)
          }
        }
        .sequenceFu
        .void inject isSandbag(game)
    }

  private def sendMessage(userId: User.ID): Funit =
    userRepo byId userId flatMap {
      _ ?? { u =>
        lila.common.Bus
          .publish(lila.hub.actorApi.mod.AutoWarning(u.id, MsgPreset.sandbagAuto.name), "autoWarning")
        messenger.postPreset(u, MsgPreset.sandbagAuto).void
      }
    }

  private def updateRecord(userId: User.ID, record: Record) =
    if (record.immaculate) fuccess(records invalidate userId)
    else {
      records.put(userId, record)
      record.alert ?? sendMessage(userId)
    }

  private val records: Cache[User.ID, Record] = lila.memo.CacheApi.scaffeineNoScheduler
    .expireAfterWrite(3 hours)
    .build[User.ID, Record]()
<commit_msg>send at most one sandback auto-message per hour<commit_after>package lila.playban

import chess.Color
import com.github.blemale.scaffeine.Cache
import scala.concurrent.duration._

import lila.game.Game
import lila.msg.{ MsgApi, MsgPreset }
import lila.user.{ User, UserRepo }

final private class SandbagWatch(
    userRepo: UserRepo,
    messenger: MsgApi
)(implicit ec: scala.concurrent.ExecutionContext) {

  import SandbagWatch._

  private val onceEvery = lila.memo.OnceEvery(1 hour)

  def apply(game: Game, loser: Color): Fu[Boolean] =
    game.rated ?? {
      game.userIds
        .map { userId =>
          (records getIfPresent userId, isSandbag(game, loser, userId)) match {
            case (None, false)         => funit
            case (Some(record), false) => updateRecord(userId, record + Good)
            case (record, true)        => updateRecord(userId, (record | newRecord) + Sandbag)
          }
        }
        .sequenceFu
        .void inject isSandbag(game)
    }

  private def sendMessage(userId: User.ID): Funit =
    onceEvery(userId) ?? {
      userRepo byId userId flatMap {
        _ ?? { u =>
          lila.common.Bus
            .publish(lila.hub.actorApi.mod.AutoWarning(u.id, MsgPreset.sandbagAuto.name), "autoWarning")
          messenger.postPreset(u, MsgPreset.sandbagAuto).void
        }
      }
    }

  private def updateRecord(userId: User.ID, record: Record) =
    if (record.immaculate) fuccess(records invalidate userId)
    else {
      records.put(userId, record)
      record.alert ?? sendMessage(userId)
    }

  private val records: Cache[User.ID, Record] = lila.memo.CacheApi.scaffeineNoScheduler
    .expireAfterWrite(3 hours)
    .build[User.ID, Record]()

{'fraction_non_alphanumeric': 0.1111111111111111, 'fraction_numerical': 0.0009107468123861566, 'mean_word_length': 2.796082949308756, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4616065', 'n_tokens_mistral': 1053, 'n_tokens_neox': 1058, 'n_words': 235}