songbird
This commit is contained in:
parent
f0a95356d4
commit
afab95f45d
|
@ -7,20 +7,21 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
music = ["serenity/voice"]
|
music = ["serenity/voice", "songbird"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serenity = "0.9"
|
serenity = "0.10"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
reqwest = "0.10"
|
reqwest = "0.10"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
tokio = { version = "0.2", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
log4rs = "0.13"
|
log4rs = "0.13"
|
||||||
ctrlc = "3.1"
|
ctrlc = "3.1"
|
||||||
|
songbird = { version = "0.1.0", features = ["driver"], optional = true }
|
|
@ -1,3 +1,4 @@
|
||||||
|
use log::debug;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -5,11 +6,13 @@ use serenity::{
|
||||||
},
|
},
|
||||||
model::prelude::*,
|
model::prelude::*,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
http::CacheHttp,
|
||||||
};
|
};
|
||||||
use log::debug;
|
|
||||||
|
use crate::data::GuildOptionsKey;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(ban, kick)]
|
#[commands(ban, kick, ghost_pings)]
|
||||||
pub struct Admin;
|
pub struct Admin;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
@ -93,5 +96,38 @@ async fn ban(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = ""]
|
||||||
|
#[only_in(guilds)]
|
||||||
|
#[required_permissions("KICK_MEMBERS")]
|
||||||
|
async fn ghost_pings(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
let guilds_options = data
|
||||||
|
.get::<GuildOptionsKey>()
|
||||||
|
.expect("Expected NonKickGuildsContainer in TypeMap.");
|
||||||
|
|
||||||
|
if let Some(guild) = guilds_options.get(&msg.guild_id.unwrap()) {
|
||||||
|
let mut message = String::from("\x60\x60\x60");
|
||||||
|
|
||||||
|
for ping in &guild.last_ghost_pings {
|
||||||
|
let sender = ctx
|
||||||
|
.http()
|
||||||
|
.get_user(ping.sender)
|
||||||
|
.await
|
||||||
|
.map(|u| u.name)
|
||||||
|
.unwrap_or_else(|_| String::from("Unkown"));
|
||||||
|
|
||||||
|
message += &format!("{} : {:?}\n", sender, ping.roles.iter().map(|r| format!("<!@{}>", r)))
|
||||||
|
}
|
||||||
|
message += "\x60\x60\x60";
|
||||||
|
|
||||||
|
crate::api::send_reply(ctx, msg, message).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
use crate::{api, data::ShardManagerContainer};
|
use crate::{api, data::ShardManagerContainer};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
use rand::prelude::IteratorRandom;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
client::bridge::gateway::ShardId,
|
client::bridge::gateway::ShardId,
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
ArgError, Args, CommandResult,
|
ArgError, Args, CommandResult,
|
||||||
},
|
},
|
||||||
|
http::CacheHttp,
|
||||||
model::prelude::*,
|
model::prelude::*,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(error, image, infos, invite, latency, longcode, older, ping)]
|
#[commands(
|
||||||
|
error,
|
||||||
|
image,
|
||||||
|
infos,
|
||||||
|
invite,
|
||||||
|
latency,
|
||||||
|
longcode,
|
||||||
|
older,
|
||||||
|
ping,
|
||||||
|
random_mute
|
||||||
|
)]
|
||||||
pub struct General;
|
pub struct General;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
@ -271,6 +283,34 @@ async fn _image(ctx: &Context, msg: &Message, mut args: Args) -> crate::commands
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[required_permissions(ADMINISTRATOR)]
|
||||||
|
async fn random_mute(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
if let Some(guild) = msg.guild_id {
|
||||||
|
let mut members = guild.members(&ctx, Some(1000), None).await?;
|
||||||
|
|
||||||
|
let mut m: usize = rand::random::<usize>() % members.len();
|
||||||
|
while members[m].permissions(&ctx).await.unwrap().administrator() || members[m].user.bot {
|
||||||
|
m = rand::random::<usize>() % members.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
let guild = ctx.http().get_guild(msg.guild_id.unwrap().0).await?;
|
||||||
|
|
||||||
|
let role = match guild.role_by_name("mute") {
|
||||||
|
Some(role) => Ok(role.id),
|
||||||
|
None => Err(tokio::io::Error::new(
|
||||||
|
tokio::io::ErrorKind::Other,
|
||||||
|
"Unkown role",
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
members[m].add_role(&ctx.http, role).await?;
|
||||||
|
|
||||||
|
api::send_reply(ctx, msg, format!("{} was choosen by the roulette of the mute !", members[m].mention())).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO JSON FILE
|
// TODO JSON FILE
|
||||||
enum Image {
|
enum Image {
|
||||||
HackerMan(),
|
HackerMan(),
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
use crate::data::{GuildOptions, GuildOptionsKey};
|
use crate::data::{GuildOptions, GuildOptionsKey};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
client::{bridge::voice::ClientVoiceManager, Context},
|
client::{Context},
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
Args, CommandResult,
|
Args, CommandResult,
|
||||||
},
|
},
|
||||||
model::{channel::Message, guild::PartialMember, id::GuildId, misc::Mentionable},
|
model::{channel::Message, guild::PartialMember, id::GuildId, misc::Mentionable},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
voice, Result as SerenityResult,
|
Result as SerenityResult,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) struct VoiceManager;
|
|
||||||
|
|
||||||
impl TypeMapKey for VoiceManager {
|
|
||||||
type Value = Arc<Mutex<ClientVoiceManager>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(join, leave, play, stop)]
|
#[commands(join, leave, play, stop)]
|
||||||
|
@ -28,7 +23,7 @@ async fn join(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let guild = match msg.guild(&ctx.cache).await {
|
let guild = match msg.guild(&ctx.cache).await {
|
||||||
Some(guild) => guild,
|
Some(guild) => guild,
|
||||||
None => {
|
None => {
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "DMs not supported").await);
|
msg.channel_id.say(&ctx.http, "DMs not supported").await;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -39,11 +34,9 @@ async fn join(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
} {
|
} {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error, you cant play music")
|
.say(&ctx.http, "Error, you cant play music")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,33 +50,25 @@ async fn join(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let connect_to = match channel_id {
|
let connect_to = match channel_id {
|
||||||
Some(channel) => channel,
|
Some(channel) => channel,
|
||||||
None => {
|
None => {
|
||||||
check_msg(msg.reply(ctx, "Not in a voice channel").await);
|
msg.reply(ctx, "Not in a voice channel").await;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let manager_lock = ctx
|
let manager = songbird::get(ctx).await
|
||||||
.data
|
.expect("Songbird Voice client placed in at initialisation.").clone();
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get::<VoiceManager>()
|
|
||||||
.cloned()
|
|
||||||
.expect("Expected VoiceManager in TypeMap.");
|
|
||||||
let mut manager = manager_lock.lock().await;
|
|
||||||
|
|
||||||
if manager.join(guild_id, connect_to).is_some() {
|
if manager.join(guild_id, connect_to).await.1.is_ok() {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, &format!("Joined {}", connect_to.mention()))
|
.say(&ctx.http, &format!("Joined {}", connect_to.mention()))
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error joining the channel")
|
.say(&ctx.http, "Error joining the channel")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -99,7 +84,7 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
{
|
{
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => {
|
None => {
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "DMs not supported").await);
|
msg.channel_id.say(&ctx.http, "DMs not supported").await;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -110,32 +95,26 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
} {
|
} {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error, you cant play music")
|
.say(&ctx.http, "Error, you cant play music")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let manager_lock = ctx
|
let manager = songbird::get(ctx).await
|
||||||
.data
|
.expect("Songbird Voice client placed in at initialisation.").clone();
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get::<VoiceManager>()
|
|
||||||
.cloned()
|
|
||||||
.expect("Expected VoiceManager in TypeMap.");
|
|
||||||
let mut manager = manager_lock.lock().await;
|
|
||||||
|
|
||||||
if let Some(handler) = manager.get_mut(guild_id) {
|
let has_handler = manager.get(guild_id).is_some();
|
||||||
handler.stop();
|
|
||||||
manager.remove(guild_id);
|
|
||||||
|
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "Left voice channel").await);
|
|
||||||
} else {
|
|
||||||
check_msg(msg.reply(ctx, "Not in a voice channel").await);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if has_handler {
|
||||||
|
if let Err(e) = manager.remove(guild_id).await {
|
||||||
|
msg.channel_id.say(&ctx.http, format!("Failed: {:?}", e)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.channel_id.say(&ctx.http, "Left voice channel").await;
|
||||||
|
} else {
|
||||||
|
msg.reply(ctx, "Not in a voice channel").await;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,22 +124,20 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let url = match args.single::<String>() {
|
let url = match args.single::<String>() {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Must provide a URL to a video or audio")
|
.say(&ctx.http, "Must provide a URL to a video or audio")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !url.starts_with("http") {
|
if !url.starts_with("http") {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Must provide a valid URL")
|
.say(&ctx.http, "Must provide a valid URL")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -168,11 +145,10 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
let guild_id = match ctx.cache.guild_channel(msg.channel_id).await {
|
let guild_id = match ctx.cache.guild_channel(msg.channel_id).await {
|
||||||
Some(channel) => channel.guild_id,
|
Some(channel) => channel.guild_id,
|
||||||
None => {
|
None => {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error finding channel info")
|
.say(&ctx.http, "Error finding channel info")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -183,45 +159,36 @@ async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
} {
|
} {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error, you cant play music")
|
.say(&ctx.http, "Error, you cant play music")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let manager_lock = ctx
|
let manager = songbird::get(ctx).await
|
||||||
.data
|
.expect("Songbird Voice client placed in at initialisation.").clone();
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get::<VoiceManager>()
|
|
||||||
.cloned()
|
|
||||||
.expect("Expected VoiceManager in TypeMap.");
|
|
||||||
let mut manager = manager_lock.lock().await;
|
|
||||||
|
|
||||||
if let Some(handler) = manager.get_mut(guild_id) {
|
if let Some(handler_lock) = manager.get(guild_id) {
|
||||||
let source = match voice::ytdl(&url).await {
|
let mut handler = handler_lock.lock().await;
|
||||||
Ok(source) => source,
|
|
||||||
Err(why) => {
|
let source = match songbird::ytdl(&url).await {
|
||||||
error!("Err starting source: {:?}", why);
|
Ok(source) => source,
|
||||||
|
Err(why) => {
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "Error sourcing ffmpeg").await);
|
error!("Err starting source: {:?}", why);
|
||||||
|
|
||||||
return Ok(());
|
msg.channel_id.say(&ctx.http, "Error sourcing ffmpeg").await;
|
||||||
}
|
|
||||||
};
|
return Ok(());
|
||||||
handler.stop();
|
},
|
||||||
handler.play(source);
|
};
|
||||||
|
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "Playing song").await);
|
handler.play_source(source);
|
||||||
} else {
|
|
||||||
check_msg(
|
msg.channel_id.say(&ctx.http, "Playing song").await;
|
||||||
msg.channel_id
|
} else {
|
||||||
.say(&ctx.http, "Not in a voice channel to play in")
|
msg.channel_id.say(&ctx.http, "Not in a voice channel to play in").await;
|
||||||
.await,
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -232,11 +199,10 @@ async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
let guild_id = match ctx.cache.guild_channel(msg.channel_id).await {
|
let guild_id = match ctx.cache.guild_channel(msg.channel_id).await {
|
||||||
Some(channel) => channel.guild_id,
|
Some(channel) => channel.guild_id,
|
||||||
None => {
|
None => {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error finding channel info")
|
.say(&ctx.http, "Error finding channel info")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -247,51 +213,35 @@ async fn stop(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
} {
|
} {
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.say(&ctx.http, "Error, you cant play music")
|
.say(&ctx.http, "Error, you cant play music")
|
||||||
.await,
|
.await;
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let manager_lock = ctx
|
|
||||||
.data
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get::<VoiceManager>()
|
|
||||||
.cloned()
|
|
||||||
.expect("Expected VoiceManager in TypeMap.");
|
|
||||||
let mut manager = manager_lock.lock().await;
|
|
||||||
|
|
||||||
if let Some(handler) = manager.get_mut(guild_id) {
|
let manager = songbird::get(ctx).await
|
||||||
handler.stop();
|
.expect("Songbird Voice client placed in at initialisation.").clone();
|
||||||
|
if let Some(handler) = manager.get(guild_id) {
|
||||||
|
|
||||||
check_msg(msg.channel_id.say(&ctx.http, "Stopping song").await);
|
let mut handler = handler.lock().await;
|
||||||
} else {
|
handler.stop();
|
||||||
check_msg(
|
|
||||||
msg.channel_id
|
msg.channel_id.say(&ctx.http, "Stopping").await;
|
||||||
.say(&ctx.http, "Not in a voice channel to play in")
|
} else {
|
||||||
.await,
|
msg.reply(ctx, "Not in a voice channel").await;
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that a message successfully sent; if not, then logs why to stderr.
|
|
||||||
fn check_msg(result: SerenityResult<Message>) {
|
|
||||||
if let Err(why) = result {
|
|
||||||
error!("Error sending message: {:?}", why);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_mute(
|
async fn is_mute(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
member: &PartialMember,
|
member: &PartialMember,
|
||||||
guild_id: GuildId,
|
guild_id: GuildId,
|
||||||
) -> tokio::io::Result<bool> {
|
) -> tokio::io::Result<bool> {
|
||||||
|
|
||||||
let mut data = ctx.data.write().await;
|
let mut data = ctx.data.write().await;
|
||||||
let data = data
|
let data = data
|
||||||
.get_mut::<GuildOptionsKey>()
|
.get_mut::<GuildOptionsKey>()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
api, commands,
|
api,
|
||||||
data::{BulletsContainer, GuildOptions, GuildOptionsKey},
|
data::{GuildOptions, GuildOptionsKey},
|
||||||
};
|
};
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
|
@ -16,7 +16,7 @@ use serenity::{
|
||||||
#[group]
|
#[group]
|
||||||
#[default_command(shot)]
|
#[default_command(shot)]
|
||||||
#[prefix("roulette")]
|
#[prefix("roulette")]
|
||||||
#[commands(reload, shot, check, kick, disable_kick)]
|
#[commands(shot, kick, disable_kick)]
|
||||||
struct Roulette;
|
struct Roulette;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
@ -30,83 +30,21 @@ async fn shot(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
} else {
|
} else {
|
||||||
api::send_reply(&ctx, &msg, format!("Click ! Reloading")).await?;
|
api::send_reply(&ctx, &msg, format!("Click ! Reloading")).await?;
|
||||||
}
|
}
|
||||||
} else if let Err(e) = api::send_reply(
|
} else {
|
||||||
ctx,
|
api::send_reply(
|
||||||
msg,
|
ctx,
|
||||||
format!("Error : {} is not a valid argument", args.message()),
|
msg,
|
||||||
)
|
format!("Error : {} is not a valid argument", args.message()),
|
||||||
.await
|
)
|
||||||
{
|
|
||||||
error!("Error : {:?}", e);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[description = "Reload"]
|
|
||||||
#[bucket = "roulette"]
|
|
||||||
async fn reload(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
if let Err(e) = _reload(ctx, msg).await {
|
|
||||||
error!("{}", e);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn _reload(ctx: &Context, msg: &Message) -> commands::Result<()> {
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
let bullets_map = data
|
|
||||||
.get_mut::<BulletsContainer>()
|
|
||||||
.expect("Expected CommandCounter in TypeMap.");
|
|
||||||
bullets_map.insert(msg.author.id.0, (5, rand::thread_rng().gen_range(0, 6)));
|
|
||||||
msg.react(ctx, ReactionType::Unicode(String::from("✅")))
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[description = "If you use that, you are a coward"]
|
|
||||||
async fn check(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
let bullets_map = data
|
|
||||||
.get_mut::<BulletsContainer>()
|
|
||||||
.expect("Expected CommandCounter in TypeMap.");
|
|
||||||
let bullets = bullets_map
|
|
||||||
.entry(msg.author.id.0)
|
|
||||||
.or_insert((5, rand::thread_rng().gen_range(0, 6)));
|
|
||||||
msg.channel_id.say(ctx, format!("Because you are a little shit, you open your barrel and you see that the bullet was at the {} position", bullet_to_str(bullets.1 + 1))).await?;
|
|
||||||
debug!("Bullets Map : {:?}", bullets_map);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bullet_to_str<'m>(nbr: u8) -> &'m str {
|
|
||||||
match nbr {
|
|
||||||
1 => "first",
|
|
||||||
2 => "second",
|
|
||||||
3 => "third",
|
|
||||||
4 => "fourth",
|
|
||||||
5 => "fifth",
|
|
||||||
6 => "sixth",
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[description = "DO IT"]
|
#[description = "DO IT"]
|
||||||
#[only_in(guilds)]
|
#[only_in(guilds)]
|
||||||
async fn kick(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn kick(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
if msg.author.bot {
|
|
||||||
if let Err(e) =
|
|
||||||
api::send_reply(ctx, msg, "Error, this command cannot be used by a bot").await
|
|
||||||
{
|
|
||||||
error!("Error in kick : {:?}", e);
|
|
||||||
}
|
|
||||||
} else if let Err(e) = _kick(ctx, msg).await {
|
|
||||||
error!("Error in kick : {:?}", e);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn _kick(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
if let Some(guild_id) = &msg.guild_id {
|
if let Some(guild_id) = &msg.guild_id {
|
||||||
let mut data = ctx.data.write().await;
|
let mut data = ctx.data.write().await;
|
||||||
let guilds_options = data
|
let guilds_options = data
|
||||||
|
@ -122,15 +60,14 @@ async fn _kick(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let bullets_map = data
|
if rand::thread_rng().gen_range(0, 6) == 0 {
|
||||||
.get_mut::<BulletsContainer>()
|
|
||||||
.expect("Expected CommandCounter in TypeMap.");
|
|
||||||
let bullets = bullets_map
|
|
||||||
.entry(msg.author.id.0)
|
|
||||||
.or_insert((5, rand::thread_rng().gen_range(0, 6)));
|
|
||||||
if bullets.0 == bullets.1 {
|
|
||||||
api::send_reply(ctx, &msg, "💥").await?;
|
api::send_reply(ctx, &msg, "💥").await?;
|
||||||
*bullets = (5, rand::thread_rng().gen_range(0, 6));
|
|
||||||
|
msg.author
|
||||||
|
.create_dm_channel(&ctx)
|
||||||
|
.await?
|
||||||
|
.send_message(&ctx.http, |m| m.content("<:cheh:780736245675982909>"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
guild_id
|
guild_id
|
||||||
.member(&ctx.http, &msg.author)
|
.member(&ctx.http, &msg.author)
|
||||||
|
@ -138,15 +75,8 @@ async fn _kick(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
.kick_with_reason(&ctx.http, "You loose at the roulette")
|
.kick_with_reason(&ctx.http, "You loose at the roulette")
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
*bullets = (bullets.0 - 1, bullets.1);
|
api::send_reply(&ctx, &msg, format!("Click ! Reloading")).await?;
|
||||||
api::send_reply(
|
|
||||||
&ctx,
|
|
||||||
&msg,
|
|
||||||
format!("Click ! bullets remaining : {}", bullets.0 + 1),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
debug!("Bullets Map : {:?}", bullets_map);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use log::error;
|
use log::error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
model::prelude::{GuildId, RoleId},
|
model::{
|
||||||
|
channel::Message,
|
||||||
|
prelude::{GuildId, RoleId, UserId},
|
||||||
|
},
|
||||||
prelude::TypeMapKey,
|
prelude::TypeMapKey,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -22,6 +25,8 @@ pub(crate) struct GuildOptions {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub(crate) mute_id: Option<RoleId>,
|
pub(crate) mute_id: Option<RoleId>,
|
||||||
pub(crate) roulette_options: RouletteOptions,
|
pub(crate) roulette_options: RouletteOptions,
|
||||||
|
pub(crate) last_ghost_pings: Vec<GhostPing>,
|
||||||
|
pub(crate) mutes : Vec<(UserId, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuildOptions {
|
impl GuildOptions {
|
||||||
|
@ -118,6 +123,29 @@ impl GuildOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert_or_ignore(&mut self, message: Message) {
|
||||||
|
let time = message.timestamp.timestamp_millis();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.last_ghost_pings.len() {
|
||||||
|
if time > self.last_ghost_pings[i].time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("u {}", i);
|
||||||
|
|
||||||
|
if i != 15 {
|
||||||
|
log::debug!("u {}", i);
|
||||||
|
self.last_ghost_pings.insert(i, message.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.last_ghost_pings.len() == 16 {
|
||||||
|
self.last_ghost_pings.remove(15);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GuildOptions {
|
impl Default for GuildOptions {
|
||||||
|
@ -126,6 +154,8 @@ impl Default for GuildOptions {
|
||||||
roulette_options: RouletteOptions::default(),
|
roulette_options: RouletteOptions::default(),
|
||||||
guild_id: None,
|
guild_id: None,
|
||||||
mute_id: None,
|
mute_id: None,
|
||||||
|
last_ghost_pings: Vec::new(),
|
||||||
|
mutes: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,3 +187,22 @@ pub(crate) struct GuildOptionsKey;
|
||||||
impl TypeMapKey for GuildOptionsKey {
|
impl TypeMapKey for GuildOptionsKey {
|
||||||
type Value = HashMap<GuildId, GuildOptions>;
|
type Value = HashMap<GuildId, GuildOptions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub(crate) struct GhostPing {
|
||||||
|
pub(crate) sender: u64,
|
||||||
|
pub(crate) channel: u64,
|
||||||
|
pub(crate) roles: Vec<RoleId>,
|
||||||
|
pub(crate) time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Message> for GhostPing {
|
||||||
|
fn from(message: Message) -> Self {
|
||||||
|
Self {
|
||||||
|
sender: *message.author.id.as_u64(),
|
||||||
|
channel: *message.channel_id.as_u64(),
|
||||||
|
roles: message.mention_roles,
|
||||||
|
time: message.timestamp.timestamp_millis(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
61
src/main.rs
61
src/main.rs
|
@ -14,7 +14,7 @@ use serenity::{
|
||||||
Args, CommandError, CommandGroup, CommandResult, DispatchError, HelpOptions,
|
Args, CommandError, CommandGroup, CommandResult, DispatchError, HelpOptions,
|
||||||
StandardFramework,
|
StandardFramework,
|
||||||
},
|
},
|
||||||
http::Http,
|
http::{CacheHttp, Http},
|
||||||
model::prelude::*,
|
model::prelude::*,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
@ -27,9 +27,10 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
use songbird::SerenityInit;
|
||||||
|
|
||||||
#[cfg(feature = "music")]
|
#[cfg(feature = "music")]
|
||||||
use crate::commands::music::{VoiceManager, MUSIC_GROUP};
|
use crate::commands::music::{MUSIC_GROUP};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod commands;
|
mod commands;
|
||||||
|
@ -131,6 +132,7 @@ async fn main() -> IoResult<()> {
|
||||||
let mut client = Client::builder(&token)
|
let mut client = Client::builder(&token)
|
||||||
.event_handler(Messages {})
|
.event_handler(Messages {})
|
||||||
.framework(framework)
|
.framework(framework)
|
||||||
|
.register_songbird()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -138,10 +140,10 @@ async fn main() -> IoResult<()> {
|
||||||
let mut data = client.data.write().await;
|
let mut data = client.data.write().await;
|
||||||
data.insert::<BulletsContainer>(HashMap::default());
|
data.insert::<BulletsContainer>(HashMap::default());
|
||||||
data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
|
data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
|
||||||
#[cfg(feature = "music")]
|
/*#[cfg(feature = "music")]
|
||||||
{
|
{
|
||||||
data.insert::<VoiceManager>(std::sync::Arc::clone(&client.voice_manager));
|
data.insert::<VoiceManager>(std::sync::Arc::clone(&client.voice_manager.unwrap()));
|
||||||
}
|
}*/
|
||||||
|
|
||||||
data.insert::<GuildOptionsKey>({
|
data.insert::<GuildOptionsKey>({
|
||||||
let options = GuildOptions::load_from_dir("./data/guilds_options").unwrap_or_default();
|
let options = GuildOptions::load_from_dir("./data/guilds_options").unwrap_or_default();
|
||||||
|
@ -215,6 +217,10 @@ impl EventHandler for Messages {
|
||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
info!("{} connected to discord", ready.user.name);
|
info!("{} connected to discord", ready.user.name);
|
||||||
|
|
||||||
|
if let Some(cache) = ctx.cache() {
|
||||||
|
cache.set_max_messages(10).await; // TODO CONFIG
|
||||||
|
}
|
||||||
|
|
||||||
ctx.set_presence(Some(Activity::listening("?")), OnlineStatus::Online)
|
ctx.set_presence(Some(Activity::listening("?")), OnlineStatus::Online)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -225,7 +231,7 @@ impl EventHandler for Messages {
|
||||||
ctx_clone
|
ctx_clone
|
||||||
.set_presence(Some(act), OnlineStatus::Online)
|
.set_presence(Some(act), OnlineStatus::Online)
|
||||||
.await;
|
.await;
|
||||||
tokio::time::delay_for(delay).await;
|
tokio::time::sleep(delay).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +248,49 @@ impl EventHandler for Messages {
|
||||||
async fn unknown(&self, _ctx: Context, name: String, raw: Value) {
|
async fn unknown(&self, _ctx: Context, name: String, raw: Value) {
|
||||||
debug!("Unknown event : {}, {:?}", name, raw);
|
debug!("Unknown event : {}, {:?}", name, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn message_delete(
|
||||||
|
&self,
|
||||||
|
ctx: Context,
|
||||||
|
channel_id: ChannelId,
|
||||||
|
deleted_message_id: MessageId,
|
||||||
|
_guild_id: Option<GuildId>
|
||||||
|
) {
|
||||||
|
let message = ctx
|
||||||
|
.cache()
|
||||||
|
.unwrap()
|
||||||
|
.message(channel_id, deleted_message_id)
|
||||||
|
.await;
|
||||||
|
log::debug!("Deletted message : {:?}", message.as_ref().map(|m| &m.content));
|
||||||
|
|
||||||
|
if let Some(message) = message {
|
||||||
|
if let Some(guild_id) = message.guild_id {
|
||||||
|
if !message.mention_roles.is_empty() {
|
||||||
|
let mut data = ctx.data.write().await;
|
||||||
|
let guilds_options = data.get_mut::<GuildOptionsKey>().unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let entry = guilds_options
|
||||||
|
.entry(guild_id)
|
||||||
|
.or_insert_with(|| GuildOptions::default().set_guild_id(guild_id));
|
||||||
|
|
||||||
|
entry.insert_or_ignore(message);
|
||||||
|
|
||||||
|
log::debug!("{:?}", entry.last_ghost_pings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn message_update(
|
||||||
|
&self,
|
||||||
|
_ctx: Context,
|
||||||
|
old_if_available: Option<Message>,
|
||||||
|
new: Option<Message>,
|
||||||
|
_event: MessageUpdateEvent,
|
||||||
|
) {
|
||||||
|
log::debug!("update : {:?} vs {:?}", old_if_available, new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download_to_log(attachment: Attachment) -> commands::Result<()> {
|
async fn download_to_log(attachment: Attachment) -> commands::Result<()> {
|
||||||
|
|
Loading…
Reference in New Issue