diff --git a/MinecraftClient/ChatBots/OkWsBot.cs b/MinecraftClient/ChatBots/OkWsBot.cs new file mode 100644 index 00000000..431c3fcd --- /dev/null +++ b/MinecraftClient/ChatBots/OkWsBot.cs @@ -0,0 +1,152 @@ +using System; +using System.Net; +using System.Text; +using MinecraftClient.Scripting; +using WebSocketSharp; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System.Linq; +using Tomlet.Attributes; +using MinecraftClient.Mapping; +using static MinecraftClient.Settings; +using System.Threading.Tasks; + +namespace MinecraftClient.OkBots; +public class OkWsBot : ChatBot +{ + WebSocket? pyWebSocketServer; + protected static OkWsBot? chatBot; + + public static Configs Config = new (); + + [TomlDoNotInlineObject] + public class Configs { + public bool Enabled = true; + + [TomlInlineComment("群号")] + public string id = "12345678"; + [TomlInlineComment("Python Websocket接口")] + public string pythonSendWsApi = "ws://127.0.0.1:12345"; + [TomlInlineComment("在群内显示的服务器名称")] + public string serverName = "1.20"; + } + + public override void Initialize() + { + chatBot = this; + InitDeathMsgs(); + + pyWebSocketServer = new WebSocket (Config.pythonSendWsApi); + pyWebSocketServer.OnMessage += (sender, e) => { + var msg = e.Data; + Console.WriteLine("We get msg:" + msg); + if (msg == "#!list") { + pyWebSocketServer.Send("#!list" + GetPlayerListMsg()); + return; + } + SendToConsole(msg); + }; + pyWebSocketServer.Connect (); + + LogToConsole("OkQQbot has been initialized!"); + } + + public override void GetText(string text) + { + string message = ""; + string username = ""; + string serverPrefix = "["+ Config.serverName +"] "; + text = GetVerbatim(text); + + if (IsChatMessage(text, ref message, ref username)) + { + if (username == GetUsername()) return; + string msg = serverPrefix + text; + SendToQQ(msg); + return; + } + + if (IsAchievementMsg(text)) { + if (text.StartsWith("bot_")) return; + string msg = serverPrefix + "<喜报> " + text; + SendToQQ(msg); + return; + } + + if (IsDeathMsg(text)) { + if (text.StartsWith("bot_")) return; + string msg = serverPrefix + "<悲报> " + text; + SendToQQ(msg); + return; + } + + if (IsLoginLogoutMsg(text)) { + if (text.StartsWith("bot_")) return; + SendToQQ(serverPrefix + text); + } + } + + public void SendToConsole(string msg) { + base.SendText(msg); + } + + public void SendToQQ(string msg) { + if (!pyWebSocketServer!.Ping()) { + Console.WriteLine("Onebot Connection down, reconnecting..."); + pyWebSocketServer = new WebSocket (Config.pythonSendWsApi); + pyWebSocketServer.Connect (); + } + pyWebSocketServer.Send (msg); + } + + public string GetPlayerListMsg() { + string result = "["+Config.serverName+"]"; + string[] playerStrs = GetOnlinePlayers(); + if (playerStrs.Length == 0) { + return result += " [鬼服]\n没有玩家在线"; + } + result += " 在线玩家:"; + foreach (var playerStr in playerStrs) + { + result += "\n" + playerStr; + } + return result; + } + + public bool IsLoginLogoutMsg(string msg) { + return msg.EndsWith(" joined the game") || + msg.EndsWith(" left the game"); + } + + public bool IsAchievementMsg(string msg) { + return msg.IndexOf(" has made the advancement ") != -1; + } + + private static List DeathMsgs = new(); + public void InitDeathMsgs() { + string translationsStr = Encoding.ASCII.GetString((byte[])MinecraftAssets.ResourceManager.GetObject("en_us.json")!); + JObject translations = JObject.Parse(translationsStr); + var enumerator = translations.GetEnumerator(); + while (enumerator.MoveNext()) { + if (!enumerator.Current.Key.StartsWith("death.")) continue; + string result = enumerator.Current.Value!.ToString(); + result = result.Replace("%1$s ", ""); + + var index2s = result.IndexOf("%2$s"); + if (index2s != -1) { + result = result.Substring(0,index2s); + } + DeathMsgs.Add(result); + } + } + public bool IsDeathMsg(string msg) { + foreach (var item in DeathMsgs) + { + if (msg.IndexOf(item) != -1) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index c7296d52..10962524 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -7,6 +7,7 @@ using System.Threading; using Brigadier.NET; using Brigadier.NET.Exceptions; using MinecraftClient.ChatBots; +using MinecraftClient.OkBots; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Commands; @@ -298,6 +299,7 @@ namespace MinecraftClient if (Config.ChatBot.TelegramBridge.Enabled) { BotLoad(new TelegramBridge()); } if (Config.ChatBot.ItemsCollector.Enabled) { BotLoad(new ItemsCollector()); } if (Config.ChatBot.WebSocketBot.Enabled) { BotLoad(new WebSocketBot()); } + if (Config.ChatBot.OkWsBot.Enabled) { BotLoad(new OkWsBot()); } //Add your ChatBot here by uncommenting and adapting //BotLoad(new ChatBots.YourBot()); } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index cf999ca2..faaed5fa 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -45,6 +45,7 @@ + diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 7982b057..3a9021c3 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -1440,6 +1440,13 @@ namespace MinecraftClient get { return ChatBots.WebSocketBot.Config!; } set { ChatBots.WebSocketBot.Config = value; } } + + [TomlPrecedingComment("Ok的QQbot")] + public OkBots.OkWsBot.Configs OkWsBot + { + get { return OkBots.OkWsBot.Config!; } + set { OkBots.OkWsBot.Config = value; } + } } } diff --git a/py-server.py b/py-server.py new file mode 100644 index 00000000..80c7dfee --- /dev/null +++ b/py-server.py @@ -0,0 +1,114 @@ +from websocket_server import WebsocketServer +import logging +import requests +import json + +# 群号 +GROUP_ID = 12345678 +# Onebot 发送群聊消息的节点 +QQ_BOT_API = f"http://127.0.0.1:5700/send_group_msg?group_id={GROUP_ID}&message=" +# 用来查询玩家列表的关键词 +LIST_WORD = "#!list" +# 第一个连接websocket的一定要是onebot +onebot_id = -1 +# 已关闭的连接列表 +closed_list = {-1} + +def new_client(client, server): + client_id = client["id"] + client_addr = client["address"] + global onebot_id + if (onebot_id == -1): + print("Onebot client join us, you can start mcc client now.") + onebot_id = client["id"] + else: + print("New mcc client join us, ") + + print(f"id:{client_id}, address:{client_addr}") + +def send_to_qq(msg): + requests.get(QQ_BOT_API + msg) + +def on_client_closed(client, server): + global closed_list + closed_list.add(client["id"]) + +def on_get_msg(client, server, message): + client_id = client["id"] + print(f"client id: {client_id}, message: {message}") + + if (message.startswith(LIST_WORD)): + send_to_qq(message[len(LIST_WORD):]) + return + + if (message.startswith("{")): + handle_qq_json(message) + return + + # 如果不是 #!list 消息和 qq消息,那就是服里发来的消息 + # 发到其他服 + send_to_mccs(message, client, True) + + # 发到qq + send_to_qq(message) + +def handle_qq_json(msg): + + qq_json = json.loads(msg) + if (qq_json["post_type"] != "message"):return + if (qq_json["message_type"] != "group"):return + if (qq_json["sub_type"] != "normal"):return + if (qq_json["group_id"] != GROUP_ID):return + + sender = qq_json["sender"]["nickname"] + result = "" + """ + Message example: + "message":[ + {"data":{"text":"首先是需要加个材质包"},"type":"text"}, + {"data":{"file":"1356f3c15301f85246b5e15dda88294e","url":"http://gchat.qpic.cn/gchatpic_new/0/0-0-1356F3C15301F85246B5E15DDA88294E/0?term=2"},"type":"image"} + ] + """ + for item in qq_json["message"]: + data_type = item["type"] + if (data_type == "text"): + result += item["data"]["text"] + continue + if (data_type == "face"): + result += "[表情]" + # TODO see https://docs-v1.zhamao.xin/face_list.html + # face to image + continue + if (data_type == "image"): + image_url = item["data"]["url"] + result += f"[[CICode,url={image_url},name=图片]]"; + continue + if (data_type == "mface"): + result += "[动画表情]" + + if (result == LIST_WORD): + # Let those MCCs return with "#!list [1.20] 在线玩家:..." which starts with #!list + python_server.send_message_to_all(LIST_WORD) + return + + # Else it is a normal message, send to all mcc + send_to_mccs(f"<{sender}> {result}", None, True) + +def send_to_mccs(msg, except_mcc_client=None, except_onebot=False): + for client_item in python_server.clients: + if client_item["id"] in closed_list: + continue + if except_mcc_client is not None and client_item["id"] == except_mcc_client["id"]: + continue + if except_onebot and client_item["id"] == onebot_id: + continue + python_server.send_message(client_item, msg) + +python_server = WebsocketServer(host='127.0.0.1', port=12345, loglevel=logging.INFO) +python_server.set_fn_new_client(new_client) +python_server.set_fn_client_left(on_client_closed) +python_server.set_fn_message_received(on_get_msg) +python_server.run_forever(True) +print("输入回车以停止") +input() +python_server.shutdown_gracefully() \ No newline at end of file