反解析md,没测试
This commit is contained in:
parent
c2ee4fca4f
commit
e4c91adac0
2 changed files with 355 additions and 107 deletions
222
src/main/java/de/strifel/VTools/listeners/MarkdownString.java
Normal file
222
src/main/java/de/strifel/VTools/listeners/MarkdownString.java
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
package de.strifel.VTools.listeners;
|
||||||
|
|
||||||
|
import com.pengrad.telegrambot.model.Message;
|
||||||
|
import com.pengrad.telegrambot.model.MessageEntity;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class MarkdownString {
|
||||||
|
|
||||||
|
private MarkdownString(){
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这里的 format 假设 BiFunction 的输入都是转义过的。
|
||||||
|
*/
|
||||||
|
private static final Map<MessageEntity.Type, BiFunction<StringBuilder, MessageEntity, StringBuilder>> formats;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<MessageEntity.Type, BiFunction<StringBuilder, MessageEntity, StringBuilder>> map = new EnumMap<>(MessageEntity.Type.class);
|
||||||
|
|
||||||
|
map.put(MessageEntity.Type.bold, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.bold;
|
||||||
|
return s.insert(0, "*").append("*");
|
||||||
|
});
|
||||||
|
map.put(MessageEntity.Type.strikethrough, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.strikethrough;
|
||||||
|
return s.insert(0, "~").append("~");
|
||||||
|
});
|
||||||
|
map.put(MessageEntity.Type.spoiler, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.spoiler;
|
||||||
|
return s.insert(0, "||").append("||");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
map.put(MessageEntity.Type.italic, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.italic;
|
||||||
|
return s.insert(0, "\r_\r").append("\r_\r"); // todo: 精简一下 到底哪里需要加\r
|
||||||
|
});
|
||||||
|
map.put(MessageEntity.Type.underline, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.underline;
|
||||||
|
return s.insert(0, "\r__\r").append("\r__\r");
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://core.telegram.org/bots/api#formatting-options
|
||||||
|
// 上面这几个套娃友好,自身随意套娃,但不能和等宽组合
|
||||||
|
|
||||||
|
|
||||||
|
map.put(MessageEntity.Type.code, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.code;
|
||||||
|
return s.insert(0, "`").append("`");
|
||||||
|
});
|
||||||
|
map.put(MessageEntity.Type.pre, (rawStr, messageEntity) -> {
|
||||||
|
assert messageEntity.type() == MessageEntity.Type.pre;
|
||||||
|
rawStr.insert(0, "\n");
|
||||||
|
String language = messageEntity.language();
|
||||||
|
if (language != null && language.length() > 0) {
|
||||||
|
rawStr.insert(0, language);
|
||||||
|
}
|
||||||
|
return rawStr.insert(0, "```").append("\n```");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 等宽(pre 和 code)完全禁止套娃
|
||||||
|
|
||||||
|
map.put(MessageEntity.Type.text_link, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.text_link;
|
||||||
|
return s.insert(0, "[").append("](").append(escapeStr(e.url())).append(")");
|
||||||
|
});
|
||||||
|
map.put(MessageEntity.Type.text_mention, (s, e) -> {
|
||||||
|
assert e.type() == MessageEntity.Type.text_mention;
|
||||||
|
if (e.user() == null || e.user().id() == null) {
|
||||||
|
return s; // 没有id时爆炸(
|
||||||
|
}
|
||||||
|
return s.insert(0, "[").append("](").append("tg://user?id=").append(e.user().id()).append(")");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 链接类不能和链接类套娃,但是可以和加粗等套娃友好的组合
|
||||||
|
|
||||||
|
formats = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class StringBlock {
|
||||||
|
private final String text;
|
||||||
|
private Map<MessageEntity.Type, MessageEntity> entities;
|
||||||
|
private final int offset;
|
||||||
|
|
||||||
|
StringBlock(String text, int offset) {
|
||||||
|
this.text = text;
|
||||||
|
this.offset = offset;
|
||||||
|
this.entities = new EnumMap<>(MessageEntity.Type.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMarkdownTextWithoutLink() { // todo: 压缩 *text1**_text2_* 为 *text1_text2_*
|
||||||
|
// 链接类不能和链接类套娃
|
||||||
|
assert !((entities.containsKey(MessageEntity.Type.text_link)) && (entities.containsKey(MessageEntity.Type.text_mention)));
|
||||||
|
// 等宽(pre 和 code)完全禁止套娃
|
||||||
|
assert !((entities.containsKey(MessageEntity.Type.pre) || entities.containsKey(MessageEntity.Type.code)) && (entities.size() > 1));
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder(escapeStr(text));
|
||||||
|
for (var entry : entities.entrySet()) {
|
||||||
|
s = formats.get(entry.getKey()).apply(s, entry.getValue()); // 无用赋值...理论上。
|
||||||
|
}
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String markdownString(Message message) {
|
||||||
|
|
||||||
|
List<StringBlock> stringBlocks = createStringBlocks(message);
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
for (int i = 0; i < stringBlocks.size(); i++) {
|
||||||
|
var stringBlock = stringBlocks.get(i);
|
||||||
|
var entities = stringBlock.entities;
|
||||||
|
String withoutLink = stringBlock.getMarkdownTextWithoutLink();
|
||||||
|
if (entities.containsKey(MessageEntity.Type.text_mention) ||
|
||||||
|
entities.containsKey(MessageEntity.Type.text_link)) {
|
||||||
|
assert (!entities.containsKey(MessageEntity.Type.text_mention) ||
|
||||||
|
!entities.containsKey(MessageEntity.Type.text_link));
|
||||||
|
StringBuilder linkText = new StringBuilder();
|
||||||
|
MessageEntity entity = entities.containsKey(MessageEntity.Type.text_mention) ?
|
||||||
|
stringBlock.entities.get(MessageEntity.Type.text_mention) :
|
||||||
|
stringBlock.entities.get(MessageEntity.Type.text_link);
|
||||||
|
assert entity.offset() == stringBlock.offset;
|
||||||
|
int end = entity.length() + entity.offset();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
linkText.append(stringBlock.getMarkdownTextWithoutLink());
|
||||||
|
if (stringBlock.text.length() + stringBlock.offset == end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert (stringBlock.text.length() + stringBlock.offset < end);
|
||||||
|
i++;
|
||||||
|
stringBlock = stringBlocks.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.append(formats.get(entity.type()).apply(linkText, entity));
|
||||||
|
} else {
|
||||||
|
s.append(withoutLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 一组 StringBlock,被完全切开的文本,每部分都和相邻部分格式不一样,顺序的。
|
||||||
|
*/
|
||||||
|
private static List<StringBlock> createStringBlocks(Message message) {
|
||||||
|
List<StringBlock> stringBlocks = new ArrayList<>();
|
||||||
|
Set<Integer> splitPoints = new HashSet<>();
|
||||||
|
|
||||||
|
// 定位切割点
|
||||||
|
for (MessageEntity entity : message.entities()) {
|
||||||
|
if (formats.containsKey(entity.type())) {
|
||||||
|
Integer offset = entity.offset();
|
||||||
|
Integer length = entity.length();
|
||||||
|
splitPoints.add(offset);
|
||||||
|
splitPoints.add(offset + length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切割文本
|
||||||
|
String text = message.text();
|
||||||
|
List<Integer> sortedSplitPoints = new ArrayList<>(splitPoints);
|
||||||
|
Collections.sort(sortedSplitPoints);
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
for (int splitPoint : sortedSplitPoints) {
|
||||||
|
if (splitPoint == 0) continue;// 跳过第一个空的
|
||||||
|
stringBlocks.add(new StringBlock(text.substring(start, splitPoint), start));
|
||||||
|
start = splitPoint;
|
||||||
|
}
|
||||||
|
if (start < text.length()) {
|
||||||
|
stringBlocks.add(new StringBlock(text.substring(start), start));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给文本重新赋格式(MessageEntity)
|
||||||
|
for (MessageEntity entity : message.entities()) {
|
||||||
|
for (StringBlock stringBlock : stringBlocks) {
|
||||||
|
int blockStart = stringBlock.offset;
|
||||||
|
int blockEnd = blockStart + stringBlock.text.length();
|
||||||
|
|
||||||
|
int entityStart = entity.offset();
|
||||||
|
int entityEnd = entityStart + entity.length();
|
||||||
|
|
||||||
|
assert (blockStart < blockEnd) && (entityStart < entityEnd);
|
||||||
|
if (blockStart > entityEnd || blockEnd < entityStart) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert (blockStart >= entityStart) && (blockEnd <= entityEnd);
|
||||||
|
MessageEntity old = stringBlock.entities.put(entity.type(), entity);
|
||||||
|
assert (old == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean[] SHOULD_ESCAPE = new boolean[128];
|
||||||
|
|
||||||
|
static {
|
||||||
|
// In all other places characters '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' must be escaped with the preceding character '\'.
|
||||||
|
char[] chars = {'_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'};
|
||||||
|
for (char c : chars) {
|
||||||
|
SHOULD_ESCAPE[c] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String escapeStr(String s) {
|
||||||
|
StringBuilder r = new StringBuilder();
|
||||||
|
for (String character : s.split("")) {
|
||||||
|
int codePoint = character.codePointAt(0);
|
||||||
|
if (codePoint < 128 && SHOULD_ESCAPE[codePoint]) {
|
||||||
|
r.append('\\');
|
||||||
|
}
|
||||||
|
r.append(character);
|
||||||
|
}
|
||||||
|
return r.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import com.pengrad.telegrambot.UpdatesListener;
|
||||||
import com.pengrad.telegrambot.model.Message;
|
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.model.request.ParseMode;
|
||||||
import com.pengrad.telegrambot.request.EditMessageText;
|
import com.pengrad.telegrambot.request.EditMessageText;
|
||||||
import com.pengrad.telegrambot.request.GetChat;
|
import com.pengrad.telegrambot.request.GetChat;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
@ -51,6 +52,9 @@ public class TGBridge {
|
||||||
private long backoffSec = 1L;
|
private long backoffSec = 1L;
|
||||||
|
|
||||||
private static final String DIVIDER = "----------\n";
|
private static final String DIVIDER = "----------\n";
|
||||||
|
/**
|
||||||
|
* markdown escaped
|
||||||
|
*/
|
||||||
private String pinNote;
|
private String pinNote;
|
||||||
|
|
||||||
public TGBridge(@NotNull VTools plugin) {
|
public TGBridge(@NotNull VTools plugin) {
|
||||||
|
@ -96,75 +100,80 @@ public class TGBridge {
|
||||||
bot.setUpdatesListener(updates -> {
|
bot.setUpdatesListener(updates -> {
|
||||||
backoffSec = 1L;
|
backoffSec = 1L;
|
||||||
for (Update update : updates) {
|
for (Update update : updates) {
|
||||||
try {
|
|
||||||
if (update != null &&
|
|
||||||
update.message() != null &&
|
|
||||||
update.message().chat() != null &&
|
|
||||||
update.message().chat().id() == CHAT_ID &&
|
|
||||||
update.message().from() != null
|
|
||||||
) {
|
|
||||||
if (update.message().text() != null && !update.message().text().isEmpty()) {
|
|
||||||
String msg = update.message().text();
|
|
||||||
if (msg.startsWith("/")) {
|
|
||||||
String[] s = msg.split("(@[A-Za-z0-9_]bot)?[\t \n]+", 2);
|
|
||||||
String command = s[0];
|
|
||||||
@Nullable String arg = s.length == 2 ? s[1] : null;
|
|
||||||
switch (command) {
|
|
||||||
case "/list" -> outbound(genOnlineStatus());
|
|
||||||
case "/setpin" -> {
|
|
||||||
boolean shouldApplyEdit = true;
|
|
||||||
boolean needUpdate = false;
|
|
||||||
Message replyTo = update.message().replyToMessage();
|
|
||||||
if (replyTo == null) {
|
|
||||||
if (arg == null || arg.length() == 0)
|
|
||||||
outbound("""
|
|
||||||
usage:
|
|
||||||
use "/setpin" reply a message that from the bot to set that message to pin-message,
|
|
||||||
or use "/setpin <note>" to update current pinned message.""");
|
|
||||||
} else if (replyTo.from() == null || replyTo.from().id() != BOT_ID || replyTo.messageId() <= 0) {
|
|
||||||
outbound("must reply a message that from the bot (or reply nothing).");
|
|
||||||
shouldApplyEdit = false;
|
|
||||||
} else {
|
|
||||||
readOldPinnedMessage(replyTo);
|
|
||||||
if (arg == null || arg.length() == 0) {
|
|
||||||
outbound("done");
|
|
||||||
}else {
|
|
||||||
ONLINE_STATUS_MESSAGE_ID = replyTo.messageId();
|
|
||||||
}
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldApplyEdit && arg != null && arg.length() > 0) {
|
try {
|
||||||
outbound("done. old pinned note: \n" + pinNote);
|
if (update == null || update.message() == null) break;
|
||||||
pinNote = arg;
|
Message message = update.message();
|
||||||
needUpdate = true;
|
if (message.chat() == null || message.chat().id() != CHAT_ID || message.from() == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (message.text() != null && !message.text().isEmpty()) {
|
||||||
|
String msgText = message.text();
|
||||||
|
if (msgText.startsWith("/")) {
|
||||||
|
String[] s = msgText.split("(@[A-Za-z0-9_]bot)?[\t \n]+", 2);
|
||||||
|
String command = s[0];
|
||||||
|
@Nullable String arg = s.length == 2 ? s[1] : null;
|
||||||
|
switch (command) {
|
||||||
|
case "/list" -> outbound(genOnlineStatus(), ParseMode.Markdown);
|
||||||
|
case "/setpin" -> {
|
||||||
|
Message replyTo = message.replyToMessage();
|
||||||
|
if (arg == null || arg.length() == 0) {
|
||||||
|
if (replyTo == null) {
|
||||||
|
outbound("""
|
||||||
|
usage:
|
||||||
|
use "/setpin" reply a message that from the bot to set that message to pin-message,
|
||||||
|
or use "/setpin <note>" to update current pinned message.""");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (needUpdate) updateOnlineStatus();
|
ONLINE_STATUS_MESSAGE_ID = replyTo.messageId();
|
||||||
|
updateOnlineStatus();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case "/genpin" -> outbound(genPinMessage(),
|
if (replyTo != null && (replyTo.from() == null || replyTo.from().id() != BOT_ID || replyTo.messageId() <= 0)) {
|
||||||
(sendMessage, sendResponse) -> {
|
outbound("must reply a message that from the bot (or reply nothing).");
|
||||||
if (!sendResponse.isOk()) {
|
break;
|
||||||
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description()));
|
}
|
||||||
} else {
|
|
||||||
int messageId = sendResponse.message().messageId();
|
String markdownString = MarkdownString.markdownString(message);
|
||||||
ONLINE_STATUS_MESSAGE_ID = messageId > 0 ? messageId : ONLINE_STATUS_MESSAGE_ID;
|
|
||||||
}
|
String shouldBeCommand = markdownString.substring(0, "/setpin ".length());
|
||||||
}
|
if (!shouldBeCommand.matches("/setpin[\t \n]")) {
|
||||||
);
|
outbound("\"/setpin\" must be plain text.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
outbound("old pinned note: \n" + pinNote, ParseMode.Markdown);
|
||||||
|
|
||||||
|
pinNote = markdownString.substring("/setpin ".length());
|
||||||
|
if (replyTo != null) {
|
||||||
|
ONLINE_STATUS_MESSAGE_ID = replyTo.messageId();
|
||||||
|
}
|
||||||
|
updateOnlineStatus();
|
||||||
}
|
}
|
||||||
|
case "/genpin" -> outbound(genPinMessage(),
|
||||||
|
(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;
|
||||||
|
}
|
||||||
|
}, ParseMode.Markdown
|
||||||
|
);
|
||||||
}
|
}
|
||||||
tgInbound(update.message().from(), msg);
|
|
||||||
} else if (update.message().sticker() != null) {
|
|
||||||
tgInbound(update.message().from(), "[sticker]");
|
|
||||||
} else if (update.message().photo() != null) {
|
|
||||||
tgInbound(update.message().from(), "[photo]");
|
|
||||||
} else if (update.message().audio() != null) {
|
|
||||||
tgInbound(update.message().from(), "[audio]");
|
|
||||||
} else if (update.message().voice() != null) {
|
|
||||||
tgInbound(update.message().from(), "[voice]");
|
|
||||||
} else if (update.message().document() != null) {
|
|
||||||
tgInbound(update.message().from(), "[document]");
|
|
||||||
}
|
}
|
||||||
|
tgInbound(message.from(), msgText);
|
||||||
|
} else if (message.sticker() != null) {
|
||||||
|
tgInbound(message.from(), "[sticker]");
|
||||||
|
} else if (message.photo() != null) {
|
||||||
|
tgInbound(message.from(), "[photo]");
|
||||||
|
} else if (message.audio() != null) {
|
||||||
|
tgInbound(message.from(), "[audio]");
|
||||||
|
} else if (message.voice() != null) {
|
||||||
|
tgInbound(message.from(), "[voice]");
|
||||||
|
} else if (message.document() != null) {
|
||||||
|
tgInbound(message.from(), "[document]");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
plugin.logger.error("handling update", e);
|
plugin.logger.error("handling update", e);
|
||||||
|
@ -182,37 +191,43 @@ public class TGBridge {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getPinnedMessage(){
|
private boolean getPinnedMessage() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GetChatResponse response = bot.execute(new GetChat(CHAT_ID));
|
GetChatResponse response = bot.execute(new GetChat(CHAT_ID));
|
||||||
Message pinnedMessage = response.chat().pinnedMessage();
|
Message pinnedMessage = response.chat().pinnedMessage();
|
||||||
if(pinnedMessage.from().id()==BOT_ID){
|
if (pinnedMessage.from().id() == BOT_ID) {
|
||||||
readOldPinnedMessage(pinnedMessage);
|
readOldPinnedMessage(pinnedMessage);
|
||||||
updateOnlineStatus();
|
updateOnlineStatus();
|
||||||
}
|
}
|
||||||
}catch (RuntimeException e){
|
} catch (RuntimeException e) {
|
||||||
plugin.logger.error("get group info failed.");
|
plugin.logger.error("get group info failed.");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readOldPinnedMessage(Message message){
|
private void readOldPinnedMessage(Message message) {
|
||||||
ONLINE_STATUS_MESSAGE_ID = message.messageId();
|
ONLINE_STATUS_MESSAGE_ID = message.messageId();
|
||||||
String text = message.text();
|
String markdownText = MarkdownString.markdownString(message);
|
||||||
String[] s = text.split(DIVIDER, 2);
|
|
||||||
pinNote = s.length == 2 ? s[1] : "(use \"/setpin\" <note> to set note here)";
|
String[] s = markdownText.split(DIVIDER, 2);
|
||||||
|
pinNote = (s.length == 2) ?
|
||||||
|
s[1] :
|
||||||
|
"\r_\r" + MarkdownString.escapeStr("(use \"/setpin\" <note> to set note here)") + "\r_\r";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return markdown escaped str
|
||||||
|
*/
|
||||||
private String genPinMessage() {
|
private String genPinMessage() {
|
||||||
String onlineStatus = genOnlineStatus();
|
return (pinNote != null && pinNote.length() != 0) ?
|
||||||
if (pinNote != null && pinNote.length() > 1) {
|
genOnlineStatus() + "\n" + DIVIDER + pinNote :
|
||||||
return onlineStatus + "\n" + DIVIDER + pinNote;
|
genOnlineStatus();
|
||||||
} else {
|
|
||||||
return onlineStatus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return markdown escaped str
|
||||||
|
*/
|
||||||
private String genOnlineStatus() {
|
private String genOnlineStatus() {
|
||||||
ArrayList<String> out = new ArrayList<>();
|
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.";
|
String fmt = server.getAllPlayers().size() > 1 ? "%d players are currently connected to the proxy." : "%d player is currently connected to the proxy.";
|
||||||
|
@ -220,8 +235,8 @@ public class TGBridge {
|
||||||
List<RegisteredServer> registeredServers = new ArrayList<>(server.getAllServers());
|
List<RegisteredServer> registeredServers = new ArrayList<>(server.getAllServers());
|
||||||
for (RegisteredServer registeredServer : registeredServers) {
|
for (RegisteredServer registeredServer : registeredServers) {
|
||||||
LinkedList<Player> onServer = new LinkedList<>();
|
LinkedList<Player> onServer = new LinkedList<>();
|
||||||
for (Player player:registeredServer.getPlayersConnected()){
|
for (Player player : registeredServer.getPlayersConnected()) {
|
||||||
if(!lastDisconnect.equals(player.getUsername())){
|
if (!lastDisconnect.equals(player.getUsername())) {
|
||||||
onServer.add(player);
|
onServer.add(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +248,7 @@ public class TGBridge {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return String.join("\n", out);
|
return MarkdownString.escapeStr(String.join("\n", out));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void tgInbound(User user, String content) {
|
protected void tgInbound(User user, String content) {
|
||||||
|
@ -248,25 +263,34 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void outbound(String content) {
|
|
||||||
outbound(content, null);
|
protected void outbound(String content, ParseMode parseMode) {
|
||||||
|
outbound(content, (sendMessage, sendResponse) -> {
|
||||||
|
if (!sendResponse.isOk()) {
|
||||||
|
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description()));
|
||||||
|
}
|
||||||
|
}, parseMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void outbound(String content, @Nullable BiConsumer<SendMessage, SendResponse> onResponse) {
|
protected void outbound(String content) {outbound(content, (ParseMode) null);}
|
||||||
|
|
||||||
|
|
||||||
|
protected void outbound(String content, @NotNull BiConsumer<SendMessage, SendResponse> onResponse) {outbound(content, onResponse, null);}
|
||||||
|
|
||||||
|
protected void outbound(String content, @NotNull BiConsumer<SendMessage, SendResponse> onResponse, ParseMode parseMode) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
bot.execute(new SendMessage(CHAT_ID, content), new Callback<SendMessage, SendResponse>() {
|
|
||||||
|
SendMessage sendMessage = new SendMessage(CHAT_ID, content);
|
||||||
|
if (parseMode != null) {
|
||||||
|
sendMessage.parseMode(parseMode);
|
||||||
|
}
|
||||||
|
bot.execute(sendMessage, new Callback<SendMessage, SendResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(SendMessage sendMessage, SendResponse sendResponse) {
|
public void onResponse(SendMessage sendMessage, SendResponse sendResponse) {
|
||||||
if (onResponse == null) {
|
onResponse.accept(sendMessage, sendResponse);
|
||||||
if (!sendResponse.isOk()) {
|
|
||||||
plugin.logger.error(String.format("sendMessage error %d: %s", sendResponse.errorCode(), sendResponse.description()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onResponse.accept(sendMessage, sendResponse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -289,8 +313,8 @@ public class TGBridge {
|
||||||
if (event.getPreviousServer().isEmpty()) {
|
if (event.getPreviousServer().isEmpty()) {
|
||||||
if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) {
|
if (!event.getPlayer().hasPermission("vtools.globalchat.bypassbridge.join")) {
|
||||||
String username = event.getPlayer().getUsername();
|
String username = event.getPlayer().getUsername();
|
||||||
if(lastDisconnect.equals(username)){
|
if (lastDisconnect.equals(username)) {
|
||||||
lastDisconnect="";
|
lastDisconnect = "";
|
||||||
}
|
}
|
||||||
joinLeftAnnounce(String.format("%s joined the proxy", username));
|
joinLeftAnnounce(String.format("%s joined the proxy", username));
|
||||||
}
|
}
|
||||||
|
@ -299,11 +323,12 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String lastDisconnect = "";
|
private String lastDisconnect = "";
|
||||||
|
|
||||||
@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")) {
|
||||||
String username = event.getPlayer().getUsername();
|
String username = event.getPlayer().getUsername();
|
||||||
if(username!=null) {
|
if (username != null) {
|
||||||
lastDisconnect = username;
|
lastDisconnect = username;
|
||||||
}
|
}
|
||||||
joinLeftAnnounce(String.format("%s left the proxy", username));
|
joinLeftAnnounce(String.format("%s left the proxy", username));
|
||||||
|
@ -313,15 +338,15 @@ public class TGBridge {
|
||||||
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onServerPostConnect(ServerPostConnectEvent event){
|
public void onServerPostConnect(ServerPostConnectEvent event) {
|
||||||
updateRequests.add(new UpdateRequest());
|
updateRequests.add(new UpdateRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private boolean PROXY_SHUT_DOWN = false;
|
private boolean PROXY_SHUT_DOWN = false;
|
||||||
|
|
||||||
private static class UpdateRequest {}
|
private static class UpdateRequest {
|
||||||
|
}
|
||||||
|
|
||||||
private LinkedBlockingQueue<UpdateRequest> updateRequests = new LinkedBlockingQueue<>();
|
private LinkedBlockingQueue<UpdateRequest> updateRequests = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
@ -350,7 +375,7 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
new Thread(()->{
|
new Thread(() -> {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (PROXY_SHUT_DOWN) {
|
if (PROXY_SHUT_DOWN) {
|
||||||
return;
|
return;
|
||||||
|
@ -360,7 +385,7 @@ public class TGBridge {
|
||||||
oldestMessage = announceQueue.take();
|
oldestMessage = announceQueue.take();
|
||||||
} catch (InterruptedException ignored) {}
|
} catch (InterruptedException ignored) {}
|
||||||
|
|
||||||
if(!currentAnnounce.isValid()){
|
if (!currentAnnounce.isValid()) {
|
||||||
currentAnnounce = new JoinLeftAnnounceMessage(oldestMessage);
|
currentAnnounce = new JoinLeftAnnounceMessage(oldestMessage);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -381,16 +406,16 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean setOnlineStatusNotAvailable() {
|
protected boolean setOnlineStatusNotAvailable() {
|
||||||
return editOnlineStatusMessage("(proxy already shutdown)");
|
return editOnlineStatusMessage("(proxy already shutdown)");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean editOnlineStatusMessage(String text) {
|
protected boolean editOnlineStatusMessage(String markdownText) {
|
||||||
if (ONLINE_STATUS_MESSAGE_ID < 1) {
|
if (ONLINE_STATUS_MESSAGE_ID < 1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
BaseResponse response;
|
BaseResponse response;
|
||||||
try {
|
try {
|
||||||
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, text));
|
response = bot.execute(new EditMessageText(CHAT_ID, ONLINE_STATUS_MESSAGE_ID, markdownText).parseMode(ParseMode.Markdown));
|
||||||
} catch (RuntimeException e) {return false;}
|
} catch (RuntimeException e) {return false;}
|
||||||
return response != null && response.isOk();
|
return response != null && response.isOk();
|
||||||
}
|
}
|
||||||
|
@ -422,24 +447,25 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
SendResponse response;
|
SendResponse response;
|
||||||
try {
|
try {
|
||||||
response = bot.execute(new SendMessage(CHAT_ID, text.toString()));
|
response = bot.execute(new SendMessage(CHAT_ID, text.toString()));
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
messageId = -1;
|
messageId = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(response.isOk() == false){
|
if (response.isOk() == false) {
|
||||||
messageId = -1;
|
messageId = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
messageId = response.message().messageId();
|
messageId = response.message().messageId();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JoinLeftAnnounceMessage(){
|
protected JoinLeftAnnounceMessage() {
|
||||||
messageId = 0;
|
messageId = 0;
|
||||||
time = 0;
|
time = 0;
|
||||||
text = new StringBuilder();
|
text = new StringBuilder();
|
||||||
} //dummy
|
} //dummy
|
||||||
|
|
||||||
private void addLines(List<String> messages) {
|
private void addLines(List<String> messages) {
|
||||||
for (String message : messages) {
|
for (String message : messages) {
|
||||||
text.append('\n').append(message);
|
text.append('\n').append(message);
|
||||||
|
@ -448,7 +474,7 @@ public class TGBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAnnounceMessage() {
|
private void updateAnnounceMessage() {
|
||||||
if(!isValid()){
|
if (!isValid()) {
|
||||||
plugin.logger.error("message should only push to a valid object");
|
plugin.logger.error("message should only push to a valid object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -465,7 +491,7 @@ public class TGBridge {
|
||||||
private JoinLeftAnnounceMessage currentAnnounce = new JoinLeftAnnounceMessage();
|
private JoinLeftAnnounceMessage currentAnnounce = new JoinLeftAnnounceMessage();
|
||||||
private LinkedBlockingQueue<String> announceQueue = new LinkedBlockingQueue<>();
|
private LinkedBlockingQueue<String> announceQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
private void joinLeftAnnounce(String message){
|
private void joinLeftAnnounce(String message) {
|
||||||
announceQueue.add(message);
|
announceQueue.add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue