経緯
このBotを作ろうとした理由と記事を書いた理由は下記の通りです。
- Discordに新しく追加されたインタラクションであるButtonsを使ってみたかった
- discord.js-buttonsに関する情報がなかった
似たようなことをしたい人の参考になれば良いなと思います。
なお、discord.js-buttonsはリポジトリが削除されているため今後無くなる可能性がありますが
discord.js自体のmasterブランチにButtonsに関するソースコードが追加されている為(2021/06/08現在)
discord.jsが今後のバージョンでButtonsに対応すると思われますのでそちらを待ってもいいかもしれません。
ソースコード
GitHubにあります。
noriokun4649/DiscordSoundEffectBot
利用してるライブラリなどの依存関係はpackage.jsonに書いてありますが下記の通りです。
- "@discordjs/opus": "^0.5.0"
- "opusscript": "^0.0.8"
- "config": "^3.3.6"
- "discord.js": "^12.5.3"
- "discord.js-buttons": "^1.0.0"
- "ffmpeg-static": "^4.3.0"
- "fs": "^0.0.1-security"
discord.js-buttons の使い方
discord.js-buttons の使い方について書いていきます。
discord.js-buttonsのセットアップ
const discord = require('discord.js');
const client = new discord.Client();
const discordbtn = require('discord.js-buttons')(client);
これでdiscord.jsからButtonsを利用する準備は完了です。
##Buttonのコンポーネントを作成
上記のセットアップで定義したdiscordbtn
からMessageButton()
を呼びだし新しいインスタンスを作ることで作成できます。
const button = new discordbtn.MessageButton()
.setStyle('green')
.setLabel('ファイル更新・読込')
.setID('scan');
シンプルな例だとこのようになり、次のようなボタンが作成されます。
そして、このボタンを押したときのButtonIDは'scan'
になります。
このButtonIDは後述する「buttonのイベントを受け取る」で利用するので覚えておきましょう。
MessageButton()
に設定できるパラメータは幾つかあるので例を挙げてみます。
const disabledbutton = new discordbtn.MessageButton()
.setStyle('green')
.setLabel('ファイル更新・読込')
.setID('scan')
.setDisabled(); //このボタンを無効化された状態に設定する
const urlbutton = new discordbtn.MessageButton()
.setStyle('url') //ボタンのスタイルをurlにする
.setLabel('ファイル更新・読込')
.setURL('https://hoge.hoge');
//スタイルがurlの場合setIDではなくsetURLを指定するので注意
指定できるスタイルに関してはDiscordのAPIドキュメントを参照してください。
setStyle
とsetLabel
とsetID
若しくはsetURL
は必須の項目なので注意が必要です。
ボタンの無効化は既に送信済みのボタンが無効化されるわけではないため、既に送信済みのメッセージを編集して無効化したボタンに置き換える時などがユーズケースだと思われます。
##メッセージの送信時にButtonを追加する
ここでは早速メッセージ送信時にButtonを付ける方法に触れていきます。
message.channel.send('1つのボタンがついてるメッセージ', button);
message.reply('1つのボタンがついてる返信メッセージ', button);
このように返信を送る際にも同様の方法でボタンを追加出来ます。
そして、このbuttonはDiscordAPI仕様によりメッセージと一緒に送る必要があります。
また、同じくDiscordAPI仕様によりメッセージは空にできない為buttonだけを送信する方法は今のところありません。
さて、このbuttonですが1つのメッセージに対して最大で5個まで追加する事ができます。
message.reply('複数のボタンがついてる返信メッセージ', { buttons: [button, disabledbutton, urlbutton] });
discord.jsから送信したメッセージのシンタックスハイライトを有効にする際などで使う文法をそのまま使えるので第2引数の連想配列には、buttons
という名前がついたbuttonコンポーネントの配列を渡してあげれば大丈夫です。
buttonのイベントを受け取る
ここではbuttonがおされた後の処理を行います。
client.on('clickButton', async (button) => {
});
このように書き、discord.jsにおけるmessageイベントを受け取るときと同じ書き方が出来ます。
この時、仮引数であるbutton
にはDiscordAPIドキュメントのインタラクションに書かれているフィールドに加えて、レスポンスを返す為の関数が用意されています。
client: Client;
id: string;
version: number;
token: string;
discordID: Snowflake;
applicationID: Snowflake;
guild: Guild;
channel: Channel;
clicker: {};
message: Message;
webhook: WebhookClient;
replied: boolean;
deferred: boolean;
defer(ephemeral: boolean): Promise<void>;
think(ephemeral: boolean): Promise<void>;
get reply(): {
send: (content: string, options?: object) => Promise<void>;
fetch: () => Promise<string>;
edit: (content: any, options?: object) => Promise<any>;
delete: () => Promise<void>;
};
フィールドについて
フィールドについては特筆すべきことは無いと思いますがあるとすればid: string
です。
これはbuttonコンポーネントが押されたときの押されたbuttonと紐付くButtonIDの文字列が入っています。
あとはclicker: {}
ですが、これにはbuttonをおしたユーザーの情報が入っています。
const guild = button.guild;
const member = guild.member(button.clicker.user);
このように使うことで、buttonをおしたユーザのGuildMemberインスタンスを取得できます。
replied
とdeferred
フィールドに関しては次の「関数について」で触れる関数が既に使われているかどうかを判定します。これは関数を使った後にすぐ状態が変化するわけではなく、もう一度buttonがおされた際に変化するので注意が必要です。
関数について
このときthink(boolean)
関数はユーザに処理中若しくは考え中を表示させる関数です。defer(boolean)
関数はユーザに処理を先延ばしにする旨を伝える関数です。先延ばしに関する処理はデスクトップ版Discordがバグっているのかわかりませんが、モバイル版のDiscordのみボタンが押せない状態で処理が先延ばしされます。
この2つの関数において引数になるbooleanはこの考え中や先延ばしというBot側のメッセージをbuttonを押したユーザーだけに伝えるかどうかを指定できます。true
の場合はbuttonを押したユーザーだけに伝えます。
reply.send()
ではbuttonのついたメッセージへ返信出来ます。
reply.delete()
では返信を削除できます。これはthink(boolean)
によって送信された「考え中・・・」というメッセージも対処です。
reply.edit()
では返信を編集出来ます。
reply.fetch()
では返信されているメッセージを取得出来ます。
そして注意が必要なのは、これらの関数のうちいずれかを利用しないと、Discord上で「インタラクションに失敗しました」というメッセージが表示される事です。そのため、buttonが押され正常な処理が出来る場合にはいずれかの関数を使いレスポンスを返してあげましょう。
では早速、具体的に機能を書いていきます。
const listButton = new disbut.MessageButton() //buttonコンポーネントを定義しておく
.setStyle('green')
.setLabel('再生可能ファイル一覧を表示')
.setID('list');
client.on('clickButton', async (button) => { //button押された時に実行される
await button.think(false); //botが考え中ということを全員にしらせる
if (button.id === 'scan') { //ButtonIDがscanの場合
const fileList = readfile(); //ファイル読み込み関数を呼ぶ(buttonには関係ない処理)
if (fileList.length > 0) { //ファイルが1以上だったら
btn = createButtns(fileList);
button.reply.edit('ファイル更新・読込に成功しました。', { code: true, buttons: [listButton] });
//考え中の返信を「ファイル更新・読込に成功しました。」というメッセージに編集する。
} else {
button.reply.edit('ファイル更新・読込に失敗しました。', { code: true });
//考え中の返信を「ファイル更新・読込に失敗しました。」というメッセージに編集する。
}
});
このようになります。わかりづらいかも知れませんが「考え中・・・」と出た後に書き換えられてるかと思います。
因みに上記プログラムのbutton.think(false);
をbutton.think(true);
に変更すると
こうなります。「これらはあなただけに表示されています」という風になりますね。
discord.js-buttonsの使い方おわり
以上がdiscord.js-buttonsを使ったButtonsの使い方です。使いこなせるようになると非常にUXに優れたBotが作成できると思います。
Buttonsを多用しすぎてメッセージチャンネルをあらさないような配慮も考えつつ活用して頂ければ幸いです。
最後に
最後に、私が今回作ったBotについて軽く触れて終わりにします。
Discordに追加されたButtonのインタラクションを使って、効果音流せるBotつくってみた pic.twitter.com/VyZ7uGsxL1
— #𝒊𝒏𝒄𝒍𝒖𝒅𝒆 "🍢🐟🍢煮込みすぎたおでん🍢🐟🍢.𝒉" (@noriokun_blog) June 6, 2021
今回作ったのはDiscordのボイスチャンネル上でサウンドエフェクトを再生できるBotです。
Buttonsを多用して非常に使いやすくできたと思っていますのでよろしければ使ってみてください。
ソースコードに飛ぶ