Skip to content

Commit

Permalink
Merge branch 'master' into ply-alert
Browse files Browse the repository at this point in the history
  • Loading branch information
johndoknjas committed Dec 27, 2024
2 parents 7165c3a + 3da70d7 commit c823d9d
Show file tree
Hide file tree
Showing 69 changed files with 760 additions and 311 deletions.
2 changes: 1 addition & 1 deletion app/controllers/Study.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import lila.core.study.Order
import lila.study.JsonView.JsData
import lila.study.PgnDump.WithFlags
import lila.study.Study.WithChapter
import lila.study.actorApi.{ BecomeStudyAdmin, Who }
import lila.study.{ BecomeStudyAdmin, Who }
import lila.study.{ Chapter, Orders, Settings, Study as StudyModel, StudyForm }
import lila.tree.Node.partitionTreeJsonWriter
import com.fasterxml.jackson.core.JsonParseException
Expand Down
2 changes: 1 addition & 1 deletion modules/api/src/main/AccountClosure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class AccountClosure(
)

def close(u: User)(using me: Me): Funit = for
playbanned <- playbanApi.HasCurrentPlayban(u.id)
playbanned <- playbanApi.hasCurrentPlayban(u.id)
selfClose = me.is(u)
teacherClose = !selfClose && !Granter(_.CloseAccount) && Granter(_.Teacher)
modClose = !selfClose && Granter(_.CloseAccount)
Expand Down
8 changes: 4 additions & 4 deletions modules/common/src/main/mon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,10 @@ object mon:
timer("relay.fetch.time").withTags(relay(official, id, slug))
def syncTime(official: Boolean, id: RelayTourId, slug: String) =
timer("relay.sync.time").withTags(relay(official, id, slug))
def httpGet(code: Int, host: String, proxy: Option[String]) = timer("relay.http.get").withTags:
tags("code" -> code, "host" -> host, "proxy" -> proxy.getOrElse("none"))
val dedup = counter("relay.fetch.dedup").withoutTags()
def etag(hit: "hit" | "miss" | "first") = counter("relay.fetch.etag").withTag("hit", hit)
def httpGet(code: Int, host: String, etag: String, proxy: Option[String]) =
timer("relay.http.get").withTags:
tags("code" -> code.toLong, "host" -> host, "etag" -> etag, "proxy" -> proxy.getOrElse("none"))
val dedup = counter("relay.fetch.dedup").withoutTags()

object bot:
def moves(username: String) = counter("bot.moves").withTag("name", username)
Expand Down
9 changes: 1 addition & 8 deletions modules/i18n/src/main/LangList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object LangList extends lila.core.i18n.LangList:
Lang("af", "ZA") -> "Afrikaans",
Lang("an", "ES") -> "Aragonés",
Lang("ar", "SA") -> "العربية",
Lang("as", "IN") -> "অসমীয়া",
Lang("ast", "ES") -> "Asturianu",
Lang("av", "DA") -> "авар мацӀ",
Lang("az", "AZ") -> "Azərbaycanca",
Lang("be", "BY") -> "Беларуская",
Expand All @@ -37,7 +37,6 @@ object LangList extends lila.core.i18n.LangList:
Lang("fi", "FI") -> "Suomen kieli",
Lang("fo", "FO") -> "Føroyskt",
Lang("fr", "FR") -> "Français",
Lang("frp", "IT") -> "Arpitan",
Lang("fy", "NL") -> "Frysk",
Lang("ga", "IE") -> "Gaeilge",
Lang("gd", "GB") -> "Gàidhlig",
Expand All @@ -51,19 +50,16 @@ object LangList extends lila.core.i18n.LangList:
Lang("hy", "AM") -> "Հայերեն",
Lang("ia", "IA") -> "Interlingua",
Lang("id", "ID") -> "Bahasa Indonesia",
Lang("io", "EN") -> "Ido",
Lang("is", "IS") -> "Íslenska",
Lang("it", "IT") -> "Italiano",
Lang("ja", "JP") -> "日本語",
Lang("jbo", "EN") -> "Lojban",
Lang("jv", "ID") -> "Basa Jawa",
Lang("ka", "GE") -> "ქართული",
Lang("kab", "DZ") -> "Taqvaylit",
Lang("kk", "KZ") -> "қазақша",
Lang("kmr", "TR") -> "Kurdî (Kurmancî)",
Lang("kn", "IN") -> "ಕನ್ನಡ",
Lang("ko", "KR") -> "한국어",
Lang("ky", "KG") -> "кыргызча",
Lang("la", "LA") -> "Lingua Latina",
Lang("lb", "LU") -> "Lëtzebuergesch",
Lang("lt", "LT") -> "Lietuvių kalba",
Expand All @@ -85,7 +81,6 @@ object LangList extends lila.core.i18n.LangList:
Lang("ro", "RO") -> "Română",
Lang("ru", "RU") -> "русский язык",
Lang("ry", "UA") -> "Русинська бисїда",
Lang("sa", "IN") -> "संस्कृत",
Lang("sk", "SK") -> "Slovenčina",
Lang("sl", "SI") -> "Slovenščina",
Lang("so", "SO") -> "Af Soomaali",
Expand All @@ -94,7 +89,6 @@ object LangList extends lila.core.i18n.LangList:
Lang("sv", "SE") -> "Svenska",
Lang("sw", "KE") -> "Kiswahili",
Lang("ta", "IN") -> "தமிழ்",
Lang("tg", "TJ") -> "тоҷикӣ",
Lang("th", "TH") -> "ไทย",
Lang("tk", "TM") -> "Türkmençe",
Lang("tl", "PH") -> "Tagalog",
Expand All @@ -104,7 +98,6 @@ object LangList extends lila.core.i18n.LangList:
Lang("ur", "PK") -> "اُردُو",
Lang("uz", "UZ") -> "oʻzbekcha",
Lang("vi", "VN") -> "Tiếng Việt",
Lang("yo", "NG") -> "Yorùbá",
Lang("zh", "CN") -> "中文",
Lang("zh", "TW") -> "繁體中文",
Lang("zu", "ZA") -> "isiZulu"
Expand Down
2 changes: 1 addition & 1 deletion modules/playban/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ final class Env(
private val feedback = wire[PlaybanFeedback]

val api = wire[PlaybanApi]
export api.{ bansOf, HasCurrentPlayban, rageSitOf }
export api.{ bansOf, hasCurrentPlayban, rageSitOf }
2 changes: 1 addition & 1 deletion modules/playban/src/main/PlaybanApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ final class PlaybanApi(
.addEffect: ban =>
if ban.isEmpty then cleanUserIds.put(user.id)

val HasCurrentPlayban: lila.core.playban.HasCurrentPlayban = userId => currentBan(userId).map(_.isDefined)
val hasCurrentPlayban: lila.core.playban.HasCurrentPlayban = userId => currentBan(userId).map(_.isDefined)

val bansOf: lila.core.playban.BansOf = userIds =>
coll
Expand Down
2 changes: 1 addition & 1 deletion modules/practice/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ final class Env(

def getStudies: lila.core.practice.GetStudies = api.structure.getStudies

lila.common.Bus.subscribeFun("study") { case lila.study.actorApi.SaveStudy(study) =>
lila.common.Bus.subscribeFun("study") { case lila.study.SaveStudy(study) =>
api.structure.onSave(study)
}
13 changes: 9 additions & 4 deletions modules/relay/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ final class Env(

private lazy val sync = wire[RelaySync]

private lazy val proxy = wire[RelayProxy]
private def selectProxy: ProxySelector = proxy.select

private lazy val httpClient = wire[HttpClient]

private lazy val formatApi = wire[RelayFormatApi]

private lazy val delay = wire[RelayDelay]
Expand Down Expand Up @@ -139,19 +144,19 @@ final class Env(
"study" -> { case lila.core.study.RemoveStudy(studyId) =>
api.onStudyRemove(studyId)
},
"relayToggle" -> { case lila.study.actorApi.RelayToggle(id, v, who) =>
"relayToggle" -> { case lila.study.RelayToggle(id, v, who) =>
studyApi
.isContributor(id, who.u)
.foreach:
_.so(api.requestPlay(id.into(RelayRoundId), v, "manual toggle"))
},
"kickStudy" -> { case lila.study.actorApi.Kick(studyId, userId, who) =>
"kickStudy" -> { case lila.study.Kick(studyId, userId, who) =>
roundRepo.tourIdByStudyId(studyId).flatMapz(api.kickBroadcast(userId, _, who))
},
"adminStudy" -> { case lila.study.actorApi.BecomeStudyAdmin(studyId, me) =>
"adminStudy" -> { case lila.study.BecomeStudyAdmin(studyId, me) =>
api.becomeStudyAdmin(studyId, me)
},
"isOfficialRelay" -> { case lila.study.actorApi.IsOfficialRelay(studyId, promise) =>
"isOfficialRelay" -> { case lila.study.IsOfficialRelay(studyId, promise) =>
promise.completeWith(api.isOfficial(studyId.into(RelayRoundId)))
}
)
Expand Down
94 changes: 94 additions & 0 deletions modules/relay/src/main/HttpClient.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package lila.relay

import java.nio.charset.{ Charset, StandardCharsets }
import io.mola.galimatias.URL
import play.api.libs.ws.*
import play.shaded.ahc.org.asynchttpclient.util.HttpUtils.extractContentTypeCharsetAttribute

import lila.core.lilaism.LilaException

/* Extra generic features for play WS client,
* without any knowledge of broadcast specifics.
* This could be moved for reuse later on.
* - Proxies
* - Etag cache
*/
private final class HttpClient(
ws: StandaloneWSClient,
cacheApi: lila.memo.CacheApi,
proxySelector: ProxySelector
)(using Executor):

import HttpClient.*

val etagCache = cacheApi.notLoadingSync[URL, (Body, Etag)](256, "relay.fetch.etagCache"):
_.expireAfterWrite(10 minutes).build()

def get(url: URL)(using CanProxy): Fu[Body] =
etagCache
.getIfPresent(url)
.match
case None =>
fetchBodyAndEtag(url, none)
case Some((prevBody, prevEtag)) =>
fetchBodyAndEtag(url, prevEtag.some).map: (newBody, newEtag) =>
val body = if newBody.isEmpty && newEtag.has(prevEtag) then prevBody.some else newBody
(body, newEtag)
.map: (body, etag) =>
(body, etag).mapN((b, e) => etagCache.put(url, b -> e))
~body

private def fetchBodyAndEtag(url: URL, etag: Option[Etag])(using
CanProxy
): Fu[(Option[Body], Option[Etag])] =
val req = etag.foldLeft(toRequest(url))((req, etag) => req.addHttpHeaders("If-None-Match" -> etag))
fetchResponse(req).map: res =>
val newEtag = res.header("Etag")
if res.status == 304
then none -> newEtag.orElse(etag)
else decodeResponseBody(res).some -> newEtag

private def fetchResponse(req: StandaloneWSRequest): Fu[StandaloneWSResponse] =
Future
.fromTry(lila.common.url.parse(req.url))
.flatMap: url =>
req
.get()
.monValue: res =>
_.relay.httpGet(
res.status,
url.host.toString,
etag = monitorEtagHit(req, res),
req.proxyServer.map(_.host)
)
.flatMap: res =>
if res.status == 200 || res.status == 304 then fuccess(res)
else fufail(Status(res.status, url))

private def decodeResponseBody(res: StandaloneWSResponse): Body =
val charset = Option(extractContentTypeCharsetAttribute(res.contentType))
.orElse(res.contentType.startsWith("text/").option(StandardCharsets.ISO_8859_1))
charset match
case None => lila.common.String.charset.guessAndDecode(res.bodyAsBytes)
case Some(known) => res.bodyAsBytes.decodeString(known)

private def toRequest(url: URL)(using CanProxy): StandaloneWSRequest =
val req = ws
.url(url.toString)
.withRequestTimeout(5.seconds)
.withFollowRedirects(false)
proxySelector(url).foldLeft(req)(_ withProxyServer _)

private def monitorEtagHit(req: StandaloneWSRequest, res: StandaloneWSResponse): String =
(req.header("If-None-Match"), res.header("Etag")) match
case (None, None) => "none" // endpoint doesn't support Etag
case (None, Some(_)) => "first" // local cache is cold
case (Some(_), _) if res.status == 304 => "hit" // cache hit
case (Some(_), Some(_)) => "miss" // new data from the endpoint
case (Some(_), None) => "fail" // we sent an etag but the endpoint doesn't support it?

private object HttpClient:
type Etag = String
type Body = String
case class Status(code: Int, url: URL) extends LilaException:
override val message = s"$code: $url"
4 changes: 2 additions & 2 deletions modules/relay/src/main/RelayDelay.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final private class RelayDelay(colls: RelayColls)(using Executor):
): Fu[RelayGames] =
dedupCache(url, round, () => doFetchUrl(url))
.flatMap: latest =>
round.sync.delay match
round.sync.delayMinusLag match
case Some(delay) if delay > 0 => store.get(url, delay).map(_ | latest.map(_.resetToSetup))
case _ => fuccess(latest)

Expand Down Expand Up @@ -48,7 +48,7 @@ final private class RelayDelay(colls: RelayColls)(using Executor):
)
.games
.addEffect: games =>
if round.sync.hasDelay then store.putIfNew(url, games)
if round.sync.delayMinusLag.isDefined then store.putIfNew(url, games)

private object store:

Expand Down
Loading

0 comments on commit c823d9d

Please sign in to comment.