2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

discord botにminecraftの座標を登録してtpコマンドを作ってもらう

Posted at

前提

この記事は,discord botを使ってminecraft serverを走査したいアドベントカレンダーの記事です.

  • WSL, Ubuntu 22.04.1
  • discord.py 2.1.0
  • Python 3.10.6

という環境で行っています.

やりたいこと

minecraftで海底神殿などを見つけたとき,座標をdiscord botにコマンド経由で別のチャンネルにまとめておいてくれるようなものを作りたいです.
そして,それだけだと面白みが何もないので,とりあえずそこにテレポートできるようにtpコマンドをコピーできるようにしたいです.

というわけで作っていきましょう.

実装

まずはとりあえず座標を入力して表示するコマンドを作りましょう.

コマンドの作成

import os
import discord
from discord import app_commands, Client, Intents, Interaction
from discord.ui import View, Button
from discord.app_commands import CommandTree
from dotenv import load_dotenv


load_dotenv()


class MyClient(Client):
    def __init__(self, intents: Intents) -> None:
        super().__init__(intents=intents)
        self.tree = CommandTree(self)

    async def setup_hook(self) -> None:
        await self.tree.sync()

    async def on_ready(self):
        print(f"login: {self.user.name} [{self.user.id}]")


intents = Intents.default()
client = MyClient(intents=intents)


@client.tree.command()
@app_commands.describe(what="what you found?")
async def set_locate(
    interaction: Interaction,
    what: str,
    x: int,
    y: int,
    z: int,
):
    message = f"{what} is located: x:{x}, y:{y}, z:{z}"
    await interaction.response.send_message(message)


client.run(os.getenv("TOKEN"))

実行してみると,とりあえず大丈夫そうですね.

次にやることは,このメッセージを別のチャンネルに送ることです.チャンネルのIDからGuildChannelを取得します.そのチャンネルに対してメッセージをsendメソッドで送ります.

別チャンネルに送信

import os
import discord
from discord import app_commands, Client, Intents, Interaction
from discord.ui import View, Button
from discord.app_commands import CommandTree
from dotenv import load_dotenv


load_dotenv()


class MyClient(Client):
    def __init__(self, intents: Intents) -> None:
        super().__init__(intents=intents)
        self.tree = CommandTree(self)

    async def setup_hook(self) -> None:
        await self.tree.sync()

    async def on_ready(self):
        print(f"login: {self.user.name} [{self.user.id}]")


intents = Intents.default()
client = MyClient(intents=intents)


@client.tree.command()
@app_commands.describe(what="what you found?")
async def set_locate(
    interaction: Interaction,
    what: str,
    x: int,
    y: int,
    z: int,
):
    message = f"{what} is located: x:{x}, y:{y}, z:{z}"

+   log_channel = client.get_channel(1052926772607463456)
+   await log_channel.send(message)

-   await interaction.response.send_message(message)
+   await interaction.response.send_message("add location!")


client.run(os.getenv("TOKEN"))

image.png

image.png

別のチャンネルに送れました.

では続いて,別チャンネルに送信したメッセージにtpコマンドのコピーができるボタンを作ります.とりあえずコピーのところは置いておいて,ボタンを作りましょう.
Viewを継承したサブクラスに,discord.ui.button()デコレータを使用しサブクラスにボタンを追加します.この下に書いた処理がcallbackとして扱われます.
注意点として,スーパークラスのコンストラクタの呼び出しを忘れないようにしましょう.1時間溶かすことになります(1敗).

import os
import discord
from discord import app_commands, Client, Intents, Interaction
from discord.ui import View, Button
from discord.app_commands import CommandTree
from dotenv import load_dotenv


load_dotenv()


+ class TpCommandView(View):
+     def __init__(self):
+         super().__init__()
+ 
+     @discord.ui.button(label="get tp command")
+     async def getTpCmddButton(self, interaction: Interaction, button: Button):
+         await interaction.response.send_message(
+             "copy to your clipboard!", ephemeral=True
+         )


class MyClient(Client):
    def __init__(self, intents: Intents) -> None:
        super().__init__(intents=intents)
        self.tree = CommandTree(self)

    async def setup_hook(self) -> None:
        await self.tree.sync()

    async def on_ready(self):
        print(f"login: {self.user.name} [{self.user.id}]")


intents = Intents.default()
client = MyClient(intents=intents)


@client.tree.command()
@app_commands.describe(what="what you found?")
async def set_locate(
    interaction: Interaction,
    what: str,
    x: int,
    y: int,
    z: int,
):
    message = f"{what} is located: x:{x}, y:{y}, z:{z}"

+   tp_cmd_view = TpCommandView()

    log_channel = client.get_channel(1052926772607463456)
-   await log_channel.send(message)
+   await log_channel.send(message, view=tp_cmd_view)

    await interaction.response.send_message("add location!")


client.run(os.getenv("TOKEN"))

実行してみると下の画像のような感じでボタンが表示され,クリックするとメッセージが送信されました.

image.png

というわけで,実際にはコピーされるようにしましょう.

tpコマンドのコピー

コピーされるというのは,ボタンを押した後Ctr+Vでコマンドが貼り付けられる,という意味です.自分でドラッグしてコピーは面倒なのでそうします.
そのためにはクリップボードに対して操作をしないといけません.どうやらPythonではpyperclipを使うとそれができるみたいです.公式ドキュメントを見てみると,copy()でコピーさせることができるみたいです.

このpyperclipを使うにはインストールが必要なのでしておきましょう.ドキュメントの方には書いていないですが,pypiには書いてあります.

bash
$ pip install pyperclip

インストールできたらさっそく使ってみましょう.
TpCommandViewに対してユーザーが入力した座標を渡すように変更します.その中でtpコマンドを作り,コピーさせます.ちなみにtpコマンドは,/tp x y zとすることで自分がその座標に飛びます.対象を選択することもできますが,ユーザーの選択は面倒そうなので止めておきます.

import os
import discord
from discord import app_commands, Client, Intents, Interaction
from discord.ui import View, Button
from discord.app_commands import CommandTree
+ import pyperclip
from dotenv import load_dotenv


load_dotenv()


class TpCommandView(View):
-   def __init__(self):
+   def __init__(self, locate: list):
        super().__init__()
+       self.locate = locate

    @discord.ui.button(label="get tp command")
    async def getTpCmddButton(self, interaction: Interaction, button: Button):
+       tp_cmd = f"tp {self.locate[0]} {self.locate[1]} {self.locate[2]}"
+       pyperclip.copy(tp_cmd)

        await interaction.response.send_message(
            "copy to your clipboard!", ephemeral=True
        )


class MyClient(Client):
    def __init__(self, intents: Intents) -> None:
        super().__init__(intents=intents)
        self.tree = CommandTree(self)

    async def setup_hook(self) -> None:
        await self.tree.sync()

    async def on_ready(self):
        print(f"login: {self.user.name} [{self.user.id}]")


intents = Intents.default()
client = MyClient(intents=intents)


@client.tree.command()
@app_commands.describe(what="what you found?")
async def set_locate(
    interaction: Interaction,
    what: str,
    x: int,
    y: int,
    z: int,
):
    message = f"{what} is located: x:{x}, y:{y}, z:{z}"

-   tp_cmd_view = TpCommandView()
+   tp_cmd_view = TpCommandView([x, y, z])

    log_channel = client.get_channel(1052926772607463456)
    await log_channel.send(message, view=tp_cmd_view)

    await interaction.response.send_message("add location!")


client.run(os.getenv("TOKEN"))

引数3つ書くのが面倒だったので配列で受け取るようにしてみました.まあ,ネザー経由の道を作るときに値を1/8にする必要があるので配列でもいいんじゃないかなという妥協です.

tpcmd.gif

ちゃんとクリップボードにコピーしてくれました.discordはWindowsで動いていますので,他のOSだとどうなるかはちょっとわかりません.

ですが,概形は同じで大丈夫だと思います.

これでtpコマンドをコピーできたのでminecraftでコマンドを簡単に使えるようになりましたね.まあ,サバイバルでやってるならコマンド打てないんですけどね.

ともかく,discord botにminecraftの座標を登録してtpコマンドを作ってもらうことができました.
お疲れ様でした.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?