user activity feed API - WIP - for #3473
parent
7200587c11
commit
278db0a245
10
README.md
10
README.md
|
@ -314,6 +314,16 @@ name | type | default | description
|
|||
|
||||
(1) All game statuses: https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala#L16-L28
|
||||
|
||||
### `GET /api/user/<username>/activity` fetch recent user activity
|
||||
|
||||
This API is a work in progress
|
||||
|
||||
```
|
||||
> curl https://lichess.org/api/user/thibault/activityy
|
||||
```
|
||||
|
||||
Returns data to generate the activity feed on https://lichess.org/@/thibault
|
||||
|
||||
### `GET /api/games/vs/<username>/<username>` fetch games between 2 users
|
||||
|
||||
```
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package controllers
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import ornicar.scalalib.Zero
|
||||
import play.api.libs.json._
|
||||
import play.api.mvc._
|
||||
import scala.concurrent.duration._
|
||||
import ornicar.scalalib.Zero
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app._
|
||||
|
@ -273,6 +273,25 @@ object Api extends LilaController {
|
|||
Ok.chunked(Env.game.stream.startedByUserIds(userIds))
|
||||
}
|
||||
|
||||
def activity(name: String) = ApiRequest { implicit ctx =>
|
||||
val cost = 50
|
||||
val ip = HTTPRequest lastRemoteAddress ctx.req
|
||||
UserGamesRateLimitPerIP(ip, cost = cost) {
|
||||
UserGamesRateLimitPerUA(~HTTPRequest.userAgent(ctx.req), cost = cost, msg = ip.value) {
|
||||
UserGamesRateLimitGlobal("-", cost = cost, msg = ip.value) {
|
||||
lila.mon.api.activity.cost(cost)
|
||||
lila.user.UserRepo named name flatMap {
|
||||
_ ?? { user =>
|
||||
Env.activity.read.recent(user) flatMap {
|
||||
_.map { Env.activity.jsonView.apply _ }.sequenceFu
|
||||
}
|
||||
}
|
||||
} map toApiResult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait ApiResult
|
||||
case class Data(json: JsValue) extends ApiResult
|
||||
case object NoData extends ApiResult
|
||||
|
|
|
@ -10,7 +10,7 @@ net {
|
|||
ip = "5.196.91.160"
|
||||
asset {
|
||||
domain = ${net.domain}
|
||||
version = 1848
|
||||
version = 1849
|
||||
}
|
||||
email = "contact@lichess.org"
|
||||
crawlable = false
|
||||
|
|
|
@ -473,6 +473,7 @@ GET /api/user controllers.Api.users
|
|||
POST /api/users controllers.Api.usersByIds
|
||||
GET /api/user/:name controllers.Api.user(name: String)
|
||||
GET /api/user/:name/games controllers.Api.userGames(name: String)
|
||||
GET /api/user/:name/activity controllers.Api.activity(name: String)
|
||||
GET /api/game/:id controllers.Api.game(id: String)
|
||||
GET /api/games/vs/:u1/:u2 controllers.Api.gamesVs(u1: String, u2: String)
|
||||
GET /api/games/team/:teamId controllers.Api.gamesVsTeam(teamId: String)
|
||||
|
|
|
@ -14,7 +14,9 @@ final class Env(
|
|||
postApi: lila.forum.PostApi,
|
||||
simulApi: lila.simul.SimulApi,
|
||||
studyApi: lila.study.StudyApi,
|
||||
tourLeaderApi: lila.tournament.LeaderboardApi
|
||||
lightUserApi: lila.user.LightUserApi,
|
||||
tourLeaderApi: lila.tournament.LeaderboardApi,
|
||||
getTourName: lila.tournament.Tournament.ID => Option[String]
|
||||
) {
|
||||
|
||||
private val activityColl = db(config getString "collection.activity")
|
||||
|
@ -33,6 +35,11 @@ final class Env(
|
|||
tourLeaderApi = tourLeaderApi
|
||||
)
|
||||
|
||||
lazy val jsonView = new JsonView(
|
||||
lightUserApi = lightUserApi,
|
||||
getTourName = getTourName
|
||||
)
|
||||
|
||||
system.lilaBus.subscribe(
|
||||
system.actorOf(Props(new Actor {
|
||||
def receive = {
|
||||
|
@ -66,6 +73,8 @@ object Env {
|
|||
postApi = lila.forum.Env.current.postApi,
|
||||
simulApi = lila.simul.Env.current.api,
|
||||
studyApi = lila.study.Env.current.api,
|
||||
tourLeaderApi = lila.tournament.Env.current.leaderboardApi
|
||||
lightUserApi = lila.user.Env.current.lightUserApi,
|
||||
tourLeaderApi = lila.tournament.Env.current.leaderboardApi,
|
||||
getTourName = lila.tournament.Env.current.cached.name _
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package lila.activity
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.json._
|
||||
|
||||
import lila.common.Iso
|
||||
import lila.common.PimpedJson._
|
||||
import lila.rating.PerfType
|
||||
import lila.tournament.JsonView._
|
||||
import lila.tournament.LeaderboardApi.{ Entry => TourEntry, Ratio => TourRatio }
|
||||
import lila.tournament.Tournament
|
||||
import lila.tournament.{ Cached => TourCached }
|
||||
|
||||
import activities._
|
||||
import model._
|
||||
|
||||
final class JsonView(
|
||||
lightUserApi: lila.user.LightUserApi,
|
||||
getTourName: Tournament.ID => Option[String]
|
||||
) {
|
||||
|
||||
private object Writers {
|
||||
implicit val perfTypeWrites = Writes[PerfType](pt => JsString(pt.key))
|
||||
implicit val ratingWrites = intIsoWriter(Iso.int[Rating](Rating.apply, _.value))
|
||||
implicit val ratingProgWrites = Json.writes[RatingProg]
|
||||
implicit val scoreWrites = Json.writes[Score]
|
||||
implicit val gamesWrites = OWrites[Games] { games =>
|
||||
JsObject { games.value.map { case (pt, score) => pt.key -> scoreWrites.writes(score) } }
|
||||
}
|
||||
implicit val dateWrites = Writes[DateTime] { date =>
|
||||
JsNumber(date.getSeconds)
|
||||
}
|
||||
implicit val variantWrites: Writes[chess.variant.Variant] = Writes { v =>
|
||||
JsString(v.key)
|
||||
}
|
||||
implicit val tourRatioWrites = intIsoWriter(Iso.int[TourRatio](r => TourRatio(r.toDouble), _.value.toInt))
|
||||
implicit val tourEntryWrites = OWrites[TourEntry] { e =>
|
||||
Json.obj(
|
||||
"tournament" -> Json.obj(
|
||||
"id" -> e.tourId,
|
||||
"name" -> ~getTourName(e.tourId)
|
||||
),
|
||||
"nbGames" -> e.nbGames,
|
||||
"score" -> e.score,
|
||||
"rank" -> e.rank,
|
||||
"rankRatio" -> e.rankRatio
|
||||
)
|
||||
}
|
||||
implicit val toursWrites = Json.writes[ActivityView.Tours]
|
||||
implicit val puzzlesWrites = Json.writes[Puzzles]
|
||||
}
|
||||
import Writers._
|
||||
|
||||
def apply(a: ActivityView): Fu[JsObject] = fuccess {
|
||||
Json.obj()
|
||||
.add("games", a.games map gamesWrites.writes)
|
||||
.add("puzzles", a.puzzles map puzzlesWrites.writes)
|
||||
.add("tournaments", a.tours map toursWrites.writes)
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ object PimpedJson {
|
|||
def stringAnyValWriter[O](f: O => String): Writes[O] = anyValWriter[O, String](f)
|
||||
|
||||
def stringIsoWriter[O](iso: Iso[String, O]): Writes[O] = anyValWriter[O, String](iso.to)
|
||||
def intIsoWriter[O](iso: Iso[Int, O]): Writes[O] = anyValWriter[O, Int](iso.to)
|
||||
|
||||
def stringIsoReader[O](iso: Iso[String, O]): Reads[O] = Reads.of[String] map iso.from
|
||||
|
||||
|
|
|
@ -464,6 +464,9 @@ object mon {
|
|||
object game {
|
||||
val cost = incX("api.game.cost")
|
||||
}
|
||||
object activity {
|
||||
val cost = incX("api.activity.cost")
|
||||
}
|
||||
}
|
||||
object export {
|
||||
object pgn {
|
||||
|
|
|
@ -5,9 +5,8 @@ import org.joda.time.format.ISODateTimeFormat
|
|||
import play.api.libs.json._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import chess.Clock.{ Config => TournamentClock }
|
||||
import lila.common.PimpedJson._
|
||||
import lila.common.LightUser
|
||||
import lila.common.PimpedJson._
|
||||
import lila.game.{ GameRepo, Pov }
|
||||
import lila.quote.Quote.quoteWriter
|
||||
import lila.rating.PerfType
|
||||
|
@ -63,7 +62,7 @@ final class JsonView(
|
|||
"perf" -> tour.perfType,
|
||||
"nbPlayers" -> tour.nbPlayers,
|
||||
"minutes" -> tour.minutes,
|
||||
"clock" -> clockJson(tour.clock),
|
||||
"clock" -> tour.clock,
|
||||
"verdicts" -> verdicts,
|
||||
"variant" -> tour.variant.key,
|
||||
"isStarted" -> tour.isStarted,
|
||||
|
@ -343,10 +342,12 @@ object JsonView {
|
|||
"speed" -> s.speed.name
|
||||
)
|
||||
|
||||
private[tournament] def clockJson(clock: TournamentClock) = Json.obj(
|
||||
"limit" -> clock.limitSeconds,
|
||||
"increment" -> clock.incrementSeconds
|
||||
)
|
||||
implicit val clockWrites: OWrites[chess.Clock.Config] = OWrites { clock =>
|
||||
Json.obj(
|
||||
"limit" -> clock.limitSeconds,
|
||||
"increment" -> clock.incrementSeconds
|
||||
)
|
||||
}
|
||||
|
||||
private[tournament] def positionJson(s: chess.StartingPosition) = Json.obj(
|
||||
"eco" -> s.eco,
|
||||
|
|
|
@ -84,7 +84,7 @@ object LeaderboardApi {
|
|||
case class Entry(
|
||||
id: String, // same as tournament player id
|
||||
userId: String,
|
||||
tourId: String,
|
||||
tourId: Tournament.ID,
|
||||
nbGames: Int,
|
||||
score: Int,
|
||||
rank: Int,
|
||||
|
|
|
@ -27,7 +27,7 @@ final class ScheduleJsonView(lightUser: LightUser.Getter) {
|
|||
"createdBy" -> tour.createdBy,
|
||||
"system" -> tour.system.toString.toLowerCase,
|
||||
"minutes" -> tour.minutes,
|
||||
"clock" -> clockJson(tour.clock),
|
||||
"clock" -> tour.clock,
|
||||
"position" -> tour.position.some.filterNot(_.initial).map(positionJson),
|
||||
"rated" -> tour.mode.rated,
|
||||
"fullName" -> tour.fullName,
|
||||
|
|
Loading…
Reference in New Issue