<commit_before>package lila.lobby

import scala.concurrent.duration._
import scala.concurrent.Future

import akka.actor._
import akka.pattern.ask
import play.api.libs.iteratee._
import play.api.libs.json._
import play.twirl.api.Html

import actorApi._
import lila.common.PimpedJson._
import lila.game.actorApi._
import lila.game.AnonCookie
import lila.hub.actorApi.game.ChangeFeatured
import lila.hub.actorApi.lobby._
import lila.hub.actorApi.timeline._
import lila.socket.actorApi.{ SocketLeave, Connected => _, _ }
import lila.socket.SocketActor
import makeTimeout.short

private[lobby] final class Socket(
    uidTtl: FiniteDuration) extends SocketActor[Member](uidTtl) {

  override val startsOnApplicationBoot = true

  override def preStart() {
    super.preStart()
    context.system.lilaBus.subscribe(self, 'changeFeaturedGame, 'streams, 'nbMembers, 'nbRounds, 'socketDoor)
  }

  override def postStop() {
    super.postStop()
    context.system.lilaBus.unsubscribe(self)
  }

  // override postRestart so we don't call preStart and schedule a new message
  override def postRestart(reason: Throwable) = {}

  var idleUids = scala.collection.mutable.Set[String]()

  def receiveSpecific = {

    case GetUids => sender ! SocketUids(members.keySet.toSet)

    case Join(uid, user, blocks, mobile) =>
      val (enumerator, channel) = Concurrent.broadcast[JsValue]
      val member = Member(channel, user, blocks, uid, mobile)
      addMember(uid, member)
      sender ! Connected(enumerator, member)

    case ReloadTournaments(html) => notifyAllActiveAsync(makeMessage("tournaments", html))

    case ReloadSimuls(html)      => notifyAllActiveAsync(makeMessage("simuls", html))

    case NewForumPost            => notifyAllActiveAsync(makeMessage("reload_forum"))

    case ReloadTimeline(userId) =>
      membersByUserId(userId) foreach (_ push makeMessage("reload_timeline"))

    case AddHook(hook) =>
      val json = hook.render
      members.foreach {
        case (uid, member) => if (!idleUids(uid)) notifyMemberOfHook(member.pp, hook, json)
      }

    case AddSeek(_) => notifySeeks

    case RemoveHook(hookId) =>
      notifyAllActiveAsync(makeMessage("hrm", hookId))

    case RemoveSeek(_) => notifySeeks

    case JoinHook(uid, hook, game, creatorColor) =>
      withMember(hook.uid)(notifyPlayerStart(game, creatorColor))
      withMember(uid)(notifyPlayerStart(game, !creatorColor))

    case JoinSeek(userId, seek, game, creatorColor) =>
      membersByUserId(seek.user.id) foreach notifyPlayerStart(game, creatorColor)
      membersByUserId(userId) foreach notifyPlayerStart(game, !creatorColor)

    case HookIds(ids)                         => notifyAllAsync(makeMessage("hli", ids mkString ","))
    case NbHooks(count)                       => notifyAllAsync(makeMessage("nb_hooks", count))

    case lila.hub.actorApi.StreamsOnAir(html) => notifyAllAsync(makeMessage("streams", html))

    case NbMembers(nb)                        => pong = pong + ("d" -> JsNumber(nb))
    case lila.hub.actorApi.round.NbRounds(nb) =>
      pong = pong + ("r" -> JsNumber(nb))

    case ChangeFeatured(_, msg) => notifyAllActiveAsync(msg)

    case SetIdle(uid, true)     => idleUids += uid
    case SetIdle(uid, false)    => idleUids -= uid
    case SocketLeave(uid, _)    => idleUids -= uid
  }

  private def notifyPlayerStart(game: lila.game.Game, color: chess.Color) =
    notifyMember("redirect", Json.obj(
      "id" -> (game fullIdOf color),
      "url" -> playerUrl(game fullIdOf color),
      "cookie" -> AnonCookie.json(game, color)
    ).noNull) _

  def notifyAllActiveAsync(msg: JsObject) = Future {<commit_msg>never send lobby seek messages to mobile app users<commit_after>package lila.lobby

import scala.concurrent.duration._
import scala.concurrent.Future

import akka.actor._
import akka.pattern.ask
import play.api.libs.iteratee._
import play.api.libs.json._
import play.twirl.api.Html

import actorApi._
import lila.common.PimpedJson._
import lila.game.actorApi._
import lila.game.AnonCookie
import lila.hub.actorApi.game.ChangeFeatured
import lila.hub.actorApi.lobby._
import lila.hub.actorApi.timeline._
import lila.socket.actorApi.{ SocketLeave, Connected => _, _ }
import lila.socket.SocketActor
import makeTimeout.short

private[lobby] final class Socket(
    uidTtl: FiniteDuration) extends SocketActor[Member](uidTtl) {

  override val startsOnApplicationBoot = true

  override def preStart() {
    super.preStart()
    context.system.lilaBus.subscribe(self, 'changeFeaturedGame, 'streams, 'nbMembers, 'nbRounds, 'socketDoor)
  }

  override def postStop() {
    super.postStop()
    context.system.lilaBus.unsubscribe(self)
  }

  // override postRestart so we don't call preStart and schedule a new message
  override def postRestart(reason: Throwable) = {}

  var idleUids = scala.collection.mutable.Set[String]()

  def receiveSpecific = {

    case GetUids => sender ! SocketUids(members.keySet.toSet)

    case Join(uid, user, blocks, mobile) =>
      val (enumerator, channel) = Concurrent.broadcast[JsValue]
      val member = Member(channel, user, blocks, uid, mobile)
      addMember(uid, member)
      sender ! Connected(enumerator, member)

    case ReloadTournaments(html) => notifyAllActiveAsync(makeMessage("tournaments", html))

    case ReloadSimuls(html)      => notifyAllActiveAsync(makeMessage("simuls", html))

    case NewForumPost            => notifyAllActiveAsync(makeMessage("reload_forum"))

    case ReloadTimeline(userId) =>
      membersByUserId(userId) foreach (_ push makeMessage("reload_timeline"))

    case AddHook(hook) => Future {
      val json = hook.render
      members.foreach {
        case (uid, member) =>
          if (!member.mobile && !idleUids(uid)) notifyMemberOfHook(member, hook, json)
      }
    }

    case AddSeek(_) => notifySeeks

    case RemoveHook(hookId) => Future {
      val msg = makeMessage("hrm", hookId)
      members.foreach {
        case (uid, member) =>
          if (!member.mobile && !idleUids(uid)) member push msg
      }
    }

    case RemoveSeek(_) => notifySeeks

    case JoinHook(uid, hook, game, creatorColor) =>
      withMember(hook.uid)(notifyPlayerStart(game, creatorColor))
      withMember(uid)(notifyPlayerStart(game, !creatorColor))

    case JoinSeek(userId, seek, game, creatorColor) =>
      membersByUserId(seek.user.id) foreach notifyPlayerStart(game, creatorColor)
      membersByUserId(userId) foreach notifyPlayerStart(game, !creatorColor)

    case HookIds(ids)                         => Future {
      val msg = makeMessage("hli", ids mkString ",")
      members.foreach {
        case (uid, member) =>
          if (!member.mobile && !idleUids(uid)) member push msg
      }
    }
    case NbHooks(count)                       => notifyAllAsync(makeMessage("nb_hooks", count))

    case lila.hub.actorApi.StreamsOnAir(html) => notifyAllAsync(makeMessage("streams", html))

    case NbMembers(nb)                        => pong = pong + ("d" -> JsNumber(nb))
    case lila.hub.actorApi.round.NbRounds(nb) =>
      pong = pong + ("r" -> JsNumber(nb))

    case ChangeFeatured(_, msg) => notifyAllActiveAsync(msg)

    case SetIdle(uid, true)     => idleUids += uid
    case SetIdle(uid, false)    => idleUids -= uid
    case SocketLeave(uid, _)    => idleUids -= uid
  }

  private def notifyPlayerStart(game: lila.game.Game, color: chess.Color) =
    notifyMember("redirect", Json.obj(
      "id" -> (game fullIdOf color),
      "url" -> playerUrl(game fullIdOf color),
      "cookie" -> AnonCookie.json(game, color)
    ).noNull) _

  def notifyAllActiveAsync(msg: JsObject) = Future {
{'fraction_non_alphanumeric': 0.11771638077779233, 'fraction_numerical': 0.0, 'mean_word_length': 3.5572792362768495, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 61, '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': '2958951', 'n_tokens_mistral': 2422, 'n_tokens_neox': 2369, 'n_words': 518}