chat starts working

This commit is contained in:
Thibault Duplessis 2014-02-01 02:02:32 +01:00
parent a34df7ceb4
commit 05e020a068
13 changed files with 68 additions and 41 deletions

View file

@ -6,7 +6,7 @@ import reactivemongo.bson.BSONDocument
import lila.db.Types.Coll import lila.db.Types.Coll
import lila.user.{ User, UserRepo } import lila.user.{ User, UserRepo }
private[chat] final class ChatApi( final class ChatApi(
coll: Coll, coll: Coll,
flood: lila.security.Flood, flood: lila.security.Flood,
maxLinesPerChat: Int, maxLinesPerChat: Int,
@ -61,9 +61,9 @@ private[chat] final class ChatApi(
private def pushLine(chatId: ChatId, line: Line) = coll.update( private def pushLine(chatId: ChatId, line: Line) = coll.update(
BSONDocument("_id" -> chatId), BSONDocument("_id" -> chatId),
BSONDocument("$push" -> BSONDocument( BSONDocument("$push" -> BSONDocument(
"messages" -> BSONDocument( Chat.BSONFields.lines -> BSONDocument(
"$each" -> line, "$each" -> List(line),
"$slice" -> maxLinesPerChat) "$slice" -> -maxLinesPerChat)
)), )),
upsert = true) upsert = true)

View file

@ -3,7 +3,7 @@ package lila.chat
import akka.actor._ import akka.actor._
import chess.Color import chess.Color
import lila.hub.actorApi.chat._ import actorApi._
private[chat] final class FrontActor(api: ChatApi) extends Actor { private[chat] final class FrontActor(api: ChatApi) extends Actor {
@ -13,18 +13,19 @@ private[chat] final class FrontActor(api: ChatApi) extends Actor {
def receive = { def receive = {
case UserTalk(chatId, userId, text) api.userChat.write(chatId, userId, text) foreach publish(chatId) case UserTalk(chatId, userId, text, replyTo)
api.userChat.write(chatId, userId, text) foreach publish(chatId, replyTo)
case PlayerTalk(chatId, color, text) api.playerChat.write(chatId, Color(color), text) foreach publish(chatId) case PlayerTalk(chatId, color, text, replyTo)
api.playerChat.write(chatId, Color(color), text) foreach publish(chatId, replyTo)
case SystemTalk(chatId, text) api.userChat.system(chatId, text) foreach publish(chatId) case SystemTalk(chatId, text, replyTo)
api.userChat.system(chatId, text) foreach publish(chatId, replyTo)
case msg: NotifyLine bus.publish(msg, 'chatOut)
} }
def publish(chatId: String)(lineOption: Option[Line]) { def publish(chatId: String, replyTo: ActorRef)(lineOption: Option[Line]) {
lineOption foreach { line lineOption foreach { line
self ! NotifyLine(chatId, Line toJson line) replyTo ! ChatLine(chatId, line)
} }
} }
} }

View file

@ -29,7 +29,7 @@ object Line {
def write(x: Line) = BSONString(lineToStr(x)) def write(x: Line) = BSONString(lineToStr(x))
} }
private val UserLineRegex = """^([\w-]{2,})[\s!](.+)$""".r private val UserLineRegex = """^([\w-]{2,})(\s|\!)(.+)$""".r
def strToUserLine(str: String): Option[UserLine] = str match { def strToUserLine(str: String): Option[UserLine] = str match {
case UserLineRegex(username, " ", text) UserLine(username, text, false).some case UserLineRegex(username, " ", text) UserLine(username, text, false).some
case UserLineRegex(username, "!", text) UserLine(username, text, true).some case UserLineRegex(username, "!", text) UserLine(username, text, true).some

View file

@ -0,0 +1,9 @@
package lila.chat
package actorApi
import akka.actor.ActorRef
case class UserTalk(chatId: String, userId: String, text: String, replyTo: ActorRef)
case class PlayerTalk(chatId: String, white: Boolean, text: String, replyTo: ActorRef)
case class SystemTalk(chatId: String, text: String, replyTo: ActorRef)
case class ChatLine(chatId: String, line: Line)

View file

@ -1,8 +1,8 @@
package lila.game package lila.game
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
import play.api.libs.json._ import play.api.libs.json._
import lila.chat.{ Line, UserLine, PlayerLine }
import chess.Pos.{ piotr, allPiotrs } import chess.Pos.{ piotr, allPiotrs }
import chess.{ PromotableRole, Pos, Color, Situation, Move ChessMove, Clock ChessClock } import chess.{ PromotableRole, Pos, Color, Situation, Move ChessMove, Clock ChessClock }
@ -107,20 +107,20 @@ object Event {
def data = JsString(pos.key) def data = JsString(pos.key)
} }
case class Message(author: String, text: String, t: Boolean) extends Event { case class PlayerMessage(line: PlayerLine) extends Event {
def typ = "message" def typ = "message"
def data = Json.obj("u" -> author, "t" -> escapeXml(text)) def data = Line toJson line
override def owner = true override def owner = true
override def troll = t override def troll = false
} }
// it *IS* a username, and not a user ID // it *IS* a username, and not a user ID
// immediately used for rendering // immediately used for rendering
case class WatcherMessage(username: Option[String], text: String, t: Boolean) extends Event { case class UserMessage(line: UserLine, w: Boolean) extends Event {
def typ = "message" def typ = "message"
def data = Json.obj("u" -> username, "t" -> escapeXml(text)) def data = Line toJson line
override def watcher = true override def troll = line.troll
override def troll = t override def watcher = w
} }
object End extends Empty { object End extends Empty {

View file

@ -35,13 +35,6 @@ case class WithUserIds(f: Iterable[String] ⇒ Unit)
case object GetUids case object GetUids
package chat {
case class UserTalk(chatId: String, userId: String, text: String)
case class PlayerTalk(chatId: String, white: Boolean, text: String)
case class SystemTalk(chatId: String, text: String)
case class NotifyLine(chatId: String, json: JsObject)
}
package report { package report {
case class Cheater(userId: String, text: String) case class Cheater(userId: String, text: String)
case class Check(userId: String) case class Check(userId: String)

View file

@ -59,7 +59,7 @@ final class Env(
}), name = ActorMapName) }), name = ActorMapName)
private val socketHub = system.actorOf( private val socketHub = system.actorOf(
Props(new lila.socket.SocketHubActor.Default[Socket] { Props(new lila.socket.SocketHubActor[Socket] {
def mkActor(id: String) = new Socket( def mkActor(id: String) = new Socket(
gameId = id, gameId = id,
history = history(), history = history(),
@ -68,6 +68,10 @@ final class Env(
socketTimeout = SocketTimeout, socketTimeout = SocketTimeout,
disconnectTimeout = PlayerDisconnectTimeout, disconnectTimeout = PlayerDisconnectTimeout,
ragequitTimeout = PlayerRagequitTimeout) ragequitTimeout = PlayerRagequitTimeout)
def receive: Receive = ({
case msg@lila.chat.actorApi.ChatLine(id, line)
self ! lila.hub.actorApi.map.Tell(id take 8, msg).pp
}: Receive) orElse socketHubReceive
}), }),
name = SocketName) name = SocketName)
@ -113,6 +117,7 @@ final class Env(
lazy val messenger = new Messenger( lazy val messenger = new Messenger(
bus = system.lilaBus, bus = system.lilaBus,
socketHub = socketHub,
i18nKeys = i18nKeys) i18nKeys = i18nKeys)
lazy val fenUrlWatch = new FenUrlWatch( lazy val fenUrlWatch = new FenUrlWatch(

View file

@ -2,17 +2,18 @@ package lila.round
import lila.common.Bus import lila.common.Bus
import lila.game.Game import lila.game.Game
import lila.hub.actorApi.chat._ import lila.chat.actorApi._
import lila.i18n.I18nKey.{ Select SelectI18nKey } import lila.i18n.I18nKey.{ Select SelectI18nKey }
import lila.i18n.I18nKeys import lila.i18n.I18nKeys
final class Messenger( final class Messenger(
bus: Bus, bus: Bus,
socketHub: akka.actor.ActorRef,
i18nKeys: I18nKeys) { i18nKeys: I18nKeys) {
def apply(game: Game, message: SelectI18nKey, args: Any*) { def apply(game: Game, message: SelectI18nKey, args: Any*) {
val translated = message(i18nKeys).en(args: _*) val translated = message(i18nKeys).en(args: _*)
bus.publish(SystemTalk(game.id + "/w", translated), 'chatIn) bus.publish(SystemTalk(game.id + "/w", translated, socketHub), 'chatIn)
if (game.nonAi) bus.publish(SystemTalk(game.id, translated), 'chatIn) if (game.nonAi) bus.publish(SystemTalk(game.id, translated, socketHub), 'chatIn)
} }
} }

View file

@ -26,6 +26,8 @@ private[round] final class Socket(
disconnectTimeout: Duration, disconnectTimeout: Duration,
ragequitTimeout: Duration) extends SocketActor[Member](uidTimeout) { ragequitTimeout: Duration) extends SocketActor[Member](uidTimeout) {
context.system.lilaBus.subscribe(self, 'chatOut)
private val timeBomb = new TimeBomb(socketTimeout) private val timeBomb = new TimeBomb(socketTimeout)
private final class Player(color: Color) { private final class Player(color: Color) {
@ -87,8 +89,13 @@ private[round] final class Socket(
sender ! Connected(enumerator, member) sender ! Connected(enumerator, member)
} }
case Nil case Nil
case events: Events notify(events) case events: Events notify(events)
case lila.chat.actorApi.ChatLine(chatId, line) notify(List(line match {
case l: lila.chat.UserLine Event.UserMessage(l, chatId endsWith "/w")
case l: lila.chat.PlayerLine Event.PlayerMessage(l)
}))
case AnalysisAvailable notifyAll("analysisAvailable", true) case AnalysisAvailable notifyAll("analysisAvailable", true)

View file

@ -8,6 +8,7 @@ import chess.Color
import play.api.libs.json.{ JsObject, Json } import play.api.libs.json.{ JsObject, Json }
import actorApi._, round._ import actorApi._, round._
import lila.chat.actorApi._
import lila.common.PimpedJson._ import lila.common.PimpedJson._
import lila.game.{ Game, Pov, PovRef, PlayerRef, GameRepo } import lila.game.{ Game, Pov, PovRef, PlayerRef, GameRepo }
import lila.hub.actorApi.map._ import lila.hub.actorApi.map._
@ -37,6 +38,11 @@ private[round] final class SocketHandler(
case ("liveGames", o) o str "d" foreach { ids case ("liveGames", o) o str "d" foreach { ids
socket ! LiveGames(uid, ids.split(' ').toList) socket ! LiveGames(uid, ids.split(' ').toList)
} }
case ("talk", o) o str "d" foreach { text
member.userId foreach { userId =>
bus.publish(UserTalk(gameId + "/w", userId, text, socket), 'chatIn)
}
}
}) { playerId }) { playerId
{ {
case ("p", o) o int "v" foreach { v socket ! PingVersion(uid, v) } case ("p", o) o int "v" foreach { v socket ! PingVersion(uid, v) }
@ -70,8 +76,8 @@ private[round] final class SocketHandler(
} }
case ("talk", o) o str "d" foreach { text case ("talk", o) o str "d" foreach { text
bus.publish(member.userId match { bus.publish(member.userId match {
case Some(userId) lila.hub.actorApi.chat.UserTalk(uid, userId, text) case Some(userId) UserTalk(gameId, userId, text, socket)
case None lila.hub.actorApi.chat.PlayerTalk(uid, member.color.white, text) case None PlayerTalk(gameId, member.color.white, text, socket)
}, 'chatIn) }, 'chatIn)
} }
} }

View file

@ -90,7 +90,7 @@ object ApplicationBuild extends Build {
play.api, play.test, RM, PRM, hasher) play.api, play.test, RM, PRM, hasher)
) )
lazy val game = project("game", Seq(common, memo, db, hub, user, chess)).settings( lazy val game = project("game", Seq(common, memo, db, hub, user, chess, chat)).settings(
libraryDependencies ++= provided( libraryDependencies ++= provided(
play.api, RM, PRM) play.api, RM, PRM)
) )
@ -106,7 +106,7 @@ object ApplicationBuild extends Build {
) )
lazy val round = project("round", Seq( lazy val round = project("round", Seq(
common, db, memo, hub, socket, chess, game, user, i18n, ai, pref)).settings( common, db, memo, hub, socket, chess, game, user, i18n, ai, pref, chat)).settings(
libraryDependencies ++= provided(play.api, RM, PRM) libraryDependencies ++= provided(play.api, RM, PRM)
) )

View file

@ -348,6 +348,11 @@ var storage = {
}); });
} }
}, },
message: function(msg) {
$('div.lichess_chat').each(function() {
$(this).chat("append", msg);
});
},
nbm: function(e) { nbm: function(e) {
$('#nb_messages').text(e || "0").toggleClass("unread", e > 0); $('#nb_messages').text(e || "0").toggleClass("unread", e > 0);
}, },
@ -1584,11 +1589,12 @@ var storage = {
if (!$toggle[0].checked) { if (!$toggle[0].checked) {
self.element.addClass('hidden'); self.element.addClass('hidden');
} }
if (self.options.messages.length > 0) self._appendMany(self.options.messages);
}, },
append: function(msg) { append: function(msg) {
this._appendHtml(this._render(msg)); this._appendHtml(this._render(msg));
}, },
appendMany: function(objs) { _appendMany: function(objs) {
var self = this, var self = this,
html = ""; html = "";
$.each(objs, function() { $.each(objs, function() {
@ -1605,7 +1611,7 @@ var storage = {
} else { } else {
user = '<span class="user">' + $.userLinkLimit(msg.u, 14) + '</span>'; user = '<span class="user">' + $.userLinkLimit(msg.u, 14) + '</span>';
} }
return '<li class="' + (u == 'system' ? ' trans_me' : '') + (msg.r ? ' troll' : '') + '">' + urlToLink(msg.t) + '</li>'; return '<li class="' + (msg.u == 'system' ? ' trans_me' : '') + (msg.r ? ' troll' : '') + '">' + user + urlToLink(msg.t) + '</li>';
}, },
_appendHtml: function(html) { _appendHtml: function(html) {
this.$msgs.append(html); this.$msgs.append(html);

View file

@ -2,7 +2,6 @@ div.lichess_board_wrap {
float: left; float: left;
border: 1px solid #ccc; border: 1px solid #ccc;
position: relative; position: relative;
display: none;
} }
span.board_mark { span.board_mark {
position: absolute; position: absolute;