| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 | package modelsimport akka.actor._import scala.concurrent.duration._import scala.language.postfixOpsimport play.api._import play.api.libs.json._import play.api.libs.iteratee._import play.api.libs.concurrent._import akka.util.Timeoutimport akka.pattern.askimport play.api.Play.currentimport play.api.libs.concurrent.Execution.Implicits._object Robot {    def apply(chatRoom: ActorRef) {        // Create an Iteratee that logs all messages to the console.    val loggerIteratee = Iteratee.foreach[JsValue](event => Logger("robot").info(event.toString))        implicit val timeout = Timeout(1 second)    // Make the robot join the room    chatRoom ? (Join("Robot")) map {      case Connected(robotChannel) =>         // Apply this Enumerator on the logger.        robotChannel |>> loggerIteratee    }        // Make the robot talk every 30 seconds    Akka.system.scheduler.schedule(      30 seconds,      30 seconds,      chatRoom,      Talk("Robot", "I'm still alive")    )  }  }object ChatRoom {    implicit val timeout = Timeout(1 second)    lazy val default = {    val roomActor = Akka.system.actorOf(Props[ChatRoom])        // Create a bot user (just for fun)    Robot(roomActor)        roomActor  }  def join(username:String):scala.concurrent.Future[(Iteratee[JsValue,_],Enumerator[JsValue])] = {    (default ? Join(username)).map {            case Connected(enumerator) =>               // Create an Iteratee to consume the feed        val iteratee = Iteratee.foreach[JsValue] { event =>          default ! Talk(username, (event \ "text").as[String])        }.map { _ =>          default ! Quit(username)        }        (iteratee,enumerator)              case CannotConnect(error) =>               // Connection error        // A finished Iteratee sending EOF        val iteratee = Done[JsValue,Unit]((),Input.EOF)        // Send an error and close the socket        val enumerator =  Enumerator[JsValue](JsObject(Seq("error" -> JsString(error)))).andThen(Enumerator.enumInput(Input.EOF))                (iteratee,enumerator)             }  }  }class ChatRoom extends Actor {  var members = Map.empty[String, Concurrent.Channel[JsValue]]  val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]  def receive = {        case Join(username) => {      if(members.contains(username)) {        sender ! CannotConnect("This username is already used")      } else {        val (personalEnumerator, personalChannel) = Concurrent.broadcast[JsValue]        members = members + (username -> personalChannel)        sender ! Connected(chatEnumerator.interleave(personalEnumerator))        self ! NotifyJoin(username)      }    }    case NotifyJoin(username) => {      notifyAll("join", username, "has entered the room")    }        case Talk(username, text) => {      notifyAll("talk", username, text)    }    case Tell(username, text, to) => {      notifyOne(to, "talk", username, text)    }        case Quit(username) => {      members = members - username      notifyAll("quit", username, "has left the room")    }      }    def notifyOne(to: String, kind: String, user: String, text: String) {    val msg = JsObject(      Seq(        "kind" -> JsString(kind),        "user" -> JsString(user),        "message" -> JsString(text),        "members" -> JsArray(          members.keys.toList.map(JsString)        )      )    )    members(to).push(msg)  }  def notifyAll(kind: String, user: String, text: String) {    val msg = JsObject(      Seq(        "kind" -> JsString(kind),        "user" -> JsString(user),        "message" -> JsString(text),        "members" -> JsArray(          members.keys.toList.map(JsString)        )      )    )    chatChannel.push(msg)  }}case class Join(username: String)case class Quit(username: String)case class Talk(username: String, text: String)case class Tell(username: String, text: String, to: String)case class NotifyJoin(username: String)case class Connected(enumerator:Enumerator[JsValue])case class CannotConnect(msg: String)
 |