package models import akka.actor._ import scala.concurrent.duration._ import scala.language.postfixOps import play.api._ import play.api.libs.json._ import play.api.libs.iteratee._ import play.api.libs.concurrent._ import akka.util.Timeout import akka.pattern.ask import play.api.Play.current import play.api.libs.concurrent.Execution.Implicits._ object ChatRoom { implicit val timeout = Timeout(1 second) lazy val default = Akka.system.actorOf(Props[ChatRoom]) 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 { val tabooGame = Akka.system.actorOf(Props(classOf[TabooGame], self)) 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) tabooGame ! Join(username) } } case NotifyJoin(username) => { notifyAll("join", username, "has entered the room") } case Talk(username, text) => { notifyAll("talk", username, text) tabooGame ! 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") tabooGame ! Quit(username) } } 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)