我尝试使用DiscordAPI将Minecraft服务器和聊天联系在一起

首先

因为这是我第一次在Qiita上发表文章,所以可能存在一些难以阅读的地方,请您谅解。

另外,由于本次不介绍Discord机器人API令牌的获取方法,请您提前知悉。

我已经编写了一个插件扩展,可以在MinecraftBE服务器软件Nukkit中实现使用Discord机器人实时显示服务器内聊天信息的功能。

使用Maven下载库文件

请在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>
...

需要手动点击的请点击这里。

尝试使用图书馆

让用户登录

public class DiscordChatService implements Listener {
    private final String Token = "取得したAPIトークン";
    private IDiscordClient client;
    private IChannel chatChannel;

    // PluginBaseを継承したクラスをコンストラクタにぶち込んでください。
    // 今回は例えとして MyPlugin にしています。
    public DiscordChatService(MyPlugin plugin) {
        this.login();

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

    private void login() {
        // Discordから取得したAPIトークンを使ってログイン。
        this.client = new ClientBuilder().withToken(TOKEN).build();

        // これを書くと @EventSubscriber ついたイベントをコールバックしてくれる。
        this.client.getDispatcher().registerListener(this);

        // ログイン
        this.client.login();
    }
}

实现登录完成的回调。

@EventSubscriber
public synchronized void onReady(ReadyEvent event) {
    Server.getInstance().getLogger().info("Botがログインしました。");

    // ここでBotが投稿するチャンネルを取得しておく。
    this.chatChannel = this.client.getChannels().stream().filter(ch -> ch.getName().equals("chat")
        && ch.getCategory().getName().equals("SERVER")).findFirst().get();
}

从这里开始实施Discord到服务器的聊天功能。

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

    // Botのチャットをブロック。
    if (user.isBot()) return;

    // メッセージからチャンネルを取得。
    IChannel channel = message.getChannel();

    // Discordのチャンネルとカテゴリーを指定(これが無いと全てのチャンネルからメッセージを受信することになります)
    // 特に指定が無い場合は消して構いません。
    if (channel.getCategory().getName().equals("SERVER")// "SERVER" という名前のカテゴリーであるか
            && channel.getName().equals("chat")) {// "chat" という名前のチャンネルであるか

        // メッセージ本体。
        String mes = message.getContent();

        // ユーザー名を取得。
        String name = user.getName();

        // 文字列を連結してサーバー内にチャットをブロードキャスト。
        Server.getInstance().broadcastMessage("[" + name + "] " + mes);
    }
}

(服务器-> Discord)聊天的实现

这个很难。
然而,这段代码有问题。
由于聊天消息不是异步发送的,所以服务器会短暂地冻结。

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

    // レート制限に配慮した実装。(勝手再送してくれる)
    // 普通に実装すると RateLimitException が出てしまう。
    RequestBuffer.request(() -> {
        // チャンネルに投稿。
        this.chatChannel.sendMessage(msg);
    });
}

改良版本

这里是“我的实现类”的登场

线程池.javaimport java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public final class 线程池 {
private static final HashMap<String, 动作线程> 池 = new HashMap<>();

static {
注册线程(“Discord消息”);
}

public static void 注册线程(String 名称) {
Safe.nullCheck(名称);

动作线程 线程 = new 动作线程(名称);
池.put(名称, 线程);
线程.start();
}

public static void 添加动作(String 线程名称, Runnable 动作) {
动作线程 线程 = 池.get(线程名称);
线程.添加动作(动作);
}

public static void 关闭(String 线程名称) {
动作线程 线程 = 池.remove(线程名称);
线程.关闭();
}

private static class 动作线程 extends Thread {
private ConcurrentLinkedQueue 动作队列 = new ConcurrentLinkedQueue<>();
private boolean 是否关闭;

public 动作线程(String 名称) {
this.setName(名称);
}

@Override
public void run() {
while (!this.是否关闭) {
if (this.动作队列.isEmpty())
continue;
this.动作队列.poll().run();
}
}

public void 添加动作(Runnable 动作) {
this.动作队列.offer(动作);
}

public void 关闭() {
this.是否关闭 = true;
}
}
}

使用自定义的实现类

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

    // オレオレ実装クラスを使う
    ThreadPool.addAction("DiscordMassage", () -> {
        // レート制限に配慮した実装。(勝手再送してくれる)
        RequestBuffer.request(() -> {
            // チャンネルに投稿。
            this.chatChannel.sendMessage(msg);
        });
    }
}

使用黑魔法删除Discord4J的日志

因为个人感到干扰所以

public DiscordChatService(MyPlugin plugin) {
...
    try {
        Field f = Discord4J.class.getDeclaredField("LOGGER");// Loggerというフィールドを取得
        Field modifiersField = Field.class.getDeclaredField("modifiers");// アクセシビリティを管理している闇クラス
        modifiersField.setAccessible(true);
        modifiersField.setInt(f, f.getModifiers() & ~Modifier.PRIVATE & ~Modifier.FINAL);// 改ざん
        f.set(null, new DummyLogger());// ダミーのLoggerをセット
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}
ダミークラス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) {

}
}

然后,和平降临了…

总结

如果有时间,还需要实现其他功能,比如长度限制和群发聊天等。

广告
将在 10 秒后关闭
bannerAds