forked from sorceress/rustcord
869 lines
28 KiB
Rust
Executable File
869 lines
28 KiB
Rust
Executable File
#![allow(clippy::unreadable_literal)]
|
|
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
|
|
use colored::*;
|
|
use rand::Rng;
|
|
use serenity::{
|
|
client::{
|
|
bridge::gateway::{ShardId, ShardManager},
|
|
Client,
|
|
},
|
|
framework::standard::{
|
|
macros::{check, command, group},
|
|
Args, CheckResult, CommandError, CommandOptions, CommandResult, DispatchError, Reason,
|
|
StandardFramework,
|
|
},
|
|
model::{
|
|
channel::Message,
|
|
gateway::Ready,
|
|
id::{ChannelId, UserId},
|
|
user::OnlineStatus,
|
|
},
|
|
prelude::*,
|
|
};
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::{env, process, sync::Arc};
|
|
|
|
struct Handler;
|
|
|
|
struct ShardManagerContainer;
|
|
|
|
impl TypeMapKey for ShardManagerContainer {
|
|
type Value = Arc<Mutex<ShardManager>>;
|
|
}
|
|
|
|
impl EventHandler for Handler {
|
|
fn ready(&self, ctx: Context, ready: Ready) {
|
|
if let Some(shard) = ready.shard {
|
|
println!(
|
|
"INFO: {} is connected on shard {}/{}!\nuwu",
|
|
ready.user.name, shard[0], shard[1]
|
|
);
|
|
|
|
use serenity::model::gateway::Activity;
|
|
let activity = Activity::listening("catgirls nyaaing");
|
|
let status = OnlineStatus::Online;
|
|
|
|
ctx.set_presence(Some(activity), status);
|
|
}
|
|
}
|
|
|
|
fn message(&self, ctx: Context, message: Message) {
|
|
let text = &message.content.to_lowercase();
|
|
if text.contains("good")
|
|
&& (text.contains("discordinator") || text.contains("discordinyator"))
|
|
{
|
|
let _ = message.channel_id.say(&ctx.http, "nyaa~ 💞");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[group]
|
|
#[commands(
|
|
init, ping, halt, servers, host, status, ship, bottom_rng, headpat, uwu, gayculator, sausage,
|
|
help, embed, define, owo, info, echo, desc, pinned, brainfuck, pfp
|
|
)]
|
|
struct General;
|
|
|
|
lazy_static! {
|
|
static ref OWNERS: std::vec::Vec<serenity::model::id::UserId> =
|
|
/* Agatha's Id Julia's Id */
|
|
vec![UserId(254310746450690048), UserId(687740609703706630)];
|
|
}
|
|
|
|
// Calculates hash of a type that implements Hash
|
|
fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
|
let mut s = DefaultHasher::new();
|
|
t.hash(&mut s);
|
|
s.finish()
|
|
}
|
|
|
|
fn main() {
|
|
let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("Invalid token"), Handler)
|
|
.expect("Error creating client");
|
|
|
|
// Updates stored data, used for ping command
|
|
{
|
|
let mut data = client.data.write();
|
|
data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
|
|
}
|
|
|
|
client.with_framework(
|
|
StandardFramework::new()
|
|
.configure(|c| {
|
|
c.with_whitespace(true)
|
|
.owners(OWNERS.clone().into_iter().collect())
|
|
.prefixes(vec!["owo!", "OwO!", "aga"])
|
|
.no_dm_prefix(true)
|
|
.case_insensitivity(true)
|
|
.by_space(false)
|
|
})
|
|
.on_dispatch_error(|ctx, msg, error| {
|
|
if let DispatchError::CheckFailed("Owner", Reason::Unknown) = error {
|
|
// triggers if user is not owner
|
|
let _ = msg.channel_id.say(&ctx.http, "nyo");
|
|
} else if let DispatchError::Ratelimited(_) = error {
|
|
// triggers if rate limited
|
|
eprintln!(
|
|
"{}",
|
|
format!(
|
|
"Rate limited in {} with message {}",
|
|
msg.channel_id.to_string().purple().bold(),
|
|
msg.content.purple()
|
|
)
|
|
);
|
|
}
|
|
})
|
|
.after(|ctx, msg, cmd_name, error| {
|
|
// prints error in chat
|
|
if let Err(why) = error {
|
|
let _ = msg.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("Error in **{}**", cmd_name))
|
|
.description(&why.0)
|
|
/*.thumbnail("https://i.imgur.com/VzOEz2E.png") oh no */
|
|
.colour(0xff6961)
|
|
})
|
|
});
|
|
// prints error in console
|
|
eprintln!(
|
|
"{}",
|
|
format!("Error in {}: {}", cmd_name.purple(), &why.0.red().bold())
|
|
);
|
|
}
|
|
})
|
|
.group(&GENERAL_GROUP),
|
|
);
|
|
|
|
if let Err(e) = client.start() {
|
|
eprintln!("An error occurred while running the client: {:?}", e);
|
|
}
|
|
}
|
|
|
|
#[check]
|
|
#[name = "Owner"]
|
|
fn owner_check(_: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> CheckResult {
|
|
if OWNERS.clone().contains(&msg.author.id) {
|
|
CheckResult::Success
|
|
} else {
|
|
CheckResult::Failure(Reason::Unknown)
|
|
}
|
|
}
|
|
|
|
#[check]
|
|
#[name = "Server"]
|
|
fn server_check(_: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> CheckResult {
|
|
(msg.guild_id == Some(serenity::model::id::GuildId(687011389294116875))).into()
|
|
}
|
|
|
|
#[command]
|
|
fn init(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let responses = [
|
|
"Discordinator9000 is gonna hug nya'll!",
|
|
"Nyaa~!",
|
|
"Hewwo uwu",
|
|
];
|
|
let num = rand::thread_rng().gen_range(0, responses.len());
|
|
let _ = message.channel_id.say(&ctx.http, responses[num]);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn ping(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
// I have no idea if this works but its 5æm and I need to sleep help
|
|
let data = ctx.data.read();
|
|
|
|
let shard_manager = match data.get::<ShardManagerContainer>() {
|
|
Some(v) => v,
|
|
None => {
|
|
return Err(CommandError(
|
|
"There was a problem getting the shard manager!".to_string(),
|
|
))
|
|
}
|
|
};
|
|
|
|
let manager = shard_manager.lock();
|
|
let runners = manager.runners.lock();
|
|
|
|
let runner = match runners.get(&ShardId(ctx.shard_id)) {
|
|
Some(v) => v,
|
|
None => return Err(CommandError("No shard found!".to_string())),
|
|
};
|
|
|
|
let ping = match runner.latency {
|
|
Some(v) => v.as_millis(),
|
|
None => return Err(CommandError("Could not get latency!".to_string())),
|
|
};
|
|
|
|
let _ = message
|
|
.channel_id
|
|
.say(&ctx, format!("Pong! Latency: {}ms", ping));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[checks(Owner)]
|
|
fn echo(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
let input: String = args.rest().trim().to_string();
|
|
if args.is_empty() {
|
|
return Err(CommandError("Called without input".to_string()));
|
|
}
|
|
|
|
let _ = message.channel_id.say(&ctx.http, input);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[checks(Owner)]
|
|
fn halt(ctx: &mut Context) -> CommandResult {
|
|
// Workaround for discord not doing this automatically
|
|
ctx.set_presence(None, OnlineStatus::Offline);
|
|
|
|
use std::{thread, time};
|
|
// Sleep for 1s
|
|
thread::sleep(time::Duration::new(1, 0));
|
|
|
|
process::exit(0);
|
|
}
|
|
|
|
// set bot's status to input text
|
|
#[command]
|
|
#[checks(Owner)]
|
|
fn status(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult {
|
|
use serenity::model::gateway::Activity;
|
|
|
|
if args.is_empty() {
|
|
return Err(CommandError("Called without args!".to_string()));
|
|
}
|
|
|
|
let mut input = args.single::<String>()?;
|
|
input = input.trim().to_string();
|
|
|
|
let activity;
|
|
|
|
// if args contain 'listening', use the appropriate activity type
|
|
if input.contains("listening") {
|
|
activity = Activity::listening(args.rest().trim());
|
|
// reset status
|
|
} else if input.contains("reset") {
|
|
activity = Activity::listening("catgirls nyaaing");
|
|
// otherwise default to playing
|
|
} else {
|
|
args.restore();
|
|
activity = Activity::playing(args.rest().trim());
|
|
};
|
|
|
|
let status = OnlineStatus::Online;
|
|
ctx.set_presence(Some(activity), status);
|
|
let _ = message.react(&ctx.http, "💜");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[checks(Owner)]
|
|
fn servers(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let mut list = String::new();
|
|
let cache = ctx.cache.read();
|
|
for (index, guild_lock) in cache.guilds.values().enumerate() {
|
|
let guild = guild_lock.read();
|
|
list.push_str(&format!("{}: {}\n", index, guild.name));
|
|
}
|
|
let _ = message
|
|
.channel_id
|
|
// Add zero width space to all mentions
|
|
.say(&ctx.http, list.replace("@", "@\u{200B}"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[checks(Owner)]
|
|
fn host(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let _ = message.channel_id.say(
|
|
&ctx.http,
|
|
format!(
|
|
"OS: {os}; {release}\nHost: {host}\nCPU: {cpu}MHz",
|
|
os = sys_info::os_type()?,
|
|
host = sys_info::hostname()?,
|
|
release = sys_info::linux_os_release()?
|
|
.pretty_name
|
|
.unwrap_or_else(|| "Unknown".to_string()),
|
|
cpu = sys_info::cpu_speed()?
|
|
),
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn embed(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
use serde::Deserialize;
|
|
use serenity::utils::Colour;
|
|
use std::{fs, io::prelude::*};
|
|
|
|
#[derive(Deserialize)]
|
|
struct EmbedProperties {
|
|
author: Option<(String, String)>,
|
|
colour: Option<String>,
|
|
description: Option<String>,
|
|
fields: Option<Vec<(String, String, bool)>>,
|
|
footer: Option<(String, String)>,
|
|
image: Option<String>,
|
|
thumbnail: Option<String>,
|
|
timestamp: Option<String>,
|
|
title: Option<String>,
|
|
url: Option<String>,
|
|
}
|
|
|
|
// print documentation from src/embed-docs.txt
|
|
if &args.rest().trim().to_string() == "help" {
|
|
let mut file = fs::File::open("./src/embed-docs.txt")?;
|
|
let mut help_string = String::new();
|
|
file.read_to_string(&mut help_string)?;
|
|
|
|
let _ = message.channel_id.say(&ctx.http, help_string);
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
let input_embed: EmbedProperties = match toml::from_str(&args.rest().trim()) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
return Err(CommandError(format!("Deserialization error: {:?}", e)));
|
|
}
|
|
};
|
|
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
// Set embed author unless empty
|
|
if input_embed.author.is_some() {
|
|
let auth = input_embed.author.unwrap();
|
|
e.author(|a| {
|
|
//assuming first array element is name and second is icon url
|
|
a.name(auth.0);
|
|
a.icon_url(auth.1);
|
|
|
|
a
|
|
});
|
|
}
|
|
|
|
// Set embed colour unless empty
|
|
if input_embed.colour.is_some() {
|
|
e.color(Colour::new(
|
|
u32::from_str_radix(&input_embed.colour.unwrap(), 16)
|
|
.ok()
|
|
.unwrap_or(0x000000),
|
|
));
|
|
}
|
|
|
|
// Set embed description unless empty
|
|
if input_embed.description.is_some() {
|
|
e.description(input_embed.description.unwrap());
|
|
}
|
|
// Set embed fields unless empty
|
|
if input_embed.fields.is_some() {
|
|
e.fields(input_embed.fields.unwrap());
|
|
}
|
|
|
|
// Set embed footer unless empty
|
|
if input_embed.footer.is_some() {
|
|
let foot = input_embed.footer.unwrap();
|
|
e.footer(|f| {
|
|
//assuming first array element is name and second is icon url
|
|
f.text(foot.0);
|
|
f.icon_url(foot.1);
|
|
|
|
f
|
|
});
|
|
}
|
|
|
|
if input_embed.image.is_some() {
|
|
e.image(input_embed.image.unwrap());
|
|
}
|
|
|
|
if input_embed.thumbnail.is_some() {
|
|
e.thumbnail(input_embed.thumbnail.unwrap());
|
|
}
|
|
|
|
if input_embed.timestamp.is_some() {
|
|
e.timestamp(input_embed.timestamp.unwrap());
|
|
}
|
|
|
|
if input_embed.title.is_some() {
|
|
e.title(input_embed.title.unwrap());
|
|
}
|
|
|
|
if input_embed.url.is_some() {
|
|
e.url(input_embed.url.unwrap());
|
|
}
|
|
|
|
e
|
|
});
|
|
m
|
|
});
|
|
|
|
let _ = message
|
|
.channel_id
|
|
.say(&ctx.http, format!("Embed requested by: {}", message.author));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// generate a random number using a keysmash as seed
|
|
#[command]
|
|
fn bottom_rng(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult {
|
|
use rand::{rngs::StdRng, SeedableRng};
|
|
|
|
// get N last messages, otherwise 10
|
|
let num = args.single::<u64>().unwrap_or(10);
|
|
let messages = message
|
|
.channel_id
|
|
.messages(&ctx.http, |get| get.before(message.id).limit(num));
|
|
if let Err(e) = messages {
|
|
let _ = message.channel_id.say(&ctx.http, format!("Error: {}", e));
|
|
} else {
|
|
let mut messages = messages?;
|
|
// remove all messages by other users
|
|
messages.retain(|v| v.author != message.mentions[0]);
|
|
let mut input = String::new();
|
|
for msg in messages {
|
|
input.push_str(&format!("{} ", msg.content));
|
|
}
|
|
let result: u64 = StdRng::seed_from_u64(calculate_hash(&input)).gen_range(0, 100);
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Bottom RNG")
|
|
.description(format!("Result: {}", result))
|
|
.color(0x800869)
|
|
})
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn ship(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
use rand::{rngs::StdRng, SeedableRng};
|
|
|
|
// Get input names
|
|
let names: String = args.rest().trim().to_string();
|
|
// Calculate compatibility based on hash
|
|
let compat: u64 = StdRng::seed_from_u64(calculate_hash(&names)).gen_range(50, 100);
|
|
|
|
// Initialize a bar to display compatibility percentage
|
|
let mut compbar = String::from("----------");
|
|
compbar.insert_str((compat / 10) as usize, ":purple_heart:");
|
|
|
|
// Convert names to a Vec<String>
|
|
let names = names
|
|
.split_whitespace()
|
|
.map(|x| x.to_owned())
|
|
.collect::<Vec<String>>();
|
|
|
|
// Concatenate names together
|
|
let shipname: Result<String, String> = match names.len() {
|
|
0 => Err("Invalid input!".to_string()),
|
|
1 => Ok(names[0].clone()),
|
|
_ => {
|
|
let mut first_halves = String::new();
|
|
for name in &names[0..names.len() - 1] {
|
|
first_halves.push_str(&name[0..name.len() / 2]);
|
|
}
|
|
let first_halves = first_halves.as_str();
|
|
let last_half = &names[names.len() - 1][(names.len() / 2) + 1..];
|
|
|
|
Ok(format!("{}{}", first_halves, last_half))
|
|
}
|
|
};
|
|
|
|
if let Err(e) = shipname {
|
|
let _ = message.channel_id.say(&ctx.http, e);
|
|
} else {
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("Original names: {}", args.rest().trim()))
|
|
.description(format!(
|
|
"Ship name:\n**{}**\nCompatibility: **{}%**\n{}",
|
|
shipname.unwrap(),
|
|
compat,
|
|
compbar
|
|
))
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[aliases("pat")]
|
|
fn headpat(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
let args = args.rest().trim();
|
|
|
|
if args.is_empty() {
|
|
return Err(CommandError("Please specify a username!".to_string()));
|
|
}
|
|
|
|
// Get username from first mention, otherwise use input text
|
|
let name = match message.mentions.len() {
|
|
0 => args,
|
|
_ => message.mentions[0].name.as_str(),
|
|
};
|
|
|
|
if let Err(e) = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("Sending headpats to **{}**...", name))
|
|
.image(
|
|
"https://i.pinimg.com/originals/83/1a/90/831a903eab6d827dcfd298b9e3196e30.jpg",
|
|
)
|
|
.description("[Source](https://www.pinterest.com/pin/377809856242075277/)")
|
|
})
|
|
}) {
|
|
let _ = message.channel_id.say(&ctx.http, format!("{:?}", e));
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// send a random uwu image
|
|
#[command]
|
|
fn uwu(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let images = [
|
|
"https://i.redditmedia.com/qDD9W7NJqTAk31y061TuRW9R8qOcCuEmmCWyOsUEavE.png?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=ebdd3f1970b4fe70ccd24a1958e7fc32",
|
|
"https://www.shitpostbot.com/img/sourceimages/smash-that-mfuckn-uwu-button-57b5aa1de9fe4.jpeg",
|
|
"https://www.shitpostbot.com/img/sourceimages/fallout-nv-owo-57e586ae15322.jpeg",
|
|
"https://i.redditmedia.com/-JaK9YW7mPz2S2xBJmXvW4fZ58uGMa4l6GIgYt3dqZg.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=ebab29a577346b4d18ec914538b69bb4",
|
|
"https://preview.redd.it/ie48xuwurzt41.jpg?width=640&crop=smart&auto=webp&s=c4a27d5ed086430cd29530a3d3c8e846cad867d5",
|
|
"https://i.redd.it/9aw0yzprztq41.jpg",
|
|
"https://i.redd.it/gxjx9jb01ef41.jpg",
|
|
"https://i.redd.it/n0liugufmks41.jpg",
|
|
"https://preview.redd.it/fcfrmarhj9s41.jpg?width=640&crop=smart&auto=webp&s=5a4ff9a471dca7cad61b8e56bc65876ef083304a",
|
|
"https://i.redd.it/ifwsmbme48q41.jpg"
|
|
];
|
|
let num = rand::thread_rng().gen_range(0, images.len());
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.image(images[num]);
|
|
e.footer(|f| f.text(format!("Source: {}", images[num])));
|
|
|
|
e
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn gayculator(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult {
|
|
let number_32: i32 = args.single::<i32>().unwrap_or(1);
|
|
let result = if number_32 % 2 == 0 {
|
|
"much straight"
|
|
} else {
|
|
"large gay"
|
|
};
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Gayness level:")
|
|
.description(result)
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[aliases("sosig")]
|
|
fn sausage(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e
|
|
.title("Dongle!")
|
|
.image("https://cdn.discordapp.com/attachments/689023662489468966/712283397015470120/26029881886330_4.gif")
|
|
})
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn help(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Availble commands:")
|
|
.description("All commands are case-insensitive")
|
|
.fields(vec![
|
|
("owo!init", "Introduce me", true),
|
|
("owo!ping", "Pong", true),
|
|
("owo!sausage", "Sosig", true),
|
|
("owo!help", "Help the fellow humanz!", true),
|
|
("owo!info", "Show information about me!", true),
|
|
(
|
|
"owo!what's this ``word``",
|
|
"Find a definition of word",
|
|
true,
|
|
),
|
|
(
|
|
"owo!embed ``[args]`` *OR* help",
|
|
"Create an embed from a Toml object",
|
|
true,
|
|
),
|
|
("owo!desc", "Display channel's topic", true),
|
|
(
|
|
"owo!pinned ``num`` ``<channel>``",
|
|
"Display channel's Nth pinned message. Channel name is optional",
|
|
true,
|
|
),
|
|
("owo!pfp ``@username``", "Post user's profile picture", true),
|
|
("owo!brainfuck ``input``", "Execute input code", true),
|
|
("owo!ship ``[names]``", "*Shipping intensifies*", true),
|
|
("owo!headpat ``name``", "Headpat someone", true),
|
|
("owo!owo ``text``", "owoify input text", true),
|
|
("\u{200B}", "**Admin commands:**", false),
|
|
("owo!halt", "Kill the bot process", true),
|
|
("owo!status ``[args]``", "Sets the bot status", true),
|
|
("owo!servers", "List the servers I'm in", true),
|
|
("owo!host", "Display host info", true),
|
|
])
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn info(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
if !args.is_empty() {
|
|
return Err(CommandError("Called with args!".to_string()));
|
|
}
|
|
|
|
let num = ctx.cache.read().guilds.len();
|
|
// get developer's username
|
|
let aganame = OWNERS.clone()[0].to_user(ctx.http.clone())?.tag();
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| m
|
|
.embed(|e| e
|
|
.title("Discordinator9000's info:")
|
|
.field("Author:", format!("{} / Agatha", aganame), false)
|
|
.field("Server count:", num , false)
|
|
.field("Invite:", "[Invite link](https://discordapp.com/api/oauth2/authorize?client_id=470350233419907129&permissions=2048&scope=bot)", true )
|
|
.field("source:", "[Gitlab](https://gitlab.com/agathasorceress/rustcord)", true)
|
|
.footer(|f| f
|
|
.text("Written in Rust using Serenity, OwOify and a few other libraries"))
|
|
.thumbnail("https://cdn.discordapp.com/attachments/687011390434967621/704118007563157544/discordinator.png")
|
|
.color(0xffd1dc)
|
|
));
|
|
Ok(())
|
|
}
|
|
|
|
// Urban Dictionary lookup
|
|
#[command]
|
|
#[aliases("what's this")]
|
|
fn define(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
let text: String = args.rest().trim().to_string();
|
|
let defs = &urbandict::get_definitions(&text);
|
|
if !args.is_empty() {
|
|
match defs {
|
|
Err(_e) => {
|
|
return Err(CommandError("Invalid query >w<".to_string()));
|
|
}
|
|
Ok(v) => {
|
|
if !v.is_empty() {
|
|
let def = &v[0];
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("Query: {}, Author: {}", text, def.author))
|
|
.field(
|
|
"Definition: ",
|
|
def.definition.replace(|c| c == '[' || c == ']', ""),
|
|
false,
|
|
)
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
} else {
|
|
return Err(CommandError("No results!".to_string()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
fn pfp(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
// Get username from first mention, otherwise use current username
|
|
let user = match message.mentions.len() {
|
|
0 => &message.author,
|
|
_ => &message.mentions[0],
|
|
};
|
|
|
|
let pfp = match user.avatar_url() {
|
|
Some(v) => v,
|
|
None => return Err(CommandError("The user does not have an avatar".to_string())),
|
|
};
|
|
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("{}'s profile picture", user.name))
|
|
.image(pfp)
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Text owoification
|
|
#[command]
|
|
fn owo(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
use owoify::OwOifiable;
|
|
|
|
let input: String = args.rest().trim().to_string();
|
|
let _ = message.channel_id.say(&ctx.http, input.owoify());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Prints channel topic
|
|
#[command]
|
|
#[aliases("description", "topic")]
|
|
fn desc(ctx: &mut Context, message: &Message) -> CommandResult {
|
|
let channel_lock = match message.channel(&ctx) {
|
|
Some(ch) => ch,
|
|
None => {
|
|
return Err(CommandError("Could not get channel!".to_string()));
|
|
}
|
|
};
|
|
let channel_lock = match channel_lock.guild() {
|
|
Some(g) => g,
|
|
None => {
|
|
return Err(CommandError("Could not get guild!".to_string()));
|
|
}
|
|
};
|
|
let channel = channel_lock.read();
|
|
|
|
let topic = if channel.topic.clone().unwrap() != String::from("") {
|
|
channel.topic.clone().unwrap()
|
|
} else {
|
|
String::from("No channel topic found")
|
|
};
|
|
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Channel's topic:")
|
|
.description(topic)
|
|
.color(0xffd1dc)
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Prints Nth pinned message
|
|
#[command]
|
|
fn pinned(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult {
|
|
// defaults to latest pinned message if no args are provided
|
|
let mut idx = args.single::<usize>().unwrap_or(1);
|
|
// Makes pinned messages 1-indexed
|
|
if idx != 0 {
|
|
idx -= 1;
|
|
}
|
|
let target_channel = match args.single::<ChannelId>() {
|
|
Ok(v) => v,
|
|
Err(_) => message.channel_id,
|
|
};
|
|
let pinned = match target_channel.pins(&ctx.http) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
return Err(CommandError(
|
|
format!("Could not get pinned messages! Error: {}", e).to_string(),
|
|
));
|
|
}
|
|
};
|
|
if pinned.is_empty() {
|
|
return Err(CommandError("No pinned messages found!".to_string()));
|
|
}
|
|
if idx > pinned.len() - 1 {
|
|
return Err(CommandError("Index out of bounds!".to_string()));
|
|
}
|
|
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title(format!("Pinned message #{}", idx + 1))
|
|
.description(&pinned[idx].content)
|
|
.timestamp(&pinned[idx].timestamp);
|
|
e.author(|a| {
|
|
a.name(&pinned[idx].author.name)
|
|
.icon_url(&pinned[idx].author.avatar_url().unwrap())
|
|
});
|
|
e.colour(0xffd1dc);
|
|
if !&pinned[idx].attachments.is_empty() {
|
|
e.image(&pinned[idx].attachments[0].url);
|
|
}
|
|
|
|
e
|
|
})
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// brainfuck interpreter
|
|
#[command]
|
|
#[aliases("bf", "brainfrick")]
|
|
fn brainfuck(ctx: &mut Context, message: &Message, args: Args) -> CommandResult {
|
|
use brainfrick::Brainfuck;
|
|
|
|
let input = match args.rest().trim() {
|
|
"" => {
|
|
return Err(CommandError("Called without input!".to_string()));
|
|
}
|
|
v @ _ => v,
|
|
};
|
|
let output = Brainfuck::execute(input);
|
|
|
|
match output {
|
|
Ok(v) => {
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Brainfuck interpreter")
|
|
.description(format!(
|
|
"Input\n```brainfuck\n{}\n```\nOutput:\n```{}\n```",
|
|
input, v
|
|
))
|
|
.author(|a| {
|
|
a.name(&message.author.name)
|
|
.icon_url(message.author.avatar_url().unwrap())
|
|
})
|
|
.colour(0xffd1dc)
|
|
})
|
|
});
|
|
}
|
|
Err(err) => {
|
|
let _ = message.channel_id.send_message(&ctx.http, |m| {
|
|
m.embed(|e| {
|
|
e.title("Brainfuck interpreter")
|
|
.description(format!("Error at:\n```\n{}:{}\n```", err.line(), err.col()))
|
|
.author(|a| {
|
|
a.name(&message.author.name)
|
|
.icon_url(message.author.avatar_url().unwrap())
|
|
})
|
|
.colour(0xff6961)
|
|
})
|
|
});
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|