auto-unfollow closed/inactive accounts on follow limit reached
expensive, only once a hour per user. closes #5737pull/5750/head
parent
e2453c93ee
commit
5ee1734648
|
@ -39,16 +39,7 @@ final class Env(
|
|||
|
||||
private lazy val repo: RelationRepo = wire[RelationRepo]
|
||||
|
||||
lazy val api = new RelationApi(
|
||||
coll = coll,
|
||||
repo = repo,
|
||||
actor = relation,
|
||||
timeline = timeline,
|
||||
prefApi = prefApi,
|
||||
asyncCache = asyncCache,
|
||||
maxFollow = config.maxFollow,
|
||||
maxBlock = config.maxBlock
|
||||
)
|
||||
lazy val api: RelationApi = wire[RelationApi]
|
||||
|
||||
lazy val stream = wire[RelationStream]
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package lila.relation
|
|||
import reactivemongo.api._
|
||||
import reactivemongo.api.bson._
|
||||
import scala.concurrent.duration._
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import BSONHandlers._
|
||||
import lila.common.Bus
|
||||
import lila.common.config.Max
|
||||
import lila.db.dsl._
|
||||
import lila.db.paginator._
|
||||
import lila.hub.actors
|
||||
|
@ -20,8 +20,8 @@ final class RelationApi(
|
|||
timeline: actors.Timeline,
|
||||
prefApi: lila.pref.PrefApi,
|
||||
asyncCache: lila.memo.AsyncCache.Builder,
|
||||
maxFollow: Max,
|
||||
maxBlock: Max
|
||||
userRepo: lila.user.UserRepo,
|
||||
config: RelationConfig
|
||||
) {
|
||||
|
||||
import RelationRepo.makeId
|
||||
|
@ -80,7 +80,7 @@ final class RelationApi(
|
|||
|
||||
def countFollowing(userId: ID) = countFollowingCache get userId
|
||||
|
||||
def reachedMaxFollowing(userId: ID): Fu[Boolean] = countFollowingCache get userId map (maxFollow <=)
|
||||
def reachedMaxFollowing(userId: ID): Fu[Boolean] = countFollowingCache get userId map (config.maxFollow <=)
|
||||
|
||||
private val countFollowersCache = asyncCache.clearable[ID, Int](
|
||||
name = "relation.count.followers",
|
||||
|
@ -135,7 +135,7 @@ final class RelationApi(
|
|||
case _ =>
|
||||
repo.follow(u1, u2) >> limitFollow(u1) >>- {
|
||||
countFollowersCache.update(u2, 1 +)
|
||||
countFollowingCache.update(u1, prev => (prev + 1) atMost maxFollow.value)
|
||||
countFollowingCache.update(u1, prev => (prev + 1) atMost config.maxFollow.value)
|
||||
reloadOnlineFriends(u1, u2)
|
||||
timeline ! Propagate(FollowUser(u1, u2)).toFriendsOf(u1).toUsers(List(u2))
|
||||
Bus.publish(lila.hub.actorApi.relation.Follow(u1, u2), "relation")
|
||||
|
@ -144,12 +144,28 @@ final class RelationApi(
|
|||
}
|
||||
}
|
||||
|
||||
private val limitFollowRateLimiter = new lila.memo.RateLimit[ID](
|
||||
credits = 1,
|
||||
duration = 1 hour,
|
||||
name = "follow limit cleanup",
|
||||
key = "follow.limit.cleanup"
|
||||
)
|
||||
|
||||
private def limitFollow(u: ID) = countFollowing(u) flatMap { nb =>
|
||||
(maxFollow < nb) ?? repo.drop(u, true, nb - maxFollow.value)
|
||||
(config.maxFollow < nb || true) ?? {
|
||||
limitFollowRateLimiter(u) {
|
||||
fetchFollowing(u) flatMap userRepo.filterClosedOrInactiveIds(DateTime.now.minusDays(90))
|
||||
} flatMap {
|
||||
case Nil => repo.drop(u, true, nb - config.maxFollow.value)
|
||||
case inactiveIds =>
|
||||
repo.unfollowMany(u, inactiveIds) >>-
|
||||
countFollowingCache.update(u, _ - inactiveIds.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def limitBlock(u: ID) = countBlocking(u) flatMap { nb =>
|
||||
(maxBlock < nb) ?? repo.drop(u, false, nb - maxBlock.value)
|
||||
(config.maxBlock < nb) ?? repo.drop(u, false, nb - config.maxBlock.value)
|
||||
}
|
||||
|
||||
def block(u1: ID, u2: ID): Funit = (u1 != u2) ?? {
|
||||
|
|
|
@ -35,7 +35,7 @@ final private class RelationRepo(coll: Coll) {
|
|||
): Fu[Set[ID]] =
|
||||
coll
|
||||
.withReadPreference(rp)
|
||||
.distinctEasy[String, Set](
|
||||
.distinctEasy[ID, Set](
|
||||
"u1",
|
||||
$doc(
|
||||
"u2" -> userId,
|
||||
|
@ -44,7 +44,7 @@ final private class RelationRepo(coll: Coll) {
|
|||
)
|
||||
|
||||
private def relating(userId: ID, relation: Relation): Fu[Set[ID]] =
|
||||
coll.distinctEasy[String, Set](
|
||||
coll.distinctEasy[ID, Set](
|
||||
"u2",
|
||||
$doc(
|
||||
"u1" -> userId,
|
||||
|
@ -57,6 +57,9 @@ final private class RelationRepo(coll: Coll) {
|
|||
def block(u1: ID, u2: ID): Funit = save(u1, u2, Block)
|
||||
def unblock(u1: ID, u2: ID): Funit = remove(u1, u2)
|
||||
|
||||
def unfollowMany(u1: ID, u2s: Iterable[ID]): Funit =
|
||||
coll.delete.one($inIds(u2s map { makeId(u1, _) })).void
|
||||
|
||||
def unfollowAll(u1: ID): Funit = coll.delete.one($doc("u1" -> u1)).void
|
||||
|
||||
private def save(u1: ID, u2: ID, relation: Relation): Funit =
|
||||
|
|
|
@ -239,12 +239,13 @@ final class UserRepo(val coll: Coll) {
|
|||
coll.primitiveOne[User.PlayTime]($id(id), F.playTime)
|
||||
|
||||
val enabledSelect = $doc(F.enabled -> true)
|
||||
def engineSelect(v: Boolean) = $doc(F.engine -> (if (v) $boolean(true) else $ne(true)))
|
||||
def trollSelect(v: Boolean) = $doc(F.troll -> (if (v) $boolean(true) else $ne(true)))
|
||||
val disabledSelect = $doc(F.enabled -> false)
|
||||
def engineSelect(v: Boolean) = $doc(F.engine -> (if (v) $boolean(true) else $ne(true)))
|
||||
def trollSelect(v: Boolean) = $doc(F.troll -> (if (v) $boolean(true) else $ne(true)))
|
||||
def boosterSelect(v: Boolean) = $doc(F.booster -> (if (v) $boolean(true) else $ne(true)))
|
||||
val goodLadSelect = enabledSelect ++ engineSelect(false) ++ boosterSelect(false)
|
||||
def stablePerfSelect(perf: String) =
|
||||
$doc(s"perfs.$perf.gl.d" -> $lt(lila.rating.Glicko.provisionalDeviation))
|
||||
val goodLadSelect = enabledSelect ++ engineSelect(false) ++ boosterSelect(false)
|
||||
val goodLadSelectBson = $doc(
|
||||
F.enabled -> true,
|
||||
F.engine $ne true,
|
||||
|
@ -544,7 +545,7 @@ final class UserRepo(val coll: Coll) {
|
|||
.find(
|
||||
$doc(
|
||||
F.enabled -> true,
|
||||
"seenAt" $gt since,
|
||||
F.seenAt $gt since,
|
||||
"count.game" $gt 9,
|
||||
"kid" $ne true
|
||||
),
|
||||
|
@ -607,6 +608,12 @@ final class UserRepo(val coll: Coll) {
|
|||
coll.exists($id(user.id) ++ $doc("erasedAt" $exists true))
|
||||
} map User.Erased.apply
|
||||
|
||||
def filterClosedOrInactiveIds(since: DateTime)(ids: Iterable[ID]): Fu[List[ID]] =
|
||||
coll.distinctEasy[ID, List](
|
||||
F.id,
|
||||
$inIds(ids) ++ $or(disabledSelect, F.seenAt $lt since)
|
||||
)
|
||||
|
||||
private def newUser(
|
||||
username: String,
|
||||
passwordHash: HashedPassword,
|
||||
|
|
Loading…
Reference in New Issue