-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
445 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
interface ISendMessage { | ||
fun sendChatMessage(message: String) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Oops, something went wrong.