Skip to content

Commit

Permalink
Revert "rewrite from scratch" lmao
Browse files Browse the repository at this point in the history
This reverts commit 8663230.
  • Loading branch information
commandblock2 committed Oct 4, 2023
1 parent 8663230 commit cfb8673
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 7 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
# Paper-Matrix-Bridge
# Note: this plugin now uses paper api
https://github.com/commandblock2/matrix-spigot-bridge isn't buildable.
I decided to write my own.
The bridge is implemented as a spigot plugin, which act as a bot for **unencrypted** room.

Rewriting. This time it should implement the `application service` API, with [trixnity](https://github.com/benkuly/trixnity).
Currently, **E2EE** is **NOT** supported.
No command support currently.

## Setup
1. create an account on your desired server for the bot.
2. (Optionally) create a dedicated room for the bot, but you can also use an existing room.
3. put the plugin jar(which can be found in Shithub CI) in the plugins' folder.
4. create the config at `plugins/SpigotMatrixBridge/config.yml` manually or run the server once.
### Default config
```
server: "https://matrix.server"
room_id: "!ZJNSsOscMMCydGSmuC:matrix.server"
user_name: "@username:home.server"
password: "password"
manage_whitelist: true
```
## Whitelist Control
Set `manage_whitelist: true`.
Type `whitelist set username` in the room the bot is in.
And after the bot replied, `Updated Whitelist: @user:home.server -> username`
the users is added to the whitelist.
The whitelist is based on username not online account.
Can be used together with AuthMeReload to prevent the players from impersonating others.
Shouldn't conflict with other plugins but IDK never tested lmao.
7 changes: 1 addition & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar


plugins {
kotlin("jvm") version "1.7.10"
id("com.github.johnrengelman.shadow") version "7.0.0"
Expand All @@ -16,13 +15,9 @@ repositories {
mavenCentral()
}

val trixnityVersion = "3.0.0" // get version from https://gitlab.com/benkuly/trixnity/-/releases
fun trixnity(module: String, version: String = trixnityVersion) =
"net.folivo:trixnity-$module:$version"

dependencies {
implementation(trixnity("applicationservice"))

dependencies {
implementation(kotlin("stdlib"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/ISendMessage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface ISendMessage {
fun sendChatMessage(message: String)
}
21 changes: 21 additions & 0 deletions src/main/kotlin/MatrixBridge.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import org.bukkit.plugin.java.JavaPlugin
import java.io.File

class MatrixBridge : JavaPlugin() {

override fun onEnable() {

saveDefaultConfig()

WhitelistManager.loadMapping(File(dataFolder, "whitelist.txt"))

Thread {
MatrixListener.updateConnection(
config.getString("server")!!,
config.getString("room_id")!!,
config.getString("user_name")!!,
config.getString("password")!!,
config.getBoolean("manage_whitelist"),
this,
)
}.start()

MinecraftListener.manageWhitelist = config.getBoolean("manage_whitelist")
server.pluginManager.registerEvents(MinecraftListener, this)
}

override fun onDisable() {
MatrixListener.logOut()
WhitelistManager.saveMapping()
}
}
74 changes: 74 additions & 0 deletions src/main/kotlin/MatrixListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import util.MatrixConnection
import java.util.*
import java.util.logging.Level
import kotlin.concurrent.fixedRateTimer

object MatrixListener : ISendMessage {

private var connection: MatrixConnection? = null

fun updateConnection(
serverName: String,
roomID: String,
username: String,
password: String,
manageWhiteList: Boolean,
bridge: MatrixBridge
) {
try {
connection = MatrixConnection(serverName, roomID, username, password)
bridge.logger.log(Level.INFO, "Login successful")
connection!!.sendMessage("bot login")

connection!!.poll { _, _ -> }

while (true) {
try {
connection!!.poll { matrixName, message ->

val trimmedMessage = message.trimStart().trimEnd()
if (trimmedMessage.startsWith("whitelist set") && bridge.config.getBoolean("manage_whitelist")) {
val split = trimmedMessage.split(" ")
if (split.size != 3)
connection!!.sendMessage("malformed whitelist set request, please use `whitelist set name`")
else
WhitelistManager.updateWhiteList(matrixName, split.last())
}

MinecraftListener.sendChatMessage(
"[${connection!!.getDisplayName(matrixName)}]: $message"
)
}
} catch (ignored: Exception) {
ignored.printStackTrace()
connection!!.disconnect()
bridge.logger.log(Level.WARNING, "Exception thrown while running, trying to reconnect")
updateConnection(serverName, roomID, username, password, manageWhiteList, bridge)
}
}

} catch (exception: Exception) {
bridge.logger.log(Level.WARNING, exception.message)
exception.printStackTrace()
bridge.logger.log(Level.WARNING, "Exception thrown while trying to connect, reconnecting in 30s")
Thread.sleep(1000 * 30)
updateConnection(serverName, roomID, username, password, manageWhiteList, bridge)
exception.printStackTrace()
}
}


fun logOut() {
connection ?: return

connection!!.sendMessage("bot logout")
connection!!.disconnect()

connection = null
}


override fun sendChatMessage(message: String) {
connection?.sendMessage(message)
}
}
75 changes: 75 additions & 0 deletions src/main/kotlin/MinecraftListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import io.papermc.paper.event.player.AbstractChatEvent
import io.papermc.paper.event.player.AsyncChatEvent
import io.papermc.paper.text.PaperComponents
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import org.bukkit.Bukkit
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.PlayerAdvancementDoneEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerLoginEvent
import org.bukkit.event.player.PlayerQuitEvent

object MinecraftListener : Listener, ISendMessage {

var manageWhitelist: Boolean = false

override fun sendChatMessage(message: String) {
// Bukkit.getServer().broadcastMessage(message)
Bukkit.getOnlinePlayers().forEach { it.sendMessage(message) }
Bukkit.getConsoleSender().sendMessage(message)
}

@EventHandler
fun onMinecraftChat(chatEvent: AsyncChatEvent) {
Thread {
MatrixListener.sendChatMessage("<${chatEvent.player.name}> ${getMessage(chatEvent.message())}")
}.start()
}

@EventHandler
fun onPlayerDeath(deathEvent: PlayerDeathEvent) {
Thread {
MatrixListener.sendChatMessage(
getMessage(deathEvent.deathMessage())
)
}.start()
}

@EventHandler
fun onPlayerQuit(playerQuitEvent: PlayerQuitEvent) {
Thread {
MatrixListener.sendChatMessage(getMessage(playerQuitEvent.quitMessage()))
}.start()
}

@EventHandler
fun onPlayerAdvancement(playerAdvancementDoneEvent: PlayerAdvancementDoneEvent) {
Thread {
MatrixListener.sendChatMessage(
getMessage(playerAdvancementDoneEvent.message())
)
}
}

@EventHandler
fun onPlayerJoin(loginEvent: PlayerLoginEvent) {
if (manageWhitelist && !WhitelistManager.isInWhiteList(loginEvent.player.name))
loginEvent.disallow(
PlayerLoginEvent.Result.KICK_WHITELIST,
Component.text("You are not in whitelist. Please type `whitelist set player-name` in the matrix server")
)
}

@EventHandler
fun onPlayerLogin(joinEvent: PlayerJoinEvent) {
Thread {MatrixListener.sendChatMessage(getMessage(joinEvent.joinMessage()))}.start()
}

private fun getMessage(component: Component?): String {
component ?: return ""
return PlainTextComponentSerializer.plainText().serialize(component)
}
}
45 changes: 45 additions & 0 deletions src/main/kotlin/WhitelistManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import java.io.File

object WhitelistManager {
private val userNameMapping = HashMap<String, String>()

private lateinit var file: File

fun loadMapping(file: File) {
this.file = file

if (!file.exists())
file.createNewFile()

file.useLines { lines ->
lines.forEach {
val (matrixName, mcWhiteListString) = it.split(" ")
userNameMapping[matrixName] = mcWhiteListString
}
}
}

fun updateWhiteList(matrixUser: String, mcName :String) {
userNameMapping[matrixUser] = mcName

MatrixListener.sendChatMessage("Updated Whitelist: $matrixUser -> $mcName")
saveMapping()
}

fun isInWhiteList(mcName: String): Boolean {
return userNameMapping.values.contains(mcName)
}

fun saveMapping() {
val content = StringBuilder()
userNameMapping.forEach {
content
.append(it.key)
.append(" ")
.append(it.value)
.append("\n")
}

file.writeText(content.toString())
}
}
84 changes: 84 additions & 0 deletions src/main/kotlin/util/HttpUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package util

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.URI
import java.net.URL
import java.util.zip.GZIPInputStream
import javax.net.ssl.HttpsURLConnection

object HttpUtils {

private val gson: Gson = GsonBuilder().setLenient().create()

fun requestWithBearer(
url: String, method: String,
payload: JsonObject = JsonObject(),
requestProperties: JsonObject = JsonObject(),
token: String
): JsonObject {
requestProperties.addProperty("Authorization", "Bearer $token")
return request(url, method, payload, requestProperties)
}

fun request(
url: String, method: String,
payload: JsonObject = JsonObject(),
requestProperties: JsonObject = JsonObject(),
timeout: Int = -1
): JsonObject {
val urlAsURL = URL(url)
val uri = URI(
urlAsURL.protocol,
urlAsURL.userInfo,
urlAsURL.host,
urlAsURL.port,
urlAsURL.path,
urlAsURL.query,
urlAsURL.ref
)
val connection = URL(uri.toASCIIString()).openConnection() as HttpsURLConnection

connection.requestMethod = method
connection.setRequestProperty("Content-Type", "application/json; utf-8")
connection.setRequestProperty("Accept", "application/json")
connection.setRequestProperty("Accept-Encoding", "gzip, deflate, br")
connection.doOutput = true

if (timeout >= 0)
connection.readTimeout = timeout

requestProperties
.entrySet()
.forEach { entry -> connection.setRequestProperty(entry.key, entry.value.asString) }


if (method != "GET")
connection
.outputStream
.write(payload.toString().toByteArray(Charsets.UTF_8))


val inputStream =
if (connection.contentEncoding == "gzip")
GZIPInputStream(connection.inputStream)
else
connection.inputStream

val response = StringBuilder()
BufferedReader(InputStreamReader(inputStream, "utf-8")).use { br ->
var responseLine: String?
while (br.readLine().also { responseLine = it } != null) {
response.append(responseLine!!.trim { it <= ' ' })
}
}

connection.disconnect()

return gson.fromJson(response.toString(), JsonElement::class.java).asJsonObject
}
}
Loading

0 comments on commit cfb8673

Please sign in to comment.