123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- 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 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)
|