diff --git a/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt b/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt index 354588f..047eb2c 100644 --- a/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt +++ b/src/main/kotlin/net/rk4z/fabricord/Fabricord.kt @@ -1,26 +1,23 @@ package net.rk4z.fabricord -import net.fabricmc.api.DedicatedServerModInitializer import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents import net.fabricmc.fabric.api.message.v1.ServerMessageEvents import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents -import net.fabricmc.loader.api.FabricLoader import net.minecraft.text.Text import net.rk4z.fabricord.discord.DiscordBotManager import net.rk4z.fabricord.discord.DiscordEmbed import net.rk4z.fabricord.discord.DiscordPlayerEventHandler.handleMCMessage +import net.rk4z.fabricord.utils.System +import net.rk4z.s1.swiftbase.core.CB +import net.rk4z.s1.swiftbase.core.LMB +import net.rk4z.s1.swiftbase.core.Logger import net.rk4z.s1.swiftbase.fabric.DedicatedServerModEntry -import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.yaml.snakeyaml.Yaml -import java.nio.file.Path -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService class Fabricord : DedicatedServerModEntry( - "fabricord", - "net.rk4z.fabricord", - false, + id= "fabricord", + packageName = "net.rk4z.fabricord", + isDebug = false, configFile = "config.yml", availableLang = listOf("ja", "en"), langDir = "lang", @@ -28,21 +25,7 @@ class Fabricord : DedicatedServerModEntry( enableUpdateChecker = true, modrinthID = "xU8Bn98V", ) { - //TODO: 言語の実装、起動時の呼び出しの実装 companion object { - private const val MOD_ID = "fabricord" - - val logger: Logger = LoggerFactory.getLogger(Fabricord::class.simpleName) - - private val loader: FabricLoader = FabricLoader.getInstance() - val executorService: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor() - - private val serverDir: Path = loader.gameDir.toRealPath() - private val modDir: Path = serverDir.resolve(MOD_ID) - - private val yaml = Yaml() - private var initializeIsDone = false - //region Configurations // Required var botToken: String? = null @@ -62,37 +45,72 @@ class Fabricord : DedicatedServerModEntry( var botActivityMessage: String? = null var messageStyle: String? = null + + var allowMention: Boolean? = false var allowEveryone: Boolean? = false var allowHere: Boolean? = false var allowRoleMention: Boolean? = false var allowedRoles: List? = null var allowUserMention: Boolean? = false var allowedUsers: List? = null + var webHookId: String? = null //endregion } - override fun onInitializeServer() { - logger.info("Initializing Fabricord...") + val name: String = description.name + val version: String = description.version.friendlyString + + override fun onInstanceInitialized() { + CB.apply { + onCheckUpdate = { + Logger.info(LMB.getSysMessage(System.Log.CHECKING_UPDATE)) + } + + onAllVersionsRetrieved = { versionCount -> + Logger.info(LMB.getSysMessage(System.Log.ALL_VERSION_COUNT, versionCount.toString())) + } + + onNewVersionFound = { latestVersion, newerVersionCount -> + Logger.info(LMB.getSysMessage(System.Log.NEW_VERSION_COUNT, newerVersionCount.toString())) + Logger.info(LMB.getSysMessage(System.Log.LATEST_VERSION_FOUND, latestVersion, version)) + Logger.info(LMB.getSysMessage(System.Log.VIEW_LATEST_VER, MODRINTH_DOWNLOAD_URL)) + } + + onNoNewVersionFound = { + Logger.info(LMB.getSysMessage(System.Log.YOU_ARE_USING_LATEST)) + } + + onUpdateCheckFailed = { responseCode -> + Logger.warn(LMB.getSysMessage(System.Log.FAILED_TO_CHECK_UPDATE, responseCode.toString())) + } + + onUpdateCheckError = { e -> + Logger.error(LMB.getSysMessage(System.Log.ERROR_WHILE_CHECKING_UPDATE, e.message ?: LMB.getSysMessage(System.Log.Other.UNKNOWN_ERROR))) + } + } + } + + override fun onDirectoriesAndFilesInitialized() { + Logger.info(LMB.getSysMessage(System.Log.LOADING, name, version)) loadConfig() if (requiredNullCheck()) { - logger.error("Bot token or log channel ID is missing in config file.") - logger.error("Maybe you are running the mod for the first time?") - logger.error("Please check the config file at $configFile") + Logger.error(LMB.getSysMessage(System.Log.MissingRequiredParam.ITEM_0)) + Logger.error(LMB.getSysMessage(System.Log.MissingRequiredParam.ITEM_1)) + Logger.error(LMB.getSysMessage(System.Log.MissingRequiredParam.ITEM_2, "$configFile")) return } nullCheck() registerEvents() - initializeIsDone = true - logger.info("Fabricord initialized successfully.") + Logger.info(LMB.getSysMessage(System.Log.INITIALIZED, name)) } private fun registerEvents() { ServerMessageEvents.CHAT_MESSAGE.register(ServerMessageEvents.ChatMessage { message, sender, _ -> - executorService.submit { + CB.executor.executeAsync { val content = message.content.string handleMCMessage(sender, content) } @@ -102,29 +120,29 @@ class Fabricord : DedicatedServerModEntry( val player = handler.player if (!DiscordBotManager.botIsInitialized) { - player.networkHandler.disconnect(Text.of("Server is still starting up, please try again later.")) + player.networkHandler.disconnect(Text.of(LMB.getSysMessage(System.Log.STILLSTARTINGUP))) return@Join } - executorService.submit { + CB.executor.executeAsync { DiscordEmbed.sendPlayerJoinEmbed(player) } }) ServerPlayConnectionEvents.DISCONNECT.register(ServerPlayConnectionEvents.Disconnect { handler, _ -> - executorService.submit { + CB.executor.executeAsync { val player = handler.player DiscordEmbed.sendPlayerLeftEmbed(player) } }) ServerLifecycleEvents.SERVER_STARTED.register { server -> - executorService.submit { + CB.executor.executeAsync { try { DiscordBotManager.init(server) DiscordBotManager.startBot() } catch (e: Exception) { - logger.error("Failed to start Discord bot", e) + logger.error(LMB.getSysMessage(System.Log.FAILEDSTART, e)) server.stop(false) } } @@ -133,9 +151,8 @@ class Fabricord : DedicatedServerModEntry( ServerLifecycleEvents.SERVER_STOPPING.register { _ -> try { DiscordBotManager.stopBot() - executorService.shutdownNow() } catch (e: Exception) { - logger.error("Failed to stop Discord bot", e) + logger.error(LMB.getSysMessage(System.Log.FAILEDSTOP, e)) } } } @@ -163,6 +180,7 @@ class Fabricord : DedicatedServerModEntry( messageStyle = lc("MessageStyle") + allowMention = lc("AllowedMentions.AllowMention") allowEveryone = lc("AllowedMentions.AllowEveryone") allowHere = lc("AllowedMentions.AllowHere") allowRoleMention = lc("AllowedMentions.AllowRole") diff --git a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt b/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt index 38397c6..1aa8278 100644 --- a/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt +++ b/src/main/kotlin/net/rk4z/fabricord/discord/DiscordBotManager.kt @@ -5,6 +5,7 @@ import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDABuilder import net.dv8tion.jda.api.OnlineStatus import net.dv8tion.jda.api.entities.Activity +import net.dv8tion.jda.api.entities.Message import net.dv8tion.jda.api.entities.Webhook import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.message.MessageReceivedEvent @@ -14,10 +15,10 @@ import net.dv8tion.jda.api.requests.GatewayIntent import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.rk4z.fabricord.Fabricord -import net.rk4z.fabricord.Fabricord.Companion.executorService +import net.rk4z.s1.swiftbase.core.CB +import net.rk4z.s1.swiftbase.core.Logger import java.awt.Color import java.util.* -import java.util.concurrent.TimeUnit import javax.security.auth.login.LoginException object DiscordBotManager : ListenerAdapter() { @@ -33,7 +34,7 @@ object DiscordBotManager : ListenerAdapter() { } fun startBot() { - executorService.submit { + CB.executor.execute { val onlineStatus = getOnlineStatus() val activity = getBotActivity() @@ -54,20 +55,20 @@ object DiscordBotManager : ListenerAdapter() { jda?.updateCommands() botIsInitialized = true - Fabricord.logger.info("Discord bot is now online") + Logger.info("Discord bot is now online") Fabricord.serverStartMessage?.let { sendToDiscord(it) } if (Fabricord.messageStyle == "modern") { if (!Fabricord.webHookId.isNullOrBlank()) { webHook = jda?.retrieveWebhookById(Fabricord.webHookId!!)?.complete() } else { - Fabricord.logger.error("The message style is set to 'modern' but the webhook URL is not configured.") + Logger.error("The message style is set to 'modern' but the webhook URL is not configured.") } } } catch (e: LoginException) { - Fabricord.logger.error("Failed to login to Discord with the provided token", e) + Logger.error("Failed to login to Discord with the provided token", e) } catch (e: Exception) { - Fabricord.logger.error("An unexpected error occurred during Discord bot startup", e) + Logger.error("An unexpected error occurred during Discord bot startup", e) } } } @@ -75,11 +76,11 @@ object DiscordBotManager : ListenerAdapter() { fun stopBot() { if (botIsInitialized) { Fabricord.serverStopMessage?.let { sendToDiscord(it) } - Fabricord.logger.info("Discord bot is now offline") + Logger.info("Discord bot is now offline") jda?.shutdown() botIsInitialized = false } else { - Fabricord.logger.error("Discord bot is not initialized. Cannot stop the bot.") + Logger.error("Discord bot is not initialized. Cannot stop the bot.") } } @@ -113,9 +114,9 @@ object DiscordBotManager : ListenerAdapter() { private val discordListener = object : ListenerAdapter() { override fun onMessageReceived(event: MessageReceivedEvent) { - val server = server ?: return Fabricord.logger.error("MinecraftServer is not initialized. Cannot process Discord message.") + val server = server ?: return Logger.error("MinecraftServer is not initialized. Cannot process Discord message.") - executorService.submit { + CB.executor.executeAsync { val mentionedPlayers = findMentionedPlayers(event.message.contentRaw, server.playerManager.playerList) if (mentionedPlayers.isNotEmpty()) { DiscordMessageHandler.handleMentionedDiscordMessage(event, server, mentionedPlayers, false) @@ -128,13 +129,13 @@ object DiscordBotManager : ListenerAdapter() { private fun handlePlayerListCommand(event: SlashCommandInteractionEvent) { val server = server ?: run { - Fabricord.logger.error("MinecraftServer is not initialized. Cannot process /playerlist command.") + Logger.error("MinecraftServer is not initialized. Cannot process /playerlist command.") event.reply("Sorry, I can't get the player list right now.") .setEphemeral(true) .queue { - executorService.schedule({ + CB.executor.executeAsyncLater({ it.deleteOriginal().queue() - }, 5, TimeUnit.SECONDS) + }, 5000) } return } @@ -176,10 +177,42 @@ object DiscordBotManager : ListenerAdapter() { } fun sendToDiscord(message: String) { - executorService.submit { - Fabricord.logChannelID?.let { - val messageAction = jda?.getTextChannelById(it)?.sendMessage(message) - if (Fabricord.allowMentions == false) { + CB.executor.executeAsync { + Fabricord.logChannelID?.let { logChannelID -> + val messageAction = jda?.getTextChannelById(logChannelID)?.sendMessage(message) + if (Fabricord.allowMention == true) { + val allowedMentions = mutableSetOf() + + if (Fabricord.allowEveryone == true) { + allowedMentions.add(Message.MentionType.EVERYONE) + } + + if (Fabricord.allowHere == true) { + allowedMentions.add(Message.MentionType.HERE) + } + + if (Fabricord.allowRoleMention == true) { + allowedMentions.add(Message.MentionType.ROLE) + + if (!Fabricord.allowedRoles.isNullOrEmpty()) { + messageAction?.mentionRoles(Fabricord.allowedRoles!!) + } else { + messageAction?.mentionRoles(emptyList()) // 空リストを指定して全ロール許可 + } + } + + if (Fabricord.allowUserMention == true) { + allowedMentions.add(Message.MentionType.USER) + + if (!Fabricord.allowedUsers.isNullOrEmpty()) { + messageAction?.mentionUsers(Fabricord.allowedUsers!!) + } else { + messageAction?.mentionUsers(emptyList()) // 空リストを指定して全ユーザー許可 + } + } + + messageAction?.setAllowedMentions(allowedMentions) + } else { messageAction?.setAllowedMentions(emptySet()) } messageAction?.queue() diff --git a/src/main/kotlin/net/rk4z/fabricord/utils/Translations.kt b/src/main/kotlin/net/rk4z/fabricord/utils/Translations.kt new file mode 100644 index 0000000..80ed5d9 --- /dev/null +++ b/src/main/kotlin/net/rk4z/fabricord/utils/Translations.kt @@ -0,0 +1,38 @@ +package net.rk4z.fabricord.utils + +import net.rk4z.s1.swiftbase.fabric.FabricMessageKey + +@Suppress("MISSING_DEPENDENCY_SUPERCLASS_IN_TYPE_ARGUMENT", "ClassName", "unused") +open class System : FabricMessageKey { + open class Log : System() { + object LOADING : Log() + object ENABLING : Log() + object DISABLING : Log() + object INITIALIZED : Log() + + open class MissingRequiredParam : Log() { + object ITEM_0 : MissingRequiredParam() + object ITEM_1 : MissingRequiredParam() + object ITEM_2 : MissingRequiredParam() + } + + object STILLSTARTINGUP : Log() + object FAILEDSTART : Log() + object FAILEDSTOP : Log() + + object CHECKING_UPDATE : Log() + object ALL_VERSION_COUNT : Log() + object NEW_VERSION_COUNT : Log() + object VIEW_LATEST_VER : Log() + object LATEST_VERSION_FOUND : Log() + object YOU_ARE_USING_LATEST : Log() + object FAILED_TO_CHECK_UPDATE : Log() + object ERROR_WHILE_CHECKING_UPDATE : Log() + + open class Other : Log() { + object UNKNOWN : Other() + object UNKNOWN_ERROR : Other() + object ERROR : Other() + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/fabricord/lang/en.yml b/src/main/resources/assets/fabricord/lang/en.yml new file mode 100644 index 0000000..6245246 --- /dev/null +++ b/src/main/resources/assets/fabricord/lang/en.yml @@ -0,0 +1,31 @@ +langVersion: 1.0 + +system: + log: + loading: "Loading %s v%s..." + enabling: "Enabling %s v%s..." + disabling: "Disabling %s v%s..." + initialized: "%s initialized successfully." + missingrequiredparams: + - "Bot token or log channel ID is missing in config file." + - "Maybe you are running the mod for the first time?" + - "Please check the config file at %s" + stillstartingup: "Server is still starting up, please try again later." + failedstart: "Failed to start the bot: %s" + failedstop: "Failed to stop the bot: %s" + checking_update: "Checking for updates..." + all_version_count: "There are %s available versions." + new_version_count: "There are %s newer versions than the current one." + latest_version_found: "A new version (%s) is available! You are using version %s." + view_latest_ver: "View the latest version here: %s" + you_are_using_latest: "You are using the latest version of BulletinBoard." + failed_to_check_update: "Failed to check for updates: %s" + error_while_checking_update: "An error occurred while checking for updates: %s" + language_file_not_found: "Language file for %s not found!" + other: + unknown: "Unknown" + unknown_error: "An unknown error has occurred." + error: "Error" + + +main: diff --git a/src/main/resources/assets/fabricord/lang/ja.yml b/src/main/resources/assets/fabricord/lang/ja.yml new file mode 100644 index 0000000..566e5f9 --- /dev/null +++ b/src/main/resources/assets/fabricord/lang/ja.yml @@ -0,0 +1,20 @@ +langVersion: 1.0 + +system: + log: + loading: "%s v%s をロード中..." + enabling: "%s v%s を起動中..." + disabling: "%s v%s を停止中..." + checking_update: "アップデートを確認中..." + all_version_count: "利用可能なバージョンが%s個あります。" + new_version_count: "現在のバージョンより新しいバージョンが%s個あります。" + latest_version_found: "新しいバージョン (%s) が利用可能です!あなたはバージョン%sを使用しています。" + view_latest_ver: "最新版はこちらから:%s" + you_are_using_latest: "最新バージョンのBulletinBoardを使用しています。" + failed_to_check_update: "更新のチェックに失敗しました:%s" + error_while_checking_update: "更新のチェック中にエラーが発生しました:%s" + language_file_not_found: "%sの言語ファイルが見つかりません!" + other: + unknown: "不明" + unknown_error: "不明なエラーが発生しました。" + error: "エラー" \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7a30c23..a19b52c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -61,7 +61,7 @@ AllowedMentions: # Blank + AllowRole: false = No role can be mentioned. # Not Blank + AllowRole: false = It also can't be mentioned. # Blank + AllowRole: true = All roles can be mentioned. - # Not Blank + AllowRole: true = Only the specified role can be mentioned. + # Not Blank + AllowRole: true = Only the specified role (Discord Role) can be mentioned. - "" # Whether to allow @user to be sent from Minecraft to Discord. @@ -73,7 +73,7 @@ AllowedMentions: # Blank + AllowUser: false = No user can be mentioned. # Not Blank + AllowUser: false = It also can't be mentioned. # Blank + AllowUser: true = All users can be mentioned. - # Not Blank + AllowUser: true = Only the specified user can be mentioned. + # Not Blank + AllowUser: true = Only the specified user (Discord User) can be mentioned. - "" # The message that appears when the server starts.