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 player;
use serenity::client::Client;
use serenity::framework::StandardFramework;
use serenity::{
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 confighandler::*;
@ -32,6 +48,12 @@ impl Default for Config {
}
}
group!({
name: "general",
options: {},
commands: [ip]
});
fn main() {
// read config file
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
// 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));
}
let media_data =
Arc::new(MediaData::new("Penis1").expect("failed to create media data handle"));
/*
client.with_framework(
StandardFramework::new()
.configure(|c| c.prefix(&config.prefix).on_mention(true))
@ -109,4 +133,6 @@ fn main() {
let _ = client
.start()
.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::str::from_utf8;
pub struct IP {}
#[command]
fn ip(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
let args = ["ifconfig.me"];
impl IP {
pub fn new() -> IP {
IP {}
}
}
impl serenity::framework::standard::Command for IP {
#[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 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(())
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(())
}

View file

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

View file

@ -1,45 +1,33 @@
use serenity;
use std::ops::DerefMut;
use std::sync::Arc;
use super::super::prelude::*;
pub struct Pause {
media: Arc<MediaData>,
}
use serenity::prelude::*;
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Pause {
pub fn new(media_data: Arc<MediaData>) -> Pause {
Pause { media: media_data }
#[command]
fn pause(ctx: &mut Context, msg: &Message, _: 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(());
}
}
impl serenity::framework::standard::Command for Pause {
#[allow(unreachable_code, unused_mut)]
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
);
let data = ctx.data.read();
let media = match data.get::<MediaData>() {
Ok(media) => media,
Err(_) => {
msg.channel_id.say("could not find media data");
return Ok(());
}
};
let song_lock = self.media.song_mut()?;
media.song().pause();
if let Some(song) = song_lock.borrow_mut().deref_mut() {
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
audio_lock.pause();
}
Ok(())
}
Ok(())
}

View file

@ -1,8 +1,5 @@
use parking_lot;
use serenity;
use serenity::voice::LockedAudio;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, MutexGuard};
@ -13,301 +10,300 @@ use super::super::prelude::*;
use rusqlite::params;
pub struct Play {
media: Arc<MediaData>,
use serenity::prelude::*;
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 {
pub fn new(media_data: Arc<MediaData>) -> Play {
Play { media: media_data }
fn check_for_continue(song_lock: MutexGuard<RefCell<Option<LockedAudio>>>) -> bool {
match song_lock.borrow_mut().deref_mut() {
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 {
match song_lock.borrow_mut().deref_mut() {
Some(song) => {
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
Self::check_join_channel(&manager_lock, msg);
audio_lock.play();
true
}
None => false,
}
let check_finished = {
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);
}
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"));
Ok(())
}
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;
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);
names.push(name);
}
fn append_songs(
&self,
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 names.len() > 0 {
print_error!(msg.channel_id.say("song already loaded!"));
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
Self::check_join_channel(&manager_lock, msg);
self.append_songs(
ctx,
msg,
names.iter().map(|n| Song { name: n.clone() }).collect(),
)?;
let check_finished = {
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(())
return Ok(());
}
fn handle_http_request(
&self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
url: &String,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let source = match youtube_dl(&url) {
Ok(source) => source,
Err(why) => {
println!("Err starting source: {:?}", why);
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()),
};
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(),
)?;
print_error!(msg
.channel_id
.say(format!("Error using youtube-dl: {}", why)));
return Ok(());
}
};
let source = match youtube_dl(&url) {
Ok(source) => source,
Err(why) => {
println!("Err starting source: {:?}", why);
let mut info = if source.len() == 1 {
"Finished downloading song:".to_string()
} else {
"Finished downloading songs:".to_string()
};
print_error!(msg
.channel_id
.say(format!("Error using youtube-dl: {}", why)));
for song in &source {
info = format!("{}\n\t{}", info, song.name);
return Ok(());
}
};
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)
if sql
.execute(
"INSERT INTO Vulva3 (name, link)
VALUES (?1, ?2)",
params![song.name, url],
)
.is_err()
{
return Err("failed inserting songs into db".to_string());
}
params![song.name, url],
)
.is_err()
{
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(
&self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
let sql = self.media.lock_db()?;
print_error!(msg.channel_id.say(info));
let mut stmt = match sql.prepare("SELECT name FROM Vulva3") {
Ok(statement) => statement,
Err(_) => return Err("failed preparing data base access".to_string()),
self.append_songs(ctx, msg, source)?;
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>) {
Ok(rows) => rows,
Err(_) => return Err("failed querying rows".to_string()),
songs.push(Song { name });
}
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();
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 });
}
songs.push(Song { name });
}
if !songs.is_empty() {
let mut rng = thread_rng();
songs.shuffle(&mut rng);
self.append_songs(ctx, msg, songs)?;
Ok(())
} else {
print_error!(msg
.channel_id
.say(format!("no song found with pattern {}", pattern)));
}
fn handle_song_request(
&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(())
}
Ok(())
}

View file

@ -1,56 +1,50 @@
use serenity;
use std::fs;
use std::sync::Arc;
use super::super::prelude::*;
use rusqlite::params;
pub struct Remove {
media: Arc<MediaData>,
}
use serenity::prelude::*;
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Remove {
pub fn new(media_data: Arc<MediaData>) -> Remove {
Remove { media: media_data }
}
}
impl serenity::framework::standard::Command for Remove {
#[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> {
// (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(),
));
#[command]
fn remove(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
let data = ctx.data.read();
let media = match data.get::<MediaData>() {
Ok(media) => media,
Err(_) => {
msg.channel_id.say("could not find media data");
return Ok(());
}
};
if !name.is_empty() {
match fs::remove_file(&name) {
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()
))),
};
}
let song_name = media.song_name();
let sql = media.db();
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::voice::ffmpeg;
use std::sync::Arc;
use super::super::prelude::*;
pub struct Skip {
media: Arc<MediaData>,
}
use serenity::prelude::*;
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Skip {
pub fn new(media_data: Arc<MediaData>) -> Skip {
Skip { media: media_data }
#[command]
fn skip(ctx: &mut Context, msg: &Message, _: 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(());
}
}
impl serenity::framework::standard::Command for Skip {
#[allow(unreachable_code, unused_mut)]
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 mut manager = manager_lock.lock();
if let Some(mut manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
let mut manager = manager_lock.lock();
let guild_id = match guild_id(msg.channel_id) {
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) {
Some(id) => id,
None => {
println!("error getting guild id");
print_error!(msg.channel_id.say("error getting guild id"));
return Ok(());
}
};
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(());
}
};
if let Some(handler) = handler(guild_id, &mut manager) {
let playlist_mutex = self.media.playlist_mut()?;
let mut playlist = playlist_mutex.borrow_mut();
let mut playlist = media.playlist_mut();
let mut song = media.song_mut();
let song_mutex = self.media.song_mut()?;
let mut song = song_mutex.borrow_mut();
// if current song is the last song in this playlist, just return
if playlist.is_empty() {
print_error!(msg
.channel_id
.say("playlist is empty, no next song available"));
if playlist.is_empty() {
print_error!(msg
.channel_id
.say("playlist is empty, no next song available"));
return Ok(());
} else {
// remove the current song from the playlist
let first = playlist.remove(0);
return Ok(());
} else {
let first = playlist.remove(0);
// stop the current song from playing
handler.stop();
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()) {
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"),
return Ok(());
}
};
// 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::*;
pub struct Stop {
media: Arc<MediaData>,
}
use serenity::prelude::*;
use serenity::{
framework::standard::{macros::command, Args, CommandResult},
model::channel::Message,
};
impl Stop {
pub fn new(media_data: Arc<MediaData>) -> Stop {
Stop { media: media_data }
#[command]
fn stop(ctx: &mut Context, msg: &Message, _: 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(());
}
}
impl serenity::framework::standard::Command for Stop {
#[allow(unreachable_code, unused_mut)]
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
);
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(());
}
};
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::LockedAudio;
use std::cell::RefCell;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::Arc;
use super::prelude::*;
@ -15,12 +14,12 @@ pub struct Song {
}
pub struct MediaData {
playlist: Mutex<RefCell<Vec<Song>>>,
current_song: Mutex<RefCell<Option<LockedAudio>>>,
song_name: Mutex<RefCell<String>>,
pub next_callback: RefCell<Option<Arc<Fn() -> ()>>>,
playlist: Vec<Song>,
current_song: Option<LockedAudio>,
song_name: String,
pub next_callback: Option<Arc<Fn() -> ()>>,
sql_data_base: Mutex<Connection>,
sql_data_base: Connection,
}
impl MediaData {
@ -32,9 +31,9 @@ impl MediaData {
match connection.execute(
"CREATE TABLE IF NOT EXISTS Vulva3 (
name TEXT PRIMARY KEY,
link TEXT NOT NULL
)",
name TEXT PRIMARY KEY,
link TEXT NOT NULL
)",
params![],
) {
Ok(_) => (),
@ -45,38 +44,24 @@ impl MediaData {
};
Ok(MediaData {
playlist: Mutex::new(RefCell::new(Vec::new())),
current_song: Mutex::new(RefCell::new(None)),
song_name: Mutex::new(RefCell::new(String::new())),
next_callback: RefCell::new(None),
playlist: Vec::new(),
current_song: None,
song_name: String::new(),
next_callback: None,
sql_data_base: Mutex::new(connection),
sql_data_base: connection,
})
}
pub fn reset(
&self,
&mut self,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
{
let playlist = self.playlist_mut()?;
playlist.borrow_mut().clear();
}
{
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;
}
self.playlist().clear();
self.song = None;
self.name = String::new();
self.next_callback = None();
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
let mut manager = manager_lock.lock();
@ -103,32 +88,32 @@ impl MediaData {
Ok(())
}
pub fn lock_db(&self) -> Result<MutexGuard<Connection>, String> {
match self.sql_data_base.lock() {
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking db".to_string()),
}
pub fn db(&self) -> &Connection {
&self.sql_data_base
}
pub fn song_mut(&self) -> Result<MutexGuard<RefCell<Option<LockedAudio>>>, String> {
match self.current_song.lock() {
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking current song".to_string()),
}
pub fn song(&self) -> &Option<LockedAudio> {
&self.current_song
}
pub fn playlist_mut(&self) -> Result<MutexGuard<RefCell<Vec<Song>>>, String> {
match self.playlist.lock() {
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking playlist".to_string()),
}
pub fn song_mut(&mut self) -> &mut Option<LockedAudio> {
&mut self.current_song
}
pub fn name_mut(&self) -> Result<MutexGuard<RefCell<String>>, String> {
match self.song_name.lock() {
Ok(sql) => Ok(sql),
Err(_) => Err("failed locking current song name".to_string()),
}
pub fn playlist(&self) -> &Vec<Song> {
&self.playlist
}
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(

View file

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

View file

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

View file

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