2021-10-05 08:39:10 +00:00
use std ::cmp ::Ordering ;
2021-08-26 20:11:20 +00:00
use std ::collections ::HashSet ;
use std ::time ::Duration ;
2021-08-30 18:03:03 +00:00
use elefren ::entities ::account ::Account ;
2021-08-26 20:11:20 +00:00
use elefren ::entities ::prelude ::Status ;
use elefren ::status_builder ::Visibility ;
2021-11-02 22:49:00 +00:00
use elefren ::{ FediClient , SearchType , StatusBuilder } ;
2021-08-26 20:11:20 +00:00
2021-11-02 22:49:00 +00:00
use crate ::command ::{ StatusCommand , RE_NOBOT_TAG } ;
2021-08-26 20:11:20 +00:00
use crate ::error ::GroupError ;
use crate ::group_handler ::GroupHandle ;
2021-10-12 08:38:02 +00:00
use crate ::store ::group_config ::GroupConfig ;
2021-11-02 22:49:00 +00:00
use crate ::store ::CommonConfig ;
2021-10-10 13:38:56 +00:00
use crate ::tr ::TranslationTable ;
2021-08-30 18:03:03 +00:00
use crate ::utils ;
2021-11-02 22:49:00 +00:00
use crate ::utils ::{ normalize_acct , LogError , VisExt } ;
2021-10-12 08:38:02 +00:00
2021-11-02 22:49:00 +00:00
use crate ::{ grp_debug , grp_info , grp_warn } ;
2021-08-26 20:11:20 +00:00
pub struct ProcessMention < ' a > {
status : Status ,
2021-08-27 19:52:33 +00:00
group_account : & ' a Account ,
2021-08-26 20:11:20 +00:00
config : & ' a mut GroupConfig ,
2021-10-05 08:39:10 +00:00
cc : & ' a CommonConfig ,
2021-08-26 20:11:20 +00:00
client : & ' a mut FediClient ,
group_acct : String ,
status_acct : String ,
status_user_id : String ,
can_write : bool ,
is_admin : bool ,
2021-10-09 19:49:26 +00:00
replies : String ,
announcements : String ,
2021-08-26 20:11:20 +00:00
do_boost_prev_post : bool ,
}
impl < ' a > ProcessMention < ' a > {
2021-10-05 23:09:20 +00:00
fn tr ( & self ) -> & TranslationTable {
2021-10-11 11:24:17 +00:00
self . config . tr ( )
2021-10-05 23:09:20 +00:00
}
2021-08-26 20:11:20 +00:00
async fn lookup_acct_id ( & self , acct : & str , followed : bool ) -> Result < Option < String > , GroupError > {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Looking up user ID by acct: {} " , acct ) ;
2021-08-26 20:11:20 +00:00
2021-10-05 08:39:10 +00:00
match tokio ::time ::timeout (
Duration ::from_secs ( 5 ) ,
self . client
. search_v2 ( acct , ! followed , Some ( SearchType ::Accounts ) , Some ( 1 ) , followed ) ,
)
2021-11-02 22:49:00 +00:00
. await
2021-10-05 08:39:10 +00:00
{
2021-08-26 20:11:20 +00:00
Err ( _ ) = > {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Account lookup timeout! " ) ;
2021-08-26 20:11:20 +00:00
Err ( GroupError ::ApiTimeout )
}
Ok ( Err ( e ) ) = > {
// Elefren error
Err ( e . into ( ) )
}
Ok ( Ok ( res ) ) = > {
2021-08-26 22:43:06 +00:00
for item in res . accounts {
2021-10-05 08:39:10 +00:00
// XXX limit is 1!
2021-08-26 22:19:43 +00:00
let acct_normalized = normalize_acct ( & item . acct , & self . group_acct ) ? ;
if acct_normalized = = acct {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Search done, account found: {} " , item . acct ) ;
2021-08-30 18:03:03 +00:00
return Ok ( Some ( item . id ) ) ;
2021-08-26 22:19:43 +00:00
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Found wrong account: {} " , item . acct ) ;
2021-08-26 22:19:43 +00:00
}
2021-08-26 20:11:20 +00:00
}
2021-08-26 22:43:06 +00:00
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Search done, nothing found " ) ;
2021-08-26 22:43:06 +00:00
Ok ( None )
2021-08-26 20:11:20 +00:00
}
}
}
fn append_admin_list_to_reply ( & mut self ) {
let mut admins = self . config . get_admins ( ) . collect ::< Vec < _ > > ( ) ;
admins . sort ( ) ;
2021-10-09 19:49:26 +00:00
let mut to_add = String ::new ( ) ;
2021-08-26 20:11:20 +00:00
for a in admins {
2021-10-09 19:49:26 +00:00
to_add . push_str ( & crate ::tr! ( self , " user_list_entry " , user = a ) ) ;
2021-08-26 20:11:20 +00:00
}
2021-10-09 19:49:26 +00:00
self . add_reply ( & to_add ) ;
2021-08-26 20:11:20 +00:00
}
fn append_member_list_to_reply ( & mut self ) {
let admins = self . config . get_admins ( ) . collect ::< HashSet < _ > > ( ) ;
let mut members = self . config . get_members ( ) . collect ::< Vec < _ > > ( ) ;
members . extend ( admins . iter ( ) ) ;
members . sort ( ) ;
members . dedup ( ) ;
2021-10-09 19:49:26 +00:00
let mut to_add = String ::new ( ) ;
2021-08-26 20:11:20 +00:00
for m in members {
2021-10-09 19:49:26 +00:00
to_add . push_str ( & if admins . contains ( & m ) {
2021-11-02 22:49:00 +00:00
crate ::tr! ( self , " user_list_entry_admin " , user = m )
2021-08-26 20:11:20 +00:00
} else {
2021-11-02 22:49:00 +00:00
crate ::tr! ( self , " user_list_entry " , user = m )
2021-08-26 20:11:20 +00:00
} ) ;
}
2021-10-09 19:49:26 +00:00
self . add_reply ( & to_add ) ;
2021-08-26 20:11:20 +00:00
}
2021-08-26 22:19:43 +00:00
async fn follow_user_by_id ( & self , id : & str ) -> Result < ( ) , GroupError > {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Trying to follow user #{} " , id ) ;
2021-08-26 20:11:20 +00:00
self . client . follow ( id ) . await ? ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-08-26 20:11:20 +00:00
Ok ( ( ) )
}
2021-08-26 22:19:43 +00:00
async fn unfollow_user_by_id ( & self , id : & str ) -> Result < ( ) , GroupError > {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Trying to unfollow user #{} " , id ) ;
2021-08-26 20:11:20 +00:00
self . client . unfollow ( id ) . await ? ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-08-26 20:11:20 +00:00
Ok ( ( ) )
}
pub ( crate ) async fn run ( gh : & ' a mut GroupHandle , status : Status ) -> Result < ( ) , GroupError > {
let group_acct = gh . config . get_acct ( ) . to_string ( ) ;
let status_acct = normalize_acct ( & status . account . acct , & group_acct ) ? . to_string ( ) ;
if gh . config . is_banned ( & status_acct ) {
2021-10-12 08:38:02 +00:00
grp_warn! ( gh , " Status author {} is banned! " , status_acct ) ;
2021-08-26 20:11:20 +00:00
return Ok ( ( ) ) ;
}
let pm = Self {
2021-08-27 19:52:33 +00:00
group_account : & gh . group_account ,
2021-08-26 20:11:20 +00:00
status_user_id : status . account . id . to_string ( ) ,
client : & mut gh . client ,
2021-10-05 08:39:10 +00:00
cc : & gh . cc ,
2021-08-26 20:11:20 +00:00
can_write : gh . config . can_write ( & status_acct ) ,
is_admin : gh . config . is_admin ( & status_acct ) ,
2021-10-09 19:49:26 +00:00
replies : String ::new ( ) ,
announcements : String ::new ( ) ,
2021-08-26 20:11:20 +00:00
do_boost_prev_post : false ,
group_acct ,
status_acct ,
status ,
config : & mut gh . config ,
} ;
pm . handle ( ) . await
}
async fn reblog_status ( & self ) {
2021-11-02 22:49:00 +00:00
self . client . reblog ( & self . status . id ) . await . log_error ( " Failed to reblog status " ) ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-08-26 20:11:20 +00:00
}
2021-08-28 08:24:35 +00:00
fn add_reply ( & mut self , line : impl AsRef < str > ) {
2021-10-09 19:49:26 +00:00
self . replies . push_str ( line . as_ref ( ) )
2021-08-26 20:11:20 +00:00
}
2021-10-05 08:39:10 +00:00
fn add_announcement ( & mut self , line : impl AsRef < str > ) {
2021-10-09 19:49:26 +00:00
self . announcements . push_str ( line . as_ref ( ) )
2021-08-26 20:11:20 +00:00
}
async fn handle ( mut self ) -> Result < ( ) , GroupError > {
let commands = crate ::command ::parse_slash_commands ( & self . status . content ) ;
if commands . is_empty ( ) {
self . handle_post_with_no_commands ( ) . await ;
} else {
if commands . contains ( & StatusCommand ::Ignore ) {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Notif ignored because of ignore command " ) ;
2021-08-26 20:11:20 +00:00
return Ok ( ( ) ) ;
}
for cmd in commands {
2021-10-09 19:49:26 +00:00
if ! self . replies . is_empty ( ) {
self . replies . push ( '\n' ) ; // make sure there's a newline between batched commands.
}
2021-08-26 20:11:20 +00:00
match cmd {
2021-08-27 19:52:33 +00:00
StatusCommand ::Undo = > {
2021-10-05 08:39:10 +00:00
self . cmd_undo ( ) . await . log_error ( " Error handling undo cmd " ) ;
2021-08-27 19:52:33 +00:00
}
2021-08-26 20:11:20 +00:00
StatusCommand ::Ignore = > {
unreachable! ( ) ; // Handled above
}
StatusCommand ::Announce ( a ) = > {
self . cmd_announce ( a ) . await ;
}
StatusCommand ::Boost = > {
self . cmd_boost ( ) . await ;
}
StatusCommand ::BanUser ( u ) = > {
2021-10-05 08:39:10 +00:00
self . cmd_ban_user ( & u ) . await . log_error ( " Error handling ban-user cmd " ) ;
2021-08-26 20:11:20 +00:00
}
StatusCommand ::UnbanUser ( u ) = > {
2021-10-05 08:39:10 +00:00
self . cmd_unban_user ( & u ) . await . log_error ( " Error handling unban-user cmd " ) ;
2021-08-26 20:11:20 +00:00
}
2021-08-30 18:03:03 +00:00
StatusCommand ::OptOut = > {
self . cmd_optout ( ) . await ;
}
StatusCommand ::OptIn = > {
self . cmd_optin ( ) . await ;
}
2021-08-26 20:11:20 +00:00
StatusCommand ::BanServer ( s ) = > {
self . cmd_ban_server ( & s ) . await ;
}
StatusCommand ::UnbanServer ( s ) = > {
self . cmd_unban_server ( & s ) . await ;
}
StatusCommand ::AddMember ( u ) = > {
2021-10-05 08:39:10 +00:00
self . cmd_add_member ( & u ) . await . log_error ( " Error handling add-member cmd " ) ;
2021-08-26 20:11:20 +00:00
}
StatusCommand ::RemoveMember ( u ) = > {
2021-10-05 08:39:10 +00:00
self . cmd_remove_member ( & u ) . await . log_error ( " Error handling remove-member cmd " ) ;
2021-08-26 20:11:20 +00:00
}
StatusCommand ::AddTag ( tag ) = > {
self . cmd_add_tag ( tag ) . await ;
}
StatusCommand ::RemoveTag ( tag ) = > {
self . cmd_remove_tag ( tag ) . await ;
}
StatusCommand ::GrantAdmin ( u ) = > {
2021-10-05 08:39:10 +00:00
self . cmd_grant_admin ( & u ) . await . log_error ( " Error handling grant-admin cmd " ) ;
2021-08-26 20:11:20 +00:00
}
StatusCommand ::RemoveAdmin ( u ) = > {
2021-10-09 19:49:26 +00:00
self . cmd_revoke_admin ( & u ) . await . log_error ( " Error handling grant-admin cmd " ) ;
2021-08-26 20:11:20 +00:00
}
StatusCommand ::OpenGroup = > {
self . cmd_open_group ( ) . await ;
}
StatusCommand ::CloseGroup = > {
self . cmd_close_group ( ) . await ;
}
StatusCommand ::Help = > {
self . cmd_help ( ) . await ;
}
StatusCommand ::ListMembers = > {
self . cmd_list_members ( ) . await ;
}
StatusCommand ::ListTags = > {
self . cmd_list_tags ( ) . await ;
}
StatusCommand ::Leave = > {
self . cmd_leave ( ) . await ;
}
StatusCommand ::Join = > {
self . cmd_join ( ) . await ;
}
StatusCommand ::Ping = > {
self . cmd_ping ( ) . await ;
}
}
}
}
if self . do_boost_prev_post {
2021-10-05 08:39:10 +00:00
if let ( Some ( prev_acct_id ) , Some ( prev_status_id ) ) = (
self . status . in_reply_to_account_id . as_ref ( ) ,
self . status . in_reply_to_id . as_ref ( ) ,
) {
2021-08-30 18:03:03 +00:00
match self . id_to_acct_check_boostable ( prev_acct_id ) . await {
Ok ( _acct ) = > {
2021-10-05 08:39:10 +00:00
self . client . reblog ( prev_status_id ) . await . log_error ( " Failed to boost " ) ;
self . delay_after_post ( ) . await ;
2021-08-30 18:03:03 +00:00
}
Err ( e ) = > {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Can't reblog: {} " , e ) ;
2021-08-30 18:03:03 +00:00
}
}
}
2021-08-26 20:11:20 +00:00
}
if ! self . replies . is_empty ( ) {
2021-10-09 19:49:26 +00:00
let mut msg = std ::mem ::take ( & mut self . replies ) ;
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " r={} " , msg ) ;
2021-08-28 08:24:35 +00:00
2021-10-12 08:38:02 +00:00
self . apply_trailing_hashtag_pleroma_bug_workaround ( & mut msg ) ;
2021-08-26 20:11:20 +00:00
2021-10-09 19:49:26 +00:00
let mention = crate ::tr! ( self , " mention_prefix " , user = & self . status_acct ) ;
2021-09-18 19:55:16 +00:00
self . send_reply_multipart ( mention , msg ) . await ? ;
}
if ! self . announcements . is_empty ( ) {
2021-10-09 19:49:26 +00:00
let mut msg = std ::mem ::take ( & mut self . announcements ) ;
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " a={} " , msg ) ;
2021-09-18 19:55:16 +00:00
2021-10-12 08:38:02 +00:00
self . apply_trailing_hashtag_pleroma_bug_workaround ( & mut msg ) ;
2021-09-18 19:55:16 +00:00
2021-10-09 19:49:26 +00:00
let msg = crate ::tr! ( self , " group_announcement " , message = & msg ) ;
2021-09-18 19:55:16 +00:00
self . send_announcement_multipart ( & msg ) . await ? ;
}
Ok ( ( ) )
}
2021-10-05 08:39:10 +00:00
async fn send_reply_multipart ( & self , mention : String , msg : String ) -> Result < ( ) , GroupError > {
2021-09-18 19:55:16 +00:00
let parts = smart_split ( & msg , Some ( mention ) , self . config . get_character_limit ( ) ) ;
2021-09-19 13:09:08 +00:00
let mut parent = self . status . id . clone ( ) ;
2021-09-18 19:55:16 +00:00
for p in parts {
2021-08-26 20:11:20 +00:00
if let Ok ( post ) = StatusBuilder ::new ( )
2021-09-18 19:55:16 +00:00
. status ( p )
2021-10-11 22:12:19 +00:00
. content_type ( " text/markdown " )
2021-09-19 13:09:08 +00:00
. in_reply_to ( & parent )
2021-08-28 08:24:35 +00:00
. visibility ( Visibility ::Direct )
2021-08-26 20:11:20 +00:00
. build ( )
{
2021-09-19 13:09:08 +00:00
let status = self . client . new_status ( post ) . await ? ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-09-19 13:09:08 +00:00
parent = status . id ;
2021-08-26 20:11:20 +00:00
}
2021-09-18 19:55:16 +00:00
// Sleep a bit to avoid throttling
tokio ::time ::sleep ( Duration ::from_secs ( 1 ) ) . await ;
2021-08-26 20:11:20 +00:00
}
2021-09-18 19:55:16 +00:00
Ok ( ( ) )
}
2021-08-28 08:24:35 +00:00
2021-10-05 08:39:10 +00:00
async fn send_announcement_multipart ( & self , msg : & str ) -> Result < ( ) , GroupError > {
2021-09-18 19:55:16 +00:00
let parts = smart_split ( msg , None , self . config . get_character_limit ( ) ) ;
2021-08-28 08:24:35 +00:00
2021-09-19 13:09:08 +00:00
let mut parent = None ;
2021-09-18 19:55:16 +00:00
for p in parts {
2021-09-19 13:09:08 +00:00
let mut builder = StatusBuilder ::new ( ) ;
2021-10-05 08:39:10 +00:00
builder . status ( p ) . content_type ( " text/markdown " ) . visibility ( Visibility ::Public ) ;
2021-09-19 13:09:08 +00:00
if let Some ( p ) = parent . as_ref ( ) {
builder . in_reply_to ( p ) ;
}
2021-10-05 08:39:10 +00:00
let post = builder . build ( ) . expect ( " error build status " ) ;
2021-08-26 20:11:20 +00:00
2021-09-19 13:09:08 +00:00
let status = self . client . new_status ( post ) . await ? ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-09-19 13:09:08 +00:00
parent = Some ( status . id ) ;
2021-09-18 19:55:16 +00:00
// Sleep a bit to avoid throttling
tokio ::time ::sleep ( Duration ::from_secs ( 1 ) ) . await ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn handle_post_with_no_commands ( & mut self ) {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " No commands in post " ) ;
if self . status . visibility . is_private ( ) {
grp_debug! ( self , " Mention is private, discard " ) ;
return ;
}
if self . can_write {
if self . status . in_reply_to_id . is_none ( ) {
2021-08-26 20:11:20 +00:00
// Someone tagged the group in OP, boost it.
2021-10-12 08:38:02 +00:00
grp_info! ( self , " Boosting OP mention " ) ;
2021-08-26 20:11:20 +00:00
// tokio::time::sleep(DELAY_BEFORE_ACTION).await;
self . reblog_status ( ) . await ;
2021-10-12 08:38:02 +00:00
// Otherwise, don't react
2021-08-26 20:11:20 +00:00
} else {
2021-10-12 08:38:02 +00:00
// Check for tags
let tags = crate ::command ::parse_status_tags ( & self . status . content ) ;
grp_debug! ( self , " Tags in mention: {:?} " , tags ) ;
for t in tags {
if self . config . is_tag_followed ( & t ) {
grp_info! ( self , " REBLOG #{} STATUS " , t ) ;
self . client . reblog ( & self . status . id ) . await . log_error ( " Failed to reblog " ) ;
self . delay_after_post ( ) . await ;
return ;
} else {
grp_debug! ( self , " #{} is not a group tag " , t ) ;
}
}
grp_debug! ( self , " Not OP & no tags, ignore mention " ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " User @{} can't post to group! " , self . status_acct ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_announce ( & mut self , msg : String ) {
2021-10-12 08:38:02 +00:00
grp_info! ( self , " Sending PSA " ) ;
2021-08-26 20:11:20 +00:00
self . add_announcement ( msg ) ;
}
async fn cmd_boost ( & mut self ) {
if self . can_write {
self . do_boost_prev_post = self . status . in_reply_to_id . is_some ( ) ;
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " User @{} can't share to group! " , self . status_acct ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 18:03:03 +00:00
async fn cmd_optout ( & mut self ) {
if self . is_admin {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optout_fail_admin_cant " ) ) ;
2021-08-30 18:03:03 +00:00
} else if self . config . is_member ( & self . status_acct ) {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optout_fail_member_cant " ) ) ;
2021-08-30 18:03:03 +00:00
} else {
self . config . set_optout ( & self . status_acct , true ) ;
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optout_ok " ) ) ;
2021-08-30 18:03:03 +00:00
}
}
async fn cmd_optin ( & mut self ) {
if self . is_admin {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optin_fail_admin_cant " ) ) ;
2021-08-30 18:03:03 +00:00
} else if self . config . is_member ( & self . status_acct ) {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optin_fail_member_cant " ) ) ;
2021-08-30 18:03:03 +00:00
} else {
self . config . set_optout ( & self . status_acct , false ) ;
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_optin_ok " ) ) ;
2021-08-30 18:03:03 +00:00
}
}
2021-08-27 19:52:33 +00:00
async fn cmd_undo ( & mut self ) -> Result < ( ) , GroupError > {
2021-10-05 08:39:10 +00:00
if let ( Some ( ref parent_account_id ) , Some ( ref parent_status_id ) ) =
2021-11-02 22:49:00 +00:00
( & self . status . in_reply_to_account_id , & self . status . in_reply_to_id )
2021-10-05 08:39:10 +00:00
{
2021-08-27 19:52:33 +00:00
if parent_account_id = = & self . group_account . id {
// This is a post sent by the group user, likely an announcement.
// Undo here means delete it.
if self . is_admin {
2021-10-12 08:38:02 +00:00
grp_info! ( self , " Deleting group post #{} " , parent_status_id ) ;
2021-08-27 19:52:33 +00:00
self . client . delete_status ( parent_status_id ) . await ? ;
2021-10-05 08:39:10 +00:00
self . delay_after_post ( ) . await ;
2021-08-27 19:52:33 +00:00
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Only admin can delete posts made by the group user " ) ;
2021-08-27 19:52:33 +00:00
}
2021-10-05 08:39:10 +00:00
} else if self . is_admin | | parent_account_id = = & self . status_user_id {
2021-10-12 08:38:02 +00:00
grp_info! ( self , " Un-reblogging post #{} " , parent_status_id ) ;
2021-10-05 08:39:10 +00:00
// User unboosting own post boosted by accident, or admin doing it
self . client . unreblog ( parent_status_id ) . await ? ;
self . delay_after_post ( ) . await ;
2021-08-27 19:52:33 +00:00
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Only the author and admins can undo reblogs " ) ;
2021-10-05 08:39:10 +00:00
// XXX this means when someone /b's someone else's post to a group,
// they then can't reverse that (only admin or the post's author can)
2021-08-27 19:52:33 +00:00
}
}
Ok ( ( ) )
}
2021-08-26 20:11:20 +00:00
async fn cmd_ban_user ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
if ! self . config . is_banned ( & u ) {
match self . config . ban_user ( & u , true ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_ban_user_ok " , user = & u ) ) ;
2021-10-05 08:39:10 +00:00
self . unfollow_by_acct ( & u ) . await . log_error ( " Failed to unfollow banned user " ) ;
2021-08-26 20:11:20 +00:00
}
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_ban_user_fail_already " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn cmd_unban_user ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
if self . config . is_banned ( & u ) {
match self . config . ban_user ( & u , false ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unban_user_ok " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
// no announcement here
}
2021-10-09 19:49:26 +00:00
Err ( e ) = > {
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unban_user_fail_already " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn cmd_ban_server ( & mut self , s : & str ) {
if self . is_admin {
if ! self . config . is_server_banned ( s ) {
match self . config . ban_server ( s , true ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_ban_server_ok " , server = s ) ) ;
2021-08-26 20:11:20 +00:00
}
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_ban_server_fail_already " , server = s ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_unban_server ( & mut self , s : & str ) {
if self . is_admin {
if self . config . is_server_banned ( s ) {
match self . config . ban_server ( s , false ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unban_server_ok " , server = s ) ) ;
2021-08-26 20:11:20 +00:00
}
2021-08-30 17:10:03 +00:00
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unban_server_fail_already " , server = s ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_add_member ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
2021-08-30 17:10:03 +00:00
// Allow even if the user is already a member - that will trigger re-follow
2021-08-26 22:30:49 +00:00
match self . config . set_member ( & u , true ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_add_user_ok " , user = & u ) ) ;
// marked as member, now also follow the user
2021-10-05 08:39:10 +00:00
self . follow_by_acct ( & u ) . await . log_error ( " Failed to follow " ) ;
2021-08-26 22:30:49 +00:00
}
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn cmd_remove_member ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
2021-08-26 22:30:49 +00:00
match self . config . set_member ( & u , false ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_remove_user_ok " , user = & u ) ) ;
2021-10-05 08:39:10 +00:00
self . unfollow_by_acct ( & u ) . await . log_error ( " Failed to unfollow removed user " ) ;
2021-08-26 22:30:49 +00:00
}
2021-08-30 17:10:03 +00:00
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn cmd_add_tag ( & mut self , tag : String ) {
if self . is_admin {
2021-08-30 17:10:03 +00:00
if ! self . config . is_tag_followed ( & tag ) {
self . config . add_tag ( & tag ) ;
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_add_tag_ok " , tag = & tag ) ) ;
2021-08-28 08:24:35 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_add_tag_fail_already " , tag = & tag ) ) ;
2021-08-28 08:24:35 +00:00
}
2021-08-26 20:11:20 +00:00
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_remove_tag ( & mut self , tag : String ) {
if self . is_admin {
2021-08-28 08:24:35 +00:00
if self . config . is_tag_followed ( & tag ) {
self . config . remove_tag ( & tag ) ;
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_remove_tag_ok " , tag = & tag ) ) ;
2021-08-28 08:24:35 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_remove_tag_fail_already " , tag = & tag ) ) ;
2021-08-28 08:24:35 +00:00
}
2021-08-26 20:11:20 +00:00
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
async fn cmd_grant_admin ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
2021-08-26 20:11:20 +00:00
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
if ! self . config . is_admin ( & u ) {
match self . config . set_admin ( & u , true ) {
Ok ( _ ) = > {
// try to make the config a little more sane, admins should be members
let _ = self . config . set_member ( & u , true ) ;
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_admin_ok " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_admin_fail_already " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
2021-10-09 19:49:26 +00:00
async fn cmd_revoke_admin ( & mut self , user : & str ) -> Result < ( ) , GroupError > {
2021-08-26 20:11:20 +00:00
let u = normalize_acct ( user , & self . group_acct ) ? ;
if self . is_admin {
if self . config . is_admin ( & u ) {
match self . config . set_admin ( & u , false ) {
Ok ( _ ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unadmin_ok " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
Err ( e ) = > {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_error " , cause = & e . to_string ( ) ) ) ;
2021-08-26 20:11:20 +00:00
}
}
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_unadmin_fail_already " , user = & u ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
async fn cmd_open_group ( & mut self ) {
if self . is_admin {
if self . config . is_member_only ( ) {
self . config . set_member_only ( false ) ;
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_open_resp " ) ) ;
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_open_resp_already " ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_close_group ( & mut self ) {
if self . is_admin {
if ! self . config . is_member_only ( ) {
self . config . set_member_only ( true ) ;
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_close_resp " ) ) ;
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_close_resp_already " ) ) ;
2021-08-26 20:11:20 +00:00
}
} else {
2021-10-12 08:38:02 +00:00
grp_warn! ( self , " Ignore cmd, user not admin " ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_help ( & mut self ) {
2021-08-30 17:10:03 +00:00
let membership_line = if self . is_admin {
2021-10-09 19:49:26 +00:00
crate ::tr! ( self , " help_membership_admin " )
2021-08-26 22:19:43 +00:00
} else if self . config . is_member ( & self . status_acct ) {
2021-10-09 19:49:26 +00:00
crate ::tr! ( self , " help_membership_member " )
2021-08-26 22:19:43 +00:00
} else if self . config . is_member_only ( ) {
2021-10-09 19:49:26 +00:00
crate ::tr! ( self , " help_membership_guest_closed " )
2021-08-30 17:10:03 +00:00
} else {
2021-10-09 19:49:26 +00:00
crate ::tr! ( self , " help_membership_guest_open " )
2021-08-30 17:10:03 +00:00
} ;
if self . config . is_member_only ( ) {
2021-11-02 22:49:00 +00:00
self . add_reply ( crate ::tr! (
self ,
" help_group_info_closed " ,
membership = & membership_line
) ) ;
2021-08-26 20:11:20 +00:00
} else {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " help_group_info_open " , membership = & membership_line ) ) ;
2021-08-26 20:11:20 +00:00
}
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " help_basic_commands " ) ) ;
if ! self . is_admin {
self . add_reply ( crate ::tr! ( self , " help_member_commands " ) ) ;
}
2021-08-27 19:52:33 +00:00
2021-08-26 20:11:20 +00:00
if self . is_admin {
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " help_admin_commands " ) ) ;
2021-08-26 20:11:20 +00:00
}
}
async fn cmd_list_members ( & mut self ) {
if self . is_admin {
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " member_list_heading " ) ) ;
2021-08-26 20:11:20 +00:00
self . append_member_list_to_reply ( ) ;
} else {
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " admin_list_heading " ) ) ;
2021-08-26 20:11:20 +00:00
self . append_admin_list_to_reply ( ) ;
}
}
async fn cmd_list_tags ( & mut self ) {
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " tag_list_heading " ) ) ;
2021-08-26 20:11:20 +00:00
let mut tags = self . config . get_tags ( ) . collect ::< Vec < _ > > ( ) ;
tags . sort ( ) ;
2021-10-09 19:49:26 +00:00
let mut to_add = String ::new ( ) ;
2021-08-26 20:11:20 +00:00
for t in tags {
2021-11-02 22:49:00 +00:00
to_add . push_str ( & crate ::tr! ( self , " tag_list_entry " , tag = t ) ) ;
2021-08-26 20:11:20 +00:00
}
2021-10-09 19:49:26 +00:00
self . add_reply ( to_add ) ;
2021-08-26 20:11:20 +00:00
}
async fn cmd_leave ( & mut self ) {
if self . config . is_member_or_admin ( & self . status_acct ) {
// admin can leave but that's a bad idea
let _ = self . config . set_member ( & self . status_acct , false ) ;
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " cmd_leave_resp " ) ) ;
2021-08-26 20:11:20 +00:00
}
2021-08-26 22:30:49 +00:00
2021-10-05 08:39:10 +00:00
self . unfollow_user_by_id ( & self . status_user_id )
. await
2021-08-26 22:30:49 +00:00
. log_error ( " Failed to unfollow " ) ;
2021-08-26 20:11:20 +00:00
}
async fn cmd_join ( & mut self ) {
if self . config . is_member_or_admin ( & self . status_acct ) {
2021-10-12 08:38:02 +00:00
grp_debug! ( self , " Already member or admin, try to follow-back again " ) ;
2021-08-26 20:11:20 +00:00
// Already a member, so let's try to follow the user
// again, maybe first time it failed
2021-10-05 08:39:10 +00:00
self . follow_user_by_id ( & self . status_user_id ) . await . log_error ( " Failed to follow " ) ;
2021-08-26 20:11:20 +00:00
} else {
// Not a member yet
if self . config . is_member_only ( ) {
// No you can't
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " welcome_closed " ) ) ;
2021-08-26 20:11:20 +00:00
self . append_admin_list_to_reply ( ) ;
} else {
// Open access, try to follow back
2021-10-05 08:39:10 +00:00
self . follow_user_by_id ( & self . status_user_id ) . await . log_error ( " Failed to follow " ) ;
2021-08-26 20:11:20 +00:00
// This only fails if the user is banned, but that is filtered above
let _ = self . config . set_member ( & self . status_acct , true ) ;
2021-10-05 23:09:20 +00:00
self . add_reply ( crate ::tr! ( self , " welcome_join_cmd " ) ) ;
2021-08-26 20:11:20 +00:00
}
}
}
async fn cmd_ping ( & mut self ) {
2021-10-09 19:49:26 +00:00
self . add_reply ( crate ::tr! ( self , " ping_response " , version = env! ( " CARGO_PKG_VERSION " ) ) ) ;
2021-08-26 20:11:20 +00:00
}
async fn unfollow_by_acct ( & self , acct : & str ) -> Result < ( ) , GroupError > {
// Try to unfollow
if let Ok ( Some ( id ) ) = self . lookup_acct_id ( acct , true ) . await {
2021-08-26 22:19:43 +00:00
self . unfollow_user_by_id ( & id ) . await ? ;
}
Ok ( ( ) )
}
async fn follow_by_acct ( & self , acct : & str ) -> Result < ( ) , GroupError > {
// Try to unfollow
if let Ok ( Some ( id ) ) = self . lookup_acct_id ( acct , false ) . await {
self . follow_user_by_id ( & id ) . await ? ;
2021-08-26 20:11:20 +00:00
}
Ok ( ( ) )
}
2021-08-30 18:03:03 +00:00
/// Convert ID to account, checking if the user is boostable
async fn id_to_acct_check_boostable ( & self , id : & str ) -> Result < String , GroupError > {
// Try to unfollow
let account = self . client . get_account ( id ) . await ? ;
let bio = utils ::strip_html ( & account . note ) ;
if RE_NOBOT_TAG . is_match ( & bio ) {
// #nobot
Err ( GroupError ::UserOptedOut )
} else {
let normalized = normalize_acct ( & account . acct , & self . group_acct ) ? ;
if self . config . is_banned ( & normalized ) {
2021-10-05 08:39:10 +00:00
Err ( GroupError ::UserIsBanned )
2021-08-30 18:03:03 +00:00
} else if self . config . is_optout ( & normalized ) {
2021-10-05 08:39:10 +00:00
Err ( GroupError ::UserOptedOut )
2021-08-30 18:03:03 +00:00
} else {
Ok ( normalized )
}
}
}
2021-10-05 08:39:10 +00:00
async fn delay_after_post ( & self ) {
tokio ::time ::sleep ( Duration ::from_secs_f64 ( self . cc . delay_after_post_s ) ) . await ;
}
2021-08-28 08:24:35 +00:00
2021-10-12 08:38:02 +00:00
fn apply_trailing_hashtag_pleroma_bug_workaround ( & self , msg : & mut String ) {
if crate ::command ::RE_HASHTAG_TRIGGERING_PLEROMA_BUG . is_match ( msg ) {
// if a status ends with a hashtag, pleroma will fuck it up
grp_debug! ( self , " Adding \" . \" to fix pleroma hashtag eating bug! " ) ;
msg . push_str ( " . " ) ;
}
2021-08-28 08:24:35 +00:00
}
}
2021-09-18 19:55:16 +00:00
2021-10-05 08:39:10 +00:00
fn smart_split ( msg : & str , prefix : Option < String > , limit : usize ) -> Vec < String > {
2021-09-18 19:55:16 +00:00
let prefix = prefix . unwrap_or_default ( ) ;
if msg . len ( ) + prefix . len ( ) < limit {
return vec! [ format! ( " {} {} " , prefix , msg ) ] ;
}
let mut parts_to_send = vec! [ ] ;
let mut this_piece = prefix . clone ( ) ;
2021-10-05 08:39:10 +00:00
for l in msg . split ( '\n' ) {
2021-10-12 08:38:02 +00:00
// println!("* Line: {:?}", l);
2021-10-05 08:39:10 +00:00
match ( this_piece . len ( ) + l . len ( ) ) . cmp ( & limit ) {
Ordering ::Less = > {
2021-10-12 08:38:02 +00:00
// println!("append line");
2021-10-05 08:39:10 +00:00
// this line still fits comfortably
this_piece . push_str ( l ) ;
this_piece . push ( '\n' ) ;
2021-09-18 19:55:16 +00:00
}
2021-10-05 08:39:10 +00:00
Ordering ::Equal = > {
2021-10-12 08:38:02 +00:00
// println!("exactly fits within limit");
2021-10-05 08:39:10 +00:00
// this line exactly reaches the limit
this_piece . push_str ( l ) ;
parts_to_send . push ( std ::mem ::take ( & mut this_piece ) . trim ( ) . to_owned ( ) ) ;
this_piece . push_str ( & prefix ) ;
}
Ordering ::Greater = > {
2021-10-12 08:38:02 +00:00
// println!("too long to append (already {} + new {})", this_piece.len(), l.len());
2021-10-05 08:39:10 +00:00
// line too long to append
if this_piece ! = prefix {
let trimmed = this_piece . trim ( ) ;
if ! trimmed . is_empty ( ) {
2021-10-12 08:38:02 +00:00
// println!("flush buffer: {:?}", trimmed);
2021-10-05 08:39:10 +00:00
parts_to_send . push ( trimmed . to_owned ( ) ) ;
}
}
2021-09-18 19:55:16 +00:00
2021-10-05 08:39:10 +00:00
// start new piece with the line. If the line is too long, break it up.
this_piece = format! ( " {} {} " , prefix , l ) ;
2021-09-18 19:55:16 +00:00
2021-10-05 08:39:10 +00:00
while this_piece . len ( ) > limit {
// line too long, try splitting at the last space, if any
let to_send = if let Some ( last_space ) = ( & this_piece [ ..= limit ] ) . rfind ( ' ' ) {
2021-10-12 08:38:02 +00:00
// println!("line split at word boundary");
2021-10-05 08:39:10 +00:00
let mut p = this_piece . split_off ( last_space + 1 ) ;
std ::mem ::swap ( & mut p , & mut this_piece ) ;
p
} else {
2021-10-12 08:38:02 +00:00
// println!("line split at exact len (no word boundary found)");
2021-10-05 08:39:10 +00:00
let mut p = this_piece . split_off ( limit ) ;
std ::mem ::swap ( & mut p , & mut this_piece ) ;
p
} ;
let part_trimmed = to_send . trim ( ) ;
2021-10-12 08:38:02 +00:00
// println!("flush buffer: {:?}", part_trimmed);
2021-10-05 08:39:10 +00:00
parts_to_send . push ( part_trimmed . to_owned ( ) ) ;
this_piece = format! ( " {} {} " , prefix , this_piece . trim ( ) ) ;
}
this_piece . push ( '\n' ) ;
2021-09-18 19:55:16 +00:00
}
}
}
2021-09-19 12:52:20 +00:00
if this_piece ! = prefix {
let leftover_trimmed = this_piece . trim ( ) ;
if ! leftover_trimmed . is_empty ( ) {
2021-10-12 08:38:02 +00:00
// println!("flush buffer: {:?}", leftover_trimmed);
2021-09-19 12:52:20 +00:00
parts_to_send . push ( leftover_trimmed . to_owned ( ) ) ;
}
}
2021-09-18 19:55:16 +00:00
parts_to_send
}
#[ cfg(test) ]
mod test {
#[ test ]
2021-09-19 12:52:20 +00:00
fn test_smart_split_lines ( ) {
2021-09-18 19:55:16 +00:00
let to_split = " a234567890 \n b234567890 \n c234567890 \n d234 \n 67890 \n e234567890 \n " ;
let parts = super ::smart_split ( to_split , None , 10 ) ;
2021-10-05 08:39:10 +00:00
assert_eq! (
vec! [
" a234567890 " . to_string ( ) ,
" b234567890 " . to_string ( ) ,
" c234567890 " . to_string ( ) ,
" d234 \n 67890 " . to_string ( ) ,
" e234567890 " . to_string ( ) ,
] ,
parts
) ;
2021-09-18 19:55:16 +00:00
}
#[ test ]
2021-09-19 12:52:20 +00:00
fn test_smart_split_nosplit ( ) {
2021-09-18 19:55:16 +00:00
let to_split = " foo \n bar \n baz " ;
let parts = super ::smart_split ( to_split , None , 1000 ) ;
2021-10-09 19:49:26 +00:00
assert_eq! ( vec! [ " foo \n bar \n baz " . to_string ( ) ] , parts ) ;
2021-09-18 19:55:16 +00:00
}
#[ test ]
2021-09-19 12:52:20 +00:00
fn test_smart_split_nosplit_prefix ( ) {
2021-09-18 19:55:16 +00:00
let to_split = " foo \n bar \n baz " ;
let parts = super ::smart_split ( to_split , Some ( " PREFIX " . to_string ( ) ) , 1000 ) ;
2021-10-09 19:49:26 +00:00
assert_eq! ( vec! [ " PREFIXfoo \n bar \n baz " . to_string ( ) ] , parts ) ;
2021-09-18 19:55:16 +00:00
}
#[ test ]
2021-09-19 12:52:20 +00:00
fn test_smart_split_prefix_each ( ) {
2021-09-18 19:55:16 +00:00
let to_split = " 1234 \n 56 \n 7 " ;
let parts = super ::smart_split ( to_split , Some ( " PREFIX " . to_string ( ) ) , 10 ) ;
2021-10-09 19:49:26 +00:00
assert_eq! ( vec! [ " PREFIX1234 " . to_string ( ) , " PREFIX56 \n 7 " . to_string ( ) ] , parts ) ;
2021-09-18 19:55:16 +00:00
}
2021-09-19 12:52:20 +00:00
#[ test ]
fn test_smart_split_words ( ) {
let to_split = " one two three four five six seven eight nine ten " ;
let parts = super ::smart_split ( to_split , None , 10 ) ;
2021-10-05 08:39:10 +00:00
assert_eq! (
vec! [
" one two " . to_string ( ) ,
" three four " . to_string ( ) ,
" five six " . to_string ( ) ,
" seven " . to_string ( ) ,
" eight nine " . to_string ( ) ,
" ten " . to_string ( ) ,
] ,
parts
) ;
2021-09-19 12:52:20 +00:00
}
#[ test ]
fn test_smart_split_words_multispace ( ) {
let to_split = " one two three four five six seven eight nine ten " ;
let parts = super ::smart_split ( to_split , None , 10 ) ;
2021-10-05 08:39:10 +00:00
assert_eq! (
vec! [
" one two " . to_string ( ) ,
" three four " . to_string ( ) ,
" five six " . to_string ( ) ,
" seven " . to_string ( ) ,
" eight nine " . to_string ( ) ,
" ten " . to_string ( ) ,
] ,
parts
) ;
2021-09-19 12:52:20 +00:00
}
#[ test ]
fn test_smart_split_words_longword ( ) {
let to_split = " one two threefourfive six " ;
let parts = super ::smart_split ( to_split , None , 10 ) ;
2021-10-05 08:39:10 +00:00
assert_eq! (
2021-10-09 19:49:26 +00:00
vec! [ " one two " . to_string ( ) , " threefourf " . to_string ( ) , " ive six " . to_string ( ) ] ,
2021-10-05 08:39:10 +00:00
parts
) ;
2021-09-19 12:52:20 +00:00
}
#[ test ]
fn test_smart_split_words_prefix ( ) {
let to_split = " one two three four five six seven eight nine ten " ;
let parts = super ::smart_split ( to_split , Some ( " PREFIX " . to_string ( ) ) , 15 ) ;
2021-10-05 08:39:10 +00:00
assert_eq! (
vec! [
" PREFIXone two " . to_string ( ) ,
" PREFIXthree " . to_string ( ) ,
" PREFIXfour five " . to_string ( ) ,
" PREFIXsix seven " . to_string ( ) ,
" PREFIXeight " . to_string ( ) ,
" PREFIXnine ten " . to_string ( ) ,
] ,
parts
) ;
2021-09-19 12:52:20 +00:00
}
#[ test ]
fn test_smart_split_realistic ( ) {
let to_split = " \
Lorem ipsum dolor sit amet , consectetur adipiscing elit . \ n \
Aenean venenatis libero ac ex suscipit , nec efficitur arcu convallis . \ n \
Nulla ante neque , efficitur nec fermentum a , fermentum nec nisl . \ n \
Sed dolor ex , vestibulum at malesuada ut , faucibus ac ante . \ n \
Nullam scelerisque magna dui , id tempor purus faucibus sit amet . \ n \
Curabitur pretium condimentum pharetra . \ n \
Aenean dictum , tortor et ultrices fermentum , mauris erat vehicula lectus . \ n \
Nec varius mauris sem sollicitudin dolor . Nunc porta in urna nec vulputate . " ;
let parts = super ::smart_split ( to_split , Some ( " @pepa@pig.club " . to_string ( ) ) , 140 ) ;
assert_eq! ( vec! [
" @pepa@pig.club Lorem ipsum dolor sit amet, consectetur adipiscing elit. " . to_string ( ) ,
" @pepa@pig.club Aenean venenatis libero ac ex suscipit, nec efficitur arcu convallis. " . to_string ( ) ,
" @pepa@pig.club Nulla ante neque, efficitur nec fermentum a, fermentum nec nisl. \n Sed dolor ex, vestibulum at malesuada ut, faucibus ac ante. " . to_string ( ) ,
" @pepa@pig.club Nullam scelerisque magna dui, id tempor purus faucibus sit amet. \n Curabitur pretium condimentum pharetra. " . to_string ( ) ,
" @pepa@pig.club Aenean dictum, tortor et ultrices fermentum, mauris erat vehicula lectus. " . to_string ( ) ,
" @pepa@pig.club Nec varius mauris sem sollicitudin dolor. Nunc porta in urna nec vulputate. " . to_string ( ) ,
] , parts ) ;
}
2021-10-05 08:39:10 +00:00
}