Various regex opts
- Prefer unanchored matching over `.*<pattern>.*` - Prefer possessive matching when possible, which is faster is nearly every circumstance and universal use minimizes bad regex behavior - guard <string>.replace, which is slow in jdk below 9. even at jdk 9, indexOf is faster than replace for misses, but the performacne isn't much different.pull/4420/head
parent
31b927a66b
commit
c5f7db1790
|
@ -26,7 +26,7 @@ private[app] final class Renderer extends Actor {
|
|||
case streams: lila.streamer.LiveStreams.WithTitles => sender ! V.streamer.liveStreams(streams)
|
||||
}
|
||||
|
||||
private val spaceRegex = """\s{2,}""".r
|
||||
private val spaceRegex = """\s{2,}+""".r
|
||||
private def spaceless(html: Html) = Html {
|
||||
spaceRegex.replaceAllIn(html.body.replace("\\n", " "), " ")
|
||||
}
|
||||
|
|
|
@ -29,10 +29,12 @@ object Auth extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
private val refRegex = """[\w@-/]++""".r
|
||||
|
||||
private def goodReferrer(referrer: String): Boolean = {
|
||||
referrer.nonEmpty &&
|
||||
referrer.stripPrefix("/") != "mobile" && {
|
||||
"""(?:[\w@-]|(:?\/[\w@-]))*\/?""".r.matches(referrer) ||
|
||||
(!referrer.contains("//") && refRegex.matches(referrer)) ||
|
||||
referrer.startsWith(Env.oAuth.baseUrl)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ object Editor extends LilaController {
|
|||
|
||||
def load(urlFen: String) = Open { implicit ctx =>
|
||||
val fenStr = lila.common.String.decodeUriPath(urlFen)
|
||||
.map(_.replace("_", " ").trim).filter(_.nonEmpty)
|
||||
.map(_.replace('_', ' ').trim).filter(_.nonEmpty)
|
||||
.orElse(get("fen"))
|
||||
fuccess {
|
||||
val situation = readFen(fenStr)
|
||||
|
|
|
@ -32,7 +32,7 @@ object UserAnalysis extends LilaController with TheftPrevention {
|
|||
|
||||
def load(urlFen: String, variant: Variant) = Open { implicit ctx =>
|
||||
val decodedFen: Option[FEN] = lila.common.String.decodeUriPath(urlFen)
|
||||
.map(_.replace("_", " ").trim).filter(_.nonEmpty)
|
||||
.map(_.replace('_', ' ').trim).filter(_.nonEmpty)
|
||||
.orElse(get("fen")) map FEN.apply
|
||||
val pov = makePov(decodedFen, variant)
|
||||
val orientation = get("color").flatMap(chess.Color.apply) | pov.color
|
||||
|
|
|
@ -31,15 +31,15 @@ trait StringHelper { self: NumberHelper =>
|
|||
|
||||
def when(cond: Boolean, str: String) = cond ?? str
|
||||
|
||||
private val NumberFirstRegex = """^(\d+)\s(.+)$""".r
|
||||
private val NumberLastRegex = """^(.+)\s(\d+)$""".r
|
||||
private val NumberFirstRegex = """(\d++)\s(.+)""".r
|
||||
private val NumberLastRegex = """\s(\d++)$""".r.unanchored
|
||||
def splitNumber(s: String)(implicit ctx: UserContext): Html = Html {
|
||||
s match {
|
||||
case NumberFirstRegex(number, text) =>
|
||||
s"<strong>${(~parseIntOption(number)).localize}</strong><br />$text"
|
||||
case NumberLastRegex(text, number) =>
|
||||
s"$text<br /><strong>${(~parseIntOption(number)).localize}</strong>"
|
||||
case h => h.replace("\n", "<br />")
|
||||
case NumberLastRegex(n) if s.length > n.length + 1 =>
|
||||
s"${s.dropRight(n.length + 1)}<br /><strong>${(~parseIntOption(n)).localize}</strong>"
|
||||
case h => h.replaceIf('\n', "<br />")
|
||||
}
|
||||
}
|
||||
def splitNumber(s: Html)(implicit ctx: UserContext): Html = splitNumber(s.body)
|
||||
|
|
|
@ -22,7 +22,7 @@ object ActivityHtml extends lila.Lilaisms {
|
|||
if (p == 0) ""
|
||||
else s"""<$tag>${wrapNumber(name.pluralSameTxt(p))}</$tag>"""
|
||||
|
||||
private val wrapNumberRegex = """(\d+)""".r
|
||||
private val wrapNumberRegex = """(\d++)""".r
|
||||
private def wrapNumber(str: String) =
|
||||
wrapNumberRegex.replaceAllIn(str, "<strong>$1</strong>")
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@url = {
|
||||
@variant match {
|
||||
case chess.variant.Standard => {https://en.wikipedia.org/wiki/Chess}
|
||||
case chess.variant.FromPosition => {@routes.Editor.index?fen=@initialFen.map(_.value.replace(" ", "_"))}
|
||||
case chess.variant.FromPosition => {@routes.Editor.index?fen=@initialFen.map(_.value.replace(' ', '_'))}
|
||||
case v => {@routes.Page.variant(v.key)}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ object Mobile {
|
|||
)
|
||||
)
|
||||
|
||||
private val PathPattern = """^.+/socket/v(\d+)$""".r
|
||||
private val HeaderPattern = """^application/vnd\.lichess\.v(\d+)\+json$""".r
|
||||
private val PathPattern = """/socket/v(\d++)$""".r.unanchored
|
||||
private val HeaderPattern = """application/vnd\.lichess\.v(\d++)\+json""".r
|
||||
|
||||
def requestVersion(req: RequestHeader): Option[ApiVersion] = {
|
||||
(req.headers.get(HeaderNames.ACCEPT), req.path) match {
|
||||
|
|
|
@ -50,7 +50,7 @@ final class PgnDump(
|
|||
// merge analysis & eval comments
|
||||
// 1. e4 { [%eval 0.17] } { [%clk 0:00:30] }
|
||||
// 1. e4 { [%eval 0.17] [%clk 0:00:30] }
|
||||
s"$pgn\n\n\n".replace("] } { [", "] [")
|
||||
s"$pgn\n\n\n".replaceIf("] } { [", "] [")
|
||||
}
|
||||
|
||||
// def exportGamesFromIds(ids: List[String]): Enumerator[String] =
|
||||
|
|
|
@ -2,9 +2,9 @@ package lila.blog
|
|||
|
||||
object ProtocolFix {
|
||||
|
||||
private val RemoveRegex = """http://(\w{2}\.)?lichess\.org""".r
|
||||
private val RemoveRegex = """http://(\w{2}\.)?+lichess\.org""".r
|
||||
def remove(html: String) = RemoveRegex.replaceAllIn(html, _ => "//lichess.org")
|
||||
|
||||
private val AddRegex = """(https?:)?(//)?(\w{2}\.)?lichess\.org""".r
|
||||
private val AddRegex = """(https?+:)?+(//)?+(\w{2}\.)?+lichess\.org""".r
|
||||
def add(html: String) = AddRegex.replaceAllIn(html, _ => "https://lichess.org")
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ package lila.blog
|
|||
|
||||
object Youtube {
|
||||
|
||||
private val EmbedRegex = """youtube\.com/watch\?v=[\w-]+\#t=([^"]+).+\?feature=oembed""".r
|
||||
private val HourMinSecRegex = """(\d+)h(\d+)m(\d+)s""".r
|
||||
private val MinSecRegex = """(\d+)m(\d+)s""".r
|
||||
private val SecRegex = """(\d+)s""".r
|
||||
private val EmbedRegex = """youtube\.com/watch\?v=[\w-]++\#t=([^"]+).+\?feature=oembed""".r
|
||||
private val HourMinSecRegex = """(\d++)h(\d++)m(\d++)s""".r
|
||||
private val MinSecRegex = """(\d++)m(\d++)s""".r
|
||||
private val SecRegex = """(\d++)s""".r
|
||||
|
||||
/*
|
||||
* <div data-oembed="https://www.youtube.com/watch?v=uz-dZ2W4Bf0#t=4m14s" data-oembed-type="video" data-oembed-provider="youtube"><iframe width="480" height="270" src="https://www.youtube.com/embed/uz-dZ2W4Bf0?feature=oembed" frameborder="0" allowfullscreen></iframe></div>
|
||||
|
|
|
@ -209,26 +209,25 @@ final class ChatApi(
|
|||
|
||||
private object Writer {
|
||||
|
||||
import java.util.regex.Matcher.quoteReplacement
|
||||
import java.util.regex.{ Pattern, Matcher }
|
||||
|
||||
def preprocessUserInput(in: String) = multiline(Spam.replace(noShouting(noPrivateUrl(in))))
|
||||
|
||||
def cut(text: String) = Some(text.trim take Line.textMaxSize) filter (_.nonEmpty)
|
||||
|
||||
private val domainRegex = netDomain.replace(".", """\.""")
|
||||
private val gameUrlRegex = (domainRegex + """\b/([\w]{8})[\w]{4}\b""").r
|
||||
private def noPrivateUrl(str: String): String =
|
||||
gameUrlRegex.replaceAllIn(str, m => quoteReplacement(netDomain + "/" + (m group 1)))
|
||||
private val multilineRegex = """\n{3,}""".r
|
||||
private val gameUrlRegex = (Pattern.quote(netDomain) + """\b/(\w{8})\w{4}\b""").r
|
||||
private val gameUrlReplace = Matcher.quoteReplacement(netDomain) + "/$1";
|
||||
private def noPrivateUrl(str: String): String = gameUrlRegex.replaceAllIn(str, gameUrlReplace)
|
||||
private val multilineRegex = """\n{3,}+""".r
|
||||
private def multiline(str: String) = multilineRegex.replaceAllIn(str, """\n\n""")
|
||||
}
|
||||
|
||||
private object noShouting {
|
||||
import java.lang.Character.isUpperCase
|
||||
private val onlyLettersRegex = """[^\w]""".r
|
||||
private val onlyLettersRegex = """\W""".r
|
||||
def apply(text: String) = if (text.size < 5) text else {
|
||||
val onlyLetters = onlyLettersRegex.replaceAllIn(text take 80, "")
|
||||
if (onlyLetters.count(isUpperCase) > onlyLetters.size / 2)
|
||||
if (2 * onlyLetters.count(isUpperCase) > onlyLetters.size)
|
||||
text.toLowerCase
|
||||
else text
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ object Line {
|
|||
def write(x: Line) = BSONString(lineToStr(x))
|
||||
}
|
||||
|
||||
private val UserLineRegex = """^(?s)([\w-]{2,})(.)(.+)$""".r
|
||||
private val UserLineRegex = """(?s)([\w-]{2,}+)([ !?])(.++)""".r
|
||||
def strToUserLine(str: String): Option[UserLine] = str match {
|
||||
case UserLineRegex(username, " ", text) => UserLine(username, text, troll = false, deleted = false).some
|
||||
case UserLineRegex(username, "!", text) => UserLine(username, text, troll = true, deleted = false).some
|
||||
|
|
|
@ -11,7 +11,7 @@ object UrlList {
|
|||
def apply(text: String): List[Url] =
|
||||
text.lines.toList.map(_.trim).filter(_.nonEmpty) flatMap toUrl take max
|
||||
|
||||
private val UrlRegex = """.*(?:youtube\.com|youtu\.be)/(?:watch)?(?:\?v=)?([^"&?\/ ]{11}).*""".r
|
||||
private val UrlRegex = """(?:youtube\.com|youtu\.be)/(?:watch)?(?:\?v=)?([^"&?/ ]{11})""".r.unanchored
|
||||
|
||||
/*
|
||||
* https://www.youtube.com/watch?v=wEwoyYp_iw8
|
||||
|
@ -29,7 +29,7 @@ object UrlList {
|
|||
|
||||
case class StudyId(value: String) extends AnyVal
|
||||
|
||||
private val UrlRegex = """.*(?:lichess\.org)/study/(\w+{8}).*$""".r
|
||||
private val UrlRegex = """(?:lichess\.org)/study/(\w{8})""".r.unanchored
|
||||
|
||||
def apply(text: String): List[StudyId] =
|
||||
text.lines.toList.map(_.trim).filter(_.nonEmpty) flatMap toId take max
|
||||
|
|
|
@ -44,11 +44,10 @@ object HTTPRequest {
|
|||
"""coccoc|integromedb|contentcrawlerspider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler\.com|siteexplorer\.info|elisabot|proximic|changedetection|blexbot|arabot|wesee:search|niki-bot|crystalsemanticsbot|rogerbot|360spider|psbot|interfaxscanbot|lipperheyseoservice|ccmetadatascaper|g00g1e\.net|grapeshotcrawler|urlappendbot|brainobot|fr-crawler|binlar|simplecrawler|simplecrawler|livelapbot|twitterbot|cxensebot|smtbot|facebookexternalhit|daumoa|sputnikimagebot|visionutils|yisouspider|parsijoobot|mediatoolkit\.com|semrushbot""")
|
||||
}
|
||||
|
||||
case class UaMatcher(regex: String) {
|
||||
val pattern = regex.r.pattern
|
||||
case class UaMatcher(rStr: String) {
|
||||
private val regex = rStr.r
|
||||
|
||||
def apply(req: RequestHeader): Boolean =
|
||||
userAgent(req) ?? { ua => pattern.matcher(ua).find }
|
||||
def apply(req: RequestHeader): Boolean = userAgent(req) ?? { regex.find(_) }
|
||||
}
|
||||
|
||||
def isFishnet(req: RequestHeader) = req.path startsWith "/fishnet/"
|
||||
|
@ -59,10 +58,9 @@ object HTTPRequest {
|
|||
ua.contains("facebookexternalhit/") || ua.contains("twitterbot/")
|
||||
}
|
||||
|
||||
private val fileExtensionPattern = """.+\.[a-z0-9]{2,4}$""".r.pattern
|
||||
private[this] val fileExtensionRegex = """\.(?<!^\.)[a-z0-9]{2,4}$""".r
|
||||
|
||||
def hasFileExtension(req: RequestHeader) =
|
||||
fileExtensionPattern.matcher(req.path).matches
|
||||
def hasFileExtension(req: RequestHeader) = fileExtensionRegex.find(req.path)
|
||||
|
||||
def weirdUA(req: RequestHeader) = userAgent(req).fold(true)(_.size < 30)
|
||||
|
||||
|
|
|
@ -5,16 +5,16 @@ object LameName {
|
|||
def username(name: String) =
|
||||
anyName(name) || lameTitlePrefix.matcher(name).lookingAt
|
||||
|
||||
def anyName(name: String) = lameWords.matcher(name).find
|
||||
def anyName(name: String) = lameWords.find(name)
|
||||
|
||||
def anyNameButLichessIsOk(name: String) = lameWords.matcher {
|
||||
def anyNameButLichessIsOk(name: String) = lameWords find {
|
||||
lichessRegex.replaceAllIn(name, "")
|
||||
}.find
|
||||
}
|
||||
|
||||
private val lichessRegex = "(?i)lichess".r
|
||||
|
||||
private val lameTitlePrefix =
|
||||
"[Ww]?[NCFIGl1L]M|(?i:w?[ncfigl1])m[-_A-Z0-9]".r.pattern
|
||||
"[Ww]?+[NCFIGl1L]M|(?i:w?+[ncfigl1])m[-_A-Z0-9]".r.pattern
|
||||
|
||||
private val lameWords = {
|
||||
val extras = Map(
|
||||
|
@ -73,6 +73,6 @@ object LameName {
|
|||
"cuck"
|
||||
).map {
|
||||
_.map(subs).map(_ + "+").mkString
|
||||
}.mkString("|").r.pattern
|
||||
}.mkString("|").r
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package lila.common
|
||||
|
||||
import java.util.regex.Matcher.quoteReplacement
|
||||
|
||||
import ornicar.scalalib.Random
|
||||
import play.api.mvc.{ Cookie, DiscardingCookie, Session, RequestHeader }
|
||||
|
||||
object LilaCookie {
|
||||
|
||||
private val domainRegex = """^.+(\.[^\.]+\.[^\.]+)$""".r
|
||||
private val domainRegex = """\.[^.]++\.[^.]++$""".r
|
||||
|
||||
private def domain(req: RequestHeader): String =
|
||||
domainRegex.replaceAllIn(req.domain, m => quoteReplacement(m group 1))
|
||||
domainRegex.findFirstIn(req.domain).getOrElse(req.domain)
|
||||
|
||||
val sessionId = "sid"
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ trait Lilaisms
|
|||
@inline implicit def toPimpedTryList[A](l: List[Try[A]]) = new PimpedTryList(l)
|
||||
@inline implicit def toPimpedList[A](l: List[A]) = new PimpedList(l)
|
||||
@inline implicit def toPimpedSeq[A](l: Seq[A]) = new PimpedSeq(l)
|
||||
@inline implicit def toPimpedChars(i: Iterable[CharSequence]) = new PimpedChars(i)
|
||||
@inline implicit def toPimpedByteArray(ba: Array[Byte]) = new PimpedByteArray(ba)
|
||||
|
||||
@inline implicit def toPimpedOption[A](a: Option[A]) = new PimpedOption(a)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lila.base
|
||||
|
||||
import java.util.Base64
|
||||
import java.lang.{ StringBuilder => jStringBuilder }
|
||||
import scala.util.Try
|
||||
|
||||
final class PimpedTryList[A](private val list: List[Try[A]]) extends AnyVal {
|
||||
|
@ -13,6 +14,23 @@ final class PimpedList[A](private val list: List[A]) extends AnyVal {
|
|||
}
|
||||
}
|
||||
|
||||
final class PimpedChars(private val iter: Iterable[CharSequence]) extends AnyVal {
|
||||
def concat: String = {
|
||||
val it = iter.iterator
|
||||
if (it.hasNext) {
|
||||
val first = it.next
|
||||
if (it.hasNext) {
|
||||
val sb = new jStringBuilder(first)
|
||||
do {
|
||||
sb.append(it.next)
|
||||
} while (it.hasNext)
|
||||
sb
|
||||
} else first
|
||||
}.toString
|
||||
else ""
|
||||
}
|
||||
}
|
||||
|
||||
final class PimpedSeq[A](private val seq: Seq[A]) extends AnyVal {
|
||||
def has(a: A) = seq contains a
|
||||
}
|
||||
|
|
|
@ -43,6 +43,15 @@ final class PimpedString(private val s: String) extends AnyVal {
|
|||
def boot[A](v: => A): A = lila.common.Chronometer.syncEffect(v) { lap =>
|
||||
lila.log.boot.info(s"${lap.millis}ms $s")
|
||||
}
|
||||
|
||||
def replaceIf(t: Char, r: Char): String =
|
||||
if (s.indexOf(t) >= 0) s.replace(t, r) else s
|
||||
|
||||
def replaceIf(t: Char, r: CharSequence): String =
|
||||
if (s.indexOf(t) >= 0) s.replace(String.valueOf(t), r) else s
|
||||
|
||||
def replaceIf(t: CharSequence, r: CharSequence): String =
|
||||
if (s.contains(t)) s.replace(t, r) else s
|
||||
}
|
||||
|
||||
final class PimpedConfig(private val config: Config) extends AnyVal {
|
||||
|
|
|
@ -18,7 +18,7 @@ object IpAddress {
|
|||
// http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
|
||||
private val ipv4Regex = """^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$""".r
|
||||
// ipv6 address in standard form (no compression, no leading zeros)
|
||||
private val ipv6Regex = """^((0|[1-9a-f][0-9a-f]{0,3}):){7}(0|[1-9a-f][0-9a-f]{0,3})""".r
|
||||
private val ipv6Regex = """^((0|[1-9a-f][0-9a-f]{0,3}+):){7}(0|[1-9a-f][0-9a-f]{0,3})""".r
|
||||
|
||||
def isv4(a: IpAddress) = ipv4Regex matches a.value
|
||||
def isv6(a: IpAddress) = ipv6Regex matches a.value
|
||||
|
@ -29,16 +29,16 @@ object IpAddress {
|
|||
}
|
||||
|
||||
case class EmailAddress(value: String) extends AnyVal with StringValue {
|
||||
def isHotmail = EmailAddress.hotmailPattern.matcher(value).find
|
||||
def isHotmail = EmailAddress.hotmailRegex.find(value)
|
||||
}
|
||||
|
||||
object EmailAddress {
|
||||
|
||||
private val pattern =
|
||||
"""^[a-zA-Z0-9\.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$""".r.pattern
|
||||
private val regex =
|
||||
"""^[a-zA-Z0-9\.!#$%&'*+/=?^_`{|}~-]++@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$""".r
|
||||
|
||||
def from(str: String): Option[EmailAddress] =
|
||||
pattern.matcher(str).find option EmailAddress(str)
|
||||
regex.find(str) option EmailAddress(str)
|
||||
|
||||
private val hotmailPattern = """(.*)@(live|hotmail|outlook)\.(.*)""".r.pattern
|
||||
private val hotmailRegex = """@(live|hotmail|outlook)\.""".r
|
||||
}
|
||||
|
|
|
@ -689,7 +689,7 @@ object mon {
|
|||
|
||||
private val stripVersionRegex = """[^\w\.\-]""".r
|
||||
private def stripVersion(v: String) = stripVersionRegex.replaceAllIn(v, "")
|
||||
private def nodots(s: String) = s.replace(".", "_")
|
||||
private def nodots(s: String) = s.replace('.', '_')
|
||||
private val makeVersion = nodots _ compose stripVersion _
|
||||
|
||||
private val logger = lila.log("monitor")
|
||||
|
|
|
@ -36,7 +36,7 @@ final class Photographer(coll: Coll, prefix: String) {
|
|||
|
||||
private def sanitizeName(name: String) = {
|
||||
// the char `^` breaks play, even URL encoded
|
||||
java.net.URLEncoder.encode(name, "UTF-8").replace("%", "")
|
||||
java.net.URLEncoder.encode(name, "UTF-8").replaceIf('%', "")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,18 +42,18 @@ object DataForm {
|
|||
) {
|
||||
|
||||
def looksLikeVenting = List(name, post.text) exists { txt =>
|
||||
mostlyUpperCase(txt) || ventingPattern.matcher(txt).find
|
||||
mostlyUpperCase(txt) || ventingRegex.find(txt)
|
||||
}
|
||||
}
|
||||
|
||||
private def mostlyUpperCase(txt: String) = {
|
||||
val extract = txt.take(300)
|
||||
(extract.contains(' ') || extract.size > 5) && {
|
||||
extract.count(_.isUpper) > extract.count(_.isLower) * 2
|
||||
(extract.contains(" ") || extract.size > 5) && {
|
||||
2 * extract.count(_.isUpper) > extract.count(_.isLower)
|
||||
}
|
||||
}
|
||||
|
||||
private val ventingPattern = """cheat|engine|rating|loser|banned|abort""".r.pattern
|
||||
private val ventingRegex = """cheat|engine|rating|loser|banned|abort""".r
|
||||
|
||||
case class PostEdit(changes: String)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import lila.user.{ User, UserContext }
|
|||
|
||||
trait Granter {
|
||||
|
||||
private val TeamSlugPattern = """^team-([\w-]+)$""".r
|
||||
private val TeamSlugPattern = """team-([\w-]++)""".r
|
||||
|
||||
protected def userBelongsToTeam(teamId: String, userId: String): Fu[Boolean]
|
||||
protected def userOwnsTeam(teamId: String, userId: String): Fu[Boolean]
|
||||
|
|
|
@ -90,7 +90,7 @@ case class Player(
|
|||
|
||||
object Player {
|
||||
|
||||
private val nameSplitRegex = """^([^\(]+)\((.+)\)$""".r
|
||||
private val nameSplitRegex = """([^(]++)\((\d++)\)""".r
|
||||
|
||||
def make(
|
||||
color: Color,
|
||||
|
|
|
@ -52,7 +52,7 @@ private[gameSearch] final class DataForm {
|
|||
|
||||
private[gameSearch] object DataForm {
|
||||
|
||||
val DateDelta = """^(\d+)(\w)$""".r
|
||||
val DateDelta = """\d++[a-z]""".r
|
||||
private val dateConstraint = Constraints.pattern(
|
||||
regex = DateDelta,
|
||||
error = "Invalid date."
|
||||
|
|
|
@ -41,7 +41,7 @@ private[i18n] final class JsDump(path: String) {
|
|||
finally { out.close }
|
||||
}
|
||||
|
||||
private def escape(text: String) = text.replace(""""""", """\"""")
|
||||
private def escape(text: String) = text.replaceIf('"', "\\\"")
|
||||
}
|
||||
|
||||
object JsDump {
|
||||
|
|
|
@ -103,7 +103,7 @@ private final class PoolActor(
|
|||
}
|
||||
|
||||
val monitor = lila.mon.lobby.pool
|
||||
val monId = config.id.value.replace("+", "_")
|
||||
val monId = config.id.value.replace('+', '_')
|
||||
}
|
||||
|
||||
private object PoolActor {
|
||||
|
|
|
@ -11,14 +11,14 @@ object PracticeGoal {
|
|||
case class EvalIn(cp: Int, nbMoves: Int) extends PracticeGoal
|
||||
case class Promotion(cp: Int) extends PracticeGoal
|
||||
|
||||
private val MateR = """(?i)^(?:check)?mate$""".r
|
||||
private val MateInR = """(?i)^(?:check)?mate in (\d+)$""".r
|
||||
private val DrawInR = """(?i)^draw in (\d+)$""".r
|
||||
private val EqualInR = """(?i)^equal(?:ize)? in (\d+)$""".r
|
||||
private val EvalInR = """(?i)^((?:\+|-|)\d+)cp in (\d+)$""".r
|
||||
private val PromotionR = """(?i)^promotion with ((?:\+|-|)\d+)cp$""".r
|
||||
private val MateR = """(?i)(?:check)?+mate""".r
|
||||
private val MateInR = """(?i)(?:check)?+mate in (\d++)""".r
|
||||
private val DrawInR = """(?i)draw in (\d++)""".r
|
||||
private val EqualInR = """(?i)equal(?:ize)?+ in (\d++)""".r
|
||||
private val EvalInR = """(?i)((?:\+|-|)\d++)cp in (\d++)""".r
|
||||
private val PromotionR = """(?i)promotion with ((?:\+|-|)\d++)cp""".r
|
||||
|
||||
private val MultiSpaceR = """\s{2,}""".r
|
||||
private val MultiSpaceR = """\s{2,}+""".r
|
||||
|
||||
def apply(chapter: lila.study.Chapter): PracticeGoal =
|
||||
chapter.tags(_.Termination).map(v => MultiSpaceR.replaceAllIn(v.trim, " ")).flatMap {
|
||||
|
|
|
@ -157,6 +157,6 @@ object Report {
|
|||
)
|
||||
}
|
||||
|
||||
private val farmWithRegex = s""".+ points from @(${User.historicalUsernameRegex.pattern}) .*""".r
|
||||
private val sandbagWithRegex = s""".+ winning player @(${User.historicalUsernameRegex.pattern}) .*""".r
|
||||
private val farmWithRegex = s""". points from @(${User.historicalUsernameRegex.pattern}) """.r.unanchored
|
||||
private val sandbagWithRegex = s""". winning player @(${User.historicalUsernameRegex.pattern}) """.r.unanchored
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ private final class ReportScore(
|
|||
def flagScore(user: User) =
|
||||
(user.lameOrTroll) ?? -30d
|
||||
|
||||
private val gamePattern = """lichess.org/(\w{8,12})""".r.pattern
|
||||
private val gameRegex = """lichess.org/\w{8,12}""".r
|
||||
|
||||
def textScore(reason: Reason, text: String) = {
|
||||
(reason == Reason.Cheat || reason == Reason.Boost) &&
|
||||
gamePattern.matcher(text).find
|
||||
gameRegex.find(text)
|
||||
} ?? 20
|
||||
|
||||
// https://github.com/ornicar/lila/issues/4093
|
||||
|
|
|
@ -78,8 +78,8 @@ object Streamer {
|
|||
def minUrl = s"twitch.tv/$userId"
|
||||
}
|
||||
object Twitch {
|
||||
private val UserIdRegex = """^([\w-]{2,24})$""".r
|
||||
private val UrlRegex = """.*twitch\.tv/([\w-]{2,24}).*""".r
|
||||
private val UserIdRegex = """([\w-]{2,24}+)""".r
|
||||
private val UrlRegex = """twitch\.tv/([\w-]{2,24}+)""".r.unanchored
|
||||
// https://www.twitch.tv/chessnetwork
|
||||
def parseUserId(str: String): Option[String] = str match {
|
||||
case UserIdRegex(u) => u.some
|
||||
|
@ -94,7 +94,7 @@ object Streamer {
|
|||
}
|
||||
object YouTube {
|
||||
private val ChannelIdRegex = """^([\w-]{24})$""".r
|
||||
private val UrlRegex = """.*youtube\.com/channel/([\w-]{24}).*""".r
|
||||
private val UrlRegex = """youtube\.com/channel/([\w-]{24})""".r.unanchored
|
||||
def parseChannelId(str: String): Option[String] = str match {
|
||||
case ChannelIdRegex(c) => c.some
|
||||
case UrlRegex(c) => c.some
|
||||
|
|
|
@ -152,8 +152,8 @@ object Chapter {
|
|||
|
||||
def defaultName(order: Int) = Name(s"Chapter $order")
|
||||
|
||||
private val defaultNamePattern = """^Chapter \d+$""".r.pattern
|
||||
def isDefaultName(n: Name) = n.value.isEmpty || defaultNamePattern.matcher(n.value).matches
|
||||
private val defaultNameRegex = """Chapter \d+""".r
|
||||
def isDefaultName(n: Name) = n.value.isEmpty || defaultNameRegex.matches(n.value)
|
||||
|
||||
def fixName(n: Name) = Name(n.value.trim take 80)
|
||||
|
||||
|
|
|
@ -146,8 +146,8 @@ private final class ChapterMaker(
|
|||
|
||||
private val UrlRegex = {
|
||||
val escapedDomain = domain.replace(".", "\\.")
|
||||
s""".*$escapedDomain/(\\w{8,12}).*"""
|
||||
}.r
|
||||
s"""$escapedDomain/(\\w{8,12})"""
|
||||
}.r.unanchored
|
||||
|
||||
private def parsePov(str: String): Fu[Option[Pov]] = str match {
|
||||
case s if s.size == Game.gameIdSize => GameRepo.pov(s, chess.White)
|
||||
|
|
|
@ -6,13 +6,13 @@ import lila.tree.Node.{ Shape, Shapes }
|
|||
|
||||
private[study] object CommentParser {
|
||||
|
||||
private val circlesRegex = """(?s).*\[\%csl[\s\r\n]+((?:\w{3}[,\s]*)+)\].*""".r
|
||||
private val circlesRegex = """(?s)\[\%csl[\s\r\n]+((?:\w{3}[,\s]*)+)\]""".r.unanchored
|
||||
private val circlesRemoveRegex = """\[\%csl[\s\r\n]+((?:\w{3}[,\s]*)+)\]""".r
|
||||
private val arrowsRegex = """(?s).*\[\%cal[\s\r\n]+((?:\w{5}[,\s]*)+)\].*""".r
|
||||
private val arrowsRegex = """(?s)\[\%cal[\s\r\n]+((?:\w{5}[,\s]*)+)\]""".r.unanchored
|
||||
private val arrowsRemoveRegex = """\[\%cal[\s\r\n]+((?:\w{5}[,\s]*)+)\]""".r
|
||||
private val clockRegex = """(?s).*\[\%clk[\s\r\n]+([\d:\.]+)\].*""".r
|
||||
private val clockRegex = """(?s)\[\%clk[\s\r\n]+([\d:\.]+)\]""".r.unanchored
|
||||
private val clockRemoveRegex = """\[\%clk[\s\r\n]+[\d:\.]+\]""".r
|
||||
private val tcecClockRegex = """(?s).*tl=([\d:\.]+).*""".r
|
||||
private val tcecClockRegex = """(?s)tl=([\d:\.]+)""".r.unanchored
|
||||
private val tcecClockRemoveRegex = """tl=[\d:\.]+""".r
|
||||
|
||||
case class ParsedComment(
|
||||
|
@ -36,8 +36,8 @@ private[study] object CommentParser {
|
|||
s <- parseIntOption(seconds)
|
||||
} yield Centis(h * 360000 + m * 6000 + s * 100)
|
||||
|
||||
private val clockHourMinuteRegex = """^(\d+):(\d+)$""".r
|
||||
private val clockHourMinuteSecondRegex = """^(\d+):(\d+)[:\.](\d+)$""".r
|
||||
private val clockHourMinuteRegex = """^(\d++):(\d+)$""".r
|
||||
private val clockHourMinuteSecondRegex = """^(\d++):(\d++)[:\.](\d+)$""".r
|
||||
|
||||
def readCentis(str: String): Option[Centis] = str match {
|
||||
case clockHourMinuteRegex(hours, minutes) => readCentis(hours, minutes, "0")
|
||||
|
|
|
@ -11,7 +11,7 @@ private final class PgnFetch {
|
|||
// http://www.chessgames.com/perl/chessgame?gid=1427487
|
||||
// http://www.chessgames.com/perl/nph-chesspgn?text=1&gid=1427487
|
||||
// http://www.chessgames.com/pgn/boleslavsky_ufimtsev_1944.pgn?gid=1427487
|
||||
private val ChessbaseRegex = """.*chessgames\.com/.*[\?&]gid=(\d+).*""".r
|
||||
private val ChessbaseRegex = """chessgames\.com/.*[\?&]gid=(\d+)""".r.unanchored
|
||||
|
||||
def fromUrl(url: String): Fu[Option[Pgn]] = url match {
|
||||
case ChessbaseRegex(id) => parseIntOption(id) ?? downloadChessbase
|
||||
|
|
|
@ -4,7 +4,7 @@ object Links {
|
|||
|
||||
def make(text: String): List[Link] = text.lines.toList.map(_.trim) flatMap toLink
|
||||
|
||||
private val UrlRegex = """^(?:http[s]?:\/\/)?([^/]+)/?.*$""".r
|
||||
private val UrlRegex = """^(?:https?://)?+([^/]+)""".r.unanchored
|
||||
|
||||
private def toLink(line: String): Option[Link] = line match {
|
||||
case UrlRegex(domain) => Link(
|
||||
|
|
|
@ -151,8 +151,6 @@ object User {
|
|||
val anonymous = "Anonymous"
|
||||
val lichessId = "lichess"
|
||||
|
||||
val idPattern = """^[\w-]{3,20}$""".r.pattern
|
||||
|
||||
case class GDPRErase(user: User) extends AnyVal
|
||||
case class Erased(value: Boolean) extends AnyVal
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ case class UserControl(
|
|||
)
|
||||
|
||||
def queryString = List(
|
||||
filter.tags.nonEmpty option s"tags=${filter.tags.sorted mkString "/"}".replace(" ", "+"),
|
||||
filter.tags.nonEmpty option s"tags=${filter.tags.sorted mkString "/"}".replace(' ', '+'),
|
||||
query.map { q => s"q=$q" }
|
||||
).flatten mkString "&"
|
||||
|
||||
|
|
Loading…
Reference in New Issue