bot/core/mc.py

192 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
import json
import os
from aiogram.enums import ParseMode
from aiogram.types import Message
async def lookup_java_server(query_enabled,server_address):
try:
from mcstatus import JavaServer
server = await JavaServer.async_lookup(server_address)
status = await server.async_status()
query = None
s_message = f"这个 Java 服务器"
# 尝试查询服务器信息
if query_enabled:
try:
query = await server.async_query()
except Exception as e:
s_message = f"_我未能成功发送 query 请求可能是因为服务器未开放对应端口。_\n这个 Java 服务器"
logging.warning('查询 Minecraft 服务器遇到了错误', e)
if query:
s_message = f"这个 Java 服务器使用了 {query.software.brand}({query.software.version})"
s_message += f"{status.players.online}(/{status.players.max}) 人在线\n"
s_message += "延迟大约有 {:.2f} ms\n".format(status.latency)
s_message += f"服务器的 MOTD 是: ```\n{status.motd.to_minecraft()}\n```"
s_message += f"版本信息: {status.version.name} ({status.version.protocol})\n"
s_message += f"你应该使用和上面的版本相同的 Minecraft 客户端连接这个服务器。\n"
if (not query) and status.players.sample:
s_message += f"在线玩家列表: \n{', '.join(player.name.replace('_',r'\_') for player in status.players.sample)}\n"
if query and query.software.plugins:
s_message += f"服务器插件: {', '.join(query.software.plugins)}\n"
if query and query.players.names:
s_message += f"查询到的玩家: {', '.join(query.players.names).replace('_',r'_')}\n"
if status.forge_data:
s_message += f"看起来这是一个有 mod 的服务器。\n"
if status.enforces_secure_chat:
s_message += "服务器启用了消息签名,这意味着你需要调整 No Chat Reports 等类似 mod 的设置。\n\n"
s_message += "声明这些结果均为服务器所公开的信息。查询结果仅代表bot所在的服务器对该服务器的查询结果可能与实际情况有出入。"
except Exception as e:
s_message = f"悲报\n服务器连接失败\n{str(e)}"
return s_message
async def lookup_bedrock_server(server_address):
try:
from mcstatus import BedrockServer
server = BedrockServer.lookup(server_address)
status = await server.async_status()
# 稍微汉化一下这个状态信息
if status.gamemode == 'Survival':
game_mode = '生存'
elif status.gamemode == 'Creative':
game_mode = '创造'
else:
game_mode = status.gamemode or '未知模式'
s_message = f"这个基岩版{game_mode}服务器有 {status.players.online}(/{status.players.max})人在线游玩{status.map_name or '一张地图'}\n"
if status.version.brand == 'MCEE':
s_message += "这个服务器是 Minecraft 教育版服务器。\n"
s_message += "延迟大约有 {:.2f} ms\n".format(status.latency)
s_message += f"服务器的 MOTD 是: ```\n{status.motd.to_minecraft()}\n```"
s_message += f"版本信息: {status.version.name or status.version.version} ({status.version.protocol})\n"
s_message += f"你应该使用和上面的版本相同的 Minecraft 客户端连接这个服务器。\n\n"
s_message += "声明这些结果均为服务器所公开的信息。这个查询结果仅代表bot所在的服务器对该服务器的查询结果可能与实际情况有出入。"
except Exception as e:
s_message = f"悲报\n服务器连接失败\n {str(e)}"
return s_message
async def handle_mc_status_command(message: Message):
"""Handle the /mc command to check Minecraft server status."""
args = message.text.replace('/mc', '').strip().split(' ')
bind_file = 'mc_bindings.json'
if args == ['']:
# Load existing bindings
if os.path.exists(bind_file):
with open(bind_file, 'r', encoding='utf-8') as f:
bindings = json.load(f)
else:
bindings = {}
chat_bindings = bindings.get(str(message.chat.id), {})
available_types = [t for t, addr in chat_bindings.items() if addr is not None]
if message.chat.type in ['group', 'supergroup'] and chat_bindings and available_types:
server_address = chat_bindings.get(available_types[0], None)
status_message = await message.reply('正在查询服务器状态...')
if available_types[0] == 'java':
s_message = await lookup_java_server(False, server_address)
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
elif available_types[0] == 'bedrock':
s_message = await lookup_bedrock_server(server_address)
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
else:
await status_message.edit_text("未知的服务器类型,请使用 'java''bedrock'")
return
else:
await message.reply("Usage: /mc <bind/server> <java/bedrock> <server_address>\n"
"Example: /mc server java play.example.com")
return
option = args[0] if args else None
server_type = args[1] if len(args) > 1 else 'java'
server_address = args[2] if len(args) >= 3 else None
query_enabled = True if len(args) >= 4 and args[3] == 'query' else False
if option not in ['bind', 'server', 'unbind']:
await message.reply("Invalid option. Use '(un)bind' or 'server'.")
return
if option != 'unbind' and not server_address:
await message.reply("你没有提供服务器地址")
return
if server_address:
import re
local_ip_regex = r'^(?:(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:(?:1[6-9])|(?:2[0-9])|(?:3[0-1]))\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::\d{1,5})?)$'
if re.match(local_ip_regex, server_address) or server_address == 'localhost' or any(server_address == address for address in ['::1', 'fe80::', '.lan']):
await message.reply("正在与本地服务器断开连接")
return
if server_address == 'dinnerbone.com':
await message.reply("ɯoɔ˙ǝuoqɹǝuuᴉp/ǝlᴉɟoɹd/ddɐ˙ʎʞsq//:sdʇʇɥ")
# https://bsky.app/profile/dinnerbone.com , but typical dinnerbone style
return
if option == 'bind':
if not message.chat.type in ['group', 'supergroup']:
await message.reply("这个命令只能在群组中使用")
return
bind_file = 'mc_bindings.json'
# Load existing bindings
if os.path.exists(bind_file):
with open(bind_file, 'r', encoding='utf-8') as f:
bindings = json.load(f)
else:
bindings = {}
# Get chat ID
chat_id = str(message.chat.id)
# Initialize chat binding if not exists
if chat_id not in bindings:
bindings[chat_id] = {'java': None, 'bedrock': None}
# Update the binding for the specified server type
bindings[chat_id][server_type] = server_address
# Save bindings back to file
with open(bind_file, 'w', encoding='utf-8') as f:
json.dump(bindings, f, ensure_ascii=False, indent=2)
await message.reply(f"已成功绑定 {server_type} 服务器: {server_address}")
return
if option == 'unbind':
if not message.chat.type in ['group', 'supergroup']:
await message.reply("这个命令只能在群组中使用")
return
bind_file = 'mc_bindings.json'
# Load existing bindings
if os.path.exists(bind_file):
with open(bind_file, 'r', encoding='utf-8') as f:
bindings = json.load(f)
else:
bindings = {}
# Get chat ID
chat_id = str(message.chat.id)
# Initialize chat binding if not exists
if chat_id not in bindings:
await message.reply("这个群组没有绑定任何服务器,请先使用 /mc bind 命令绑定服务器")
return
# Unbind the specified server type
if server_type in bindings[chat_id]:
bindings[chat_id][server_type] = None
# Save bindings back to file
with open(bind_file, 'w', encoding='utf-8') as f:
json.dump(bindings, f, ensure_ascii=False, indent=2)
await message.reply(f"已成功解绑 {server_type} 服务器")
return
if option == 'server':
status_message = await message.reply('正在查询服务器状态...')
if server_type.lower() == 'java':
s_message = await lookup_java_server(query_enabled, server_address)
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
elif option == 'server' and server_type.lower() == 'bedrock':
s_message = await lookup_bedrock_server(server_address)
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
else:
await status_message.edit_text("未知的服务器类型,请使用 'java''bedrock'")