Skip to content

Commit

Permalink
Update README, finish code, add Travis
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobwgillespie committed Nov 9, 2015
1 parent cc5ec0a commit eeac083
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 83 deletions.
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
language: java
jdk:
- oraclejdk8

deploy:
provider: releases
api_key:
secure: fUPNY9FkaCCpKytHSCCaOibPAxdJtEOsxtRXXlNgTPQ0dCXbuwJlLUwuy9bqR0ZO4t2L3UritbZ8yNrZnFiaBAxJQzLtpFOGgQdOaQUgukL4KW33oIy2+TUnV/JN/xGhXCsb6Ija9wqEVeWVTTCqDjPZYeECe46Z5NhdZsiA8RUB5IIouisnJFhM4rdY8vyLI3Xvp3WGBPxVOD+uZlALWZrUiqPhe9CJpgoOkuwijQHNb39CYtknW6Q1Z+Pg1HcukUDDpZZnHhp1lXDJOQAjdg5MAMhcpS6rk6HINPmtnmE6gCD74Iofr9NAGbLefD95UkSJUhyUOQI9mGY5nv3aCgBHIXa+9NuXsqAzkF3tXAbYAtkKf42QJzWFSRMfaBA96oADR/oCUTwVE1O+SnfGBLy2XgBWEVDZ76zaUiad7ttx+TtAhNm80ab67zBkBoMZLTKrQzy9BN5uY9B/Gc42hfiPhNwBRTlWJN8FIL8tdh3wt+nGQc5wMBKpZFm+BHPUP/GnrspbrvVka6Tfc/yavFrqOzgkbvWal54V+YHbwTozEwjTa6anIsI2bKlJwU0tlOAyHUKydiOjMDO9iV/r3y4er8nHLnbqfjEYRtFuIv5n6d9MzK0wa8eJUUlAvTMnuE82Onw2Sz9wAsFszaPSYRVkalRyV9S+HO/HMvftVU0=
file: target/DiscordBridge-*.jar
file_glob: true
on:
repo: the-obsidian/DiscordBridge
tags: true
52 changes: 30 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
# DiscordBridge [![Build Status](https://travis-ci.org/the-obsidian/DiscordBridge.svg?branch=master)](https://travis-ci.org/the-obsidian/DiscordBridge)
# DiscourseGroupSync [![Build Status](https://travis-ci.org/the-obsidian/DiscourseGroupSync.svg?branch=master)](https://travis-ci.org/the-obsidian/DiscourseGroupSync)

Bridges chat between Discord and Minecraft (Bukkit/Spigot).
Synchronizes Minecraft groups with Discourse groups

## Dependencies

* Vault

## Installation

1. Download the [latest release](https://github.com/the-obsidian/DiscordBridge/releases) from GitHub
1. Download the [latest release](https://github.com/the-obsidian/DiscourseGroupSync/releases) from GitHub
1. Add it to your `plugins` folder
1. Either run Bukkit/Spigot once to generate `DiscordBridge/config.yml` or create it using the guide below.
1. Either run Bukkit/Spigot once to generate `DiscourseGroupSync/config.yml` or create it using the guide below.
1. All done!

## Configuration

DiscordBridge has several options that can be configured in the `config.yml` file:
DiscourseGroupSync has several options that can be configured in the `config.yml` file:

```yaml
settings:
server-id: '00000000'
channel: 'test'
username: 'username'
email: '[email protected]'
password: 'password'
# The URL of your Discourse installation (without the trailing slash)
discourse-url: http://forum.example.com

# A mapping between Discourse groups (by integer ID) and Minecraft groups (by name)
groups:

# Add user to groupname when they are in Discourse group 4
- discourse: 4
minecraft: groupname

# Remove user from groupname when they are not in Discourse group 4
- discourse: 4
minecraft: groupname
remove: true

# Add user to guestgroup if they do not have any Discourse groups
- discourse: 0
minecraft: guestgroup
```
* `server-id` is the ID if your Discord server. This can be found under *Server Settings > Widget > Server ID*
* `channel` is the Discord channel name you would like to bridge with your Minecraft server
* `username` is the Discord username of your bot user
* `email` is the Discord email address of your bot user
* `password` is the Discord password of your bot user
`discourse` keys are the IDs of your chosen Discourse groups. A negative number means the absence of the group, so `discourse: -20` would target users who were not a member of group `20`. `0` is a special group meaning users who are not a member of any Discourse groups.

## 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)
* Synchronizes Minecraft groups with Discourse groups on player join

## Upcoming Features

* Ability to customize the display format
* Deeper integration into Minecraft chat (like supporting chat channels inside Minecraft)
* A "merge accounts" function to allow Minecraft players to associate their Discord accounts with their Minecraft accounts so that usernames are properly translated
* Ability to post messages to Discord on behalf of Discord users, rather than using a bot user (hopefully after the official API is released)
* Add more hooks for synchronization, including recurring tasks and possibly webhooks
31 changes: 16 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.theyeticave.net/content/repositories/pub_releases</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
Expand All @@ -44,20 +48,18 @@
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>

<dependency>
<groupId>com.github.wareninja</groupId>
<artifactId>discourse-api-client</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-gson</artifactId>
<version>1.20.0</version>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.5.0</version>
</dependency>

<dependency>
<groupId>com.mashape.unirest</groupId>
<artifactId>unirest-java</artifactId>
<version>1.4.7</version>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.5</version>
<scope>compile</scope>
</dependency>
</dependencies>

Expand Down Expand Up @@ -113,10 +115,9 @@
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<artifactSet>
<includes>
<include>org.jetbrains.kotlin:*</include>
<include>com.github.wareninja:discourse-api-client:*</include>
</includes>
<excludes>
<exclude>org.spigotmc:*</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
Expand Down
35 changes: 19 additions & 16 deletions src/main/kotlin/gg/obsidian/discoursegroupsync/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,35 @@ import java.util.*
class Configuration(val plugin: DiscourseGroupSync) {

var DISCOURSE_URL = ""
var GROUPS: MutableMap<Int, Group> = HashMap<Int, Group>()
var GROUPS: HashSet<Group> = HashSet<Group>()

fun load() {
plugin.reloadConfig()

DISCOURSE_URL = plugin.getConfig().getString("discourse-url")

if (plugin.getConfig().isList("groups")) {
for (rawDefinition in plugin.getConfig().getMapList("groups")) {
val definition = rawDefinition as Map<String, Any>
var discourseGroup: Int? = null
var minecraftGroup: String? = null
for (rawDefinition in plugin.getConfig().getMapList("groups")) {
val definition = rawDefinition as Map<String, Any>
var discourseGroup: Int? = null
var minecraftGroup: String? = null
var remove = false

if (definition.contains("discourse") && definition.getRaw("discourse") is String) {
discourseGroup = definition["discourse"] as Int
}

if (definition.contains("minecraft")) {
minecraftGroup = definition["minecraft"] as String
}
if (definition.contains("discourse")) {
discourseGroup = definition["discourse"] as Int
}

if (discourseGroup == null || minecraftGroup == null) return
if (definition.contains("minecraft")) {
minecraftGroup = definition["minecraft"] as String
}

val group = Group(discourseGroup, minecraftGroup)
GROUPS.put(discourseGroup, group)
if (definition.contains("remove")) {
remove = definition["remove"] as Boolean
}

if (discourseGroup == null || minecraftGroup == null) continue

val group = Group(discourseGroup, minecraftGroup, remove)
GROUPS.add(group)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package gg.obsidian.discoursegroupsync

import net.milkbowl.vault.permission.Permission
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.plugin.RegisteredServiceProvider
import org.bukkit.plugin.java.JavaPlugin
import java.io.File

class DiscourseGroupSync : JavaPlugin(), Listener {

val config = Configuration(this)
val userManager = UserManager(this)
var permissions: Permission? = null

override fun onEnable() {
val configFile = File(dataFolder, "config.yml")
Expand All @@ -20,11 +23,23 @@ class DiscourseGroupSync : JavaPlugin(), Listener {

config.load()

if (!setupPermissions()) {
logger.severe("Disabled due to no Vault dependency found!")
server.pluginManager.disablePlugin(this)
return
}

server.pluginManager.registerEvents(this, this)
}

@EventHandler
fun onJoin(e: PlayerJoinEvent) {
userManager.syncGroups(e.player)
}

fun setupPermissions(): Boolean {
val rsp: RegisteredServiceProvider<Permission> = getServer().getServicesManager().getRegistration(Permission::class.java)
permissions = rsp.provider
return permissions != null
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/gg/obsidian/discoursegroupsync/Group.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package gg.obsidian.discoursegroupsync

data class Group(var discourseGroup: Int, var minecraftGroup: String)
data class Group(val discourseGroup: Int, val minecraftGroup: String, val remove: Boolean = false)
20 changes: 12 additions & 8 deletions src/main/kotlin/gg/obsidian/discoursegroupsync/UUIDHelper.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
package gg.obsidian.discoursegroupsync

import com.mashape.unirest.http.HttpResponse
import com.mashape.unirest.http.JsonNode
import com.mashape.unirest.http.Unirest
import com.squareup.okhttp.OkHttpClient
import com.squareup.okhttp.Request
import org.json.simple.JSONObject
import org.json.simple.JSONValue
import java.util.*

object UUIDHelper {

val PROFILE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/"
val httpClient = OkHttpClient()

fun uuidToUsername(uuid: UUID): String {
val url = PROFILE_URL + uuid.toString().replace("-", "")
val jsonResponse: HttpResponse<JsonNode> = Unirest.get(url).asJson()
val body = jsonResponse.body.`object`
val request = Request.Builder().url(url).get().build();
val response = httpClient.newCall(request).execute()
val body = JSONValue.parse(response.body().string()) as JSONObject

if (body.has("error")) {
if (body.containsKey("error")) {
return ""
}

return body.getString("name")
return body.get("name") as String
}
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/gg/obsidian/discoursegroupsync/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package gg.obsidian.discoursegroupsync

import java.util.*

data class User(val username: String = "", val minecraftGroups: Set<String> = HashSet(), val exists: Boolean = true)
data class User(val username: String = "", val discourseGroups: Set<Int> = HashSet(), val exists: Boolean = true)
87 changes: 69 additions & 18 deletions src/main/kotlin/gg/obsidian/discoursegroupsync/UserManger.kt
Original file line number Diff line number Diff line change
@@ -1,43 +1,94 @@
package gg.obsidian.discoursegroupsync

import com.mashape.unirest.http.HttpResponse
import com.mashape.unirest.http.JsonNode
import com.mashape.unirest.http.Unirest
import com.squareup.okhttp.OkHttpClient
import com.squareup.okhttp.Request
import org.bukkit.entity.Player
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.JSONValue
import java.util.*

class UserManager(val plugin: DiscourseGroupSync) {

val httpClient = OkHttpClient()

fun getDiscourseUser(username: String): User {
val url = plugin.config.DISCOURSE_URL + "/users/" + username + ".json"
val jsonResponse: HttpResponse<JsonNode> = Unirest.get(url).asJson()
val request = Request.Builder().url(url).get().build();
val response = httpClient.newCall(request).execute()

if (jsonResponse.status != 200) {
if (response.code() != 200) {
return User(exists = false)
}

val body = jsonResponse.body.`object`

val minecraftGroups = HashSet<String>()
val bodyString = response.body().string()
val body = JSONValue.parse(bodyString) as JSONObject
val user = body["user"] as JSONObject
val customGroups = user["custom_groups"] as JSONArray

val customGroups = body.getJSONArray("custom_groups")
val discourseGroups = HashSet<Int>()

for (i in (0..customGroups.length() - 1)) {
val group = customGroups.get(i) as JsonNode
val groupId = group.getObject().getInt("id")
for (g in customGroups) {
val group = g as JSONObject
val id = group.get("id") as Long
discourseGroups.add(id.toInt())
}

if (plugin.config.GROUPS.containsKey(groupId)) {
minecraftGroups.add(plugin.config.GROUPS[groupId].minecraftGroup)
}
if (customGroups.size == 0) {
discourseGroups.add(0)
}

return User(username = username, minecraftGroups = HashSet<String>())
return User(username = username, discourseGroups = discourseGroups)
}

fun syncGroups(player: Player) {
val username = UUIDHelper.uuidToUsername(player.uniqueId)
if (username == "") return

val discourseUser = getDiscourseUser(username)
val user = getDiscourseUser(username)

val groupsToAdd = HashSet<String>()
val groupsToRemove = HashSet<String>()

for (group in plugin.config.GROUPS) {
val absence = group.discourseGroup < 0
val discordGroup = if (absence) Math.abs(group.discourseGroup) else group.discourseGroup
val hasGroup = user.discourseGroups.contains(discordGroup)

if (group.discourseGroup == 0 && user.discourseGroups.size == 0) {
if (group.remove) {
groupsToRemove.add(group.minecraftGroup)
} else {
groupsToAdd.add(group.minecraftGroup)
}
continue
}

if (hasGroup && !absence) {
if (group.remove) {
groupsToRemove.add(group.minecraftGroup)
} else {
groupsToAdd.add(group.minecraftGroup)
}
}

if (!hasGroup && absence) {
if (group.remove) {
groupsToRemove.add(group.minecraftGroup)
} else {
groupsToAdd.add(group.minecraftGroup)
}
}
}

for (group in groupsToAdd) {
plugin.logger.info("Adding " + username + " to group " + group)
plugin.permissions?.playerAddGroup(player, group)
}

for (group in groupsToRemove) {
plugin.logger.info("Removing " + username + " from group " + group)
plugin.permissions?.playerRemoveGroup(player, group)
}
}
}
}
Loading

0 comments on commit eeac083

Please sign in to comment.