Unverified Commit cf3d7abe authored by tec's avatar tec

Refactor

parent 6525c9ff
use serenity;
pub static DISCORD_TOKEN: &str = include_str!("discord_token");
pub static SERVER_ID: u64 = 606351521117896704;
// #general
pub static MAIN_CHANNEL: serenity::model::id::ChannelId =
serenity::model::id::ChannelId(606351521117896706);
// #the-corner
pub static WELCOME_CHANNEL: serenity::model::id::ChannelId =
serenity::model::id::ChannelId(606351613816209418);
// #general
pub static ANNOUNCEMENT_CHANNEL: serenity::model::id::ChannelId =
serenity::model::id::ChannelId(606351521117896706);
pub static BOT_ID: u64 = 607078903969742848;
pub static VOTE_POOL_SIZE: i8 = 2;
pub static VOTE_ROLE: u64 = 607478818038480937;
pub static TIEBREAKER_ROLE: u64 = 607509283483025409;
pub static UNREGISTERED_MEMBER_ROLE: u64 = 608282247350714408;
pub static REGISTERED_MEMBER_ROLE: u64 = 608282133118582815;
pub static FOR_VOTE: &str = "👍";
pub static AGAINST_VOTE: &str = "👎";
pub static ABSTAIN_VOTE: &str = "🙊";
pub static APPROVE_REACT: &str = "⬆";
pub static DISAPPROVE_REACT: &str = "⬇";
pub static UNSURE_REACT: &str = "❔";
pub static ALLOWED_REACTS: &[&'static str] = &[
FOR_VOTE,
AGAINST_VOTE,
ABSTAIN_VOTE,
APPROVE_REACT,
DISAPPROVE_REACT,
UNSURE_REACT,
];
This diff is collapsed.
use rand::Rng;
use serenity::{
model::{channel::Message, guild::Member},
prelude::*,
utils::MessageBuilder,
};
use crate::config;
macro_rules! e {
($error: literal, $x:expr) => {
match $x {
Ok(_) => (),
Err(why) => eprintln!($error, why),
}
};
}
pub fn new_member(ctx: &Context, mut new_member: Member) {
let mut message = MessageBuilder::new();
message.push("Nice to see you here ");
message.mention(&new_member);
message.push_line("! Would you care to introduce yourself?");
message.push_line("If you're not sure where to start, perhaps you could tell us about your projects, your first computer…");
message.push_line("You should also know that we follow the Freenode Channel Guidelines: https://freenode.net/changuide, and try to avoid defamatory content");
if let Err(why) = config::WELCOME_CHANNEL.say(&ctx, message.build()) {
println!("Error sending message: {:?}", why);
}
let mut message = MessageBuilder::new();
message.push(format!("Say hi to {} in ", new_member.display_name()));
message.mention(&config::WELCOME_CHANNEL);
if let Err(why) = config::MAIN_CHANNEL.say(&ctx, message.build()) {
println!("Error sending message: {:?}", why);
}
if let Err(why) = new_member.add_role(&ctx.http, config::UNREGISTERED_MEMBER_ROLE) {
println!("Error adding user role: {:?}", why);
};
}
pub struct Commands;
impl Commands {
pub fn join(ctx: Context, msg: Message, _content: &str) {
e!(
"Unable to get user: {:?}",
serenity::model::id::GuildId(config::SERVER_ID)
.member(ctx.http.clone(), msg.author.id)
.map(|member| new_member(&ctx, member))
);
}
pub fn register(ctx: Context, msg: Message, content: &str) {
let name = content;
if name.len() > 0 {
e!(
"Unable to get member: {:?}",
serenity::model::id::GuildId(config::SERVER_ID)
.member(ctx.http.clone(), msg.author.id)
.map(|mut member| {
e!(
"Unable to remove role: {:?}",
member.remove_role(&ctx.http, config::UNREGISTERED_MEMBER_ROLE)
);
e!(
"Unable to edit nickname: {:?}",
member
.edit(&ctx.http, |m| {
let mut rng = rand::thread_rng();
m.nickname(format!(
"{}, {}",
name,
[
"The Big Cheese",
"The One and Only",
"The Exalted One",
"not to be trusted",
"The Scoundrel",
"A big fish in a small pond",
][rng.gen_range(0, 5)]
));
m
})
.map(|()| {
e!(
"Unable to add role: {:?}",
member.add_role(&ctx.http, config::REGISTERED_MEMBER_ROLE)
);
})
);
})
);
e!("Error deleting register message: {:?}", msg.delete(ctx));
} else {
e!(
"Error sending message: {:?}",
msg.channel_id
.say(&ctx.http, "Usage: !register <ucc username>")
);
}
}
}
use serenity::{
model::{channel, channel::Message},
prelude::*,
utils::MessageBuilder,
};
use crate::config;
macro_rules! e {
($error: literal, $x:expr) => {
match $x {
Ok(_) => (),
Err(why) => eprintln!($error, why),
}
};
}
pub struct Commands;
impl Commands {
pub fn move_something(ctx: Context, msg: Message, content: &str) {
let motion = content;
if motion.len() > 0 {
create_motion(&ctx, &msg, motion);
} else {
e!(
"Error sending message: {:?}",
msg.channel_id.say(
&ctx.http,
"If there's something you want to motion, put it after the !move keyword",
)
);
}
}
pub fn motion(ctx: Context, msg: Message, _content: &str) {
e!("Error sending message: {:?}",
msg.channel_id.say(
&ctx.http,
"I hope you're not having a motion. You may have wanted to !move something instead."
));
}
pub fn poll(ctx: Context, msg: Message, content: &str) {
let topic = content;
if topic.len() > 0 {
create_poll(&ctx, &msg, topic);
} else {
e!(
"Error sending message: {:?}",
msg.channel_id.say(
&ctx.http,
"If there's something you want to motion, put it after the !move keyword",
)
);
}
}
pub fn cowsay(ctx: Context, msg: Message, content: &str) {
let mut text = content.to_owned();
text.escape_default();
// Guess what buddy! You definitely are passing a string to cowsay
text.insert(0, '\'');
text.insert(text.len(), '\'');
let output = std::process::Command::new("cowsay")
.arg(text)
.output()
// btw, if we can't execute cowsay we crash
.expect("failed to execute cowsay");
let mut message = MessageBuilder::new();
message.push_codeblock(
String::from_utf8(output.stdout).expect("unable to parse stdout to String"),
None,
);
e!(
"Error sending message: {:?}",
msg.channel_id.say(&ctx.http, message.build())
);
}
}
fn create_motion(ctx: &Context, msg: &Message, topic: &str) {
println!("{} created a motion {}", msg.author.name, topic);
match msg.channel_id.send_message(&ctx.http, |m| {
m.embed(|embed| {
embed.author(|a| {
a.name(&msg.author.name);
a.icon_url(
msg.author
.static_avatar_url()
.expect("Expected author to have avatar"),
);
a
});
embed.colour(serenity::utils::Colour::GOLD);
embed.title(format!("Motion to {}", topic));
let mut desc = MessageBuilder::new();
desc.role(config::VOTE_ROLE);
desc.push(" take a look at this motion from ");
desc.mention(&msg.author);
embed.description(desc.build());
embed.field("Status", "Under Consideration", true);
embed.field("Votes", "For: 0\nAgainst: 0\nAbstain: 0", true);
embed.timestamp(msg.timestamp.to_rfc3339());
embed
});
m.reactions(vec![
config::FOR_VOTE,
config::AGAINST_VOTE,
config::ABSTAIN_VOTE,
config::APPROVE_REACT,
config::DISAPPROVE_REACT,
]);
m
}) {
Err(why) => {
println!("Error sending message: {:?}", why);
}
Ok(_) => {
if let Err(why) = msg.delete(ctx) {
println!("Error deleting motion prompt: {:?}", why);
}
}
}
}
fn create_poll(ctx: &Context, msg: &Message, topic: &str) {
println!("{} created a poll {}", msg.author.name, topic);
match msg.channel_id.send_message(&ctx.http, |m| {
m.embed(|embed| {
embed.author(|a| {
a.name(&msg.author.name);
a.icon_url(
msg.author
.static_avatar_url()
.expect("Expected author to have avatar"),
);
a
});
embed.colour(serenity::utils::Colour::BLUE);
embed.title(format!("Poll {}", topic));
let mut desc = MessageBuilder::new();
desc.mention(&msg.author);
desc.push(" wants to know what you think.");
embed.description(desc.build());
embed.timestamp(msg.timestamp.to_rfc3339());
embed
});
m.reactions(vec![
config::APPROVE_REACT,
config::DISAPPROVE_REACT,
config::UNSURE_REACT,
]);
m
}) {
Err(why) => {
println!("Error sending message: {:?}", why);
}
Ok(_) => {
if let Err(why) = msg.delete(ctx) {
println!("Error deleting motion prompt: {:?}", why);
}
}
}
}
fn update_motion(
ctx: &Context,
msg: &mut Message,
user: &serenity::model::user::User,
change: &str,
reaction: channel::Reaction,
) {
let for_votes = msg
.reaction_users(ctx, config::FOR_VOTE, None, None)
.unwrap()
.len() as isize
- 1;
let against_votes = msg
.reaction_users(ctx, config::AGAINST_VOTE, None, None)
.unwrap()
.len() as isize
- 1;
let abstain_votes = msg
.reaction_users(ctx, config::ABSTAIN_VOTE, None, None)
.unwrap()
.len() as isize
- 1;
let strength_buff = |react: &str| {
msg.reaction_users(ctx, react, None, None)
.unwrap()
.iter()
.filter(
|u| match u.has_role(ctx, config::SERVER_ID, config::TIEBREAKER_ROLE) {
Ok(true) => true,
_ => false,
},
)
.count()
> 0
};
let for_strength = for_votes as f32
+ (if strength_buff(config::FOR_VOTE) {
0.5
} else {
0.0
});
let against_strength = against_votes as f32
+ (if strength_buff(config::AGAINST_VOTE) {
0.5
} else {
0.0
});
let abstain_strength = abstain_votes as f32
+ (if strength_buff(config::ABSTAIN_VOTE) {
0.5
} else {
0.0
});
let old_embed = msg.embeds[0].clone();
let topic = old_embed.clone().title.unwrap();
println!(
" {:10} {:6} {} on {}",
user.name,
change,
reaction.emoji.as_data().as_str(),
topic
);
let update_status = |e: &mut serenity::builder::CreateEmbed,
status: &str,
last_status_full: String,
topic: &str| {
let last_status = last_status_full.lines().next().expect("No previous status");
if last_status == status {
e.field("Status", last_status_full, true);
} else {
e.field(
"Status",
format!("{}\n_was_ {}", status, last_status_full),
true,
);
println!("Motion to {} now {}", topic, status);
//
let mut message = MessageBuilder::new();
message.push_bold(topic);
message.push(" is now ");
message.push_bold(status);
message.push_italic(format!(" (was {})", last_status));
if let Err(why) = config::ANNOUNCEMENT_CHANNEL.say(&ctx.http, message.build()) {
println!("Error sending message: {:?}", why);
};
}
};
if let Err(why) = msg.edit(ctx, |m| {
m.embed(|e| {
e.author(|a| {
let old_author = old_embed.clone().author.expect("Expected author in embed");
a.name(old_author.name);
a.icon_url(
old_author
.icon_url
.expect("Expected embed author to have icon"),
);
a
});
e.title(&topic);
e.description(old_embed.description.unwrap());
let last_status_full = old_embed
.fields
.iter()
.filter(|f| f.name == "Status")
.next()
.expect("No previous status")
.clone()
.value;
if for_strength > (config::VOTE_POOL_SIZE / 2) as f32 {
e.colour(serenity::utils::Colour::TEAL);
update_status(e, "Passed", last_status_full, &topic);
} else if against_strength + abstain_strength > (config::VOTE_POOL_SIZE / 2) as f32 {
e.colour(serenity::utils::Colour::RED);
update_status(e, "Failed", last_status_full, &topic);
} else {
e.colour(serenity::utils::Colour::GOLD);
update_status(e, "Under Consideration", last_status_full, &topic);
}
e.field(
format!(
"Votes ({}/{})",
for_votes + against_votes + abstain_votes,
config::VOTE_POOL_SIZE
),
format!(
"For: {}\nAgainst: {}\nAbstain: {}",
for_votes, against_votes, abstain_votes
),
true,
);
e.timestamp(
old_embed
.timestamp
.expect("Expected embed to have timestamp"),
);
e
})
}) {
println!("Error updating motion: {:?}", why);
}
}
pub fn reaction_add(ctx: Context, add_reaction: channel::Reaction) {
match add_reaction.message(&ctx.http) {
Ok(mut message) => {
if message.author.id.0 == config::BOT_ID {
if let Ok(user) = add_reaction.user(&ctx) {
match user.has_role(&ctx, config::SERVER_ID, config::VOTE_ROLE) {
Ok(true) => {
for react in
[config::FOR_VOTE, config::AGAINST_VOTE, config::ABSTAIN_VOTE]
.iter()
.filter(|r| r != &&add_reaction.emoji.as_data().as_str())
{
for a_user in
message.reaction_users(&ctx, *react, None, None).unwrap()
{
if a_user.id.0 == user.id.0 {
if let Err(why) = add_reaction.delete(&ctx) {
println!("Error deleting react: {:?}", why);
};
}
}
}
if !config::ALLOWED_REACTS
.contains(&add_reaction.emoji.as_data().as_str())
{
if let Err(why) = add_reaction.delete(&ctx) {
println!("Error deleting react: {:?}", why);
};
}
if user.id.0 != config::BOT_ID {
update_motion(&ctx, &mut message, &user, "add", add_reaction);
}
}
Ok(false) => {
if user.id.0 != config::BOT_ID {
if ![config::APPROVE_REACT, config::DISAPPROVE_REACT]
.contains(&add_reaction.emoji.as_data().as_str())
{
if let Err(why) = add_reaction.delete(&ctx) {
println!("Error deleting react: {:?}", why);
};
}
}
}
Err(why) => {
println!("Error getting user role: {:?}", why);
}
}
}
}
}
Err(why) => {
println!("Error processing react: {:?}", why);
}
}
}
pub fn reaction_remove(ctx: Context, removed_reaction: channel::Reaction) {
match removed_reaction.message(&ctx.http) {
Ok(mut message) => {
if message.author.id.0 == config::BOT_ID {
if let Ok(user) = removed_reaction.user(&ctx) {
update_motion(&ctx, &mut message, &user, "remove", removed_reaction);
}
}
}
Err(why) => {
println!("Error getting user role: {:?}", why);
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment