lila/modules/irc/src/main/IrcApi.scala

230 lines
8.8 KiB
Scala
Raw Normal View History

2021-06-10 03:03:47 -06:00
package lila.irc
import org.joda.time.DateTime
2021-06-22 12:41:37 -06:00
2021-06-10 03:15:08 -06:00
import lila.common.IpAddress
2021-06-22 12:41:37 -06:00
import lila.common.{ ApiVersion, EmailAddress, Heapsort, IpAddress, LightUser }
import lila.hub.actorApi.irc._
import lila.user.Holder
import lila.user.User
2021-06-10 03:03:47 -06:00
final class IrcApi(
zulip: ZulipClient,
noteApi: lila.user.NoteApi,
implicit val lightUser: LightUser.Getter
)(implicit ec: scala.concurrent.ExecutionContext) {
import IrcApi._
def commReportBurst(user: User): Funit = {
val md = markdown.linkifyUsers(s"Burst of comm reports about @${user.username}")
2021-07-06 23:40:21 -06:00
zulip(_.mod.commsPrivate, "burst")(md)
2021-06-10 03:03:47 -06:00
}
def inquiry(user: User, mod: Holder, domain: ModDomain, room: String): Funit = {
2021-07-08 12:49:21 -06:00
val stream = domain match {
case ModDomain.Comm => ZulipClient.stream.mod.commsPrivate
case ModDomain.Hunt => ZulipClient.stream.mod.hunterCheat
case _ => ZulipClient.stream.mod.adminGeneral
2021-07-08 12:49:21 -06:00
}
2021-06-10 03:03:47 -06:00
noteApi
2021-07-08 12:49:21 -06:00
.byUserForMod(user.id)
2021-06-10 03:03:47 -06:00
.map(_.headOption.filter(_.date isAfter DateTime.now.minusMinutes(5)))
.flatMap {
case None =>
zulip.sendAndGetLink(stream, "/" + user.username)(
2021-07-14 00:20:12 -06:00
s"${markdown.userLink(mod.user.username)} :monkahmm: is looking at a $room report about **${markdown
.userLink(user.username)}**"
2021-06-10 03:03:47 -06:00
)
case Some(note) =>
zulip.sendAndGetLink(stream, "/" + user.username)(
2021-12-10 03:16:35 -07:00
s"${markdown.modLink(mod.user)} :pepenote: **${markdown
.userLink(user.username)}** (${markdown.userNotesLink(user.username)}):\n" +
2021-06-10 03:03:47 -06:00
markdown.linkifyUsers(note.text take 2000)
)
}
.flatMap {
_ ?? { ZulipLink =>
2021-10-03 08:54:59 -06:00
noteApi.write(
user,
s"$domain discussion: $ZulipLink",
mod.user,
modOnly = true,
dox = (domain == ModDomain.Admin)
)
}
}
2021-07-08 12:49:21 -06:00
}
2021-06-10 03:03:47 -06:00
def userModNote(modName: String, username: String, note: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.mod.adminLog, "notes")(
s"${markdown.modLink(modName)} :note: **${markdown.userLink(username)}** (${markdown.userNotesLink(username)}):\n" +
markdown.linkifyUsers(note take 2000)
)
2021-06-10 03:15:08 -06:00
def selfReport(typ: String, path: String, user: User, ip: IpAddress): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.mod.adminLog, "self report")(
2021-07-05 00:01:19 -06:00
s"[**$typ**] ${markdown.userLink(user)}@$ip ${markdown.gameLink(path)}"
2021-06-10 03:15:08 -06:00
)
2021-06-22 12:41:37 -06:00
def commlog(mod: Holder, user: User, reportBy: Option[User.ID]): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.mod.adminLog, "private comms checks")({
2021-06-22 12:41:37 -06:00
val finalS = if (user.username endsWith "s") "" else "s"
2021-12-10 03:16:35 -07:00
s"**${markdown modLink mod.user}** checked out **${markdown userLink user.username}**'$finalS communications "
2021-06-22 12:41:37 -06:00
} + reportBy.filter(mod.id !=).fold("spontaneously") { by =>
s"while investigating a report created by ${markdown.userLink(by)}"
})
2021-07-08 12:49:21 -06:00
def monitorMod(modId: User.ID, icon: String, text: String, tpe: ModDomain): Funit =
2021-06-22 12:41:37 -06:00
lightUser(modId) flatMap {
_ ?? { mod =>
2021-07-07 02:22:37 -06:00
zulip(_.mod.adminMonitor(tpe), mod.name)(
2021-07-14 01:32:39 -06:00
s"${markdown.userLink(mod.name)} :$icon: ${markdown.linkifyPostsAndUsers(text)}"
2021-07-07 02:22:37 -06:00
)
2021-06-22 12:41:37 -06:00
}
}
def logMod(modId: User.ID, icon: String, text: String): Funit =
lightUser(modId) flatMap {
_ ?? { mod =>
2021-07-06 23:40:21 -06:00
zulip(_.mod.log, "actions")(
2021-07-14 01:32:39 -06:00
s"${markdown.modLink(modId)} :$icon: ${markdown.linkifyPostsAndUsers(text)}"
2021-07-06 23:40:21 -06:00
)
}
}
// def printBan(mod: Holder, print: String, userIds: List[User.ID]): Funit =
// logMod(mod.id, "footprints", s"Ban print $print of ${userIds} users: ${userIds map linkifyUsers}")
def chatPanic(mod: Holder, v: Boolean): Funit = {
2021-11-11 03:49:17 -07:00
val msg =
s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}"
zulip(_.mod.log, "chat panic")(msg) >> zulip(_.mod.commsPublic, "main")(msg)
}
def garbageCollector(msg: String): Funit =
2021-07-07 02:22:37 -06:00
zulip(_.mod.adminLog, "garbage collector")(markdown linkifyUsers msg)
def broadcastError(id: String, name: String, error: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.broadcast, "lila error log")(s"${markdown.broadcastLink(id, name)} $error")
2021-09-10 07:44:34 -06:00
def ublogPost(
user: User,
id: String,
slug: String,
2021-09-11 00:46:33 -06:00
title: String,
intro: String
2021-09-10 07:44:34 -06:00
): Funit =
zulip(_.blog, "non-tiered new posts")(
2021-09-11 00:46:33 -06:00
s":note: ${markdown
.lichessLink(s"/@/${user.username}/blog/$slug/$id", title)} $intro - by ${markdown
2021-09-05 04:06:30 -06:00
.userLink(user)}"
)
def userAppeal(user: User, mod: Holder): Funit =
zulip
.sendAndGetLink(_.mod.adminAppeal, "/" + user.username)(
s"${markdown.modLink(mod.user)} :monkahmm: is looking at the appeal of **${markdown
.lichessLink(s"/appeal/${user.username}", user.username)}**"
)
.flatMap {
_ ?? { zulipAppealConv =>
noteApi.write(user, s"Appeal discussion: $zulipAppealConv", mod.user, modOnly = true, dox = true)
}
}
def nameClosePreset(username: String): Funit =
zulip(_.mod.commsPublic, "/" + username)("@**remind** here in 48h to close this account")
2021-07-06 23:40:21 -06:00
def stop(): Funit = zulip(_.general, "lila")("Lichess is restarting.")
2021-06-22 12:41:37 -06:00
def publishEvent(event: Event): Funit = event match {
case Error(msg) => publishError(msg)
case Warning(msg) => publishWarning(msg)
case Info(msg) => publishInfo(msg)
case Victory(msg) => publishVictory(msg)
}
2021-10-12 23:57:16 -06:00
def signupAfterTryingDisposableEmail(user: User, email: EmailAddress, previous: Set[EmailAddress]) =
2021-10-07 03:54:31 -06:00
zulip(_.mod.adminLog, "disposable email")(
2021-10-12 23:57:16 -06:00
s"${markdown userLink user} signed up with ${email.value} after trying: ${previous.map(_.value) mkString ", "}"
2021-10-07 03:54:31 -06:00
)
2021-06-22 12:41:37 -06:00
private def publishError(msg: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.general, "lila")(s":lightning: ${markdown linkifyUsers msg}")
2021-06-22 12:41:37 -06:00
private def publishWarning(msg: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.general, "lila")(s":thinking: ${markdown linkifyUsers msg}")
2021-06-22 12:41:37 -06:00
private def publishVictory(msg: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.general, "lila")(s":tada: ${markdown linkifyUsers msg}")
2021-06-22 12:41:37 -06:00
private[irc] def publishInfo(msg: String): Funit =
2021-07-06 23:40:21 -06:00
zulip(_.general, "lila")(s":info: ${markdown linkifyUsers msg}")
2021-06-22 12:41:37 -06:00
object charge {
import lila.hub.actorApi.plan.ChargeEvent
private var buffer: Vector[ChargeEvent] = Vector.empty
implicit private val amountOrdering = Ordering.by[ChargeEvent, Int](_.cents)
def apply(event: ChargeEvent): Funit = {
buffer = buffer :+ event
buffer.head.date.isBefore(DateTime.now.minusHours(12)) ?? {
val firsts = Heapsort.topN(buffer, 10, amountOrdering).map(_.username).map(userAt).mkString(", ")
val amountSum = buffer.map(_.cents).sum
val patrons =
if (firsts.lengthIs > 10) s"$firsts and, like, ${firsts.length - 10} others,"
else firsts
displayMessage {
s"$patrons donated ${amount(amountSum)}. Monthly progress: ${buffer.last.percent}%"
} >>- {
buffer = Vector.empty
}
}
}
private def displayMessage(text: String) =
2021-07-06 23:40:21 -06:00
zulip(_.general, "lila")(markdown.linkifyUsers(text))
2021-06-22 12:41:37 -06:00
private def userAt(username: String) =
if (username == "Anonymous") "Anonymous"
else s"@$username"
private def amount(cents: Int) = s"$$${BigDecimal(cents.toLong, 2)}"
}
2021-06-10 03:03:47 -06:00
}
object IrcApi {
2021-07-08 12:49:21 -06:00
sealed trait ModDomain {
2021-07-02 02:03:41 -06:00
def key = toString.toLowerCase
}
2021-07-08 12:49:21 -06:00
object ModDomain {
case object Admin extends ModDomain
2021-07-08 12:49:21 -06:00
case object Hunt extends ModDomain
case object Comm extends ModDomain
case object Other extends ModDomain
}
2021-06-10 03:03:47 -06:00
private val userRegex = lila.common.String.atUsernameRegex.pattern
2021-07-15 02:16:40 -06:00
private val postRegex = lila.common.String.forumPostPathRegex.pattern
2021-06-10 03:03:47 -06:00
2021-07-06 23:40:21 -06:00
private object markdown {
2021-06-10 03:03:47 -06:00
def link(url: String, name: String) = s"[$name]($url)"
def lichessLink(path: String, name: String) = s"[$name](https://lichess.org$path)"
2021-12-10 03:13:33 -07:00
def userLink(name: String): String = lichessLink(s"/@/$name?mod&notes", name)
2021-06-10 03:03:47 -06:00
def userLink(user: User): String = userLink(user.username)
2021-07-05 00:01:19 -06:00
def modLink(name: String): String = lichessLink(s"/@/$name", name)
def modLink(user: User): String = modLink(user.username)
2021-06-10 03:15:08 -06:00
def gameLink(id: String) = lichessLink(s"/$id", s"#$id")
2021-06-10 03:03:47 -06:00
def userNotesLink(name: String) = lichessLink(s"/@/$name?notes", "notes")
2021-06-29 08:32:50 -06:00
def broadcastLink(id: String, name: String) = lichessLink(s"/broadcast/-/$id", name)
2021-06-10 03:03:47 -06:00
val userReplace = link("https://lichess.org/@/$1?mod", "$1")
2021-06-22 12:41:37 -06:00
def linkifyUsers(msg: String) = userRegex matcher msg replaceAll userReplace
2021-07-14 01:34:43 -06:00
val postReplace = lichessLink("/forum/$1", "$1")
def linkifyPosts(msg: String) = postRegex matcher msg replaceAll postReplace
def linkifyPostsAndUsers(msg: String) = linkifyPosts(linkifyUsers(msg))
def fixImageUrl(url: String) = url.replace("/display?", "/display.jpg?")
2021-06-10 03:03:47 -06:00
}
}