我尝试使用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);
});
}
改良版本
这里是“我的实现类”的登场
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();
}
}
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) {
}
}
然后,和平降临了…
总结
如果有时间,还需要实现其他功能,比如长度限制和群发聊天等。