如何制作MinecraftServerðíd的Discord机器人
#typescript #node #discord #minecraft

您是否曾经拥有自己的Minecraft服务器,并觉得自己想:“如果我能够展示有关我的服务器的有用信息,或者在Discord中跟踪其延迟,那不是很棒吗?”

好吧,你很幸运!本教程将引导您完成整个过程,从头开始,因此您可以实现所有这些。

我们将使用discord.jskoude1库,该库是一个现代,小型,性能,零依赖项打字库,我写的不扰乱服务器的响应。

Discord embed of server info

让我们开始吧!

步骤1:设置Discord.js

这很简单。运行npm install discord.js,导入discord.js,声明客户端,并且意图是登录机器人并在客户端准备就绪后记录消息。

import { Client, Events, EmbedBuilder, REST, SlashCommandBuilder, Routes, GatewayIntentBits, AttachmentBuilder }  from 'discord.js';
import mc from "minecraftstatuspinger"

const client = new Client({intents: [GatewayIntentBits.Guilds]});

client.once("ready", c => {
    console.log(`Ready! Logged in as ${c.user.tag}`);
});

// Log in to Discord with your client's token
client.login(process.env.token);

步骤2:做一个斜杠命令。

在此步骤中,我们将制作一个"ping"斜杠命令。我们将首先宣布REST,该REST用于更新Slash命令。我们将其设置为“令牌环境变量”。我们在命令-hostnameport中添加两个选项。我们将Slash命令发送并注册到Discord,提供您的应用程序ID

const rest = new REST({ version: "10" }).setToken(process.env.token)
let commands = [
        new SlashCommandBuilder().setName("ping").setDescription("Pings a Minecraft server.")
            .addStringOption(option =>
                option.setName("ip")
                    .setDescription("IP of server.")
                    .setRequired(true)
            )
            .addIntegerOption(option =>
                option.setName("port")
                    .setDescription("Port of server.")
                    .setRequired(false)
                    .setMinValue(0)
                    .setMaxValue(65535)
            )
    ].map(command => command.toJSON())
   await rest.put(Routes.applicationCommands("app ID"), { body: commands })

步骤3:处理斜线命令。

订阅交互作用事件。收到一个人时,我们检查是否是斜线命令交互。是吗?答对了。完成此操作后,我们会检查其名称,如果正确,我们将调用getMcStatus函数,我们在下一步中创建的函数

client.on(Events.InteractionCreate, (interaction: Interaction) => {
    if (!interaction.isChatInputCommand()) return;
    const { commandName } = interaction;
    if (commandName === 'ping') return getMcStatus(interaction);
});

步骤4:设置MinecraftStatuspinger

如前所述,我们将使用MinecraftStatusPinger库。
运行此命令以从NPMnpm install minecraftstatuspinger安装它。这样可以确保它在您的计算机上,并且您可以导入并使用它。

然后,像这样导入:

let mc = require("minecraftstatuspinger")

设置比Discord.js容易!只需安装它,导入并上路即可。没有其他设置,也没有创建pinger对象,它只是从开箱即用的。

步骤5:Ping Minecraft服务器

我们最终创建了getMcStatus函数。我们需要两个选项-IP和端口。如果端口为null,则默认情况下将自动切换为25565。一旦获得这些,我们就可以用options调用.lookup()。这使得库直接将Ping请求直接发送给Minecraft服务器,而无需与任何中介API联系。

async function getMcStatus(interaction){
    let IP = interaction.options.getString("ip")
    let port = interaction.options.getInteger("port") || 25565

    let result = await mc.lookup({ hostname: IP, port: port })
}

现在在status内,有一个与这三个孩子的物体-statusstatusRawlatency
status是包含播放器,版本,favicon和motd的对象!我们将在下一步中使用所有这些。
statusRaw就像status一样 latency是一个数字,它表示服务器的延迟。它是通过将任意数据包发送到服务器并等待响应来实现的,因此它比普通Ping更准确。

步骤6:创建Favicon的附件

Minecraft服务器返回它的favicon作为数据URL BLOB。我们需要删除使用替换功能和REGEX完成的前缀,该函数替换所有Filetypes(例如data:image/png;base64,data:image/jpeg;base64,)。然后,它将其变成一个缓冲区,即Discord.js可以用于图像附件。我们将其设置为icon.png

async function getMcStatus(interaction){
    let IP = interaction.options.getString("ip")
    let port = interaction.options.getInteger("port") || 25565

    let result = await mc.lookup({ hostname: IP, port: port })

    let buf = Buffer.from(result.status.favicon.replace(/data:image\/[^;]+;base64,/, ""), "base64")
    let attach = await new AttachmentBuilder(buf)
    .setName("icon.png")
}

步骤7:嵌入并发送

我们做了一个新的嵌入,我们给出了IP和MOTD的标题。 MOTD在版本中各不相同,对于hyvermyshypixel使用result.status.description,现代服务器应使用result.status.description.text。我们添加了播放器计数,添加了一个版本字段,该字段显示您可以从哪个版本加入服务器。最后,我们添加一个缩略图,即先前定义的附件。然后,我们回复互动,我们完成了!

async function getMcStatus(interaction){
    let IP = interaction.options.getString("ip")
    let port = interaction.options.getInteger("port") || 25565

    let result = await mc.lookup({ hostname: IP, port: port })

    let buf = Buffer.from(result.status.favicon.replace(/data:image\/[^;]+;base64,/, ""), "base64")
    let attach = await new AttachmentBuilder(buf)
    .setName("icon.png")
    
    let motdEmbed = new EmbedBuilder()
        .setTitle(`Status of ${IP}`)
        .setDescription(result.status.description?.text || result.status.description) // MOTD
        .addFields(
            {name: "Players:", value: `${result.status.players.online}/${result.status.players.max}`},
            {name: "Version:", value: result.status.version.name}
        .setThumbnail("attachment://icon.png")
        .setColor("DarkGreen")
    )

    interaction.reply({ embeds: [ motdEmbed ], files: [ attach ] })
}

最终代码

,我们完成了!不是那么艰难吗?这就是代码应该看起来:

import { Client, Events, EmbedBuilder, REST, SlashCommandBuilder, Routes, GatewayIntentBits, AttachmentBuilder }  from 'discord.js';
import mc from "minecraftstatuspinger"

const client = new Client({intents: [GatewayIntentBits.Guilds]});

client.once("ready", c => {
    console.log(`Ready! Logged in as ${c.user.tag}`);
});

client.on(Events.InteractionCreate, (interaction) => {
    if (!interaction.isChatInputCommand()) return;
    const { commandName } = interaction;
    if (commandName === 'ping') return getMcStatus(interaction);
});

async function getMcStatus(interaction){
    let IP = interaction.options.getString("ip")
    let port = interaction.options.getInteger("port") || 25565

    let result = await mc.lookup({hostname: IP, port: port})

    let buf = Buffer.from(result.status.favicon.replace(/data:image\/[^;]+;base64,/, ""), "base64")
    let attach = await new AttachmentBuilder(buf)
    .setName("icon.png")


    let motdEmbed = new EmbedBuilder()
        .setTitle(`Status of ${IP}`)
        .setDescription(result.status.description?.text || result.status.description) // MOTD
        .addFields(
            {name: "Players:", value: `${result.status.players.online}/${result.status.players.max}`},
            {name: "Version:", value: result.status.version.name})
        .setThumbnail("attachment://icon.png")
        .setColor("DarkGreen")

    interaction.reply({ embeds: [ motdEmbed ], files: [ attach ] })
}


const rest = new REST({ version: "10" }).setToken(process.env.token)
let commands = [
        new SlashCommandBuilder().setName("ping").setDescription("Pings a Minecraft server.")
            .addStringOption(option =>
                    option.setName("ip")
                    .setDescription("Sex")
                    .setRequired(true)
            )
            .addIntegerOption(option =>
                option.setName("port")
                .setDescription("Sex") 
                    .setRequired(false)
                    .setMinValue(0)
                    .setMaxValue(65535)
            )
    ].map(command => command.toJSON())
    rest.put(Routes.applicationCommands("Application ID"), { body: commands })


// Log in to Discord with your client's token
client.login(process.env.token);

感谢您遵循本教程,我希望一切顺利!