feat: add mc server command

This commit is contained in:
草师傅 2025-08-07 22:28:29 +08:00
parent 11e44d7a0f
commit 7106d0a18a
4 changed files with 137 additions and 1 deletions

View file

@ -10,6 +10,7 @@ from aiogram.filters import CommandStart, Command
from aiogram.client.session.aiohttp import AiohttpSession from aiogram.client.session.aiohttp import AiohttpSession
from aiogram import F from aiogram import F
from core.mc import handle_mc_status_command
from core.middleware.rikki import RikkiMiddleware from core.middleware.rikki import RikkiMiddleware
from core.post_to_fedi import router as fedi_router from core.post_to_fedi import router as fedi_router
@ -66,6 +67,8 @@ class TelegramAdapter:
# link 模块 # link 模块
router.message(Command('report_broken_links'))(report_broken_links) router.message(Command('report_broken_links'))(report_broken_links)
router.message(F.text.contains('http') & ~F.text.contains('/report_broken_links'))(handle_links) router.message(F.text.contains('http') & ~F.text.contains('/report_broken_links'))(handle_links)
# mc 模块
router.message(Command('mc'))(handle_mc_status_command) # 这个模块
# unpin 模块 # unpin 模块
# 不知道为什么检测不到频道的消息被置顶这个事件,暂时认为所有的频道消息都是被置顶的 # 不知道为什么检测不到频道的消息被置顶这个事件,暂时认为所有的频道消息都是被置顶的
unpin_router.message(F.chat.type.in_({'group', 'supergroup'}) & F.sender_chat & ( unpin_router.message(F.chat.type.in_({'group', 'supergroup'}) & F.sender_chat & (

87
core/mc.py Normal file
View file

@ -0,0 +1,87 @@
import logging
from aiogram.enums import ParseMode
from aiogram.types import 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(' ')
server_type = args[0] if args else 'java'
server_address = args[1] if len(args) >= 2 else None
if not args:
await message.reply("Usage: /mc <java/bedrock> <server_address>\n"
"Example: /mc java play.example.com")
return
if not server_address:
await message.reply("你没有提供服务器地址")
return
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
status_message = await message.reply('正在查询服务器状态...')
if server_type == 'java':
try:
from mcstatus import JavaServer
server = JavaServer.lookup(server_address)
status = server.status()
query = None
# 尝试查询服务器信息
try:
query = server.query()
except Exception as e:
logging.warning('查询 Minecraft 服务器遇到了错误',e)
s_message = f"*我未能成功发送 query 请求,显示的结果可能有出入。*\n这个 Java 服务器"
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 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)}\n"
if status.forge_data:
s_message += f"看起来这是一个有 mod 的服务器。\n"
if status.enforces_secure_chat:
s_message += "服务器启用了消息签名,这意味着你需要调整 No Chat Reports 等类似 mod 的设置。\n"
s_message += "声明这些结果均为服务器所公开的信息。查询结果仅代表bot所在的服务器对该服务器的查询结果可能与实际情况有出入。"
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
except Exception as e:
await status_message.edit_text(f"悲报\n服务器连接失败\n{str(e)}")
elif server_type == 'bedrock':
try:
from mcstatus import BedrockServer
server = BedrockServer.lookup(server_address)
status = server.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所在的服务器对该服务器的查询结果可能与实际情况有出入。"
await status_message.edit_text(s_message, parse_mode=ParseMode.MARKDOWN)
except Exception as e:
await status_message.edit_text(f"悲报\n服务器连接失败\n {str(e)}")
else:
await status_message.edit_text("未知的服务器类型,请使用 'java''bedrock'")

View file

@ -9,6 +9,7 @@ dependencies = [
"dulwich==0.24.1", "dulwich==0.24.1",
"mastodon-py==2.0.1", "mastodon-py==2.0.1",
"matrix-nio==0.25.2", "matrix-nio==0.25.2",
"mcstatus==12.0.2",
"python-abp==0.2.0", "python-abp==0.2.0",
"pyyaml>=6.0.2", "pyyaml>=6.0.2",
"requests>=2.32.4", "requests>=2.32.4",

47
uv.lock generated
View file

@ -1,5 +1,5 @@
version = 1 version = 1
revision = 2 revision = 3
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]] [[package]]
@ -117,6 +117,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
] ]
[[package]]
name = "asyncio-dgram"
version = "2.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/6b/7c3e984ef144c2a034bd7c881f2ae0516df8e8f845909f757a3ae04e5532/asyncio-dgram-2.2.0.tar.gz", hash = "sha256:73362b491786153d8b888936c5780548b40b4e6f5e0d62bfef956cb7b6ed9684", size = 11944, upload-time = "2024-05-08T19:21:49.269Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/00/cb33d8a9ebad87c9507262b131c92659bcf62975320b7feb9acdfb260ba0/asyncio_dgram-2.2.0-py3-none-any.whl", hash = "sha256:7afe5a587d1d57908c7a02fe84c785f075d3fb59b555039a6ff8aead28622743", size = 7403, upload-time = "2024-05-08T19:21:48.138Z" },
]
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "25.3.0" version = "25.3.0"
@ -197,6 +209,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
] ]
[[package]]
name = "dnspython"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" },
]
[[package]] [[package]]
name = "dulwich" name = "dulwich"
version = "0.24.1" version = "0.24.1"
@ -378,6 +399,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7b/0f/8b958d46e23ed4f69d2cffd63b46bb097a1155524e2e7f5c4279c8691c4a/matrix_nio-0.25.2-py3-none-any.whl", hash = "sha256:9c2880004b0e475db874456c0f79b7dd2b6285073a7663bcaca29e0754a67495", size = 181982, upload-time = "2024-10-04T07:51:39.451Z" }, { url = "https://files.pythonhosted.org/packages/7b/0f/8b958d46e23ed4f69d2cffd63b46bb097a1155524e2e7f5c4279c8691c4a/matrix_nio-0.25.2-py3-none-any.whl", hash = "sha256:9c2880004b0e475db874456c0f79b7dd2b6285073a7663bcaca29e0754a67495", size = 181982, upload-time = "2024-10-04T07:51:39.451Z" },
] ]
[[package]]
name = "mcstatus"
version = "12.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asyncio-dgram" },
{ name = "dnspython" },
]
sdist = { url = "https://files.pythonhosted.org/packages/14/f0/0cd5bc53eaa003d9578d931894948a4726d607ec67d7ccf8cc22e162e035/mcstatus-12.0.2.tar.gz", hash = "sha256:85546aa508d023524ffcbdb6911307cbaf88809465dfd71ef08e5edee1690e26", size = 121573, upload-time = "2025-06-22T11:31:57.419Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d3/eb/ede21d01d19e957573c88ff685401341a02ab595b4dba9a4a41fd382676c/mcstatus-12.0.2-py3-none-any.whl", hash = "sha256:b2ee5ff189a4ebf255c658e3983b3e2c74a1e0d222d3e74cfe04c2b4f64f66e6", size = 43105, upload-time = "2025-06-22T11:31:55.765Z" },
]
[[package]] [[package]]
name = "multidict" name = "multidict"
version = "6.6.3" version = "6.6.3"
@ -644,6 +678,7 @@ dependencies = [
{ name = "dulwich" }, { name = "dulwich" },
{ name = "mastodon-py" }, { name = "mastodon-py" },
{ name = "matrix-nio" }, { name = "matrix-nio" },
{ name = "mcstatus" },
{ name = "python-abp" }, { name = "python-abp" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "requests" }, { name = "requests" },
@ -656,6 +691,7 @@ requires-dist = [
{ name = "dulwich", specifier = "==0.24.1" }, { name = "dulwich", specifier = "==0.24.1" },
{ name = "mastodon-py", specifier = "==2.0.1" }, { name = "mastodon-py", specifier = "==2.0.1" },
{ name = "matrix-nio", specifier = "==0.25.2" }, { name = "matrix-nio", specifier = "==0.25.2" },
{ name = "mcstatus", specifier = "==12.0.2" },
{ name = "python-abp", specifier = "==0.2.0" }, { name = "python-abp", specifier = "==0.2.0" },
{ name = "pyyaml", specifier = ">=6.0.2" }, { name = "pyyaml", specifier = ">=6.0.2" },
{ name = "requests", specifier = ">=2.32.4" }, { name = "requests", specifier = ">=2.32.4" },
@ -751,6 +787,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" }, { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821, upload-time = "2025-07-01T15:55:55.167Z" },
] ]
[[package]]
name = "setuptools"
version = "80.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
]
[[package]] [[package]]
name = "six" name = "six"
version = "1.17.0" version = "1.17.0"