use chrono::Utc; use crate::core::colours; use crate::core::consts::*; use crate::core::consts::DB as db; use crate::core::model::*; use crate::core::utils::*; // use forecast::Icon::*; // use forecast::Units; // use rand::prelude::*; use serenity::CACHE; use serenity::client::bridge::gateway::ShardId; use serenity::framework::standard::{ Args, Command, CommandError, CommandOptions }; use serenity::model::{ channel::Message, guild::Role, }; use serenity::prelude::{ Context, Mentionable }; // use std::f64::NAN; use std::sync::Arc; use sys_info; use sysinfo::{ ProcessExt, SystemExt, System, get_current_pid }; // TODO: Get member count working. pub struct MemberCount; impl Command for MemberCount { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Wanna check how many cool people are a part of your server?".to_string()), guild_only: true, aliases: vec!["mc"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _ctx: &mut Context, message: &Message, _args: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { let member_count = CACHE.read().guild(guild_id).unwrap().read().member_count; message.channel_id.say(format!( "There are {:?} members in this guild!", member_count))?; } Ok(()) } } pub struct UserId; impl Command for UserId { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Need your ID?".to_string()), usage: Some("[user_resolvable]".to_string()), example: Some("@fun".to_string()), guild_only: true, aliases: vec!["userid", "uid"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { if let Some((id,_)) = parse_user(args.single::().unwrap_or(String::new()), guild_id) { message.channel_id.say(format!("{}", id.0))?; } else { message.channel_id.say("I couldn't find that user.")?; } } Ok(()) } } pub struct PFP; impl Command for PFP { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Need a close up? No creeping! (Defaults to the author of the command.)".to_string()), usage: Some("[user_resolvable]".to_string()), example: Some("@fun".to_string()), guild_only: true, aliases: vec!["avi", "avatar"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _ctx: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { let (user, member) = match parse_user(args.single::().unwrap_or(String::new()), guild_id) { Some((id, member)) => (id.to_user()?, member), None => (message.author.clone(), message.member().ok_or("Failed to get member.")?), }; message.channel_id.send_message(|m| m .embed(|e| e .colour(member.colour().unwrap_or(*colours::MAIN)) .image(user.face()) ))?; } Ok(()) } } pub struct Ping; impl Command for Ping { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("No, I will not play ping-pong with you...".to_string()), ..default }; Arc::new(options) } fn execute(&self, ctx: &mut Context, message: &Message, _: Args) -> Result<(), CommandError> { let data = ctx.data.lock(); let mut lat = 0; if let Some(sm_lock) = data.get::() { let sm = sm_lock.lock(); let runners = sm.runners.lock(); if let Some(shard_runner) = runners.get(&ShardId(ctx.shard_id)) { if let Some(la) = shard_runner.latency { lat = la.as_secs() as u32 + la.subsec_millis(); } } } let mut m = message.channel_id.send_message(|m| m.embed(|e| e.title("Pong!")))?; let t = m.timestamp.timestamp_millis() - message.timestamp.timestamp_millis(); m.edit(|m| m.embed(|e| e .title("Pong!") .description(format!("**Shard Latency:** {}\n**Response Time:** {} ms", if lat==0 { String::from("Failed to retrieve") } else { format!("{} ms", lat) }, t)) .colour(*colours::MAIN) ))?; Ok(()) } } pub struct Prefix; impl Command for Prefix { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Need the prefix of the current guild?".to_string()), guild_only: true, aliases: vec!["pre"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, _: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { if let Ok(settings) = db.get_guild(guild_id.0 as i64) { message.channel_id.say(format!("The prefix for this guild is `{}`", settings.prefix))?; } else { message.channel_id.say("Failed to get guild data.")?; } } Ok(()) } } pub struct Reminder; impl Command for Reminder { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Like a calender, but cooler. (Sent to whatever channel the reminder was created in.)".to_string()), usage: Some(" ".to_string()), example: Some("do the thing /t 1 day 10 min 25 s".to_string()), ..default }; Arc::new(options) } fn execute(&self, ctx: &mut Context, message: &Message, args: Args) -> Result<(), CommandError> { let data = ctx.data.lock(); if let Some(tc_lock) = data.get::() { let tc = tc_lock.lock(); let channel_id = message.channel_id; let user_id = message.author.id; let switches = get_switches(args.rest().to_string()); let reminder = match switches.get("rest") { Some(s) => s.clone(), None => String::new(), }; let start_time = Utc::now().timestamp(); let dur = hrtime_to_seconds(match switches.get("t") { Some(s) => s.clone(), None => String::new(), }); if dur>0 { let end_time = start_time + dur; let reminder_fmt = format!("REMINDER||{}||{}||{}||{}", channel_id.0, user_id.0, dur, reminder); // mut db.new_timer(start_time, end_time, reminder_fmt.clone())?; tc.request(); message.channel_id.say(format!("Got it! I'll remind you to {} in {}", reminder, seconds_to_hrtime(dur as usize) ))?; } else { message.channel_id.say("Sorry, I wasn't able to find a time there. Make sure you to add `/t time_resolvable` after your reminder text.")?; } } else { failed!(TC_FAIL); } Ok(()) } } pub struct RoleInfo; impl Command for RoleInfo { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Mods lacking? Don't worry, I got you.".to_string()), usage: Some("".to_string()), example: Some("@example role".to_string()), guild_only: true, aliases: vec!["roleinfo", "ri", "rinfo"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, args: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { match parse_role(args.rest().to_string(), guild_id) { Some((role_id, role)) => { let role_data = db.get_role(role_id.0 as i64, guild_id.0 as i64).ok(); let mut fields = vec![ ("Name", role.name.clone(), true), ("ID", role_id.0.to_string(), true), ("Hex", format!("#{}", role.colour.hex()), true), ("Hoisted", String::from(if role.hoist { "Yes" } else { "No" }), true), ("Mentionable", String::from(if role.mentionable { "Yes" } else { "No" }), true), ("Position", role.position.to_string(), true), ]; match role_data { Some(r) => { fields.push(("Self Assignable", String::from("Yes"), true)); if !r.aliases.is_empty() { fields.push(("Self Role Aliases", r.aliases.join(", "), true)); } }, None => { fields.push(("Self Assignable", String::from("No"), true)); } } message.channel_id.send_message(|m| m .embed(|e| e .thumbnail(format!("https://www.colorhexa.com/{}.png", role.colour.hex().to_lowercase())) .colour(role.colour) .fields(fields) ))?; }, None => { message.channel_id.say("Unable to find that role.")?; } } } else { failed!(GUILDID_FAIL); } Ok(()) } } pub struct ServerInfo; impl Command for ServerInfo { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Need some quick info about the current guild?".to_string()), guild_only: true, aliases: vec!["serverinfo", "si", "sinfo"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, args: Args) -> Result<(), CommandError> { use serenity::model::channel::ChannelType::*; use serenity::model::user::OnlineStatus::*; let switches = get_switches(args.full().to_string()); let g = match switches.get("rest") { Some(s) => { if let Some((_, lock)) = parse_guild(s.to_string()) { Some(lock) } else { None } }, None => message.guild() }; if let Some(guild_lock) = g { let guild = guild_lock.read().clone(); match switches.get("roles") { None => { let mut channels = (0,0,0); for (_, channel_lock) in guild.channels.iter() { let channel = channel_lock.read(); // mut match channel.kind { Text => { channels.0 += 1; }, Voice => { channels.1 += 1; }, Category => { channels.2 += 1; }, Group => {}, Private => {}, } } let mut members = (0,0,0); for (user_id, _) in guild.members.iter() { match user_id.to_user() { Ok(u) => { if u.bot { members.1 += 1; } else { members.0 += 1; } }, Err(_) => {}, } } for (_, presence) in guild.presences.iter() { match presence.status { DoNotDisturb => { members.2 += 1; }, Idle => { members.2 += 1; }, Invisible => {}, Offline => {}, Online => { members.2 += 1; }, } } message.channel_id.send_message(|m| m .embed(|e| e .thumbnail(guild.icon_url().unwrap_or("https://cdn.discordapp.com/embed/avatars/0.png".to_string())) .color(*colours::MAIN) .field("Name", &guild.name, true) .field("ID", guild.id, true) .field("Owner", guild.owner_id.mention(), true) .field("Region", guild.region, true) .field(format!("Members [{}/{}]", members.2, guild.members.len()), format!("Humans: {}\nBots: {}", members.0, members.1), true) .field(format!("Channels [{}]", guild.channels.len()), format!("Categories: {}\nText: {}\nVoice: {}", channels.2, channels.0, channels.1), true) .field("Roles", guild.roles.len(), true) .field("Emojis", guild.emojis.len(), true) .field("Created", guild.id.created_at().format("%a, %d %h %Y @ %H:%M:%S").to_string(), false) .title(guild.name) ))?; }, Some(_) => { let mut roles_raw = guild.roles.values().collect::>(); roles_raw.sort_by(|a, b| b.position.cmp(&a.position)); let roles = roles_raw.iter().map(|e| e.name.clone()).collect::>(); message.channel_id.send_message(|m| m .embed(|e| e .title(format!("Roles for {}. Count: {}", guild.name, roles.len())) .description(roles.join("\n")) .colour(*colours::BLUE) ))?; }, } } else { message.channel_id.say("Could not find that guild.")?; } Ok(()) } } pub struct Stats; impl Command for Stats { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("You wanna get to know me more? How sweet.".to_string()), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, _: Args) -> Result<(), CommandError> { let (cached_guilds ,cached_channels ,cached_users ,cached_messages) = { let cache = CACHE.read(); (cache.guilds.len() ,cache.channels.len() ,cache.users.len() ,cache.messages.values() .fold(0, |a,m| { a + m.len() })) }; let (db_guilds ,db_users ,db_notes ,db_roles ,db_timers ,db_cases ,db_tags ,db_hackbans ,db_premium) = { (db.count_guilds().unwrap_or(-1) ,db.count_users().unwrap_or(-1) ,db.count_notes().unwrap_or(-1) ,db.count_roles().unwrap_or(-1) ,db.count_timers().unwrap_or(-1) ,db.count_cases().unwrap_or(-1) ,db.count_tags().unwrap_or(-1) ,db.count_hackbans().unwrap_or(-1) ,db.count_premium().unwrap_or(-1) ) }; message.channel_id.send_message(|m| m .embed(|e| e .title("Bot Stats") .field("Cache", format!( "Guilds: {}\nChannels: {}\nUsers: {}\nMessages: {}" ,cached_guilds ,cached_channels ,cached_users ,cached_messages ), false) .field("Database", format!( "Guilds: {}\nUsers: {}\nNotes: {}\nSelf Roles: {}\nTimers: {}\nCases: {}\nTags: {}\nHackbans: {}\nPremium Guilds: {}" ,db_guilds ,db_users ,db_notes ,db_roles ,db_timers ,db_cases ,db_tags ,db_hackbans ,db_premium ), false) .field("More coming soon", "...", false) .colour(*colours::MAIN) .timestamp(now!()) ))?; Ok(()) } } pub struct Time; // Now impl Command for Time { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Don't have a clock? (Optionally, you can provide an amount of hours to offset the time by.".to_string()), usage: Some("[hour]".to_string()), example: Some("-5".to_string()), aliases: vec!["now"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> { use chrono::offset::FixedOffset; let utc = Utc::now(); let datetime = match args.single::() { Ok(data) => { let tz = FixedOffset::east(data * 3600); utc.with_timezone(&tz) }, Err(_) => { let tz = FixedOffset::east(0); utc.with_timezone(&tz) }, }; let time = datetime.format("%H:%M").to_string(); let date = datetime.format("%A %e %B %Y").to_string(); message.channel_id.send_message(|m| m .embed(|e| e .colour(*colours::MAIN) .description(format!("**Time:** {}\n**Date:** {}\n**Timezone:** UTC{}", time, date, datetime.timezone())) ))?; Ok(()) } } pub struct UserInfo; impl Command for UserInfo { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Yea I'll bring something up for you, just don't be a creep. (Defaults to the author of the command.)".to_string()), usage: Some("[user_resolvable]".to_string()), example: Some("@fun".to_string()), guild_only: true, aliases: vec!["userinfo", "ui", "uinfo", "whois"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> { if let Some(guild_id) = message.guild_id { let (user, member) = match parse_user(args.single::().unwrap_or(String::new()), guild_id) { Some((id, member)) => (id.to_user()?, member), None => (message.author.clone(), message.member().ok_or("Failed to get member.")?), }; let user_data = db.get_user(user.id.0 as i64, guild_id.0 as i64)?; let mut roles = member.roles.iter() .map(|c| match c.to_role_cached() { Some(r) => r.name, None => c.0.to_string(), }) .collect::>(); roles.sort(); let roles = if roles.is_empty() { "None".to_string() } else { roles.join(", ") }; let dates = format!( "Created: {}\nJoined: {}{}", user.created_at() .format("%a, %d %h %Y @ %T") .to_string(), member.joined_at .and_then(|t| Some(t.with_timezone(&Utc))) .unwrap_or(Utc::now()) .format("%a, %d %h %Y @ %T") .to_string(), user_data.registered.map_or(String::new(), |r| { format!("\nRegistered: {}", r .format("%a, %d %h %Y @ %T") .to_string()) }) ); message.channel_id.send_message(|m| m .embed(|e| e .colour(member.colour().unwrap_or(*colours::MAIN)) .thumbnail(user.face()) .title(&user.tag()) .field("ID", user.id, true) .field("Mention", user.mention(), true) .field("Nickname", member.display_name().into_owned(), true) .field("Dates", dates, false) .field(format!("Roles [{}]", member.roles.len()), roles, false) ))?; } Ok(()) } } /* pub struct Weather; impl Command for Weather { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { bucket: Some("weather".to_string()), desc: Some("Check on the current weather at a given city. By default this will use the units used at that location, but units can be manually selected. Options are si, us, uk, ca".to_string()), usage: Some(" [/unit]".to_string()), example: Some("london /us".to_string()), ..default }; Arc::new(options) } fn execute(&self, ctx: &mut Context, message: &Message, args: Args) -> Result<(), CommandError> { let data = ctx.data.lock(); if let Some(api) = data.get::() { let switches = get_switches(args.full().to_string()); let rest = switches.get("rest"); // TODO: Refactor this let mut units = Units::Auto; if switches.len() > 1 { switches.keys().for_each(|k| { match k.as_str() { "uk" => { units = Units::UK; }, "c" | "ca" => { units = Units::CA; }, "si" => { units = Units::SI; }, "us" => { units = Units::Imperial; }, _ => {}, } }); } message.channel_id.broadcast_typing()?; if let Some(loc) = rest { match api.weather(loc, units) { Some((city_info, Ok(body))) => { if let Some(current) = body.currently { if let Some(daily_data) = body.daily { let daily = &daily_data.data[0]; let temp = current.temperature.unwrap_or(NAN); let temp_high = current.temperature_high.unwrap_or(daily.temperature_high.unwrap_or(NAN)); let temp_low = current.temperature_low.unwrap_or(daily.temperature_low.unwrap_or(NAN)); let feels_like = current.apparent_temperature.unwrap_or(NAN); let wind = current.wind_speed.unwrap_or(NAN); let visi = current.visibility.unwrap_or(NAN); let pressure = current.pressure.unwrap_or(NAN); let humidity = current.humidity.unwrap_or(NAN)*100.0; let icon = match current.icon { Some(ic) => { match ic { ClearDay => "The sky is clear", ClearNight => "The sky is clear", Rain => "It is raining", Snow => "It is snowing", Sleet => "It is sleeting", Wind => "It is windy", Fog => "It is foggy", Cloudy => "The sky is cloudy", PartlyCloudyDay => "The sky is partly cloudy", PartlyCloudyNight => "The sky is partly cloudy", Hail => "It is hailing", Thunderstorm => "There is a thunderstorm", Tornado => "There is a tornado", } }, None => "The sky is clear", }; let response_units = body.flags.and_then(|e| Some(e.units)).unwrap_or(Units::Imperial); let (temp_unit, speed_unit, dist_unit) = match response_units { Units::SI => { ("C", "m/s", "km") }, Units::CA => { ("C", "kmph", "km") }, Units::UK => { ("C", "mph", "mi") }, _ => { ("F", "mph", "mi") }, }; message.channel_id.send_message(|m| m .embed(|e| e .title(format!("Weather in {}", city_info)) .description(format!("_It is currently **{}°{temp}** with wind of **{} {speed}** making it feel like **{}°{temp}**. {} with a visibility of about **{} {dist}**._", temp, wind, feels_like, icon, visi, temp = temp_unit, speed = speed_unit, dist = dist_unit )) .field("Temperature", format!( "Current: **{}°{temp}**\nLow/High: **{}°{temp} / {}°{temp}**", temp, temp_low, temp_high, temp = temp_unit ), true) .field("Wind Chill", format!( "Feels Like: **{}°{temp}**\nWind Speed: **{} {speed}**", feels_like, wind, temp = temp_unit, speed = speed_unit ), true) .field("Atmosphere", format!( "Humidity: **{}%**\nPressure: **{} mbar**", humidity, pressure, ), true) .colour(*colours::MAIN) .timestamp(now!()) .footer(|f| f.text("Forecast by Dark Sky")) ))?; } } }, Some((_, Err(why))) => { message.channel_id.say(format!("Something went wrong while getting the forecast.\n{}", why))?; }, None => { message.channel_id.say("An error occurred while resolving the location.")?; }, } } else { message.channel_id.say("Please enter a location.")?; } } else { failed!(API_FAIL); } Ok(()) } } */ pub struct PrivacyPolicy; impl Command for PrivacyPolicy { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("View my privacy policy!".to_string()), aliases: vec!["privacy-policy", "privacy"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, _ctx: &mut Context, message: &Message, _args: Args) -> Result<(), CommandError> { use serenity::builder::CreateEmbed; let embed = CreateEmbed::default() .field("Privacy Policy", "**Does Wisp store user/ guild data?**\nYes! We store various pieces of information which is only kept for the lifetime of the command/ feature/ module.\n\n**How can I request to have my data removed?**\nMost of the data that we keep can be retracted by disabling the command/ feature/ module, however, if you want to make *100%* sure that all your data has been erased, join our support server and we will personally make sure that your requests are satisfied.\n\n*In the future, the process of requesting the deletion of your data might be an automated or semi-automated process.*", false) .colour(*colours::MAIN); message.channel_id.send_message(|m| m .embed(|_| embed))?; Ok(()) } } pub struct Wisp; // BotInfo impl Command for Wisp { fn options(&self) -> Arc { let default = CommandOptions::default(); let options = CommandOptions { desc: Some("Information about me!".to_string()), usage: Some("".to_string()), aliases: vec!["botinfo", "bi", "binfo", "bot", "invite", "vote", "topgg", "top.gg"].iter().map(|e| e.to_string()).collect(), ..default }; Arc::new(options) } fn execute(&self, ctx: &mut Context, message: &Message, _: Args) -> Result<(), CommandError> { use serenity::builder::CreateEmbed; let data = ctx.data.lock(); // let (guild_count, shard_count, thumbnail) = { // let cache = CACHE.read(); // (cache.guilds.len(), cache.shard_count, cache.user.face()) // }; let thumbnail = CACHE.read().user.face(); let owner = data.get::().expect("Failed to get owner.").to_user()?; let sys = System::new(); // let mut privacy_policy_message: String = "".to_string(); // if let Some(guild_id) = message.guild_id { // if let Ok(settings) = db.get_guild(guild_id.0 as i64) { // privacy_policy_message = format!("To Wisp's privacy policy, simple run `{}privacypolicy`.", settings.prefix); // } else { // privacy_policy_message = format!("To Wisp's privacy policy, simple run `privacypolicy`."); // } // } let embed = CreateEmbed::default() // .description("Hi! I'm Wisp, a general purpose bot created in [Rust](http://www.rust-lang.org/) using [Serenity](https://github.com/serenity-rs/serenity).") .description("Hey! I'm Wisp, I was written in [Rust](https://www.rust-lang.org/) using [Serenity](https://github.com/serenity-rs/serenity).") .field("Owner/ Developer", format!( "Name: {}\nID: {}" ,owner.mention() ,owner.id) ,true) // .field("Links", format!( // "[Support Server]({})\n[Invite]({})\n[GitLab]({})\n[Patreon]({})" // ,SUPPORT_SERV_INVITE // ,BOT_INVITE // ,GITLAB_LINK // ,PATREON_LINK) // ,true) .field("Useful Links", format!( // "[Support Server]({})\n[Invite]({})\n[GitLab]({})\n[Patreon]({})" "[Support Server]({})\n[GitHub Organization]({})\n[Feature Request]({})\n[Top.gg]({}), [DiscordBots]({}), [Bots On Discord]({})\n[Invite]({})" ,SUPPORT_SERV_INVITE ,GITHUB_ORG_LINK ,FEATURE_REQUEST_LINK ,TOPGG_LINK ,DBOTS_CO_LINK ,BOTSOND_LINK ,BOT_INVITE) // ,GITLAB_LINK // ,PATREON_LINK) ,true) // true // .field("Counts", format!( // "Guilds: {}\nShards: {}" // ,guild_count // ,shard_count) // ,false) .footer(|f| f .text("Want to request a feature? Submit one over at contact@fuwn.me!")) .thumbnail(thumbnail) .colour(*colours::MAIN); if let Some(process) = sys.get_process(get_current_pid()) { message.channel_id.send_message(|m| m .embed(|_| embed .field("System Info", format!( "Type: {} {}\nUptime: {}" ,sys_info::os_type().unwrap_or(String::from("OS Not Found")) ,sys_info::os_release().unwrap_or(String::from("Release Not Found")) ,seconds_to_hrtime(sys.get_uptime() as usize)) ,false) .field("Process Info", format!( "Memory Usage: {} MB\nCPU Usage {}%\nUptime: Temporarily Disabled" ,process.memory()/1000 // convert to MB ,(process.cpu_usage()*100.0).round()/100.0 // round to 2 decimals /* ,seconds_to_hrtime((sys.get_uptime() - process.start_time()) as usize) */) ,true) // .field("Privacy Policy", &privacy_policy_message, false) .field("Privacy Policy", "To Wisp's privacy policy, simple run `privacypolicy`.", false) ))?; } else { message.channel_id.send_message(|m| m .embed(|_| embed // .field("Privacy Policy", &privacy_policy_message, false) .field("Privacy Policy", "To Wisp's privacy policy, simple run the `privacypolicy` command.", false) ))?; } Ok(()) } }