Skip to content

Commit

Permalink
Merge branch 'master' into language-mapping-revamp
Browse files Browse the repository at this point in the history
  • Loading branch information
superuser-does authored Dec 28, 2024
2 parents 2ba416d + c811034 commit 2cf23d4
Show file tree
Hide file tree
Showing 56 changed files with 358 additions and 158 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/common/src/main/mon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ object mon:
timer("relay.sync.time").withTags(relay(official, id, slug))
def httpGet(code: Int, host: String, etag: String, proxy: Option[String]) =
timer("relay.http.get").withTags:
tags("code" -> code, "host" -> host, "etag" -> etag, "proxy" -> proxy.getOrElse("none"))
tags("code" -> code.toLong, "host" -> host, "etag" -> etag, "proxy" -> proxy.getOrElse("none"))
val dedup = counter("relay.fetch.dedup").withoutTags()

object bot:
Expand Down
8 changes: 0 additions & 8 deletions modules/i18n/src/main/LangList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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", "RU") -> "авар мацӀ",
Lang("az", "AZ") -> "Azərbaycanca",
Expand All @@ -38,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 @@ -52,19 +50,16 @@ object LangList extends lila.core.i18n.LangList:
Lang("hy", "AM") -> "Հայերեն",
Lang("ia", "AA") -> "Interlingua",
Lang("id", "ID") -> "Bahasa Indonesia",
Lang("io", "EN") -> "Ido",
Lang("is", "IS") -> "Íslenska",
Lang("it", "IT") -> "Italiano",
Lang("ja", "JP") -> "日本語",
Lang("jbo", "AA") -> "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", "VA") -> "Lingua Latina",
Lang("lb", "LU") -> "Lëtzebuergesch",
Lang("lt", "LT") -> "Lietuvių kalba",
Expand All @@ -86,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 @@ -95,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 @@ -105,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/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)
}
8 changes: 4 additions & 4 deletions modules/relay/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,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
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
20 changes: 12 additions & 8 deletions modules/relay/src/main/RelayFetch.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,9 @@ final private class RelayFetch(
private def continueRelay(tour: RelayTour, updating: Updating[RelayRound]): Updating[RelayRound] =
val round = updating.current
round.sync.upstream.fold(updating): upstream =>
reportBroadcastFailure(round.withTour(tour))
val seconds: Seconds =
if round.sync.log.alwaysFails then
round.sync.log.events.lastOption
.filterNot(_.isTimeout)
.flatMap(_.error)
.ifTrue(tour.official && round.shouldHaveStarted)
.filterNot(_.contains("Cannot parse move"))
.filterNot(_.contains("Cannot parse pgn"))
.filterNot(_.contains("Found an empty PGN"))
.foreach { irc.broadcastError(round.id, round.withTour(tour).fullName, _) }
Seconds(tour.tier.fold(60):
case RelayTour.Tier.best => 10
case RelayTour.Tier.high => 20
Expand All @@ -173,6 +166,17 @@ final private class RelayFetch(
}.some
)

private def reportBroadcastFailure(r: RelayRound.WithTour): Unit =
if r.round.sync.log.alwaysFails then
r.round.sync.log.events.lastOption
.filterNot(_.isTimeout)
.flatMap(_.error)
.ifTrue(r.tour.official && r.round.shouldHaveStarted)
.filterNot(_.contains("Cannot parse move"))
.filterNot(_.contains("Cannot parse pgn"))
.filterNot(_.contains("Found an empty PGN"))
.foreach { irc.broadcastError(r.round.id, r.fullName, _) }

private def dynamicPeriod(tour: RelayTour, round: RelayRound, upstream: Sync.Upstream) = Seconds:
val base =
if upstream.hasLcc then 5
Expand Down
2 changes: 1 addition & 1 deletion modules/relay/src/main/RelayPlayerEnrich.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,6 @@ private final class RelayPlayerEnrich(
chapterId = chapter.id,
tags = enriched,
newName = newName.filter(_ != chapter.name)
)(lila.study.actorApi.Who(chapter.ownerId, Sri("")))
)(lila.study.Who(chapter.ownerId, Sri("")))
.runWith(Sink.ignore)
yield ()
2 changes: 1 addition & 1 deletion modules/relay/src/main/RelayPush.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class RelayPush(
parsed.map(_.map(g => Success(g.tags, g.root.mainline.size)))
val andSyncTargets = response.exists(_.isRight)

rt.round.sync.nonEmptyDelay
rt.round.sync.delayMinusLag
.ifTrue(games.exists(_.root.children.nonEmpty))
.match
case None => push(rt, games, andSyncTargets).inject(response)
Expand Down
5 changes: 3 additions & 2 deletions modules/relay/src/main/RelayRound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ object RelayRound:
def addLog(event: SyncLog.Event) = copy(log = log.add(event))
def clearLog = copy(log = SyncLog.empty)

def nonEmptyDelay = delay.filter(_.value > 0)
def hasDelay = nonEmptyDelay.isDefined
// subtract estimated source polling lag from transmission delay
private def pollingLag = Seconds(if isPush then 1 else 6)
def delayMinusLag = delay.map(_ - pollingLag).filter(_.value > 0)

override def toString = upstream.toString

Expand Down
37 changes: 19 additions & 18 deletions modules/relay/src/main/RelaySync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import chess.format.pgn.{ Tag, Tags }
import lila.core.socket.Sri
import lila.study.*
import lila.tree.Branch
import lila.study.AddNode

final private class RelaySync(
studyApi: StudyApi,
Expand Down Expand Up @@ -91,20 +92,19 @@ final private class RelaySync(
studyId = study.id,
position = Position(chapter, path).ref,
toMainline = true
)(by) >> chapterRepo.setRelayPath(chapter.id, path)
)(using by) >> chapterRepo.setRelayPath(chapter.id, path)
_ <- newNode match
case Some(newNode) =>
newNode.mainline
.foldM(Position(chapter, path).ref): (position, n) =>
studyApi
.addNode(
studyId = study.id,
position = position,
node = n,
opts = moveOpts,
relay = makeRelayFor(game, position.path + n.id).some
)(by)
.inject(position + n)
val node = AddNode(
studyId = study.id,
positionRef = position,
node = n,
opts = moveOpts,
relay = makeRelayFor(game, position.path + n.id).some
)(using by)
studyApi.addNode(node).inject(position + n)
case None =>
// the chapter already has all the game moves,
// but its relayPath might be out of sync. This can happen if the broadcast
Expand All @@ -121,13 +121,14 @@ final private class RelaySync(
game.root.children
.nodeAt(gameMainlinePath)
.so: lastMainlineNode =>
studyApi.addNode(
studyId = study.id,
position = Position(chapter, gameMainlinePath.parent).ref,
node = lastMainlineNode,
opts = moveOpts,
relay = makeRelayFor(game, gameMainlinePath).some
)(by)
studyApi.addNode:
AddNode(
studyId = study.id,
positionRef = Position(chapter, gameMainlinePath.parent).ref,
node = lastMainlineNode,
opts = moveOpts,
relay = makeRelayFor(game, gameMainlinePath).some
)(using by)
yield newNode.so(_.mainline.size)

private def updateChapterTags(
Expand Down Expand Up @@ -212,7 +213,7 @@ final private class RelaySync(
)

private val sri = Sri("")
private def who(userId: UserId) = actorApi.Who(userId, sri)
private def who(userId: UserId) = Who(userId, sri)

private def vs(tags: Tags) = s"${tags(_.White) | "?"} - ${tags(_.Black) | "?"}"

Expand Down
4 changes: 2 additions & 2 deletions modules/relay/src/main/ui/RelayTourUi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ final class RelayTourUi(helpers: Helpers, ui: RelayUi):
nonEmptyTier(_.high),
nonEmptyTier(_.normal),
h2(cls := "relay-index__section")(trc.pastBroadcasts()),
div(cls := "relay-cards relay-cards--past"):
div(cls := "relay-cards"):
past.map: t =>
card.render(t, live = _ => false)
,
Expand Down Expand Up @@ -150,7 +150,7 @@ final class RelayTourUi(helpers: Helpers, ui: RelayUi):
div(cls := "page-menu__content box box-pad")(
boxTop(h1(dataIcon := Icon.RadioTower, cls := "text")(trc.broadcastCalendar())),
dateForm("top"),
div(cls := "relay-cards relay-cards--past"):
div(cls := "relay-cards"):
tours.map(card.renderCalendar)
,
(tours.sizeIs > 8).option(dateForm("bottom"))
Expand Down
19 changes: 13 additions & 6 deletions modules/security/src/main/EmailAddressValidator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ final class EmailAddressValidator(

// only compute valid and non-whitelisted email domains
private[security] def apply(e: EmailAddress): Fu[Result] =
e.domain.map(_.lower).fold(fuccess(Result.DomainMissing))(validateDomain)
if isInfiniteAlias(e) then fuccess(Result.Alias)
else e.domain.map(_.lower).fold(fuccess(Result.DomainMissing))(validateDomain)

private[security] def validateDomain(domain: Domain.Lower): Fu[Result] =
if DisposableEmailDomain.whitelisted(domain.into(Domain)) then fuccess(Result.Passlist)
Expand Down Expand Up @@ -95,6 +96,14 @@ final class EmailAddressValidator(
case (acc, _) => acc
if variations.isEmpty then List(email) else variations

private def isInfiniteAlias(e: EmailAddress) =
duckAliases.is(e)

private object duckAliases:
private val domain = Domain.Lower.from("duck.com")
private val regex = """^\w{3,}-\w{3,}-\w{3,}$""".r
def is(e: EmailAddress) = e.nameAndDomain.exists((n, d) => d.lower == domain && regex.matches(n))

private def wasUsedTwiceRecently(email: EmailAddress): Fu[Boolean] =
userRepo.countRecentByPrevEmail(email.normalize, nowInstant.minusWeeks(1)).dmap(_ >= 2) >>|
userRepo.countRecentByPrevEmail(email.normalize, nowInstant.minusMonths(1)).dmap(_ >= 4)
Expand All @@ -106,10 +115,8 @@ object EmailAddressValidator:
case Alright extends Result(none)
case DomainMissing extends Result("The email address domain is missing.".some) // no translation needed
case Blocklist extends Result("Cannot use disposable email addresses (Blocklist).".some)
case Alias extends Result("Cannot use email address aliases.".some)
case DnsMissing extends Result("This email domain doesn't seem to work (missing MX DNS)".some)
case DnsTimeout extends Result("This email domain doesn't seem to work (timeout MX DNS)".some)
case DnsBlocklist
extends Result(
"Cannot use disposable email addresses (DNS blocklist).".some
)
case Reputation extends Result("This email domain has a poor reputation and cannot be used.".some)
case DnsBlocklist extends Result("Cannot use disposable email addresses (DNS blocklist).".some)
case Reputation extends Result("This email domain has a poor reputation and cannot be used.".some)
2 changes: 1 addition & 1 deletion modules/security/src/main/VerifyMail.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ final private class VerifyMail(
if res.status == 429
then
logger.info(s"Mailcheck rate limited $url")
rateLimitedUntil = nowInstant.plusMinutes(2)
rateLimitedUntil = nowInstant.plusMinutes(5)
true
else
(for
Expand Down
2 changes: 1 addition & 1 deletion modules/study/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ final class Env(

private lazy val chapterMaker = wire[ChapterMaker]

private lazy val explorerGame = wire[ExplorerGame]
private lazy val explorerGame = wire[ExplorerGameApi]

private lazy val studyMaker = wire[StudyMaker]

Expand Down
2 changes: 1 addition & 1 deletion modules/study/src/main/ExplorerGame.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import chess.format.{ Fen, UciPath }
import lila.tree.Node.Comment
import lila.tree.{ Branch, Node, Root }

final private class ExplorerGame(
final private class ExplorerGameApi(
explorer: lila.core.game.Explorer,
namer: lila.core.game.Namer,
lightUserApi: lila.core.user.LightUserApi,
Expand Down
2 changes: 1 addition & 1 deletion modules/study/src/main/JsonView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,5 @@ object JsonView:

private[study] given Writes[Chapter.ServerEval] = Json.writes

private[study] given OWrites[actorApi.Who] = OWrites: w =>
private[study] given OWrites[Who] = OWrites: w =>
Json.obj("u" -> w.u, "s" -> w.sri)
Loading

0 comments on commit 2cf23d4

Please sign in to comment.