Miscellaneous moderation QoL improvements

This commit is contained in:
ceikry 2022-09-07 20:20:19 -05:00
parent 785f72215f
commit c3b91b2240
10 changed files with 123 additions and 4 deletions

View file

@ -1,6 +1,7 @@
package core.game.content.global.report;
import core.game.node.entity.player.Player;
import discord.Discord;
import rs09.game.system.command.CommandMapping;
/**
@ -50,6 +51,7 @@ public final class AbuseReport {
CommandMapping.INSTANCE.get("mute").attemptHandling(player, new String[] {"mute", victim, "48h"});
}
player.getPacketDispatch().sendMessage("Thank-you, your abuse report has been received.");
Discord.INSTANCE.postPlayerAlert(victim, "Abuse Report - " + rule.name());
}
/**

View file

@ -1,6 +1,7 @@
package discord
import api.getItemName
import core.game.node.entity.player.Player
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.json.simple.JSONArray
@ -24,7 +25,7 @@ object Discord {
GlobalScope.launch {
val offer = encodeOfferJson(isSale, itemId, value, qty, user)
try {
sendJsonPost(offer)
sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer)
} catch (e: Exception) {
e.printStackTrace()
}
@ -36,7 +37,18 @@ object Discord {
GlobalScope.launch {
val offer = encodeUpdateJson(isSale, itemId, value, amtLeft)
try {
sendJsonPost(offer)
sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun postPlayerAlert(player: String, type: String) {
GlobalScope.launch {
val alert = encodeUserAlert(type, player)
try {
sendJsonPost(ServerConstants.DISCORD_MOD_WEBHOOK, alert)
} catch (e: Exception) {
e.printStackTrace()
}
@ -88,6 +100,23 @@ object Discord {
return obj.toJSONString()
}
private fun encodeUserAlert(type: String, player: String) : String {
val obj = JSONObject()
val embeds = JSONArray()
val embed = JSONObject()
val fields = arrayOf(
EmbedField("Player", player, false),
EmbedField("Type", type, false)
)
embed["title"] = "Player Alert"
embed["fields"] = getFields(fields)
embeds.add(embed)
obj["embeds"] = embeds
return obj.toJSONString()
}
private fun getFields(fields: Array<EmbedField>): JSONArray {
val arr = JSONArray()
@ -110,8 +139,8 @@ object Discord {
return obj
}
private fun sendJsonPost(data: String) {
val conn = URL(ServerConstants.DISCORD_GE_WEBHOOK).openConnection() as HttpURLConnection
private fun sendJsonPost(url: String = ServerConstants.DISCORD_GE_WEBHOOK, data: String) {
val conn = URL(url).openConnection() as HttpURLConnection
conn.doOutput = true
conn.requestMethod = "POST"
conn.setRequestProperty("Content-Type", "application/json")

View file

@ -233,6 +233,9 @@ class ServerConstants {
@JvmField
var DISCORD_GE_WEBHOOK = ""
@JvmField
var DISCORD_MOD_WEBHOOK = ""
@JvmField
var PRELOAD_MAP = false

View file

@ -10,6 +10,7 @@ import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Graphics
import discord.Discord
import rs09.game.content.ame.events.MysteriousOldManNPC
import rs09.game.content.global.WeightBasedTable
import rs09.tools.secondsToTicks
@ -104,6 +105,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
player.properties.teleportLocation = Location.create(3212, 9620, 0)
}
player.graphics(SMOKE_GRAPHICS)
Discord.postPlayerAlert(player.username, "Ignored Random")
}
override fun clear() {

View file

@ -7,6 +7,8 @@ import rs09.game.world.GameWorld
import core.game.world.map.Location
import rs09.game.world.repository.Repository
import core.plugin.Initializable
import rs09.ServerStore
import rs09.ServerStore.Companion.addToList
import rs09.game.system.command.Privilege
import java.util.concurrent.TimeUnit
@ -84,6 +86,59 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){
* =============================================================================================================
*/
/**
* Ban all players on a given IP
* =============================================================================================================
*/
define("ipban", Privilege.ADMIN, "::ipban <lt>IP<gt> <lt>TIME<gt>", "Bans all players on the given ip. Time format: <lt>INT<gt>d/s/m/h ex: 30d for 30 days."){ player, args ->
val ip = args[1]
val durationString = args[2]
val durationTokens = durationString.toCharArray()
var intToken = ""
var durationMillis = 0L
var durationUnit: TimeUnit = TimeUnit.NANOSECONDS
for(token in durationTokens){
if(token.toString().toIntOrNull() != null) intToken += token
else {
val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") }
durationUnit = when(token) {
'd' -> TimeUnit.DAYS
's' -> TimeUnit.SECONDS
'm' -> TimeUnit.MINUTES
'h' -> TimeUnit.HOURS
else -> TimeUnit.SECONDS
}
durationMillis = durationUnit.toMillis(durationInt.toLong())
}
}
val playersToBan = GameWorld.accountStorage.getUsernamesWithIP(ip)
if (playersToBan.isEmpty()) {
reject(player, "No accounts found on IP $ip")
}
for (p in playersToBan) {
val playerToKick = Repository.getPlayerByName(p)
playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis
playerToKick?.clear(true)
GameWorld.Pulser.submit(object : Pulse(2) {
override fun pulse(): Boolean {
val info = GameWorld.accountStorage.getAccountInfo(p)
info.banEndTime = System.currentTimeMillis() + durationMillis
GameWorld.accountStorage.update(info)
return true
}
})
}
ServerStore.getArchive("flagged-ips").addToList("ips", ip)
notify(player, "Banned all accounts on $ip for $intToken ${durationUnit.name.toLowerCase()}.")
}
/**
* =============================================================================================================
*/
/**
* Mute a player
* =============================================================================================================

View file

@ -124,6 +124,7 @@ object ServerConfigParser {
ServerConstants.USE_AUTH = data.getBoolean("server.use_auth", true)
ServerConstants.PERSIST_ACCOUNTS = data.getBoolean("server.persist_accounts", true)
ServerConstants.DAILY_ACCOUNT_LIMIT = data.getLong("server.daily_accounts_per_ip", 3L).toInt()
ServerConstants.DISCORD_MOD_WEBHOOK = data.getString("server.moderation_webhook", "")
}

View file

@ -10,6 +10,7 @@ import core.game.node.entity.player.info.login.LoginType
import core.net.Constants
import core.net.IoSession
import core.tools.StringUtils
import discord.Discord
import proto.management.JoinClanRequest
import proto.management.PlayerStatusUpdate
import proto.management.RequestContactInfo
@ -124,6 +125,12 @@ object Login {
details.session = session
details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED"))
val archive = ServerStore.getArchive("flagged-ips")
val flaggedIps = archive.getList<String>("ips")
if (flaggedIps.contains(details.ipAddress)) {
Discord.postPlayerAlert(details.username, "Login from flagged IP ${details.ipAddress}")
}
if (checkAccountLimit(details.ipAddress, details.username)) {
val player = Player(details)
if (Repository.getPlayerByName(player.name) == null) {

View file

@ -5,6 +5,7 @@ import rs09.auth.UserAccountInfo
interface AccountStorageProvider {
fun checkUsernameTaken(username: String): Boolean
fun getAccountInfo(username: String): UserAccountInfo
fun getUsernamesWithIP(ip: String) : List<String>
fun store(info: UserAccountInfo)
fun update(info: UserAccountInfo)
fun remove(info: UserAccountInfo)

View file

@ -28,4 +28,8 @@ class InMemoryStorageProvider : AccountStorageProvider {
override fun getOnlineFriends(username: String): List<String> {
return ArrayList()
}
override fun getUsernamesWithIP(ip: String): List<String> {
return ArrayList()
}
}

View file

@ -177,9 +177,24 @@ class SQLStorageProvider : AccountStorageProvider {
return friends
}
override fun getUsernamesWithIP(ip: String): List<String> {
val conn = getConnection()
val res = ArrayList<String>()
conn.use {
val query = it.prepareStatement(accountsByIPQuery)
query.setString(1, ip)
val r = query.executeQuery()
while (r.next()) {
res.add(r.getString(1))
}
}
return res
}
companion object {
private const val usernameQuery = "SELECT username FROM members WHERE username = ?;"
private const val removeInfoQuery = "DELETE FROM members WHERE username = ?;"
private const val accountsByIPQuery = "SELECT username FROM members WHERE lastGameIp = ?;"
private const val accountInfoQuery = "SELECT " +
"username," +
"password," +