Skip to content

Commit

Permalink
v1.4.0
Browse files Browse the repository at this point in the history
- adds ability to reload the plugin configuration and reconnect to
Discord (fixes #15)
- automatically reconnects to Discord on disconnect (fixes #9)
- strips colors from messages when sending to Discord (fixes #4)
- optionally sends death messages to Discord (fixes #12)
- adds option to use display name in message templates (fixes #13)
- adds option to use world name in message templates (fixes #14)
- changes backing API library to fix changes on Discord's end (fixes
#18)
  • Loading branch information
jacobwgillespie committed Feb 8, 2016
1 parent 9d698f2 commit 818dc33
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 48 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,16 @@ settings:
password: 'password'
debug: false
relay_cancelled_messages: true
messages:
join: true
leave: true
death: false
templates:
discord:
chat_message: '<%u> %m'
player_join: '%u joined the server'
player_leave: '%u left the server'
player_death: '%r'
minecraft:
chat_message: '<%u&b(discord)&r> %m'
```
Expand All @@ -42,15 +47,34 @@ settings:
* `password` is the Discord password of your bot user
* `debug` enables more verbose logging
* `relay_cancelled_messages` will relay chat messages even if they are cancelled
* `templates` - customize the message text - `%u` will be replaced with the username and `%m` will be replaced with the message. Color codes, prefixed with `&`, will be translated on the Minecraft end.
* `messages` enables or disables certain kinds of messages
* `templates` - customize the message text
**Templates**
- `%u` will be replaced with the username
- '%d' will be replaced with the user's display name
- `%m` will be replaced with the message
- `%w` will be replaced with the world name
- `%r` will be replaced with the death reason
- Color codes, prefixed with `&`, will be translated on the Minecraft end
## Features
* Anything said in Minecraft chat will be sent to your chosen Discord channel
* Anything said in your chosen Discord channel will be sent to your Minecraft chat (with a `(discord)` suffix added to usernames)
* Join / leave messages are sent to Discord
* Death messages can optionally be sent to Discord
* Message templates are customized
## Permissions
- `discordbridge.reload` - ability to reload config and reconnect the Discord connection
## Commands
- `/discord reload` - reloads config and reconnects to Discord
## Upcoming Features
* Deeper integration into Minecraft chat (like supporting chat channels inside Minecraft)
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>gg.obsidian</groupId>
<artifactId>DiscordBridge</artifactId>
<version>1.3.0</version>
<version>1.4.0</version>
<description>Bridge chat between Discord and Minecraft</description>
<url>https://github.com/the-obsidian/DiscordBridge</url>

Expand Down
36 changes: 36 additions & 0 deletions src/main/kotlin/gg/obsidian/discordbridge/CommandHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package gg.obsidian.discordbridge

import org.bukkit.ChatColor
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player

class CommandHandler(val plugin: Plugin): CommandExecutor {

override fun onCommand(player: CommandSender, cmd: Command, alias: String?, args: Array<out String>?): Boolean {
if (player is Player && !Permissions.reload.has(player)) return true

val isConsole = (player is Player)

if (cmd.name != "discord") return true

if (args == null || args.size != 1 || !args[0].equals("reload")) {
sendMessage("&eUsage: /discord reload", player, isConsole)
return true
}

sendMessage("&eReloading Discord Bridge...", player, isConsole)
plugin.reload()
return true
}

private fun sendMessage(message: String, player: CommandSender, isConsole: Boolean) {
val formattedMessage = ChatColor.translateAlternateColorCodes('&', message)
if (isConsole) {
plugin.server.consoleSender.sendMessage(formattedMessage)
} else {
player.sendMessage(formattedMessage)
}
}
}
18 changes: 17 additions & 1 deletion src/main/kotlin/gg/obsidian/discordbridge/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@ class Configuration(val plugin: Plugin) {
var PASSWORD: String = ""
var DEBUG: Boolean = false
var RELAY_CANCELLED_MESSAGES = true

// Toggle message types
var MESSAGES_JOIN = true
var MESSAGES_LEAVE = true
var MESSAGES_DEATH = false

// Discord message templates
var TEMPLATES_DISCORD_CHAT_MESSAGE = ""
var TEMPLATES_DISCORD_PLAYER_JOIN = ""
var TEMPLATES_DISCORD_PLAYER_LEAVE = ""
var TEMPLATES_DISCORD_PLAYER_DEATH = ""

// Minecraft message templates
var TEMPLATES_MINECRAFT_CHAT_MESSAGE = ""

fun load() {
Expand All @@ -23,11 +33,17 @@ class Configuration(val plugin: Plugin) {
EMAIL = plugin.config.getString("settings.email")
PASSWORD = plugin.config.getString("settings.password")
DEBUG = plugin.config.getBoolean("settings.debug", false)
RELAY_CANCELLED_MESSAGES = plugin.config.getBoolean("settings.relay_cancelled_messages", true);
RELAY_CANCELLED_MESSAGES = plugin.config.getBoolean("settings.relay_cancelled_messages", true)

MESSAGES_JOIN = plugin.config.getBoolean("settings.messages.join", true)
MESSAGES_LEAVE = plugin.config.getBoolean("settings.messages.leave", true)
MESSAGES_DEATH = plugin.config.getBoolean("settings.messages.death", false)

TEMPLATES_DISCORD_CHAT_MESSAGE = plugin.config.getString("settings.templates.discord.chat_message", "<%u> %m")
TEMPLATES_DISCORD_PLAYER_JOIN = plugin.config.getString("settings.templates.discord.player_join", "%u joined the server")
TEMPLATES_DISCORD_PLAYER_LEAVE = plugin.config.getString("settings.templates.discord.player_leave", "%u left the server")
TEMPLATES_DISCORD_PLAYER_DEATH = plugin.config.getString("settings.templates.discord.player_death", "%r")

TEMPLATES_MINECRAFT_CHAT_MESSAGE = plugin.config.getString("settings.templates.minecraft.chat_message", "<%u&b(discord)&r> %m")
}
}
11 changes: 10 additions & 1 deletion src/main/kotlin/gg/obsidian/discordbridge/DiscordConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import net.dv8tion.jda.entities.TextChannel

class DiscordConnection(val plugin: Plugin) : Runnable {
var api = JDABuilder(plugin.configuration.EMAIL, plugin.configuration.PASSWORD).build()
var listener = DiscordListener(plugin, api, this)
var server: Guild? = null
var channel: TextChannel? = null

override fun run() {
try {
api.addEventListener(DiscordListener(plugin, api))
api.addEventListener(listener)
} catch (e: Exception) {
plugin.logger.severe("Error connecting to Discord: " + e)
}
Expand All @@ -28,6 +29,14 @@ class DiscordConnection(val plugin: Plugin) : Runnable {
channel!!.sendMessage(message)
}

fun reconnect() {
api.removeEventListener(listener)
api.shutdown(false)
api = JDABuilder(plugin.configuration.EMAIL, plugin.configuration.PASSWORD).build()
listener = DiscordListener(plugin, api, this)
api.addEventListener(listener)
}

private fun getServerById(id: String): Guild? {
for (server in api.guilds)
if (server.id.equals(id, true))
Expand Down
8 changes: 7 additions & 1 deletion src/main/kotlin/gg/obsidian/discordbridge/DiscordListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package gg.obsidian.discordbridge

import com.neovisionaries.ws.client.WebSocket
import com.neovisionaries.ws.client.WebSocketException
import com.neovisionaries.ws.client.WebSocketFrame
import net.dv8tion.jda.JDA
import net.dv8tion.jda.events.message.MessageReceivedEvent
import net.dv8tion.jda.hooks.ListenerAdapter

class DiscordListener(val plugin: Plugin, val api: JDA) : ListenerAdapter() {
class DiscordListener(val plugin: Plugin, val api: JDA, val connection: DiscordConnection) : ListenerAdapter() {

override fun onMessageReceived(event: MessageReceivedEvent) {
plugin.logDebug("Received message ${event.message.id} from Discord")
Expand Down Expand Up @@ -35,4 +36,9 @@ class DiscordListener(val plugin: Plugin, val api: JDA) : ListenerAdapter() {
fun onUnexpectedError(ws: WebSocket, wse: WebSocketException) {
plugin.logger.severe("Unexpected error from DiscordBridge: ${wse.message}")
}

fun onDisconnected(webSocket: WebSocket, serverCloseFrame: WebSocketFrame, clientCloseFrame: WebSocketFrame, closedByServer: Boolean) {
plugin.logDebug("Discord disconnected - attempting to reconnect")
connection.reconnect()
}
}
88 changes: 88 additions & 0 deletions src/main/kotlin/gg/obsidian/discordbridge/EventListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package gg.obsidian.discordbridge

import org.bukkit.ChatColor
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.AsyncPlayerChatEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent

class EventListener(val plugin: Plugin): Listener {

@EventHandler(priority = EventPriority.MONITOR)
fun onChat(event: AsyncPlayerChatEvent) {
plugin.logDebug("Received a chat event from ${event.player.name}: ${event.message}")
if (!event.isCancelled || plugin.configuration.RELAY_CANCELLED_MESSAGES) {
val username = ChatColor.stripColor(event.player.name)
val formattedMessage = Util.formatMessage(
plugin.configuration.TEMPLATES_DISCORD_CHAT_MESSAGE,
mapOf(
"%u" to username,
"%m" to ChatColor.stripColor(event.message),
"%d" to ChatColor.stripColor(event.player.displayName),
"%w" to event.player.world.name
)
)

plugin.sendToDiscord(formattedMessage)
}
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlayerJoin(event: PlayerJoinEvent) {
if (!plugin.configuration.MESSAGES_JOIN) return

val username = ChatColor.stripColor(event.player.name)
plugin.logDebug("Received a join event for $username")

val formattedMessage = Util.formatMessage(
plugin.configuration.TEMPLATES_DISCORD_PLAYER_JOIN,
mapOf(
"%u" to username,
"%d" to ChatColor.stripColor(event.player.displayName)
)
)

plugin.sendToDiscord(formattedMessage)
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlayerQuit(event: PlayerQuitEvent) {
if (!plugin.configuration.MESSAGES_LEAVE) return

val username = ChatColor.stripColor(event.player.name)
plugin.logDebug("Received a leave event for $username")

val formattedMessage = Util.formatMessage(
plugin.configuration.TEMPLATES_DISCORD_PLAYER_LEAVE,
mapOf(
"%u" to username,
"%d" to ChatColor.stripColor(event.player.displayName)
)
)

plugin.sendToDiscord(formattedMessage)
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlayerDeath(event: PlayerDeathEvent) {
if (!plugin.configuration.MESSAGES_DEATH) return

val username = ChatColor.stripColor(event.entity.name)
plugin.logDebug("Received a death event for $username")

val formattedMessage = Util.formatMessage(
plugin.configuration.TEMPLATES_DISCORD_PLAYER_DEATH,
mapOf(
"%u" to username,
"%d" to ChatColor.stripColor(event.entity.displayName),
"%r" to event.deathMessage,
"%w" to event.entity.world.name
)
)

plugin.sendToDiscord(formattedMessage)
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/gg/obsidian/discordbridge/Permissions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gg.obsidian.discordbridge

import org.bukkit.entity.Player

enum class Permissions(val node: String) {
reload("discordbridge.reload");

fun has(player: Player): Boolean {
return player.hasPermission(node)
}
}
59 changes: 16 additions & 43 deletions src/main/kotlin/gg/obsidian/discordbridge/Plugin.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
package gg.obsidian.discordbridge

import org.bukkit.ChatColor
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.player.AsyncPlayerChatEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.plugin.java.JavaPlugin

class Plugin : JavaPlugin(), Listener {
class Plugin : JavaPlugin() {

val configuration = Configuration(this)
var connection: DiscordConnection? = null
Expand All @@ -20,39 +13,14 @@ class Plugin : JavaPlugin(), Listener {
this.connection = DiscordConnection(this)

server.scheduler.runTaskAsynchronously(this, connection)
server.pluginManager.registerEvents(this, this)
server.pluginManager.registerEvents(EventListener(this), this)
getCommand("discord").executor = CommandHandler(this)
}

// Event Handlers

@EventHandler(priority = EventPriority.MONITOR)
fun onChat(event: AsyncPlayerChatEvent) {
logDebug("Received a chat event from ${event.player.name}: ${event.message}")
if (!event.isCancelled || configuration.RELAY_CANCELLED_MESSAGES) {
val username = ChatColor.stripColor(event.player.name)
val formattedMessage = configuration.TEMPLATES_DISCORD_CHAT_MESSAGE
.replace("%u", username)
.replace("%m", event.message)
sendToDiscord(formattedMessage)
}
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlayerJoin(event: PlayerJoinEvent) {
val username = ChatColor.stripColor(event.player.name)
logDebug("Received a join event for $username")
val formattedMessage = configuration.TEMPLATES_DISCORD_PLAYER_JOIN
.replace("%u", username)
sendToDiscord(formattedMessage)
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlayerQuit(event: PlayerQuitEvent) {
val username = ChatColor.stripColor(event.player.name)
logDebug("Received a leave event for $username")
val formattedMessage = configuration.TEMPLATES_DISCORD_PLAYER_LEAVE
.replace("%u", username)
sendToDiscord(formattedMessage)
fun reload() {
reloadConfig()
configuration.load()
connection?.reconnect()
}

// Message senders
Expand All @@ -63,10 +31,15 @@ class Plugin : JavaPlugin(), Listener {
}

fun sendToMinecraft(username: String, message: String) {
val formattedMessage = ChatColor.translateAlternateColorCodes('&',
configuration.TEMPLATES_MINECRAFT_CHAT_MESSAGE
.replace("%u", username)
.replace("%m", message))
val formattedMessage = Util.formatMessage(
configuration.TEMPLATES_MINECRAFT_CHAT_MESSAGE,
mapOf(
"%u" to username,
"%m" to message
),
colors = true
)

server.broadcastMessage(formattedMessage)
}

Expand Down
16 changes: 16 additions & 0 deletions src/main/kotlin/gg/obsidian/discordbridge/Util.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gg.obsidian.discordbridge

import org.bukkit.ChatColor

object Util {
fun formatMessage(message: String, replacements: Map<String, String>, colors: Boolean = false): String {
var formattedString = message
for ((token, replacement) in replacements) {
formattedString = formattedString.replace(token, replacement)
}

if (colors) formattedString = ChatColor.translateAlternateColorCodes('&', formattedString)

return formattedString
}
}
Loading

0 comments on commit 818dc33

Please sign in to comment.