Welcome readers! This week I am switching to our final language— Java, a great
name for coffee and a great language for discord bots. As stated in the
announcement channel in our discord server, all Dovahzul bots are hosted on
the Google Cloud Platform (GCP). Expect the next post to cover my experience
with compute engines on GCP, including all the mistakes I made along the way.
All the Dovahzul bots are fully functional and may be used at your discretion
anytime. Before writing code, we need to create our Java
environment. Full disclaimer: both workspace set-up and coding
sections require way more steps than the other two Dovahzul bots.
Java: Workspace
An IDE such as IntelliJ, which supports Java and Gradle may be used to develop a discord bot. The JDK version is unimportant for development of Dovahzul in this post, however I recommend you use at least Java 8 (1.8.0_211 is the exact build I am using). When deploying a bot to a cloud-hosting provider, you will need to be conscious of which version of Java you use.
 |
| new project in IntelliJ |
 |
| project file tree |
Same as the other two bots, I needed to create .env file in root folder of the project to store sensitive data. Now with all our files in place, we can work on the Gradle build script.
Java: Build Script
There is no specific reason I chose Gradle, as other build tools / dependency managers such as Maven or Ant also work. If you are unfamiliar with Gradle feel free to look at the documentation at
 |
| build script blocks: plugins, jar |
To execute an exported jar file we need to specify a manifest and the main-class attribute. The Main-Class indicates where the main method is to the JVM. The other two necessary build scripts, repositories and dependencies, load the other libraries needed to develop the bot.
 |
| build script blocks: repositories, dependencies |
For knowing whether to use the
compile or
implementation method, I followed the documentation for the dependencies. I am not entirely sure of the difference between them, but I found the non-standardization to be annoying. Thankfully, I only needed four other libraries so the process was not exactly time consuming.
 |
| custom tasks |
There are three tasks I defined, uberJar, setEnv, and run, for an easier export process. The tasks build, uberJar, and setEnv, follow a task dependency chain beginning in line 56. These tasks are technically optional since you can run the project through IntelliJ, but for practical use you will need a solution for executing an outputted jar. Additionally, the extra tasks help streamline the process for executing the project in a cloud-based environment.
- uberJar - zips all the dependencies into a single large output jar.
- setEnv - copies the .env from the root to the output directory
- run - executes the outputted jar file
 |
| execute build task |
 |
| list all tasks |
Java: JDA (Java Discord API)
The
Java Discord API sits within a long list of discord libraries according to Discord's developer portal. You can view the documentation for the library at
https://github.com/DV8FromTheWorld/JDA. Under the assumption that discord would only list relevant good-quality libraries, which library we use shouldn't be a large factor in later comparisons. Given
Java has an
object-oriented code-style,
I created three classes for our bot:
Driver,
MessageListener, and
Translator. The Driver stores a
JDA object which similarly acts as the discord client. While building the
JDA object, we must pass any listeners to the client using JDA's
addEventListerners.
 |
| lines 21-34 Driver.java |
The construction of the JDA instance appears to be streamlined with the builder design pattern and method chaining. The MessageListener extends the JDA Listener Adapter, however for shorter callbacks an anonymous class will work just as well.
 |
| lines 12-16 MessageListener.java |
Using the MessageRecievedEvent argument, the JDA instance can be retrieved by the listener.
Java: Parsing Commands
The regular expression of "\s+" makes parsing the message into a message-parts array rather easy. The message-parts split into the command and message arguments variables. After testing the message should be processed, a switch statement handles each command.
 |
| lines 18-28 MessageListener.java |
The Translator class processes the message content and returns the translation using the message reply method. JDA required me to enter the message onto its action queue, perhaps for asynchronous purposes.
 |
| lines 38-56 MessageListener.java |
Java: POST Request (No Asynchronous)
To send the HTTP request to the translator api, I used Apache HttpClient. For more information on HttpClient, the documentation resides at https://hc.apache.org/httpcomponents-client-5.1.x/index.html. For the sake of avoiding Java threading, I will be using their legacy synchronous approach. Please note asynchronous calls seem to be better, especially for bots with heavy loads and sharding.
 |
| lines 19-29 Translator.java |
After the Translator receives the text and translator api key, the translate method sends the request. The request parameters are specified using a NameValuePair object, which are then encoded into a Entity.
 |
| lines 49-59 Translator.java |
The line, httpclient.execute(httpPost), sends the generated post request to the server and receive a returned response entity. Reading the translation from response entity deserves its own section as there are many steps.
 |
| lines 61-64 Translator.java |
Java: Parsing Response
An old yet decent library for parsing JSON, simple.json, is overviewed at https://www.tutorialspoint.com/json_simple/json_simple_useful_resources.htm. The developers poorly documented the library, but regardless— like its name suggests, it is simple to use. Other better alternatives for parsing JSON exist, I just clumsily stumbled upon simple.json first. Before creating a simple.json parser, we need extract the JSON as a string from the response entity. The response entity getContent returns an InputStream we need to read.
 |
| lines 61-64 Translator.java |
The readResponseString uses a BufferedReader to build each of the response lines into a string.
 |
| lines 66-73 Translator.java |
Once we have the response as a recognizable JSON string, simple.json can retrieve the translation using a JSONParser. After we do not need the entity anymore I consume it using EntityUtils. This closes any lurking content streams which may cause memory issues.
Java: Results
With success, the java version of Dovahzul performed identically to the other languages. According to my experience, JDA has quite a steep learning curve. For even those already proficient with Java, writing a discord bot presents challenges. Previously familiar with object-oriented code, I believe discord bots are better off coded in more functional style— at least with outwards HTTP requests. Writing the request synchronously did not appear to have any impact with only a few people using the bots. The project also functioned as expected when exported using gradle build and gradle run. When forcefully exiting the bot, I noticed the process would hang for a lot longer than the other bots. I am unsure whether this was caused by JVM being naturally slower, or some other unknown external factor from my machine.
Language Comparison As an object-oriented language Java code is naturally more verbose. The innate strict structure of Java lead to less bugs and an easier debugging process. Unlike Node and Python, Java datatypes produce noticeable compiler errors. The price to pay for easy debugging may be too much to bear in the case of JSON parsing. Compared to Node and Python, the HTTP request and JSON processing segments were extremely long and complex. Projects with a large tech stack, and many moving people and parts may work better with the strictness and neatness of Java. For the purposes of writing Dovahzul, Python seemed to be the best choice.
If you want to analyze the code for yourself, all three bots are uploaded on GitHub. Please feel free to follow and star my repos!
Comments
Post a Comment