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,24 +1,16 @@
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 {}
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> {
#[command]
fn ip(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
let args = ["ifconfig.me"];
let out = match Command::new("curl")
@ -40,5 +32,4 @@ impl serenity::framework::standard::Command for IP {
};
Ok(())
}
}

View file

@ -1,27 +1,13 @@
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 }
}
}
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> {
#[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",
@ -32,9 +18,16 @@ impl serenity::framework::standard::Command for List {
let mut output = String::new();
{
let playlist_mutex = self.media.playlist_mut()?;
let playlist = playlist_mutex.borrow();
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 playlist = media.playlist();
output += &format!(
"{} {} queued\n",
@ -52,10 +45,8 @@ impl serenity::framework::standard::Command for List {
break;
}
}
}
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,28 +1,15 @@
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 }
}
}
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> {
#[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",
@ -31,15 +18,16 @@ impl serenity::framework::standard::Command for Pause {
return Ok(());
}
let song_lock = self.media.song_mut()?;
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();
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(());
}
};
media.song().pause();
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,16 +10,58 @@ 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,
};
impl Play {
pub fn new(media_data: Arc<MediaData>) -> Play {
Play { media: media_data }
#[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(());
}
fn check_for_continue(song_lock: MutexGuard<RefCell<Option<LockedAudio>>>) -> bool {
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(())
}
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();
@ -33,12 +72,12 @@ impl Play {
}
None => false,
}
}
}
fn check_join_channel(
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 => {
@ -68,14 +107,14 @@ impl Play {
let mut manager = manager_lock.lock();
manager.join(guild_id, connect_to);
}
}
fn append_songs(
&self,
fn append_songs(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut source: Vec<Song>,
) -> Result<(), String> {
) -> Result<(), String> {
{
let playlist_mutex = self.media.playlist_mut()?;
playlist_mutex.borrow_mut().append(&mut source);
@ -106,14 +145,14 @@ impl Play {
}
Ok(())
}
}
fn handle_http_request(
&self,
fn handle_http_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
url: &String,
) -> Result<(), String> {
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") {
@ -187,13 +226,13 @@ impl Play {
self.append_songs(ctx, msg, source)?;
Ok(())
}
}
fn handle_local_request(
&self,
fn handle_local_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare("SELECT name FROM Vulva3") {
@ -222,14 +261,14 @@ impl Play {
self.append_songs(ctx, msg, songs)?;
Ok(())
}
}
fn handle_song_request(
&self,
fn handle_song_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
pattern: &str,
) -> Result<(), String> {
) -> Result<(), String> {
let sql = self.media.lock_db()?;
let mut stmt = match sql.prepare(&format!(
@ -267,47 +306,4 @@ impl Play {
}
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 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 }
#[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(());
}
}
};
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 = media.song_name();
let sql = media.db();
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]) {
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 !name.is_empty() {
match fs::remove_file(&name) {
Ok(_) => print_error!(msg
.channel_id
.say(format!("Remove current song ({}) from local disk", name))),
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 ({}): {:?}",
name,
song_name,
err.kind()
))),
};
}
Ok(())
}
}

View file

@ -1,28 +1,16 @@
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 }
}
}
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> {
#[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",
@ -45,12 +33,19 @@ impl serenity::framework::standard::Command for Skip {
};
if let Some(handler) = handler(guild_id, &mut manager) {
let playlist_mutex = self.media.playlist_mut()?;
let mut playlist = playlist_mutex.borrow_mut();
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(());
}
};
let song_mutex = self.media.song_mut()?;
let mut song = song_mutex.borrow_mut();
let mut playlist = media.playlist_mut();
let mut song = media.song_mut();
// if current song is the last song in this playlist, just return
if playlist.is_empty() {
print_error!(msg
.channel_id
@ -58,10 +53,13 @@ impl serenity::framework::standard::Command for Skip {
return Ok(());
} else {
// remove the current song from the playlist
let first = playlist.remove(0);
// stop the current song from playing
handler.stop();
// load next song into memory
let source = match ffmpeg(first.name.clone()) {
Ok(mpeg) => mpeg,
Err(_) => {
@ -72,10 +70,11 @@ impl serenity::framework::standard::Command for Skip {
}
};
// 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()));
*song = Some(handler.play_returning_and_callback(source, callback.clone()));
let song_name = self.media.name_mut()?;
*song_name.borrow_mut() = first.name.clone();
@ -85,10 +84,16 @@ impl serenity::framework::standard::Command for Skip {
}
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(())
}
}

View file

@ -1,27 +1,13 @@
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 }
}
}
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> {
#[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",
@ -30,8 +16,16 @@ impl serenity::framework::standard::Command for Stop {
return Ok(());
}
print_error!(self.media.reset(ctx, msg));
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!(data.reset(ctx, msg));
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 {
@ -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;