use once_cell::sync::Lazy; use regex::Regex; use crate::utils; #[derive(Debug, Clone, PartialEq)] pub enum StatusCommand { /// Ignore this post Ignore, /// Boost the previous post in the thread Boost, /// Un-reblog parent post, or delete an announcement Undo, /// Admin: Ban a user BanUser(String), /// Admin: Un-ban a server UnbanUser(String), /// Admin: Ban a server BanServer(String), /// Admin: Un-ban a server UnbanServer(String), /// Admin: Add a member to a closed group (or force join) AddMember(String), /// Admin: Remove a user from the group, also unfollow RemoveMember(String), /// Admin: Add a hashtag to the group AddTag(String), /// Admin: Remove a hashtag from the group RemoveTag(String), /// Admin: Give admin to a user GrantAdmin(String), /// Admin: Revoke admin to a user RemoveAdmin(String), /// Admin: Send a public announcement Announce(String), /// Opt out of boosts OptOut, /// Opt in to boosts OptIn, /// Admin: Make the group open-access OpenGroup, /// Admin: Make the group member-only, this effectively disables posting from non-members /// and disables /join and follow-back CloseGroup, /// Show help. The content varies by group params (open/closed access), the user's privileges /// and membership status. Help, /// Show members. Non-admins will only see a list of admins. ListMembers, /// Show tags. ListTags, /// Leave the group, this asks the group to unfollow the user and also revokes their membership. Leave, /// Join a public group. This is normally not needed, as the group follows back and adds followers as members. /// Manual join is useful when the follow somehow fails, or when the user wants to be able to /// post without receiving the group's posts (naughty!) /// /// In member-only groups, this will just DM the user some info on how to get added. Join, /// The group will DM "Pong" back, this is to test that the daemon is running and also that the /// user is not banned and federates. Ping, } macro_rules! p_user { () => { r"(@?[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\.[a-z0-9_-]+|@[a-zA-Z0-9_.-]+)" }; } macro_rules! p_server { () => { r"([a-zA-Z0-9_.-]+\.[a-zA-Z0-9_-]+)" }; } macro_rules! p_hashtag { () => { r"#(\w+)" }; } macro_rules! command { ($($val:expr),+) => { Regex::new(concat!(r"(?:^|\s|>|\n)[\\/]", $($val,)+ r"(?:$|[!,]|\W)")).unwrap() } } static RE_BOOST: once_cell::sync::Lazy = Lazy::new(|| command!(r"b(?:oost)?")); static RE_UNDO: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:delete|undo)")); static RE_IGNORE: once_cell::sync::Lazy = Lazy::new(|| command!(r"i(?:g(?:n(?:ore)?)?)?")); static RE_BAN_USER: once_cell::sync::Lazy = Lazy::new(|| command!(r"ban\s+", p_user!())); static RE_UNBAN_USER: once_cell::sync::Lazy = Lazy::new(|| command!(r"unban\s+", p_user!())); static RE_BAN_SERVER: once_cell::sync::Lazy = Lazy::new(|| command!(r"ban\s+", p_server!())); static RE_UNBAN_SERVER: once_cell::sync::Lazy = Lazy::new(|| command!(r"unban\s+", p_server!())); static RE_ADD_MEMBER: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:add|follow)\s+", p_user!())); static RE_REMOVE_MEMBER: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:kick|unfollow|remove)\s+", p_user!())); static RE_ADD_TAG: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:add|follow)\s+", p_hashtag!())); static RE_REMOVE_TAG: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:remove|unfollow)\s+", p_hashtag!())); static RE_GRANT_ADMIN: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:op|admin)\s+", p_user!())); static RE_REVOKE_ADMIN: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:deop|deadmin)\s+", p_user!())); static RE_OPEN_GROUP: once_cell::sync::Lazy = Lazy::new(|| command!(r"opengroup")); static RE_CLOSE_GROUP: once_cell::sync::Lazy = Lazy::new(|| command!(r"closegroup")); static RE_HELP: once_cell::sync::Lazy = Lazy::new(|| command!(r"help")); static RE_MEMBERS: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:members|who|admins)")); static RE_TAGS: once_cell::sync::Lazy = Lazy::new(|| command!(r"(?:hashtags|tags)")); static RE_LEAVE: once_cell::sync::Lazy = Lazy::new(|| command!(r"leave")); static RE_JOIN: once_cell::sync::Lazy = Lazy::new(|| command!(r"join")); static RE_OPTOUT: once_cell::sync::Lazy = Lazy::new(|| command!(r"optout")); static RE_OPTIN: once_cell::sync::Lazy = Lazy::new(|| command!(r"optin")); static RE_PING: once_cell::sync::Lazy = Lazy::new(|| command!(r"ping")); static RE_ANNOUNCE: once_cell::sync::Lazy = Lazy::new(|| Regex::new(r"(?:^|\s|>|\n)[\\/]announce\s+(.*)$").unwrap()); static RE_A_HASHTAG: once_cell::sync::Lazy = Lazy::new(|| Regex::new(r"(?:^|\b|\s|>|\n)#(\w+)").unwrap()); pub static RE_NOBOT_TAG: once_cell::sync::Lazy = Lazy::new(|| Regex::new(r"(?:^|\b|\s|>|\n)#nobot(?:\b|$)").unwrap()); pub static RE_HASHTAG_TRIGGERING_PLEROMA_BUG: once_cell::sync::Lazy = Lazy::new(|| Regex::new(r"(?:^|\b|\s|>|\n)#\w+[^\s]*$").unwrap()); pub fn parse_status_tags(content: &str) -> Vec { debug!("Raw content: {}", content); let content = utils::strip_html(content); debug!("Stripped tags: {}", content); let mut tags = vec![]; for c in RE_A_HASHTAG.captures_iter(&content) { if let Some(s) = c.get(1) { tags.push(s.as_str().to_lowercase()) } } tags } pub fn parse_slash_commands(content: &str) -> Vec { debug!("Raw content: {}", content); let content = utils::strip_html(content); debug!("Stripped tags: {}", content); if !content.contains('/') && !content.contains('\\') { // No slash = no command return vec![]; } // short-circuiting commands if RE_IGNORE.is_match(&content) { debug!("IGNORE"); return vec![StatusCommand::Ignore]; } if RE_HELP.is_match(&content) { debug!("HELP"); return vec![StatusCommand::Help]; } if RE_UNDO.is_match(&content) { debug!("UNDO"); return vec![StatusCommand::Undo]; } // additive commands let mut commands = vec![]; // one-use commands if RE_BOOST.is_match(&content) { debug!("BOOST"); commands.push(StatusCommand::Boost); } if RE_LEAVE.is_match(&content) { debug!("LEAVE"); commands.push(StatusCommand::Leave); } else if RE_JOIN.is_match(&content) { debug!("JOIN"); commands.push(StatusCommand::Join); } if RE_OPTOUT.is_match(&content) { debug!("OPT-OUT"); commands.push(StatusCommand::OptOut); } else if RE_OPTIN.is_match(&content) { debug!("OPT-IN"); commands.push(StatusCommand::OptIn); } if RE_PING.is_match(&content) { debug!("PING"); commands.push(StatusCommand::Ping); } if RE_MEMBERS.is_match(&content) { debug!("MEMBERS"); commands.push(StatusCommand::ListMembers); } if RE_TAGS.is_match(&content) { debug!("TAGS"); commands.push(StatusCommand::ListTags); } if RE_OPEN_GROUP.is_match(&content) { debug!("OPEN GROUP"); commands.push(StatusCommand::OpenGroup); } if RE_CLOSE_GROUP.is_match(&content) { debug!("CLOSE GROUP"); commands.push(StatusCommand::CloseGroup); } if let Some(c) = RE_ANNOUNCE.captures(&content) { if let Some(s) = c.get(1) { let s = s.as_str().trim(); debug!("ANNOUNCE: «{}»", s); commands.push(StatusCommand::Announce(s.to_owned())); } } // multi-occurence commands for c in RE_BAN_USER.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("BAN USER: {}", s); commands.push(StatusCommand::BanUser(s.to_lowercase())); } } for c in RE_UNBAN_USER.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("UNBAN USER: {}", s); commands.push(StatusCommand::UnbanUser(s.to_lowercase())); } } for c in RE_BAN_SERVER.captures_iter(&content) { if let Some(s) = c.get(1) { debug!("BAN SERVER: {}", s.as_str()); commands.push(StatusCommand::BanServer(s.as_str().to_lowercase())); } } for c in RE_UNBAN_SERVER.captures_iter(&content) { if let Some(s) = c.get(1) { debug!("UNBAN SERVER: {}", s.as_str()); commands.push(StatusCommand::UnbanServer(s.as_str().to_lowercase())); } } for c in RE_ADD_MEMBER.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("ADD MEMBER: {}", s); commands.push(StatusCommand::AddMember(s.to_lowercase())); } } for c in RE_REMOVE_MEMBER.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("REMOVE USER: {}", s); commands.push(StatusCommand::RemoveMember(s.to_lowercase())); } } for c in RE_ADD_TAG.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); debug!("ADD TAG: {}", s); commands.push(StatusCommand::AddTag(s.to_lowercase())); } } for c in RE_REMOVE_TAG.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); debug!("REMOVE TAG: {}", s); commands.push(StatusCommand::RemoveTag(s.to_lowercase())); } } for c in RE_GRANT_ADMIN.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("ADD ADMIN: {}", s); commands.push(StatusCommand::GrantAdmin(s.to_lowercase())); } } for c in RE_REVOKE_ADMIN.captures_iter(&content) { if let Some(s) = c.get(1) { let s = s.as_str(); let s = s.trim_start_matches('@'); debug!("REMOVE ADMIN: {}", s); commands.push(StatusCommand::RemoveAdmin(s.to_lowercase())); } } commands } #[cfg(test)] mod test { use crate::command::{parse_slash_commands, RE_A_HASHTAG, RE_HASHTAG_TRIGGERING_PLEROMA_BUG, RE_NOBOT_TAG, RE_ADD_TAG, RE_JOIN, StatusCommand}; use super::{ RE_ADD_MEMBER, RE_ANNOUNCE, RE_BAN_SERVER, RE_BAN_USER, RE_BOOST, RE_CLOSE_GROUP, RE_GRANT_ADMIN, RE_HELP, RE_IGNORE, RE_LEAVE, RE_OPTOUT, RE_OPTIN, RE_MEMBERS, RE_OPEN_GROUP, RE_REMOVE_MEMBER, RE_REVOKE_ADMIN, RE_TAGS, RE_UNDO, }; #[test] fn test_boost() { assert!(RE_BOOST.is_match("/b")); assert!(RE_BOOST.is_match(">/b")); assert!(RE_BOOST.is_match("/b mm")); assert!(RE_BOOST.is_match("/b.")); assert!(RE_BOOST.is_match("\\b")); assert!(!RE_BOOST.is_match("boo/b")); assert!(RE_BOOST.is_match("bla\n/b")); assert!(RE_BOOST.is_match("/boost")); assert!(RE_BOOST.is_match("/boost\n")); assert!(RE_BOOST.is_match("/boost dfdfg")); assert!(!RE_BOOST.is_match("/boosty")); assert!(RE_BOOST.is_match("/b\nxxx")); assert!(!RE_BOOST.is_match("/bleble\n")); } #[test] fn test_ignore() { assert!(RE_IGNORE.is_match("/i")); assert!(RE_IGNORE.is_match("/ig")); assert!(RE_IGNORE.is_match("/ign")); assert!(RE_IGNORE.is_match("/i mm")); assert!(RE_IGNORE.is_match("/i.")); assert!(RE_IGNORE.is_match("\\i")); assert!(!RE_IGNORE.is_match("boo/i")); assert!(RE_IGNORE.is_match("bla\n/i")); assert!(RE_IGNORE.is_match("/ignore")); assert!(RE_IGNORE.is_match("/ignore x")); assert!(RE_IGNORE.is_match("/ignore\n")); assert!(RE_IGNORE.is_match("/ignore dfdfg")); assert!(!RE_IGNORE.is_match("/ignorey")); assert!(RE_IGNORE.is_match("/i\nxxx")); assert!(!RE_IGNORE.is_match("/ileble\n")); assert!(!RE_IGNORE.is_match("/ignx")); } #[test] fn test_ban_user() { assert!(RE_BAN_USER.is_match("/ban lain@pleroma.soykaf.com")); assert!(RE_BAN_USER.is_match("/ban lain@stupidname.uk")); assert!(RE_BAN_USER.is_match("bababababa /ban lain@stupidname.uk lala")); assert!(!RE_BAN_USER.is_match("/ban stupidname.uk")); assert!(RE_BAN_USER.is_match("/ban @lain")); assert!(RE_BAN_USER.is_match("/ban @lain aaa")); assert!(RE_BAN_USER.is_match("/ban \t lain@pleroma.soykaf.com")); assert!(RE_BAN_USER.is_match("/ban @lain@pleroma.soykaf.com")); assert!(RE_BAN_USER.is_match("/ban @l-a_i.n9@xn--999pleroma-weirdname.com")); assert!(RE_BAN_USER.is_match("/ban @LAIN@PleromA.soykaf.com")); let c = RE_BAN_USER.captures("/ban lain@pleroma.soykaf.com"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "lain@pleroma.soykaf.com"); let c = RE_BAN_USER.captures("/ban lain@pleroma.soykaf.com xx"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "lain@pleroma.soykaf.com"); let c = RE_BAN_USER.captures("/ban @lain@pleroma.soykaf.com"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain@pleroma.soykaf.com"); let c = RE_BAN_USER.captures("/ban @lain"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain"); let c = RE_BAN_USER.captures("/ban @lain xx"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain"); } #[test] fn test_ban_server() { assert!(!RE_BAN_SERVER.is_match("/ban lain@pleroma.soykaf.com")); assert!(RE_BAN_SERVER.is_match("/ban pleroma.soykaf.com")); assert!(RE_BAN_SERVER.is_match("/ban xn--999pleroma-weirdname.com")); assert!(RE_BAN_SERVER.is_match("/ban \t xn--999pleroma-weirdname.com")); assert!(RE_BAN_SERVER.is_match("mamama /ban pleroma.soykaf.com momomo")); assert!(!RE_BAN_SERVER.is_match("/ban @pleroma.soykaf.com")); let c = RE_BAN_SERVER.captures("/ban pleroma.soykaf.com"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "pleroma.soykaf.com"); let c = RE_BAN_SERVER.captures("/ban pleroma.soykaf.com xx"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "pleroma.soykaf.com"); } #[test] fn test_add_member() { assert!(RE_ADD_MEMBER.is_match("/add lain@pleroma.soykaf.com")); assert!(RE_ADD_MEMBER.is_match("/add @lain@pleroma.soykaf.com")); assert!(RE_ADD_MEMBER.is_match("/follow @lain@pleroma.soykaf.com")); assert!(RE_ADD_MEMBER.is_match("\\add @lain")); let c = RE_ADD_MEMBER.captures("/add @lain"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain"); } #[test] fn test_remove_member() { assert!(!RE_REMOVE_MEMBER.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_REMOVE_MEMBER.is_match("/remove lain@pleroma.soykaf.com")); assert!(RE_REMOVE_MEMBER.is_match("/remove @lain@pleroma.soykaf.com")); assert!(RE_REMOVE_MEMBER.is_match("\\remove @lain")); assert!(RE_REMOVE_MEMBER.is_match("/kick @lain")); assert!(RE_REMOVE_MEMBER.is_match("/remove @lain")); let c = RE_REMOVE_MEMBER.captures("/kick lain@pleroma.soykaf.com"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "lain@pleroma.soykaf.com"); } #[test] fn test_add_tag() { assert!(RE_ADD_TAG.is_match("/add #breadposting")); assert!(RE_ADD_TAG.is_match("/add #čučkaři")); assert!(RE_ADD_TAG.is_match("/add #χαλβάς")); assert!(RE_ADD_TAG.is_match("\\add #ласточка")); assert!(RE_ADD_TAG.is_match("/add #nya.")); assert!(RE_ADD_TAG.is_match("/add #nya)")); assert!(RE_ADD_TAG.is_match("/follow #nya)")); assert!(RE_ADD_TAG.is_match("/add #nya and more)")); let c = RE_ADD_TAG.captures("/add #breadposting"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "breadposting"); let c = RE_ADD_TAG.captures("/add #χαλβάς"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "χαλβάς"); let c = RE_ADD_TAG.captures("/add #ласточка"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "ласточка"); let c = RE_ADD_TAG.captures("/add #nya."); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "nya"); let c = RE_ADD_TAG.captures("/add #nya)"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "nya"); } #[test] fn test_add_admin() { assert!(!RE_GRANT_ADMIN.is_match("/expel lain@pleroma.soykaf.com")); assert!(RE_GRANT_ADMIN.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_GRANT_ADMIN.is_match("/op @lain@pleroma.soykaf.com")); assert!(RE_GRANT_ADMIN.is_match("\\op @lain")); let c = RE_GRANT_ADMIN.captures("/op @lain@pleroma.soykaf.com"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain@pleroma.soykaf.com"); } #[test] fn test_remove_admin() { assert!(!RE_REVOKE_ADMIN.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_REVOKE_ADMIN.is_match("/deop @lain")); assert!(RE_REVOKE_ADMIN.is_match("/deadmin @lain")); let c = RE_REVOKE_ADMIN.captures("/deadmin @lain"); assert_eq!(c.unwrap().get(1).unwrap().as_str(), "@lain"); } #[test] fn test_opengroup() { assert!(!RE_OPEN_GROUP.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_OPEN_GROUP.is_match("/opengroup")); assert!(RE_OPEN_GROUP.is_match("x /opengroup")); assert!(RE_OPEN_GROUP.is_match("/opengroup dfgdfg")); assert!(RE_OPEN_GROUP.is_match("\n\n/opengroup\n dfgdfg\n\n")); } #[test] fn test_closegroup() { assert!(!RE_CLOSE_GROUP.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_CLOSE_GROUP.is_match("/closegroup")); assert!(RE_CLOSE_GROUP.is_match("x /closegroup")); assert!(RE_CLOSE_GROUP.is_match("/closegroup dfgdfg")); assert!(RE_CLOSE_GROUP.is_match("\n\n/closegroup\n dfgdfg\n\n")); } #[test] fn test_help() { assert!(!RE_HELP.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_HELP.is_match("/help")); assert!(!RE_HELP.is_match("/helpx")); assert!(!RE_HELP.is_match("a/help")); assert!(!RE_HELP.is_match("help")); assert!(RE_HELP.is_match("x /help")); assert!(RE_HELP.is_match("/help dfgdfg")); assert!(RE_HELP.is_match("\n\n/help\n dfgdfg\n\n")); } #[test] fn test_members() { assert!(!RE_MEMBERS.is_match("/admin lain@pleroma.soykaf.com")); assert!(RE_MEMBERS.is_match("/members")); assert!(RE_MEMBERS.is_match("/who")); assert!(RE_MEMBERS.is_match("/admins")); } #[test] fn test_tags() { assert!(!RE_TAGS.is_match("/members")); assert!(RE_TAGS.is_match("/hashtags")); assert!(RE_TAGS.is_match("dsfsd /tags dfgd d")); } #[test] fn test_match_tag() { assert!(!RE_A_HASHTAG.is_match("banana sdfsdf sdfsd fdsf sd")); assert!(RE_A_HASHTAG.is_match("#banana")); assert!(RE_A_HASHTAG.is_match("#ласточка")); assert!(RE_A_HASHTAG.is_match("#χαλβάς")); assert!(RE_A_HASHTAG.is_match("foo #banana gfdfgd")); for (i, c) in RE_A_HASHTAG.captures_iter("foo #banana #χαλβάς #ласточка").enumerate() { if i == 0 { assert_eq!(c.get(1).unwrap().as_str(), "banana"); } else if i == 1 { assert_eq!(c.get(1).unwrap().as_str(), "χαλβάς"); } else if i == 2 { assert_eq!(c.get(1).unwrap().as_str(), "ласточка"); } } } #[test] fn test_match_tag_at_end() { assert!(!RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("banana #tag sdfsd")); assert!(!RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("banana #tag .")); assert!(RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("banana #tag")); assert!(RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("banana #tag.")); assert!(RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("banana #tag...")); assert!(RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match("#tag...")); } #[test] fn test_match_tag_nobot() { assert!(!RE_NOBOT_TAG.is_match("banana #tag sdfsd")); assert!(!RE_NOBOT_TAG.is_match("banana #nobotanicals sdfsd")); assert!(RE_NOBOT_TAG.is_match("#nobot")); assert!(RE_NOBOT_TAG.is_match("aaa#nobot")); assert!(RE_NOBOT_TAG.is_match("aaa #nobot")); assert!(RE_NOBOT_TAG.is_match("#nobot xxx")); assert!(RE_NOBOT_TAG.is_match("#nobot\nxxx")); } #[test] fn test_leave() { assert!(!RE_LEAVE.is_match("/list")); assert!(RE_LEAVE.is_match("/leave")); assert!(RE_LEAVE.is_match("/leave")); assert!(RE_LEAVE.is_match("x /leave")); assert!(RE_LEAVE.is_match("/leave z")); } #[test] fn test_optout() { assert!(!RE_OPTOUT.is_match("/list")); assert!(!RE_OPTOUT.is_match("/optoutaaa")); assert!(RE_OPTOUT.is_match("/optout")); assert!(RE_OPTOUT.is_match("x /optout")); assert!(RE_OPTOUT.is_match("/optout z")); } #[test] fn test_optin() { assert!(!RE_OPTIN.is_match("/list")); assert!(!RE_OPTIN.is_match("/optinaaa")); assert!(RE_OPTIN.is_match("/optin")); assert!(RE_OPTIN.is_match("x /optin")); assert!(RE_OPTIN.is_match("/optin z")); } #[test] fn test_undo() { assert!(!RE_UNDO.is_match("/list")); assert!(RE_UNDO.is_match("/undo")); assert!(RE_UNDO.is_match("/delete")); assert!(RE_UNDO.is_match("/undo")); assert!(RE_UNDO.is_match("x /undo")); assert!(RE_UNDO.is_match("/undo z")); } #[test] fn test_join() { assert!(!RE_JOIN.is_match("/list")); assert!(RE_JOIN.is_match("/join")); assert!(RE_JOIN.is_match("/join")); assert!(RE_JOIN.is_match("x /join")); assert!(RE_JOIN.is_match("/join z")); } #[test] fn test_announce() { assert!(!RE_ANNOUNCE.is_match("/list")); assert!(RE_ANNOUNCE.is_match("sdfsdffsd /announce b")); assert!(RE_ANNOUNCE.is_match("/announce bla bla bla")); assert!(RE_ANNOUNCE.is_match("sdfsdffsd /announce bla bla bla")); assert_eq!( "bla bla bla", RE_ANNOUNCE .captures("sdfsdffsd /announce bla bla bla") .unwrap() .get(1) .unwrap() .as_str() ); } #[test] fn test_real_post() { assert_eq!( Vec::::new(), parse_slash_commands("Hello there is nothing here /fake command") ); assert_eq!( vec![StatusCommand::Help], parse_slash_commands("lets see some \\help and /ban @lain") ); assert_eq!( vec![StatusCommand::Ignore], parse_slash_commands("lets see some /ignore and /ban @lain") ); assert_eq!( vec![ StatusCommand::BanUser("lain".to_string()), StatusCommand::BanUser("piggo@piggo.space".to_string()), StatusCommand::BanServer("soykaf.com".to_string()), ], parse_slash_commands("let's /ban @lain! /ban @piggo@piggo.space and also /ban soykaf.com") ); } #[test] fn test_strip() { assert_eq!( vec![StatusCommand::BanUser("betty".to_string())], parse_slash_commands( r#"Let's bad the naughty bot: /ban @betty"# ) ); assert_eq!( vec![StatusCommand::BanUser("betty@abstarbauze.com".to_string())], parse_slash_commands( r#"Let's bad the naughty bot: /ban @betty@abstarbauze.com"# ) ); } }