~phate/rsPhate

03649d03cab548b69dc3f25ab2bfa4e6a164bdef — Ash 1 year, 3 months ago 4e9df71
add emacs autosave files to gitignore, remove autosaved main.rs, better logging in about.rs, replace the if statements with a match statement in ww.rs
4 files changed, 65 insertions(+), 280 deletions(-)

M .gitignore
D src/#main.rs#
M src/commands/about.rs
M src/commands/ww.rs
M .gitignore => .gitignore +3 -1
@@ 1,2 1,4 @@
/Cargo.lock
/target
\ No newline at end of file
/target
\#*\#
.\#*
\ No newline at end of file

D src/#main.rs# => src/#main.rs# +0 -228
@@ 1,228 0,0 @@
use log::{error, info};
use serenity::{
    client::bridge::gateway::ShardManager,
    framework::standard::{
        help_commands,
        macros::{check, group, help},
        Args, CheckResult, CommandGroup, CommandOptions, CommandResult,
        DispatchError::CheckFailed,
        HelpOptions, StandardFramework,
    },
    model::{gateway::Ready, id::UserId, prelude::Message},
    prelude::*,
};
use std::{
    collections::{HashMap, HashSet},
    env,
    sync::Arc,
};

// Load and use commands from src/commands/
mod commands;
use commands::{
    about::*, date::*, embed::*, fortune::*, git::*, hmm::*, iv::*, math::*, meme::*, owo::*,
    projects::*, quit::*, rng::*, rr::*, star::*, wipltrn::*, ww::*,
};

// Load and use extra functions from src/functions/
mod functions;
use functions::{prefix_space_check::prefix_space_check, yt_tw_check::yt_tw_check};

// A container type is created for inserting into the Client's `data`, which
// allows for data to be accessible across all events and framework commands, or
// anywhere else that has a copy of the `data` Arc.
struct ShardManagerContainer;

impl TypeMapKey for ShardManagerContainer {
    type Value = Arc<Mutex<ShardManager>>;
}

struct CommandCounter;

impl TypeMapKey for CommandCounter {
    type Value = HashMap<String, u64>;
}

struct Handler;

impl EventHandler for Handler {
    // Set a handler for the `message` event - so that whenever a new message
    // is received - the closure (or function) passed will be called.
    //
    // Event handlers are dispatched through a threadpool, and so multiple
    // events can be dispatched simultaneously.
    //
    // Set a handler to be called on the `ready` event. This is called when a
    // shard is booted, and a READY payload is sent by Discord. This payload
    // contains data like the current user's guild Ids, current user data,
    // private channels, and more.
    //
    // In this case, just print what the current user's username is.
    fn ready(&self, ctx: Context, ready: Ready) {
        use serenity::model::gateway::Activity;
        use serenity::model::user::OnlineStatus;

        let activity = Activity::playing("^help for help");
        let status = OnlineStatus::Online;

        ctx.set_presence(Some(activity), status);
        info!("Connected as {}", ready.user.name);
    }
}

// Groups
#[group]
#[description = "Functions for the bot that do not belong in any specific category."]
#[commands(date, hmm, iv, fortune, meme, rr, wipltrn, ww)]
struct Functions;

#[group]
#[description = "Generalized functions for the bot."]
#[commands(about, projects, quit)]
struct General;

#[group]
#[description = "Functions that are related to number operations."]
#[commands(math, rng)]
struct Numbers;

#[group]
#[description = "Functions that are related to message operations."]
#[commands(embed, git, owo, star)]
struct Messages;

#[help]
#[individual_command_tip = "`^help` | `^help command` | `^help group`\nNOTE: Args are delimited via `,`."]
fn my_help(
    context: &mut Context,
    msg: &Message,
    args: Args,
    help_options: &'static HelpOptions,
    groups: &[&'static CommandGroup],
    owners: HashSet<UserId>,
) -> CommandResult {
    help_commands::with_embeds(context, msg, args, help_options, groups, owners)
}

fn main() {
    // This will load the environment variables located at `./.env`, relative to the CWD.
    kankyo::load().expect("Failed to load .env file");

    // Initialize the logger to use environment variables.
    env_logger::init();

    // Configure the client with your Discord bot token in the environment.
    let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");

    // Create a new instance of the Client, logging in as a bot. This will
    // automatically prepend your bot token with "Bot ", which is a requirement
    // by Discord for bot users.
    let mut client = Client::new(&token, Handler).expect("Err creating client");

    {
        let mut data = client.data.write();
        data.insert::<CommandCounter>(HashMap::default());
        data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
    }

    client.with_framework(
        // Configures the client, allowing for options to mutate how the
        // framework functions.
        //
        // Refer to the documentation for
        // `serenity::ext::framework::Configuration` for all available
        // configurations.
        StandardFramework::new()
            .configure(|c| {
                c.with_whitespace(true)
                    .prefix("^")
                    // Delimiters are: " ", ", ", and ",".
                    .delimiters(vec![","])
            })
            // Set a function to be called prior to each command execution. This
            // provides the context of the command, the message that was received,
            // and the full name of the command that will be called.
            //
            // You can not use this to determine whether a command should be
            // executed. Instead, the `#[check]` macro gives you this functionality.
            .before(|ctx, msg, command_name| {
                info!(
                    "command: '{}', user: '{}'",
                    command_name, msg.author.name
                );

                // Increment the number of times this command has been run once. If
                // the command's name does not exist in the counter, add a default
                // value of 0.
                let mut data = ctx.data.write();
                let counter = data
                    .get_mut::<CommandCounter>()
                    .expect("Expected CommandCounter in ShareMap.");
                let entry = counter.entry(command_name.to_string()).or_insert(0);
                *entry += 1;

                true // if `before` returns false, command processing doesn't happen.
            })
            // Similar to `before`, except will be called directly _after_
            // command execution.
            .after(|_, _, command_name, error| match error {
                Ok(()) => info!("Processed command '{}'", command_name),
                Err(why) => info!("Command '{}' returned error {:?}", command_name, why),
            })
            // Set a function that's called whenever an attempted command-call's
            // command could not be found.
            .unrecognised_command(|ctx, msg, unknown_command_name| {
                prefix_space_check(ctx, msg, unknown_command_name);
            })
            .normal_message(|ctx, msg| {
                yt_tw_check(ctx, msg);
            })
            // Set a function that's called whenever a command's execution didn't complete for one
            // reason or another. For example, when a user has exceeded a rate-limit or a command
            // can only be performed by the bot owner.
            .on_dispatch_error(|ctx, msg, error| match error {
                CheckFailed(Owner, owner_check) => {
                    msg.reply(
                        &ctx.http,
                        "Owner check failed! I will ping you a hundredfold if you do that again! <:sadgry:676458405342216195>",
                    );
                }
                _ => {
                    error!("Unhandled dispatch error!");
                }
            })
            // Set the help function
            .help(&MY_HELP)
            // The `#[group]` macro generates `static` instances of the options set for the group.
            // They're made in the pattern: `#name_GROUP` for the group instance and `#name_GROUP_OPTIONS`.
            // #name is turned all uppercase
            .group(&FUNCTIONS_GROUP)
            .group(&GENERAL_GROUP)
            .group(&NUMBERS_GROUP)
            .group(&MESSAGES_GROUP),
    );

    if let Err(why) = client.start() {
        error!("Client error: {:?}", why);
    }
}

#[check]
#[name = "Owner"]
fn owner_check(_: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> CheckResult {
    // Replace 7 with your ID to make this check pass.
    //
    // `true` will convert into `CheckResult::Success`,
    //
    // `false` will convert into `CheckResult::Failure(Reason::Unknown)`,
    //
    // and if you want to pass a reason alongside failure you can do:
    // `CheckResult::new_user("Lacked admin permission.")`,
    //
    // if you want to mark it as something you want to log only:
    // `CheckResult::new_log("User lacked admin permission.")`,
    //
    // and if the check's failure origin is unknown you can mark it as such (same as using `false.into`):
    // `CheckResult::new_unknown()`
    (msg.author.id == 534379378432540675).into()
}

M src/commands/about.rs => src/commands/about.rs +2 -1
@@ 1,3 1,4 @@
use log::error;
use serenity::{
    framework::standard::{macros::command, CommandError, CommandResult},
    model::channel::Message,


@@ 41,7 42,7 @@ fn about(ctx: &mut Context, msg: &Message) -> CommandResult {
    });

    if let Err(why) = msg {
        println!("Error sending message: {:?}", why);
        error!("Error sending message: {:?}", why);
    }

    Ok(())

M src/commands/ww.rs => src/commands/ww.rs +60 -50
@@ 1,3 1,4 @@
use log::error;
use serenity::{
    framework::standard::{macros::command, Args, CommandResult},
    model::channel::Message,


@@ 13,64 14,73 @@ use serenity::{
fn ww(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
    let arg = args.rest();

	if arg == "apple" {
		let msg = msg.channel_id.send_message(&ctx.http, |m| {
            m.embed(|e| {
                e.title("Why Phate6660 hates Apple:");
                e.fields(vec![
                    ("They are Evil", "They are a despicable, evil, and disgusting company. I find them to be even worse than Google, and probably even Amazon. They've done some truly terrible things. Some examples: mass censorship, worker abuse (manipulation/brainwashing, sweatshops), repairing your own device is not allowed, they fully support DRM, they exploit developers.", false),
                    ("They Pretend", "They like to pretend that they are the good guys of tech. While companies like Google are extremely terrible for your privacy, at least they aren't pretending like they aren't. Apple likes to give people the illusion that you can pay for your privacy, which to put frankly, is not true at all. They still spy on you just as much, or even more than, Google does.", false),
                    ("They are Restrictive and Controlling", "They limit and control what you are allowed to do with your own device. Want to repair your Mac? Nope, can't do that. Want to install a different OS? Nope, they'll do as much as they can to stop you from doing that. The reason why I prefer Google more (not that I like them, this is more about choosing the lesser evil), is because you are allowed to do something about it. Don't want Android spying on you? Most of the time (depending on the phone brand) you can easily unlock your phone, install a custom recovery, and install a custom ROM like LineageOS without installing GApps. With iPhones, try as you may, you will never have that same amount of control that you can have on an Android device.", false),
                ]);
                e
    match arg {
        "apple" => {
            let msg = msg.channel_id.send_message(&ctx.http, |m| {
                m.embed(|e| {
                    e.title("Why Phate6660 hates Apple:");
                    e.fields(vec![
                        ("They are Evil", "They are a despicable, evil, and disgusting company. I find them to be even worse than Google, and probably even Amazon. They've done some truly terrible things. Some examples: mass censorship, worker abuse (manipulation/brainwashing, sweatshops), repairing your own device is not allowed, they fully support DRM, they exploit developers.", false),
                        ("They Pretend", "They like to pretend that they are the good guys of tech. While companies like Google are extremely terrible for your privacy, at least they aren't pretending like they aren't. Apple likes to give people the illusion that you can pay for your privacy, which to put frankly, is not true at all. They still spy on you just as much, or even more than, Google does.", false),
                        ("They are Restrictive and Controlling", "They limit and control what you are allowed to do with your own device. Want to repair your Mac? Nope, can't do that. Want to install a different OS? Nope, they'll do as much as they can to stop you from doing that. The reason why I prefer Google more (not that I like them, this is more about choosing the lesser evil), is because you are allowed to do something about it. Don't want Android spying on you? Most of the time (depending on the phone brand) you can easily unlock your phone, install a custom recovery, and install a custom ROM like LineageOS without installing GApps. With iPhones, try as you may, you will never have that same amount of control that you can have on an Android device.", false),
                    ]);
                    e
                });
                m
            });
            m
        });

        if let Err(why) = msg {
            println!("Error sending message: {:?}", why);
            if let Err(why) = msg {
                error!("Error sending message: {:?}", why);
            }
        }
	}

    if arg == "steam" {
        let msg = msg.channel_id.send_message(&ctx.http, |m| {
            m.embed(|e| {
                e.title("Why Phate6660 hates Steam:");
                e.fields(vec![
                    ("DRM", "You don't own the games you buy, you own the right to play the game off of Steam.", false),
                    ("Mistreatment of Developers", "Steam mistreats game devs and publishers (this is why you see more and more games using GOG or even their own launchers/stores).", false),
                    ("It is Forced Onto You", "Steam is *forced* onto you by various games. Imagine my surprise when I buy the Elder Scrolls anthology (as a physical collector's set complete with DISC COPIES of the game), and every game works... except for Skyrim. Skyrim requires you to use Steam. It's a shame that I saved up 50 whole dollars for shit. I can tell you Skyrim went straight into the trash, right after being broken into 600 little pieces.", false),
                    ("Privacy Violations", "I shouldn't have to explain this one right?", false),
                    ("Centralization", "Having all of your games centralized into one place is stupid, and this ties into the DRM point. If Steam were to shut down right now, I guarantee that you would lose access to at least 80% of your games.", false),
                ]);
                e
        "steam" => {
            let msg = msg.channel_id.send_message(&ctx.http, |m| {
                m.embed(|e| {
                    e.title("Why Phate6660 hates Steam:");
                    e.fields(vec![
                        ("DRM", "You don't own the games you buy, you own the right to play the game off of Steam.", false),
                        ("Mistreatment of Developers", "Steam mistreats game devs and publishers (this is why you see more and more games using GOG or even their own launchers/stores).", false),
                        ("It is Forced Onto You", "Steam is *forced* onto you by various games. Imagine my surprise when I buy the Elder Scrolls anthology (as a physical collector's set complete with DISC COPIES of the game), and every game works... except for Skyrim. Skyrim requires you to use Steam. It's a shame that I saved up 50 whole dollars for shit. I can tell you Skyrim went straight into the trash, right after being broken into 600 little pieces.", false),
                        ("Privacy Violations", "I shouldn't have to explain this one right?", false),
                        ("Centralization", "Having all of your games centralized into one place is stupid, and this ties into the DRM point. If Steam were to shut down right now, I guarantee that you would lose access to at least 80% of your games.", false),
                    ]);
                    e
                });
                m
            });
            m
        });

        if let Err(why) = msg {
            println!("Error sending message: {:?}", why);
            if let Err(why) = msg {
                error!("Error sending message: {:?}", why);
            }
        }
    }

    if arg == "systemd" {
        let msg = msg.channel_id.send_message(&ctx.http, |m| {
            m.embed(|e| {
                e.title("Why Phate6660 hates SystemD:");
                e.fields(vec![
                    ("Feature Creep", "The undeniable feature creep. While some people actually enjoy the features brought in from it (boot manager, network manager, login manager?, etc), I find them to be nothing but bloat. An init system should be just that. An init system. Not <insert an exaggerated amount of functions here>.", false),
                    ("Slow", "It is slow. Slow to shutdown, slow to boot up, etc. Here are actual timed reboots from my machine using 3 init systems. SystemD (17s), OpenRC (11s), Runit (7s). 17s vs 7s, which would you choose?", false),
                    ("Bugs and Insecurities", "Due to the feature creep, there is a larger attack service for bugs and security vulnerabilities. And there are security issues with SystemD.", false),
                    ("Devs don't Care", "This is the one that bothers me the most. It's almost as if the dev(s) are completely oblivious or at least ignorant to the feature creep and security issues. Hell, Poettering even got awarded by Red Hat for making lame excuses for not fixing important bugs.", false),
                    ("Hard Requirement", "It is a hard dependency for a large and *still growing* list of programs. Which forces users to use tools such as consolekit or elogind or eudev, or even patch the program themselves not to use systemd. This is a trivial thing when using distros like Gentoo, but I feel sincerely sorry for those using distros like Debian where it's near impossible to escape.", false),
                ]);
                e
        "systemd" => {
            let msg = msg.channel_id.send_message(&ctx.http, |m| {
                m.embed(|e| {
                    e.title("Why Phate6660 hates SystemD:");
                    e.fields(vec![
                        ("Feature Creep", "The undeniable feature creep. While some people actually enjoy the features brought in from it (boot manager, network manager, login manager?, etc), I find them to be nothing but bloat. An init system should be just that. An init system. Not <insert an exaggerated amount of functions here>.", false),
                        ("Slow", "It is slow. Slow to shutdown, slow to boot up, etc. Here are actual timed reboots from my machine using 3 init systems. SystemD (17s), OpenRC (11s), Runit (7s). 17s vs 7s, which would you choose?", false),
                        ("Bugs and Insecurities", "Due to the feature creep, there is a larger attack service for bugs and security vulnerabilities. And there are security issues with SystemD.", false),
                        ("Devs don't Care", "This is the one that bothers me the most. It's almost as if the dev(s) are completely oblivious or at least ignorant to the feature creep and security issues. Hell, Poettering even got awarded by Red Hat for making lame excuses for not fixing important bugs.", false),
                        ("Hard Requirement", "It is a hard dependency for a large and *still growing* list of programs. Which forces users to use tools such as consolekit or elogind or eudev, or even patch the program themselves not to use systemd. This is a trivial thing when using distros like Gentoo, but I feel sincerely sorry for those using distros like Debian where it's near impossible to escape.", false),
                    ]);
                    e
                });
                m
            });
            m
        });

        if let Err(why) = msg {
            println!("Error sending message: {:?}", why);
            if let Err(why) = msg {
                error!("Error sending message: {:?}", why);
            }
        }
        _ => {
            error!("Could not find matching rant.");
            if let Err(why) = msg
                .channel_id
                .say(&ctx.http, "**ERROR:** Could not find matching rant.")
            {
                error!("Error sending message: {:?}", why);
            }
        }
    }