add explicit join/leave
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -486,7 +486,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "disconic"
|
name = "disconic"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "disconic"
|
name = "disconic"
|
||||||
description = "Discord bot for interacting with subsonic music libraries"
|
description = "Discord bot for interacting with subsonic music libraries"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
authors = [ "Gabriel Fontes <eu@misterio.me>" ]
|
authors = [ "Gabriel Fontes <eu@misterio.me>" ]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
homepage = "https://misterio.me"
|
homepage = "https://misterio.me"
|
||||||
|
|||||||
@@ -7,21 +7,26 @@ use serenity::{
|
|||||||
Args, CommandResult,
|
Args, CommandResult,
|
||||||
},
|
},
|
||||||
model::channel::Message,
|
model::channel::Message,
|
||||||
|
prelude::Mutex,
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
};
|
};
|
||||||
use songbird::input::{Input, Metadata};
|
use songbird::{
|
||||||
use std::sync::Arc;
|
input::{Input, Metadata},
|
||||||
|
Call,
|
||||||
|
};
|
||||||
use sunk::{
|
use sunk::{
|
||||||
search::{self, SearchPage},
|
search::{self, SearchPage},
|
||||||
song::Song,
|
song::Song,
|
||||||
Streamable,
|
Streamable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::MusicClient;
|
use crate::MusicClient;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(
|
#[commands(
|
||||||
song, random, skip, stop, pause, resume, queue, nowplaying, remove, album
|
song, random, skip, stop, pause, resume, queue, nowplaying, remove, album, join, leave
|
||||||
)]
|
)]
|
||||||
pub struct General;
|
pub struct General;
|
||||||
|
|
||||||
@@ -38,6 +43,37 @@ pub async fn after_hook(ctx: &Context, msg: &Message, cmd_name: &str, error: Com
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
||||||
|
let guild_id = guild.id;
|
||||||
|
|
||||||
|
let manager = songbird::get(ctx)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| anyhow!("Couldn't start manager"))?;
|
||||||
|
|
||||||
|
manager.remove(guild_id).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
async fn join(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
||||||
|
let guild_id = guild.id;
|
||||||
|
|
||||||
|
let channel = guild
|
||||||
|
.voice_states
|
||||||
|
.get(&msg.author.id)
|
||||||
|
.and_then(|voice_state| voice_state.channel_id)
|
||||||
|
.ok_or_else(|| anyhow!("You must be in a voice channel to use this command"))?;
|
||||||
|
|
||||||
|
let manager = songbird::get(ctx)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| anyhow!("Couldn't start manager"))?;
|
||||||
|
let _handler = manager.join(guild_id, channel).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[aliases(s, p, play)]
|
#[aliases(s, p, play)]
|
||||||
/// Play a named song
|
/// Play a named song
|
||||||
@@ -58,7 +94,7 @@ async fn song(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
let song = result
|
let song = result
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("No song matching search found"))?;
|
.ok_or_else(|| anyhow!("No song matching search found"))?;
|
||||||
queue_song(ctx, msg, &song, &music_client).await?;
|
queue_song(ctx, msg, song, music_client).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -85,7 +121,7 @@ async fn album(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|||||||
.ok_or_else(|| anyhow!("No albums matching search found"))?;
|
.ok_or_else(|| anyhow!("No albums matching search found"))?;
|
||||||
|
|
||||||
for song in album.songs(music_client).await? {
|
for song in album.songs(music_client).await? {
|
||||||
queue_song(ctx, msg, &song, &music_client).await?;
|
queue_song(ctx, msg, &song, music_client).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -100,12 +136,12 @@ async fn random(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
.get::<MusicClient>()
|
.get::<MusicClient>()
|
||||||
.expect("Couldn't retrieve music client");
|
.expect("Couldn't retrieve music client");
|
||||||
|
|
||||||
let result = Song::random(&music_client, 1).await?;
|
let result = Song::random(music_client, 1).await?;
|
||||||
|
|
||||||
let song = result
|
let song = result
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("No song matching search found"))?;
|
.ok_or_else(|| anyhow!("No song matching search found"))?;
|
||||||
queue_song(ctx, msg, &song, &music_client).await?;
|
queue_song(ctx, msg, song, music_client).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -113,7 +149,7 @@ async fn random(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[command]
|
#[command]
|
||||||
/// Skip current song
|
/// Skip current song
|
||||||
async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let queue = handler.queue();
|
let queue = handler.queue();
|
||||||
@@ -127,7 +163,7 @@ async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[command]
|
#[command]
|
||||||
/// Clear queue and stop playing
|
/// Clear queue and stop playing
|
||||||
async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let queue = handler.queue();
|
let queue = handler.queue();
|
||||||
@@ -141,7 +177,7 @@ async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[command]
|
#[command]
|
||||||
/// Pause playing current song
|
/// Pause playing current song
|
||||||
async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let queue = handler.queue();
|
let queue = handler.queue();
|
||||||
@@ -159,7 +195,7 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[aliases(resume)]
|
#[aliases(resume)]
|
||||||
/// Resume playing current song
|
/// Resume playing current song
|
||||||
async fn resume(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn resume(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let queue = handler.queue();
|
let queue = handler.queue();
|
||||||
@@ -174,7 +210,7 @@ async fn resume(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[aliases(nowplaying, now, np, playing)]
|
#[aliases(nowplaying, now, np, playing)]
|
||||||
/// Show currently playing song
|
/// Show currently playing song
|
||||||
async fn nowplaying(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn nowplaying(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let queue = handler.queue();
|
let queue = handler.queue();
|
||||||
@@ -192,7 +228,7 @@ async fn nowplaying(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[aliases(q)]
|
#[aliases(q)]
|
||||||
/// Show song queue
|
/// Show song queue
|
||||||
async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let current_queue = handler.queue().current_queue();
|
let current_queue = handler.queue().current_queue();
|
||||||
@@ -203,7 +239,7 @@ async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
for (i, track) in current_queue.iter().enumerate() {
|
for (i, track) in current_queue.iter().enumerate() {
|
||||||
text.push_str(&song_message(Some(i), track.metadata()));
|
text.push_str(&song_message(Some(i), track.metadata()));
|
||||||
text.push_str("\n");
|
text.push('\n');
|
||||||
}
|
}
|
||||||
text
|
text
|
||||||
};
|
};
|
||||||
@@ -215,7 +251,7 @@ async fn queue(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
#[command]
|
#[command]
|
||||||
/// Remove song from queue, given id
|
/// Remove song from queue, given id
|
||||||
async fn remove(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
async fn remove(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
let call = get_handler(ctx, msg).await?;
|
||||||
let handler = call.lock().await;
|
let handler = call.lock().await;
|
||||||
|
|
||||||
let index = args.single()?;
|
let index = args.single()?;
|
||||||
@@ -253,7 +289,7 @@ fn song_message(index: Option<usize>, metadata: &Metadata) -> String {
|
|||||||
"{} - {} ({})",
|
"{} - {} ({})",
|
||||||
metadata.artist.to_owned().unwrap_or_default(),
|
metadata.artist.to_owned().unwrap_or_default(),
|
||||||
metadata.track.to_owned().unwrap_or_default(),
|
metadata.track.to_owned().unwrap_or_default(),
|
||||||
duration.to_owned().unwrap_or_default(),
|
duration.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
@@ -268,10 +304,19 @@ async fn queue_song(
|
|||||||
song: &Song,
|
song: &Song,
|
||||||
client: &sunk::Client,
|
client: &sunk::Client,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let input = load_song(song, client).await?;
|
let guild = msg
|
||||||
println!("{:?}", input);
|
.guild(&ctx.cache)
|
||||||
let call = join_channel(&ctx, &msg).await?;
|
.await
|
||||||
|
.ok_or_else(|| anyhow!("Couldn't get guild id"))?;
|
||||||
|
let manager = songbird::get(ctx)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| anyhow!("Couldn't start manager"))?;
|
||||||
|
let call = manager
|
||||||
|
.get(guild.id)
|
||||||
|
.ok_or_else(|| anyhow!("Not currently in a channel"))?;
|
||||||
|
|
||||||
let mut handler = call.lock().await;
|
let mut handler = call.lock().await;
|
||||||
|
let input = load_song(song, client).await?;
|
||||||
handler.enqueue_source(input);
|
handler.enqueue_source(input);
|
||||||
|
|
||||||
let song_info = format!(
|
let song_info = format!(
|
||||||
@@ -295,33 +340,24 @@ async fn queue_song(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn join_channel(
|
async fn get_handler(ctx: &Context, msg: &Message) -> Result<Arc<Mutex<Call>>> {
|
||||||
ctx: &Context,
|
let guild = msg
|
||||||
msg: &Message,
|
.guild(&ctx.cache)
|
||||||
) -> Result<Arc<serenity::prelude::Mutex<songbird::Call>>> {
|
.await
|
||||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
.ok_or_else(|| anyhow!("Couldn't get guild id"))?;
|
||||||
let guild_id = guild.id;
|
let manager = songbird::get(ctx)
|
||||||
|
.await
|
||||||
let caller_channel = guild
|
.ok_or_else(|| anyhow!("Couldn't start manager"))?;
|
||||||
.voice_states
|
Ok(manager
|
||||||
.get(&msg.author.id)
|
.get(guild.id)
|
||||||
.and_then(|voice_state| voice_state.channel_id);
|
.ok_or_else(|| anyhow!("Not currently in a channel"))?)
|
||||||
|
|
||||||
let connect_to = match caller_channel {
|
|
||||||
Some(channel) => channel,
|
|
||||||
None => {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"You must be in a voice channel to use this command"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let manager = songbird::get(ctx).await.unwrap();
|
|
||||||
let handler = manager.join(guild_id, connect_to).await.0;
|
|
||||||
Ok(handler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_song(song: &Song, client: &sunk::Client) -> Result<Input> {
|
async fn load_song(song: &Song, client: &sunk::Client) -> Result<Input> {
|
||||||
let url = song.stream_url(&client)?;
|
let url = song.stream_url(client)?;
|
||||||
Ok(songbird::ffmpeg(url).await?)
|
let mut input = songbird::ffmpeg(&url).await?;
|
||||||
|
input.metadata.track = Some(song.title.clone());
|
||||||
|
input.metadata.artist = song.artist.clone();
|
||||||
|
input.metadata.source_url = Some(url);
|
||||||
|
Ok(input)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user