~phate/rsPhate

617f0d72ef1e694a1b8b0d3e5df4aff3ef8e002a — ValleyKnight 11 months ago b879421
update repo
18 files changed, 49 insertions(+), 238 deletions(-)

M .gitignore
R LICENSE.md => NON-LICENSE.md
M README.md
M src/commands/about.rs
M src/commands/embed.rs
D src/commands/iv.rs
D src/commands/meme.rs
M src/commands/mod.rs
M src/commands/noice.rs
D src/commands/owo.rs
M src/commands/projects.rs
M src/commands/rng.rs
D src/commands/rr.rs
M src/commands/wipltrn.rs
M src/functions/mod.rs
D src/functions/star.rs
M src/functions/yt_tw_check.rs
M src/main.rs
M .gitignore => .gitignore +2 -3
@@ 1,4 1,3 @@
/Cargo.lock
Cargo.lock
/images/just_yes
/target
\#*\#
.\#*
\ No newline at end of file

R LICENSE.md => NON-LICENSE.md +1 -1
@@ 1,2 1,2 @@
Anybody, is allowed to do anything, with this software.
Anybody, is allowed to do anything, with this software.<br>
*Any and all copyright restrictions are hereby revoked.*

M README.md => README.md +1 -0
@@ 24,6 24,7 @@ How to use:
- `^git site,owner/repo`: Bot will reply with full link to repo.
- `^hmm`: How much music does Phate have?
- `^iv SEARCH`: Bot will reply with an invidio link of the search query.*
- `^noice`: Bot will reply with image from his just_yes folder, image will be spoiler'd
- `^math operation,num,num`: Bot will do math for you (basic add/sub/div/mul) and reply with the result.*
- `^meme position,text`: Bot will generate a meme based on the input and send it as an image.
- `^owo input`: Bot will reply with OwO-ified input.

M src/commands/about.rs => src/commands/about.rs +5 -5
@@ 30,11 30,11 @@ fn about(ctx: &mut Context, msg: &Message) -> CommandResult {
            e.thumbnail(bot_icon);
            // false = not inline
            e.fields(vec![
                ("Scripting/Programming", "[Codeberg](https://codeberg.org/Phate6660), [sourcehut](https://sr.ht/~phate)", false),
                ("Social", "[Reddit](https://reddit.com/u/Valley6660), [Lobsters](https://lobste.rs/u/Phate6660)", false),
                ("Personal Site", "https://pages.codeberg.org/Phate6660/", false),
                ("Discord", "Phate6660#6270", false),
                ("Source Code", "[Phate6660/rsPhate](https://codeberg.org/Phate6660/rsPhate)", false),
                ("Scripting/Programming", "[GitHub](https://github.com/Phate6660), [Codeberg](https://codeberg.org/Phate6660), [sourcehut](https://sr.ht/~phate)", false),
                ("Social", "[Lobsters](https://lobste.rs/u/Phate6660), [Mastodon](https://fosstodon.org/@Phate6660)", false),
                ("Personal Site", "https://Phate6660.codeberg.page", false),
                ("Discord", "Phate#6660", false),
                ("Source Code", "[Phate6660/rsPhate](https://github.com/Phate6660/rsPhate)", false),
            ]);
            e
        });

M src/commands/embed.rs => src/commands/embed.rs +1 -1
@@ 11,7 11,7 @@ use serenity::{
fn embed(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    let title = args.single::<String>()?;
    let description = args.single::<String>()?;
    let mut image = args.single::<String>().unwrap_or("false".to_string());
    let image = args.single::<String>().unwrap_or_else(|_| "false".to_string());
    
    let link = if image == "false" {
        "https://i.imgur.com/pMBcpoq.png".to_string()

D src/commands/iv.rs => src/commands/iv.rs +0 -37
@@ 1,37 0,0 @@
use log::{error, info};
use serenity::{
    framework::standard::{macros::command, Args, CommandResult},
    model::channel::Message,
    prelude::*,
};
use std::process::Command;

#[command]
#[description = "Bot will reply with an invidio link of the search query."]
#[usage = "search"]
#[example = "mindless self indulgence unsociable"]
fn iv(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
    let mut args = args.rest();
    info!("search query: {}", args);
    let args = str::replace(args, " ", "+");
    info!("ytdl search query: {}", args);
    let args: String = "ytsearch:".to_string() + &args.to_string();

    // todo: somehow obtain video id through a crate or something, maybe somebody made a youtube-dl crate?
    let id = Command::new("youtube-dl")
        .arg("--get-id")
        .arg(args)
        .output()
        .expect("Could not generate link.");
    info!("video id: {}", String::from_utf8_lossy(&id.stdout).trim());

    let link: String =
        "https://invidio.us/watch?v=".to_string() + &String::from_utf8_lossy(&id.stdout).trim();
    info!("invidio link: {}", link);

    if let Err(why) = msg.channel_id.say(&ctx.http, link) {
        error!("Error sending message: {:?}", why);
    }

    Ok(())
}

D src/commands/meme.rs => src/commands/meme.rs +0 -65
@@ 1,65 0,0 @@
use log::{error, info};
use rofl;
use serenity::{
    framework::standard::{macros::command, Args, CommandResult},
    http::AttachmentType,
    model::channel::Message,
    prelude::*,
};
use std::{fs, io::Write, path::Path};

#[command]
#[description = "Bot will generate a meme based on input."]
#[usage = "top bottom template"]
#[example = "ah yes"]
#[example = "ah yes,enslaved meme generator"]
#[example = "ah yes,enslaved meme generator,anditsgone"]
fn meme(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    let meme = args.single::<String>()?;
    let meme2 = args.single::<String>().unwrap_or("empty".to_string());
    let template = args.single::<String>().unwrap_or("zoidberg".to_string());

    let pos = if meme2 == "empty" {
        vec![rofl::Caption::text_at(rofl::VAlign::Top, meme)]
    } else {
        vec![
            rofl::Caption::text_at(rofl::VAlign::Top, meme),
            rofl::Caption::text_at(rofl::VAlign::Bottom, meme2),
        ]
    };
    let engine = rofl::Engine::new(
        "/home/valley/downloads/git/rofld/data/templates",
        "/home/valley/downloads/git/rofld/data/fonts",
    );
    let image_macro = rofl::ImageMacro {
        template: template.into(),
        captions: pos,
        ..rofl::ImageMacro::default()
    };
    let output = engine.caption(image_macro)?;
    info!("meme has been generated");

    let mut file = fs::OpenOptions::new()
        .write(true)
        .open("/tmp/meme.png")?;
    file.write_all(&*output)?;
    info!("meme has been written to /tmp/meme.png");

    let msg = msg.channel_id.send_message(&ctx.http, |m| {
        m.embed(|e| {
            e.title("rofl meme generator");
            e.image("attachment://meme.png");
            e
        });
        m.add_file(AttachmentType::Path(Path::new(
            "/tmp/meme.png",
        )));
        m
    });

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

    Ok(())
}

M src/commands/mod.rs => src/commands/mod.rs +0 -4
@@ 7,14 7,10 @@ pub mod embed;
pub mod fortune;
pub mod git;
pub mod hmm;
pub mod iv;
pub mod math;
pub mod meme;
pub mod noice;
pub mod owo;
pub mod projects;
pub mod quit;
pub mod rng;
pub mod rr;
pub mod wipltrn;
pub mod ww;

M src/commands/noice.rs => src/commands/noice.rs +9 -16
@@ 1,39 1,32 @@
use log::{error, info};
use log::error;
use serenity::{
    framework::standard::{macros::command, CommandResult},
    http::AttachmentType,
    model::channel::Message,
    prelude::*,
};
use std::path::Path;
use std::{fs, path::Path};
use std::process::Command;

#[command]
#[description = "Bot will post a random picture from Phate's \"just yes\" folder. May or may not post NSFW material. Use at own discretion."]
#[description = "Bot will post a random picture from Phate's \"just yes\" folder. Images will be spoiler'd, have fun!"]
fn noice(ctx: &mut Context, msg: &Message) -> CommandResult {
    let file = Command::new("scripts/noice")
        .arg("file")
        .output()
        .expect("Could not obtain random image");
    let path = format!("{}", String::from_utf8_lossy(&file.stdout).trim());
    let path = String::from_utf8_lossy(&file.stdout).trim().to_string();
    let name = Command::new("scripts/noice")
        .arg("name")
        .arg(path)
        .output()
        .expect("Could not get base name.");
    let path2 = format!("{}", String::from_utf8_lossy(&file.stdout).trim());
    let attachment = format!("attachment://{}", String::from_utf8_lossy(&name.stdout).trim());
    info!(
        "\nfile: {}\nname: {}\nattachment: {}",
        String::from_utf8_lossy(&file.stdout).trim(),
        String::from_utf8_lossy(&name.stdout).trim(),
        attachment
    );
    let new_file = format!("images/just_yes/SPOILER_{}", String::from_utf8_lossy(&name.stdout).trim());
    fs::copy(String::from_utf8_lossy(&file.stdout).trim(), new_file).expect("could not copy file");
    let new_file2 = format!("images/just_yes/SPOILER_{}", String::from_utf8_lossy(&name.stdout).trim());
    let path2 = new_file2;
    
    let msg = msg.channel_id.send_message(&ctx.http, |m| {
        m.embed(|e| {
            e.image(attachment);
            e
        });
        m.add_file(AttachmentType::Path(Path::new(&path2)));
        m
    });

D src/commands/owo.rs => src/commands/owo.rs +0 -22
@@ 1,22 0,0 @@
use log::error;
use owoify::OwOifiable;
use serenity::{
    framework::standard::{macros::command, Args, CommandResult},
    model::channel::Message,
    prelude::*,
};

#[command]
#[description = "Bot will reply with OwO-ified version of input."]
#[usage = "input"]
#[example = "// kill me, please -- phate"]
fn owo(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
    let input = String::from(args.rest());
    let owo_text = input.owoify();

    if let Err(why) = msg.channel_id.say(&ctx.http, owo_text) {
        error!("Error sending message: {:?}", why);
    }

    Ok(())
}

M src/commands/projects.rs => src/commands/projects.rs +10 -10
@@ 11,17 11,17 @@ fn projects(ctx: &mut Context, msg: &Message) -> CommandResult {
        m.embed(|e| {
            e.title("Other Projects Created/Co-Created by The Author");
            e.fields(vec![
                ("rsfetch", "https://github.com/rsfetch/rsfetch", false),
                ("pkg", "https://github.com/Phate6660/pkg", false),
                ("cfg", "https://github.com/Phate6660/cfg", false),
                ("gzdoom-discordrpc", "https://github.com/Phate6660/gzdoom-discordrpc", false),
                ("musinfo", "https://github.com/Phate6660/musinfo", false),
				("nixinfo", "https://github.com/Phate6660/rsnixinfo", false),
                ("p6nc-overlay", "https://github.com/p6nc/overlay", false),
                ("bindings", "https://github.com/Phate6660/bindings", false),
                ("sxhkd-bindings", "https://github.com/Phate6660/sxhkd-bindings", false),
                ("rust-server", "https://github.com/Phate6660/rust-server", false),
				("WBMPFMPD", "https://github.com/Phate6660/WBMPFMPD", false),
				("valleyTERM", "https://github.com/Phate6660/term", false),
				("FFNIFLFYTU", "https://github.com/Phate6660/FFNIFDBDFYTU", false),
				("cfg", "https://github.com/Phate6660/cfg", false),
				("fet", "https://github.com/Phate6660/fet", false),
                ("pkg", "https://github.com/Phate6660/pkg", false),
                ("rsfetch", "https://github.com/Phate6660/rsfetch", false),
                ("rsmpc", "https://github.com/Phate6660/rsmpc", false),
                ("rsmpv", "https://github.com/Phate6660/rsmpv", false),
                ("undeprecated-overlay", "https://github.com/Phate6660/undeprecated", false),
                ("WBMPFMPD", "https://github.com/Phate6660/WBMPFMPD", false),
            ]);
            e
        });

M src/commands/rng.rs => src/commands/rng.rs +10 -10
@@ 10,7 10,7 @@ use serenity::{
#[description = "Bot will generate a random number between min and max and reply with the result."]
#[usage = "min max"]
#[example = "-999 999"]
fn rng(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
fn rng(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult {
    fn min_and_max(mut a: Args) -> (i64, i64) {
        let min = a.single::<i64>().unwrap_or(9223372036854775807);
        let max = a.single::<i64>().unwrap_or(9223372036854775807);


@@ 22,28 22,28 @@ fn rng(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    if min == 9223372036854775807 {
        let message_specific: String = "The min value supplied is invalid.".to_string();
        error!("{}", message_specific);
        msg.channel_id.say(&ctx.http, message_specific);
        msg.channel_id.say(&ctx.http, message);
        msg.channel_id.say(&ctx.http, message_specific)?;
        msg.channel_id.say(&ctx.http, message)?;
        return Ok(());
    } else if max == 9223372036854775807 {
        let message_specific: String = "The max value supplied is invalid.".to_string();
        error!("{}", message_specific);
        msg.channel_id.say(&ctx.http, message_specific);
        msg.channel_id.say(&ctx.http, message);
        msg.channel_id.say(&ctx.http, message_specific)?;
        msg.channel_id.say(&ctx.http, message)?;
        return Ok(());
    } else if min == max {
        let message_specific: String =
            "The min value must not be equal to, or greater than, the max value.".to_string();
        error!("{}", message_specific);
        msg.channel_id.say(&ctx.http, message_specific);
        msg.channel_id.say(&ctx.http, message);
        msg.channel_id.say(&ctx.http, message_specific)?;
        msg.channel_id.say(&ctx.http, message)?;
        return Ok(());
    } else if min > max {
        let message_specific: String =
            "The min value must be lower than the max value.".to_string();
        error!("{}", message_specific);
        msg.channel_id.say(&ctx.http, message_specific);
        msg.channel_id.say(&ctx.http, message);
        msg.channel_id.say(&ctx.http, message_specific)?;
        msg.channel_id.say(&ctx.http, message)?;
        return Ok(());
    }



@@ 54,7 54,7 @@ fn rng(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
        msg.channel_id.say(
            &ctx.http,
            "Could not generate a random number, please try again.",
        );
        )?;
    }

    Ok(())

D src/commands/rr.rs => src/commands/rr.rs +0 -18
@@ 1,18 0,0 @@
use serenity::{
    framework::standard::{macros::command, CommandResult},
    model::channel::Message,
    prelude::*,
};

#[command]
#[description = "Bot will reply with a link (without a link preview) to Rick Astley's \"Never Gonna Give You Up\"."]
fn rr(ctx: &mut Context, msg: &Message) -> CommandResult {
    if let Err(why) = msg.channel_id.say(
        &ctx.http,
        "<https://invidio.us/watch?v=dQw4w9WgXcQ&local=1>",
    ) {
        println!("Error sending message: {:?}", why);
    }

    Ok(())
}

M src/commands/wipltrn.rs => src/commands/wipltrn.rs +1 -4
@@ 1,5 1,4 @@
use mpd::{Client, Song};
use sedregex::find_and_replace;
use serenity::{
    framework::standard::{macros::command, CommandResult},
    http::AttachmentType,


@@ 13,14 12,12 @@ use std::path::Path;
fn wipltrn(ctx: &mut Context, msg: &Message) -> CommandResult {
    let mut c = Client::connect("127.0.0.1:6600").unwrap();
    let song: Song = c.currentsong().unwrap().unwrap();
    let fil = song.file;
    let format = find_and_replace(&fil, &["s/.*\\.//"]).unwrap();
    let tit = song.title.as_ref().unwrap();
    let na = "N/A".to_string();
    let art = song.tags.get("Artist").unwrap_or(&na);
    let alb = song.tags.get("Album").unwrap_or(&na);
    let dat = song.tags.get("Date").unwrap_or(&na);
    let mus_title = [art, " - ", tit, " [", &format, "]"].concat();
    let mus_title = [art, " - ", tit].concat();
    let mus_album = [alb, " (", dat, ")"].concat();
    let msg = msg.channel_id.send_message(&ctx.http, |m| {
        m.embed(|e| {

M src/functions/mod.rs => src/functions/mod.rs +0 -1
@@ 1,3 1,2 @@
pub mod prefix_space_check;
pub mod star;
pub mod yt_tw_check;

D src/functions/star.rs => src/functions/star.rs +0 -9
@@ 1,9 0,0 @@
use log::{error, info};
use serenity::{model::channel::{Message, ReactionType}, prelude::*};

// If the user's message is starred, copy it and send it as an embed in #starboard.
pub fn star(ctx: &mut Context, msg: &Message) {
    let sent_message = &msg.content;
    let reactions = &msg.reactions;
    info!("Content: {}, Reactions: {:#?}", sent_message, reactions);
}

M src/functions/yt_tw_check.rs => src/functions/yt_tw_check.rs +2 -26
@@ 3,35 3,11 @@ use serenity::{model::channel::Message, prelude::*};

// If "youtube.com" is found, react with disappointed and message a pretty embed about invidio.
// Do the same for "twitter.com", except the embed pertains to Nitter.
pub fn yt_tw_check(ctx: &mut Context, msg: &Message) {
pub fn tw_check(ctx: &mut Context, msg: &Message) {
    let sent_message = &msg.content;
    if sent_message.contains("youtube.com") {
        info!("YouTube link was found! It was sent by: {}", msg.author.name);
        msg.react(&ctx.http, '😞');
        let message = msg.channel_id.send_message(&ctx.http, |m| {
            m.embed(|e| {
                e.title("`rsPhate`");
                e.description("YouTube link discovered! Please link to invidio instead. Why:");
                // false = not inline
                e.fields(vec![
                    ("FOSS", "Invidio is a [FOSS](https://github.com/omarroth/invidious) frontend to YouTube.", false),
                    ("Proxy", "Invidio has the ability to proxy YouTube videos through their servers, allowing you to watch videos without ever touching Google's servers. Add `&local=1` to an invidio URL to proxy.", false),
                    ("Lightweight", "The entire site is very small, and works without JavaScript or XHR.", false),
                    ("Self-Hostable", "You can self-host your own instance if you really wanted to. And if you don't trust the [main instance](https://invidio.us) there are a [variety of other instances](https://github.com/omarroth/invidious/wiki/Invidious-Instances) to choose from.", false),
                    ("I'm interested, how do I convert URLs?", "Replace `youtube.com` with `invidio.us`. For `youtu.be` URLs, take the string after the last slash and append it to `https://invidio.us/watch?v=`.", false),
                ]);
                e
            });
            m
        });

        if let Err(why) = message {
            error!("Error sending message: {:?}", why);
        }
    }
    if sent_message.contains("twitter.com") {
        info!("Twitter link was found! It was sent by: {}", msg.author.name);
        msg.react(&ctx.http, '😞');
        msg.react(&ctx.http, '😞').expect("could not react");
        let message = msg.channel_id.send_message(&ctx.http, |m| {
            m.embed(|e| {
                e.title("`rsPhate`");

M src/main.rs => src/main.rs +7 -6
@@ 20,13 20,13 @@ use std::{
// Load and use commands from src/commands/
mod commands;
use commands::{
    about::*, date::*, embed::*, fortune::*, git::*, hmm::*, iv::*, math::*, meme::*, noice::*,
    owo::*, projects::*, quit::*, rng::*, rr::*, wipltrn::*, ww::*,
    about::*, date::*, embed::*, fortune::*, git::*, hmm::*, math::*, noice::*,
    projects::*, quit::*, rng::*, wipltrn::*, ww::*,
};

// Load and use extra functions from src/functions/
mod functions;
use functions::{prefix_space_check::prefix_space_check, star::star};
use functions::{prefix_space_check::prefix_space_check, yt_tw_check::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


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

#[group]


@@ 88,7 88,7 @@ struct Numbers;

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

#[help]


@@ 175,6 175,7 @@ fn main() {
                prefix_space_check(ctx, msg, unknown_command_name);
            })
            .normal_message(|ctx, msg| {
                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


@@ 184,7 185,7 @@ fn main() {
                    msg.reply(
                        &ctx.http,
                        "Owner check failed! I will ping you a hundredfold if you do that again! <:sadgry:676458405342216195>",
                    );
                    ).expect("could not reply with angry failed owner check");
                }
                _ => {
                    error!("Unhandled dispatch error!");