├── settings.gradle.kts ├── .idea ├── .gitignore ├── vcs.xml ├── kotlinc.xml ├── gradle.xml └── misc.xml ├── icon.png ├── icon_8x.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── bungee.yml │ └── plugin.yml │ └── kotlin │ └── io │ └── github │ └── bluesheep2804 │ └── selenechat │ ├── common │ ├── Platforms.kt │ ├── ConvertMode.kt │ └── ComponentSerializer.kt │ ├── command │ ├── ChannelCommandSpigot.kt │ ├── MessageCommandSpigot.kt │ ├── ChannelCommandBungee.kt │ ├── ChannelCommandVelocity.kt │ ├── JapanizeCommandBungee.kt │ ├── JapanizeCommandSpigot.kt │ ├── MessageCommandBungee.kt │ ├── MessageCommandVelocity.kt │ ├── JapanizeCommandVelocity.kt │ ├── SeleneChatCommandBungee.kt │ ├── SeleneChatCommandSpigot.kt │ ├── SeleneChatCommandVelocity.kt │ ├── ICommand.kt │ ├── channel │ │ ├── SubCommand.kt │ │ ├── SubEditCommand.kt │ │ ├── EditFormatCommand.kt │ │ ├── CreateCommand.kt │ │ ├── EditVisibleCommand.kt │ │ ├── EditJpCommand.kt │ │ ├── EditInfoCommand.kt │ │ ├── LeaveCommand.kt │ │ ├── DeleteCommand.kt │ │ ├── ListCommand.kt │ │ ├── JoinCommand.kt │ │ └── EditModeratorCommand.kt │ ├── CommandBungee.kt │ ├── CommandSpigot.kt │ ├── CommandVelocity.kt │ ├── JapanizeCommand.kt │ ├── SeleneChatCommand.kt │ ├── MessageCommand.kt │ └── ChannelCommand.kt │ ├── player │ ├── SeleneChatPlayerOffline.kt │ ├── SeleneChatPlayerConsole.kt │ ├── SeleneChatPlayerBungeeConsole.kt │ ├── SeleneChatPlayerVelocityConsole.kt │ ├── SeleneChatPlayerVelocity.kt │ ├── SeleneChatPlayerSpigotConsole.kt │ ├── SeleneChatPlayerSpigot.kt │ ├── SeleneChatPlayerBungee.kt │ └── SeleneChatPlayer.kt │ ├── japanize │ ├── GoogleIME.kt │ ├── Japanizer.kt │ ├── JapanizePlayersManager.kt │ ├── IMEConverter.kt │ └── RomaToKana.kt │ ├── message │ ├── PluginMessage.kt │ └── ChatMessage.kt │ ├── SeleneChat.kt │ ├── listener │ ├── ChatListenerSpigot.kt │ ├── ChatListener.kt │ ├── ChatListenerVelocity.kt │ └── ChatListenerBungee.kt │ ├── IPlugin.kt │ ├── resource │ ├── ResourceManager.kt │ ├── ResourceData.kt │ └── CommandResourceData.kt │ ├── config │ ├── SeleneChatConfigManager.kt │ └── SeleneChatConfigData.kt │ ├── channel │ ├── ChannelData.kt │ └── ChannelManager.kt │ ├── SeleneChatBungee.kt │ ├── SeleneChatSpigot.kt │ └── SeleneChatVelocity.kt ├── .github └── workflows │ └── release-please.yml ├── .gitignore ├── README_ja.md ├── README.md ├── gradlew.bat ├── CHANGELOG.md ├── LICENSE └── gradlew /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "SeleneChat" 2 | 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # デフォルトの無視対象ファイル 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSheep2804/SeleneChat/HEAD/icon.png -------------------------------------------------------------------------------- /icon_8x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSheep2804/SeleneChat/HEAD/icon_8x.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSheep2804/SeleneChat/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: SeleneChat 2 | version: ${version} 3 | description: Chat plugin inspired by LunaChat 4 | main: io.github.bluesheep2804.selenechat.SeleneChatBungee 5 | author: BlueSheep2804 -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/common/Platforms.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.common 2 | 3 | enum class Platforms { 4 | BUKKIT, 5 | BUNGEECORD, 6 | VELOCITY 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/ChannelCommandSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class ChannelCommandSpigot : CommandSpigot() { 4 | override val command = ChannelCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/MessageCommandSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class MessageCommandSpigot : CommandSpigot() { 4 | override val command = MessageCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/ChannelCommandBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class ChannelCommandBungee(override val command: ICommand = ChannelCommand()) : CommandBungee(command) -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/ChannelCommandVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class ChannelCommandVelocity : CommandVelocity() { 4 | override val command = ChannelCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/JapanizeCommandBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class JapanizeCommandBungee(override val command: ICommand = JapanizeCommand()) : CommandBungee(command) -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/JapanizeCommandSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class JapanizeCommandSpigot : CommandSpigot() { 4 | override val command = JapanizeCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/MessageCommandBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class MessageCommandBungee(override val command: ICommand = MessageCommand()) : CommandBungee(command) -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/MessageCommandVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class MessageCommandVelocity : CommandVelocity() { 4 | override val command = MessageCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/JapanizeCommandVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class JapanizeCommandVelocity : CommandVelocity() { 4 | override val command = JapanizeCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/SeleneChatCommandBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class SeleneChatCommandBungee(override val command: ICommand = SeleneChatCommand()) : CommandBungee(command) -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/SeleneChatCommandSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class SeleneChatCommandSpigot : CommandSpigot() { 4 | override val command = SeleneChatCommand() 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/SeleneChatCommandVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | class SeleneChatCommandVelocity : CommandVelocity() { 4 | override val command = SeleneChatCommand() 5 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/common/ConvertMode.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.common 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | enum class ConvertMode { 8 | @SerialName("none") NONE, 9 | @SerialName("kana") KANA, 10 | @SerialName("ime") IME 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | name: release-please 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | jobs: 12 | release-please: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: google-github-actions/release-please-action@v3 16 | with: 17 | release-type: simple 18 | package-name: selenechat -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/ICommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 4 | 5 | interface ICommand { 6 | val COMMAND_NAME: String 7 | val COMMAND_ALIASES: Array 8 | val PERMISSION: String 9 | fun execute(sender: SeleneChatPlayer, args: Array): Boolean 10 | fun suggest(sender: SeleneChatPlayer, args: Array): List 11 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerOffline.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import net.kyori.adventure.text.Component 4 | import java.util.* 5 | 6 | class SeleneChatPlayerOffline(override val uniqueId: UUID = UUID.fromString("0-0-0-0-0"), override val displayName: String = "", override val currentServerName: String = "") : SeleneChatPlayer() { 7 | override val isOnline = false 8 | override fun sendMessage(msg: Component) {} 9 | override fun sendCommandResult(msg: Component) {} 10 | override fun hasPermission(permission: String): Boolean { 11 | return false 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/japanize/GoogleIME.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.japanize 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonArray 5 | 6 | /** 7 | * GoogleIMEの最初の変換候補を抽出して結合するクラス 8 | * 9 | * @author ucchy, BlueSheep2804 10 | * @see [LunaChat Github](https://github.com/ucchyocean/LunaChat/tree/master/src/main/java/com/github/ucchyocean/lc3/japanize/GoogleIME.java) 11 | */ 12 | object GoogleIME { 13 | @JvmStatic 14 | fun parseJson(json: String?): String { 15 | val result = StringBuilder() 16 | for (response in Gson().fromJson(json, JsonArray::class.java)) { 17 | result.append(response.asJsonArray[1].asJsonArray[0].asString) 18 | } 19 | return result.toString() 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/SubCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 5 | 6 | abstract class SubCommand { 7 | abstract val commandName: String 8 | abstract val permission: String 9 | 10 | abstract fun execute(sender: SeleneChatPlayer, args: Array): Boolean 11 | 12 | fun checkPermission(sender: SeleneChatPlayer): Boolean { 13 | return if (!sender.hasPermission(permission)) { 14 | sender.sendCommandResult(resource.command.generalErrorNoPermission) 15 | false 16 | } else { 17 | true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerConsole.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import net.kyori.adventure.text.Component 4 | import net.kyori.adventure.text.event.HoverEvent 5 | import net.kyori.examination.Examinable 6 | import java.util.* 7 | 8 | abstract class SeleneChatPlayerConsole: SeleneChatPlayer() { 9 | override val displayName: String 10 | get() = "CONSOLE" 11 | 12 | override val uniqueId: UUID 13 | get() = UUID.fromString("0-0-0-0-0") 14 | 15 | override val currentServerName: String 16 | get() = "" 17 | 18 | override val isConsole: Boolean 19 | get() = true 20 | 21 | override fun asHoverEvent(): HoverEvent { 22 | return HoverEvent.showText(Component.text("CONSOLE")) 23 | } 24 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/uiDesigner.xml 12 | .idea/discord.xml 13 | .idea/libraries/ 14 | *.iws 15 | *.iml 16 | *.ipr 17 | out/ 18 | !**/src/main/**/out/ 19 | !**/src/test/**/out/ 20 | 21 | ### Eclipse ### 22 | .apt_generated 23 | .classpath 24 | .factorypath 25 | .project 26 | .settings 27 | .springBeans 28 | .sts4-cache 29 | bin/ 30 | !**/src/main/**/bin/ 31 | !**/src/test/**/bin/ 32 | 33 | ### NetBeans ### 34 | /nbproject/private/ 35 | /nbbuild/ 36 | /dist/ 37 | /nbdist/ 38 | /.nb-gradle/ 39 | 40 | ### VS Code ### 41 | .vscode/ 42 | 43 | ### Mac OS ### 44 | .DS_Store 45 | 46 | /run_paper 47 | /run_velocity 48 | /run_waterfall -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/SubEditCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.channel.ChannelData 5 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 6 | 7 | abstract class SubEditCommand { 8 | abstract val commandName: String 9 | abstract val permission: String 10 | 11 | abstract fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean 12 | 13 | fun checkPermission(sender: SeleneChatPlayer): Boolean { 14 | return if (!sender.hasPermission(permission)) { 15 | sender.sendCommandResult(SeleneChat.resource.command.generalErrorNoPermission) 16 | false 17 | } else { 18 | true 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerBungeeConsole.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import net.kyori.adventure.text.Component 5 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 6 | import net.md_5.bungee.api.CommandSender 7 | 8 | class SeleneChatPlayerBungeeConsole(private val player: CommandSender) : SeleneChatPlayerConsole() { 9 | override fun sendMessage(msg: Component) { 10 | player.sendMessage(*BungeeComponentSerializer.get().serialize(msg)) 11 | } 12 | 13 | override fun sendCommandResult(msg: Component) { 14 | sendMessage(resource.prefix.append(msg)) 15 | } 16 | 17 | override fun hasPermission(permission: String): Boolean { 18 | return player.hasPermission(permission) 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/common/ComponentSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.common 2 | 3 | import kotlinx.serialization.KSerializer 4 | import kotlinx.serialization.descriptors.PrimitiveKind 5 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor 6 | import kotlinx.serialization.descriptors.SerialDescriptor 7 | import kotlinx.serialization.encoding.Decoder 8 | import kotlinx.serialization.encoding.Encoder 9 | import net.kyori.adventure.text.Component 10 | import net.kyori.adventure.text.minimessage.MiniMessage 11 | 12 | object ComponentSerializer : KSerializer { 13 | private val mm = MiniMessage.miniMessage() 14 | override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Component", PrimitiveKind.STRING) 15 | override fun serialize(encoder: Encoder, value: Component) { 16 | encoder.encodeString(mm.serialize(value)) 17 | } 18 | 19 | override fun deserialize(decoder: Decoder): Component { 20 | return mm.deserialize(decoder.decodeString()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerVelocityConsole.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import com.velocitypowered.api.command.CommandSource 4 | import com.velocitypowered.api.proxy.ConsoleCommandSource 5 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 6 | import net.kyori.adventure.text.Component 7 | 8 | class SeleneChatPlayerVelocityConsole(private val player: ConsoleCommandSource) : SeleneChatPlayerConsole() { 9 | override fun sendMessage(msg: Component) { 10 | player.sendMessage(msg) 11 | } 12 | 13 | override fun sendCommandResult(msg: Component) { 14 | sendMessage(resource.prefix.append(msg)) 15 | } 16 | 17 | override fun hasPermission(permission: String): Boolean { 18 | return player.hasPermission(permission) 19 | } 20 | 21 | companion object { 22 | fun getPlayer(source: CommandSource): SeleneChatPlayerVelocityConsole { 23 | return SeleneChatPlayerVelocityConsole(source as ConsoleCommandSource) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/japanize/Japanizer.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.japanize 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.config 4 | import io.github.bluesheep2804.selenechat.common.ConvertMode 5 | import io.github.bluesheep2804.selenechat.common.ConvertMode.* 6 | 7 | /** 8 | * ローマ字表記を漢字に変換するクラス 9 | * @author BlueSheep2804 10 | * @see [LunaChat Github](https://github.com/ucchyocean/LunaChat/tree/master/src/main/java/com/github/ucchyocean/lc3/japanize/Japanizer.java) 11 | */ 12 | class Japanizer(private val original: String) { 13 | fun japanize(convertMode: ConvertMode): String { 14 | return when (convertMode) { 15 | NONE -> "" 16 | KANA -> RomaToKana.convert(original) 17 | IME -> IMEConverter.googleIME(RomaToKana.convert(original)) 18 | } 19 | } 20 | 21 | fun shouldConvert(): Boolean { 22 | return (original.toByteArray().size == original.length // 2バイト文字が無い 23 | && !original.matches("[ \\uFF61-\\uFF9F]+".toRegex())) // 半角カタカナが無い 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/message/PluginMessage.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.message 2 | 3 | import com.google.common.io.ByteArrayDataInput 4 | import com.google.common.io.ByteStreams 5 | import io.github.bluesheep2804.selenechat.SeleneChat 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | import java.util.* 8 | 9 | class PluginMessage(val message: String, val player: SeleneChatPlayer) { 10 | fun build(): ByteArray { 11 | val output = ByteStreams.newDataOutput() 12 | output.writeUTF(message) 13 | output.writeUTF(player.uniqueId.toString()) 14 | output.writeUTF(player.displayName) 15 | return output.toByteArray() 16 | } 17 | 18 | companion object { 19 | fun fromByteArrayDataInput(input: ByteArrayDataInput): PluginMessage { 20 | val message = input.readUTF() 21 | val playerUUID = input.readUTF() 22 | val playerDisplayName = input.readUTF() 23 | val player = SeleneChat.plugin.getPlayer(UUID.fromString(playerUUID)) 24 | return PluginMessage(message, player) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/EditFormatCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.channel.ChannelData 5 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 6 | 7 | class EditFormatCommand: SubEditCommand() { 8 | override val commandName: String = "format" 9 | override val permission: String = "selenechat.channel.format" 10 | 11 | override fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean { 12 | if (args.size < 3) { 13 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditFormatCurrentValue(channel.format)) 14 | } else { 15 | var format = args[2] 16 | if (args.size > 3) { 17 | for (i in 3 until args.size) { 18 | format += " ${args[i]}" 19 | } 20 | } 21 | channel.format = format 22 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditFormat(channel.format)) 23 | SeleneChat.channelManager.save(channel) 24 | } 25 | return true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import com.velocitypowered.api.command.CommandSource 4 | import com.velocitypowered.api.proxy.Player 5 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 6 | import net.kyori.adventure.text.Component 7 | import java.util.* 8 | 9 | class SeleneChatPlayerVelocity(private val player: Player) : SeleneChatPlayer() { 10 | override val displayName: String 11 | get() = player.username 12 | 13 | override val uniqueId: UUID 14 | get() = player.uniqueId 15 | 16 | override val currentServerName: String 17 | get() = player.currentServer.get().serverInfo.name 18 | 19 | override fun sendMessage(msg: Component) { 20 | player.sendMessage(msg) 21 | } 22 | 23 | override fun sendCommandResult(msg: Component) { 24 | sendMessage(resource.prefix.append(msg)) 25 | } 26 | 27 | override fun hasPermission(permission: String): Boolean { 28 | return player.hasPermission(permission) 29 | } 30 | 31 | companion object { 32 | fun getPlayer(source: CommandSource): SeleneChatPlayerVelocity { 33 | return SeleneChatPlayerVelocity(source as Player) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/SeleneChat.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat 2 | 3 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 4 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigData 5 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigManager 6 | import io.github.bluesheep2804.selenechat.japanize.JapanizePlayersManager 7 | import io.github.bluesheep2804.selenechat.resource.ResourceData 8 | import io.github.bluesheep2804.selenechat.resource.ResourceManager 9 | 10 | object SeleneChat { 11 | lateinit var plugin: IPlugin 12 | val configManager: SeleneChatConfigManager 13 | get() = plugin.configManager 14 | val config: SeleneChatConfigData 15 | get() = plugin.config 16 | val resourceManager: ResourceManager 17 | get() = plugin.resourceManager 18 | val resource: ResourceData 19 | get() = plugin.resource 20 | val japanizePlayersManager: JapanizePlayersManager 21 | get() = plugin.japanizePlayersManager 22 | val japanizePlayers: MutableMap 23 | get() = plugin.japanizePlayers 24 | val channelManager: ChannelManager 25 | get() = plugin.channelManager 26 | 27 | fun setPluginInstance(plugin: IPlugin) { 28 | this.plugin = plugin 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/CommandBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerBungee 4 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerBungeeConsole 5 | import net.md_5.bungee.api.CommandSender 6 | import net.md_5.bungee.api.connection.ProxiedPlayer 7 | import net.md_5.bungee.api.plugin.Command 8 | import net.md_5.bungee.api.plugin.TabExecutor 9 | 10 | abstract class CommandBungee(open val command: ICommand) : Command(command.COMMAND_NAME, command.PERMISSION, *command.COMMAND_ALIASES), TabExecutor { 11 | override fun execute(commandSender: CommandSender, args: Array) { 12 | val sender = when (commandSender) { 13 | is ProxiedPlayer -> SeleneChatPlayerBungee(commandSender) 14 | else -> SeleneChatPlayerBungeeConsole(commandSender) 15 | } 16 | command.execute(sender, args) 17 | } 18 | 19 | override fun onTabComplete(commandSender: CommandSender, args: Array): List { 20 | val sender = when (commandSender) { 21 | is ProxiedPlayer -> SeleneChatPlayerBungee(commandSender) 22 | else -> SeleneChatPlayerBungeeConsole(commandSender) 23 | } 24 | return command.suggest(sender, args) 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/listener/ChatListenerSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.listener 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.config 4 | import io.github.bluesheep2804.selenechat.SeleneChatSpigot 5 | import io.github.bluesheep2804.selenechat.message.PluginMessage 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerSpigot 7 | import net.kyori.adventure.text.Component 8 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer 9 | import org.bukkit.event.EventHandler 10 | import org.bukkit.event.Listener 11 | import org.bukkit.event.player.AsyncPlayerChatEvent 12 | 13 | class ChatListenerSpigot(private val plugin: SeleneChatSpigot) : Listener { 14 | @EventHandler 15 | fun onPlayerChat(event: AsyncPlayerChatEvent) { 16 | val message = event.message 17 | val sender = SeleneChatPlayerSpigot(event.player) 18 | 19 | val result = ChatListener.chat(message, sender) 20 | if (result is Component) 21 | event.message = LegacyComponentSerializer.legacySection().serialize(result) 22 | else 23 | event.isCancelled = true 24 | 25 | if (config.shouldSendPluginMessage) { 26 | plugin.sendPluginMessage(PluginMessage(event.message, sender).build()) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/CommandSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerSpigot 4 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerSpigotConsole 5 | import org.bukkit.command.* 6 | import org.bukkit.entity.Player 7 | 8 | abstract class CommandSpigot : CommandExecutor, TabExecutor { 9 | abstract val command: ICommand 10 | override fun onCommand(commandSender: CommandSender, command: Command, label: String, args: Array): Boolean { 11 | val sender = when (commandSender) { 12 | is Player -> SeleneChatPlayerSpigot(commandSender) 13 | is ConsoleCommandSender -> SeleneChatPlayerSpigotConsole(commandSender) 14 | else -> return false 15 | } 16 | return this.command.execute(sender, args) 17 | } 18 | 19 | override fun onTabComplete(commandSender: CommandSender, command: Command, label: String, args: Array): List { 20 | val sender = when (commandSender) { 21 | is Player -> SeleneChatPlayerSpigot(commandSender) 22 | is ConsoleCommandSender -> SeleneChatPlayerSpigotConsole(commandSender) 23 | else -> return emptyList() 24 | } 25 | return this.command.suggest(sender, args) 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/CreateCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import arrow.core.Either 4 | import io.github.bluesheep2804.selenechat.SeleneChat 5 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | 8 | class CreateCommand: SubCommand() { 9 | override val commandName: String = "create" 10 | override val permission: String = "selenechat.channel.create" 11 | 12 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 13 | if (args.size < 2) { 14 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorCreateEmpty) 15 | return false 16 | } 17 | 18 | when (val result = SeleneChat.channelManager.create(args[1].lowercase(), sender)) { 19 | is Either.Left -> { 20 | sender.sendCommandResult(when (result.value) { 21 | is ChannelManager.ChannelCreateError.AlreadyExists -> SeleneChat.resource.command.channelErrorCreateExists 22 | }) 23 | return false 24 | } 25 | is Either.Right -> sender.sendCommandResult(SeleneChat.resource.command.channelSuccessCreate(result.value)) 26 | } 27 | return true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/EditVisibleCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.channel.ChannelData 5 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 6 | 7 | class EditVisibleCommand: SubEditCommand() { 8 | override val commandName: String = "visible" 9 | override val permission: String = "selenechat.channel.visible" 10 | 11 | override fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean { 12 | if (args.size < 3) { 13 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditVisibleCurrentValue(channel.visible)) 14 | } else { 15 | channel.visible = when (args[2].lowercase()) { 16 | "true" -> true 17 | "false" -> false 18 | else -> { 19 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorEditVisibleUnexpectedArgs) 20 | return false 21 | } 22 | } 23 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditVisible(channel.visible)) 24 | SeleneChat.channelManager.save(channel) 25 | } 26 | return true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README_ja.md: -------------------------------------------------------------------------------- 1 | # SeleneChat 2 | [![License](https://img.shields.io/github/license/BlueSheep2804/SeleneChat)](https://github.com/BlueSheep2804/SeleneChat/blob/main/LICENSE) 3 | [![GitHub release (with filter)](https://img.shields.io/github/v/release/BlueSheep2804/SeleneChat)](https://github.com/BlueSheep2804/SeleneChat/releases/latest) 4 | 5 | SeleneChatは、LunaChatに影響を受けたMinecraft用チャットプラグインです。 6 | Bukkit, BungeeCord, Velocityで利用可能です。 7 | 動作確認済みのMinecraftバージョン: 1.12.2-1.20.1 8 | 9 | ## Feature 10 | - MiniMessage形式のフォーマットされたチャット 11 | - チャットで送信されたローマ字を日本語に変換 12 | - プライベートメッセージ 13 | 14 | ## Customize 15 | 詳しい説明は[wiki](https://github.com/BlueSheep2804/SeleneChat/wiki)をご覧ください。 16 | 17 | ## Support 18 | バグ報告や新機能の提案などは[Issues](https://github.com/BlueSheep2804/SeleneChat/issues)で受け付けています。 19 | [Pull requests](https://github.com/BlueSheep2804/SeleneChat/pulls)も歓迎です。 20 | 21 | ## Links 22 | - [ソースコード](https://github.com/BlueSheep2804/SeleneChat) 23 | - [SpigotMC project page](https://www.spigotmc.org/resources/selenechat.111119/) 24 | - [CurseForge project page](https://curseforge.com/minecraft/bukkit-plugins/selenechat) 25 | - [Modrinth project page](https://modrinth.com/plugin/selenechat) 26 | - [Hangar project page](https://hangar.papermc.io/BlueSheep/SeleneChat) 27 | - [Twitter](https://twitter.com/BlueSheep2804) 28 | 29 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I3I2F9ODT) 30 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerSpigotConsole.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import net.kyori.adventure.text.Component 5 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 6 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer 7 | import org.bukkit.command.CommandSender 8 | import org.bukkit.command.ConsoleCommandSender 9 | 10 | class SeleneChatPlayerSpigotConsole(private val player: ConsoleCommandSender) : SeleneChatPlayerConsole() { 11 | override fun sendMessage(msg: Component) { 12 | try { 13 | player.spigot().sendMessage(*BungeeComponentSerializer.get().serialize(msg)) 14 | } catch (_: NoSuchMethodError) { 15 | player.sendMessage(LegacyComponentSerializer.legacySection().serialize(msg)) 16 | } 17 | } 18 | 19 | override fun sendCommandResult(msg: Component) { 20 | sendMessage(resource.prefix.append(msg)) 21 | } 22 | 23 | override fun hasPermission(permission: String): Boolean { 24 | return player.hasPermission(permission) 25 | } 26 | 27 | companion object { 28 | fun getPlayer(source: CommandSender): SeleneChatPlayerSpigotConsole { 29 | return SeleneChatPlayerSpigotConsole(source as ConsoleCommandSender) 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/IPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat 2 | 3 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 4 | import io.github.bluesheep2804.selenechat.common.Platforms 5 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigData 6 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigManager 7 | import io.github.bluesheep2804.selenechat.japanize.JapanizePlayersManager 8 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 9 | import io.github.bluesheep2804.selenechat.resource.ResourceData 10 | import io.github.bluesheep2804.selenechat.resource.ResourceManager 11 | import net.kyori.adventure.text.Component 12 | import java.util.* 13 | 14 | interface IPlugin { 15 | val platform: Platforms 16 | val configManager: SeleneChatConfigManager 17 | val config: SeleneChatConfigData 18 | get() = configManager.config 19 | val resourceManager: ResourceManager 20 | val resource: ResourceData 21 | get() = resourceManager.resource 22 | val japanizePlayersManager: JapanizePlayersManager 23 | val japanizePlayers: MutableMap 24 | get() = japanizePlayersManager.japanizePlayers 25 | val channelManager: ChannelManager 26 | fun getAllPlayers(): List 27 | fun getPlayer(name: String): SeleneChatPlayer 28 | fun getPlayer(uuid: UUID): SeleneChatPlayer 29 | fun sendMessage(component: Component) 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/EditJpCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.channel.ChannelData 5 | import io.github.bluesheep2804.selenechat.common.ConvertMode 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | 8 | class EditJpCommand: SubEditCommand() { 9 | override val commandName: String = "jp" 10 | override val permission: String = "selenechat.channel.jp" 11 | 12 | override fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean { 13 | if (args.size < 3) { 14 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditJapanizeCurrentValue(channel.japanize)) 15 | } else { 16 | channel.japanize = when (args[2]) { 17 | "none" -> ConvertMode.NONE 18 | "kana" -> ConvertMode.KANA 19 | "ime" -> ConvertMode.IME 20 | else -> { 21 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorEditJapanizeUnexpectedArgs) 22 | return false 23 | } 24 | } 25 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditJapanize(channel.japanize)) 26 | SeleneChat.channelManager.save(channel) 27 | } 28 | return true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/listener/ChatListener.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.listener 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.channelManager 4 | import io.github.bluesheep2804.selenechat.SeleneChat.config 5 | import io.github.bluesheep2804.selenechat.SeleneChat.plugin 6 | import io.github.bluesheep2804.selenechat.channel.ChannelData 7 | import io.github.bluesheep2804.selenechat.common.Platforms 8 | import io.github.bluesheep2804.selenechat.message.ChatMessage 9 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 10 | import net.kyori.adventure.text.Component 11 | 12 | object ChatListener { 13 | fun chat(message: String, sender: SeleneChatPlayer): Component? { 14 | val channel = if (message.startsWith(config.globalMarker) || !config.enableChannelChat) null else channelManager.getPlayerChannel(sender) 15 | return if (channel is ChannelData) { 16 | channel.sendMessage(ChatMessage.channelChat(message, sender, channel)) 17 | null 18 | } else { 19 | val message = if (message.startsWith(config.globalMarker)) message.removePrefix(config.globalMarker) else message 20 | if (plugin.platform == Platforms.BUKKIT && !config.useSeleneChatFormat) { 21 | ChatMessage.message(message, sender) 22 | } else { 23 | plugin.sendMessage(ChatMessage.chat(message, sender)) 24 | null 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SeleneChat 2 | [![License](https://img.shields.io/github/license/BlueSheep2804/SeleneChat)](https://github.com/BlueSheep2804/SeleneChat/blob/main/LICENSE) 3 | [![GitHub release (with filter)](https://img.shields.io/github/v/release/BlueSheep2804/SeleneChat)](https://github.com/BlueSheep2804/SeleneChat/releases/latest) 4 | 5 | ([Click here for Japanese documentation. / 日本語ドキュメントはこちら](README_ja.md)) 6 | 7 | SeleneChat is a chat plugin for Minecraft inspired by LunaChat. 8 | Available at Bukkit, BungeeCord, and Velocity. 9 | Tested minecraft versions: 1.12.2-1.20.1 10 | 11 | ## Feature 12 | - MiniMessage formatted chat 13 | - Converts romaji sent in chat to Japanese 14 | - Private message 15 | 16 | ## Customize 17 | You can find detailed instructions on the [Wiki](https://github.com/BlueSheep2804/SeleneChat/wiki). 18 | 19 | ## Support 20 | Bug reports and requests for new features are welcome on [Issues](https://github.com/BlueSheep2804/SeleneChat/issues). 21 | [Pull requests](https://github.com/BlueSheep2804/SeleneChat/pulls) are also accepted. 22 | 23 | ## Links 24 | - [Source code](https://github.com/BlueSheep2804/SeleneChat) 25 | - [SpigotMC project page](https://www.spigotmc.org/resources/selenechat.111119/) 26 | - [CurseForge project page](https://curseforge.com/minecraft/bukkit-plugins/selenechat) 27 | - [Modrinth project page](https://modrinth.com/plugin/selenechat) 28 | - [Hangar project page](https://hangar.papermc.io/BlueSheep/SeleneChat) 29 | - [Twitter](https://twitter.com/BlueSheep2804) 30 | 31 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I3I2F9ODT) -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import net.kyori.adventure.text.Component 5 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 6 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer 7 | import org.bukkit.command.CommandSender 8 | import org.bukkit.entity.Player 9 | import java.util.* 10 | 11 | open class SeleneChatPlayerSpigot(private val player: Player) : SeleneChatPlayer() { 12 | override val displayName: String 13 | get() = player.displayName 14 | 15 | override val uniqueId: UUID 16 | get() = player.uniqueId 17 | 18 | override val currentServerName: String 19 | get() = "" 20 | 21 | override fun sendMessage(msg: Component) { 22 | try { 23 | player.spigot().sendMessage(*BungeeComponentSerializer.get().serialize(msg)) 24 | } catch (_: NoSuchMethodError) { 25 | player.sendMessage(LegacyComponentSerializer.legacySection().serialize(msg)) 26 | } 27 | } 28 | 29 | override fun sendCommandResult(msg: Component) { 30 | sendMessage(resource.prefix.append(msg)) 31 | } 32 | 33 | override fun hasPermission(permission: String): Boolean { 34 | return player.hasPermission(permission) 35 | } 36 | 37 | companion object { 38 | fun getPlayer(source: CommandSender): SeleneChatPlayerSpigot { 39 | return SeleneChatPlayerSpigot(source as Player) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayerBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import net.kyori.adventure.text.Component 5 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 6 | import net.md_5.bungee.api.CommandSender 7 | import net.md_5.bungee.api.connection.Connection 8 | import net.md_5.bungee.api.connection.ProxiedPlayer 9 | import java.util.* 10 | 11 | class SeleneChatPlayerBungee(private val player: ProxiedPlayer) : SeleneChatPlayer() { 12 | override val displayName: String 13 | get() = player.displayName 14 | 15 | override val uniqueId: UUID 16 | get() = player.uniqueId 17 | 18 | override val currentServerName: String 19 | get() = player.server.info.name 20 | 21 | override fun sendMessage(msg: Component) { 22 | player.sendMessage(*BungeeComponentSerializer.get().serialize(msg)) 23 | } 24 | 25 | override fun sendCommandResult(msg: Component) { 26 | sendMessage(resource.prefix.append(msg)) 27 | } 28 | 29 | override fun hasPermission(permission: String): Boolean { 30 | return player.hasPermission(permission) 31 | } 32 | 33 | companion object { 34 | fun getPlayer(source: CommandSender): SeleneChatPlayerBungee { 35 | return SeleneChatPlayerBungee(source as ProxiedPlayer) 36 | } 37 | 38 | fun getPlayer(connection: Connection): SeleneChatPlayerBungee { 39 | return SeleneChatPlayerBungee(connection as ProxiedPlayer) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/japanize/JapanizePlayersManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.japanize 2 | 3 | import com.charleskorn.kaml.Yaml 4 | import kotlinx.serialization.builtins.MapSerializer 5 | import kotlinx.serialization.builtins.serializer 6 | import java.io.File 7 | import java.io.FileInputStream 8 | import java.io.FileOutputStream 9 | 10 | class JapanizePlayersManager(private val file: File) { 11 | lateinit var japanizePlayers: MutableMap 12 | private val japanizePlayersSerializer = MapSerializer(String.serializer(), Boolean.serializer()) 13 | init { 14 | reload() 15 | } 16 | fun reload() { 17 | val japanizeFile = File(file, "japanize.yml") 18 | if (!file.exists()) { 19 | file.mkdir() 20 | } 21 | if (!japanizeFile.exists()) { 22 | japanizeFile.createNewFile() 23 | val japanizePlayers = emptyMap() 24 | val output = FileOutputStream(japanizeFile) 25 | Yaml.default.encodeToStream(japanizePlayersSerializer, japanizePlayers, output) 26 | } 27 | val japanizeFileInputStream = FileInputStream(japanizeFile) 28 | japanizePlayers = Yaml.default.decodeFromStream(japanizePlayersSerializer, japanizeFileInputStream).toMutableMap() 29 | } 30 | fun save() { 31 | val japanizeFile = File(file, "japanize.yml") 32 | if (!file.exists()) { 33 | file.mkdir() 34 | } 35 | val output = FileOutputStream(japanizeFile) 36 | Yaml.default.encodeToStream(japanizePlayersSerializer, japanizePlayers, output) 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/CommandVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import com.velocitypowered.api.command.SimpleCommand 4 | import com.velocitypowered.api.proxy.ConsoleCommandSource 5 | import com.velocitypowered.api.proxy.Player 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerVelocity 7 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerVelocityConsole 8 | 9 | abstract class CommandVelocity : SimpleCommand { 10 | abstract val command: ICommand 11 | override fun execute(invocation: SimpleCommand.Invocation) { 12 | val source = invocation.source() 13 | val args = invocation.arguments() 14 | val sender = when (source) { 15 | is Player -> SeleneChatPlayerVelocity(source) 16 | is ConsoleCommandSource -> SeleneChatPlayerVelocityConsole(source) 17 | else -> return 18 | } 19 | command.execute(sender, args) 20 | } 21 | 22 | override fun hasPermission(invocation: SimpleCommand.Invocation): Boolean { 23 | return invocation.source().hasPermission(command.PERMISSION) 24 | } 25 | 26 | override fun suggest(invocation: SimpleCommand.Invocation): List { 27 | val sender = when (val source = invocation.source()) { 28 | is Player -> SeleneChatPlayerVelocity(source) 29 | is ConsoleCommandSource -> SeleneChatPlayerVelocityConsole(source) 30 | else -> return emptyList() 31 | } 32 | val args = if (invocation.arguments().isNotEmpty()) invocation.arguments() else invocation.arguments() + "" 33 | return command.suggest(sender, args) 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/player/SeleneChatPlayer.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.player 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.SeleneChat.config 5 | import net.kyori.adventure.key.Key 6 | import net.kyori.adventure.text.Component 7 | import net.kyori.adventure.text.event.HoverEvent 8 | import net.kyori.examination.Examinable 9 | import java.util.* 10 | 11 | abstract class SeleneChatPlayer { 12 | abstract val displayName: String 13 | abstract val uniqueId: UUID 14 | abstract val currentServerName: String 15 | open val isOnline = true 16 | open val isConsole = false 17 | val isEnabledJapanize: Boolean 18 | get() { 19 | return if (SeleneChat.japanizePlayers.containsKey(uniqueId.toString())) { 20 | SeleneChat.japanizePlayers[uniqueId.toString()]!! 21 | } else { 22 | config.japanizeDefault 23 | } 24 | } 25 | abstract fun sendMessage(msg: Component) 26 | abstract fun sendCommandResult(msg: Component) 27 | open fun asHoverEvent(): HoverEvent { 28 | return HoverEvent.showEntity(Key.key("player"), uniqueId, Component.text(displayName)) 29 | } 30 | abstract fun hasPermission(permission: String): Boolean 31 | fun enableJapanize() { 32 | SeleneChat.japanizePlayers[uniqueId.toString()] = true 33 | SeleneChat.japanizePlayersManager.save() 34 | } 35 | fun disableJapanize() { 36 | SeleneChat.japanizePlayers[uniqueId.toString()] = false 37 | SeleneChat.japanizePlayersManager.save() 38 | } 39 | fun toggleJapanize() { 40 | if (isEnabledJapanize) disableJapanize() else enableJapanize() 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/JapanizeCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 4 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 5 | 6 | class JapanizeCommand : ICommand { 7 | override val COMMAND_NAME: String = JapanizeCommand.COMMAND_NAME 8 | override val COMMAND_ALIASES: Array = JapanizeCommand.COMMAND_ALIASES 9 | override val PERMISSION: String = JapanizeCommand.PERMISSION 10 | 11 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 12 | if (args.isEmpty()) { 13 | sender.sendCommandResult(resource.command.japanizeSuccessCurrentValueComponent(sender.isEnabledJapanize)) 14 | } else { 15 | if (args[0] == "on") { 16 | sender.enableJapanize() 17 | } else if (args[0] == "off") { 18 | sender.disableJapanize() 19 | } else { 20 | sender.sendCommandResult(resource.command.japanizeErrorUnexpectedArgs) 21 | return false 22 | } 23 | sender.sendCommandResult(resource.command.japanizeSuccessChangedComponent(sender.isEnabledJapanize)) 24 | } 25 | return true 26 | } 27 | 28 | override fun suggest(sender: SeleneChatPlayer, args: Array): List { 29 | return when (args.size) { 30 | 1 -> listOf("on", "off").filter { it.startsWith(args[0]) || args[0] == "" } 31 | else -> emptyList() 32 | } 33 | } 34 | 35 | companion object { 36 | const val COMMAND_NAME = "japanize" 37 | val COMMAND_ALIASES = arrayOf("jp") 38 | const val PERMISSION = "selenechat.command.japanize" 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/resource/ResourceManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.resource 2 | 3 | import com.charleskorn.kaml.Yaml 4 | import com.charleskorn.kaml.YamlConfiguration 5 | import java.io.File 6 | import java.io.FileInputStream 7 | import java.io.FileOutputStream 8 | 9 | class ResourceManager(private val file: File) { 10 | lateinit var resource: ResourceData 11 | private val yamlConfiguration = YamlConfiguration(strictMode = false, breakScalarsAt = 160) 12 | private val yaml = Yaml(configuration = yamlConfiguration) 13 | private val defaultResource = ResourceData() 14 | init { 15 | reload() 16 | } 17 | 18 | fun reload() { 19 | val resourceFile = File(file, "message.yml") 20 | if (!file.exists()) { 21 | file.mkdir() 22 | } 23 | if (!resourceFile.exists()) { 24 | resourceFile.createNewFile() 25 | val messages = ResourceData() 26 | val output = FileOutputStream(resourceFile) 27 | yaml.encodeToStream(ResourceData.serializer(), messages, output) 28 | } 29 | val resourceFileInputStream = FileInputStream(resourceFile) 30 | this.resource = yaml.decodeFromStream(ResourceData.serializer(), resourceFileInputStream) 31 | 32 | if (resource.resourceVersion < defaultResource.resourceVersion) { 33 | resource.resourceVersion = defaultResource.resourceVersion 34 | save() 35 | } 36 | } 37 | 38 | fun save() { 39 | val resourceFile = File(file, "message.yml") 40 | if (!file.exists()) { 41 | file.mkdir() 42 | } 43 | val output = FileOutputStream(resourceFile) 44 | yaml.encodeToStream(ResourceData.serializer(), resource, output) 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/EditInfoCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 5 | import io.github.bluesheep2804.selenechat.channel.ChannelData 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | import net.kyori.adventure.text.Component 8 | import net.kyori.adventure.text.format.NamedTextColor 9 | import java.util.* 10 | 11 | class EditInfoCommand: SubEditCommand() { 12 | override val commandName: String = "info" 13 | override val permission: String = "selenechat.channel.info" 14 | 15 | override fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean { 16 | val component = Component.text().append(resource.command.channelSuccessEditInfoHeader(channel)).appendNewline() 17 | 18 | if (channel.playerList.isEmpty()) { 19 | component.append(resource.command.channelSuccessEditInfoNoPlayer) 20 | } else { 21 | component.append(resource.command.channelSuccessEditInfoPlayer(channel.playerList.size)).appendNewline() 22 | channel.playerList.forEach { 23 | val player = SeleneChat.plugin.getPlayer(UUID.fromString(it)) 24 | component.append( 25 | ( 26 | if (player.isOnline) Component.text(player.displayName) else Component.translatable("gui.socialInteractions.status_offline").color(NamedTextColor.GRAY) 27 | ).hoverEvent(player.asHoverEvent()) 28 | ).append(Component.text(", ")) 29 | } 30 | } 31 | 32 | sender.sendMessage(component.build()) 33 | return true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/SeleneChatCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 5 | 6 | class SeleneChatCommand : ICommand { 7 | override val COMMAND_NAME: String = SeleneChatCommand.COMMAND_NAME 8 | override val COMMAND_ALIASES: Array = SeleneChatCommand.COMMAND_ALIASES 9 | override val PERMISSION: String = SeleneChatCommand.PERMISSION 10 | 11 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 12 | if (args.isEmpty()) { 13 | sender.sendCommandResult(SeleneChat.resource.command.selenechatErrorSubCommandEmpty) 14 | return false 15 | } 16 | if (args[0] == "reload") { 17 | if (!sender.hasPermission("selenechat.command.selenechat.reload")) { 18 | sender.sendCommandResult(SeleneChat.resource.command.selenechatErrorSubCommandPermission) 19 | return false 20 | } 21 | SeleneChat.configManager.reload() 22 | SeleneChat.resourceManager.reload() 23 | SeleneChat.japanizePlayersManager.reload() 24 | SeleneChat.channelManager.reload() 25 | sender.sendCommandResult(SeleneChat.resource.command.selenechatSuccessReload) 26 | } 27 | return true 28 | } 29 | 30 | override fun suggest(sender: SeleneChatPlayer, args: Array): List { 31 | return when (args.size) { 32 | 1 -> listOf("reload").filter { it.startsWith(args[0]) || args[0] == "" } 33 | else -> emptyList() 34 | } 35 | } 36 | 37 | companion object { 38 | const val COMMAND_NAME = "selenechat" 39 | val COMMAND_ALIASES = arrayOf("sc") 40 | const val PERMISSION = "selenechat.command.selenechat" 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/LeaveCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import arrow.core.Either 4 | import io.github.bluesheep2804.selenechat.SeleneChat 5 | import io.github.bluesheep2804.selenechat.channel.ChannelData 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | 8 | class LeaveCommand: SubCommand() { 9 | override val commandName: String = "leave" 10 | override val permission: String = "selenechat.channel.leave" 11 | 12 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 13 | val channel = if (args.size < 2) { 14 | SeleneChat.channelManager.getPlayerChannel(sender) 15 | } else { 16 | SeleneChat.channelManager.allChannels[args[1]] 17 | } 18 | if (channel is ChannelData) { 19 | when (val result = channel.leave(sender)) { 20 | is Either.Left -> { 21 | sender.sendCommandResult(when (result.value) { 22 | is ChannelData.ChannelLeaveError.NotInChannel -> SeleneChat.resource.command.channelErrorLeaveNotInChannel 23 | }) 24 | } 25 | is Either.Right -> { 26 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessLeave(channel)) 27 | 28 | if (channel.name == SeleneChat.channelManager.playerChannelMap[sender.uniqueId]) { 29 | SeleneChat.channelManager.playerChannelMap.remove(sender.uniqueId) 30 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessJoinSwitchGlobal) 31 | } 32 | } 33 | } 34 | } else { 35 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorLeaveNotFound) 36 | return false 37 | } 38 | return true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/DeleteCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import arrow.core.Either 4 | import io.github.bluesheep2804.selenechat.SeleneChat 5 | import io.github.bluesheep2804.selenechat.channel.ChannelData 6 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 7 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 8 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerConsole 9 | 10 | class DeleteCommand: SubCommand() { 11 | override val commandName: String = "delete" 12 | override val permission: String = "selenechat.channel.delete" 13 | 14 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 15 | if (args.size < 2) { 16 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorDeleteEmpty) 17 | return false 18 | } 19 | val channel = SeleneChat.channelManager.allChannels[args[1]] 20 | if (channel !is ChannelData) { 21 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorDeleteNotExists) 22 | return false 23 | } 24 | 25 | if (!channel.isModerator(sender) && sender !is SeleneChatPlayerConsole) { 26 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorDeleteNotModerator) 27 | return false 28 | } 29 | when (val result = SeleneChat.channelManager.delete(args[1])) { 30 | is Either.Left -> { 31 | sender.sendCommandResult(when (result.value) { 32 | is ChannelManager.ChannelDeleteError.ChannelNotFound -> SeleneChat.resource.command.channelErrorDeleteNotExists 33 | }) 34 | return false 35 | } 36 | is Either.Right -> sender.sendCommandResult(SeleneChat.resource.command.channelSuccessDelete(result.value)) 37 | } 38 | return true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/listener/ChatListenerVelocity.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.listener 2 | 3 | import com.velocitypowered.api.event.Subscribe 4 | import com.velocitypowered.api.event.connection.PluginMessageEvent 5 | import com.velocitypowered.api.event.player.PlayerChatEvent 6 | import io.github.bluesheep2804.selenechat.SeleneChat.config 7 | import io.github.bluesheep2804.selenechat.SeleneChat.plugin 8 | import io.github.bluesheep2804.selenechat.SeleneChatVelocity 9 | import io.github.bluesheep2804.selenechat.message.ChatMessage 10 | import io.github.bluesheep2804.selenechat.message.PluginMessage 11 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerVelocity 12 | 13 | class ChatListenerVelocity(plugin: SeleneChatVelocity) { 14 | private val proxy = plugin.proxy 15 | private val logger = plugin.logger 16 | @Subscribe 17 | fun onPlayerChatEvent(event: PlayerChatEvent) { 18 | val sender = SeleneChatPlayerVelocity(event.player) 19 | val message = event.message 20 | 21 | // デフォルトのイベントを無効化する 22 | // クライアントのバージョンが1.19.1以降だとキックされるがUnSignedVelocityで回避できる 23 | event.result = PlayerChatEvent.ChatResult.denied() 24 | ChatListener.chat(message, sender) 25 | } 26 | 27 | @Subscribe 28 | fun onPluginMessageEvent(event: PluginMessageEvent) { 29 | if (event.identifier.id != "selenechat:message") { 30 | return 31 | } 32 | if (!config.shouldReceivePluginMessage) { 33 | return 34 | } 35 | val input = event.dataAsDataStream() 36 | val pm = PluginMessage.fromByteArrayDataInput(input) 37 | val sender = plugin.getPlayer(pm.player.uniqueId) 38 | val returnMessage = ChatMessage.chat(pm.message, sender) 39 | 40 | for (server in proxy.allServers) { 41 | if (server.serverInfo.name != sender.currentServerName) { 42 | server.sendMessage(returnMessage) 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/ListCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat.channelManager 4 | import io.github.bluesheep2804.selenechat.SeleneChat.resource 5 | import io.github.bluesheep2804.selenechat.channel.ChannelData 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | import net.kyori.adventure.text.Component 8 | import net.kyori.adventure.text.format.NamedTextColor 9 | 10 | class ListCommand: SubCommand() { 11 | override val commandName: String = "list" 12 | override val permission: String = "selenechat.channel.list" 13 | 14 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 15 | val returnMessage = Component.text().append(resource.command.channelSuccessList) 16 | channelManager.allChannels.forEach { (_, channel) -> 17 | if (!channel.visible) return@forEach 18 | returnMessage.appendNewline() 19 | .append(channelIndicator(sender, channel)).appendSpace() 20 | .append(channel.displayName) 21 | .append(Component.text("(${channel.name})", NamedTextColor.GRAY)) 22 | } 23 | sender.sendCommandResult(returnMessage.build()) 24 | return true 25 | } 26 | 27 | private fun channelIndicator(sender: SeleneChatPlayer, channel: ChannelData): Component { 28 | if (channelManager.playerChannelMap[sender.uniqueId] == channel.name) { 29 | return resource.command.channelSuccessListIndicatorSpeak.hoverEvent(resource.command.channelSuccessListIndicatorSpeakHover) 30 | } 31 | 32 | if (channel.playerList.contains(sender.uniqueId.toString())) { 33 | return resource.command.channelSuccessListIndicatorJoins.hoverEvent(resource.command.channelSuccessListIndicatorJoinsHover) 34 | } 35 | 36 | return resource.command.channelSuccessListIndicator.hoverEvent(resource.command.channelSuccessListIndicatorHover) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/resource/ResourceData.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.resource 2 | 3 | import io.github.bluesheep2804.selenechat.common.ComponentSerializer 4 | import kotlinx.serialization.Serializable 5 | import net.kyori.adventure.text.Component 6 | import net.kyori.adventure.text.format.NamedTextColor 7 | import net.kyori.adventure.text.minimessage.MiniMessage 8 | 9 | @Serializable 10 | data class ResourceData( 11 | var resourceVersion: Int = 1, 12 | val configVersionOutdated: String = "The config file appears to be out of date. The config file will be loaded, but unexpected glitches may occur.", 13 | val configVersionNewer: String = "The config file has been created with a newer version than the current one. The config file will be loaded, but unexpected glitches may occur.", 14 | val configVersionLatest: String = "The config file is up-to-date!", 15 | @Serializable(with = ComponentSerializer::class) 16 | val prefix: Component = MiniMessage.miniMessage().deserialize("[<#d9e4ff>SC]"), 17 | @Serializable(with = ComponentSerializer::class) 18 | val enabled: Component = Component.text("Enabled", NamedTextColor.GREEN), 19 | @Serializable(with = ComponentSerializer::class) 20 | val disabled: Component = Component.text("Disabled", NamedTextColor.RED), 21 | @Serializable(with = ComponentSerializer::class) 22 | val offline: Component = Component.translatable("gui.socialInteractions.status_offline"), 23 | @Serializable(with = ComponentSerializer::class) 24 | val hoverTextServer: Component = Component.translatable("selectServer.select"), 25 | @Serializable(with = ComponentSerializer::class) 26 | val hoverTextChannel: Component = Component.text("Change chat channel"), 27 | val command: CommandResourceData = CommandResourceData() 28 | ) { 29 | fun switch(value: Boolean): Component { 30 | return if (value) { 31 | enabled 32 | } else { 33 | disabled 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/japanize/IMEConverter.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.japanize 2 | 3 | import com.google.common.io.CharStreams 4 | import io.github.bluesheep2804.selenechat.japanize.GoogleIME.parseJson 5 | import java.io.BufferedReader 6 | import java.io.IOException 7 | import java.io.InputStreamReader 8 | import java.io.UnsupportedEncodingException 9 | import java.net.* 10 | 11 | /** 12 | * ひらがなのみの文章を漢字に変換するクラス 13 | * @author ucchy, BlueSheep2804 14 | * @see [LunaChat Github](https://github.com/ucchyocean/LunaChat/tree/master/src/main/java/com/github/ucchyocean/lc3/japanize/IMEConverter.java) 15 | */ 16 | object IMEConverter { 17 | private const val GOOGLE_IME_URL = "http://www.google.com/transliterate?langpair=ja-Hira|ja&text=" 18 | fun googleIME(original: String?): String { 19 | var urlconn: HttpURLConnection? = null 20 | var reader: BufferedReader? = null 21 | return try { 22 | val url = URL(GOOGLE_IME_URL + URLEncoder.encode(original, "UTF-8")) 23 | urlconn = url.openConnection() as HttpURLConnection 24 | urlconn.requestMethod = "GET" 25 | urlconn.instanceFollowRedirects = false 26 | urlconn.connect() 27 | reader = BufferedReader( 28 | InputStreamReader(urlconn.inputStream, "UTF-8") 29 | ) 30 | val json = CharStreams.toString(reader) 31 | parseJson(json) 32 | } catch (e: ProtocolException) { 33 | throw RuntimeException(e) 34 | } catch (e: MalformedURLException) { 35 | throw RuntimeException(e) 36 | } catch (e: UnsupportedEncodingException) { 37 | throw RuntimeException(e) 38 | } catch (e: IOException) { 39 | throw RuntimeException(e) 40 | } finally { 41 | urlconn?.disconnect() 42 | if (reader != null) { 43 | try { 44 | reader.close() 45 | } catch (e: IOException) { 46 | throw RuntimeException(e) 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/JoinCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import arrow.core.Either 4 | import io.github.bluesheep2804.selenechat.SeleneChat 5 | import io.github.bluesheep2804.selenechat.channel.ChannelData 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 7 | 8 | class JoinCommand: SubCommand() { 9 | override val commandName: String = "join" 10 | override val permission: String = "selenechat.channel.join" 11 | 12 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 13 | if (args.size < 2) { 14 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorJoinEmpty) 15 | return false 16 | } 17 | 18 | val channel = SeleneChat.channelManager.allChannels[args[1]] 19 | if (channel is ChannelData) { 20 | when (val result = channel.join(sender)) { 21 | is Either.Left -> when (result.value) { 22 | ChannelData.ChannelJoinError.ConsolePlayer -> { 23 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorJoinConsole) 24 | return false 25 | } 26 | ChannelData.ChannelJoinError.AlreadyJoins -> {} 27 | } 28 | is Either.Right -> sender.sendCommandResult(SeleneChat.resource.command.channelSuccessJoin(channel)) 29 | } 30 | SeleneChat.channelManager.playerChannelMap[sender.uniqueId] = channel.name 31 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessJoinSwitch(channel)) 32 | } else { 33 | if (args[1] == SeleneChat.config.globalMarker) { 34 | SeleneChat.channelManager.playerChannelMap.remove(sender.uniqueId) 35 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessJoinSwitchGlobal) 36 | } else { 37 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorJoinNotFound) 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/config/SeleneChatConfigManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.config 2 | 3 | import com.charleskorn.kaml.Yaml 4 | import com.charleskorn.kaml.YamlConfiguration 5 | import io.github.bluesheep2804.selenechat.SeleneChat 6 | import java.io.File 7 | import java.io.FileInputStream 8 | import java.io.FileOutputStream 9 | 10 | class SeleneChatConfigManager(private val file: File) { 11 | lateinit var config: SeleneChatConfigData 12 | private val yamlConfiguration = YamlConfiguration(strictMode = false, breakScalarsAt = 120) 13 | private val yaml = Yaml(configuration = yamlConfiguration) 14 | private val defaultConfig = SeleneChatConfigData() 15 | init { 16 | reload() 17 | } 18 | fun reload() { 19 | val configFile = File(file, "config.yml") 20 | if (!file.exists()) { 21 | file.mkdir() 22 | } 23 | if (!configFile.exists()) { 24 | configFile.createNewFile() 25 | val config = SeleneChatConfigData() 26 | val output = FileOutputStream(configFile) 27 | yaml.encodeToStream(SeleneChatConfigData.serializer(), config, output) 28 | } 29 | val configFileInputStream = FileInputStream(configFile) 30 | config = yaml.decodeFromStream(SeleneChatConfigData.serializer(), configFileInputStream) 31 | 32 | if (config.configVersion < defaultConfig.configVersion) { 33 | config.configVersion = defaultConfig.configVersion 34 | save() 35 | } 36 | } 37 | 38 | fun save() { 39 | val configFile = File(file, "config.yml") 40 | if (!file.exists()) { 41 | file.mkdir() 42 | } 43 | val output = FileOutputStream(configFile) 44 | yaml.encodeToStream(SeleneChatConfigData.serializer(), config, output) 45 | } 46 | 47 | fun checkVersion(): String { 48 | return if (config.configVersion < defaultConfig.configVersion) SeleneChat.resource.configVersionOutdated 49 | else if (config.configVersion > defaultConfig.configVersion) SeleneChat.resource.configVersionNewer 50 | else SeleneChat.resource.configVersionLatest 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/listener/ChatListenerBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.listener 2 | 3 | import com.google.common.io.ByteStreams 4 | import io.github.bluesheep2804.selenechat.SeleneChat 5 | import io.github.bluesheep2804.selenechat.SeleneChat.config 6 | import io.github.bluesheep2804.selenechat.SeleneChatBungee 7 | import io.github.bluesheep2804.selenechat.message.ChatMessage 8 | import io.github.bluesheep2804.selenechat.message.PluginMessage 9 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerBungee 10 | import net.md_5.bungee.api.connection.ProxiedPlayer 11 | import net.md_5.bungee.api.event.ChatEvent 12 | import net.md_5.bungee.api.event.PluginMessageEvent 13 | import net.md_5.bungee.api.plugin.Listener 14 | import net.md_5.bungee.event.EventHandler 15 | 16 | class ChatListenerBungee(private val plugin: SeleneChatBungee) : Listener { 17 | private val proxy = plugin.proxy 18 | private val logger = proxy.logger 19 | @EventHandler 20 | fun onChat(event: ChatEvent) { 21 | if (event.isCommand || event.isProxyCommand) { 22 | return 23 | } 24 | if (event.sender !is ProxiedPlayer) { 25 | return 26 | } 27 | proxy.scheduler.runAsync(plugin) { 28 | val message = event.message 29 | val sender = SeleneChatPlayerBungee.getPlayer(event.sender) 30 | ChatListener.chat(message, sender) 31 | } 32 | event.isCancelled = true 33 | } 34 | 35 | @EventHandler 36 | fun onPluginMessage(event: PluginMessageEvent) { 37 | if (event.tag != "selenechat:message") { 38 | return 39 | } 40 | if (!config.shouldReceivePluginMessage) { 41 | return 42 | } 43 | val input = ByteStreams.newDataInput(event.data) 44 | val pm = PluginMessage.fromByteArrayDataInput(input) 45 | val sender = SeleneChat.plugin.getPlayer(pm.player.uniqueId) 46 | val returnMessage = ChatMessage.chat(pm.message, sender) 47 | 48 | for (player in SeleneChat.plugin.getAllPlayers()) { 49 | if (player.currentServerName != sender.currentServerName) { 50 | player.sendMessage(returnMessage) 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/MessageCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.message.ChatMessage 5 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 6 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerOffline 7 | 8 | class MessageCommand : ICommand { 9 | override val COMMAND_NAME: String = MessageCommand.COMMAND_NAME 10 | override val COMMAND_ALIASES: Array = MessageCommand.COMMAND_ALIASES 11 | override val PERMISSION: String = MessageCommand.PERMISSION 12 | 13 | override fun execute(sender: SeleneChatPlayer, args: Array): Boolean { 14 | if (args.isEmpty()) { 15 | sender.sendCommandResult(SeleneChat.resource.command.messageErrorPlayer) 16 | return false 17 | } 18 | if (args.size == 1) { 19 | sender.sendCommandResult(SeleneChat.resource.command.messageErrorMessage) 20 | return false 21 | } 22 | val receiver = SeleneChat.plugin.getPlayer(args[0]) 23 | if (receiver is SeleneChatPlayerOffline) { 24 | sender.sendCommandResult(SeleneChat.resource.command.messageErrorPlayerNotFoundComponent(args[0])) 25 | return false 26 | } 27 | 28 | var message = args[1] 29 | if (args.size > 2) { 30 | for (i in 2 until args.size) { 31 | message += " ${args[i]}" 32 | } 33 | } 34 | 35 | val returnMessage = ChatMessage.privateMessage(message, sender, receiver) 36 | sender.sendMessage(returnMessage) 37 | receiver.sendMessage(returnMessage) 38 | return true 39 | } 40 | 41 | override fun suggest(sender: SeleneChatPlayer, args: Array): List { 42 | return when (args.size) { 43 | 1 -> (SeleneChat.plugin.getAllPlayers().map { it.displayName }).filter { it.startsWith(args[0]) || args[0] == "" } 44 | else -> emptyList() 45 | } 46 | } 47 | 48 | companion object { 49 | const val COMMAND_NAME = "message" 50 | val COMMAND_ALIASES = arrayOf("msg", "tell", "w") 51 | const val PERMISSION = "selenechat.command.message" 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/channel/ChannelData.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.channel 2 | 3 | import arrow.core.Either 4 | import arrow.core.left 5 | import arrow.core.right 6 | import io.github.bluesheep2804.selenechat.SeleneChat 7 | import io.github.bluesheep2804.selenechat.common.ComponentSerializer 8 | import io.github.bluesheep2804.selenechat.common.ConvertMode 9 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 10 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerConsole 11 | import kotlinx.serialization.Serializable 12 | import net.kyori.adventure.text.Component 13 | import java.util.* 14 | 15 | @Serializable 16 | data class ChannelData( 17 | var version: Int = 1, 18 | val name: String, 19 | @Serializable(with = ComponentSerializer::class) 20 | var displayName: Component = Component.text(name), 21 | var format: String = "", 22 | var japanize: ConvertMode = ConvertMode.IME, 23 | var moderators: MutableList = mutableListOf(), 24 | val playerList: MutableList = mutableListOf(), 25 | var visible: Boolean = true, 26 | ) { 27 | fun join(player: SeleneChatPlayer): Either { 28 | if (player is SeleneChatPlayerConsole) return ChannelJoinError.ConsolePlayer.left() 29 | if (playerList.contains(player.uniqueId.toString())) return ChannelJoinError.AlreadyJoins.left() 30 | playerList.add(player.uniqueId.toString()) 31 | SeleneChat.channelManager.save(this) 32 | return player.right() 33 | } 34 | 35 | fun leave(player: SeleneChatPlayer): Either { 36 | if (!playerList.contains(player.uniqueId.toString())) return ChannelLeaveError.NotInChannel.left() 37 | playerList.remove(player.uniqueId.toString()) 38 | SeleneChat.channelManager.save(this) 39 | return player.right() 40 | } 41 | 42 | fun sendMessage(text: Component) { 43 | playerList.forEach { 44 | SeleneChat.plugin.getPlayer(UUID.fromString(it)).sendMessage(text) // TODO: プレイヤーのキャッシュをするべきな気がする 45 | } 46 | } 47 | 48 | fun isModerator(player: SeleneChatPlayer): Boolean { 49 | return moderators.contains(player.uniqueId.toString()) 50 | } 51 | 52 | sealed interface ChannelJoinError { 53 | object AlreadyJoins: ChannelJoinError 54 | object ConsolePlayer: ChannelJoinError 55 | } 56 | 57 | sealed interface ChannelLeaveError { 58 | object NotInChannel: ChannelLeaveError 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: SeleneChat 2 | version: ${version} 3 | description: Chat plugin inspired by LunaChat 4 | main: io.github.bluesheep2804.selenechat.SeleneChatSpigot 5 | author: BlueSheep2804 6 | api-version: 1.13 7 | 8 | commands: 9 | selenechat: 10 | description: SeleneChat command 11 | aliases: 12 | - sc 13 | permission: selenechat.command.selenechat 14 | message: 15 | description: Selenechat message command 16 | aliases: 17 | - msg 18 | - tell 19 | - w 20 | permission: selenechat.command.message 21 | japanize: 22 | description: SeleneChat japanize command 23 | aliases: 24 | - jp 25 | permission: selenechat.command.japanize 26 | channel: 27 | description: SeleneChat channel command 28 | aliases: 29 | - ch 30 | permission: selenechat.command.channel 31 | 32 | permissions: 33 | selenechat.colorcode: 34 | description: Allow color codes to be used in chat 35 | default: true 36 | selenechat.command.selenechat: 37 | description: Allow the use of SeleneChat commands 38 | default: op 39 | selenechat.command.selenechat.reload: 40 | description: Allow use of SeleneChat reload command 41 | default: op 42 | selenechat.command.message: 43 | description: Allow use of SeleneChat message command 44 | default: true 45 | selenechat.command.japanize: 46 | description: Allow use of SeleneChat japanize command 47 | default: true 48 | selenechat.channel: 49 | description: Allow use of SeleneChat channel command 50 | default: true 51 | selenechat.channel.create: 52 | description: Allow use of channel create command 53 | default: true 54 | selenechat.channel.delete: 55 | description: Allow use of channel delete command 56 | default: true 57 | selenechat.channel.list: 58 | description: Allow use of channel list command 59 | default: true 60 | selenechat.channel.join: 61 | description: Allow use of channel join command 62 | default: true 63 | selenechat.channel.leave: 64 | description: Allow use of channel leave command 65 | default: true 66 | 67 | selenechat.channel.format: 68 | description: Allow use of channel format command 69 | default: true 70 | selenechat.channel.info: 71 | description: Allow use of channel info command, 72 | default: true 73 | selenechat.channel.jp: 74 | description: Allow use of channel jp command 75 | default: true 76 | selenechat.channel.moderator: 77 | description: Allow use of channel moderator command 78 | default: true 79 | selenechat.channel.visible: 80 | description: Allow use of channel visible command 81 | default: true 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/command/channel/EditModeratorCommand.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.command.channel 2 | 3 | import io.github.bluesheep2804.selenechat.SeleneChat 4 | import io.github.bluesheep2804.selenechat.channel.ChannelData 5 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 6 | import net.kyori.adventure.key.Key 7 | import net.kyori.adventure.text.Component 8 | import net.kyori.adventure.text.event.HoverEvent 9 | import net.kyori.adventure.text.format.NamedTextColor 10 | import java.util.* 11 | 12 | class EditModeratorCommand: SubEditCommand() { 13 | override val commandName: String = "moderator" 14 | override val permission: String = "selenechat.channel.moderator" 15 | 16 | override fun execute(sender: SeleneChatPlayer, args: Array, channel: ChannelData): Boolean { 17 | if (args.size < 3) { 18 | val moderators = Component.text().append(SeleneChat.resource.command.channelSuccessEditModeratorCurrentValue) 19 | channel.moderators.forEach { 20 | val player = SeleneChat.plugin.getPlayer(UUID.fromString(it)) 21 | val playerComponent = if (player.isOnline) { 22 | Component.text(player.displayName).hoverEvent(player.asHoverEvent()) 23 | } else { 24 | Component.text().content("(") 25 | .append(SeleneChat.resource.offline) 26 | .append(Component.text(")${it}")) 27 | .color(NamedTextColor.GRAY) 28 | .hoverEvent(HoverEvent.showEntity(Key.key("player"), player.uniqueId)) 29 | } 30 | moderators.appendNewline() 31 | .append(Component.text("- ")) 32 | .append(playerComponent) 33 | } 34 | sender.sendCommandResult(moderators.build()) 35 | } else { 36 | val player = SeleneChat.plugin.getPlayer(if (args[2].startsWith("-")) args[2].removePrefix("-") else args[2]) 37 | if (!player.isOnline) { 38 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorEditModeratorNotOnline) 39 | return false 40 | } 41 | if (args[2].startsWith("-")) { 42 | if (!channel.isModerator(player)) { 43 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorEditModeratorNotModerator) 44 | return false 45 | } 46 | channel.moderators -= player.uniqueId.toString() 47 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditModeratorExclude(player)) 48 | } else { 49 | if (channel.isModerator(player)) { 50 | sender.sendCommandResult(SeleneChat.resource.command.channelErrorEditModeratorAlreadyModerator) 51 | return false 52 | } 53 | channel.moderators += player.uniqueId.toString() 54 | sender.sendCommandResult(SeleneChat.resource.command.channelSuccessEditModerator(player)) 55 | } 56 | SeleneChat.channelManager.save(channel) 57 | } 58 | return true 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/channel/ChannelManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.channel 2 | 3 | import arrow.core.Either 4 | import arrow.core.left 5 | import arrow.core.right 6 | import com.charleskorn.kaml.Yaml 7 | import com.charleskorn.kaml.YamlConfiguration 8 | import io.github.bluesheep2804.selenechat.SeleneChat 9 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 10 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerConsole 11 | import java.io.File 12 | import java.io.FileInputStream 13 | import java.io.FileOutputStream 14 | import java.util.* 15 | 16 | class ChannelManager(private val file: File) { 17 | private val channelDirectory = File(file, "channel") 18 | private val yamlConfiguration = YamlConfiguration(strictMode = false) 19 | private val yaml = Yaml(configuration = yamlConfiguration) 20 | val allChannels = mutableMapOf() 21 | val playerChannelMap = mutableMapOf() 22 | val defaultChannel = ChannelData(name = "") 23 | 24 | init { 25 | reload() 26 | } 27 | 28 | fun reload() { 29 | if (allChannels.isNotEmpty()) { 30 | allChannels.clear() 31 | } 32 | channelDirectory.listFiles()?.forEach { 33 | val input = FileInputStream(it) 34 | val channel = yaml.decodeFromStream(ChannelData.serializer(), input) 35 | allChannels[channel.name] = channel 36 | 37 | if (defaultChannel.version > channel.version) { 38 | channel.version = defaultChannel.version 39 | save(channel) 40 | } 41 | } 42 | } 43 | 44 | fun save(channel: ChannelData) { 45 | val channelFile = File(channelDirectory, "${channel.name}.yml") 46 | val output = FileOutputStream(channelFile) 47 | yaml.encodeToStream(ChannelData.serializer(), channel, output) 48 | } 49 | 50 | fun create(name: String, moderator: SeleneChatPlayer): Either { 51 | if (!allChannels.none { it.key == name }) { 52 | return ChannelCreateError.AlreadyExists.left() 53 | } 54 | val channelFile = File(channelDirectory, "${name}.yml") 55 | val channel = ChannelData(name=name) 56 | channel.japanize = SeleneChat.plugin.config.convertMode 57 | if (moderator !is SeleneChatPlayerConsole) { 58 | channel.moderators += moderator.uniqueId.toString() 59 | } 60 | 61 | if (!channelDirectory.exists()) { 62 | channelDirectory.mkdirs() 63 | } 64 | channelFile.createNewFile() 65 | val output = FileOutputStream(channelFile) 66 | yaml.encodeToStream(ChannelData.serializer(), channel, output) 67 | allChannels[channel.name] = channel 68 | return channel.right() 69 | } 70 | 71 | sealed interface ChannelCreateError { 72 | object AlreadyExists : ChannelCreateError 73 | } 74 | 75 | fun delete(name: String): Either { 76 | val channel = allChannels.remove(name) ?: return ChannelDeleteError.ChannelNotFound.left() 77 | val channelFile = File(channelDirectory, "${name}.yml") 78 | if (channelFile.exists()) { 79 | channelFile.delete() 80 | } 81 | return channel.right() 82 | } 83 | 84 | sealed interface ChannelDeleteError { 85 | object ChannelNotFound : ChannelDeleteError 86 | } 87 | 88 | fun getPlayerChannel(player: SeleneChatPlayer): ChannelData? { 89 | val channelName = playerChannelMap[player.uniqueId] 90 | return allChannels[channelName] 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/SeleneChatBungee.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat 2 | 3 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 4 | import io.github.bluesheep2804.selenechat.command.ChannelCommandBungee 5 | import io.github.bluesheep2804.selenechat.command.JapanizeCommandBungee 6 | import io.github.bluesheep2804.selenechat.command.MessageCommandBungee 7 | import io.github.bluesheep2804.selenechat.command.SeleneChatCommandBungee 8 | import io.github.bluesheep2804.selenechat.common.Platforms 9 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigManager 10 | import io.github.bluesheep2804.selenechat.japanize.JapanizePlayersManager 11 | import io.github.bluesheep2804.selenechat.listener.ChatListenerBungee 12 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 13 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerBungee 14 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerOffline 15 | import io.github.bluesheep2804.selenechat.resource.ResourceManager 16 | import net.kyori.adventure.platform.bungeecord.BungeeAudiences 17 | import net.kyori.adventure.text.Component 18 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 19 | import net.md_5.bungee.api.plugin.Plugin 20 | import java.util.* 21 | 22 | class SeleneChatBungee : Plugin(), IPlugin { 23 | private lateinit var adventure: BungeeAudiences 24 | override val platform = Platforms.BUNGEECORD 25 | override val configManager: SeleneChatConfigManager = SeleneChatConfigManager(dataFolder) 26 | override val resourceManager: ResourceManager = ResourceManager(dataFolder) 27 | override val japanizePlayersManager: JapanizePlayersManager = JapanizePlayersManager(dataFolder) 28 | override val channelManager: ChannelManager = ChannelManager(dataFolder) 29 | init { 30 | SeleneChat.setPluginInstance(this) 31 | 32 | logger.info(configManager.checkVersion()) 33 | } 34 | 35 | override fun onEnable() { 36 | adventure = BungeeAudiences.create(this) 37 | proxy.pluginManager.registerListener(this, ChatListenerBungee(this)) 38 | proxy.registerChannel("selenechat:message") 39 | 40 | proxy.pluginManager.registerCommand(this, SeleneChatCommandBungee()) 41 | proxy.pluginManager.registerCommand(this, MessageCommandBungee()) 42 | proxy.pluginManager.registerCommand(this, JapanizeCommandBungee()) 43 | proxy.pluginManager.registerCommand(this, ChannelCommandBungee()) 44 | 45 | logger.info("Loaded!") 46 | } 47 | 48 | override fun onDisable() { 49 | adventure.close() 50 | } 51 | 52 | override fun getAllPlayers(): List { 53 | val players = mutableListOf() 54 | for (p in proxy.players) { 55 | when (val player = getPlayer(p.uniqueId)) { 56 | is SeleneChatPlayerBungee -> players += player 57 | is SeleneChatPlayerOffline -> continue 58 | } 59 | } 60 | return players.toList() 61 | } 62 | 63 | override fun getPlayer(name: String): SeleneChatPlayer { 64 | val player = proxy.getPlayer(name) 65 | return if (player == null) SeleneChatPlayerOffline(displayName = name) else SeleneChatPlayerBungee(player) 66 | } 67 | 68 | override fun getPlayer(uuid: UUID): SeleneChatPlayer { 69 | val player = proxy.getPlayer(uuid) 70 | return if (player == null) SeleneChatPlayerOffline(uuid) else SeleneChatPlayerBungee(player) 71 | } 72 | 73 | override fun sendMessage(component: Component) { 74 | proxy.broadcast(*BungeeComponentSerializer.get().serialize(component)) 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/SeleneChatSpigot.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat 2 | 3 | import io.github.bluesheep2804.selenechat.channel.ChannelManager 4 | import io.github.bluesheep2804.selenechat.command.* 5 | import io.github.bluesheep2804.selenechat.common.Platforms 6 | import io.github.bluesheep2804.selenechat.config.SeleneChatConfigManager 7 | import io.github.bluesheep2804.selenechat.japanize.JapanizePlayersManager 8 | import io.github.bluesheep2804.selenechat.listener.ChatListenerSpigot 9 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayer 10 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerOffline 11 | import io.github.bluesheep2804.selenechat.player.SeleneChatPlayerSpigot 12 | import io.github.bluesheep2804.selenechat.resource.ResourceManager 13 | import net.kyori.adventure.platform.bukkit.BukkitAudiences 14 | import net.kyori.adventure.text.Component 15 | import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer 16 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer 17 | import org.bukkit.plugin.java.JavaPlugin 18 | import java.util.* 19 | 20 | class SeleneChatSpigot : JavaPlugin(), IPlugin { 21 | private lateinit var adventure: BukkitAudiences 22 | override val platform = Platforms.BUKKIT 23 | override val configManager: SeleneChatConfigManager = SeleneChatConfigManager(dataFolder) 24 | override val resourceManager: ResourceManager = ResourceManager(dataFolder) 25 | override val japanizePlayersManager: JapanizePlayersManager = JapanizePlayersManager(dataFolder) 26 | override val channelManager: ChannelManager = ChannelManager(dataFolder) 27 | init { 28 | SeleneChat.setPluginInstance(this) 29 | 30 | logger.info(configManager.checkVersion()) 31 | } 32 | 33 | override fun onEnable() { 34 | adventure = BukkitAudiences.create(this) 35 | server.pluginManager.registerEvents(ChatListenerSpigot(this), this) 36 | server.messenger.registerOutgoingPluginChannel(this, "selenechat:message") 37 | 38 | this.getCommand(SeleneChatCommand.COMMAND_NAME)?.setExecutor(SeleneChatCommandSpigot()) 39 | this.getCommand(MessageCommand.COMMAND_NAME)?.setExecutor(MessageCommandSpigot()) 40 | this.getCommand(JapanizeCommand.COMMAND_NAME)?.setExecutor(JapanizeCommandSpigot()) 41 | this.getCommand(ChannelCommand.COMMAND_NAME)?.setExecutor(ChannelCommandSpigot()) 42 | 43 | logger.info("Loaded!") 44 | } 45 | 46 | override fun onDisable() { 47 | adventure.close() 48 | } 49 | 50 | fun sendPluginMessage(msg: ByteArray) { 51 | server.sendPluginMessage(this, "selenechat:message", msg) 52 | } 53 | 54 | override fun getAllPlayers(): List { 55 | val players = mutableListOf() 56 | for (p in server.onlinePlayers) { 57 | when (val player = getPlayer(p.uniqueId)) { 58 | is SeleneChatPlayerSpigot -> players += player 59 | is SeleneChatPlayerOffline -> continue 60 | } 61 | } 62 | return players.toList() 63 | } 64 | 65 | override fun getPlayer(name: String): SeleneChatPlayer { 66 | val player = server.getPlayerExact(name) 67 | return if (player == null) SeleneChatPlayerOffline(displayName = name) else SeleneChatPlayerSpigot(player) 68 | } 69 | 70 | override fun getPlayer(uuid: UUID): SeleneChatPlayer { 71 | val player = server.getPlayer(uuid) 72 | return if (player == null) SeleneChatPlayerOffline(uuid) else SeleneChatPlayerSpigot(player) 73 | } 74 | 75 | override fun sendMessage(component: Component) { 76 | try { 77 | server.spigot().broadcast(*BungeeComponentSerializer.get().serialize(component)) 78 | } catch (_: NoSuchMethodError) { 79 | server.broadcastMessage(LegacyComponentSerializer.legacySection().serialize(component)) 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/github/bluesheep2804/selenechat/config/SeleneChatConfigData.kt: -------------------------------------------------------------------------------- 1 | package io.github.bluesheep2804.selenechat.config 2 | 3 | import com.charleskorn.kaml.YamlComment 4 | import io.github.bluesheep2804.selenechat.common.ConvertMode 5 | import kotlinx.serialization.Serializable 6 | 7 | @Serializable 8 | data class SeleneChatConfigData( 9 | @YamlComment("Version of the config file. Do not change.") 10 | var configVersion: Int = 2, 11 | 12 | @YamlComment("Specifying the chat conversion mode.", "Possible values: none, hiragana, ime") 13 | var convertMode: ConvertMode = ConvertMode.IME, 14 | @YamlComment("Set a marker to indicate that you do not want to Japanize temporarily.") 15 | var nonJapanizeMarker: String = "$", 16 | @YamlComment("Whether to Japanize by default.") 17 | var japanizeDefault: Boolean = true, 18 | @YamlComment("Enables the channel chat feature.") 19 | var enableChannelChat: Boolean = false, 20 | @YamlComment("Set a marker for the global channel.") 21 | var globalMarker: String = "!", 22 | @YamlComment("Whether to send message content to proxy servers using plugin messages.", "Applies to Spigot only.") 23 | var shouldSendPluginMessage: Boolean = false, 24 | @YamlComment("Choose whether to send the message in the standard Minecraft message format or in SeleneChat's own format.", "If true, SeleneChat's format will be used.", "Applies to Spigot only.") 25 | var useSeleneChatFormat: Boolean = false, 26 | @YamlComment("Whether to receive plugin messages from the server to the proxy server.", "Applies to Bungeecord and Velocity only.") 27 | var shouldReceivePluginMessage: Boolean = false, 28 | @YamlComment("Whether to use color codes (e.g. &4, &b) in the chat.") 29 | var useColorCode: Boolean = true, 30 | 31 | @YamlComment( 32 | "Specifies the format of the chat.", 33 | "MiniMessage tags can be used.", 34 | " -> Display name of sender", 35 | " -> Name of the server where the sender is located", 36 | " -> Date", 37 | "