[JAVA] I tried to link chat with Minecraft server with Discord API

Introduction

** Please note that it may be difficult to read because it is the first post on qiita. ** **

** Please note that we will not introduce how to get the API token of Discord Bot this time. ** **

Minecraft BE server software With the plugin extension of Nukkit I wrote up to the implementation of the function to display the chat in the server in real time with Discord Bot.

Drop the library with Maven

Add these to pom.xml.

pom.xml


...
<repositories>
    ...
    <repository>
        <id>jcenter</id>
        <url>http://jcenter.bintray.com</url>
    </repository>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>
...

...
<dependencies>
    ...
    <dependency>
        <groupId>com.github.austinv11</groupId>
        <artifactId>Discord4J</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>
...

If you want to drop it manually, click here (https://discord4j.com/downloads.html)

Try using the library

Log in

DiscordChatService.java


public class DiscordChatService implements Listener {
    private final String Token = "Obtained API token";
    private IDiscordClient client;
    private IChannel chatChannel;

    //Put a class that inherits PluginBase into the constructor.
    //This time, I use MyPlugin as an example.
    public DiscordChatService(MyPlugin plugin) {
        this.login();

        Server.getInstance().getPluginManager().registerEvents(this, plugin);
    }

    private void login() {
        //Log in using the API token obtained from Discord.
        this.client = new ClientBuilder().withToken(TOKEN).build();

        //When I write this@EventSubscriber Calls back the event that was attached.
        this.client.getDispatcher().registerListener(this);

        //Login
        this.client.login();
    }
}

Implement a login completion callback

DiscordChatService.java


@EventSubscriber
public synchronized void onReady(ReadyEvent event) {
    Server.getInstance().getLogger().info("The bot has logged in.");

    //Get the channel posted by the bot here.
    this.chatChannel = this.client.getChannels().stream().filter(ch -> ch.getName().equals("chat")
        && ch.getCategory().getName().equals("SERVER")).findFirst().get();
}

From here, the main subject (Discord-> Server) Chat implementation

DiscordChatService.java


@EventSubscriber
public synchronized void onMessage(MessageReceivedEvent event) throws RateLimitException, DiscordException, MissingPermissionsException {
    IMessage message = event.getMessage();
    IUser user = message.getAuthor();

    //Block bot chat.
    if (user.isBot()) return;

    //Get the channel from the message.
    IChannel channel = message.getChannel();

    //Specify Discord channels and categories(Without it, you will receive messages from all channels)
    //Unless otherwise specified, you can delete it.
    if (channel.getCategory().getName().equals("SERVER")// "SERVER"Is it a category named
            && channel.getName().equals("chat")) {// "chat"Is it a channel named

        //Message body.
        String mes = message.getContent();

        //Get the user name.
        String name = user.getName();

        //Concatenate strings and broadcast chat within the server.
        Server.getInstance().broadcastMessage("[" + name + "] " + mes);
    }
}

(Server-> Discord) Chat implementation

This was difficult. However, there is a problem with this code. The server freezes momentarily because chats are not sent asynchronously.

DiscordChatService.java


@EventHandler
public void onPlayerChatEvent(PlayerChatEvent event) {
    Player player = event.getPlayer();
    String name = player.getName();
    String msg = "[" + name + "] " + event.getMessage();

    //Implementation that takes rate limiting into consideration.(Will resend without permission)
    //If you implement it normally, you will get a RateLimitException.
    RequestBuffer.request(() -> {
        //Post to the channel.
        this.chatChannel.sendMessage(msg);
    });
}

Improved version

Here comes the Oreore implementation class

Oreore implementation class

ThreadPool.java



import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public final class ThreadPool {
    private static final HashMap<String, ActionThread> pool = new HashMap<>();

    static {
        registerThread("DiscordMassage");
    }

    public static void registerThread(String name) {
        Safe.nullCheck(name);

        ActionThread thread = new ActionThread(name);
        pool.put(name, thread);
        thread.start();
    }

    public static void addAction(String threadName, Runnable action) {
        ActionThread thread = pool.get(threadName);
        thread.addAction(action);
    }

    public static void close(String threadName) {
        ActionThread thread = pool.remove(threadName);
        thread.close();
    }

    private static class ActionThread extends Thread {
        private ConcurrentLinkedQueue<Runnable> actions = new ConcurrentLinkedQueue<>();
        private boolean isClose;

        public ActionThread(String name) {
            this.setName(name);
        }

        @Override
        public void run() {
            while (!this.isClose) {
                if (this.actions.isEmpty())
                    continue;
                this.actions.poll().run();
            }
        }

        public void addAction(Runnable action) {
            this.actions.offer(action);
        }

        public void close() {
            this.isClose = true;
        }
    }
}

And use the oleore implementation class

DiscordChatService.java


@EventHandler
public void onPlayerChatEvent(PlayerChatEvent event) {
    Player player = event.getPlayer();
    String name = player.getName();
    String msg = "[" + name + "] " + event.getMessage();

    //Use my implementation class
    ThreadPool.addAction("DiscordMassage", () -> {
        //Implementation that takes rate limiting into consideration.(Will resend without permission)
        RequestBuffer.request(() -> {
            //Post to the channel.
            this.chatChannel.sendMessage(msg);
        });
    }
}

Use black magic to erase the Discord 4J log

Because it was an obstacle personally

DiscordChatService.java


public DiscordChatService(MyPlugin plugin) {
...
    try {
        Field f = Discord4J.class.getDeclaredField("LOGGER");//Get a field called Logger
        Field modifiersField = Field.class.getDeclaredField("modifiers");//Darkness class that manages accessibility
        modifiersField.setAccessible(true);
        modifiersField.setInt(f, f.getModifiers() & ~Modifier.PRIVATE & ~Modifier.FINAL);//Tampering
        f.set(null, new DummyLogger());//Set a dummy Logger
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}
Dummy class

DummyLogger


import org.slf4j.Logger;
import org.slf4j.Marker;

public class DummyLogger implements Logger {
    @Override
    public String getName() {
        return null;
    }

    @Override
    public boolean isTraceEnabled() {
        return false;
    }


    @Override
    public void trace(String s) {

    }

    @Override
    public void trace(String s, Object o) {

    }

    @Override
    public void trace(String s, Object o, Object o1) {

    }

    @Override
    public void trace(String s, Object... object) {

    }

    @Override
    public void trace(String s, Throwable throwable) {

    }

    @Override
    public boolean isTraceEnabled(Marker marker) {
        return false;
    }

    @Override
    public void trace(Marker marker, String s) {

    }

    @Override
    public void trace(Marker marker, String s, Object o) {

    }

    @Override
    public void trace(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void trace(Marker marker, String s, Object... object) {

    }

    @Override
    public void trace(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isDebugEnabled() {
        return false;
    }

    @Override
    public void debug(String s) {

    }

    @Override
    public void debug(String s, Object o) {

    }

    @Override
    public void debug(String s, Object o, Object o1) {

    }

    @Override
    public void debug(String s, Object... object) {

    }

    @Override
    public void debug(String s, Throwable throwable) {

    }

    @Override
    public boolean isDebugEnabled(Marker marker) {
        return false;
    }

    @Override
    public void debug(Marker marker, String s) {

    }

    @Override
    public void debug(Marker marker, String s, Object o) {

    }

    @Override
    public void debug(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void debug(Marker marker, String s, Object... object) {

    }

    @Override
    public void debug(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isInfoEnabled() {
        return false;
    }

    @Override
    public void info(String s) {

    }

    @Override
    public void info(String s, Object o) {

    }

    @Override
    public void info(String s, Object o, Object o1) {

    }

    @Override
    public void info(String s, Object... object) {

    }

    @Override
    public void info(String s, Throwable throwable) {

    }

    @Override
    public boolean isInfoEnabled(Marker marker) {
        return false;
    }

    @Override
    public void info(Marker marker, String s) {

    }

    @Override
    public void info(Marker marker, String s, Object o) {

    }

    @Override
    public void info(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void info(Marker marker, String s, Object... object) {

    }

    @Override
    public void info(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isWarnEnabled() {
        return false;
    }

    @Override
    public void warn(String s) {

    }

    @Override
    public void warn(String s, Object o) {

    }

    @Override
    public void warn(String s, Object... object) {

    }

    @Override
    public void warn(String s, Object o, Object o1) {

    }

    @Override
    public void warn(String s, Throwable throwable) {

    }

    @Override
    public boolean isWarnEnabled(Marker marker) {
        return false;
    }

    @Override
    public void warn(Marker marker, String s) {

    }

    @Override
    public void warn(Marker marker, String s, Object o) {

    }

    @Override
    public void warn(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void warn(Marker marker, String s, Object... object) {

    }

    @Override
    public void warn(Marker marker, String s, Throwable throwable) {

    }

    @Override
    public boolean isErrorEnabled() {
        return false;
    }

    @Override
    public void error(String s) {

    }

    @Override
    public void error(String s, Object o) {

    }

    @Override
    public void error(String s, Object o, Object o1) {

    }

    @Override
    public void error(String s, Object... object) {

    }

    @Override
    public void error(String s, Throwable throwable) {

    }

    @Override
    public boolean isErrorEnabled(Marker marker) {
        return false;
    }

    @Override
    public void error(Marker marker, String s) {

    }

    @Override
    public void error(Marker marker, String s, Object o) {

    }

    @Override
    public void error(Marker marker, String s, Object o, Object o1) {

    }

    @Override
    public void error(Marker marker, String s, Object... object) {

    }

    @Override
    public void error(Marker marker, String s, Throwable throwable) {

    }
}

And peace has come ...

Summary

In addition to this, it is necessary to implement functions such as length limit and batch chat transmission, but I will write it if there is time in the future.

Recommended Posts

I tried to link chat with Minecraft server with Discord API
I tried to draw animation with Blazor + canvas API
I tried to interact with Java
I tried to build an API server with Go (Echo) x MySQL x Docker x Clean Architecture
I tried to check the operation of gRPC server with grpcurl
I tried to get started with WebAssembly
I tried to summarize the Stream API
I tried to implement ModanShogi with Kinx
I tried to make a Web API that connects to DB with Quarkus
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I tried to make Basic authentication with Java
I tried to manage struts configuration with Coggle
I tried to manage login information with JMX
I tried to link grafana and postgres [docker-compose]
I tried to link JavaFX and Spring Framework.
I tried to implement a server using Netty
I tried to break a block with java (1)
I tried to create a portfolio with AWS, Docker, CircleCI, Laravel [with reference link]
I tried what I wanted to try with Stream softly.
I tried to implement file upload with Spring MVC
I tried to read and output CSV with Outsystems
I tried to implement TCP / IP + BIO with JAVA
[Java 11] I tried to execute Java without compiling with javac
I started MySQL 5.7 with docker-compose and tried to connect
I tried to get started with Spring Data JPA
I tried to implement Stalin sort with Java Collector
[Java] I tried to implement Yahoo API product search
roman numerals (I tried to simplify it with hash)
I tried to check the operation of http request (Put) with Talented API Tester
I tried to make an introduction to PHP + MySQL with Docker
I tried to modernize a Java EE application with OpenShift.
I tried DI with Ruby
I tried to increase the processing speed with spiritual engineering
I tried to introduce UI animation to Pokedex using Poké API
[Rails] I tried to implement batch processing with Rake task
What I was addicted to with the Redmine REST API
I tried to automate LibreOffice Calc with Ruby + PyCall.rb (Ubuntu 18.04)
I tried UPSERT with PostgreSQL.
Link API with Spring + Vue.js
I tried BIND with Docker
I tried to verify yum-cron
I tried to create a padrino development environment with Docker
I tried to get started with Swagger using Spring Boot
I tried upgrading from CentOS 6.5 to CentOS 7 with the upgrade tool
I tried to be able to pass multiple objects with Ractor
I tried to create an API to get data from a spreadsheet in Ruby (with service account)
I tried to solve the problem of "multi-stage selection" with Ruby
I tried connecting to MySQL using JDBC Template with Spring MVC
I tried to implement the image preview function with Rails / jQuery
I tried to build an http2 development environment with Eclipse + Tomcat
I tried to implement flexible OR mapping with MyBatis Dynamic SQL
I tried connecting to Oracle Autonomous Database 21c with JDBC Thin
I tried to reimplement Ruby Float (arg, exception: true) with builtin
I tried to make an Android application with MVC now (Java)
I tried to make a group function (bulletin board) with Rails
I tried to chew C # (indexer)
I tried using JOOQ with Gradle
I tried morphological analysis with MeCab
I tried to summarize iOS 14 support
I tried UDP communication with Java
I tried to explain the method