[精讚] [會員登入]
90

【Discord bot 6.2.1】[JDA6](StringSelectMenu)如何從訊息中提取選單並取得使用者選擇的選項

從 JDA5 使用到現在的 JDA6,時間過得還真快

分享此文連結 //n.sfs.tw/16569

分享連結 【Discord bot 6.2.1】[JDA6](StringSelectMenu)如何從訊息中提取選單並取得使用者選擇的選項@小編過路君子
(文章歡迎轉載,務必尊重版權註明連結來源)
2026-01-05 05:29:46 最後編修
2026-01-05 00:07:26 By 過路君子
 

哈囉大家好,這裡是正在修復JDA升級後出現的BUG的小編過路君子

這次 JDA5 到 JDA6 真的算是很大一次的升級,很多的類別路徑和功能的改了。

 

 

算從 2021 年 6 月 21 號小編就開始撰寫 Discord Bot 了,但是那時候還是使用 Pyhon discord bot v1.7.3,看看現在都更新到了 v2.6.4,但是也無所謂了,因為小編早早就投向 JDA 的懷抱了。

不得不說在按鈕響應上面,Python 的寫法確實比較簡單,在 Java 上小編用了一套非常縝密的寫法才實現了相同的功能,簡單來說使用到了 Java 中的 Reflection 功能才好不容易實現類似 Python Cog 的功能。

小編將 JDA 內的指令寫成跟 Python 中的 Cog 一樣,可以用比較簡單的方式靜態加載或是卸載,動態的小編實在懶得實現,就實現靜態就好,當時也是砸了好幾天才實現呢。

 

這次非常多的類別路徑都改了,JDA6 最大的更新就是 Compose 重製,而非常不幸的 StringSelectMenu 就是這次被重製的其中一員......

現在的 Compose 限制更少且功能更加強大(但仍有部分功能需要手動開啟 Components V2),但小編覺得若非真的需要一次提供多個選單這樣的功能,要不可以維持在 JDA4 或是 JDA5 就可以了。

在開始之前,小編先在這邊丟出版本表,如果以後還有 JDA7,那這篇可能又要更新了:

軟體 版本
舊JDA版本 5.6.1
新JDA版本 6.2.1
Java 25.0.1 LTS

 

創建選單

接下來小編會同時丟出新的寫法和舊的寫法,要注意別看錯囉!

在 JAD5 (舊版)內我們可以這樣創建一個選單:

import module java.base;

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;


void sendStringSelectMenu(JDA jda)
{
	String[] testData = {"option1", "option2", "option3", "option4", "option5"};
	
	List<SelectOption> options = new LinkedList<SelectOption>();

	for(int x=0, length=testData.length; length>x; x++)
		options.add(SelectOption.of(testData[x], x));

	// VM selected menu.
	StringSelectMenu vm = StringSelectMenu.create("menuUUID")
										  .addOptions(options)
										  .setDefaultOptions(options.get(0))
										  .setPlaceholder(options.get(0).getValue())
										  .build();

	jda.getTextChannelById(00000000000000000000L)
	   .sendMessage("test message")
	   .setActionRow(vm)
	   .queue();
}

※ import module 為 Java 25 的新語法,如果編譯報錯,請檢查使用的 Java 版本是否過低或是手動 import 所需的 Java 內建的類別。

小編很推薦大家將 Java 升級到 Java 25,因為 Java 25 最佳化了執行階段,相較於舊版的 Java 擁有更高、更好的執行效率,小編覺得最大的改動就是「 緊湊物件標頭(JEP 519: Compact Object Headers)」,將 Java 物件標頭的大小從傳統的 12 bytes 或 16 bytes 縮減至 8 bytes,小編覺得實屬不錯。

而 JDA6(新版)的寫法則改成以下:

import module java.base;

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.components.actionrow.ActionRow;
import net.dv8tion.jda.api.components.selections.SelectOption;
import net.dv8tion.jda.api.components.selections.StringSelectMenu;


void sendStringSelectMenu(JDA jda)
{
	String[] testData = {"option1", "option2", "option3", "option4", "option5"};
	
	List<SelectOption> options = new LinkedList<SelectOption>();

	for(int x=0, length=testData.length; length>x; x++)
		options.add(SelectOption.of(testData[x], x));

	// VM selected menu.
	StringSelectMenu vm = StringSelectMenu.create("menuUUID")
										  .addOptions(options)
										  .setDefaultOptions(options.get(0))
										  .setPlaceholder(options.get(0).getValue())
										  .build();

	jda.getTextChannelById(00000000000000000000L)
	   .sendMessage("test message")
	   .setComponents(ActionRow.of(vm))
	   .queue();
}

其中兩個比較重大的更新:

一、import 資訊更新,刪除了 interactions 資料夾,所以 JDA6 中的相關包路徑變短。

二、setActionRow() 函數棄用,全面改成使用 setComponents(),在 JDA5 內可以在這兩者中擇一使用,JDA6 則只能使用 setComponents();順帶一提,addActionRow() 也同樣被棄用了,請改用 addComponents。

 

讀取使用者的選擇

這邊小編所提到的部分不是使用 onStringSelectInteraction(StringSelectInteractionEvent event) 來讀取當前使用者的選擇,因為若使用該方法來讀取,無論在 JDA5 或是 JDA6 都是直接呼叫 event.getValue() 就可以取得使用者的選擇了。

小編這邊介紹的是直接透過 Message 類別來讀取使用者所做的選項,那在什麼狀況下會需要用到這樣的手法?就是我們不希望使用者一對選單做選擇就觸發接下來的事件,而是需要使用者點擊一下按鈕之後才進行下一步的動作,這時候就不會觸發 onStringSelectInteraction 事件,而是會觸發 onButtonInteraction 這個事件,這時候就必須用迂迴的手法取得使用者對選單的選擇了。

那就廢話不多說,直接看 JDA5(舊版)的程式碼吧:

import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;


// jda.getTextChannelById(00000000000000000000L)
// 	  .sendMessage("test message")
// 	  .setComponents(ActionRow.of(vm))
//	  .queue();

@Override
void onButtonInteraction(ButtonInteractionEvent event)
{
	Message message = event.getMessage();
	
	// Get String select menu.
	ItemComponent menu = message.getComponents()	// List<LayoutComponent>
								.get(0)				// LayoutComponent
								.getComponents()	// List<ItemComponent>
								.get(0);			// ItemComponent

	// Get User select.
	String selected = ((StringSelectMenu)menu).getPlaceholder();
	
	System.out.println(selected);
}

其中對於 getComponents().get(0) 的邏輯可以參考下面 JDA6 中小編的解釋,邏輯仍然是一樣的。

而在 JDA6 內雖然呼叫變得稍稍複雜了一點,但是小編卻覺得邏輯變得非常清楚,不會再出現那個奇怪的 ItemComponent 類別,尤其是最後一步,必須將 ItemComponent 類別顯性的強制轉型成 StringSelectMenu 類別,小編就試問,這麼不直覺的寫法到底是在寫程式還是考驗我們的看 Document 的能力......

那現在就拋棄 JDA5,跟著小編來看看 JDA6(新版)的程式碼吧:

import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.components.selections.StringSelectMenu;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;


// jda.getTextChannelById(00000000000000000000L)
// 	  .sendMessage("test message")
// 	  .setComponents(ActionRow.of(vm))
//	  .queue();

@Override
void onButtonInteraction(ButtonInteractionEvent event)
{
	Message message = event.getMessage();
	
	// Get String select menu.
	StringSelectMenu menu = message.getComponents()			// List<MessageTopLevelComponentUnion>
								   .get(0)					// MessageTopLevelComponentUnion
								   .asActionRow()			// ActionRow
								   .getComponents()			// List<ActionRowChildComponentUnion>
								   .get(0)					// ActionRowChildComponentUnion
								   .asStringSelectMenu();	// StringSelectMenu
	
	// Get User select.
	String selected = menu.getPlaceholder();
	
	System.out.println(selected);
}

另外,這邊的 message.getComponents() 是有序的,而且順序就是傳送訊息的順序。

例如 Discord Bot 在發送訊息的時候送了一個這樣的訊息:

jda.getTextChannelById(00000000000000000000L)
   .sendMessage("test message")
   .setComponents(ActionRow.of(button1, button2), ActionRow.of(vm))
   .queue();

那就要改成這樣取得選單:

StringSelectMenu menu = message.getComponents()
							   .get(1)
							   .asActionRow()
							   .getComponents()
							   .get(0)
							   .asStringSelectMenu();

 

以上就是 JDA5 和 JDA6 選單方面的差別啦,雖然在 Components 這方面更新非常巨大,但是撇除這點,其他程式碼幾乎都可以直接沿用。

所以總結來說,小編可以接受這樣的升級,但是如果以後真的出了 JDA7,那......小編可能會慎重考慮,考慮成為 JDA6 釘子戶,除非 JDA6 有重大的安全性問題才會更新上去,或許啦。

不排除小編突然一拍腦袋就更新上去整自己的可能性,就像現在一樣。

 

 

 

後記

除了上文介紹到的 StringSelectMenu 以外,其中比較大的更新還有 TextInput 和 Label......等等,其中又以 Label 更新最大,Label 直接被拆分成了 Label、TextDisplay 和 FileDisplay。

以前只要一個 Label 就可以解決所有文字的顯示問題,現在則是在不同的地方要用不同的方式來顯示文字或檔案,小編也不知道這樣的改動好不好,但可以肯定的是,小編覺得程式碼寫起來的邏輯比以前好太多了。

所以小編對於這次的 JDA 換代給予非常高的評價!

END

你可能感興趣的文章

【Maven】如何創建一個簡單可部屬的WAR檔 滿重要的一個大功能,在使用JAVA網頁伺服器的時候一定會需要這個WAR檔來進行部屬

【JDA/discord bot】package does not exist fix JDA 4.0 和 5.0 差別還是很大的

【PaperMC - API】如何發送指令到伺服器內 How to sending or executing commands to server

【Wildfly/jBoss】[Linux](Connection Datasource)如何與MySQL資料庫建立連線 網頁瀏覽器和資料庫的關係密不可分,而通常會將兩者分開架設在不同的伺服器上面來提供服務,這時要如何進行連線呢?

【C++】使用SFML製作讓方塊落下的畫面 從開啟新視窗延伸過來的應用(?),配合上一篇所使用到開啟一個可渲染視窗的那堆程式碼的延伸。

【教程】(進階版)如何用Sigil製作一本高質量的epub 下載好了Sigil之後除了直接把文字貼進去以外,還有:變更字型、著色、導入CSS……等等功能,不知道你有沒有發現呢?

隨機好文

高捷少女:地下城的探險少女② 「等我一下喔,我好像有帶去漬的清潔噴霧。」婕兒翻翻飛揚,拿出噴霧劑給小穹,小穹趕緊對著汙漬噴了噴,紅茶漬果然乾淨了許多。「婕兒,謝謝妳。來,還妳。」小穹感謝地把噴霧還給她,卻發現婕兒盯著打開的飛揚,一

高捷少女:美麗島的守護者① 婕兒跟耐耐嚇得臉色發白。「耐耐,小穹有這方面的愛好喔?」婕兒小聲地說。耐耐默默的拿出筆記本,臉上浮現一層微笑:「這麼大的新聞,要趕快記下來!」

高捷少女:美麗島的守護者③ 小雅閉上眼睛,思索在高捷發生的點點滴滴。她心意已決,在高捷的日子的確也有快樂的部分,不過她相信換個方向是更好的決定。有關高捷的所有美好回憶,小雅決定保留在心裡就好,繼續在高捷工作只會讓自己更痛苦而已,

【歌評】蓮台野夜行 - 魔術師梅莉(魔術師メリー)  對於同一首歌每一個人都有不同的見解,看看別人對於這一首歌的看法,說不定就可以聽出這首歌想要表達的事情!

【歌評】蓮台野夜行 - 幻想的永遠祭 蓮台野的探險雖然結束了,但是,誰知道是不是一段新的探險的序曲呢?