1
0
Fork 0
forked from mc/VTools

master #1

Merged
NaAlOH4 merged 3 commits from mc/VTools:master into master 2023-10-28 15:23:28 +08:00
Showing only changes of commit 706c5fade5 - Show all commits

View file

@ -4,19 +4,25 @@ import com.google.common.collect.ImmutableList;
import com.pengrad.telegrambot.Callback; import com.pengrad.telegrambot.Callback;
import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.UpdatesListener; import com.pengrad.telegrambot.UpdatesListener;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.User;
import com.pengrad.telegrambot.request.EditMessageText;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.response.BaseResponse;
import com.pengrad.telegrambot.response.SendResponse; import com.pengrad.telegrambot.response.SendResponse;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import de.strifel.VTools.VTools; import de.strifel.VTools.VTools;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import java.io.IOException; import java.io.IOException;
@ -27,6 +33,8 @@ import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.BiConsumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class TGBridge { public class TGBridge {
@ -36,11 +44,14 @@ public class TGBridge {
private TelegramBot bot; private TelegramBot bot;
private String TOKEN = ""; private String TOKEN = "";
private long BOT_ID = -1;
private long CHAT_ID = 0L; private long CHAT_ID = 0L;
private int ONLINE_STATUS_MESSAGE_ID = -1;
private long backoffSec = 1L; private long backoffSec = 1L;
public TGBridge(VTools plugin) { public TGBridge(@NotNull VTools plugin) {
INSTANCE = this; INSTANCE = this;
this.plugin = plugin; this.plugin = plugin;
this.server = plugin.getServer(); this.server = plugin.getServer();
@ -49,6 +60,7 @@ public class TGBridge {
public void register() { public void register() {
server.getEventManager().register(plugin, this); server.getEventManager().register(plugin, this);
botInit(); botInit();
initUpdateThread();
} }
private void loadConfig() { private void loadConfig() {
@ -67,6 +79,7 @@ public class TGBridge {
synchronized (this) { synchronized (this) {
this.CHAT_ID = Long.parseLong(config.getOrDefault("chat_id", "0")); this.CHAT_ID = Long.parseLong(config.getOrDefault("chat_id", "0"));
this.TOKEN = config.getOrDefault("token", ""); this.TOKEN = config.getOrDefault("token", "");
this.BOT_ID = Long.parseLong(TOKEN.split(":")[0]);
} }
} catch (Exception e) { } catch (Exception e) {
plugin.logger.error("parsing config", e); plugin.logger.error("parsing config", e);
@ -82,51 +95,54 @@ public class TGBridge {
for (Update update : updates) { for (Update update : updates) {
try { try {
if (update != null && if (update != null &&
update.message() != null && update.message() != null &&
update.message().chat() != null && update.message().chat() != null &&
update.message().chat().id() == CHAT_ID && update.message().chat().id() == CHAT_ID &&
update.message().from() != null update.message().from() != null
) { ) {
if (update.message().text() != null && !update.message().text().isEmpty()) { if (update.message().text() != null && !update.message().text().isEmpty()) {
String msg = update.message().text(); String msg = update.message().text();
if (msg.equals("/list")) { switch (msg) {
ArrayList<String> out = new ArrayList<>(); case "/list":
String fmt = server.getAllPlayers().size() > 1 ? "%d players are currently connected to the proxy." : "%d player is currently connected to the proxy."; outbound(genOnlineStatus());
out.add(String.format(fmt, server.getAllPlayers().size())); break;
List<RegisteredServer> servers = new ArrayList<>(server.getAllServers()); case "/setpin":
for (RegisteredServer server : servers) { Message replyTo = update.message().replyToMessage();
List<Player> onServer = ImmutableList.copyOf(server.getPlayersConnected()); if (replyTo == null || replyTo.from() == null || replyTo.from().id() != BOT_ID) {
if (!onServer.isEmpty()) { outbound("must reply a message that from the bot");
out.add(String.format("[%s] (%d): %s", } else {
server.getServerInfo().getName(), int messageId = replyTo.messageId();
onServer.size(), ONLINE_STATUS_MESSAGE_ID = messageId > 0 ? messageId : ONLINE_STATUS_MESSAGE_ID;
onServer.stream().map(Player::getUsername).collect(Collectors.joining(", "))) outbound("done");
);
} }
} break;
outbound(String.join("\n", out)); case "/genpin":
outbound(genOnlineStatus(),
(sendMessage, sendResponse) -> {
if (!sendResponse.isOk()) {
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description()));
} else {
int messageId = sendResponse.message().messageId();
ONLINE_STATUS_MESSAGE_ID = messageId > 0 ? messageId : ONLINE_STATUS_MESSAGE_ID;
}
}
);
break;
} }
tgInbound(update.message().from(), msg); tgInbound(update.message().from(), msg);
} } else if (update.message().sticker() != null) {
else if (update.message().sticker() != null) {
tgInbound(update.message().from(), "[sticker]"); tgInbound(update.message().from(), "[sticker]");
} } else if (update.message().photo() != null) {
else if (update.message().photo() != null) {
tgInbound(update.message().from(), "[photo]"); tgInbound(update.message().from(), "[photo]");
} } else if (update.message().audio() != null) {
else if (update.message().audio() != null) {
tgInbound(update.message().from(), "[audio]"); tgInbound(update.message().from(), "[audio]");
} } else if (update.message().voice() != null) {
else if (update.message().voice() != null) {
tgInbound(update.message().from(), "[voice]"); tgInbound(update.message().from(), "[voice]");
} } else if (update.message().document() != null) {
else if (update.message().document() != null) {
tgInbound(update.message().from(), "[document]"); tgInbound(update.message().from(), "[document]");
} }
} }
} } catch (Exception e) {
catch (Exception e) {
plugin.logger.error("handling update", e); plugin.logger.error("handling update", e);
} }
} }
@ -134,7 +150,7 @@ public class TGBridge {
}, (e) -> { }, (e) -> {
plugin.logger.error("getting update", e); plugin.logger.error("getting update", e);
plugin.logger.error(String.format("waiting %ds before getting another update", backoffSec)); plugin.logger.error(String.format("waiting %ds before getting another update", backoffSec));
try { Thread.sleep(backoffSec * 1000); } catch (InterruptedException ignored) {} try {Thread.sleep(backoffSec * 1000);} catch (InterruptedException ignored) {}
backoffSec *= 2L; backoffSec *= 2L;
if (backoffSec > 3600) { if (backoffSec > 3600) {
backoffSec = 3600; backoffSec = 3600;
@ -142,6 +158,24 @@ public class TGBridge {
}); });
} }
private String genOnlineStatus() {
ArrayList<String> out = new ArrayList<>();
String fmt = server.getAllPlayers().size() > 1 ? "%d players are currently connected to the proxy." : "%d player is currently connected to the proxy.";
out.add(String.format(fmt, server.getAllPlayers().size()));
List<RegisteredServer> registeredServers = new ArrayList<>(server.getAllServers());
for (RegisteredServer registeredServer : registeredServers) {
List<Player> onServer = ImmutableList.copyOf(registeredServer.getPlayersConnected());
if (!onServer.isEmpty()) {
out.add(String.format("[%s] (%d): %s",
registeredServer.getServerInfo().getName(),
onServer.size(),
onServer.stream().map(Player::getUsername).collect(Collectors.joining(", ")))
);
}
}
return String.join("\n", out);
}
protected void tgInbound(User user, String content) { protected void tgInbound(User user, String content) {
inbound(String.format("[tg] <%s> %s", user.lastName() == null ? user.firstName() : String.format("%s %s", user.firstName(), user.lastName()), content)); inbound(String.format("[tg] <%s> %s", user.lastName() == null ? user.firstName() : String.format("%s %s", user.firstName(), user.lastName()), content));
} }
@ -155,6 +189,10 @@ public class TGBridge {
} }
protected void outbound(String content) { protected void outbound(String content) {
outbound(content, null);
}
protected void outbound(String content, @Nullable BiConsumer<SendMessage, SendResponse> onResponse) {
if (bot == null) return; if (bot == null) return;
if (content.length() > 4000) { if (content.length() > 4000) {
content = content.substring(0, 4000); content = content.substring(0, 4000);
@ -162,8 +200,12 @@ public class TGBridge {
bot.execute(new SendMessage(CHAT_ID, content), new Callback<SendMessage, SendResponse>() { bot.execute(new SendMessage(CHAT_ID, content), new Callback<SendMessage, SendResponse>() {
@Override @Override
public void onResponse(SendMessage sendMessage, SendResponse sendResponse) { public void onResponse(SendMessage sendMessage, SendResponse sendResponse) {
if (!sendResponse.isOk()) { if (onResponse == null) {
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description())); if (!sendResponse.isOk()) {
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description()));
}
} else {
onResponse.accept(sendMessage, sendResponse);
} }
} }
@ -179,21 +221,195 @@ public class TGBridge {
if (bot == null) return; if (bot == null) return;
bot.removeGetUpdatesListener(); bot.removeGetUpdatesListener();
bot.shutdown(); bot.shutdown();
PROXY_SHUT_DOWN = true;
} }
@Subscribe @Subscribe
public void onServerConnected(ServerConnectedEvent event) { public void onServerConnected(ServerConnectedEvent event) {
if (event.getPreviousServer().isEmpty()) { if (event.getPreviousServer().isEmpty()) {
if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) { if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) {
outbound(String.format("%s joined the proxy", event.getPlayer().getUsername())); joinLeftAnnounce(String.format("%s joined the proxy", event.getPlayer().getUsername()));
} }
} }
updateRequests.add(new UpdateRequest());
} }
@Subscribe @Subscribe
public void onDisconnect(DisconnectEvent event) { public void onDisconnect(DisconnectEvent event) {
if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) { if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) {
outbound(String.format("%s left the proxy", event.getPlayer().getUsername())); joinLeftAnnounce(String.format("%s left the proxy", event.getPlayer().getUsername()));
}
updateRequests.add(new UpdateRequest(2));
}
@Subscribe
public void onServerPostConnect(ServerPostConnectEvent event){
updateRequests.add(new UpdateRequest());
}
private boolean PROXY_SHUT_DOWN = false;
private static class UpdateRequest {
int updateTimes;
UpdateRequest() {
this(1);
}
UpdateRequest(int updateTimes) {
this.updateTimes = updateTimes;
} }
} }
private LinkedBlockingQueue<UpdateRequest> updateRequests = new LinkedBlockingQueue<>();
private void initUpdateThread() {
new Thread(() -> {
while (true) {
if (PROXY_SHUT_DOWN) {
setOnlineStatusNotAvailable();
return;
}
int maxiterationNum = 0;
UpdateRequest oldestRequest = null;
try {
oldestRequest = updateRequests.take();
} catch (InterruptedException ignored) {}
if (oldestRequest == null) continue;
maxiterationNum = Math.max(maxiterationNum, oldestRequest.updateTimes - 1);
while (!updateRequests.isEmpty()){
try {
maxiterationNum = Math.max(maxiterationNum, updateRequests.take().updateTimes - 1);
} catch (InterruptedException ignored) {}
}
if (!updateOnlineStatus()) {
updateRequests.add(oldestRequest); // 更新失败 回去吧您内
}
if(maxiterationNum>0){
updateRequests.add(new UpdateRequest(maxiterationNum));
}
}
}).start();
new Thread(()->{
while (true) {
if (PROXY_SHUT_DOWN) {
return;
}
String oldestMessage = null;
try {
oldestMessage = announceQueue.take();
} catch (InterruptedException ignored) {}
if(!currentAnnounce.isValid()){
currentAnnounce = new JoinLeftAnnounceMessage(oldestMessage);
continue;
}
ArrayList<String> messages = new ArrayList<>(announceQueue.size() + 1);
messages.add(oldestMessage);
while (!announceQueue.isEmpty()) {
try {
messages.add(announceQueue.take());
} catch (InterruptedException ignored) {}
}
currentAnnounce.addLines(messages);
}
}).start();
}
protected boolean updateOnlineStatus() {
return editOnlineStatusMessage(genOnlineStatus());
}
protected boolean setOnlineStatusNotAvailable() {
return editOnlineStatusMessage("(proxy already shutdown)");
}
protected boolean editOnlineStatusMessage(String text) {
if (ONLINE_STATUS_MESSAGE_ID < 1) {
return true;
}
BaseResponse response;
try {
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, text));
} catch (RuntimeException e) {return false;}
return response != null && response.isOk();
}
private class JoinLeftAnnounceMessage {
private int messageId;
private long time;
private StringBuilder text;
boolean isValid() {
if (messageId < 1) {
return false;
}
long dt = System.currentTimeMillis() - time;
return dt <= 60_000 && dt >= 0;
}
protected JoinLeftAnnounceMessage(String firstMessage) {
text = new StringBuilder(firstMessage);
time = System.currentTimeMillis();
sendAnnounceMessage();
}
private void sendAnnounceMessage() {
if (bot == null) {
messageId = -1;
return;
}
SendResponse response;
try {
response = bot.execute(new SendMessage(CHAT_ID, text.toString()));
} catch (RuntimeException e) {
messageId = -1;
return;
}
if(response.isOk() == false){
messageId = -1;
return;
}
messageId = response.message().messageId();
}
protected JoinLeftAnnounceMessage(){
messageId = 0;
time = 0;
text = new StringBuilder();
} //dummy
private void addLines(List<String> messages) {
for (String message : messages) {
text.append('\n').append(message);
}
updateAnnounceMessage();
}
private void updateAnnounceMessage() {
if(!isValid()){
plugin.logger.error("message should only push to a valid object");
return;
}
if (bot == null) {
messageId = -1;
return;
}
try {
bot.execute(new EditMessageText(CHAT_ID, messageId, text.toString()));
} catch (RuntimeException ignored) {}
}
}
private JoinLeftAnnounceMessage currentAnnounce = new JoinLeftAnnounceMessage();
private LinkedBlockingQueue<String> announceQueue = new LinkedBlockingQueue<>();
private void joinLeftAnnounce(String message){
announceQueue.add(message);
}
} }