[WIP] Rewrite with Poise
Still some missing commands
This commit is contained in:
parent
f00c76f2c5
commit
1342253a76
3 changed files with 166 additions and 526 deletions
27
Cargo.toml
27
Cargo.toml
|
@ -1,23 +1,20 @@
|
|||
[package]
|
||||
name = "dj-kitty-cat"
|
||||
version = "0.1.0"
|
||||
authors = ["my name <my@email.address>"]
|
||||
edition = "2018"
|
||||
name = "dj_kitty_cat"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.2"
|
||||
tracing-futures = "0.2"
|
||||
anyhow = "1.0.69"
|
||||
parking_lot = "0.12.1"
|
||||
poise = "0.5.2"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.3.16"
|
||||
tracing-futures = "0.2.5"
|
||||
|
||||
[dependencies.songbird]
|
||||
version = "0.2"
|
||||
features = ["builtin-queue"]
|
||||
|
||||
[dependencies.serenity]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["cache", "client", "gateway", "model", "voice", "rustls_backend", "unstable_discord_api"]
|
||||
version = "0.3.0"
|
||||
features = ["yt-dlp"]
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.0"
|
||||
version = "1.26.0"
|
||||
features = ["macros", "rt-multi-thread", "signal"]
|
||||
|
|
398
src/commands.rs
398
src/commands.rs
|
@ -1,326 +1,110 @@
|
|||
use serenity::client::Context;
|
||||
|
||||
use serenity::model::interactions::application_command::{
|
||||
ApplicationCommandInteraction, ApplicationCommandInteractionDataOptionValue,
|
||||
};
|
||||
use serenity::utils::{EmbedMessageBuilding, MessageBuilder};
|
||||
use anyhow::{Context, Result};
|
||||
use poise::serenity_prelude::{EmbedMessageBuilding, MessageBuilder};
|
||||
use songbird::create_player;
|
||||
use songbird::input::Input;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{CurrentVolume, CurrentlyPlayingTrack};
|
||||
use crate::{CommandContext, Error};
|
||||
|
||||
pub async fn join(ctx: &Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
let guild = guild_id.to_guild_cached(&ctx.cache).await.unwrap();
|
||||
|
||||
let channel_id = guild
|
||||
.voice_states
|
||||
.get(&command.user.id)
|
||||
.and_then(|voice_state| voice_state.channel_id);
|
||||
|
||||
let connect_to = match channel_id {
|
||||
Some(channel) => channel,
|
||||
None => {
|
||||
return "You're not in a voice channel. How do I know where to go?".to_string();
|
||||
}
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn join(ctx: CommandContext<'_>) -> Result<(), Error> {
|
||||
let Some(guild) = ctx.guild() else {
|
||||
ctx.say("You're not in a server, silly.").await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.clone();
|
||||
|
||||
let _handler = manager.join(guild_id, connect_to).await;
|
||||
|
||||
"Joining your channel!".to_string()
|
||||
}
|
||||
|
||||
pub async fn leave(ctx: &Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.clone();
|
||||
let has_handler = manager.get(guild_id).is_some();
|
||||
|
||||
if has_handler {
|
||||
if let Err(e) = manager.remove(guild_id).await {
|
||||
return format!("Failed: {:?}", e);
|
||||
}
|
||||
|
||||
"Goodbye!".to_string()
|
||||
} else {
|
||||
"I can't leave if I'm not there to bein with!".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn play(ctx: &mut Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let options = command
|
||||
.data
|
||||
.options
|
||||
.get(0)
|
||||
.expect("Expected url option")
|
||||
.resolved
|
||||
.as_ref()
|
||||
.expect("Expected url object");
|
||||
|
||||
let url = match options {
|
||||
ApplicationCommandInteractionDataOptionValue::String(url) => url,
|
||||
_ => {
|
||||
return "You didn't tell me what to play.".to_string();
|
||||
}
|
||||
let Some(Some(channel_id)) = guild.voice_states.get(&ctx.author().id).map(|vs| vs.channel_id) else {
|
||||
ctx.say("You're not in a voice channel, silly.").await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if !url.starts_with("http") {
|
||||
return "That's not a real URL. I'm onto you.".to_string();
|
||||
}
|
||||
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
let manager = songbird::get(ctx.serenity_context())
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.context("Expected a songbird manager")?
|
||||
.clone();
|
||||
let _handler = manager.join(guild.id, channel_id).await;
|
||||
|
||||
ctx.say("Joining your channel!").await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn leave(ctx: CommandContext<'_>) -> Result<(), Error> {
|
||||
let Some(guild) = ctx.guild() else {
|
||||
ctx.say("You're not in a server, silly.").await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let manager = songbird::get(ctx.serenity_context())
|
||||
.await
|
||||
.context("Expected a songbird manager")?
|
||||
.clone();
|
||||
|
||||
// Try to join the caller's channel
|
||||
if manager.get(guild_id).is_none() {
|
||||
join(ctx, command).await;
|
||||
if manager.get(guild.id).is_none() {
|
||||
ctx.say("I'm not even in a voice channel!").await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let _handler = manager.remove(guild.id).await;
|
||||
|
||||
ctx.say("Okay bye!").await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn play(
|
||||
ctx: CommandContext<'_>,
|
||||
#[description = "The URL of the song to play"] url: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let Some(url) = url else {
|
||||
ctx.say("You need to give me a URL to play!").await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(guild) = ctx.guild() else {
|
||||
ctx.say("You're not in a server, silly.").await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let manager = songbird::get(ctx.serenity_context())
|
||||
.await
|
||||
.context("Expected a songbird manager")?
|
||||
.clone();
|
||||
|
||||
if manager.get(guild.id).is_none() {
|
||||
let Some(Some(channel_id)) = guild.voice_states.get(&ctx.author().id).map(|vs| vs.channel_id) else {
|
||||
ctx.say("Neither of us are in a voice channel, silly.").await?;
|
||||
return Ok(());
|
||||
};
|
||||
let _handler = manager.join(guild.id, channel_id).await;
|
||||
}
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild.id) {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
|
||||
let source = match songbird::input::Restartable::ytdl(url.clone(), false).await {
|
||||
Ok(source) => source,
|
||||
Err(why) => {
|
||||
println!("Err starting source: {:?}", why);
|
||||
debug!("Trying to play: {}", url);
|
||||
let source = songbird::ytdl(&url).await?;
|
||||
debug!("Playing: {:?}", source.metadata);
|
||||
let title = source
|
||||
.metadata
|
||||
.title
|
||||
.clone()
|
||||
.unwrap_or(String::from("This video"));
|
||||
let msg = MessageBuilder::new()
|
||||
.push("Now playing: ")
|
||||
.push_named_link(title, url)
|
||||
.build();
|
||||
ctx.say(msg).await?;
|
||||
|
||||
return "Something went horribly wrong. Go yell at Valter.".to_string();
|
||||
}
|
||||
};
|
||||
|
||||
let source_input: Input = source.into();
|
||||
|
||||
let message = {
|
||||
if let Some(title) = &source_input.metadata.title {
|
||||
let mut msg = MessageBuilder::new();
|
||||
msg.push_line("Playing this:");
|
||||
msg.push_named_link(title, url);
|
||||
msg.build()
|
||||
} else {
|
||||
"Playing something, I dunno what.".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
let (mut audio, track_handle) = create_player(source_input);
|
||||
|
||||
let mut data = ctx.data.write().await;
|
||||
|
||||
let current_track = data.get_mut::<CurrentlyPlayingTrack>().unwrap();
|
||||
*current_track = Some(track_handle);
|
||||
|
||||
let volume = data.get::<CurrentVolume>().unwrap();
|
||||
|
||||
audio.set_volume(*volume);
|
||||
let (audio, track_handle) = create_player(source);
|
||||
let mut currently_playing = ctx.data().currently_playing.lock();
|
||||
*currently_playing = Some(track_handle);
|
||||
handler.play_only(audio);
|
||||
|
||||
message
|
||||
} else {
|
||||
"Somehow neither of us are in a voice channel to begin with.".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn queue(ctx: &mut Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let options = command
|
||||
.data
|
||||
.options
|
||||
.get(0)
|
||||
.expect("Expected url option")
|
||||
.resolved
|
||||
.as_ref()
|
||||
.expect("Expected url object");
|
||||
|
||||
let url = match options {
|
||||
ApplicationCommandInteractionDataOptionValue::String(url) => url,
|
||||
_ => {
|
||||
return "You didn't tell me what to play.".to_string();
|
||||
}
|
||||
};
|
||||
|
||||
if !url.starts_with("http") {
|
||||
return "That's not a real URL. I'm onto you.".to_string();
|
||||
}
|
||||
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.clone();
|
||||
|
||||
// Try to join the caller's channel
|
||||
if manager.get(guild_id).is_none() {
|
||||
join(ctx, command).await;
|
||||
}
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
|
||||
let source = match songbird::input::Restartable::ytdl(url.clone(), false).await {
|
||||
Ok(source) => source,
|
||||
Err(why) => {
|
||||
println!("Err starting source: {:?}", why);
|
||||
|
||||
return "Something went horribly wrong. Go yell at Valter.".to_string();
|
||||
}
|
||||
};
|
||||
|
||||
let source_input: Input = source.into();
|
||||
|
||||
let message = {
|
||||
if let Some(title) = &source_input.metadata.title {
|
||||
let mut msg = MessageBuilder::new();
|
||||
msg.push_line(format!(
|
||||
"Queueing this up at position {}:",
|
||||
handler.queue().len()
|
||||
));
|
||||
msg.push_named_link(title, url);
|
||||
msg.build()
|
||||
} else {
|
||||
format!(
|
||||
"Queueing something up at position {}, I dunno what.",
|
||||
handler.queue().len()
|
||||
)
|
||||
.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
let (mut audio, track_handle) = create_player(source_input);
|
||||
|
||||
let mut data = ctx.data.write().await;
|
||||
|
||||
let current_track = data.get_mut::<CurrentlyPlayingTrack>().unwrap();
|
||||
*current_track = Some(track_handle);
|
||||
|
||||
let volume = data.get::<CurrentVolume>().unwrap();
|
||||
|
||||
audio.set_volume(*volume);
|
||||
handler.enqueue(audio);
|
||||
|
||||
message
|
||||
} else {
|
||||
"Somehow neither of us are in a voice channel to begin with.".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn skip(ctx: &Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.clone();
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let handler = handler_lock.lock().await;
|
||||
|
||||
let queue = handler.queue();
|
||||
let _ = queue.skip();
|
||||
|
||||
format!("Yeah, I didn't like this one very much anyway. Skip! Now we're at number {} in the queue.", queue.len())
|
||||
} else {
|
||||
"I'm not even in a channel to begin with. Silly.".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stop(ctx: &Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let guild_id = command.guild_id.unwrap();
|
||||
|
||||
let manager = songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialisation.")
|
||||
.clone();
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
|
||||
handler.stop();
|
||||
|
||||
"Alright, I guess I'll stop.".to_string()
|
||||
} else {
|
||||
"I'm not even in a channel to begin with. Silly.".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_volume(ctx: &mut Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let options = command
|
||||
.data
|
||||
.options
|
||||
.get(0)
|
||||
.expect("Expected volume option")
|
||||
.resolved
|
||||
.as_ref()
|
||||
.expect("Expected volume object");
|
||||
|
||||
let volume = match options {
|
||||
ApplicationCommandInteractionDataOptionValue::Number(volume) => *volume,
|
||||
_ => {
|
||||
return "You've gotta give me a volume level to set.".to_string();
|
||||
}
|
||||
};
|
||||
|
||||
if !(0.0..=100.0).contains(&volume) {
|
||||
return "Volume has to be between 0 and 100.".to_string();
|
||||
}
|
||||
|
||||
let mut data = ctx.data.write().await;
|
||||
|
||||
let current_volume = data.get_mut::<CurrentVolume>().unwrap();
|
||||
let new_volume = (volume / 100.0) as f32;
|
||||
*current_volume = new_volume;
|
||||
|
||||
let current_track = data.get::<CurrentlyPlayingTrack>().unwrap();
|
||||
if let Some(track) = current_track {
|
||||
if track.set_volume(new_volume).is_err() {
|
||||
return format!(
|
||||
"Setting volume to {}%, but it didn't work for the current track for some reason.",
|
||||
volume
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
format!("Setting volume to {}%.", volume)
|
||||
}
|
||||
|
||||
pub async fn set_loop(ctx: &mut Context, command: &ApplicationCommandInteraction) -> String {
|
||||
let options = command
|
||||
.data
|
||||
.options
|
||||
.get(0)
|
||||
.expect("Expected loop option")
|
||||
.resolved
|
||||
.as_ref()
|
||||
.expect("Expected loop object");
|
||||
|
||||
let loops = match options {
|
||||
ApplicationCommandInteractionDataOptionValue::Boolean(loops) => *loops,
|
||||
_ => {
|
||||
return "Do you want me to loop the song or not? Be specific.".to_string();
|
||||
}
|
||||
};
|
||||
|
||||
let data = ctx.data.write().await;
|
||||
let current_track = data.get::<CurrentlyPlayingTrack>().unwrap();
|
||||
if let Some(track) = current_track {
|
||||
if loops {
|
||||
track.enable_loop().expect("Couldn't enable looping");
|
||||
"Loopin'!".to_string()
|
||||
} else {
|
||||
track.disable_loop().expect("Couldn't disable looping");
|
||||
"This is the last time this track will EVER be played.".to_string()
|
||||
}
|
||||
} else {
|
||||
"I can't loop a song if there is no song to loop.".to_string()
|
||||
ctx.say("Neither of us are in a voice channel, silly.")
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
267
src/main.rs
267
src/main.rs
|
@ -1,222 +1,81 @@
|
|||
#![warn(clippy::all)]
|
||||
|
||||
use std::env;
|
||||
|
||||
use serenity::{
|
||||
async_trait,
|
||||
model::{
|
||||
gateway::Ready,
|
||||
id::GuildId,
|
||||
interactions::{
|
||||
application_command::{ApplicationCommandOptionType, ApplicationCommandPermissionType},
|
||||
Interaction, InteractionResponseType,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use songbird::{tracks::TrackHandle, SerenityInit};
|
||||
|
||||
mod commands;
|
||||
|
||||
struct CurrentlyPlayingTrack;
|
||||
use commands::*;
|
||||
|
||||
impl TypeMapKey for CurrentlyPlayingTrack {
|
||||
type Value = Option<TrackHandle>;
|
||||
use std::{env, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use parking_lot::Mutex;
|
||||
use poise::serenity_prelude::{self as serenity};
|
||||
use songbird::serenity::SerenityInit;
|
||||
use tracing_subscriber::filter;
|
||||
|
||||
pub struct Data {
|
||||
currently_playing: Arc<Mutex<Option<songbird::tracks::TrackHandle>>>,
|
||||
}
|
||||
pub type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
pub type CommandContext<'a> = poise::Context<'a, Data, Error>;
|
||||
|
||||
async fn event_event_handler(
|
||||
_ctx: &serenity::Context,
|
||||
event: &poise::Event<'_>,
|
||||
_framework: poise::FrameworkContext<'_, Data, Error>,
|
||||
_user_data: &Data,
|
||||
) -> Result<(), Error> {
|
||||
if let poise::Event::Ready { data_about_bot } = event {
|
||||
println!("{} is connected!", data_about_bot.user.name)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct CurrentVolume;
|
||||
/// Registers slash commands in this guild or globally
|
||||
#[poise::command(prefix_command, hide_in_help)]
|
||||
async fn register(ctx: CommandContext<'_>) -> Result<(), Error> {
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
|
||||
impl TypeMapKey for CurrentVolume {
|
||||
type Value = f32;
|
||||
}
|
||||
|
||||
struct Handler;
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn interaction_create(&self, mut ctx: Context, interaction: Interaction) {
|
||||
if let Interaction::ApplicationCommand(command) = interaction {
|
||||
let content = match command.data.name.as_str() {
|
||||
"join" => commands::join(&ctx, &command).await,
|
||||
"leave" => commands::leave(&ctx, &command).await,
|
||||
"play" => commands::play(&mut ctx, &command).await,
|
||||
"queue" => commands::queue(&mut ctx, &command).await,
|
||||
"skip" => commands::skip(&mut ctx, &command).await,
|
||||
"stop" => commands::stop(&ctx, &command).await,
|
||||
"volume" => commands::set_volume(&mut ctx, &command).await,
|
||||
"loop" => commands::set_loop(&mut ctx, &command).await,
|
||||
_ => "not implemented :(".to_string(),
|
||||
};
|
||||
|
||||
if let Err(why) = command
|
||||
.create_interaction_response(&ctx.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| message.content(content))
|
||||
})
|
||||
.await
|
||||
{
|
||||
println!("Cannot respond to slash command: {}", why);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) {
|
||||
for guild in guilds {
|
||||
let commands = guild
|
||||
.set_application_commands(&ctx.http, |commands| {
|
||||
commands
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("join")
|
||||
.description("Join your current channel")
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("leave")
|
||||
.description("Leave the bot's current channel")
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("play")
|
||||
.description("Play a song")
|
||||
.create_option(|option| {
|
||||
option
|
||||
.name("url")
|
||||
.description("The URL of the song to play")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("queue")
|
||||
.description("Queue up a song")
|
||||
.create_option(|option| {
|
||||
option
|
||||
.name("url")
|
||||
.description("The URL of the song to queue")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("skip")
|
||||
.description("Skip the currently playing queued song")
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("stop")
|
||||
.description("Stop any currently playing songs")
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("volume")
|
||||
.description("Set the bot's playback volume")
|
||||
.create_option(|option| {
|
||||
option
|
||||
.name("volume")
|
||||
.description("The volume on a scale from 0 to 100")
|
||||
.kind(ApplicationCommandOptionType::Number)
|
||||
.required(true)
|
||||
})
|
||||
.default_permission(false)
|
||||
})
|
||||
.create_application_command(|command| {
|
||||
command
|
||||
.name("loop")
|
||||
.description(
|
||||
"Enable or disable looping the currently playing track",
|
||||
)
|
||||
.create_option(|option| {
|
||||
option
|
||||
.name("loop")
|
||||
.description("Whether or not the track should loop")
|
||||
.kind(ApplicationCommandOptionType::Boolean)
|
||||
.required(true)
|
||||
})
|
||||
.default_permission(false)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.expect("Couldn't create commands");
|
||||
|
||||
println!("I created the following guild commands: {:#?}", commands);
|
||||
|
||||
let role_id = env::var("ROLE_ID")
|
||||
.expect("Expected a role id in the environment")
|
||||
.parse::<u64>()
|
||||
.expect("Role id is not a valid id");
|
||||
|
||||
let permissions = guild
|
||||
.set_application_commands_permissions(&ctx.http, |permissions| {
|
||||
for command in commands {
|
||||
permissions.create_application_command(|permissions| {
|
||||
permissions
|
||||
.id(command.id.into())
|
||||
.create_permissions(|permission| {
|
||||
permission
|
||||
.id(role_id)
|
||||
.kind(ApplicationCommandPermissionType::Role)
|
||||
.permission(true)
|
||||
})
|
||||
});
|
||||
}
|
||||
permissions
|
||||
})
|
||||
.await
|
||||
.expect("Couldn't set permissions");
|
||||
|
||||
println!("I created the following permissions: {:#?}", permissions);
|
||||
}
|
||||
}
|
||||
|
||||
async fn ready(&self, _ctx: Context, ready: Ready) {
|
||||
println!("{} is connected!", ready.user.name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(filter::LevelFilter::DEBUG)
|
||||
.init();
|
||||
|
||||
let token =
|
||||
env::var("DISCORD_TOKEN").expect("Expected a bot token in the environment: DISCORD_TOKEN");
|
||||
|
||||
let application_id: u64 = env::var("APPLICATION_ID")
|
||||
.expect("Expected an application id in the environment: APPLICATION_ID")
|
||||
.parse()
|
||||
.expect("application id is not a valid id");
|
||||
let options = poise::FrameworkOptions {
|
||||
commands: vec![register(), join(), leave(), play()],
|
||||
event_handler: |ctx, event, framework, user_data| {
|
||||
Box::pin(event_event_handler(ctx, event, framework, user_data))
|
||||
},
|
||||
prefix_options: poise::PrefixFrameworkOptions {
|
||||
prefix: Some(String::from("~")),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
env::var("ROLE_ID")
|
||||
.expect("Expected a role id in the environment: ROLE_ID")
|
||||
.parse::<u64>()
|
||||
.expect("Role id is not a valid id");
|
||||
let intents = serenity::GatewayIntents::non_privileged();
|
||||
|
||||
env::var("CHANNEL_ID")
|
||||
.expect("Expected a channel id in the environment: CHANNEL_ID")
|
||||
.parse::<u64>()
|
||||
.expect("Channel id is not a valid id");
|
||||
poise::Framework::builder()
|
||||
.token(token)
|
||||
.options(options)
|
||||
.intents(intents)
|
||||
.setup(|_ctx, _data, _framework| {
|
||||
Box::pin(async move {
|
||||
Ok(Data {
|
||||
currently_playing: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
})
|
||||
})
|
||||
.client_settings(|client_builder| client_builder.register_songbird())
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
let mut client = Client::builder(token)
|
||||
.event_handler(Handler)
|
||||
.application_id(application_id)
|
||||
.register_songbird()
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
{
|
||||
let mut data = client.data.write().await;
|
||||
data.insert::<CurrentlyPlayingTrack>(None);
|
||||
data.insert::<CurrentVolume>(1.0);
|
||||
}
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue