Start implementing serenity 0.6

This commit is contained in:
hodasemi 2019-07-12 10:39:03 +02:00
parent da1c9a5d2e
commit 4276428a8b
14 changed files with 557 additions and 628 deletions

View file

@ -11,8 +11,24 @@ mod confighandler;
mod macros; mod macros;
mod player; mod player;
use serenity::client::Client; use serenity::{
use serenity::framework::StandardFramework; framework::standard::{
help_commands,
macros::{check, command, group, help},
Args, CheckResult, CommandGroup, CommandOptions, CommandResult, DispatchError, HelpOptions,
StandardFramework,
},
model::{
channel::{Channel, Message},
gateway::Ready,
id::UserId,
},
utils::{content_safe, ContentSafeOptions},
};
// This imports `typemap`'s `Key` as `TypeMapKey`.
use serenity::prelude::*;
use std::sync::Arc; use std::sync::Arc;
use confighandler::*; use confighandler::*;
@ -32,6 +48,12 @@ impl Default for Config {
} }
} }
group!({
name: "general",
options: {},
commands: [ip]
});
fn main() { fn main() {
// read config file // read config file
let config_file = check_result_return!(read_config("bot.conf")); let config_file = check_result_return!(read_config("bot.conf"));
@ -71,13 +93,15 @@ fn main() {
// voice manager into it. This allows the voice manager to be accessible by // voice manager into it. This allows the voice manager to be accessible by
// event handlers and framework commands. // event handlers and framework commands.
{ {
let mut data = client.data.lock(); let mut data = client.data.write();
data.insert::<VoiceManager>(Arc::clone(&client.voice_manager)); data.insert::<VoiceManager>(Arc::clone(&client.voice_manager));
} }
let media_data = let media_data =
Arc::new(MediaData::new("Penis1").expect("failed to create media data handle")); Arc::new(MediaData::new("Penis1").expect("failed to create media data handle"));
/*
client.with_framework( client.with_framework(
StandardFramework::new() StandardFramework::new()
.configure(|c| c.prefix(&config.prefix).on_mention(true)) .configure(|c| c.prefix(&config.prefix).on_mention(true))
@ -109,4 +133,6 @@ fn main() {
let _ = client let _ = client
.start() .start()
.map_err(|why| println!("Client ended: {:?}", why)); .map_err(|why| println!("Client ended: {:?}", why));
*/
} }

View file

@ -1,37 +0,0 @@
use serenity;
pub struct Help {
prefix: String,
commands: Vec<String>,
}
impl Help {
pub fn new(prefix: &String, commands: Vec<String>) -> Help {
Help {
prefix: prefix.clone(),
commands: commands,
}
}
}
impl serenity::framework::standard::Command for Help {
#[allow(unreachable_code, unused_mut)]
fn execute(
&self,
_: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
_: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
let mut output = String::new();
output += "Available commands:\n";
for command in &self.commands {
output += &format!("\t{}{}\n", self.prefix, command);
}
print_error!(msg.channel_id.say(output));
Ok(())
}
}

View file

@ -1,44 +1,35 @@
use serenity; use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
// This imports `typemap`'s `Key` as `TypeMapKey`.
use serenity::prelude::*;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::str::from_utf8; use std::str::from_utf8;
pub struct IP {} #[command]
fn ip(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
let args = ["ifconfig.me"];
impl IP { let out = match Command::new("curl")
pub fn new() -> IP { .args(&args)
IP {} .stdin(Stdio::null())
} .output()
} {
Ok(out) => out,
impl serenity::framework::standard::Command for IP { Err(_) => return Ok(()),
#[allow(unreachable_code, unused_mut)] };
fn execute(
&self, if !out.status.success() {
_: &mut serenity::client::Context, return Ok(());
msg: &serenity::model::channel::Message,
_: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
let args = ["ifconfig.me"];
let out = match Command::new("curl")
.args(&args)
.stdin(Stdio::null())
.output()
{
Ok(out) => out,
Err(_) => return Ok(()),
};
if !out.status.success() {
return Ok(());
}
match from_utf8(out.stdout.as_slice()) {
Ok(string) => print_error!(msg.author.direct_message(|m| m.content(string))),
Err(_) => print_error!(msg.channel_id.say("error getting IP string")),
};
Ok(())
} }
match from_utf8(out.stdout.as_slice()) {
Ok(string) => print_error!(msg.author.direct_message(|m| m.content(string))),
Err(_) => print_error!(msg.channel_id.say("error getting IP string")),
};
Ok(())
} }

View file

@ -1,61 +1,52 @@
use serenity;
use std::sync::Arc;
use super::super::prelude::*; use super::super::prelude::*;
pub struct List { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::{
} framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl List { #[command]
pub fn new(media_data: Arc<MediaData>) -> List { fn list(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
List { media: media_data } if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
} }
}
impl serenity::framework::standard::Command for List { let mut output = String::new();
#[allow(unreachable_code, unused_mut)]
fn execute( let data = ctx.data.read();
&self, let media = match data.get::<MediaData>() {
ctx: &mut serenity::client::Context, Ok(media) => media,
msg: &serenity::model::channel::Message, Err(_) => {
_: serenity::framework::standard::Args, msg.channel_id.say("could not find media data");
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(()); return Ok(());
} }
};
let mut output = String::new(); let playlist = media.playlist();
{ output += &format!(
let playlist_mutex = self.media.playlist_mut()?; "{} {} queued\n",
let playlist = playlist_mutex.borrow(); playlist.len(),
if playlist.len() == 1 { "song" } else { "songs" }
);
output += &format!( let max_output = 5;
"{} {} queued\n",
playlist.len(),
if playlist.len() == 1 { "song" } else { "songs" }
);
let max_output = 5; for (i, song) in playlist.iter().enumerate() {
if i < max_output {
for (i, song) in playlist.iter().enumerate() { output += &format!("\t{}.\t{}\n", i + 1, song.name.clone());
if i < max_output { } else {
output += &format!("\t{}.\t{}\n", i + 1, song.name.clone()); output += &format!("\t... and {} more", playlist.len() - max_output);
} else { break;
output += &format!("\t... and {} more", playlist.len() - max_output);
break;
}
}
} }
print_error!(msg.channel_id.say(output));
Ok(())
} }
print_error!(msg.channel_id.say(output));
Ok(())
} }

View file

@ -1,4 +1,3 @@
pub mod help;
pub mod ip; pub mod ip;
pub mod list; pub mod list;
pub mod pause; pub mod pause;

View file

@ -1,45 +1,33 @@
use serenity; use serenity;
use std::ops::DerefMut;
use std::sync::Arc;
use super::super::prelude::*; use super::super::prelude::*;
pub struct Pause { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::{
} framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Pause { #[command]
pub fn new(media_data: Arc<MediaData>) -> Pause { fn pause(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
Pause { media: media_data } if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
} }
}
impl serenity::framework::standard::Command for Pause { let data = ctx.data.read();
#[allow(unreachable_code, unused_mut)] let media = match data.get::<MediaData>() {
fn execute( Ok(media) => media,
&self, Err(_) => {
ctx: &mut serenity::client::Context, msg.channel_id.say("could not find media data");
msg: &serenity::model::channel::Message,
_: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(()); return Ok(());
} }
};
let song_lock = self.media.song_mut()?; media.song().pause();
if let Some(song) = song_lock.borrow_mut().deref_mut() { Ok(())
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
audio_lock.pause();
}
Ok(())
}
} }

View file

@ -1,8 +1,5 @@
use parking_lot; use parking_lot;
use serenity;
use serenity::voice::LockedAudio;
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::{Arc, MutexGuard}; use std::sync::{Arc, MutexGuard};
@ -13,301 +10,300 @@ use super::super::prelude::*;
use rusqlite::params; use rusqlite::params;
pub struct Play { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::voice::LockedAudio;
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
#[command]
fn play(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
}
let data = ctx.data.read();
let mut media = match data.get_mut::<MediaData>() {
Ok(media) => media,
Err(_) => {
msg.channel_id.say("could not find media data");
return Ok(());
}
};
if args.len() == 0 {
if !Self::check_for_continue(self.media.song_mut()?) {
print_error!(msg.channel_id.say("Must provide a URL to a video or audio"));
}
} else if args.len() == 1 {
let arg = match args.single::<String>() {
Ok(arg) => arg,
// can't happen, since we tested for length == 1
Err(_) => return Ok(()),
};
if arg == "--local" {
self.handle_local_request(ctx, msg)?;
} else if arg.starts_with("http") {
self.handle_http_request(ctx, msg, &arg)?;
} else {
self.handle_song_request(ctx, msg, &arg)?;
}
} else {
print_error!(msg.channel_id.say("Unsupported argument list"));
}
Ok(())
} }
impl Play { fn check_for_continue(song_lock: MutexGuard<RefCell<Option<LockedAudio>>>) -> bool {
pub fn new(media_data: Arc<MediaData>) -> Play { match song_lock.borrow_mut().deref_mut() {
Play { media: media_data } Some(song) => {
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
audio_lock.play();
true
}
None => false,
}
}
fn check_join_channel(
manager_lock: &Arc<parking_lot::Mutex<serenity::client::bridge::voice::ClientVoiceManager>>,
msg: &serenity::model::channel::Message,
) {
let guild = match msg.guild() {
Some(guild) => guild,
None => {
display_error!(msg.channel_id.say("Groups and DMs not supported"));
return;
}
};
let guild_id = guild.read().id;
let channel_id = guild
.read()
.voice_states
.get(&msg.author.id)
.and_then(|voice_state| voice_state.channel_id);
let connect_to = match channel_id {
Some(channel) => channel,
None => {
display_error!(msg.reply("Not in a voice channel"));
return;
}
};
let mut manager = manager_lock.lock();
manager.join(guild_id, connect_to);
}
fn append_songs(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut source: Vec<Song>,
) -> Result<(), String> {
{
let playlist_mutex = self.media.playlist_mut()?;
playlist_mutex.borrow_mut().append(&mut source);
} }
fn check_for_continue(song_lock: MutexGuard<RefCell<Option<LockedAudio>>>) -> bool { if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
match song_lock.borrow_mut().deref_mut() { Self::check_join_channel(&manager_lock, msg);
Some(song) => {
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
audio_lock.play(); let check_finished = {
true let media_clone = self.media.clone();
} let channel_id = msg.channel_id;
None => false, let manager_lock_clone = manager_lock.clone();
}
Arc::new(move || {
let callback = match media_clone.next_callback.borrow().deref() {
Some(callback) => callback.clone(),
None => {
println!("next_callback not set!");
return;
}
};
MediaData::next_song(&media_clone, channel_id, &manager_lock_clone, callback);
})
};
MediaData::start_playing(&self.media, check_finished, msg.channel_id, &manager_lock);
} }
fn check_join_channel( Ok(())
manager_lock: &Arc<parking_lot::Mutex<serenity::client::bridge::voice::ClientVoiceManager>>, }
msg: &serenity::model::channel::Message,
) {
let guild = match msg.guild() {
Some(guild) => guild,
None => {
display_error!(msg.channel_id.say("Groups and DMs not supported"));
return; fn handle_http_request(
} media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
url: &String,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") {
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()),
};
let rows = match stmt.query_map(&[url], |row| row.get(0) as rusqlite::Result<String>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
};
let mut names = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
}; };
let guild_id = guild.read().id; names.push(name);
let channel_id = guild
.read()
.voice_states
.get(&msg.author.id)
.and_then(|voice_state| voice_state.channel_id);
let connect_to = match channel_id {
Some(channel) => channel,
None => {
display_error!(msg.reply("Not in a voice channel"));
return;
}
};
let mut manager = manager_lock.lock();
manager.join(guild_id, connect_to);
} }
fn append_songs( if names.len() > 0 {
&self, print_error!(msg.channel_id.say("song already loaded!"));
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut source: Vec<Song>,
) -> Result<(), String> {
{
let playlist_mutex = self.media.playlist_mut()?;
playlist_mutex.borrow_mut().append(&mut source);
}
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() { self.append_songs(
Self::check_join_channel(&manager_lock, msg); ctx,
msg,
names.iter().map(|n| Song { name: n.clone() }).collect(),
)?;
let check_finished = { return Ok(());
let media_clone = self.media.clone();
let channel_id = msg.channel_id;
let manager_lock_clone = manager_lock.clone();
Arc::new(move || {
let callback = match media_clone.next_callback.borrow().deref() {
Some(callback) => callback.clone(),
None => {
println!("next_callback not set!");
return;
}
};
MediaData::next_song(&media_clone, channel_id, &manager_lock_clone, callback);
})
};
MediaData::start_playing(&self.media, check_finished, msg.channel_id, &manager_lock);
}
Ok(())
} }
fn handle_http_request( let source = match youtube_dl(&url) {
&self, Ok(source) => source,
ctx: &mut serenity::client::Context, Err(why) => {
msg: &serenity::model::channel::Message, println!("Err starting source: {:?}", why);
url: &String,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") { print_error!(msg
Ok(statement) => statement, .channel_id
Err(_) => return Err("failed preparing data base access".to_string()), .say(format!("Error using youtube-dl: {}", why)));
};
let rows = match stmt.query_map(&[url], |row| row.get(0) as rusqlite::Result<String>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
};
let mut names = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
};
names.push(name);
}
if names.len() > 0 {
print_error!(msg.channel_id.say("song already loaded!"));
self.append_songs(
ctx,
msg,
names.iter().map(|n| Song { name: n.clone() }).collect(),
)?;
return Ok(()); return Ok(());
} }
};
let source = match youtube_dl(&url) { let mut info = if source.len() == 1 {
Ok(source) => source, "Finished downloading song:".to_string()
Err(why) => { } else {
println!("Err starting source: {:?}", why); "Finished downloading songs:".to_string()
};
print_error!(msg for song in &source {
.channel_id info = format!("{}\n\t{}", info, song.name);
.say(format!("Error using youtube-dl: {}", why)));
return Ok(()); if sql
} .execute(
}; "INSERT INTO Vulva3 (name, link)
let mut info = if source.len() == 1 {
"Finished downloading song:".to_string()
} else {
"Finished downloading songs:".to_string()
};
for song in &source {
info = format!("{}\n\t{}", info, song.name);
if sql
.execute(
"INSERT INTO Vulva3 (name, link)
VALUES (?1, ?2)", VALUES (?1, ?2)",
params![song.name, url], params![song.name, url],
) )
.is_err() .is_err()
{ {
return Err("failed inserting songs into db".to_string()); return Err("failed inserting songs into db".to_string());
}
} }
print_error!(msg.channel_id.say(info));
self.append_songs(ctx, msg, source)?;
Ok(())
} }
fn handle_local_request( print_error!(msg.channel_id.say(info));
&self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3") { self.append_songs(ctx, msg, source)?;
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()), Ok(())
}
fn handle_local_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3") {
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()),
};
let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
};
let mut songs = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
}; };
let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) { songs.push(Song { name });
Ok(rows) => rows, }
Err(_) => return Err("failed querying rows".to_string()),
let mut rng = thread_rng();
songs.shuffle(&mut rng);
self.append_songs(ctx, msg, songs)?;
Ok(())
}
fn handle_song_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
pattern: &str,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare(&format!(
"SELECT name FROM Vulva3 WHERE name LIKE '%{}%'",
pattern
)) {
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()),
};
let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
};
let mut songs = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
}; };
let mut songs = Vec::new(); songs.push(Song { name });
for name_result in rows { }
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
};
songs.push(Song { name });
}
if !songs.is_empty() {
let mut rng = thread_rng(); let mut rng = thread_rng();
songs.shuffle(&mut rng); songs.shuffle(&mut rng);
self.append_songs(ctx, msg, songs)?; self.append_songs(ctx, msg, songs)?;
} else {
Ok(()) print_error!(msg
.channel_id
.say(format!("no song found with pattern {}", pattern)));
} }
fn handle_song_request( Ok(())
&self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
pattern: &str,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare(&format!(
"SELECT name FROM Vulva3 WHERE name LIKE '%{}%'",
pattern
)) {
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()),
};
let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
};
let mut songs = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
};
songs.push(Song { name });
}
if !songs.is_empty() {
let mut rng = thread_rng();
songs.shuffle(&mut rng);
self.append_songs(ctx, msg, songs)?;
} else {
print_error!(msg
.channel_id
.say(format!("no song found with pattern {}", pattern)));
}
Ok(())
}
}
impl serenity::framework::standard::Command for Play {
#[allow(unreachable_code, unused_mut)]
fn execute(
&self,
mut ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut args: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
}
if args.len() == 0 {
if !Self::check_for_continue(self.media.song_mut()?) {
print_error!(msg.channel_id.say("Must provide a URL to a video or audio"));
}
} else if args.len() == 1 {
let arg = match args.single::<String>() {
Ok(arg) => arg,
// can't happen, since we tested for length == 1
Err(_) => return Ok(()),
};
if arg == "--local" {
self.handle_local_request(ctx, msg)?;
} else if arg.starts_with("http") {
self.handle_http_request(ctx, msg, &arg)?;
} else {
self.handle_song_request(ctx, msg, &arg)?;
}
} else {
print_error!(msg.channel_id.say("Unsupported argument list"));
}
Ok(())
}
} }

View file

@ -1,56 +1,50 @@
use serenity; use serenity;
use std::fs; use std::fs;
use std::sync::Arc;
use super::super::prelude::*; use super::super::prelude::*;
use rusqlite::params; use rusqlite::params;
pub struct Remove { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::{
} framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Remove { #[command]
pub fn new(media_data: Arc<MediaData>) -> Remove { fn remove(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
Remove { media: media_data } let data = ctx.data.read();
} let media = match data.get::<MediaData>() {
} Ok(media) => media,
Err(_) => {
impl serenity::framework::standard::Command for Remove { msg.channel_id.say("could not find media data");
#[allow(unreachable_code, unused_mut)] return Ok(());
fn execute(
&self,
mut _ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut _args: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
// (1)
let song_name = self.media.name_mut()?;
let name: String = song_name.borrow().clone();
let sql = self.media.lock_db()?;
if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![name]) {
return Err(serenity::framework::standard::CommandError(
"failed executing sql delete".to_string(),
));
} }
};
if !name.is_empty() { let song_name = media.song_name();
match fs::remove_file(&name) { let sql = media.db();
Ok(_) => print_error!(msg
.channel_id
.say(format!("Remove current song ({}) from local disk", name))),
Err(err) => print_error!(msg.channel_id.say(format!(
"Error removing file ({}): {:?}",
name,
err.kind()
))),
};
}
Ok(()) if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![song_name]) {
return Err(serenity::framework::standard::CommandError(
"failed executing sql delete".to_string(),
));
} }
if !song_name.is_empty() {
match fs::remove_file(&song_name) {
Ok(_) => print_error!(msg.channel_id.say(format!(
"Remove current song ({}) from local disk",
song_name
))),
Err(err) => print_error!(msg.channel_id.say(format!(
"Error removing file ({}): {:?}",
song_name,
err.kind()
))),
};
}
Ok(())
} }

View file

@ -1,94 +1,99 @@
use serenity; use serenity;
use serenity::voice::ffmpeg; use serenity::voice::ffmpeg;
use std::sync::Arc;
use super::super::prelude::*; use super::super::prelude::*;
pub struct Skip { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::{
} framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Skip { #[command]
pub fn new(media_data: Arc<MediaData>) -> Skip { fn skip(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
Skip { media: media_data } if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
} }
}
impl serenity::framework::standard::Command for Skip { if let Some(mut manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
#[allow(unreachable_code, unused_mut)] let mut manager = manager_lock.lock();
fn execute(
&self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
_: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
}
if let Some(mut manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() { let guild_id = match guild_id(msg.channel_id) {
let mut manager = manager_lock.lock(); Some(id) => id,
None => {
println!("error getting guild id");
print_error!(msg.channel_id.say("error getting guild id"));
let guild_id = match guild_id(msg.channel_id) { return Ok(());
Some(id) => id, }
None => { };
println!("error getting guild id");
print_error!(msg.channel_id.say("error getting guild id"));
if let Some(handler) = handler(guild_id, &mut manager) {
let data = ctx.data.read();
let mut media = match data.get_mut::<MediaData>() {
Ok(media) => media,
Err(_) => {
msg.channel_id.say("could not find media data");
return Ok(()); return Ok(());
} }
}; };
if let Some(handler) = handler(guild_id, &mut manager) { let mut playlist = media.playlist_mut();
let playlist_mutex = self.media.playlist_mut()?; let mut song = media.song_mut();
let mut playlist = playlist_mutex.borrow_mut();
let song_mutex = self.media.song_mut()?; // if current song is the last song in this playlist, just return
let mut song = song_mutex.borrow_mut(); if playlist.is_empty() {
print_error!(msg
.channel_id
.say("playlist is empty, no next song available"));
if playlist.is_empty() { return Ok(());
print_error!(msg } else {
.channel_id // remove the current song from the playlist
.say("playlist is empty, no next song available")); let first = playlist.remove(0);
return Ok(()); // stop the current song from playing
} else { handler.stop();
let first = playlist.remove(0);
handler.stop(); // load next song into memory
let source = match ffmpeg(first.name.clone()) {
Ok(mpeg) => mpeg,
Err(_) => {
playlist.clear();
*song = None;
let source = match ffmpeg(first.name.clone()) { return Ok(());
Ok(mpeg) => mpeg,
Err(_) => {
playlist.clear();
*song = None;
return Ok(());
}
};
match self.media.next_callback.borrow().as_ref() {
Some(callback) => {
*song =
Some(handler.play_returning_and_callback(source, callback.clone()));
let song_name = self.media.name_mut()?;
*song_name.borrow_mut() = first.name.clone();
print_error!(msg
.channel_id
.say(format!("Skipped current song, now playing: {}", first.name)));
}
None => println!("error getting callback from media"),
} }
};
// start the next song if possible
/*
match self.media.next_callback.borrow().as_ref() {
Some(callback) => {
*song = Some(handler.play_returning_and_callback(source, callback.clone()));
let song_name = self.media.name_mut()?;
*song_name.borrow_mut() = first.name.clone();
print_error!(msg
.channel_id
.say(format!("Skipped current song, now playing: {}", first.name)));
}
None => println!("error getting callback from media"),
} }
*/
*song = Some(handler.play_returning(source));
*media.song_name_mut() = first.name.clone();
print_error!(msg
.channel_id
.say(format!("Skipped current song, now playing: {}", first.name)));
} }
} }
Ok(())
} }
Ok(())
} }

View file

@ -1,37 +1,31 @@
use serenity;
use std::sync::Arc;
use super::super::prelude::*; use super::super::prelude::*;
pub struct Stop { use serenity::prelude::*;
media: Arc<MediaData>, use serenity::{
} framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Stop { #[command]
pub fn new(media_data: Arc<MediaData>) -> Stop { fn stop(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
Stop { media: media_data } if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(());
} }
}
impl serenity::framework::standard::Command for Stop { let data = ctx.data.read();
#[allow(unreachable_code, unused_mut)] let mut media = match data.get_mut::<MediaData>() {
fn execute( Ok(media) => media,
&self, Err(_) => {
ctx: &mut serenity::client::Context, msg.channel_id.say("could not find media data");
msg: &serenity::model::channel::Message,
_: serenity::framework::standard::Args,
) -> ::std::result::Result<(), serenity::framework::standard::CommandError> {
if !channel_contains_author(ctx, msg) {
println!(
"user {} is not in the same voice channel as the bot",
msg.author.name
);
return Ok(()); return Ok(());
} }
};
print_error!(self.media.reset(ctx, msg)); print_error!(data.reset(ctx, msg));
Ok(()) Ok(())
}
} }

View file

@ -3,8 +3,7 @@ use serenity::model::id::ChannelId;
use serenity::voice::ffmpeg; use serenity::voice::ffmpeg;
use serenity::voice::LockedAudio; use serenity::voice::LockedAudio;
use std::cell::RefCell; use std::sync::Arc;
use std::sync::{Arc, Mutex, MutexGuard};
use super::prelude::*; use super::prelude::*;
@ -15,12 +14,12 @@ pub struct Song {
} }
pub struct MediaData { pub struct MediaData {
playlist: Mutex<RefCell<Vec<Song>>>, playlist: Vec<Song>,
current_song: Mutex<RefCell<Option<LockedAudio>>>, current_song: Option<LockedAudio>,
song_name: Mutex<RefCell<String>>, song_name: String,
pub next_callback: RefCell<Option<Arc<Fn() -> ()>>>, pub next_callback: Option<Arc<Fn() -> ()>>,
sql_data_base: Mutex<Connection>, sql_data_base: Connection,
} }
impl MediaData { impl MediaData {
@ -32,9 +31,9 @@ impl MediaData {
match connection.execute( match connection.execute(
"CREATE TABLE IF NOT EXISTS Vulva3 ( "CREATE TABLE IF NOT EXISTS Vulva3 (
name TEXT PRIMARY KEY, name TEXT PRIMARY KEY,
link TEXT NOT NULL link TEXT NOT NULL
)", )",
params![], params![],
) { ) {
Ok(_) => (), Ok(_) => (),
@ -45,38 +44,24 @@ impl MediaData {
}; };
Ok(MediaData { Ok(MediaData {
playlist: Mutex::new(RefCell::new(Vec::new())), playlist: Vec::new(),
current_song: Mutex::new(RefCell::new(None)), current_song: None,
song_name: Mutex::new(RefCell::new(String::new())), song_name: String::new(),
next_callback: RefCell::new(None), next_callback: None,
sql_data_base: Mutex::new(connection), sql_data_base: connection,
}) })
} }
pub fn reset( pub fn reset(
&self, &mut self,
ctx: &mut serenity::client::Context, ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message, msg: &serenity::model::channel::Message,
) -> Result<(), String> { ) -> Result<(), String> {
{ self.playlist().clear();
let playlist = self.playlist_mut()?; self.song = None;
playlist.borrow_mut().clear(); self.name = String::new();
} self.next_callback = None();
{
let song = self.song_mut()?;
*song.borrow_mut() = None;
}
{
let name = self.name_mut()?;
*name.borrow_mut() = String::new();
}
{
*self.next_callback.borrow_mut() = None;
}
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() { if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
let mut manager = manager_lock.lock(); let mut manager = manager_lock.lock();
@ -103,32 +88,32 @@ impl MediaData {
Ok(()) Ok(())
} }
pub fn lock_db(&self) -> Result<MutexGuard<Connection>, String> { pub fn db(&self) -> &Connection {
match self.sql_data_base.lock() { &self.sql_data_base
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking db".to_string()),
}
} }
pub fn song_mut(&self) -> Result<MutexGuard<RefCell<Option<LockedAudio>>>, String> { pub fn song(&self) -> &Option<LockedAudio> {
match self.current_song.lock() { &self.current_song
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking current song".to_string()),
}
} }
pub fn playlist_mut(&self) -> Result<MutexGuard<RefCell<Vec<Song>>>, String> { pub fn song_mut(&mut self) -> &mut Option<LockedAudio> {
match self.playlist.lock() { &mut self.current_song
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking playlist".to_string()),
}
} }
pub fn name_mut(&self) -> Result<MutexGuard<RefCell<String>>, String> { pub fn playlist(&self) -> &Vec<Song> {
match self.song_name.lock() { &self.playlist
Ok(sql) => Ok(sql), }
Err(_) => Err("failed locking current song name".to_string()),
} pub fn playlist_mut(&mut self) -> &mut Vec<Song> {
&mut self.playlist
}
pub fn song_name(&self) -> &String {
&self.name
}
pub fn song_name_mut(&mut self) -> &mut String {
&mut self.name
} }
pub fn start_playing( pub fn start_playing(

View file

@ -1,13 +1,12 @@
use parking_lot; use parking_lot::MutexGuard;
use serenity; use serenity;
use serenity::client::CACHE;
use serenity::model::id::{ChannelId, GuildId}; use serenity::model::id::{ChannelId, GuildId};
use serenity::voice::Handler; use serenity::voice::Handler;
use super::prelude::*; use super::prelude::*;
pub fn guild_id(channel_id: ChannelId) -> Option<GuildId> { pub fn guild_id(channel_id: ChannelId) -> Option<GuildId> {
/*
match CACHE.read().guild_channel(channel_id) { match CACHE.read().guild_channel(channel_id) {
Some(channel) => Some(channel.read().guild_id), Some(channel) => Some(channel.read().guild_id),
None => { None => {
@ -15,14 +14,13 @@ pub fn guild_id(channel_id: ChannelId) -> Option<GuildId> {
None None
} }
} }
*/
None
} }
pub fn handler<'a>( pub fn handler<'a>(
guild_id: GuildId, guild_id: GuildId,
manager: &'a mut parking_lot::MutexGuard< manager: &'a mut MutexGuard<'_, serenity::client::bridge::voice::ClientVoiceManager>,
'_,
serenity::client::bridge::voice::ClientVoiceManager,
>,
) -> Option<&'a mut Handler> { ) -> Option<&'a mut Handler> {
manager.get_mut(guild_id) manager.get_mut(guild_id)
} }
@ -32,7 +30,7 @@ pub fn channel_contains_author(
msg: &serenity::model::channel::Message, msg: &serenity::model::channel::Message,
) -> bool { ) -> bool {
let (guild_id, voice_channel_id_bot) = { let (guild_id, voice_channel_id_bot) = {
match ctx.data.lock().get::<VoiceManager>().cloned() { match ctx.data.read().get::<VoiceManager>().cloned() {
Some(manager_lock) => { Some(manager_lock) => {
let mut manager = manager_lock.lock(); let mut manager = manager_lock.lock();
let guild_id = match guild_id(msg.channel_id) { let guild_id = match guild_id(msg.channel_id) {
@ -68,7 +66,7 @@ pub fn channel_contains_author(
let author = &msg.author; let author = &msg.author;
let guild = match guild_id.to_guild_cached() { let guild = match guild_id.to_guild_cached(&ctx) {
Some(guild) => guild, Some(guild) => guild,
None => { None => {
println!("error getting guild from cache"); println!("error getting guild from cache");

View file

@ -1,13 +1,12 @@
pub use super::mediadata::{MediaData, Song}; pub use super::mediadata::{MediaData, Song};
pub use super::commands::help::Help; pub use super::commands::ip::*;
pub use super::commands::ip::IP; pub use super::commands::list::*;
pub use super::commands::list::List; pub use super::commands::pause::*;
pub use super::commands::pause::Pause; pub use super::commands::play::*;
pub use super::commands::play::Play; pub use super::commands::remove::*;
pub use super::commands::remove::Remove; pub use super::commands::skip::*;
pub use super::commands::skip::Skip; pub use super::commands::stop::*;
pub use super::commands::stop::Stop;
pub use super::eventhandler::Handler; pub use super::eventhandler::Handler;
pub use super::voicemanager::VoiceManager; pub use super::voicemanager::VoiceManager;

View file

@ -1,5 +1,5 @@
use parking_lot::Mutex;
use serenity::client::bridge::voice::ClientVoiceManager; use serenity::client::bridge::voice::ClientVoiceManager;
use serenity::prelude::Mutex;
use std::sync::Arc; use std::sync::Arc;
use typemap::Key; use typemap::Key;