patron notifications
This commit is contained in:
parent
11c6339888
commit
572baa4b19
|
@ -14,9 +14,9 @@ object Plan extends LilaController {
|
|||
ctx.me.fold(indexAnon) { me =>
|
||||
import lila.plan.PlanApi.SyncResult._
|
||||
Env.plan.api.sync(me) flatMap {
|
||||
case ReloadUser => Redirect(routes.Plan.index).fuccess
|
||||
case Synced(Some(patron)) => indexPatron(me, patron)
|
||||
case Synced(None) => indexFreeUser(me)
|
||||
case ReloadUser => Redirect(routes.Plan.index).fuccess
|
||||
case Synced(Some(patron)) if patron.isDefined => indexPatron(me, patron)
|
||||
case Synced(_) => indexFreeUser(me)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,20 +93,22 @@ package timeline {
|
|||
case class ReloadTimeline(user: String)
|
||||
|
||||
sealed abstract class Atom(val channel: String, val okForKid: Boolean)
|
||||
case class Follow(u1: String, u2: String) extends Atom(s"follow", true)
|
||||
case class TeamJoin(userId: String, teamId: String) extends Atom(s"teamJoin", false)
|
||||
case class TeamCreate(userId: String, teamId: String) extends Atom(s"teamCreate", false)
|
||||
case class Follow(u1: String, u2: String) extends Atom("follow", true)
|
||||
case class TeamJoin(userId: String, teamId: String) extends Atom("teamJoin", false)
|
||||
case class TeamCreate(userId: String, teamId: String) extends Atom("teamCreate", false)
|
||||
case class ForumPost(userId: String, topicId: Option[String], topicName: String, postId: String) extends Atom(s"forum:${~topicId}", false)
|
||||
case class NoteCreate(from: String, to: String) extends Atom(s"note", false)
|
||||
case class TourJoin(userId: String, tourId: String, tourName: String) extends Atom(s"tournament", true)
|
||||
case class QaQuestion(userId: String, id: Int, title: String) extends Atom(s"qa", true)
|
||||
case class QaAnswer(userId: String, id: Int, title: String, answerId: Int) extends Atom(s"qa", true)
|
||||
case class QaComment(userId: String, id: Int, title: String, commentId: String) extends Atom(s"qa", true)
|
||||
case class GameEnd(playerId: String, opponent: Option[String], win: Option[Boolean], perf: String) extends Atom(s"gameEnd", true)
|
||||
case class SimulCreate(userId: String, simulId: String, simulName: String) extends Atom(s"simulCreate", true)
|
||||
case class SimulJoin(userId: String, simulId: String, simulName: String) extends Atom(s"simulJoin", true)
|
||||
case class StudyCreate(userId: String, studyId: String, studyName: String) extends Atom(s"studyCreate", true)
|
||||
case class StudyLike(userId: String, studyId: String, studyName: String) extends Atom(s"studyLike", true)
|
||||
case class NoteCreate(from: String, to: String) extends Atom("note", false)
|
||||
case class TourJoin(userId: String, tourId: String, tourName: String) extends Atom("tournament", true)
|
||||
case class QaQuestion(userId: String, id: Int, title: String) extends Atom("qa", true)
|
||||
case class QaAnswer(userId: String, id: Int, title: String, answerId: Int) extends Atom("qa", true)
|
||||
case class QaComment(userId: String, id: Int, title: String, commentId: String) extends Atom("qa", true)
|
||||
case class GameEnd(playerId: String, opponent: Option[String], win: Option[Boolean], perf: String) extends Atom("gameEnd", true)
|
||||
case class SimulCreate(userId: String, simulId: String, simulName: String) extends Atom("simulCreate", true)
|
||||
case class SimulJoin(userId: String, simulId: String, simulName: String) extends Atom("simulJoin", true)
|
||||
case class StudyCreate(userId: String, studyId: String, studyName: String) extends Atom("studyCreate", true)
|
||||
case class StudyLike(userId: String, studyId: String, studyName: String) extends Atom("studyLike", true)
|
||||
// case class PlanStart(userId: String) extends Atom("planStart", true)
|
||||
// case class PlanExpire(userId: String) extends Atom("planExpire", true)
|
||||
|
||||
object propagation {
|
||||
sealed trait Propagation
|
||||
|
|
|
@ -51,6 +51,9 @@ private object BSONHandlers {
|
|||
implicit val GameEndWinHandler = booleanAnyValHandler[GameEnd.Win](_.value, GameEnd.Win.apply)
|
||||
implicit val GameEndHandler = Macros.handler[GameEnd]
|
||||
|
||||
implicit val PlanStartHandler = Macros.handler[PlanStart]
|
||||
implicit val PlanExpireHandler = Macros.handler[PlanExpire]
|
||||
|
||||
implicit val ColorBSONHandler = new BSONHandler[BSONBoolean, chess.Color] {
|
||||
def read(b: BSONBoolean) = chess.Color(b.value)
|
||||
def write(c: chess.Color) = BSONBoolean(c.white)
|
||||
|
@ -70,6 +73,8 @@ private object BSONHandlers {
|
|||
case b: NewBlogPost => NewBlogPostHandler.write(b)
|
||||
case LimitedTournamentInvitation => $empty
|
||||
case x: GameEnd => GameEndHandler.write(x)
|
||||
case x: PlanStart => PlanStartHandler.write(x)
|
||||
case x: PlanExpire => PlanExpireHandler.write(x)
|
||||
}
|
||||
} ++ $doc("type" -> notificationContent.key)
|
||||
|
||||
|
@ -100,11 +105,11 @@ private object BSONHandlers {
|
|||
case "newBlogPost" => NewBlogPostHandler read reader.doc
|
||||
case "u" => LimitedTournamentInvitation
|
||||
case "gameEnd" => GameEndHandler read reader.doc
|
||||
case "planStart" => PlanStartHandler read reader.doc
|
||||
case "planExpire" => PlanExpireHandler read reader.doc
|
||||
}
|
||||
|
||||
def writes(writer: Writer, n: NotificationContent): dsl.Bdoc = {
|
||||
writeNotificationContent(n)
|
||||
}
|
||||
def writes(writer: Writer, n: NotificationContent): dsl.Bdoc = writeNotificationContent(n)
|
||||
}
|
||||
|
||||
implicit val NotificationBSONHandler = Macros.handler[Notification]
|
||||
|
|
|
@ -41,6 +41,8 @@ final class JSONHandlers(
|
|||
"id" -> gameId.value,
|
||||
"opponent" -> opponentId.map(_.value).flatMap(getLightUser),
|
||||
"win" -> win.map(_.value))
|
||||
case _: PlanStart => Json.obj()
|
||||
case _: PlanExpire => Json.obj()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,9 +59,9 @@ final class JSONHandlers(
|
|||
|
||||
implicit val newNotificationWrites: Writes[NewNotification] = new Writes[NewNotification] {
|
||||
|
||||
def writes(newNotification: NewNotification) = {
|
||||
Json.obj("notification" -> newNotification.notification, "unread" -> newNotification.unreadNotifications)
|
||||
}
|
||||
def writes(newNotification: NewNotification) = Json.obj(
|
||||
"notification" -> newNotification.notification,
|
||||
"unread" -> newNotification.unreadNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,3 +113,6 @@ object GameEnd {
|
|||
case class OpponentId(value: String) extends AnyVal
|
||||
case class Win(value: Boolean) extends AnyVal
|
||||
}
|
||||
|
||||
case class PlanStart(userId: String) extends NotificationContent("planStart")
|
||||
case class PlanExpire(userId: String) extends NotificationContent("planExpire")
|
||||
|
|
|
@ -9,6 +9,8 @@ import lila.common.PimpedConfig._
|
|||
final class Env(
|
||||
config: Config,
|
||||
db: lila.db.Env,
|
||||
hub: lila.hub.Env,
|
||||
notifyApi: lila.notify.NotifyApi,
|
||||
bus: lila.common.Bus,
|
||||
scheduler: lila.common.Scheduler) {
|
||||
|
||||
|
@ -24,15 +26,20 @@ final class Env(
|
|||
publicKey = stripePublicKey,
|
||||
secretKey = config getString "stripe.keys.secret"))
|
||||
|
||||
private lazy val notifier = new PlanNotifier(
|
||||
notifyApi = notifyApi,
|
||||
timeline = hub.actor.timeline)
|
||||
|
||||
lazy val api = new PlanApi(
|
||||
stripeClient,
|
||||
patronColl = patronColl,
|
||||
chargeColl = db(CollectionCharge),
|
||||
notifier = notifier,
|
||||
bus)
|
||||
|
||||
private lazy val webhookHandler = new WebhookHandler(api)
|
||||
|
||||
private lazy val expiration = new Expiration(patronColl)
|
||||
private lazy val expiration = new Expiration(patronColl, notifier)
|
||||
|
||||
scheduler.future(10 seconds, "Expire patron plans") {
|
||||
expiration.run
|
||||
|
@ -46,6 +53,8 @@ object Env {
|
|||
lazy val current: Env = "plan" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "plan",
|
||||
db = lila.db.Env.current,
|
||||
hub = lila.hub.Env.current,
|
||||
notifyApi = lila.notify.Env.current.api,
|
||||
bus = lila.common.PlayApp.system.lilaBus,
|
||||
scheduler = lila.common.PlayApp.scheduler)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import lila.user.{ User, UserRepo }
|
|||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
private final class Expiration(patronColl: Coll) {
|
||||
private final class Expiration(patronColl: Coll, notifier: PlanNotifier) {
|
||||
|
||||
import BsonHandlers._
|
||||
import PatronHandlers._
|
||||
|
@ -21,7 +21,8 @@ private final class Expiration(patronColl: Coll) {
|
|||
private def disableUserPlanOf(patron: Patron): Funit =
|
||||
UserRepo byId patron.userId flatMap {
|
||||
_ ?? { user =>
|
||||
UserRepo.setPlan(user, user.plan.disable)
|
||||
UserRepo.setPlan(user, user.plan.disable) >>
|
||||
notifier.onExpire(user)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ case class Patron(
|
|||
def removePayPal = copy(
|
||||
payPal = none,
|
||||
expiresAt = none)
|
||||
|
||||
def isDefined = stripe.isDefined || payPal.isDefined
|
||||
}
|
||||
|
||||
object Patron {
|
||||
|
|
|
@ -9,6 +9,7 @@ final class PlanApi(
|
|||
stripeClient: StripeClient,
|
||||
patronColl: Coll,
|
||||
chargeColl: Coll,
|
||||
notifier: PlanNotifier,
|
||||
bus: lila.common.Bus) {
|
||||
|
||||
import BsonHandlers._
|
||||
|
@ -90,7 +91,8 @@ final class PlanApi(
|
|||
payPal = Patron.PayPal(email, subId, DateTime.now).some,
|
||||
lastLevelUp = DateTime.now
|
||||
).expireInOneMonth) >>
|
||||
UserRepo.setPlan(user, lila.user.Plan.start)
|
||||
UserRepo.setPlan(user, lila.user.Plan.start) >>
|
||||
notifier.onStart(user)
|
||||
case Some(patron) if patron.canLevelUp =>
|
||||
patronColl.update($id(patron.id), patron.levelUpNow.expireInOneMonth) >>
|
||||
UserRepo.setPlan(user, user.plan.incMonths)
|
||||
|
@ -206,7 +208,8 @@ final class PlanApi(
|
|||
private def createCustomer(user: User, data: Checkout, plan: StripePlan): Fu[StripeCustomer] =
|
||||
stripeClient.createCustomer(user, data, plan) flatMap { customer =>
|
||||
saveStripePatron(user, customer.id, data.isMonthly) >>
|
||||
UserRepo.setPlan(user, lila.user.Plan.start) >>-
|
||||
UserRepo.setPlan(user, lila.user.Plan.start) >>
|
||||
notifier.onStart(user) >>-
|
||||
logger.info(s"Create ${user.username} customer $customer") inject customer
|
||||
}
|
||||
|
||||
|
|
60
modules/plan/src/main/PlanNotifier.scala
Normal file
60
modules/plan/src/main/PlanNotifier.scala
Normal file
|
@ -0,0 +1,60 @@
|
|||
package lila.plan
|
||||
|
||||
import lila.hub.actorApi.timeline.{ Propagate }
|
||||
import lila.notify.Notification.Notifies
|
||||
import lila.notify.{ Notification, NotifyApi }
|
||||
import lila.user.User
|
||||
|
||||
import akka.actor.ActorSelection
|
||||
|
||||
private[plan] final class PlanNotifier(
|
||||
notifyApi: NotifyApi,
|
||||
timeline: ActorSelection) {
|
||||
|
||||
def onStart(user: User) =
|
||||
notifyApi.addNotification(Notification(
|
||||
Notifies(user.id),
|
||||
lila.notify.PlanStart(user.id)
|
||||
))
|
||||
|
||||
def onExpire(user: User) =
|
||||
notifyApi.addNotification(Notification(
|
||||
Notifies(user.id),
|
||||
lila.notify.PlanExpire(user.id)
|
||||
))
|
||||
|
||||
// private[qa] def createQuestion(q: Question, u: User) {
|
||||
// val msg = Propagate(QaQuestion(u.id, q.id, q.title))
|
||||
// timeline ! (msg toFollowersOf u.id)
|
||||
// }
|
||||
|
||||
// private[qa] def createAnswer(q: Question, a: Answer, u: User) {
|
||||
// val msg = Propagate(QaAnswer(u.id, q.id, q.title, a.id))
|
||||
// timeline ! (msg toFollowersOf u.id toUser q.userId exceptUser u.id)
|
||||
// if (u.id != q.userId) notifyAsker(q, a)
|
||||
// }
|
||||
|
||||
// private[qa] def notifyAsker(q: Question, a: Answer) = {
|
||||
// import lila.notify.QaAnswer
|
||||
// import lila.common.String.shorten
|
||||
|
||||
// val answererId = QaAnswer.AnswererId(a.userId)
|
||||
// val question = QaAnswer.Question(id = q.id, slug = q.slug, title = shorten(q.title, 80))
|
||||
// val answerId = QaAnswer.AnswerId(a.id)
|
||||
|
||||
// val notificationContent = QaAnswer(answererId, question, answerId)
|
||||
// val notification = Notification(Notifies(q.userId), notificationContent)
|
||||
|
||||
// notifyApi.addNotification(notification)
|
||||
// }
|
||||
|
||||
// private[qa] def createQuestionComment(q: Question, c: Comment, u: User) {
|
||||
// val msg = Propagate(QaComment(u.id, q.id, q.title, c.id))
|
||||
// timeline ! (msg toFollowersOf u.id toUser q.userId exceptUser u.id)
|
||||
// }
|
||||
|
||||
// private[qa] def createAnswerComment(q: Question, a: Answer, c: Comment, u: User) {
|
||||
// val msg = Propagate(QaComment(u.id, q.id, q.title, c.id))
|
||||
// timeline ! (msg toFollowersOf u.id toUser a.userId exceptUser u.id)
|
||||
// }
|
||||
}
|
|
@ -3,14 +3,13 @@ package lila.qa
|
|||
import com.typesafe.config.Config
|
||||
import lila.common.DetectLanguage
|
||||
import lila.common.PimpedConfig._
|
||||
import lila.notify.NotifyApi
|
||||
|
||||
final class Env(
|
||||
config: Config,
|
||||
hub: lila.hub.Env,
|
||||
detectLanguage: DetectLanguage,
|
||||
mongoCache: lila.memo.MongoCache.Builder,
|
||||
notifyApi : NotifyApi,
|
||||
notifyApi: lila.notify.NotifyApi,
|
||||
db: lila.db.Env) {
|
||||
|
||||
private val CollectionQuestion = config getString "collection.question"
|
||||
|
|
|
@ -258,7 +258,7 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= provided(play.api, RM)
|
||||
)
|
||||
|
||||
lazy val plan = project("plan", Seq(common, user)).settings(
|
||||
lazy val plan = project("plan", Seq(common, user, notifyModule)).settings(
|
||||
libraryDependencies ++= provided(play.api, RM)
|
||||
)
|
||||
|
||||
|
|
|
@ -184,7 +184,35 @@ var handlers = {
|
|||
}
|
||||
return result + ' vs ' + userFullName(n.content.opponent);
|
||||
}
|
||||
}
|
||||
},
|
||||
planStart: {
|
||||
html: function(notification) {
|
||||
return genericNotification(notification, '/patron', '', [
|
||||
m('span', [
|
||||
m('strong', 'Thank you!'),
|
||||
drawTime(notification)
|
||||
]),
|
||||
m('span', 'You just became a lichess patron.')
|
||||
]);
|
||||
},
|
||||
text: function(n) {
|
||||
return 'You just became a lichess patron.';
|
||||
}
|
||||
},
|
||||
planExpire: {
|
||||
html: function(notification) {
|
||||
return genericNotification(notification, '/patron', '', [
|
||||
m('span', [
|
||||
m('strong', 'Patron account expired'),
|
||||
drawTime(notification)
|
||||
]),
|
||||
m('span', 'Please consider renewing it!')
|
||||
]);
|
||||
},
|
||||
text: function(n) {
|
||||
return 'Patron account expired';
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function drawNotification(notification) {
|
||||
|
|
Loading…
Reference in a new issue