add correspondence move alarm desktop notification

This commit is contained in:
Thibault Duplessis 2017-01-02 13:52:59 +01:00
parent a718def197
commit 91318fa8dd
12 changed files with 87 additions and 45 deletions

View file

@ -17,4 +17,6 @@ case class InsertGame(game: Game)
case class AbortedBy(pov: Pov)
case class CorresAlarmEvent(pov: Pov)
private[game] case object NewCaptcha

View file

@ -166,7 +166,6 @@ case class MoveEvent(
alarmable: Boolean,
opponentUserId: Option[String],
simulId: Option[String])
case class CorresAlarmEvent(gameId: String)
case class NbRounds(nb: Int)
case class Abort(gameId: String, byColor: String)
case class Berserk(gameId: String, userId: String)

View file

@ -55,6 +55,7 @@ private object BSONHandlers {
implicit val PlanExpireHandler = Macros.handler[PlanExpire]
implicit val RatingRefundHandler = Macros.handler[RatingRefund]
implicit val CorresAlarmHandler = Macros.handler[CorresAlarm]
implicit val ColorBSONHandler = new BSONHandler[BSONBoolean, chess.Color] {
def read(b: BSONBoolean) = chess.Color(b.value)
@ -80,6 +81,7 @@ private object BSONHandlers {
case x: RatingRefund => RatingRefundHandler.write(x)
case ReportedBanned => $empty
case CoachReview => $empty
case x: CorresAlarm => CorresAlarmHandler.write(x)
}
} ++ $doc("type" -> notificationContent.key)
@ -115,6 +117,7 @@ private object BSONHandlers {
case "ratingRefund" => RatingRefundHandler read reader.doc
case "reportedBanned" => ReportedBanned
case "coachReview" => CoachReview
case "corresAlarm" => CorresAlarmHandler read reader.doc
}
def writes(writer: Writer, n: NotificationContent): dsl.Bdoc = writeNotificationContent(n)

View file

@ -22,17 +22,25 @@ final class Env(
repo = repo)
// api actor
system.actorOf(Props(new Actor {
system.lilaBus.subscribe(system.actorOf(Props(new Actor {
def receive = {
case lila.hub.actorApi.notify.Notified(userId) =>
api markAllRead Notification.Notifies(userId)
case lila.game.actorApi.CorresAlarmEvent(pov) => pov.player.userId ?? { userId =>
api addNotification Notification.make(
Notification.Notifies(userId),
CorresAlarm(
gameId = pov.gameId,
opponent = lila.game.Namer.playerString(pov.opponent)(getLightUser)))
}
}
}), name = ActorName)
}), name = ActorName), 'corresAlarm)
}
object Env {
lazy val current = "notify" boot new Env(db = lila.db.Env.current,
lazy val current = "notify" boot new Env(
db = lila.db.Env.current,
config = lila.common.PlayApp loadConfig "notify",
getLightUser = lila.user.Env.current.lightUser,
system = lila.common.PlayApp.system)

View file

@ -46,6 +46,9 @@ final class JSONHandlers(
case RatingRefund(perf, points) => Json.obj(
"perf" -> perf,
"points" -> points)
case CorresAlarm(gameId, opponent) => Json.obj(
"id" -> gameId,
"op" -> opponent)
}
}

View file

@ -35,7 +35,8 @@ object Notification {
sealed abstract class NotificationContent(val key: String)
case class MentionedInThread(mentionedBy: MentionedInThread.MentionedBy,
case class MentionedInThread(
mentionedBy: MentionedInThread.MentionedBy,
topic: MentionedInThread.Topic,
topidId: MentionedInThread.TopicId,
category: MentionedInThread.Category,
@ -49,7 +50,8 @@ object MentionedInThread {
case class PostId(value: String) extends AnyVal with StringValue
}
case class InvitedToStudy(invitedBy: InvitedToStudy.InvitedBy,
case class InvitedToStudy(
invitedBy: InvitedToStudy.InvitedBy,
studyName: InvitedToStudy.StudyName,
studyId: InvitedToStudy.StudyId) extends NotificationContent("invitedStudy")
@ -122,3 +124,7 @@ case object CoachReview extends NotificationContent("coachReview")
case class PlanStart(userId: String) extends NotificationContent("planStart")
case class PlanExpire(userId: String) extends NotificationContent("planExpire")
case class CorresAlarm(
gameId: lila.game.Game.ID,
opponent: String) extends NotificationContent("corresAlarm")

View file

@ -47,12 +47,12 @@ final class Env(
system.lilaBus.subscribe(system.actorOf(Props(new Actor {
import akka.pattern.pipe
def receive = {
case lila.game.actorApi.FinishGame(game, _, _) => pushApi finish game
case move: lila.hub.actorApi.round.MoveEvent => pushApi move move
case lila.message.Event.NewMessage(t, p) => pushApi newMessage (t, p)
case lila.challenge.Event.Create(c) => pushApi challengeCreate c
case lila.challenge.Event.Accept(c, joinerId) => pushApi.challengeAccept(c, joinerId)
case lila.hub.actorApi.round.CorresAlarmEvent(id) => pushApi corresAlarm id
case lila.game.actorApi.FinishGame(game, _, _) => pushApi finish game
case move: lila.hub.actorApi.round.MoveEvent => pushApi move move
case lila.message.Event.NewMessage(t, p) => pushApi newMessage (t, p)
case lila.challenge.Event.Create(c) => pushApi challengeCreate c
case lila.challenge.Event.Accept(c, joinerId) => pushApi.challengeAccept(c, joinerId)
case lila.game.actorApi.CorresAlarmEvent(pov) => pushApi corresAlarm pov
}
})), 'finishGame, 'moveEvent, 'newMessage, 'challenge, 'corresAlarm)
}

View file

@ -78,29 +78,23 @@ private final class PushApi(
}
}
def corresAlarm(gameId: String): Funit = GameRepo game gameId flatMap {
_ ?? { game =>
val pov = Pov(game, game.turnColor)
game.player(pov.color).userId ?? { userId =>
IfAway(pov) {
pushToAll(userId, _.corresAlarm, PushApi.Data(
title = "Time is almost up!",
body = s"You are about to lose on time against ${opponentName(pov)}",
stacking = Stacking.GameMove,
payload = Json.obj(
"userId" -> userId,
"userData" -> Json.obj(
"type" -> "corresAlarm",
"gameId" -> game.id,
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(game.toChess.board),
"lastMove" -> game.castleLastMoveTime.lastMoveString,
"secondsLeft" -> pov.remainingSeconds))))
}
}
def corresAlarm(pov: Pov): Funit =
pov.player.userId ?? { userId =>
pushToAll(userId, _.corresAlarm, PushApi.Data(
title = "Time is almost up!",
body = s"You are about to lose on time against ${opponentName(pov)}",
stacking = Stacking.GameMove,
payload = Json.obj(
"userId" -> userId,
"userData" -> Json.obj(
"type" -> "corresAlarm",
"gameId" -> pov.gameId,
"fullId" -> pov.fullId,
"color" -> pov.color.name,
"fen" -> Forsyth.exportBoard(pov.game.toChess.board),
"lastMove" -> pov.game.castleLastMoveTime.lastMoveString,
"secondsLeft" -> pov.remainingSeconds))))
}
}
def newMessage(t: Thread, p: Post): Funit =
lightUser(t.senderOf(p)) ?? { sender =>

View file

@ -1,15 +1,21 @@
package lila.round
import akka.actor._
import akka.pattern.ask
import org.joda.time.DateTime
import play.api.libs.iteratee._
import reactivemongo.api._
import scala.concurrent.duration._
import lila.db.dsl._
import lila.game.GameRepo
import lila.hub.actorApi.map.Ask
import lila.hub.actorApi.round.IsOnGame
import lila.game.{ GameRepo, Pov }
import makeTimeout.short
private final class CorresAlarm(coll: Coll) extends Actor {
private final class CorresAlarm(
coll: Coll,
roundSocketHub: ActorSelection) extends Actor {
object Schedule
object Run
@ -45,12 +51,17 @@ private final class CorresAlarm(coll: Coll) extends Actor {
)).cursor[Alarm](ReadPreference.secondaryPreferred)
.enumerator(100, Cursor.ContOnError())
.|>>>(Iteratee.foldM[Alarm, Int](0) {
case (count, alarm) => {
context.system.lilaBus.publish(
lila.hub.actorApi.round.CorresAlarmEvent(alarm._id),
'corresAlarm)
coll.remove($id(alarm._id))
} inject (count + 1)
case (count, alarm) => GameRepo.game(alarm._id).flatMap {
_ ?? { game =>
val pov = Pov(game, game.turnColor)
roundSocketHub ? Ask(pov.gameId, IsOnGame(pov.color)) mapTo manifest[Boolean] addEffect {
case true => // already looking at the game
case false => context.system.lilaBus.publish(
lila.game.actorApi.CorresAlarmEvent(pov),
'corresAlarm)
}
}
} >> coll.remove($id(alarm._id)) inject (count + 1)
})
.chronometer.mon(_.round.alarm.time).result
.addEffect(c => lila.mon.round.alarm.count(c))

View file

@ -193,11 +193,11 @@ final class Env(
scheduler.message(2.1 seconds)(roundMap -> actorApi.GetNbRounds)
system.actorOf(
Props(classOf[Titivate], roundMap, hub.actor.bookmark, hub.actor.chat),
Props(new Titivate(roundMap, hub.actor.bookmark, hub.actor.chat)),
name = "titivate")
system.lilaBus.subscribe(system.actorOf(
Props(classOf[CorresAlarm], db(CollectionAlarm)),
Props(new CorresAlarm(db(CollectionAlarm), hub.socket.round)),
name = "corres-alarm"), 'moveEvent, 'finishGame)
lazy val takebacker = new Takebacker(

View file

@ -327,7 +327,7 @@ object ApplicationBuild extends Build {
reactivemongo.driver, reactivemongo.iteratees)
)
lazy val notifyModule = project("notify", Seq(common, db, user, hub, relation)).settings(
lazy val notifyModule = project("notify", Seq(common, db, game, user, hub, relation)).settings(
libraryDependencies ++= provided(play.api, reactivemongo.driver)
)

View file

@ -255,6 +255,22 @@ var handlers = {
return 'Refund: ' + n.content.points + ' ' + n.content.perf + ' rating points.'
}
},
corresAlarm: {
html: function(notification) {
var url = '/' + notification.content.id;
return genericNotification(notification, url, ';', [
m('span', [
m('strong', 'Time is almost up!'),
drawTime(notification)
]),
m('span', 'Game vs ' + notification.content.op)
]);
},
text: function(n) {
return 'Time is almost up!';
}
},
};
function drawNotification(notification) {