🤖

[Rust] DiscordのBOT作成 < serenity > - ⑥

2022/09/10に公開

セレクトメニュー作成と応答

選択された項目名を返す

  • 下記のようなセレクトメニューを作成し、選ばれた項目の値を返す


セレクトメニュー

ソースコード

  • このサンプルではセレクトメニューの選択肢項目を各々作成しているが、項目の数が多い場合は列挙型などでまとめると管理しやすい
  • 項目を一度選ぶと変更することができないため、場合によっては確定用のボタンなどを別途追加してもよい
main.rs
use serenity::builder::{CreateActionRow, CreateSelectMenu, CreateSelectMenuOption};     // アクション関係のモジュール

use std::time::Duration;        // タイムアウト用

~~~~~~~~~~中略~~~~~~~~~~

if msg.content == "!menu" {

    //----------項目1の作成----------
    let mut opt1 = CreateSelectMenuOption::default();

    opt1.label(format!("{} {}", '\u{1F170}', "A型"));
    opt1.value("Atype");

    //----------項目2の作成----------
    let mut opt2 = CreateSelectMenuOption::default();

    opt2.label(format!("{} {}", '\u{1F171}', "B型"));
    opt2.value("Btype");

    //----------項目3の作成----------
    let mut opt3 = CreateSelectMenuOption::default();

    opt3.label(format!("{} {}", '\u{1F17E}', "O型"));
    opt3.value("Otype");

    //----------項目4の作成----------
    let mut opt4 = CreateSelectMenuOption::default();

    opt4.label(format!("{} {}", '\u{1F18E}', "AB型"));
    opt4.value("ABtype");


    //----------セレクトメニューの作成----------
    let mut menu = CreateSelectMenu::default();

    menu.custom_id("menu");
    menu.placeholder("選んでください");
    menu.options(|f| {
        f.add_option(opt1)
            .add_option(opt2)
            .add_option(opt3)
            .add_option(opt4)
    });

    //----------アクションにセレクトメニューを追加----------
    let mut act = CreateActionRow::default();

    act.add_select_menu(menu);

    //----------メッセージ(セレクトメニュー)の送信----------
    let m = msg.channel_id.send_message(&ctx, |m| {
            m.content("項目を選択してください")
                .components(|c| c.add_action_row(act))
        })
        .await
        .expect("エラー");

    //----------インタラクションとタイムアウトの設定----------
    let mi = match m
        .await_component_interaction(&ctx)
        .timeout(Duration::from_secs(10))
        .await
    {
        Some(interaction) => interaction,
        None => {
            m.delete(&ctx).await.expect("エラー");
            msg.reply(&ctx, "タイムアウト").await.expect("エラー");
            return
        }
    };

    //----------インタラクションに対する応答----------
    m.delete(&ctx).await.expect("エラー");
    
    match &*mi.data.values[0] {
        "Atype"    => msg.reply(&ctx.http, "A型").await.expect("エラー"),
        "Btype"    => msg.reply(&ctx.http, "B型").await.expect("エラー"),
        "Otype"    => msg.reply(&ctx.http, "O型").await.expect("エラー"),
        "ABtype"    => msg.reply(&ctx.http, "AB型").await.expect("エラー"),
        _           => {
            println!("エラー");
            return
        }
    };
}

クレートの設定

  • 前述のボタンの作成と同じ
Cargo.toml
serenity = { version = "0.11.5", default-features = false, features = ["cache", "client", "collector", "gateway", "rustls_backend", "model"] }

ソースコード解説

項目の作成

  • CreateSelectMenuOption::default()
    • 項目のオブジェクトを作成する

  • .label()
    • 項目に表示するテキスト等を設定する

  • .value()
    • 項目に持たせる値を設定する
セレクトメニューの作成
let mut opt1 = CreateSelectMenuOption::default();

opt1.label(format!("{} {}", '\u{1F170}', "A型"));
opt1.value("Atype");
opt1.description("これは血液型です");

  • 項目の作成時に.description()を追加すると、セレクトメニューの説明文を追加できる
  • 複数の項目で設定した場合、最新のものが反映される
説明文を追加する
let mut opt1 = CreateSelectMenuOption::default();

opt1.label(format!("{} {}", '\u{1F170}', "A型"));
opt1.value("Atype");
opt1.description("これは血液型です");       // 説明文を追加する


説明文を追加する


  • 項目の作成時に.default_selection(true)を追加すると、セレクトメニュー未選択時のデフォルトの項目に設定できる
セレクトメニューのデフォルト項目
let mut opt1 = CreateSelectMenuOption::default();

opt1.label(format!("{} {}", '\u{1F170}', "A型"));
opt1.value("Atype");
opt1.description("これは血液型です");

opt1.default_selection(true);        // デフォルトの項目に設定する


セレクトメニューのデフォルト項目

セレクトメニューの作成

  • CreateSelectMenu::default()
    • セレクトメニューのオブジェクトを作成する

  • .custom_id()
    • セレクトメニューのID(名前)を設定する

  • .placeholder()
    • セレクトメニュー未選択時の表示を設定する

  • .options()
    • セレクトメニューの項目を設定する

  • .add_option()
    • セレクトメニューのオブジェクトに作成した項目を追加する
セレクトメニューの作成
let mut menu = CreateSelectMenu::default();

menu.custom_id("menu");
menu.placeholder("選んでください");
menu.options(|f| {
    f.add_option(opt1)
        .add_option(opt2)
        .add_option(opt3)
        .add_option(opt4)
    });

アクションにセレクトメニューを追加

  • CreateActionRow::default()
    • アクションのオブジェクトを作成する

  • .add_select_menu()
    • アクションのオブジェクトに作成したセレクトメニューを追加する
アクションにセレクトメニューを追加
let mut act = CreateActionRow::default();

act.add_select_menu(menu);

メッセージ(セレクトメニュー)の送信

  • セレクトメニューの送信には.send_message()を使う

  • .components()
    • .send_message()で送信するテキスト以外のコンポーネント(ここではセレクトメニュー)をセットする

  • .add_action_row()
    • コンポーネントにアクションのオブジェクトを追加する
メッセージ(セレクトメニュー)の送信
let m = msg.channel_id.send_message(&ctx, |m| {
    m.content("ボタンを選択してください")
        .components(|c| c.add_action_row(act))
    })
    .await
    .expect("エラー");

インタラクションとタイムアウトの設定

  • .await_component_interaction()
    • 送信したメッセージ(ここではセレクトメニュー)に対してのインタラクション(ユーザーが項目を選ぶ動作)を待つ

  • .timeout()
    • インタラクションの待機時間を設定する
    • Duration::from_secs(10)によって10秒が設定される

  • インタラクションが時間内に行われた場合、Some(interaction)が返されmiにインタラクション結果(interaction)を取り込む
  • インタラクションがタイムアウトした場合、Noneが返されNone => { }を実行する
インタラクションとタイムアウトの設定
let mi = match m
        .await_component_interaction(&ctx)
        .timeout(Duration::from_secs(10))
        .await
    {
        Some(interaction) => interaction,
        None => {
            m.delete(&ctx).await.expect("エラー");
            msg.reply(&ctx, "タイムアウト").await.expect("エラー");
            return
        }
    };

インタラクションに対する応答

  • .delete()を使用して送信したメッセージ(ここではセレクトメニュー)を削除し、選ばれた項目の値( mi.data.values[0])に合わせたリプライを返す
インタラクションに対する応答
m.delete(&ctx).await.expect("エラー");

match &*mi.data.values[0] {
    "Atype"    => msg.reply(&ctx.http, "A型").await.expect("エラー"),
    "Btype"    => msg.reply(&ctx.http, "B型").await.expect("エラー"),
    "Otype"    => msg.reply(&ctx.http, "O型").await.expect("エラー"),
    "ABtype"   => msg.reply(&ctx.http, "AB型").await.expect("エラー"),
    _          => {
        println!("エラー");
        return
    }
};

Discussion