我想让Minecraft服务器的Op权限与Discord机器人的命令执行权限相对应
前提 tí) – premise, assumption, prerequisite
这是一篇关于使用Discord机器人操控Minecraft服务器的圣诞日历文章。
这篇文章是考虑将Minecraft服务器的操作权限对应到Discord机器人的命令执行权限的文章。我们已经实现了对应的程序,但并不代表只需按照该程序执行即可。请事先知悉。
此外,環境也
-
WSL, Ubuntu 22.04.1
minecraft server 1.19.2
Python 3.10.6
正在进行中。
假设从机器人中使用命令的情况
首先,我们要考虑从 Discord 机器人执行指令到 Minecraft 服务器上的问题。
在中文中,可以使用的命令可能是list、op和msg/tell等。这些命令可以
list
現在ログインしている人数と,誰がログインしているかの表示
op
serverに対するoperater権限を与える
私のpapermcの環境だと,ops.jsonのlevelを変更してもlevel4相当の権限になりました
ほかに設定があるかもしれませんが,papermcのドキュメントそれっぽいやつがなかったので諦めました
公式のjarファイルだとops.jsonを変更しserverを再起動することで権限が更新されます
msg/tell
誰かにメッセージを送る
这些都是类似的东西。只有op需要权限才能执行这些命令,所以可能没有必要。但实际上,本来stop()也需要权限,所以可以应用在那里。
将Minecraft的用户名和Discord的名称匹配。
可以使用discord bot来检查Minecraft服务器的权限,最好的方法是检查服务器所在目录中的ops.json文件。ops.json文件中包含用户ID、用户名以及相应的权限级别等信息。然而,ops.json文件中记录的用户名是用于玩Minecraft时的用户名,不一定与Discord上的名称相匹配。因此,需要进行对应处理。
这里,ops.json在初始状态下是空的,所以一开始需要手动执行op命令。因此,请先执行op命令。
怎么办?
考虑到具体要做什么,本文中我们将创建一个文件,使用类似于ops.json中所描述的格式,其中包含Minecraft用户ID、Minecraft用户名和Discord用户ID的JSON数组。此外,我们将在discord上右键点击自己并运行应用程序时更新该JSON文件。我在一篇简短的文章中对context_menu()进行了说明,请您能够查看一下,我会很高兴的。
编程
获取ops.json文件的内容
首先,介绍的是如何获取Minecraft服务器所在目录下的ops.json文件的内容,并将其转换为自己要使用的JSON格式,并保存到文件中的部分说明。这里将自己要使用的JSON文件命名为ops.json。为了区分,我们称之为「discord的ops.json」等等。请忽略其他部分,因为这个地方是唯一有冲突的地方。
代码如下:
from MCServer import MCServer
from MinecraftCommand import MinecraftCmd
from discord import Intents, Client
from discord.app_commands import CommandTree
import json
import os
class MCClient(Client):
def __init__(self, intents: Intents) -> None:
super().__init__(intents=intents)
self.tree = CommandTree(self)
self.server = MCServer()
self.ops: dict = self.get_ops()
async def setup_hook(self) -> None:
self.tree.add_command(MinecraftCmd(self.server, self))
await self.tree.sync()
async def on_ready(self):
print(f"login: {self.user.name} [{self.user.id}]")
self.channel = self.get_channel(1047700283712622643)
def get_ops(self) -> dict:
json_file_name = "ops.json"
if os.path.exists(f"./{json_file_name}"):
ops = {}
with open(json_file_name, "r") as file:
ops = json.load(file)
return ops
ops = []
ops_json = None
with open("../mcserver/ops.json") as file:
ops_json = json.load(file)
for user in ops_json:
ops.append(
{"uuid": user["uuid"],
"name": user["name"],
"id": None}
)
with open("ops.json", "w") as write_file:
print("dump")
json.dump(ops, write_file, indent=2)
return ops
获取内容的位置是get_ops()方法。
如果discord的ops.json文件已经存在,则假设已经读取了minecraft的ops.json并进行了早期返回。原因是之后的代码使用了id来初始化。现在我意识到,当id的值已经存在时,什么也不做的条件分叉更好。请忽略这个错误。
然后,只需简单地读取文件内容,转换为我要使用的形式,并以JSON格式输出,然后返回discord的ops.json文件。
打开上下文菜单()部
接下来是context_menu()部分的代码如下所示。
import os
import json
from discord import Intents, Client, Interaction, Member
from discord.ui import Select, View
from discordbot import MCClient
from dotenv import load_dotenv
load_dotenv()
class UnregisteredNameSelect(Select):
def __init__(self, *, placeholder: str = None, client: Client) -> None:
super().__init__(placeholder=placeholder)
self.client = client
async def callback(self, interaction: Interaction):
self.client.ops[int(self.values[0])]["id"] = interaction.user.id
with open("ops.json", "w") as write_file:
print("dump")
json.dump(self.client.ops, write_file, indent=2)
await interaction.response.send_message("registered!", ephemeral=True)
intents = Intents.default()
client = MCClient(intents=intents)
@client.tree.context_menu(name="register name")
async def registerName(interaction: Interaction, member: Member):
if not interaction.user == member:
await interaction.response.send_message("sorry. please select yourself", ephemeral=True)
return
unregisteredName = []
for index, user in enumerate(client.ops):
if user["id"] is None:
unregisteredName.append([user["name"], index])
if not unregisteredName:
await interaction.response.send_message("all user registered", ephemeral=True)
return
unregisteredNameSelector = UnregisteredNameSelect(client=client)
for item in unregisteredName:
unregisteredNameSelector.add_option(
label=item[0], value=item[1]
)
select_view = View()
select_view.add_item(unregisteredNameSelector)
await interaction.response.send_message(
f"hi, {member.mention}. select your name.", ephemeral=True, view=select_view
)
client.run(os.getenv("TOKEN"))
暂不考虑class的部分,我将解释context_menu()函数的部分。
首先,为了确保只有自己能够对其他用户进行注册,我们提前终止。此外,如果没有未注册的用户,也就是说所有拥有权限的人都已经注册了,我们会传达这一信息并提前终止。
之后,为了进行注册操作,将显示未注册的人员名单并要求在下拉列表中进行选择。为了描述在选择主下拉列表时要执行的操作,我们创建了UnregisteredNameSelect类。关于Select,请先阅读文档。暂时可以理解为正在编写选中时的处理。当注册id后,将写入JSON文件中。
确认动作
我们试着去执行一下。

在试图注册他人时无法成功,但当尝试选择自己时,可以操作下拉列表,并且选择后显示注册成功。换言之,我认为期望的操作已成功实现。
但是,由于选择后仍可操作下拉列表,这并不太好。我认为再次从列表中选择将重新注册。由于Select有一个名为disabled的参数,只需将其设置为True即可在选择后禁用。
下一步,我们来看一下生成的discord的ops.json文件的内容。
[
{
"uuid": "uuid",
"name": "itousagi",
"id": 000000000000000000
}
]
实际上,UUID或ID中会包含自己的身份信息。
因此,我认为可以将Minecraft用户和Discord用户进行关联。
上述内容表示了从想要将Minecraft服务器的OP权限与Discord机器人的命令执行权限相对应的方针,并做出了各种考虑。希望对您有所参考。辛苦了。