Implement Onebot Group message

This commit is contained in:
2023-03-04 21:10:08 +08:00
parent e4037549b4
commit cefd69c6f8
5 changed files with 225 additions and 58 deletions

View File

@ -8,25 +8,6 @@ import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// System.out.println("Hello world!");
//
// // {"group_id":685816675,"raw_message":"Go","sender":{"area":"","role":"member","level":"108","user_id":840465812,"sex":"unknown","nickname":"筱傑","title":"加油啊5号马","age":22,"card":""},"sub_type":"normal","user_id":840465812,"self_id":1379892815,"message_id":-518394285,"message_type":"group","post_type":"message","time":1677306537,"message":"Go","font":0}
// var gson = new Gson();
// var json = "{\"type\":\"message\",\"data\":{\"type\":\"text\",\"data\":\"Hello world!\"}}";
// var obj = gson.fromJson(json, JsonObject.class);
// System.out.println(obj.get("type").getAsString());
// var data = obj.get("data").getAsJsonObject();
// System.out.println(data.get("type").getAsString());
// System.out.println(data.get("data").getAsString());
//
// var map = gson.fromJson(json, Map.class);
// System.out.println(map);
// System.out.println(Objects.equals(map.get("type"), "message"));
// var dataMap = (Map<?,?>) map.get("data");
// System.out.println(Objects.equals(dataMap.get("type"), "text"));
// System.out.println((String)dataMap.get("data"));
Javalin app = Javalin.create().start(7070);
var bot = new MiniOneBot(app, "HelloOneBot");
bot.startWsServer("/openchat");
@ -35,17 +16,21 @@ public class Main {
} catch (URISyntaxException ignored) {
System.out.println("Uri Syntax error!");
}
bot.subscribeGroupMessageEvent(event -> System.out.println(event.message));
bot.subscribeGuildChannelMessageEvent(event -> System.out.println(event.message));
bot.subscribeGroupMessageEvent(event -> System.out.printf("[QQ]<%s> %s%n", event.senderCardOrNickname(), event.message()));
bot.subscribeGuildChannelMessageEvent(event -> System.out.printf("[Cl]<%s> %s%n", event.senderName(), event.message()));
var scanner = new Scanner(System.in);
while (true) {
var input = scanner.nextLine();
if (input.isEmpty() || input.equals("exit") || input.equals("quit")) {
if (input.isEmpty() || input.equals("stop") || input.equals("exit") || input.equals("quit")) {
break;
}
bot.sendMessage("Console", input);
}
bot.sendGroupMessage(0, "[MOB] Console: " + input);
}
System.out.println("Ending...");
bot.stop();
app.stop();
System.out.println("Ended...");
}
}

View File

@ -1,12 +1,19 @@
package top.jie65535.minionebot;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.javalin.Javalin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.jie65535.minionebot.events.GroupMessage;
import top.jie65535.minionebot.events.GroupMessageHandler;
import top.jie65535.minionebot.events.GuildChannelMessage;
import top.jie65535.minionebot.events.GuildChannelMessageHandler;
import java.io.IOException;
import java.net.URI;
import java.util.Objects;
import java.util.regex.Pattern;
public class MiniOneBot implements WsStream.WsMessageHandler {
private final Logger logger = LoggerFactory.getLogger("Console");
@ -14,11 +21,11 @@ public class MiniOneBot implements WsStream.WsMessageHandler {
private final String token;
private MiniOneBotWsServer server;
private MiniOneBotWsClient client;
private final Gson gson = new Gson();
public MiniOneBot(Javalin javalin, String token) {
this.javalin = javalin;
this.token = token;
logger.info("Test");
}
// region WebSocket
@ -39,30 +46,178 @@ public class MiniOneBot implements WsStream.WsMessageHandler {
}
}
public void stop() {
if (client != null) {
client.close();
}
if (server != null) {
try {
server.close();
} catch (IOException e) {
logger.error("Stop MiniOneBot WebSocket Server Failed!", e);
}
}
}
private void sendMessageToAll(String message) {
logger.info("Sending... message=\"{}\"", message);
server.send(message);
client.send(message);
}
// endregion
// region OneBot message
@Override
public void onMessage(String message) {
// TODO
var map = gson.fromJson(message, JsonObject.class);
if (!map.has("post_type")) return;
var postType = map.get("post_type").getAsString();
// 消息事件上报
if (Objects.equals(postType, "message")) {
var messageType = map.get("message_type").getAsString();
var subType = map.get("sub_type").getAsString();
// 发送者信息 https://docs.go-cqhttp.org/reference/data_struct.html#post-message-messagesender
var sender = map.get("sender").getAsJsonObject();
var senderId = sender.get("user_id").getAsLong();
var senderNickname = sender.get("nickname").getAsString();
// 群消息上报 https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%B6%88%E6%81%AF
if (Objects.equals(messageType, "group")
&& Objects.equals(subType, "normal")) {
var groupId = map.get("group_id").getAsLong();
// var message = (List<Map<?, ?>>)map.get("message");
var rawMessage = map.get("raw_message").getAsString();
var senderCard = sender.get("card").getAsString();
var senderLevel = sender.get("level").getAsString();
var senderRole = sender.get("role").getAsString();
var senderCardOrNickname = senderCard == null || senderCard.isEmpty() ? senderNickname : senderCard;
var senderTitle = sender.get("title").getAsString();
onGroupMessage(groupId, handleRawMessage(rawMessage), senderId, senderCardOrNickname, senderLevel, senderRole, senderTitle);
}
// 频道消息上报 https://docs.go-cqhttp.org/event/guild.html#%E6%94%B6%E5%88%B0%E9%A2%91%E9%81%93%E6%B6%88%E6%81%AF
else if (Objects.equals(messageType, "guild")
&& Objects.equals(subType, "channel")) {
var guildId = map.get("guild_id").getAsString();
var channelId = map.get("channel_id").getAsString();
var rawMessage = map.get("message").getAsString(); // 需要 Message 消息链类型处理
var tinyId = map.get("user_id").getAsString();
onGuildMessage(guildId, channelId, handleRawMessage(rawMessage), tinyId, senderNickname);
}
}
}
/**
* 当收到群消息时触发
* @param groupId 群号
* @param message 消息
* @param senderId 发送者ID
* @param senderCardOrNickname 发送者群名片或昵称名片为空时是昵称
* @param senderLevel 发送者群等级
* @param senderRole 发送者群身份
* @param senderTitle 发送者群专属头衔
*/
private void onGroupMessage(long groupId,
String message,
long senderId,
String senderCardOrNickname,
String senderLevel,
String senderRole,
String senderTitle) {
logger.info("groupId={}, message={}, senderId={}, senderCardOrNickname={}, senderLevel={}, senderRole={}, senderTitle={}",
groupId, message, senderId, senderCardOrNickname, senderLevel, senderRole, senderTitle);
groupMessageHandler.handleGroupMessage(new GroupMessage(groupId, message, senderId, senderCardOrNickname, senderLevel, senderRole, senderTitle));
}
/**
* 当收到频道消息时触发
* @param guildId 频道Id
* @param channelId 子频道Id
* @param message 消息
* @param senderId 发送者Id
* @param senderName 发送者昵称
*/
private void onGuildMessage(String guildId, String channelId, String message, String senderId, String senderName) {
logger.info("guildId={}, channelId={}, message={}, senderId={}, senderNickname={}",
guildId, channelId, message, senderId, senderName);
guildChannelMessageHandler.handleGuildChannelMessage(new GuildChannelMessage(guildId, channelId, message, senderId, senderName));
}
// endregion
// region Message API
public void sendMessage(String sender, String message) {
// TODO
// region Models
private static class Action {
public String action;
public Object params;
public Action(String action, Object params) {
this.action = action;
this.params = params;
}
}
private static class SendGroupMsgArgs {
public long group_id;
public String message;
public boolean auto_escape = true;
public SendGroupMsgArgs(long groupId, String message) {
this.group_id = groupId;
this.message = message;
}
}
private static class SendGuildChannelMsgArgs {
public String guild_id;
public String channel_id;
public String message;
public boolean auto_escape = true;
public SendGuildChannelMsgArgs(String guildId, String channelId, String message) {
this.guild_id = guildId;
this.channel_id = channelId;
this.message = message;
}
}
// endregion
/**
* 发送消息到群
* @param groupId 群号
* @param message 消息
*/
public void sendGroupMessage(long groupId, String message) {
sendMessageToAll(gson.toJson(new Action("send_group_msg", new SendGroupMsgArgs(groupId, message))));
}
/**
* 发送消息到子频道
* @param guildId 频道ID
* @param channelId 子频道ID
* @param message 消息
*/
public void sendGuildChannelMessage(String guildId, String channelId, String message) {
sendMessageToAll(gson.toJson(new Action("send_guild_channel_msg", new SendGuildChannelMsgArgs(guildId, channelId, message))));
}
GroupMessageHandler groupMessageHandler;
GuildChannelMessageHandler guildChannelMessageHandler;
/**
* 订阅群消息事件
* @param handler 群消息处理器
*/
public void subscribeGroupMessageEvent(GroupMessageHandler handler) {
groupMessageHandler = handler;
}
GuildChannelMessageHandler guildChannelMessageHandler;
/**
* 订阅频道消息事件
* @param handler 频道消息处理器
*/
public void subscribeGuildChannelMessageEvent(GuildChannelMessageHandler handler) {
guildChannelMessageHandler = handler;
}
@ -71,6 +226,33 @@ public class MiniOneBot implements WsStream.WsMessageHandler {
// region Utils
private static final Pattern cqCodePattern = Pattern.compile("\\[CQ:(\\w+).*?]");
private static String handleRawMessage(String rawMessage) {
if (rawMessage.indexOf('[') == -1)
return unescape(rawMessage);
var message = new StringBuilder();
var matcher = cqCodePattern.matcher(rawMessage);
while (matcher.find()) {
var type = matcher.group(1);
var replacement = switch (type) {
case "image" -> "[图片]";
case "reply" -> "[回复]";
case "at" -> "[@]";
case "record" -> "[语音]";
case "forward" -> "[合并转发]";
case "video" -> "[视频]";
case "music" -> "[音乐]";
case "redbag" -> "[红包]";
case "poke" -> "[戳一戳]";
default -> "";
};
matcher.appendReplacement(message, replacement);
}
matcher.appendTail(message);
return unescape(message.toString());
}
private static String escape(String msg) {
return msg.replace("&", "&amp;")
.replace("[", "&#91;")

View File

@ -5,10 +5,12 @@ import io.javalin.websocket.*;
import org.eclipse.jetty.websocket.api.CloseStatus;
import org.slf4j.Logger;
import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MiniOneBotWsServer implements WsStream {
public class MiniOneBotWsServer implements WsStream, Closeable {
private final String token;
private final Logger logger;
@ -70,31 +72,6 @@ public class MiniOneBotWsServer implements WsStream {
logger.debug("onMessage: {}", ctx.message());
callback.onMessage(ctx.message());
// var map = JsonUtils.decode(ctx.message(), Map.class);
// // 消息事件上报
// if (Objects.equals(map.get("post_type"), "message")) {
// // 群消息上报 https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%B6%88%E6%81%AF
// if (Objects.equals(map.get("message_type"), "group")
// && Objects.equals(map.get("sub_type"), "normal")) {
// // 检查群号
// var groupId = (Long)map.get("group_id");
// if (Objects.equals(config.groupId, groupId)) {
//// var message = (List<Map<?, ?>>)map.get("message");
// var rawMessage = map.get("raw_message").toString();
//
// // 发送者信息 https://docs.go-cqhttp.org/reference/data_struct.html#post-message-messagesender
// var sender = (Map<?, ?>) map.get("sender");
// var senderId = sender.get("user_id").toString();
// var senderNickname = sender.get("nickname").toString();
// var senderCard = sender.get("card").toString();
// chatSystem.broadcastChatMessage(config.groupToGameFormat
// .replace("{card}", senderCard)
// .replace("{id}", senderId)
// .replace("{nickname}", senderNickname)
// .replace("{message}", rawMessage));
// }
// }
// }
}
private WsMessageHandler callback;
@ -113,4 +90,15 @@ public class MiniOneBotWsServer implements WsStream {
}
}
}
@Override
public void close() throws IOException {
if (connections.isEmpty()) return;
for (var ctx : connections.keySet()) {
if (ctx.session.isOpen()) {
ctx.session.close(1001, "Service stopped");
}
}
connections.clear();
}
}

View File

@ -1,5 +1,12 @@
package top.jie65535.minionebot.events;
public class GroupMessage {
public String message;
public record GroupMessage(
long groupId,
String message,
long senderId,
String senderCardOrNickname,
String senderLevel,
String senderRole,
String senderTitle
) {
}

View File

@ -1,5 +1,10 @@
package top.jie65535.minionebot.events;
public class GuildChannelMessage {
public String message;
public record GuildChannelMessage(
String guildId,
String channelId,
String message,
String senderId,
String senderName
) {
}