介绍
本文是为没有经验的 DiscordBot 创建者编写的。如有任何疑问,请参阅附录(尚未编写)。我很想在推特上收到你的来信。
对于那些有创建机器人经验的人,我们总结了 DiscordBot 可以完成的基本处理,所以请使用它。
目录
设置创建机器人的环境
让你的机器人上线
创建命令
在 Discord 服务器上获取成员
授予机器人权限
让你的机器人离线
__文字频道相关____
命令实现
发信息
获取和删除消息(删除所有消息)
发送文件
嵌入消息
__语音频道相关__
授予语音通道相关权限
联系
发送音频
连续音频播放
设置创建机器人的环境
将你的机器人连接到 Discord 服务器
这个Discord 开发者门户我将制作一个机器人。
点击右上角NewApplication->自由设置bot名称->点击“Crete”。
单击左侧菜单中的机器人 -> 添加机器人。
当屏幕看起来像图像时,使用“重置令牌”->“复制”复制此机器人的令牌。
我稍后会使用它。
如果这个令牌泄露了,就会被劫持,所以请不要告诉别人。如果有的话,请使用“重置令牌”重新生成令牌。
向下滚动并更改复选框,如下所示:
PUBLIC BOT 是是否将其设为公共机器人(本次禁用)。
Require OAuth2 Code Grant 是是否指定可以添加的服务器(这次禁用)。
Privileged Gateway Intents 是是否扩展bot的功能(本次全部启用)。允许您向服务器发送消息并获取成员。
接下来,将机器人连接到 Discord 服务器。
OAuth2 ->
☑[范围]机器人
☑ [BOT 权限] 管理员
向下滚动,将生成 URL,复制并搜索。选择要添加机器人的服务器,进行身份验证,机器人将被添加到所选服务器!
java的介绍
接下来,我们将编写程序来操作机器人。首先,我们将准备编程环境。本文:下载并安装 OpenJDK我觉得可以参考一下。
介绍 IntelliJ 和 Kotlin
这个网站下载社区(免费版)从
按照以下步骤安装 IntelliJ。
对于 Windows
当您启动它时,会显示“欢迎〜”并出现屏幕,因此单击“下一步>”按钮继续。
然后,将出现“选择安装位置”屏幕,如果没有特殊原因,请单击“下一步>”按钮继续。
在显示“安装选项”的下一个屏幕上,如果没有特殊原因,请关闭所有内容并继续按“下一步>”按钮。
最后,在显示“选择开始菜单文件夹”的屏幕上,如果没有特别的原因,保持原样。按“安装”按钮开始安装。
当出现“Completing~”安装完成画面时,勾选“Run~”复选框并按{Finish. IntelliJ 将在退出后启动。
对于 macOS
本文:IntelliJ IDEA 安装说明<macOS>请参阅“安装 IntelliJ IDEA”。我认为最好不要“日本化”。更新后,应用程序可能无法启动(可用的解决方法)。
项目创建
首次启动 IntelJ 时,会出现“导入 IntelJ IDE 设置”窗口。由于是交接用,如果没有什么特别的,请选择“不导入设置”,然后确定。
此外,如果您看到诸如“插件更新可用”之类的链接,请单击它进行更新。
我们将创建一个新项目。
新项目-> Gradle
☑Java
☑Kotlin/JVM
☑ Kotlin DSL buld 脚本
在按“下一步”后的屏幕上,自由设置此项目名称。 (例如:MyTestBot)
确定项目名称后,按完成开始!
更新项目
我们将更新版本。
制作文件项目-> gradle(红色)-> 6.8 7.0.2。
将Project -> gradle(black) -> wrapper -> gradle-wrapper.properties 中的distributionUrl 6.8 改写为7.0.2 并按右上角显示的大象标记进行更新。更新完成后删除 6.8 文件。
现在您已准备好构建您的机器人!
实现功能
让你的机器人上线
Discord 服务器上的 bot 还处于离线状态,所以我要上线了!
现在,机器人生产正式开始!
- 更新构建工具
- 编辑源代码
更新构建工具
首先,我们将重写 Gradle,这是用于构建和运行项目的工具。
将以下内容添加到项目文件 -> src -> build.gradle.kts 文件中,然后按大象标记进行更新。repositories { ... maven("https://m2.dv8tion.net/releases") } dependencies { ... implementation("net.dv8tion:JDA:5.0.0-alpha.18") }然后添加 Application{} 并再次更新。
application { mainClass.set("${group}.${rootProject.name}.MainKt") }编辑源代码
接下来,我们将创建一个地方来编写程序。
点击项目名称 -> src -> main -> kotlin -> new -> Kotlin Class/File。
将文件的名称创建为 BotClient。
将以下源代码复制并粘贴到创建的文件中。解释附在这里!import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDABuilder import net.dv8tion.jda.api.OnlineStatus import net.dv8tion.jda.api.entities.Activity import net.dv8tion.jda.api.entities.Guild import net.dv8tion.jda.api.hooks.ListenerAdapter import net.dv8tion.jda.api.requests.GatewayIntent const val TOKEN = "CopyしたToken" const val GUILD_ID = "DiscordサーバーのID" fun main(){ BotClient().main() } class BotClient : ListenerAdapter() { private lateinit var jda: JDA private lateinit var guild: Guild fun main() { val intents = listOf(GatewayIntent.GUILD_MESSAGES) jda = JDABuilder.createLight(TOKEN, intents) .setRawEventsEnabled(true) .addEventListeners(this) .setStatus(OnlineStatus.ONLINE) .setActivity(Activity.playing("@セカイ")) .build() jda.awaitReady()// ログイン完了まで待機 guild = jda.getGuildById(GUILD_ID)!!// ぬるぽ -> 開発者ポータルからBotをサーバーに入れる } }按 fun main(){} 右侧的绿色 ▶ 运行此应用程序。
每次重写程序时,按此 ▶ 应用更新。
如果您的日志看起来像这样,那么您就可以开始了!
检查您的 Discord 服务器,看看它是否在线!
要退出应用程序,请按右上角的“红色方块”标记。渠道相关
总结了该频道的主要操作。
创建命令
当您想到机器人时,您会想到命令。编写处理命令的代码。
斜线命令
斜杠命令是在发送 Discord 消息时在开头添加“/”的命令。
这一次,如果您发送“/hallo”,它将返回“Hello World!”。
添加在 fun main() 中注册命令的代码和在外部处理命令的代码。
解释附在这里!fun main(){ BotClient().main() } class BotClient : ListenerAdapter() { ... fun main() { ... // コマンドをリセット guild.updateCommands().queue() // スラッシュコマンドを作成 val slash: SlashCommandData = Commands.slash("hallo", "「Hallo World!」を送信します") val cmdAry = arrayListOf<CommandData>(slash) // スラッシュコマンドを指定したサーバーに登録 guild.updateCommands().addCommands(cmdAry).queue() } override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "hallo") { //「Hallo World!」を送信 event.reply("Hallo World!").setEphemeral(false).queue() } } }消息命令
可以通过“右键单击 Discord 中的消息正文 -> 单击应用程序”来使用消息命令。
更改 BotClient() 的 main() 中的创建斜线命令。// スラッシュコマンドを作成 val slash: SlashCommandData = Commands.slash("hallo", "「Hallo World!」を送信します") val message = Commands.message("neko") val cmdAry = arrayListOf<CommandData>(slash, message)在 BotClient 中添加它。
override fun onMessageContextInteraction(event: MessageContextInteractionEvent) { if (event.name == "neko") { //「にゃん(⋈◍>◡<◍)。✧♡」を送信 event.reply("にゃん(⋈◍>◡<◍)。✧♡").setEphemeral(false).queue() } }用户命令
用户命令可以通过“右键单击消息屏幕上的用户图标->单击应用程序”来使用。
更改 BotClient() 的 main() 中的创建斜杠命令。// スラッシュコマンドを作成 val slash: SlashCommandData = Commands.slash("hallo", "「Hallo World!」を送信します") val message = Commands.message("neko") val user = Commands.user("dog") val cmdAry = arrayListOf<CommandData>(slash, message, user)在 BotClient 中添加它。
override fun onUserContextInteraction(event: UserContextInteractionEvent) { if (event.name == "dog") { //「わん★わん」を送信 event.reply("わん★わん").setEphemeral(false).queue() } }在 Discord 服务器上获取成员
让我们获取频道中的成员。
为此,需要从机器人端和程序端传递访问权限(特权)。
机器人端的权限没有问题,因为所有 Privileged Gateway Intents 都在 Discord 的开发者门户上启用。我们将编写代码从程序端获取访问会员信息的权限。
在 BotClient 中重写 main 的意图并添加其他的。fun main() { val intents = listOf( GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, GatewayIntent.MESSAGE_CONTENT) val cacheFlags = listOf(CacheFlag.MEMBER_OVERRIDES) jda = JDABuilder.createLight(TOKEN, intents) .setRawEventsEnabled(true) .addEventListeners(this) .setStatus(OnlineStatus.ONLINE) .setActivity(Activity.playing("@セカイ")) .enableCache(cacheFlags) //追記 .setMemberCachePolicy(MemberCachePolicy.ALL) //追記 .build() ... //メンバー情報を取得 guild.members.forEach { member -> println("MemberName: " + member.effectiveName) } }授予机器人权限
与之前获取成员的情况一样,可能需要获取权限。
以下是如何获得它。・通过 Discord 开发者门户端的许可
OAuth2 ->
☑[范围]机器人
☑ [BOT 权限] 管理员
・通过程序方面的许可
启用意图
启用缓存标志
启用缓存策略让你的机器人离线
这个过程的实现在本文的文本通道相关命令的实现中有所涉及。
jda.shutdownNow()文字频道相关
命令实现
使用您自己的前缀实施命令,以随时使您的机器人离线。
前缀用于识别定向到机器人的消息。
这次,我们将设置“!shutdown”(*带半角空格)以“!”为前缀,并描述使机器人下线的过程。override fun onMessageReceived(event: MessageReceivedEvent) { //prefix(接頭詞)メッセージの処理 val msg = event.message.contentRaw val msgList = msg.split(" ") //""の間は半角スペース if (msgList[0] == "!"){ //接頭詞「!」で始まっているかどうか when(msgList[1]) { "shutdown" -> { Thread.sleep(3000) //処理を3秒間一時停止 jda.shutdownNow() //オフラインにする } } } }发信息
发送消息可以获取发送消息的通道并发送。
channel.sendMessage("またね~♪").queue()让我们将它添加到命令实现中编写的代码中。
"shutdown" -> { event.channel.sendMessage("またね~♪").queue() Thread.sleep(3000) //処理を3秒間一時停止 jda.shutdownNow() //オフラインにする }要获取要发送的通道,您可以从事件中获取它,该事件包含有关发送“!shutdown”消息的通道的信息。
“再见~♪”应该已经发送到 Discord 并且机器人应该已经离线了。获取和删除消息
我们将实施消息删除。如果你做一个机器人,Discord 的消息字段会变得粗糙,所以我们也会编写一个程序来删除所有消息。
我们想在发送“!delMsg”时清除它。
消息删除过程如下。when(msgList[1]) { ... "delMsg" -> { val textChannel = event.channel val delMsg = textChannel.history.retrievePast(1).complete()[0] //Discordnの最新メッセージから1番目までを取得 delMsg.delete().queue() println("$delMsg を削除したよ♪") } } }要删除所有消息:
我想在发送“!allDelMsg”时清除所有内容。when(msgList[1]) { ... "allDelMsg" -> { //全メッセージを削除 val textChannel = event.channel var delMsg = textChannel.history.retrievePast(1).complete() var nextDelMsg = delMsg event.channel.sendMessage("いいライブだったね♪").queue() Thread { while (delMsg != null) { try { textChannel.deleteMessageById(delMsg[0].id).queue() } catch (e: IndexOutOfBoundsException) { println("削除が完了したよ♪") return@Thread } println("$delMsg を削除したよ♪") while (delMsg == nextDelMsg) { //削除完了まで待機 Thread.sleep(500) nextDelMsg = textChannel.history.retrievePast(1).complete() } delMsg = nextDelMsg } }.start() } }发送文件
发送文件与发送消息同时完成。我似乎无法发送文件。
channel.sendMessage("またね~♪").addFiles(file).queue()我还将描述一个实现示例。
首先,复制本地文件路径(指向位置)。
更改“!shutdown”的处理。该文件将在机器人脱机后立即发送。
*如果将路径复制并粘贴到“C:\Users\***\Pictures\Screenshots\***”,请将“\”更改为“\\”和“C:\\Users\\***” \\图片\\截图\\***”。
* 如果显示“日元标记”,则为错误。它实际上是一个半角正斜杠。"shutdown" -> { val file = FileUpload.fromData(File("コピーしたパス")) event.channel.sendMessage("またね~♪").addFiles(file).queue() Thread.sleep(3000) //処理を3秒間一時停止 jda.shutdownNow() //オフラインにする }嵌入消息
嵌入消息 (Embed) 是只能由机器人发送的类似图像的消息。
这个嵌入的消息部分引用了这篇文章:使用 Kotlin 3 启动 Discord Bot ~Embed implementation~。
首先创建一个新文件。
“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 Embed。
即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
将创建的文件命名为 Embed。
在创建的文件中编写代码。import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.entities.MessageEmbed import net.dv8tion.jda.api.events.message.MessageReceivedEvent class Embed(private val event: MessageReceivedEvent) { fun buildEmbed(): MessageEmbed { val embed = EmbedBuilder() .setTitle("Tutorial Embed","https://example.com") //タイトル文字列。第2引数にURLを入れるとタイトルを指定URLへのリンクにできます .setAuthor("tutorial bot","https://example.com", event.member?.avatarUrl) //Botの情報。タイトルと同じくリンクを指定できる他、第3引数にアイコン画像を指定できます。 .appendDescription("Embed made with Kotlin JDA!!") //Embedの説明文 .setColor(0x00ff00) //Embed左端の色を設定します. .addField("フィールド1","値1",false) //以下3つフィールドをセット .addField("フィールド2","値2",true) .addField("フィールド3","値3",true) .setThumbnail("https://example.com") //サムネイル(小さい画像) .setImage("https://example.com") //イメージ(大きい画像) .setFooter("made by Yosuga","https://example.com")//フッターには開発者情報を入れるといいでしょう。 .build() return embed } }包括它,以便它可以由命令处理。
override fun onMessageReceived(event: MessageReceivedEvent) { ... if (msgList[0] == "!"){ //接頭詞「!」で始まっているかどうか when(msgList[1]) { ... "Embed" -> { event.channel.sendMessageEmbeds(Embed(event).buildEmbed()).queue() //送信 } } } }语音通道相关
我们将描述与语音通道相关的处理。
首先,授予与语音通道相关的机器人权限。class BotClient : ListenerAdapter() { ... fun main() { val intents = listOf( GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_VOICE_STATES, //追記 GatewayIntent.MESSAGE_CONTENT) val cacheFlags = listOf( CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE) //追記 ... guild.members.forEach { member -> println("VoiceState @${member.effectiveName}: ${member.voiceState?.inAudioChannel()}") } }现在您可以获取成员是否连接到语音通道。
请删除一次添加的部分,并检查您这次是否无法获取。
在这种情况下,不要忘记再次添加它。联系
连接语音通道。就是这样:
guild.audioManager.openAudioConnection(voiceChannel)允许这由命令处理。
override fun onMessageReceived(event: MessageReceivedEvent) { ... if (msgList[0] == "!"){ //接頭詞「!」で始まっているかどうか ... "play" -> { //コマンドが送信されたテキストチャンネルを取得 val textChannel = event.channel.asTextChannel() //コマンドを送信したメンバーが接続しているボイスチャンネルを取得(していない場合はnull) val voiceChannel = guild.getMemberById(event.member!!.id)!!.voiceState!!.channel if(!guild.audioManager.isConnected){ if (voiceChannel == null){ //ボイスチャンネルに接続してるメンバーがいるかどうか textChannel.sendMessage("ボイスチャンネルがみつかりません").queue() return } //ボイスチャンネルに接続 guild.audioManager.openAudioConnection(voiceChannel) } } } } }发送音频
我们会让机器人说话。这很难。
对于音频传输,视频在这里我引用的内容播放的声音是音效实验室我从
- 准备音频文件
- 编辑 Gradle
- 创建处理程序
- 创建监听器
- 创建音乐播放器
- 创建音乐播放器管理器
- 执行命令
准备音频文件
音频文件可以自己准备。
这次音效实验室我下载了“战斗->用剑砍”和“语音素材->哟,非常感谢”。编辑 Gradle
现在让我们谈谈API。
API 是允许应用程序在外部运行的东西。因此,可以通过从外部操作名为 Discord 的应用程序来开发 Bot 应用程序。特别是,JDA(Java Discord API)是允许您使用 Java 操作 Discord 的一种。
实际上,这个 JDA 包被设置为可以在本文的“让 Bot 上线 -> 更新构建工具”中使用。这次还有一个JDA推荐的“播放音频文件”的包,所以我们会使用它。
我们将编辑文件 build.gradle.kts。
将以下内容添加到依赖项中。//音声ファイルの再生ツール implementation("com.sedmelluq:lavaplayer:1.3.76")
在这里,不要忘记按右上角出现的大象标记进行更新。
您现在已完成添加包。创建处理程序
让我们谈谈处理程序和侦听器。
用户执行的操作称为“事件”,响应该事件执行的过程称为“事件处理程序”,检测是否发生事件的处理称为“侦听器”。
例如,
“用户触摸屏幕!”(事件)
“让我们密切关注它!”(听众)
“因为我被感动了,所以我会做〇〇!”(处理程序)从这里开始,我们将编写代码。
创建处理程序。创建一个新文件。
转到“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 MyAudioHandler。即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
在新创建的文件或 BotClient { } 之外编写以下代码。class MyAudioHandler : AudioSendHandler, AudioReceiveHandler { private val queue: Queue<ByteArray> = ConcurrentLinkedQueue() private val audioPlayer: AudioPlayer private var lastFrame: AudioFrame? = null constructor(audioPlayer: AudioPlayer) { this.audioPlayer = audioPlayer } /** 送信処理 */ override fun canProvide(): Boolean { lastFrame = audioPlayer.provide() return lastFrame != null } override fun provide20MsAudio(): ByteBuffer? { return ByteBuffer.wrap(lastFrame!!.data) } override fun isOpus(): Boolean { return true } }这里,我们已经描述了响应事件要执行的处理。
其实我也不是很了解这个过程。很高兴了解这是典型的。创建监听器
创建一个监听器。创建一个新文件。
转到“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 MyAudioListener。即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
在新创建的文件或 BotClient { } 之外编写以下代码。import com.sedmelluq.discord.lavaplayer.player.AudioPlayer import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter import com.sedmelluq.discord.lavaplayer.track.AudioTrack import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason import java.util.concurrent.BlockingQueue import java.util.concurrent.LinkedBlockingQueue class MyAudioListener : AudioEventAdapter { private val player: MyMusicPlayer private val tracks: BlockingQueue<AudioTrack> = LinkedBlockingQueue() constructor(player: MyMusicPlayer) : super() { this.player = player } fun getTracks(): BlockingQueue<AudioTrack> { return tracks } fun getTrackSize(): Int { return tracks.size } fun nextTrack(){ if(tracks.isEmpty()){ // 音楽のトラックが全て再生された時の処理 } player.getAudioPlayer().startTrack(tracks.poll(), false) } //トラック終わった事を検知 override fun onTrackEnd(player: AudioPlayer?, track: AudioTrack?, endReason: AudioTrackEndReason?) { //トラックが終わった時の処理 if (endReason != null) { if (endReason.mayStartNext) nextTrack() println("endReason: " + endReason.name + "\n") } } //トラック始まった事を検知 override fun onTrackStart(player: AudioPlayer?, track: AudioTrack?) { if (track != null) { //トラックが始まった時の処理(トラックの情報を出力) val info = track.info println("[track]@onTrackStart\n title: ${info.title},\n author: ${info.author},\n length: ${info.length},\n identifier: ${info.identifier},\n" + " isStream: ${info.isStream},\n uri: ${info.uri}\nPLAY") } } fun queue(track: AudioTrack) { if (!player.getAudioPlayer().startTrack(track, true)) tracks.offer(track) } }由于这是一个侦听器,因此定义了方法来检测轨道开始和轨道结束事件。
红色字母有错误,但这是因为我们还没有创建音乐播放器。您可以放心地忽略它。创建音乐播放器
创建一个音乐播放器。创建一个新文件。
转到“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 MyMusicPlayer。即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
在新创建的文件或 BotClient { } 之外编写以下代码。import com.sedmelluq.discord.lavaplayer.player.AudioPlayer import com.sedmelluq.discord.lavaplayer.track.AudioTrack import net.dv8tion.jda.api.entities.Guild class MyMusicPlayer { private val audioPlayer: AudioPlayer private var listener: MyAudioListener private val guild: Guild constructor(audioPlayer: AudioPlayer, guild: Guild) { this.audioPlayer = audioPlayer this.guild = guild listener = MyAudioListener(this) audioPlayer.addListener(listener) //リスナーを追加 } fun musicPlayer(){ listener = MyAudioListener(this) audioPlayer.addListener(listener) } fun getAudioPlayer(): AudioPlayer { return audioPlayer } fun getGuild(): Guild { return guild } fun getListener(): MyAudioListener { return listener } fun getAudioHandler(): MyAudioHandler { return MyAudioHandler(audioPlayer) } @Synchronized fun playTrack(track: AudioTrack){ listener.queue(track) } @Synchronized fun skipTrack() { listener.nextTrack() } }重要的部分是添加听众。之后,我添加了任何可能需要的处理。
创建音乐播放器管理器
创建音乐播放器管理器。创建一个新文件。
转到“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 MyMusicPlayerManager。即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
在新创建的文件或 BotClient { } 之外编写以下代码。import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers import com.sedmelluq.discord.lavaplayer.tools.FriendlyException import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist import com.sedmelluq.discord.lavaplayer.track.AudioTrack import net.dv8tion.jda.api.entities.Guild import net.dv8tion.jda.api.entities.TextChannel import java.util.HashMap //プレイヤーマネージャー class MyMusicManager { private val manager = DefaultAudioPlayerManager() private val players: MutableMap<String, MyMusicPlayer> = HashMap() constructor(){ AudioSourceManagers.registerRemoteSources(manager) AudioSourceManagers.registerLocalSource(manager) } @Synchronized fun getPlayer(guild: Guild): MyMusicPlayer? { // プレイヤーマネージャーの作成 if (!players.containsKey(guild.id)) players[guild.id] = MyMusicPlayer(manager.createPlayer(), guild) return players[guild.id] } fun loadTrack(channel: TextChannel, source: String){ val player = getPlayer(channel.guild) if (player != null) { channel.guild.audioManager.sendingHandler = player.getAudioHandler() } manager.loadItemOrdered(player, source, object : AudioLoadResultHandler { /** トラックの再生 */ override fun trackLoaded(track: AudioTrack?) { if (track != null && player != null) { player.playTrack(track) //再生 } } override fun playlistLoaded(playlist: AudioPlaylist?) { val builder = StringBuilder() builder.append("プレイリストの追加 **").append(playlist?.name).append("**\n") for (i in 0..4){ if (playlist != null){ if (playlist.tracks.size >= i) break val track = playlist.tracks[i] builder.append("\n ** -> ** ").append(track.info.title) player?.playTrack(track) } else println("playList@MyMusicManager null") } } override fun noMatches() { channel.sendMessage("トラックが見つかりません").queue() println("トラックが見つかりません") } override fun loadFailed(exception: FriendlyException?) { val em = exception?.message channel.sendMessage("[トラック]$source の再生が出来ません。\n理由:$em") } }) } }在此处获取曲目信息。如果信息处理得当,就可以回放。
您可以使用命令执行此操作。实施指挥
如果您发送“!播放”,机器人将连接到语音通道并播放音频文件。
首先,复制您要播放的音频文件的路径。
并将您之前制作的“播放”内容更新如下。override fun onMessageReceived(event: MessageReceivedEvent) { ... if (msgList[0] == "!"){ //接頭詞「!」で始まっているかどうか when(msgList[1]) { ... "play" -> { //コマンドが送信されたテキストチャンネルを取得 val textChannel = event.channel.asTextChannel() //コマンドを送信したメンバーが接続しているボイスチャンネルを取得(していない場合はnull) val voiceChannel = guild.getMemberById(event.member!!.id)!!.voiceState!!.channel if (!guild.audioManager.isConnected) { if (voiceChannel == null) { //ボイスチャンネルに接続してるメンバーがいるかどうか textChannel.sendMessage("ボイスチャンネルがみつかりません").queue() return } //ボイスチャンネルに接続 guild.audioManager.openAudioConnection(voiceChannel) } //コピーしたパス val track = "C:\\Users\\***\\Music\\Playlists\\「よ、よろしくお願いします」.mp3" MyMusicManager().loadTrack(textChannel, track) //再生 } } } }如果音频文件播放成功。
连续音频播放
用我之前做的过程,如果路径是播放列表,似乎可以连续播放。
但是,就我而言,它并不顺利,所以我将实现自己的流程来连续播放。创建一个新文件。
“项目 -> src -> kotlin(右键单击)-> 新建 -> Kotlin 文件/类”并将其命名为 ContinuousPlay。即使不创建,也可以在 BotClient 的 { } 之外编写如下代码。
在新创建的文件或 BotClient { } 之外编写以下代码。import net.dv8tion.jda.api.entities.TextChannel import kotlin.properties.Delegates var myPlayList = listOf<String>() lateinit var myPlayTextChannel : TextChannel //連続再生処理(observePropertyが変更される度に処理される) var observeProperty: Int by Delegates.observable(-1) { property, oldValue, newValue -> println("[${property.name}] $oldValue -> $newValue (${if (newValue!=-1) newValue+1 else myPlayList.size}文字目を再生)") ContinuousPlay().inObserve(newValue) } class ContinuousPlay{ fun start(){ //myPlaylistに何もないなら再生する(再生中の場合はトラックに追加される) if(observeProperty == -1) observeProperty ++ } /** MyAudioListenerのTrackEndEvent内にこの関数を追記 */ fun inTrackEndEvent(){ //連続再生処理。最後のtrackの場合はobservePropertyに-1を代入する val endPoint = myPlayList.size - 2 when { observeProperty == -1 -> endProcess() observeProperty < endPoint -> observeProperty ++ observeProperty == endPoint -> observeProperty = -1 } } /** observe内にこの関数を追記 */ fun inObserve(newValue:Int){ var number = newValue val isLastTrack: Boolean = newValue == -1 if (isLastTrack) number = myPlayList.lastIndex ////最後のtrackの時は、myPlayList.lastIndexを参照 MyMusicManager().loadTrack(myPlayTextChannel, myPlayList[number]) //再生 if (isLastTrack) myPlayList = listOf() //最後のtrack終了後に、myPlayListをリセット } //myPlayListの終了処理 private fun endProcess(){} }接下来,在 MyAudioListener 的 TrackEndEvent 中添加以下内容。
/** 連続再生処理を追記 */ override fun onTrackEnd(player: AudioPlayer?, track: AudioTrack?, endReason: AudioTrackEndReason?) { if (endReason != null) { if (endReason.mayStartNext) nextTrack() println("endReason: " + endReason.name + "\n") ContinuousPlay().inTrackEndEvent() //追記 } }最后,我们将其实现为命令!
如果发送“!play ai”,则“a”和“i”对应的曲目将连续播放。
请更改播放的处理如下。"play" -> { //コマンドが送信されたテキストチャンネルを取得 val textChannel = event.channel.asTextChannel() //コマンドを送信したメンバーが接続しているボイスチャンネルを取得(していない場合はnull) val voiceChannel = guild.getMemberById(event.member!!.id)!!.voiceState!!.channel if (!guild.audioManager.isConnected) { if (voiceChannel == null) { //ボイスチャンネルに接続してるメンバーがいるかどうか textChannel.sendMessage("ボイスチャンネルがみつかりません").queue() return } //ボイスチャンネルに接続 guild.audioManager.openAudioConnection(voiceChannel) } /* 連続再生 */ val messageData = msgList[2].trim() //文字列の前後の空白を削除(途中の空白)は削除されない val myPlayMap = mapOf( "あ" to "C:\\Users\\***\\Music\\Playlists\\剣で斬る1.mp3", "い" to "C:\\Users\\***\\Music\\Playlists\\「よ、よろしくお願いします」.mp3") //myPlayListの作成(文字でリストを作成) for (i in messageData.indices){ val moji = messageData[i].toString() //i文字目を取得 println("\n[track追加] ${if (observeProperty==-1) i+1 else myPlayList.size+1}文字目:$moji\n") val myTrack: String? = myPlayMap[moji] //文字に紐づけられた音声ファイルでPlaylistを作成(再生中の場合はトラックに追加される) if (myTrack != null) myPlayList = myPlayList + myTrack } if (myPlayList.isEmpty()) return //myPlayListが空なら処理を中断 //連続再生開始 myPlayTextChannel = textChannel ContinuousPlay().start() }如果你发送了“!play ai”,那么“a”和“i”对应的曲目应该已经连续播放了!
此外,如果您在连续播放期间键入命令,则会添加与角色对应的曲目。参考
下载并安装 OpenJDK
IntelliJ IDEA 安装过程<macOS>
Kotlin 3 中的 Discord Bot 入门~嵌入实现~
[JDA] Créer un bot discord : LES BOTS MUSICAUX avec LavaPlayer
音效实验室
Kotlin 动手实践 Tsuyano Shoda [作者]
Android应用开发教材Shinzo Saito [作者]
音频播放包https://github.com/sedmelluq/lavaplayer
JDA 包
https://github.com/DV8FromTheWorld/JDA/releases
https://javadoc.io/doc/net.dv8tion/JDA/latest/index.html附录
截至2022/09/29,附录正在编写中。请稍等。
如果您有任何问题,请随时在 Twitter 上问我。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308628062.html