[精讚] [會員登入]
51

【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】用Maven來託管Wildfly/jBoss的部屬(deploy)、解部屬(undeploy) 極簡版,給未來想要抄作業的小編自己,完整的一切設定檔,可以依照個人需求作增刪。

【Wildfly/jBoss】[Linux] 如何從無到有建立Wildfly網頁伺服器 包含一切所需的指令

【Minecraft】[CoreProtect|BungeeCord]如何重新命名世界或維度名稱 當只有一個伺服器的時候,問題往往處理起來非常簡單,但是一旦當伺服器成長至兩台以上,事情便開始有趣了起來

【教程】[HTML](進階版)如何在手機上編輯電子書(epub)預覽介面 可能會有人認為電子書(epub)只能用電腦來開啟、編輯,其實不是的,手機也可以編輯喔!

【C】(%c, %d)解決讀取字元時的緩衝區殘留 不解決就會莫名其妙地冒出一些莫名其妙的東西

【Discord bot】(ERROR)await self.bot.wait_for() 大坑 今天真的是採到大坑,只找到解決方法,具體原因不明

隨機好文

高捷少女:地下城的探險少女① 婕兒心中一奇,便走上前看著仔細。那塊凹進去的地方中心大約三公分厚,越往邊緣就越淺,圓型直徑十五公分。婕兒拿出銅盤對比一下,發現兩者大小竟然一致,銅盤似乎能夠完整的嵌進去。     婕兒看著凹槽,心中

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

高捷少女:地下城的探險少女⑤ 小穹寫好後,耐耐看了一遍。「我想我應該辦得到。」她說完跪坐在地上,然後把古箏安放在大腿上,並將樂譜放在前面的地板。「要開始囉。」耐耐閉上眼睛,深呼吸一口氣,小穹等人在一旁看著她,心中暗自替她打氣。

高捷少女:購票大作戰② 一個不好的預感浮現,艾米莉亞開始檢查屋子四處。窗戶跟陽台都有關好,也沒有被打開的跡象。但一股無形的壓力,開始在寂靜的公寓中蔓延,她不安地嚥一下喉嚨。最後,她走向那扇窗戶,那前天晚上,白龍為了逃脫,而撞

婕兒──她的青春④ 「投降吧,耐耐!這回合妳將不會再有獲勝的機會了!哈哈哈哈!」 「妳確定?」耐耐臉上泛起一絲微笑,並將手中的牌展示給婕兒看 婕兒的笑容僵住了。恐懼浮現在她的臉。